☰
IngeTrazo 纯 Python 解析 .skp 内幕:OpenSKP 后端与 Legacy MFC 容器格式
2026/10/19 3:19:00 网站建设 项目流程

【免费下载链接】ingetrazo

Traza como a mano: modelador 3D libre para arquitectura, ingeniería y diseño 3D. Linux, Windows y macOS. BIM e IFC con IngePresupuestos. Abre archivos .skp.

项目地址:https://gitcode.com/gh_mirrors/in/ingetrazo
点击查看免费下载

IngeTrazo是一款自由 3D 建模器(Linux / Windows / macOS 全平台),面向建筑、工程与 3D 设计。它有一个杀手级能力:不依赖任何专有工具、不需要 Wine、也不需要安装 SketchUp,就能原生打开从 2013 年到 2026 年的任意版本 .skp 文件——全程纯 Python 实现。这篇文章带你走进 formats/skp.py 这条"单一接缝"背后的完整架构,看它如何同时驾驭 2021 年前的 Legacy MFC 二进制容器和 ZIP 新格式,以及那些藏在位级细节里的经典坑。

为什么"纯 Python 读 .skp"很难

.skp 是 SketchUp 的私有格式:没有公开规范、二进制布局随版本悄悄变化、且和微软 MFC 序列化库深度绑定。大多数开源软件的常见做法是调用专有 SDK 或转一道中间格式,但这意味着——

  • 依赖闭源组件,无法全平台分发;
  • 版本支持范围被供应商锁死;
  • 出问题时没有任何调试手段。

IngeTrazo 的选择是把解析器做成可选依赖 + 优雅降级:解析器用的是 MIT 协议的纯 Python 库 OpenSKP(pip install openskp),没有安装时应用照常启动,只是把 .skp 标记为"暂不可读"。相关设计文档见 docs/skp-backend.md 与 docs/openskp-collaboration.md。

单一接缝:先解析、后落盘

整个导入流程被收敛到 formats/skp.py 一个模块,分三步走:

  1. 识别容器:detect_format(path)只读文件前 64 个字节——ZIP 签名PK\x03\x04,或 UTF-16LE 编码的 "Sketch… Model" 标记,命中其一即认定为 .skp(见 detect_format);
  2. 解析为 payload:parse_skp(path)调用后端,把文件解析成一份纯数据 payload(世界坐标面环 + 材质 + 分组),全程不触碰场景对象;
  3. 落盘为组:apply_payload(scene, payload)把 payload 廉价地挂进场景,并在同一条撤销命令里完成共面合并与软边处理。

这种"Parse then apply"结构有个关键安全性质:解析失败或为空时,抛出的NeedsConverter异常发生在任何场景修改之前,绝不会留下半成品的编辑记录(见 parse_skp)。

上图就是一个典型的 .skp 导入结果:右侧浏览器里能看到 .skp 自带的组件定义被逐一列出。

两种容器时代:Legacy MFC 流 vs ZIP 包裹

.skp 的历史上存在两种物理容器,而麻烦在于——文件头的 magic bytes 长得一样,光看前几字节无法区分是哪个时代的文件。于是 IngeTrazo 干脆不区分,把两代文件都交给解析器自己识别:

特征Legacy(≤2020,v8–v20)2021 及以后
物理结构一条未压缩的 MFCCArchive二进制流,带全局 1-based 存储映射表ZIP 容器,模型数据放在内部的model.dat
无 ZIP / 无压缩是(整文件即一条序列化流)否
解析器openskp/legacy.py(IngeTrazo 维护的 fork 补全)OpenSKP 主体

Legacy 解析器是 IngeTrazo fork 中最重要的补丁之一:一个完整的 MFC 流遍历器,依次读出材质与贴图、图层、半边(half-edge)拓扑、定义/实例/组、面-相机标志、UV 矩阵、标注/文字/辅助线/剖面,最终输出与新版解析器完全一致的full_parse()字典。这带来的工程红利是:formats/skp.py 的接缝代码一行都不用改。

它已在真实的 2016/2017/2018 版模型上验证:与这些文件重新另存为新版格式后的结果对比,面数、边数、面积、包围盒精确一致,skp_diff指纹逐位相同。已知的遗留缺口记录在 docs/skp-backend.md(如材质数少于 2 的极老文件尚不能引导槽位基址、CImage实体与文档缩略图被跳过等)。

OpenSKP 数据模型:一份文件的"解剖图"

OpenSKP 的SkpFile.open(path).parse()返回一棵清晰的对象树:

  • SkpModel:definitions(id →Definition)、materials、layers、version;
  • Definition:顶点字典、边字典、面字典 + 实例列表;
  • Face:loops——第一个环是外边界,其余是孔洞,另附法线与material_id;
  • Instance:13 个浮点数(行主序 3×3 矩阵 + 平移)、指向被放置定义的ref_idx、children。

适配层 formats/skp_openskp.py 负责把这棵树压平进 IngeTrazo 的场景,其中有两个格式层面的关键约定:

  • 单位是英寸、坐标 Z-up——与 IngeTrazo 的向上轴一致,所以只需乘以 0.0254 换算成米,无需交换坐标轴(见_INCH定义);
  • 实例树压平为世界坐标多边形:根节点的散落面合成一个以文件名命名的组,每个顶层实例成为携带 .skp 定义名的独立组;被放置 ≥2 次的定义会被提取为共享原型,每份拷贝只是一个 O(1) 的变换实例。

材质与贴图通过SkpModel.materials_by_id连接(这是 IngeTrazo 向上游提交的 PR 带来的能力):纯色材质变成attrs["color"],贴图材质的原始图像字节被提取进应用自己的缓存目录——注意,导入永远不会在用户的 .skp 旁边生成文件夹(见_texture_dir),保存为 .igz 时贴图会被直接打进容器文件,缓存可随时清空。

三个真实的坑:flag 语义漂移、编码与位流错位

逆向解析最折磨人的从来不是大结构,而是位级细节。这里有三个 IngeTrazo 实战中踩到并解决的案例:

①sense标志语义漂移——一次上游改动差点抹掉 68% 的面。OpenSKP 不同版本对环上"方向标志"的约定发生了变化(原来是 0/1 存储位,后来归一化为 +1/-1)。适配层干脆不读这个标志,改为沿环的连接关系推断每个顶点:相邻共边在头尾相接,无论哪种编码都成立。否则广场模型会丢失 68% 表面积(43008 → 13590 m²)并画成一地飞刺——这段事故记录写在_ring_raw的文档字符串里,值得每个逆向工程师一读。

② 一个单词的编码 bug——"缺失的功能"其实只是errors='ignore'。解析器曾用 ASCII 解码实体名,西语材质名"Celtis_australis_s cópia"变成"...s cpia",与 XML 材质表对不上号,于是整棵树的树叶材质"凭空消失"。凡是西语/葡语/法语环境下创建的模型必中。修复只需把六个解码点换成 UTF-8,修复后树叶表面积从 0 恢复到 445 m²。

③ Legacy 尺寸标注锚进组内——位流读偏,72 个根实体只剩 2 个。2021 年前的线性标注(CDimensionLinear)在锚点位于嵌套组内时,固定的跳字节数失效,流位置滑出记录,根实体读取器在第一个读不懂的条目处整体停下——一栋 2018 版模型只开出了墙和草坪,二层、屋顶、门窗、树木全部丢失(12 个实例而非 2384 个)。补丁在 formats/openskp_compat.py:它不判断版本号,而是按行为检测——一旦上游解析器自带_connection_paths就自动让位,是"上游优先、下游兜底"策略的样板实现(见 apply())。

用数据说话:差分验证与实测表现

IngeTrazo 没有"感觉差不多"这种验收方式。scripts/skp_diff.py 是一个无头差分测试架:加载模型主人用原程序导出的 COLLADA 文件作为 ground truth,再把同一模型走 IngeTrazo 自己的 .skp 读取路径,对两份场景做结构指纹比对(几何计数、包围盒、材质数、分组):

python scripts/skp_diff.py path/to/model.skp --dae model.dae

对真实 2022 版文件的实测结论:

  • ✅包围盒精确——单位、Z-up 与实例变换全部正确;
  • ✅几何 95%+ 完整——最初的"5–9% 缺失"经排查是测量假象而非解析丢失(参考 DAE 的 4516 个三角形与解析数完全相等,总面积差 0.00%);
  • ✅材质 18/18、贴图 2/2 精确对齐——包括实例级材质继承;
  • ⚡性能:34 MB 的广场模型(9.7 万面、42273 m²、19 种材质 + 10 张贴图)纯 Python 约 12 秒解析完成。

对应测试覆盖见 tests/test_skp_backend.py,容器识别、级联解析、降级路径都有独立用例。

架构上的"保险单":上游优先,下游兜底

最后值得一提的是协作策略(详见 docs/openskp-collaboration.md):

  • A 层(上游优先):所有修复先给 OpenSKP 提 PR——材质 id 暴露、贴图提取、UTF-8 解码、UV 变换、图片实体等均已提交;
  • B 层(维护中的 fork 兜底):被拒或待合并的补丁留在友好 fork 的分支里,IngeTrazo 锁定版本使用。由于 OpenSKP 是 MIT 协议(与 GPL 兼容),上游是否合并 PR 不影响 IngeTrazo 的能力上限,只影响维护成本。

而验证全程严格黑盒:只对比"观察到的 .skp 文件 + 原程序导出的 COLLADA",从不反编译、从不复制专有条带进解析器。

小结

IngeTrazo 的 .skp 读取栈可以浓缩成三句话:

  1. formats/skp.py 用"单一接缝 + parse-then-apply"把解析器与应用彻底解耦,失败绝不污染场景;
  2. 解析核心是纯 Python 的 OpenSKP:2021+ 的 ZIP 新格式走主体,≤2020 的 Legacy MFC 单流容器走 fork 补全的openskp/legacy.py遍历器;
  3. 质量靠skp_diff差分指纹与真实用户文件持续背书——从位流错位到编码细节的坑,每一个都有案底、有修复、有测试。

如果你需要处理 .skp 相关的 Python 工作,OpenSKP 本身也值得直接使用——它正是这条纯 Python 路径的基石。

【免费下载链接】ingetrazo

Traza como a mano: modelador 3D libre para arquitectura, ingeniería y diseño 3D. Linux, Windows y macOS. BIM e IFC con IngePresupuestos. Abre archivos .skp.

项目地址:https://gitcode.com/gh_mirrors/in/ingetrazo
点击查看免费下载
上一篇:3步解锁SWF文件:JPEXS免费Flash反编译器的终极指南
下一篇:openGauss 仓颉驱动(opengauss-driver)执行 SQL 语句完全指南:从预处理语句到结果集读取

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询