Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

10
docs/status/N-001.md Normal file
View File

@@ -0,0 +1,10 @@
# N-001 Native Depsgraph 安全求值
状态:`done`(持续回归)
`empty.blend``basic_scene.blend``rigged_shape_scene.blend` 已通过 Blender 5.2
headless full loader、WASM Depsgraph 和结构化 modifier 报告frame/pose golden 已加入
默认回归。重复求值和发布级内存门仍由 N-008 复核。
验收:`bash tools/web/check-dual-engine.sh``npm --prefix web run test:depsgraph`

21
docs/status/N-002.md Normal file
View File

@@ -0,0 +1,21 @@
# N-002 Modifier/Depsgraph 能力矩阵
状态:`done`(能力矩阵契约)
当前报告覆盖 modifier UID、堆栈顺序、viewport/render/edit/cage 开关、目标对象依赖和
`EVALUATED`/`DISABLED`/`BLOCKED` 结构化状态。跨对象循环返回
`MODIFIER_DEPENDENCY_CYCLE`,缺目标返回 `MODIFIER_TARGET_MISSING`。native target 遍历覆盖
Array、Mirror、Boolean、Lattice、Curve、Armature、Hook、Mesh Deform、Shrinkwrap、
Surface Deform、Screw 和 Displace mapping object。
本轮继续加入受控 Displace无纹理、无顶点组、Local Space 的常量 X/Y/Z/Normal 位移与
Blender 5.2 golden 一致纹理、RGB 和 mapping object 返回
`MODIFIER_CONFIGURATION_UNSUPPORTED`。Geometry Nodes 白名单从 Transform Geometry 扩为
constant-offset Set Position二者都使用桌面 golden 比较。simulation zone/bake 不执行不确定
状态,保留输入几何并返回 `GEOMETRY_NODES_SIMULATION_UNAVAILABLE`。机器门逐项覆盖 Blender
5.2 的 086 共 87 个类型27 类进入 native 白名单60 类保留身份并使用 disabled sentinel
启用时结构化阻断。完整 lazy-function runtime、deterministic simulation cache、Grease Pencil
求值不是该能力矩阵的隐含承诺;未来扩白名单必须新增 Blender golden。
验收:`npm --prefix web run test:modifier-goldens``npm --prefix web run test:modifier-cycle`
`npm --prefix web run test:depsgraph``npm --prefix web run test:modifier-matrix`

12
docs/status/N-003.md Normal file
View File

@@ -0,0 +1,12 @@
# N-003 骨骼、Shape Key、约束和多帧误差
状态:`done`
已修复 Web RNA 浮点属性默认 `0..0` 钳制、显式补齐 headless transform 更新,并统一
SceneIR 矩阵为 Blender Python/glTF 使用的列主序展开animation 和 pose constraint
golden 已扩为 frame 1/5/10/15。当前 fixture 使用四根骨骼、独立 Auxiliary 分支、两骨 IK、
Copy Rotation、非单位 mesh bind、Armature 对象变换和父级非均匀缩放;每帧同时比较全部
pose matrix、constraint target/pole target 和最终蒙皮顶点的最大/RMS 误差。
验收:`npm --prefix web run test:frame-evaluation`
`npm --prefix web run test:pose-constraint-goldens`

18
docs/status/N-004.md Normal file
View File

@@ -0,0 +1,18 @@
# N-004 Packed Image 覆盖
状态:`completed_current_scope`
单 PackedFile 与 PackedFile 列表已覆盖。`image_resource_matrix.blend` 进一步覆盖两个独立
asset ID 的 packed UDIM tile、generated image 元数据、缺失外部路径和 linked library
`corrupt_packed_image.blend` 验证 PNG 签名损坏返回 `PACKED_IMAGE_SIGNATURE_INVALID`
SceneIR 公开 `sourceKind``assetStatus`、tile、library 和稳定错误码Worker 可按 tile asset
ID 提取字节。
packed/tile 字节现按 SHA-256 写入
`projects/<project>/assets/sha256/<prefix>/<digest>`;重复内容只保留一份,读取时复验长度和
摘要IndexedDB 保存项目级 asset metadata。Storage Worker 重启后可重新列举并读取 OPFS
资产。Blender `//` 相对路径会规范化绝对路径、URI、反斜杠、百分号编码和 `..` 逃逸均以
`ASSET_PATH_INVALID`/`ASSET_PATH_OUTSIDE_PROJECT` 阻断。
验收:`npm --prefix web run test:image-resources`,以及 E2E
`content-addresses assets, deduplicates them, and rediscovers them after worker restart`

11
docs/status/N-005.md Normal file
View File

@@ -0,0 +1,11 @@
# N-005 GLB Blender 级往返
状态:`completed_current_scope`
Web 严格 GLB 解析和 SceneIR 语义比较保留为快速门。独立回归现在调用项目 TypeScript
exporter随后使用桌面 Blender 5.2 headless importer比较网格/对象变换、帧 1/25 动画、
PBR、显式 sampler、嵌入 PNG/sRGB、morph、双骨骼层级、skin modifier、vertex group 和
权重归一化。该门同时修复了 glTF 动画首帧被平移到 0 的问题。
验收:`npm --prefix web run test:glb-blender-roundtrip`。未来新增 interpolation、相机/灯光
或材质扩展时,必须同步扩大该 Blender 语义摘要,不能退回仅解析 Web GLB。

30
docs/status/N-006.md Normal file
View File

@@ -0,0 +1,30 @@
# N-006 Blender Main 权威回写与恢复
状态:`completed_current_scope`
`setFrame``setObjectVisibility``setModifierEnabled` 现在直接更新持久化
Blender Main保存通过非 undo 的标准 write-file callback 写出内存 `.blend`。动画与 Pose
constraint fixture 已连续执行两代保存/重开,并在 1/5/10 帧比较网格、world matrix 和 bone
pose matrix。权威 Main dirty 后的 Depsgraph 现在直接使用 writer 输出,不再回退原始 reader
buffer。当前协议命令集中的 frame、visibility、modifier 开关和无 skin/shape-key 的
`decimateMesh` 均回写 Main。decimate 会重建三角 Mesh并保存 UV、corner color 和 material
indexCollapse/Un-Subdivide/Dissolve 都经过保存门。带 skin/shape-key 的 decimate 继续保持
snapshot-only保存返回 `BLEND_WRITE_REQUIRES_MAIN_AUTHORITY`,不会丢失 Key/vertex-group 后
伪造成功。
历史项现同时保存 SceneIR 与可重开的 Main bufferMain 命令 undo/redo 后可立即保存,撤销
snapshot-only 状态会恢复权威性。OPFS 保存使用 SHA-256、staging、journal、提交后复验和
manifest新 Worker 可从 staging 后或 scene commit 后两种故障点恢复,并串行化同项目的并发
保存。AutosaveScheduler 复用同一事务。N-007/W-080 新增的 Mesh Edit、UV、材质、packed
image、动画、约束、层级、Collection、Join/Separate、Apply Transform 和 Origin 已全部复用
Main mutation、undo/redo、原子保存和保存重开契约。
验收命令:
```bash
node tools/web/run-web-engine-smoke.mjs
bash tools/web/check-dual-engine.sh
npm --prefix web run test:main-roundtrip
npm --prefix web run test:authoring-roundtrip
WEB_TEST_PORT=5193 npm --prefix web run test:e2e -- --grep "atomic|journal|concurrent saves"
```

17
docs/status/N-007.md Normal file
View File

@@ -0,0 +1,17 @@
# N-007 建模和 UI 对标
状态:`completed_current_scope`
Cube/Plane primitive、独立 Mesh duplicate、delete、完整 TRS、Edit Mode 顶点平移,以及
Merge/Dissolve/Extrude/Inset/Bevel/Loop Cut 均直接 mutation Blender Main。结构变化统一通过
Main 内存写出再生成 SceneIR失败会恢复前一历史项shape key、linked data 和非法选择会
结构化阻断。
UI 已接入视口 raycast、Shift/Ctrl 多选、隐藏对象过滤、删除清理、X/Y/Z 可拖拽 gizmo、
Object/Edit Mode、点/边/面选择、Tab/Delete/Shift+D/Ctrl+Z/Ctrl+Y/G/R/S 快捷键。对象层级
已覆盖 Parent/Unparent、Collection、新命名、Join/Separate、Apply Transform 和 Origin。
材质/UV 与关键帧编辑复用相同 Main/history/save 契约。
验收:`node tools/web/run-web-engine-smoke.mjs`,以及 E2E
`runs Main-authoritative object and edit-mode modeling workflows`,以及
`npm --prefix web run test:authoring-roundtrip`

13
docs/status/N-008.md Normal file
View File

@@ -0,0 +1,13 @@
# N-008 发布门
状态:`done`
发布门现覆盖 100k 三角形 0.9 Collapse 与 1M 三角形 native 建网格/校验;本机结果分别为
86.1s/64MiB 和 0.51s/346,554,368 bytes默认门限 120s/180s 与 1.5GiB。Chromium、Firefox、
WebKitSafari engine核心启动、离线资源、SceneIR、非空 WebGL 和 Main 建模全绿OPFS、
IndexedDB、quota 与 Worker commit 故障由 Chromium 专项覆盖。WebKit headless 不提供 Worker
IndexedDB 时明确显示 `Storage: unavailable`,不影响引擎门。
五个恶意/截断 `.blend` 均返回有界错误notice、GPL、GMP/OpenSubdiv、本地依赖和 dist 远程
URL 扫描进入发布命令。验收:`test:release-performance``test:malicious-blends`
`test:cross-browser``test:release-package`

12
docs/status/N-009.md Normal file
View File

@@ -0,0 +1,12 @@
# N-009 快照、操作日志和崩溃重放
状态:`done`
Storage schema 5 加入 `operation_quarantine`schema 6 新增经过哈希验证的
`simulation_manifest`,不改变既有日志/快照语义。命令按 revision/时间/ID 确定排序,非法记录移入
隔离区revision 冲突结构化失败;显式保存后清理已提交日志。每 10 条命令写一份 SHA-256
`.blend` 快照,默认保留 5 份且总量不超过 256MiB保存点也写快照。恢复入口优先打开原子
提交项目,缺失时使用最新快照,然后只重放提交 revision 之后的可重放 Main 命令。
验收E2E `retains bounded snapshots and returns a validated operation replay plan`,以及原子恢复、
并发保存和 quota 专项。

11
docs/status/N-010.md Normal file
View File

@@ -0,0 +1,11 @@
# N-010 可复现离线发布包
状态:`done`
`release:offline` 生成 `release/blender-web-offline.tar.gz`
`release/blender-web-corresponding-source.tar.gz`。二进制包包含同源 app、WASM、GPL、GMP、
OpenSubdiv license、第三方 notice、文件 SHA-256 manifest 和 source offer源码包包含 Blender
5.2、Web app/protocol、锁文件、Web 构建脚本与状态/构建文档。tar 使用固定排序、mtime、owner
`gzip -n`,连续生成两次后必须得到相同的两个 SHA-256顶层 `SHA256SUMS.txt` 覆盖交付物。
验收:`npm --prefix web run release:offline`

20
docs/status/N-011.md Normal file
View File

@@ -0,0 +1,20 @@
# N-011 Sculpt 子集
状态:`in_progress属性 + 有界 Main stroke 子集)`
详细分解、依赖、错误码、停止条件和验收命令见
[`docs/UNDECLARED_CAPABILITIES_EXECUTION_PLAN.md`](../UNDECLARED_CAPABILITIES_EXECUTION_PLAN.md)。
本轮已交付 `SculptStrokeIR`、Mask/Face Set 校验、单用户/linked/Shape Key/拓扑
modifier 能力门、点数和属性预算。SceneIR 及 WBG1 header version 2 可携带并校验
`sculptMask``faceSets``MeshGeometryDelta` 可对两个字段执行 replacement/range patch。
`setSculptMeshAttributes` 已通过 Blender Main 写入、undo 历史、保存重开和 WBG 二进制
回归;非持久的 active Face Set 仍返回 `CAPABILITY_MISSING`
`sculptStroke` 现在开放一个明确受限的 native Main 顶点事务Draw/Inflate/Grab/Smooth、
压力、Mask、对象空间对称、2048 samples/10000 path/250000 vertices 预算,以及 Shape Key、
linked、单用户和启用拓扑 modifier 安全门。它不是 Blender PBVH/BMesh 等价求值,仍不声明
动态拓扑、多分辨率、纹理笔刷、法线/切线 golden、Sculpt UI 或浏览器故障恢复;这些继续
返回结构化阻断并由 N-011-C2/D/E 负责。
验收:`npm --prefix web run test:authoring-roundtrip`

20
docs/status/N-012.md Normal file
View File

@@ -0,0 +1,20 @@
# N-012 Geometry Nodes 与 Simulation
状态:`in_progress资源/Simulation cache 安全层)`
详细分解、GraphIR、字段/实例闭包、deterministic simulation cache、错误码和验收命令见
[`docs/UNDECLARED_CAPABILITIES_EXECUTION_PLAN.md`](../UNDECLARED_CAPABILITIES_EXECUTION_PLAN.md)。
本轮已交付版本化 `GeometryNodeGraphIR`、node/socket/link/domain 校验、支持节点白名单、
循环依赖和自递归 group 检测。Object/Collection/Image Info 资源现在按 SceneIR stable ID、
资源类型、owner cycle 和 linked/missing/corrupt 沙箱校验。合法图仍只通过协议门native
lazy-function evaluator 未启用时返回 `CAPABILITY_MISSING`
`SimulationCacheManifestIR` 已绑定 graph/source blend/input hash、Blender 5.2、完整帧范围、
连续 byte range、总 payload 和逐帧 SHA-256。StorageWorker schema 6 使用 OPFS 内容寻址资产
`simulation_manifest` 索引,写入前核对当前 committed blendWorker 重启后重新逐帧验证;
缺失、版本不符和损坏均结构化阻断。
仍未声明:浏览器生成 bake、WASM Simulation Zone 求值、cache frame seek 接入 GN modifier、
任意 lazy-function/field/instance graph。验收`npm --prefix web run test:simulation-cache`
`npm --prefix web run test:capability-gates`

21
docs/status/N-013.md Normal file
View File

@@ -0,0 +1,21 @@
# N-013 Shader Node 图
状态:`in_progress七节点 Main + 常量 PBR 映射子集)`
详细分解、ShaderIR、typed socket 校验、Main 写回、Web 编译、GLB 映射和验收命令见
[`docs/UNDECLARED_CAPABILITIES_EXECUTION_PLAN.md`](../UNDECLARED_CAPABILITIES_EXECUTION_PLAN.md)。
本轮已交付版本化 `ShaderGraphIR`、单 Material Output、typed socket/隐式转换、图循环、
Material/Image stable ID 以及 linked/missing/corrupt image 资源门。首个 native Main 写回
子集开放 RGB、Value、有限 Math、Principled、Image Texture、Normal Map 和 Material
Output整图替换在一个 history transaction 中完成,并通过节点/链接及 RGB/Value
defaultValue 保存重开回归。
Math 已开放 ADD/SUBTRACT/MULTIPLY/DIVIDE/MINIMUM/MAXIMUM 的 Main `custom1` 写回和
operation 保存重开其他运算继续结构化阻断。RGB/Value 到 Principled Base Color、
Roughness、Metallic 的有限常量链接现可确定性映射为 glTF PBR factor缺值、越界、
重复输入和未知链接继续返回 `SHADER_GRAPH_UNMAPPABLE`。Mix、Mapping、Texture
Coordinate、Bump 及任意节点仍返回 `SHADER_NODE_UNSUPPORTED`。node properties 仅开放
Math operation受限 Web 编译、完整色彩管理、完整 GLB 图映射和桌面渲染 golden 仍为 planned。
验收:`npm --prefix web run test:authoring-roundtrip``npm --prefix web run test:capability-gates`
以及 Shader GLB mapping E2E。

20
docs/status/N-014.md Normal file
View File

@@ -0,0 +1,20 @@
# N-014 NLA
状态:`in_progressAction Clip Main/求值 + reverse/repeat 子集)`
详细分解、AnimationIR strip/track、Main 编辑、原生 NLA 求值、UI、GLB 映射和验收命令见
[`docs/UNDECLARED_CAPABILITIES_EXECUTION_PLAN.md`](../UNDECLARED_CAPABILITIES_EXECUTION_PLAN.md)。
本轮已交付 `NlaTrackIR`/`NlaStripIR`、Action 存在性、owner/path、稳定排序、不重叠、
range/scale/repeat/influence 一致性、blend/extrapolation 和 time-warp 阻断。SceneIR reader
可恢复 Track/Action Clip 摘要;`setNLAStack` 以整栈替换事务写 Blender Main并经过
undo 历史、保存重开和 frame 5 native depsgraph 求值。Action Clip `reverse` 已映射到
Blender `NLASTRIP_FLAG_REVERSE`reader 可恢复该标志;反向正例得到预期线性位置约
`[0.555556, 1.111111, 1.666667]`
同一正例已扩为 `repeat=2`frame 5 与第二周期 frame 14 的 native Depsgraph 矩阵一致。
当前只支持单对象 Action Clip、正 scale、Replace/Add/Multiply/Combine 和有限外插。
Transition/Meta/Sound、Animated Time、Drivers、细粒度拖动/resize 命令、NLA UI、
骨骼/约束混合矩阵及 GLB 展开仍为 planned/结构化阻断。
验收:`npm --prefix web run test:authoring-roundtrip``npm --prefix web run test:capability-gates`

113
docs/status/N-015.md Normal file
View File

@@ -0,0 +1,113 @@
# N-015 非 Mesh 几何数据块
状态:`in_progress`本地有界切片已落地VDB renderer/完整交互仍阻断)
更新时间2026-08-12
## 当前声明
当前声明覆盖 Blender 5.2 `.blend` 中有限非 Mesh 数据的稳定识别、Main 权威写回、undo/redo、
保存重开、真实 evaluated mesh 报告和 WNM 二进制分块。Curve cyclic/handle 目前只有有界
属性读写,未声明完整 topology editor、handle/cyclic gizmo、VDB 体渲染、完整 USD loss
fixture 或跨浏览器性能等价。
| 数据族 | Reader | Web 预览 | 当前门 |
| --- | --- | --- | --- |
| legacy Curve | 控制点、spline offset、resolution | WNM 控制折线depsgraph 真 mesh | `READY` |
| legacy Surface | 控制点、spline offset、resolution | WNM 控制折线depsgraph 真 mesh | `READY` |
| Font | body、对齐、extrude/bevel/offset 与摘要 | depsgraph 真 mesh源文本仍保留 | `READY`(无控制笼) |
| Metaball | 元素类型、位置、半径、尺度 | bounded sphere proxydepsgraph implicit mesh | `READY` |
| PointCloud | position/radius/POINT 属性 | WNM 分块 Points | `READY`(空数据为 `summary-only` |
| Curves/Hair | position/radius/curve/POINT 属性 | WNM 分块 Points/curve line | `READY`(缺数据阻断) |
| Volume | 项目内 OpenVDB path/grid metadata | 无 renderer可取消 decoder API | `NON_MESH_RESOURCE_MISSING` 或预算阻断 |
## 已实现任务
1. `NonMeshDataIR` 加入 SceneIR schema 1可选字段保持旧快照兼容。
2. parser 校验类型、状态、有限数、三元控制点、spline offset、Metaball 元素和错误码。
3. native reader 为数据块生成稳定 ID并把对象 `dataId` 指向同一 IDWeb runtime 注册
`ID_MB``ID_PT``ID_VO`,不再把三类对象降为 Empty。
4. 单数据块最多读取 1M 点;越界或不完整数据返回
`NON_MESH_DATA_BUDGET_EXCEEDED`
5. `NonMeshGeometryChunk` WNM schema 1 提供 65,536 点分块、SHA-256、属性 domain/storage、
256 MB 场景预算和 1M 点性能门Worker/主线程/Offscreen 均传输并消费分块。
6. native reader 读取 PointCloud/Curves/Hair 的 position、radius、curve offset 和属性值,
不以摘要计数伪造点Volume 提供项目路径、OpenVDB grid metadata 和可取消 decoder。
7. depsgraph 对 Curve/Surface/Font/Metaball 通过 `BKE_mesh_new_from_object` 输出真实位置、法线、
边/三角索引、UV、材质槽和 source face/edge mapping并在 1M/2M 拓扑预算处结构化阻断。
Web ID registry 已注册 `ID_VF` 并在加载时恢复 Blender 未写入 `.blend``<builtin>` 字体
引用Curve/Surface/Font/Metaball 的几何计数、边界、面积和位置/索引矩均通过 Blender
5.2 desktop golden。
8. Main 已支持 Curve/Surface 控制点、resolution、有界 cyclic/handle 类型与 Bezier 左右 handle
坐标、Font body/geometry/layout 标量属性spacing、line distance、word space、shear、font
size、x/y offset、逐字符 kern/material/style flags 和最多 256 个 textbox、
`.blend` 内已有/packed VFont 的 regular/bold/italic/bold-italic 四样式链接、
Metaball element 写回,以及 bounded Poly Curve create、delete、1D Poly/Bezier/NURBS conversion、
二维 Surface U/V dimensions/order/rational weight 网格替换和 Curve/Surface/Font/Metaball 数据块
重命名;一维多 spline 创建/删除/重排和批量 handle/cyclic 已由单个原子 Main 事务覆盖。
多 handle 选择拖拽/gizmo UI 仍阻断。单用户、undo/redo、save/reopen 由
`check-nonmesh-roundtrip.mjs` 验证。
9. 主线程与 OffscreenCanvas 对 PointCloud/Curve 控制点和 Bezier handle 代理提供 bounded raycast并对 Mesh 的
VERT/EDGE/FACE 使用一致的三角顶点、边索引解析,回传既有 selection callback完整
gizmo、跨对象 selection history 与精确范围 patch 仍未声明;新增 local bounded selection
history 仅用于 revision/stale-hit 门和撤销栈验收。Bezier 左/右 handle 现以全局控制点映射
独立拾取,轴向 gizmo 通过精确 Main 命令写回并通过 undo/redo、save/reopen多选拖拽、跨对象
history 和 range patch 仍未声明。
10. `queryNonMeshCapability(dataId)` 返回 N-015 `READY/BLOCKED` 与机器可读错误码GLB 对未求值
非 Mesh 拒绝导出。Font/Metaball/二维 NURBS Surface 的 depsgraph 三角网格与 Curve 的
evaluated edge line primitive 已通过 GLB 导出和 Blender 5.2 桌面回导几何校验;零三角且零边的结果仍返回
`GLB_NON_MESH_UNMAPPED`,不生成空 primitive。确定性 USDA 1.0 序列化器将三角求值写为
`UsdGeomMesh`、edge-only 求值写为 `UsdGeomBasisCurves`,缺 buffer/空场景结构化阻断;四对象
已通过 USD-enabled Blender 5.2.0 LTS desktop reimport。PointCloud 使用 GLB `POINTS`/USD
`UsdGeomPoints`Curves/Hair 使用 GLB `LINES`/USD `UsdGeomBasisCurves`radius 映射 USD
widthstyped WNM 属性映射 primvarsGLB 属性损失结构化报告7 对象 desktop reimport 已通过。
Volume loss fixture 仍由 VDB 资源/renderer 阻断。
11. Blender 5.2 fixture 已包含真实 PointCloud/Curves 属性Chromium 主线程/Offscreen、真实
depsgraph、WNM 1M 性能测试均接入。
## 后续分解
1. N-015-B3接入真实 OpenVDB decoder 与体素纹理/射线步进;在此之前保持 Volume renderer 阻断。
2. N-015-C1多 handle 选择拖拽与 gizmo UI多 spline 创建/删除/重排、批量 handle/cyclic
editor transaction 和二维 Surface U/V topology/order/rational weight 网格替换已完成。
3. N-015-C2任意外部路径的新字体导入与资源沙箱已有/packed VFont 的四 style link Main
写回、字符级样式、kern/material 与 textbox 已完成并通过 PFB desktop/WASM golden。
4. N-015-D1跨对象 selection history、多 handle 拖拽和 SceneDelta range patch左右 handle
identity raycast、单 handle 轴向 gizmo/Main 写回已完成。
5. N-015-D2evaluated preview 与源控制笼分层显示、WebGPU 等价。
6. N-015-E1补齐 Volume 的完整 loss fixturePointCloud/Curves/Hair、Curve line bake、真实
4x4 NURBS Surface mesh、desktop geometry golden、7 对象 GLB 与 USDA desktop round-trip 已通过。
7. N-015-E2100k/1M WASM/GPU 内存、Worker restart 和 Chromium OPFS quotaChromium
64 KiB 真实 quota、失败后旧 revision 保持、Worker restart 恢复已通过。后续浏览器验收
仅以 Chromium 为基线,不配置 Firefox/WebKit。
## 停止条件
- `summary-only` 不得进入 `READY`
- legacy 控制折线不得标记为 evaluated/tessellated curve。
- Metaball sphere proxy 不得用于导出或保存为求值曲面。
- Volume decoder/renderer、Hair 属性 buffer 缺失时不得生成占位几何。
- Metaball sphere proxy 不得进入 GLBGLB 只能消费 depsgraph evaluated mesh。
- 任意新增 Main authoring 命令在 save/reopen 与 undo/redo 完成前不得开放 UI。
## 验收命令
```bash
source tools/web/emscripten-env.sh
cmake --build build_web_blender6 --target web_engine -- -j8
bash tools/web/install-web-engine-assets.sh
build_blender_5.2.0/bin/blender -b --factory-startup \
--python tools/web/generate-nonmesh-fixture.py -- tests/files/web/nonmesh_scene.blend
npm --prefix web run typecheck
npm --prefix web run lint
npm --prefix web run test:nonmesh-roundtrip
npm --prefix web run test:nonmesh-desktop-golden
npm --prefix web run test:nonmesh-glb-blender-roundtrip
npm --prefix web run test:nonmesh-usd-serialization
BLENDER_BIN=/path/to/usd-enabled/blender \
npm --prefix web run test:nonmesh-usd-blender-roundtrip
npm --prefix web run test:nonmesh-binary
npm --prefix web run test:vdb
WEB_TEST_PORT=5349 npm --prefix web run test:e2e -- -g "real OPFS quota|reports quota exhaustion"
WEB_TEST_PORT=5201 npm --prefix web run test:e2e -- -g "N-015|non-mesh"
```

39
docs/status/N-016.md Normal file
View File

@@ -0,0 +1,39 @@
# N-016 Grease Pencil
状态:`BLOCKED`(协议、有限 reader 和有限 Main transaction 已落地;完整 2D/3D 编辑器、onion skin、modifier 语义和桌面 golden 仍阻断)
## 已验证切片
1. N-016-ASceneIR 中提供 Layer/Frame/Drawing/Stroke/Point/Attribute schema校验
finite position、radius、opacity、vertex color、cyclic、material index并以
1024 layer、100k frame、1M stroke、1M point、65,536 attribute 预算拒绝越界输入。
超预算只允许 `blocked + GREASE_PENCIL_BUDGET_EXCEEDED` 摘要。
2. N-016-ABlender 5.2 Main reader 输出真实 Grease Pencil layer/frame/drawing/stroke
数据,稳定映射 `grease-pencil:*``grease-pencil-layer:*` ID对象 dataId 不再降级
为 Empty。
3. N-016-BMain 支持有界 create/remove/reorder layer、insert/remove frame以及整帧
`setGreasePencilStrokes` transaction空 stroke 数组用于擦除)。命令要求单用户数据块,
使用既有 authoritative Main、undo/redo 和 save/reopen 流程。
4. N-016-C部分radius、opacity、vertex color、cyclic stroke 和 material index
已读写onion skin 仅输出 layer 状态,不声明时间邻帧渲染。
5. N-016-D部分共享 Three.js 适配器按当前 frame 选择每个可见 layer 最近的有效
drawing渲染真实 3D stroke/闭环Chromium 主线程与 OffscreenCanvas 均通过非空像素门。
## 仍然阻断
- N-016-Cmaterial datablock 事务、onion skin preview 和完整 Grease Pencil modifier reader。
- N-016-D2D editor、stroke/point selection、timeline/dope integration、gizmo 和 worker restart
3D current-frame stroke viewport 已完成。
- N-016-Edesktop drawing hash、Chromium 像素 golden、GLB/USD loss report 和 OPFS。
## 验收
```bash
npm --prefix web run typecheck
npm --prefix web run lint
npm --prefix web run test:grease-pencil
WEB_TEST_PORT=5202 npm --prefix web run test:e2e -- --grep "N-016 Grease Pencil"
```
`test:grease-pencil` 使用 `modifier_grease_pencil_scene.blend`,覆盖 reader、layer/frame/stroke
Main transaction、undo/redo 和保存重开。它不替代桌面 drawing hash。

33
docs/status/N-017.md Normal file
View File

@@ -0,0 +1,33 @@
# N-017 Paint 与权重
状态:`BLOCKED`stroke/patch schema、Main 顶点色/权重 transaction 已落地真实命中、texture paint 和 GPU/image 生命周期未实现)
## 已验证切片
1. N-017-A部分`PaintStrokeIR` 覆盖 Vertex Color、Weight、Texture Paint携带
object/revision、radius/strength、pressure、PBVH face/barycentric hit 和 UV hit。
2. N-017-A部分`WeightPatchIR` 覆盖有界 vertex index/value、normalize 和 mirror 意图。
3. 预算限制为单 stroke 100k samples/64 MiB 估算、单 weight patch 1M entries、单 texture
tile 256 MiB非法 barycentric、pressure、color、weight 和非有限数会结构化拒绝。
4. N-017-B部分`setVertexColors` 在单用户 Mesh 上创建/转换 `POINT``CORNER`
`FLOAT_COLOR` 属性,并更新 active color`setVertexWeights` 在真实 Object/Mesh 上创建顶点组、
写入/移除 `MDeformWeight`,支持逐顶点 normalize。
5. 顶点色和权重都已通过 Main undo/redo 与 `.blend` save/reopen`mirror:true` 在没有已验证
对称拓扑映射时返回 `CAPABILITY_MISSING`,不会伪造镜像结果。
## 仍然阻断
- N-017-A真实 PBVH/UV raycast 与 brush falloff 对桌面语义的对应。
- N-017-Blimit/clean、已验证拓扑映射上的 mirror、桌面 brush/falloff 对照。
- N-017-Cpacked/UDIM tile transaction、色彩空间、dirty tile 和原子保存。
- N-017-D/Earmature golden、seam bleed、mask/selection、GPU dispose、quota、坏图和 UI。
## 验收
```bash
WEB_TEST_PORT=5203 npm --prefix web run test:e2e -- --grep "N-017 paint"
npm --prefix web run test:paint-roundtrip
```
`test:paint-roundtrip` 覆盖 CORNER color、vertex group patch、normalize、mirror 错误门、
undo/redo 与 save/reopen。

30
docs/status/N-018.md Normal file
View File

@@ -0,0 +1,30 @@
# N-018 Physics 与 Simulation
状态:`BLOCKED`family capability、settings/dependency/cache manifest 与错误帧门已落地;
cache playback、WASM solver 和 bake job 未实现)
## 已验证切片
1. N-018-ARigid Body、Soft Body、Cloth、Fluid、Dynamic Paint、Particle、Hair
使用固定枚举能力清单metadata/cache manifest 为 `LOCAL_BOUNDED`cache playback、
local solver、server job 均保持 `BLOCKED`
2. N-018-B部分`PhysicsSimulationManifestIR` 绑定 owner Object、settings SHA-256、
有界设置摘要、依赖 stable ID 和 desktop bake cache系统依赖环、重复依赖和超预算拒绝。
3. N-018-Ccache 绑定 source blend/settings/input/cache SHA-256、帧范围和实际已缓存帧
`COMPLETE` 必须逐帧连续frame seek 只返回精确命中帧,禁止复用相邻错误帧。
4. 单 manifest 上限 4096 systems、每系统 1024 dependencies、256 settings/64 KiB、
100k frames。
## 仍然阻断
- N-018-A/B从真实 Main 提取完整 family settings、collection/effector/collision 依赖。
- N-018-Cdesktop bake payload 的 family decoder、depsgraph frame playback 和 100 帧 golden。
- N-018-D逐 family WASM solver 初始化、线程、内存和确定性验证。
- N-018-Ebake start/cancel/commit、服务端 job、故障恢复、进度与 UI。
## 验收
```bash
WEB_TEST_PORT=5204 npm --prefix web run test:e2e -- --grep "N-018 physics"
npm --prefix web run test:simulation-cache
```

33
docs/status/N-019.md Normal file
View File

@@ -0,0 +1,33 @@
# N-019 灯光与渲染
状态:`BLOCKED`Camera/Light/World/Scene metadata、Light/World 有界 Main 写回与
Three exposure/shadow 映射已落地;完整 Camera/颜色管理 writer 和渲染等价未实现)
## 已验证切片
1. N-019-A部分Camera 区分 Perspective/Orthographic/Panoramic/Custom读取 sensor、
shift、clip、panorama 和 DOFLight 读取 energy/exposure/temperature/shadow/shapeWorld
读取环境、exposure 和 mistScene 读取 render engine、display/view/look/exposure/gamma/白平衡。
2. N-019-A部分Light 与 World 使用严格属性白名单写入真实 Main覆盖 color、energy、
exposure、temperature、shadow、World color/mist已通过 undo/redo 与 save/reopen。
3. N-019-B部分主线程和 OffscreenCanvas renderer 都优先使用 Scene color-management
exposureWorld exposure 仅作旧数据回退Light exposure 转换为强度,`castsShadow:false`
不再被 Three 强制打开。
4. 白平衡读取有完整性门Main Light/World 重写若导致 Blender 5.2 tint 序列化为异常近零值,
不暴露垃圾数值而返回 `whiteBalanceStatus: BLOCKED`
## 仍然阻断
- N-019-ACamera 与 Scene color-management writer直接 DNA 写入会破坏 white balance
必须改接 Blender/RNA 颜色管理 API 后才能开放。
- N-019-B/CAgX/Standard/Raw 的视觉等价、temperature 颜色、Area spread、Mist、DOF、
transparent sorting、probe 和高级 shadow 参数。
- N-019-DCycles/Freestyle/denoise 服务端 job 协议与结果 hash。
- N-019-Edesktop/Chromium 像素 golden、设备丢失和 1M triangles。
## 验收
```bash
npm --prefix web run test:lighting-roundtrip
WEB_TEST_PORT=5319 npm --prefix web run test:e2e -- --grep "N-019 Scene exposure"
```

29
docs/status/N-020.md Normal file
View File

@@ -0,0 +1,29 @@
# N-020 Compositor
状态:`BLOCKED`GraphIR 与有界 CPU executor 已落地;真实 Main graph、WebGPU、HDR golden
和服务端执行未实现)
## 已验证切片
1. N-020-A版本化 `CompositorGraphIR` 覆盖 Image、Render Layer、Constant Color、
Transform、Invert、Exposure、Alpha Over、Blur、Mix、Viewer、Composite 与 Unsupported
节点/资源 ID、重复 input socket、缺失引用和 graph cycle 会拒绝。
2. N-020-BCPU 子集):线性 sRGB Float32 RGBA executor 按拓扑序执行 Transform、颜色、
Alpha Over、Blur、Mix并返回 Composite 与多 Viewer 输出。
3. N-020-C部分最大 4096 nodes、16384 links、4096 resources、8192 dimension、
16M pixels、256 MiB image 和 100M blur operations分配前检查支持 worker cancellation。
4. N-020-DUnsupported node 保留 Blender type metadata并返回
`COMPOSITOR_NODE_UNSUPPORTED`,不会删节点后继续运行。
## 仍然阻断
- N-020-A从真实 Main 读取/写回 compositor node tree、socket defaults、Render Layer pass。
- N-020-B/CWebGPU executor、tile/frame cache、增量 invalidation 和 GPU dispose。
- N-020-D服务端 Blender job、source hash 和结果提交。
- N-020-Edesktop HDR/alpha/color-space golden、OOM/fault/device-loss。
## 验收
```bash
WEB_TEST_PORT=5320 npm --prefix web run test:e2e -- --grep "N-020 CPU compositor"
```

31
docs/status/N-021.md Normal file
View File

@@ -0,0 +1,31 @@
# N-021 Sequencer 与音频
状态:`BLOCKED`strip schema、确定性时间编辑和 codec 能力门已落地;真实 Main
写回、媒体解码/渲染、音频波形和服务端编码仍未实现)
## 已验证切片
1. N-021-A部分版本化 `SequencerTimelineIR` 覆盖 Scene、Movie、Image、Sound、
Effect、Meta strip、channel、帧范围、source range、speed、proxy 和依赖strip、
image、dependency 数量以及帧范围都有预算,资源路径经过项目沙箱规范化。
2. N-021-B部分`MOVE``TRIM``SPLIT` 以 revision 乐观并发控制作用于内存
timeline编辑后重新解析依赖和预算锁定 strip 与 stale revision 会拒绝。
3. N-021-B/C`sequencerSourceFrame` 对 speed、trim、split 使用确定性的 source-frame
映射,避免左右片段复用越界源帧。
4. N-021-C/D运行时只报告 WebCodecs/HTMLMedia 需要精确 probecodec 必须
出现在已验证 MIME 集合中才可放行,本地编码固定为 `BLOCKED`
## 仍然阻断
- N-021-A/B从真实 Main 读取并写回 scene sequence editor 的 strip、transition、
modifier、undo/redo、save/reopen。
- N-021-CWebCodecs 精确 seek/decode、音频 waveform、proxy 生成、A/V sync、丢帧和
损坏媒体处理。
- N-021-D浏览器不支持的 codec、混音与最终编码的服务端 Blender job。
- N-021-E桌面/Chromium golden、OPFS 和大规模媒体性能门。
## 验收
```bash
WEB_TEST_PORT=5321 npm --prefix web run test:e2e -- --grep "N-021 sequencer"
```

28
docs/status/N-022.md Normal file
View File

@@ -0,0 +1,28 @@
# N-022 Tracking 与 Mask
状态:`BLOCKED`(资源 schema、marker/mask 有界事务和 solve 能力门已落地;真实 Main、
跟踪/相机求解、编辑器 overlay 与桌面 golden 未实现)
## 已验证切片
1. N-022-A版本化 project schema 覆盖 MovieClip、Track/Marker、Plane Track、Camera
Solve metadata、Mask Layer/Spline/Point 及 compositor/scene binding。
2. N-022-A/Bclip 路径限制在项目内source 与 solve 绑定 SHA-256marker frame、
plane track 引用、mask point/handle、资源 binding、重复 ID 与总量预算都严格校验。
3. N-022-B部分marker upsert/delete、track selection、mask point 与 spline cyclic
修改使用 revision 事务,锁定项和 stale revision 会拒绝,提交后再次完整解析。
4. N-022-Cmarker/mask schema edit 可用browser tracking 只有显式 probe 成功后
放行camera solve 保持 `BLOCKED` 并要求受验证的服务端 Blender。
## 仍然阻断
- N-022-A/B真实 Blender Main MovieClip/Mask 读取、写回、undo/redo、save/reopen。
- N-022-C浏览器 tracking 实现、完整 camera/plane solve 与服务端 job/hash 提交。
- N-022-DClip/Mask editor、overlay、selection/raycast 和 compositor/scene 实际绑定。
- N-022-Edesktop solve/error golden、媒体故障和 Chromium 测试。
## 验收
```bash
WEB_TEST_PORT=5322 npm --prefix web run test:e2e -- --grep "N-022 tracking"
```

27
docs/status/N-023.md Normal file
View File

@@ -0,0 +1,27 @@
# N-023 Asset、Library 与 IO
状态:`BLOCKED`asset catalog、来源/许可证元数据、库依赖与 IO 安全门已落地;真实
Append/Link/Override Main、非 GLB 本地导入和跨桌面重导入未实现)
## 已验证切片
1. N-023-A版本化 manifest 覆盖 catalog、asset kind/tag、preview、author、license、
source path/SHA-256、library source/dependency/read-only metadata重复 ID、缺 catalog、
缺许可证和哈希不匹配会拒绝。
2. N-023-A/B部分catalog 与 library dependency 图做确定性拓扑检查并返回 load order
content-addressed index 报告 `LOCAL_BOUNDED`OPFS 只在运行时 API 存在时报告 `PROBE_REQUIRED`
3. N-023-C现有 GLB 导出与 USD semantic analysis 可放行GLTF/OBJ/PLY/STL、
USD/Alembic 实际导入保持 `IO_FORMAT_UNSUPPORTED`,库 mutation 需要真实 Main。
4. N-023-E项目路径、外部 URI、archive entry 数量/单项/总量、压缩展开比率均有边界。
## 仍然阻断
- N-023-BAppend/Link/Library Override、reload/relocate 和真实 Main transaction。
- N-023-C/DGLTF/OBJ/PLY/STL、USD/Alembic import/export/save/reopen/desktop reimport。
- N-023-Ezip fuzz、OPFS quota/recovery、license/source offer 发布审计和大文件流式性能。
## 验收
```bash
WEB_TEST_PORT=5323 npm --prefix web run test:e2e -- --grep "N-023 asset"
```

27
docs/status/N-024.md Normal file
View File

@@ -0,0 +1,27 @@
# N-024 Editors 与工作流
状态:`BLOCKED`(统一 context、只读 editor manifest、selection sync、keymap 和布局预算
已落地;完整 Blender editor writer、gizmo/触控和跨设备 golden 未实现)
## 已验证切片
1. N-024-A版本化 workflow manifest 统一 workspace、area、region、active editor、mode、
view layer、pinned data 与 revision13 类 editor 可被明确识别。
2. N-024-B部分只读 editor context、区域可见性、workspace/area 切换使用同一份
workflow staterect 边界与不可重叠布局经过校验。
3. N-024-C部分selection sync 以 revision 事务更新 active object 与 selected IDs
active object 必须属于 selection过期 revision 会拒绝。
4. N-024-Dkeymap 绑定和预算可验证writer、gizmo 与 touch drag 保持能力阻断。
## 仍然阻断
- N-024-B/CProperties、UV/Image、Node、Graph、Dope Sheet、NLA、Spreadsheet 等真实
Blender 数据读取/写回、selection history、operator search 和 context menu。
- N-024-D/EBlender-compatible keymap 执行、gizmo/drag preview/commit、桌面/mobile/笔
触控布局、无重叠截图和 accessibility golden。
## 验收
```bash
WEB_TEST_PORT=5324 npm --prefix web run test:e2e -- --grep "N-024 editor"
```

28
docs/status/N-025.md Normal file
View File

@@ -0,0 +1,28 @@
# N-025 Scripting 与平台
状态:`BLOCKED`(默认拒绝策略、签名 manifest、权限/资源预算、平台报告和服务端 hash
门已落地;本地隔离执行、真实 server job 与发布审计未实现)
## 已验证切片
1. N-025-A脚本默认拒绝网络、autorun、driver expression 和 add-on 安装entry/dependency
path 必须在项目内,未知权限和重复 ID 会拒绝。
2. N-025-B部分脚本 manifest 要求 SHA-256、固定长度签名、publisher/keyId、权限
allowlist、CPU/内存/墙钟预算,并检查脚本依赖图。
3. N-025-C合法脚本仍要求隔离 sandboxserver job 需要 script/source/input
hash 一致,但当前 endpoint 未配置,返回 `SERVER_JOB_UNAVAILABLE`
4. N-025-DWorker/WebGPU/OffscreenCanvas/OPFS 与 native/CUDA/Metal/HIP/OptiX capability
报告只反映 API 存在或显式 `BLOCKED`
## 仍然阻断
- N-025-B/C签名验证密钥管理、无网络 CPython/native sandbox、server Blender job 和
output hash 提交。
- N-025-D/E真实 GPU/native window/file watcher 适配、恶意脚本/依赖混淆/逃逸/重放、
审计日志和发布门。
## 验收
```bash
WEB_TEST_PORT=5325 npm --prefix web run test:e2e -- --grep "N-025 script"
```

37
docs/status/N-026.md Normal file
View File

@@ -0,0 +1,37 @@
# N-026 全域发布门
状态:`BLOCKED`machine-readable parity manifest、依赖/状态检查、证据聚合和确定性
序列化已落地跨浏览器、离线包、OPFS、性能/故障、SBOM 与发布审计证据未齐)
## 已验证切片
1. N-026-A版本化 `ReleaseManifestIR` 为每个 family 记录 `LOCAL_EXACT`
`LOCAL_BOUNDED``SERVER``BLOCKED`、roadmap 状态、完成/阻断切片、验收命令和
依赖;缺失 family、非法非阻断状态或依赖环会拒绝。
2. N-026-B/CChromium 主线程/OffscreenCanvas、offline/Worker restart/OPFS recovery、
1M/10M geometry、4K/8K texture、长媒体、simulation cache、OOM/device loss/网络
中断/损坏 blend/zip bomb 等证据字段必须逐项为 true 才能放行。
3. N-026-D/Elicense、SBOM、source offer、deterministic package 为发布必需证据;
manifest family 排序可确定性序列化,当前缺证据聚合为 `BLOCKED`,没有虚报发布通过。
4. 当前 Chromium smoke 已通过引擎启动/Main 编辑与内容寻址资产恢复,并通过真实 64 KiB
OPFS quota 下的失败保持旧 revision、Worker 重启恢复门。这只计为 partial evidence不等于
完整 suite 或其余 family 的桌面 golden。发布门 schema 2 仅接受 Chromium 浏览器证据。
5. N-015 Curve/Surface/Font/Metaball 已通过 Blender 5.2 desktop geometry golden这四类及
PointCloud/Curves/Hair 共 7 对象已通过 GLB/USDA desktop round-trip。Volume/VDB 仍无 renderer
与 loss fixture这些证据也不替代其余 family 的 desktop golden。
6. 当前发布包已通过本地 third-party notices、Blender/Three license 文件、无远程运行时依赖、
非空 SHA-256 manifest、离线二进制/源码包确定性复建和 100k/1M decimate + 5 类损坏 blend
拒绝门;这些是 partial evidence不等于完整 SBOM/source offer、10M/长媒体或 OPFS quota。
## 仍然阻断
- 完整跨浏览器矩阵、跨 family 桌面 golden、离线/OPFS quota、1M/10M/长媒体基准和 fault fuzz 尚未有
真实证据;上游 N-015 至 N-025 的阻断能力会传递到发布门。
- 完整 release package 审计、许可证/source offer/SBOM、10M/长媒体性能和 OPFS quota 发布流程仍未完成;
当前离线包确定性检查只覆盖已有二进制/源码归档门。
## 验收
```bash
WEB_TEST_PORT=5326 npm --prefix web run test:e2e -- --grep "N-026 release"
```

17
docs/status/W-000.md Normal file
View File

@@ -0,0 +1,17 @@
# W-000 Status
status: done
task: 固定源码和版本
## Evidence
- Blender source: `blender-5.2.0/`
- Native binary: `build_blender_5.2.0/bin/blender`
- Blender report: `Blender 5.2.0 LTS`
- Toolchain: CMake 3.31.6, Ninja 1.12.1, Emscripten 3.1.69, Node 20.19.2, npm 9.2.0
- Reference Git commit: `fbe6228777e7d9afefcd61a413844e790ae75db7`
## Risk
The reference `blender/` checkout was dirty before this work. It is not modified
by the Web implementation.

15
docs/status/W-001.md Normal file
View File

@@ -0,0 +1,15 @@
# W-001 Status
status: done
task: 建立依赖和许可证清单
## Evidence
- Matrix: `docs/web/dependency-matrix.md`
- Notices: `docs/web/third-party-notices.json`
- No CAD kernel or Bitbybit/OCCT dependency is included.
- Local runtime audit: `cd web && npm run check:local-deps` -> `local-dependencies-ok`.
- Browser assets are vendored under `web/app/src/vendor/` and
`web/app/public/vendor/`; no CDN/remote ESM URL is present in source or `dist/`.
- Offline bootstrap check: `npm ci --offline --ignore-scripts` passed with the
existing local npm cache.

10
docs/status/W-002.md Normal file
View File

@@ -0,0 +1,10 @@
# W-002 Status
status: done
task: 建立浏览器能力矩阵
## Evidence
- Capability implementation: `web/app/src/platform/capabilities.ts`
- The probe is side-effect free and returns explicit false values when a browser
API is unavailable. Browser screenshot validation is scheduled by W-015.

11
docs/status/W-003.md Normal file
View File

@@ -0,0 +1,11 @@
# W-003 Status
status: done
task: 建立 golden 场景目录
## Evidence
- Fixtures: `tests/files/web/empty.blend`, `tests/files/web/basic_scene.blend`
- Manifest: `tests/files/web/manifest.json`
- Golden manifest: `tests/golden/W-003/manifest.json`
- Blender validation: empty scene has 0 objects; basic scene has 3 named objects.

10
docs/status/W-004.md Normal file
View File

@@ -0,0 +1,10 @@
# W-004 Status
status: done
task: 定义错误、进度和 revision 规范
## Evidence
- Error schema: `web/protocol/error.ts`
- Progress schema: `web/protocol/progress.ts`
- Protocol version: `web/protocol/schema-version` (`1`)

10
docs/status/W-005.md Normal file
View File

@@ -0,0 +1,10 @@
# W-005 Status
status: done
task: 规定数据坐标和颜色语义
## Evidence
- Contract: `docs/web/coordinate-color-contract.md`
- Golden inputs: `tests/files/web/empty.blend`, `tests/files/web/basic_scene.blend`
- Full adapter screenshot comparison is scheduled by W-051.

11
docs/status/W-006.md Normal file
View File

@@ -0,0 +1,11 @@
# W-006 Status
status: done
task: 建立任务状态和日志模板
## Evidence
- Status files are stored in `docs/status/`.
- Golden manifests are stored under `tests/golden/<task-id>/`.
- Baseline checker: `tools/web/check-baseline.sh`.
- Task-card and validation rules: execution plan sections 4.1~4.3.

21
docs/status/W-010.md Normal file
View File

@@ -0,0 +1,21 @@
# W-010 Status
status: done
task: 创建 React 工程
## Evidence
- Package: `web/package.json`
- Lockfile: `web/package-lock.json`
- Build: `npm run build` passed with Vite 8.2.0.
- Three.js: `0.185.1` vendored under `web/app/src/vendor/three/` (latest stable
version selected from npm registry, then copied into the project).
- Screenshot: `tests/golden/W-010/desktop-1440x900.png`
## Verification
```text
npm run typecheck # passed
npm run lint # passed
npm run build # passed
```

15
docs/status/W-011.md Normal file
View File

@@ -0,0 +1,15 @@
# W-011 Status
status: done
task: 建立应用布局和状态层
## Evidence
- App shell: `web/app/src/app/App.tsx`
- UI styles: `web/app/src/app/app-shell.css`
- Entry point: `web/app/src/main.tsx`
- Screenshot: `tests/golden/W-011/layout-1440x900.png`
The first shell includes Blender-style Topbar, Workspace tabs, Viewport,
Outliner, Properties, Timeline and StatusBar. Workspace selection and timeline
frame state are local UI state; no geometry arrays are stored in React state.

21
docs/status/W-012.md Normal file
View File

@@ -0,0 +1,21 @@
# W-012 Status
status: done
task: 建立 EngineClient 和 Worker 通信
## Evidence
- Client: `web/app/src/engine-client/EngineClient.ts`
- Worker: `web/app/src/workers/engine.worker.ts`
- Protocol: `web/protocol/engine.ts`, `web/protocol/scene-ir.ts`
- The browser status changes to `Mock Engine: r0` after init and snapshot.
- Screenshot: `tests/golden/W-012/mock-worker-1440x900.png`
## Verification
```text
npm run typecheck # passed
npm run lint # passed
npm run build # passed
Chrome headless --virtual-time-budget=4000 # Mock Engine: r0
```

26
docs/status/W-013.md Normal file
View File

@@ -0,0 +1,26 @@
# W-013 Status
status: done
task: 建立 Three.js 视口烟测
## Evidence
- Adapter: `web/app/src/three-adapter/viewport.ts`
- Uses vendored Three.js `0.185.1` from `web/app/src/vendor/three/`,
`WebGLRenderer`, `Scene`, `PerspectiveCamera`,
`OrbitControls`, `GridHelper`, lights, resize observer and disposal.
- No runtime CDN request is required; Playwright asserts all requests stay on
the local dev origin.
- Screenshot: `tests/golden/W-013/three-viewport-1440x900.png`
## Verification
```text
npm run typecheck # passed
npm run lint # passed
npm run build # passed
Chrome headless --use-gl=swiftshader --virtual-time-budget=60000 # rendered cube/grid
```
The renderer failure path is contained in the viewport component, so browsers
without a WebGL context keep the editor shell available.

24
docs/status/W-014.md Normal file
View File

@@ -0,0 +1,24 @@
# W-014 Status
status: done
task: 建立 StorageWorker 烟测(原生 IndexedDB
## Evidence
- Client: `web/app/src/storage/StorageClient.ts`
- Worker: `web/app/src/workers/storage.worker.ts`
- Backend: browser-native IndexedDB in a dedicated Worker
- Screenshot: `tests/golden/W-014/storage-viewport-1440x900.png`
The worker writes and reads a smoke record through IndexedDB and reports whether
the Worker can also access OPFS. SQLite WASM is intentionally deferred because
it is not required for the current project metadata and file workflow.
## Verification
```text
npm run typecheck # passed
npm run lint # passed
npm run build # passed
npm run test:e2e # Storage: IndexedDB (or IndexedDB + OPFS)
```

20
docs/status/W-015.md Normal file
View File

@@ -0,0 +1,20 @@
# W-015 Status
status: done
task: 建立 Playwright 冒烟测试
## Evidence
- Config: `web/playwright.config.ts`
- Tests: `web/tests/e2e/smoke.spec.ts`
- Browser: system Google Chrome with SwiftShader flags
The smoke suite covers editor startup, mock EngineWorker initialization,
IndexedDB/OPFS capability status, Canvas presence, orbit input and Outliner
scene content.
## Verification
```text
npm run test:e2e # 2 passed
```

23
docs/status/W-016.md Normal file
View File

@@ -0,0 +1,23 @@
# W-016 Status
status: done
task: 建立资源 manifest 和 WASM 校验
## Evidence
- Manifest: `web/app/public/engine-manifest.json`
- Protocol and loader: `web/protocol/manifest.ts`
- App status reports `Manifest: verified r1`; the optional local `web_engine.wasm`
resource is available for the real ABI worker path.
Version mismatch, malformed manifests, unavailable resources and digest
mismatches reject before a required resource is accepted by the loader.
## Verification
```text
npm run typecheck # passed
npm run lint # passed
npm run build # passed
npm run test:e2e # passed with manifest verification
```

18
docs/status/W-017.md Normal file
View File

@@ -0,0 +1,18 @@
# W-017 Status
status: done
task: 完成 Web 壳里程碑
React + Three.js + EngineWorker + StorageWorker + versioned manifest now run
together with the local minimal WebEngine ABI. The mock SceneIR remains the
scene source in this milestone; Blender data libraries and `.blend` loading are
deliberately deferred to W-030 and later.
## Verification
```text
npm run typecheck # passed
npm run lint # passed
npm run build # passed
npm run test:e2e # 4 passed
```

21
docs/status/W-018.md Normal file
View File

@@ -0,0 +1,21 @@
# W-018 Status
status: done
task: 定义 Blender 风格 UI 协议和上下文
## Evidence
- Protocol: `web/protocol/ui-schema.ts`
- Golden: `tests/golden/W-018/ui-schema.json`
- State includes versioned Workspace/Area/Region layout, active Editor,
Blender mode, active object, selection, view layer, pinned data and revision.
- `reduceUICommand()` keeps UI revisions separate from EngineWorker revisions.
## Verification
```text
npm run typecheck # passed
npm run lint # passed
npm run build # passed
npm run test:e2e # 3 passed
```

23
docs/status/W-019.md Normal file
View File

@@ -0,0 +1,23 @@
# W-019 Status
status: done
task: 实现 Blender 风格默认工作区和交互
## Evidence
- Shell: `web/app/src/app/App.tsx`
- Styles: `web/app/src/app/app-shell.css`
- Default areas: 3D Viewport, Outliner, Properties and Timeline.
- Interactions: Layout/Modeling/Animation tabs, Object/Edit mode context and
F3 Operator Search are wired through the UI command reducer.
- Golden: `tests/golden/W-019/ui-layout-1440x900.png`
The current milestone implements the default shell and context routing. Area
split/join and full OperatorRegistry commands remain later work, as specified
by W-069 and the subsequent modeling packages.
## Verification
```text
npm run test:e2e # 3 passed
```

21
docs/status/W-020.md Normal file
View File

@@ -0,0 +1,21 @@
# W-020 Status
status: done
task: 建立 Emscripten 工具链
## Evidence
- Environment: `tools/web/emscripten-env.sh`
- Smoke runner: `tools/web/check-emscripten.sh`
- Source: `web/engine/emscripten_smoke.c`
- Golden: `tests/golden/W-020/toolchain.json`
The fixed toolchain is Emscripten 3.1.69. The smoke runner builds both a
single-thread and pthread WASM module with ES module, Worker environment,
memory growth and filesystem flags.
## Verification
```text
tools/web/check-emscripten.sh # emscripten-toolchain-ok
```

22
docs/status/W-021.md Normal file
View File

@@ -0,0 +1,22 @@
# W-021 Status
status: done
task: 添加 Web CMake 平台
## Evidence
- Platform policy: `blender-5.2.0/build_files/cmake/platform/web.cmake`
- Standalone target: `web/engine/CMakeLists.txt`
- Configure/build runner: `tools/web/configure-web-engine.sh`
- Golden: `tests/golden/W-021/cmake-cache.json`
The Web platform fragment enforces Emscripten, disables desktop GHOST,
Python, Cycles and optional desktop codecs, and sets ES module/Worker output
flags. It is currently consumed by the isolated smoke target; the full Blender
target integration remains the next C-stage task.
## Verification
```text
tools/web/configure-web-engine.sh # web-cmake-ok
```

23
docs/status/W-022.md Normal file
View File

@@ -0,0 +1,23 @@
# W-022 Status
status: done
task: 建立最小 web_engine target
## Evidence
- C ABI header: `blender-5.2.0/source/blender/web_engine/web_engine_api.h`
- C ABI implementation: `blender-5.2.0/source/blender/web_engine/web_engine_api.cpp`
- CMake target: `web/engine/CMakeLists.txt`
- Golden: `tests/golden/W-022/web-engine-api.json`
The standalone target remains an ABI-only smoke target and links a test reader
stub. The canonical Blender data target is `blender-5.2.0` with
`WITH_WEB=ON` (`build_web_blender6/bin/web_engine.*`); it links BLO/DNA and the
vendored zlib/zstd readers behind the same handle boundary.
## Verification
```text
tools/web/configure-web-engine.sh # standalone ABI gate
node tools/web/run-web-engine-smoke.mjs # web-engine-blend-ok
```

20
docs/status/W-023.md Normal file
View File

@@ -0,0 +1,20 @@
# W-023 Status
status: done
task: 处理 Emscripten 运行参数
## Evidence
- Platform flags: `blender-5.2.0/build_files/cmake/platform/web.cmake`
- Variant runner: `tools/web/build-web-engine-variants.sh`
- Golden: `tests/golden/W-023/runtime-profiles.json`
Single-thread and pthread builds use `MODULARIZE`, `EXPORT_ES6`,
`ENVIRONMENT=web,worker`, memory growth and filesystem flags. The pthread
artifact is explicitly marked as requiring COOP/COEP at runtime.
## Verification
```text
tools/web/build-web-engine-variants.sh # passed
```

21
docs/status/W-024.md Normal file
View File

@@ -0,0 +1,21 @@
# W-024 Status
status: done
task: 固定内存和分配策略
## Evidence
- Memory ABI: `web_engine_get_memory_stats()` and scalar accessors
- Golden: `tests/golden/W-024/memory-stats.json`
- Node smoke: `tools/web/run-web-engine-smoke.mjs`
The ABI reports live handles and allocated bytes without exposing WASM heap
pointers to React. Document memory is held inside the engine state and output
buffers are explicitly freed after save; the first mesh snapshot also retains
bounded arrays before the later transferable-buffer path.
## Verification
```text
node tools/web/run-web-engine-smoke.mjs # blend open/save, then destroy
```

22
docs/status/W-025.md Normal file
View File

@@ -0,0 +1,22 @@
# W-025 Status
status: done
task: 建立 Web C ABI
## Evidence
- Header: `blender-5.2.0/source/blender/web_engine/web_engine_api.h`
- Implementation: `blender-5.2.0/source/blender/web_engine/web_engine_api.cpp`
- Golden: `tests/golden/W-025/c-api.json`
The ABI now has stable POD pointer/length entry points for create/destroy,
open, apply, snapshot, save and buffer release. Open accepts an empty input;
command and full `.blend` serialization return an explicit not-implemented
code until Blender libraries are linked.
## Verification
```text
tools/web/build-web-engine-variants.sh # passed
node tools/web/run-web-engine-smoke.mjs # passed
```

20
docs/status/W-026.md Normal file
View File

@@ -0,0 +1,20 @@
# W-026 Status
status: done
task: 接入 Blender 报告系统边界
## Evidence
- Error ABI: `web_engine_last_error_code()` and
`web_engine_last_error_message()`
- Golden: `tests/golden/W-026/error-report.json`
- Error codes are deterministic and do not expose raw WASM pointers to the UI.
The native Blender `ReportList` bridge is deferred until W-030 data libraries
are linked; this task freezes the Web-facing error contract first.
## Verification
```text
node tools/web/run-web-engine-smoke.mjs # explicit -3 not-implemented path
```

27
docs/status/W-027.md Normal file
View File

@@ -0,0 +1,27 @@
# W-027 Status
status: done
task: 建立真实 WebEngineWorker ready 路径
## Evidence
- Worker: `web/app/src/workers/web-engine.worker.ts`
- Client: `web/app/src/engine-client/WebEngineClient.ts`
- Vendored runtime: `web/app/src/vendor/blender/web_engine.js` and
`web/app/src/vendor/blender/web_engine.wasm`
- Static deployment copies: `web/app/public/vendor/blender/`
- Install script: `tools/web/install-web-engine-assets.sh`
- Golden: `tests/golden/W-027/worker-ready.json`
The worker dynamically imports the local Emscripten glue, fetches the local
WASM asset, initializes the ABI and returns deterministic memory stats. The UI
reports this as `WASM ABI: ready`; `.blend` loading now uses the same real ABI
and returns SceneIR snapshots. The legacy mock EngineClient remains only for
protocol regression tests.
## Verification
```text
npm run test:e2e # 4 passed, including local web_engine worker
npm run build # web-engine.worker and vendor/blender assets present
```

22
docs/status/W-028.md Normal file
View File

@@ -0,0 +1,22 @@
# W-028 Status
status: done
task: 原生/WASM 双端回归
## Evidence
- Native runner: `web/engine/web_engine_native_smoke.cpp`
- Dual runner: `tools/web/check-dual-engine.sh`
- Golden: `tests/golden/W-028/dual-engine.json`
The same C ABI returns matching create, invalid-empty-open, and destroy
results in native C++ and Emscripten builds. The WASM runner additionally
opens local `.blend` fixtures, checks SceneIR, verifies an authoritative
Main save/reopen, verifies ordinary decimate Main write/save/reopen, and verifies that the
skin/shape-key decimate boundary blocks save with `BLEND_WRITE_REQUIRES_MAIN_AUTHORITY`.
## Verification
```text
tools/web/check-dual-engine.sh # dual-engine-ok
```

23
docs/status/W-029.md Normal file
View File

@@ -0,0 +1,23 @@
# W-029 Status
status: done
task: 完成最小引擎里程碑
## Evidence
- Gate: `tests/golden/W-029/min-engine-gate.json`
- Local browser worker: `web/app/src/workers/web-engine.worker.ts`
- Native/WASM check: `tools/web/check-dual-engine.sh`
The Web runtime has a reproducible Blender/Emscripten CMake target, local
worker loading and a stable C ABI gate. The canonical target links the local
Blender BLO/DNA reader and is vendored into both source and public asset trees;
the UI opens `.blend` ArrayBuffers and displays the resulting SceneIR.
## Verification
```text
npm run build # passed
npm run test:e2e # 5 passed
tools/web/check-dual-engine.sh # passed, including empty/basic/save fixture
```

30
docs/status/W-030.md Normal file
View File

@@ -0,0 +1,30 @@
# W-030 Status
status: done
task: 从内存读取 `.blend`
## Evidence
- Reader: `blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp`
- ABI: `web_engine_open_blend(handle, data, length)` copies an `ArrayBuffer` into
WASM memory and never receives a browser or POSIX path.
- Worker: `web/app/src/workers/web-engine.worker.ts` reports started/progress/completed
phases and maps malformed input to `BLEND_READ_FAILED`.
- Golden: `tests/golden/W-030/manifest.json`
The reader accepts uncompressed, gzip and zstd Blender streams, validates the
little-endian Blender header, decodes the embedded DNA1 schema and returns an
empty or populated SceneIR snapshot. `empty.blend` and `basic_scene.blend` are
both exercised by the WASM smoke.
## Verification
```text
node tools/web/run-web-engine-smoke.mjs # empty + basic + save passed
tools/web/check-dual-engine.sh # native ABI + WASM reader passed
```
## Remaining risk
The first reader is deliberately bounded to a 1 GiB block and summary export;
evaluated mesh buffers are W-033.

29
docs/status/W-031.md Normal file
View File

@@ -0,0 +1,29 @@
# W-031 Status
status: done
task: 定义 SceneIR v1
## Evidence
- TypeScript contract: `web/protocol/scene-ir.ts`
- C++ serializer: `blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp`
- Golden: `tests/golden/W-031/scene-ir-v1.json`
SceneIR v1 now carries versioned source metadata, Blender Z-up coordinate
semantics, stable type/name IDs, nodes, collections, meshes, materials, images,
cameras, lights, animations, scenes, frame bounds and active object. Browser
validation rejects unsupported schema, coordinate, matrix and tuple shapes.
IDs are generated from Blender ID names with deterministic duplicate suffixes;
the wire format contains no raw WASM pointer.
## Verification
```text
npm run typecheck
node tools/web/run-web-engine-smoke.mjs
```
## Remaining risk
Image and animation arrays are schema-ready but are still empty until their
datablock extraction tasks are implemented.

28
docs/status/W-032.md Normal file
View File

@@ -0,0 +1,28 @@
# W-032 Status
status: done
task: 导出场景树和集合
## Evidence
- C++ collection/object traversal: `web_engine_blend_reader.cpp`
- Dynamic Outliner: `web/app/src/app/App.tsx`
- Golden: `tests/golden/W-032/basic-scene-tree.json`
The reader resolves Scene root collections, linked collection objects, object
parent/data links, active camera, local/world matrices, viewport visibility and
selectability. Collection and node arrays are sorted by stable IDs before
serialization; parent cycles fall back to the local matrix instead of recursing
without a bound.
## Verification
```text
node tools/web/run-web-engine-smoke.mjs
npm run test:e2e # local basic_scene.blend appears in Outliner
```
## Remaining risk
The fixture proves one nested collection. View-layer overrides and evaluated
depsgraph visibility remain part of the later scene-context work.

37
docs/status/W-033.md Normal file
View File

@@ -0,0 +1,37 @@
# W-033 Status
status: done_current_scope
task: 导出 MeshIR
## Implemented
Mesh summaries export vertex, edge, face and corner counts. Blender 5.2
`AttributeStorage` DATA blocks are now resolved by exact old-pointer matches
and by SDNA pointer width. The reader exports position floats, normals,
triangulated corner indices, corner UV/color attributes, triangle material
indices, material slot IDs and object-space bounds with
`geometryStatus: "available"`. The Three adapter applies Z-up conversion,
uses exported normals, expands corner attributes into render vertices, and
binds local material groups.
## Accepted bounded scope
The exporter now includes tangent and corner split-normal buffers, seam/sharp
edge flags, custom attribute metadata, vertex groups and UV-layer selection.
The native WBG1 C ABI carries these buffers without JSON construction and the
Worker validates/transfers them to Three.js. Mesh Edit operations rebuild
topology metadata through Blender Main and reject linked/shape-key topology
changes with structured errors.
Evaluated buffers for unsupported modifier/simulation families remain blocked
by the N-002 capability matrix; this is an explicit boundary rather than a
claim of universal modifier evaluation.
The field and future transferable layout is documented in
`docs/web/mesh-buffer-layout.md`.
## Verification
```text
node tools/web/run-web-engine-smoke.mjs # basic + attribute fixtures
```

29
docs/status/W-034.md Normal file
View File

@@ -0,0 +1,29 @@
# W-034 Status
status: done_current_scope
task: 导出材质、贴图和颜色
## Implemented
The SceneIR contract includes Principled-compatible base color, roughness,
metallic, emission, alpha and IOR fields. The native reader walks embedded
Principled node inputs, collects Image Texture IDs, and emits warnings for
linked inputs or unsupported nodes. Blender Image datablocks export stable
asset IDs, MIME guesses, generated dimensions and the original (possibly
`//`-relative) source path without embedding base64 pixels. The EngineWorker
exposes a local `requestAsset` metadata path for external and
packed-unavailable assets. The Three adapter applies the exported material
color, metalness, roughness, emission and alpha to local materials.
## Accepted bounded scope
Material slots can be created, removed and assigned to selected faces through
Blender Main. The Web authoring surface writes Principled values and creates a
limited `Principled -> Output`, `Image Texture`, `Normal Map` and `UV Map` graph.
PNG/JPEG imports become packed Blender Image datablocks without allocating a
headless ImBuf, and active UV-map/planar/cube unwrap operations round-trip.
Packed bytes remain available through the asset API and are covered by the
UDIM/generated/linked/corrupt resource matrix.
Arbitrary node graphs and renderer-specific shader execution are deliberately
outside this bounded contract; unsupported nodes are reported by SceneIR.

24
docs/status/W-035.md Normal file
View File

@@ -0,0 +1,24 @@
# W-035 Status
status: in_progress
task: 导出相机和灯光
## Implemented
Perspective/orthographic camera fields, sensor/shift values, clip range and
orthographic scale are read from the file and are visible in the SceneIR
snapshot. Light type/color/energy, radius, spot cone, area dimensions and sun
angle are exported. World background color and exposure are also versioned.
The Three adapter creates local point/spot/directional/area light objects and
applies the exported World color to the viewport background.
## Not yet accepted
Sensor fit is exported numerically, but exact sensor-fit projection math,
camera replacement for orthographic view, light target direction, and complete
world node settings remain pending.
## Golden fixture
`tests/golden/W-035/camera-light-world.json` covers the real
`basic_scene.blend` camera, area light and world linkage.

28
docs/status/W-036.md Normal file
View File

@@ -0,0 +1,28 @@
# W-036 Status
status: done_current_scope
task: 导出动画和帧状态
## Implemented
Scene frame current/start/end and object transforms are exported from the
serialized Scene block. Legacy FCurve lists and Blender 5.2 layered Actions
(keyframe strips/channelbags) are decoded into target-scoped `AnimationIR`
channels with RNA paths, scalar keyframes and bounded frame ranges. The
Timeline uses scene bounds instead of a fixed 250-frame range after a `.blend`
is opened.
## Accepted bounded scope
Timeline keyframe insertion/deletion for object location, Euler rotation and
scale writes Blender layered Actions through Main. The UI exposes frame range,
key dots, active Action and CONSTANT/LINEAR/BEZIER interpolation selection.
Object constraint enable/influence is editable and node constraints plus pose
constraints are exported in SceneIR. Frame/pose goldens cover 1/5/10/15 and
save/reopen compares evaluated matrices and vertices.
The N-014 bounded extension now reads and atomically replaces single-object
Action Clip stacks, and Blender's native depsgraph evaluates the saved Main at
the requested frame. Transition/meta/sound strips, Animated Time, reverse,
Drivers, complete blending matrices and arbitrary constraint types remain
structured future extensions; Three.js does not approximate them.

26
docs/status/W-037.md Normal file
View File

@@ -0,0 +1,26 @@
# W-037 Status
status: done_current_scope
task: 实现场景完整快照
## Implemented
`get_scene_snapshot` remains a validated JSON compatibility ABI, including
bounded positions/index/normals and optional
UV/color/material-slot arrays for available meshes. The Worker validates that
fallback snapshot, packs the geometry into little-endian `Float32Array`/`Uint32Array`
buffers, strips the large arrays from the browser snapshot, and transfers the
buffers to React/Three.js. Browser parsing is centralized in
`parseSceneSnapshotIR` and failures become stable worker error reports. The
envelope and chunk rules are documented in `docs/web/mesh-buffer-layout.md`.
## Accepted bounded scope
The preferred native WBG1 metadata/geometry ABI now emits positions, indices,
corner attributes, material/face/edge indices, tangents and split normals as
tightly packed little-endian buffers. The Worker validates lengths/bounds and
keeps large arrays out of React state. Per-mesh envelopes, Worker processing,
Three.js frustum culling, linked-data `InstancedMesh` groups and LOD cover the
current large-scene contract. `?offscreen=1` capability-gates real canvas/WebGL
ownership in a render Worker with orbit, selection and nonblank-pixel E2E;
unsupported browsers retain the main-thread renderer.

29
docs/status/W-038.md Normal file
View File

@@ -0,0 +1,29 @@
# W-038 Status
status: done_current_scope
task: 实现场景增量
## Implemented
`web/protocol/scene-delta.ts` defines revisioned node add/update/remove, active
object/frame changes and mesh/material/camera/light/animation resources.
`diffSceneSnapshots` emits changed node and resource fields;
`applySceneDelta` rejects stale base revisions and returns sorted immutable node
arrays. The native C ABI computes the same minimal delta whenever a new
`.blend` is opened, and the Worker exposes it through the `delta` request after
validating the envelope. The command registry covers scene/frame state,
modifier switches, mesh editing, material/UV authoring, keyframes/constraints
and object hierarchy operations. Each command mutates Blender Main and returns
the corresponding revisioned delta through `applyCommand`.
## Accepted bounded scope
SceneDelta now covers resource collections for meshes, materials, cameras,
lights and animations in addition to nodes/frame/active object. Mesh payloads
transfer separately so topology and attribute changes replace the associated
resource envelope, while node-only changes stay in place. All declared
authoring commands are Main-authoritative and covered by save/reopen. Fine-
grained `MeshGeometryDelta` patches now transfer only changed byte ranges when
field lengths stay stable; topology/attribute layout changes replace only the
affected per-mesh envelope. The main-thread client applies patches immutably
and retains unchanged mesh buffers.

25
docs/status/W-039.md Normal file
View File

@@ -0,0 +1,25 @@
# W-039 Status
status: done_current_scope
task: 完成场景数据里程碑
## Current gate
The `.blend` -> Blender Main/WASM -> binary SceneIR -> Three.js path is proven
for basic, animation, pose, modifier, resource and authoring fixtures. The
browser opens files, renders geometry, keeps Outliner/viewport selection linked,
and applies Main edits with revisioned snapshot/delta responses.
## Accepted bounded scope
W-034 through W-038 are complete for the declared bounded contract: binary
SceneIR, resource deltas, packed assets, animation editing and Blender Main
authoring all have native and browser regression evidence. New Blender
families must add independent goldens instead of reusing W-080.
## Verification
```text
npm --prefix web run test:e2e
npm --prefix web run test:authoring-roundtrip
```

14
docs/status/W-040.md Normal file
View File

@@ -0,0 +1,14 @@
# W-040 Status
status: in_progress
task: 固定 OPFS 目录布局
`web/app/src/storage/opfs-files.ts` validates project IDs and file names,
creates `projects/<id>/scene.blend`, `snapshots/`, `assets/`, `thumbs/` and
`tmp/`, and writes a complete scene buffer through a temporary file before
replacement. The standard `FileSystemHandle.move` path is used when available;
the copy-and-remove fallback is bounded to the same project directory.
Large asset hashing, crash-recovery manifests and cross-browser atomic rename
guarantees remain subsequent tasks.

12
docs/status/W-041.md Normal file
View File

@@ -0,0 +1,12 @@
# W-041 Status
status: done_current_scope
task: 建立 IndexedDB schema 和 migration
`web/app/src/storage/migrations.ts` upgrades `blender-web-metadata` to schema
version 4 while preserving the existing smoke store. It creates `project`,
`asset`, `snapshot`, `operation_log`, `setting` and `migration` stores and
records each schema migration. Schema 4 adds content-addressed asset metadata;
StorageWorker `info`, project layout and Worker-restart discovery are covered by Playwright.
Downgrade/rollback across older deployed browser builds remains outside the current scope.

16
docs/status/W-042.md Normal file
View File

@@ -0,0 +1,16 @@
# W-042 Status
status: done
task: 保存 `.blend` 原子事务
StorageWorker `saveProject` accepts a transferable ArrayBuffer, creates the
OPFS project layout, writes and SHA-256-verifies `scene.blend.<uuid>.stage`, records a
recovery journal, and uses same-directory move (or a complete-copy fallback) before re-verifying
`scene.blend`. Only then does it write the manifest, clear the journal and update the IndexedDB
`project` record with revision, byte count, digest, backend and timestamp.
The React Save command now submits this transaction before downloading the
`.blend` Blob, and keeps the StorageClient alive for the project session.
When OPFS is unavailable, the record stores the buffer in IndexedDB instead.
Playwright injects failures after staging and after scene commit, restarts the Worker, and verifies
exact bytes/revision/digest through `readProject`. Concurrent same-project saves are serialized and
the last submitted revision wins without journal races.

13
docs/status/W-043.md Normal file
View File

@@ -0,0 +1,13 @@
# W-043 Status
status: done
task: 自动保存和快照策略
`AutosaveScheduler` debounces a dirty project and submits the current engine
buffer through StorageWorker after 1.5 seconds without triggering a download.
The React Save button reuses the same persistence path before exporting a
download. A browser regression confirms the dirty marker returns to saved.
Every ten commands and every explicit save now retain a SHA-256 verified blend snapshot. The
StorageWorker enforces count and total-byte limits and can list/read retained revisions. Large-file
writes remain Worker-isolated and never block the renderer thread.

16
docs/status/W-044.md Normal file
View File

@@ -0,0 +1,16 @@
# W-044 Status
status: done
task: 记录操作日志和恢复
StorageWorker now validates and persists operation records with project id,
revision, payload, inverse payload and timestamp in IndexedDB's
`operation_log` store. StorageClient exposes `appendOperation`, and the
operation/inverse path has a browser regression.
Project file recovery is now separate and complete for the current save path:
an OPFS SHA-256 journal recovers interrupted staging/commit after Worker restart.
Schema 5 adds corruption quarantine. Recovery sorts validated operations by revision/time/ID,
rejects revision conflicts, opens the committed project or latest retained snapshot, and applies
only uncommitted replayable commands to the native Main. A successful save prunes committed rows.

22
docs/status/W-050.md Normal file
View File

@@ -0,0 +1,22 @@
# W-050 Status
status: done_current_scope
task: SceneIR adapter
ViewportRenderer now tags imported meshes and lights with
`userData.blenderId`, `userData.sceneNodeId` and `userData.revision`, keeps a
Blender-ID object index, and exposes `applyDelta()`. Pure node visibility and
transform updates mutate the existing Three object; node lifecycle changes
fall back to a validated snapshot rebuild so geometry/material ownership stays
correct.
The adapter now performs mesh raycast selection and applies multi-selection
highlights, consumes binary WBG1 mesh resources, and installs validated LOD
levels with hysteresis. Resource-aware SceneDelta entries trigger a validated
resource rebuild while node-only deltas update existing Three objects.
The Worker keeps the previous geometry generation and emits byte-range patches
or affected-mesh replacements. Objects sharing one Blender Mesh datablock are
coalesced into a raycastable `InstancedMesh` with per-instance transform and
selection. A capability-gated OffscreenCanvas Worker owns WebGL when requested;
browsers without that capability retain the same main-thread renderer.

11
docs/status/W-052.md Normal file
View File

@@ -0,0 +1,11 @@
# W-052 Status
status: done_current_scope
task: 相机和输入系统
OrbitControls, resize handling and mouse orbit are active in the local Three
viewport. Timeline transport controls now implement first/previous/play/next/
last frame state transitions over the imported SceneIR range. Exact Blender
camera basis conversion is preserved at the adapter boundary. Object/Edit mode keyboard dispatch
and draggable X/Y/Z transform handles route to Main commands; additional view presets and touch
gesture variants remain additive UI work.

12
docs/status/W-053.md Normal file
View File

@@ -0,0 +1,12 @@
# W-053 Status
status: done_current_scope
task: 选择和 Outliner 联动
Outliner rows are keyboard- and pointer-activatable and update
`SceneSnapshotIR.activeObjectId`; the Three adapter consumes the new snapshot
and highlights the active object's material. Stable Blender IDs are used for
the binding.
Viewport raycast selection, Shift/Ctrl multi-select, hidden-object filtering and deletion cleanup
are implemented. Selected IDs remain a UI-session concern while the active ID stays in SceneIR.

34
docs/status/W-060.md Normal file
View File

@@ -0,0 +1,34 @@
# W-060 Status
status: done_current_scope
task: 对象模式命令
The first native command registry slice is implemented end to end. The Worker
serializes validated `WebEngineEditCommand` payloads into the local Blender
WASM ABI, and the native engine currently supports:
- `setFrame`: updates the current timeline frame and emits a frame SceneDelta.
- `setObjectVisibility`: updates an existing object node and emits a node update
SceneDelta.
- `setModifierEnabled`: updates the persistent modifier mode in Blender Main.
- `decimateMesh`: commits ordinary mesh topology/UV/color/material output to Blender Main;
skin/shape-key replacement remains structurally blocked from save.
- `undo`/`redo`: restore both SceneIR and a reopenable Blender Main history entry.
- `createPrimitive`, `duplicateObject`, `deleteObject`: mutate Main IDs and Collections, then rebuild SceneIR from the writer buffer; linked duplicate preserves the shared Mesh datablock for instancing.
- `setObjectTransform`, `translateMeshVertices`: persist Object Mode TRS and the first Edit Mode vertex workflow.
- `meshEdit`: Main-authoritative Merge, Dissolve, Extrude, Inset, Bevel and Loop Cut transactions.
- UV/material/image commands: UV map lifecycle/projection, material slots/face assignment, bounded Principled graph and packed PNG/JPEG images.
- animation/hierarchy commands: keyframes/interpolation/actions, constraints, parent/collection moves, rename, Join/Separate, Apply Transform and Origin.
The commands have native smoke coverage and browser E2E tests that check
revision base/next values and the resulting snapshot. Unknown commands and
malformed payloads return typed native errors.
The Blender-style UI now routes Timeline frame changes and Outliner eye-button
visibility changes through the same command path. React SceneIR, transferable
geometry state, Three visibility and the dirty marker are updated from the
command response; the browser E2E suite covers the Outliner interaction.
Every declared command writes back to `.blend`, survives undo/redo and passes save/reopen
regression. Unsupported modifier families, mutations of read-only linked-library IDs and topology
changes that would invalidate shape keys remain structured native blocks.

35
docs/status/W-073.md Normal file
View File

@@ -0,0 +1,35 @@
# W-073 Status
status: in_progress
task: 定义 Blender 三角面简化和 LOD 协议
`web/protocol/simplify.ts` now defines versioned `SimplifyProfile`,
`SimplifyResult` and `LODManifest` contracts. The profile validator keeps the
Blender modifier modes separate:
- `COLLAPSE` requires `ratio` in `(0, 1]` and supports triangle budget,
vertex-group weighting/inversion, triangulate, symmetry axis and tolerance.
- `UNSUBDIV` requires a positive integer `iterations` and rejects collapse or
dissolve-only parameters.
- `DISSOLVE_PLANAR` requires `angleLimit` in `[0, PI]`, with explicit
boundary delimit flags and dissolve-boundary handling.
The parser also enforces non-negative source revisions and error codes for
missing vertex groups, unsupported cross-mode fields, invalid budgets and LOD
manifests whose triangle budgets do not decrease. A browser E2E test covers a
valid collapse profile, a two-level manifest and the missing-vertex-group
failure.
The common profile now carries an explicit `attributePolicy`
(`PRESERVE`/`RECOMPUTE_NORMALS`/`DROP`) and a validated `SkinSimplifyPolicy`
with maximum influences, minimum weight, maximum position error and shape-key
handling. Vertex-group and symmetry-only fields are conditionally optional, so
the TypeScript contract and runtime validator no longer disagree when those
features are disabled.
The protocol is now exercised end-to-end by the native/WASM command path:
`decimateMesh` validates `sourceMeshRevision`, returns a new SceneIR revision,
and the browser-facing smoke checks topology counts, index bounds and attribute
buffer lengths. Native Blender/WASM golden parity for all parameter
combinations remains part of W-079; implementation of the declared skin policy
remains W-076.

37
docs/status/W-074.md Normal file
View File

@@ -0,0 +1,37 @@
# W-074 Status
status: in_progress
task: 编译并复用 Blender Decimate 实现
`web_engine_decimate.cpp` now wraps Blender's native BMesh decimate entry points
(`BM_mesh_decimate_collapse`, `BM_mesh_decimate_unsubdivide` and
`BM_mesh_decimate_dissolve`) behind the local `web_engine_decimate_apply`
two-pass C ABI. The wrapper accepts triangle buffers, returns required output
capacities on the first call, and leaves the input mesh untouched.
`web_engine_apply_command` now accepts `decimateMesh` with a versioned
`SimplifyProfile`, validates the source revision, calls the native wrapper, and
replaces the evaluated SceneIR mesh topology. The command allocates output from
the input upper bounds and evaluates Decimate once; it does not run the
modifier twice merely to query exact capacities. Normals are recomputed from
the result, BMFace material indices are retained, and UV/float-color corners
are transferred by the W-076 policy layer. Collapse now accepts boundary and
non-manifold triangle meshes through the same BMesh bridge. The blend reader
reconstructs corner vertices from `.corner_edge` and `.edge_verts` only after
validating every face cycle, so it never treats edge indices as vertex indices.
`npm run test:topology-collapse` independently opens and collapses an open quad,
an open n-gon and a three-face shared-edge mesh; malformed cycles remain
`summary-only`.
The WebEngine CMake target exports the ABI symbol and links a small archive-file
group containing Blender BMesh, CustomData-capable BKernel objects, DNA/BLO,
local zlib/zstd and allocator support; this avoids pulling the desktop
editor/image dependency graph into the WASM module.
The local artifact is installed in both `web/app/public/vendor/blender/` and
`web/app/src/vendor/blender/`, with the manifest SHA-256 updated. The native
smoke covers all three modes, Collapse topology reduction, revision sequencing,
SceneDelta and normal/UV/material attribute lengths. The browser regression
suite covers the same local WASM command path, including native command,
preview and isolated LOD workers. Modifier-stack transactions and full
skin/seam/sharp preservation remain W-075/W-076.

86
docs/status/W-075.md Normal file
View File

@@ -0,0 +1,86 @@
# W-075 Status
status: in_progress
task: 实现三种模式和修改器堆栈求值
The React Properties editor now exposes a Blender-style Decimate inspector for
all three native modes. Collapse has ratio, triangulate and optional symmetry
axis controls; Un-Subdivide has bounded iterations; Dissolve Planar has angle,
boundary handling and Normal/Material/Seam/Sharp/UV delimit toggles. A shared
attribute-policy selector makes preservation or dropping explicit. Browser E2E
switches through the mode-specific controls, while native probes confirm all
three BMesh entry points execute against the local WASM artifact.
Applying the inspector commits a revisioned `decimateMesh` command. A
`previewDecimateMesh` command now evaluates the profile in a separate WASM
module instance, returns temporary geometry to the Three.js viewport, keeps
the authoritative handle/revision unchanged and supports explicit cancellation
from the inspector.
The SceneIR contract now carries modifier UUID/type/name/enabled state and a
deterministic modifier-stack hash. The native `.blend` reader traverses each
`Object.modifiers` list, uses Blender's persistent modifier UID for a stable
UUID when available, and exports viewport/render modes plus common Subsurf,
Mirror, Decimate and Bevel parameters. The generated
`rigged_shape_scene.blend` fixture verifies the real Decimate modifier payload
instead of a mock stack. The native command
`setModifierEnabled` toggles a declared modifier, and the native history ring
implements bounded undo/redo with revisioned snapshots. React exposes the
modifier checkbox and top-bar undo/redo commands; browser E2E verifies that
undo/redo changes the authoritative native snapshot rather than a Three.js
shadow copy. Native smoke now covers the same modifier toggle and its
undo/redo cycle against a local WASM engine.
`protocol/modifier-graph.ts` now builds a stable per-mesh topological order from
stack order and explicit `dependsOn` edges. Cycles and unresolved external
inputs produce `BLOCKED` with the offending IDs; declared but unexecuted
modifiers remain `METADATA_ONLY`. The WebEngine Worker requires `EVALUATED`
before Decimate preview, commit or LOD generation. Browser E2E checks the
topological order, metadata-only handling and cycle blocking.
The WebEngine exposes `evaluateDepsgraph` and the native module executes
Blender 5.2's `BLO_read_from_memory` loader, headless runtime bootstrap,
view-layer sync, graph build, tagged update and frame evaluation. A wasm32 DNA
alignment bridge and the Web ID registry for NodeTree/Camera/Light preserve the
current C++ layout. The report contains evaluated mesh positions/triangles,
source Mesh IDs, object world matrices and modifier counts. The report also
contains ordered modifier entries with persistent UID, mode flags, stack
dependencies and explicit `EVALUATED`, `DISABLED` or `BLOCKED` status with
stable error codes and suggestions. Empty,
basic and rigged/shape-key fixtures now pass without a WASM trap; the rigged
positions match the Blender deformation golden. Broader modifier and constraint
parity is tracked separately and unsupported cases remain structured errors.
The lightweight reader's name table now covers all Blender 5.2 DNA modifier
slots 0 through 86, including the Grease Pencil range, with a compile-time size
check. Recognition remains separate from evaluated parity in
`docs/web/modifier-support-matrix.md`.
SceneIR now extracts target IDs and core parameters for Lattice, Curve,
Armature, Hook, Mesh Deform, Shrinkwrap and Surface Deform, and appends their
external objects to `dependsOn`; they deliberately remain `METADATA_ONLY`.
Six reproducible Blender 5.2 category goldens now cover Generate, Deform,
legacy Curve, Physics/time, Geometry Nodes and Grease Pencil. The native report
now includes non-Mesh objects, including all 26 Grease Pencil modifier types
exposed by Blender 5.2 Python. Twenty-four category types now have evaluated Web
goldens. Subdivision uses the local OpenSubdiv 3.7.0 CPU evaluator and matches
the 26-vertex/48-triangle desktop result. Exact Boolean uses local GMP 6.3.0
and matches the 12-vertex/20-triangle surface result; coplanar triangulation is
compared by oriented surface area because wasm32 and desktop can select
different valid diagonals. Curve now runs its native modifier and matches the
desktop golden after the Web ID registry was extended for Curves data.
Geometry Nodes has bounded native implementations for a single constant
Transform Geometry or Set Position node. Both topology/position results match
Blender 5.2. Unknown graphs retain `BLENDER_MODIFIER_ERROR`; simulation zones
and bake data preserve input geometry and return
`GEOMETRY_NODES_SIMULATION_UNAVAILABLE`. The full node registration/lazy-function
runtime is not claimed. Displace adds a bounded constant local-space closure;
resource-dependent configurations return `MODIFIER_CONFIGURATION_UNSUPPORTED`.
Grease Pencil modifiers remain disabled.
Mesh and Surface Deform fixtures continue to serialize real bind caches and
deform their cage/target after binding.
This is not yet complete native Blender dependency-graph parity. Unsupported
external runtime resources and unsupported modifier/constraint types return
structured errors; the lightweight SceneIR history remains separate and is not
silently serialized as a full Blender undo stack.

46
docs/status/W-076.md Normal file
View File

@@ -0,0 +1,46 @@
# W-076 Status
status: in_progress
task: 属性、UV 和蒙皮安全简化
`SimplifyProfile` now requires an explicit attribute policy after parsing and
defines `SkinSimplifyPolicy` limits for influences, weights, position error and
shape keys. The native command currently implements this partial protection
matrix:
- normals are recomputed from the evaluated triangle topology;
- BMFace material indices and material slot IDs are retained;
- existing UV and float-color corner values are transferred to output corners
using a deterministic closest-source-triangle barycentric projection;
- `DROP` is the only policy that removes these arrays intentionally;
- Collapse validates and accepts open/non-manifold Mesh-to-BMesh topology;
- the native reader exports Mesh `MDeformVert` CustomData as fixed four-slot
`SkinWeightsIR` arrays, carries Mesh vertex-group names, and exports
non-Basis `KeyBlock` positions as `ShapeKeyIR`; the local
`rigged_shape_scene.blend` fixture validates both on a real `.blend` file;
- a skinned or shape-key mesh requires explicit `skinPolicy`; omitting it is a
structured `NOT_IMPLEMENTED` failure rather than silent attribute loss.
Native smoke and browser Worker E2E verify normal, UV, corner and material
buffer sizes on the closed cube fixture. The WebEngine protocol validates
`SkinWeightsIR` and `ShapeKeyIR`. After BMesh produces the authoritative
topology, native code projects each output vertex onto the closest source
triangle, merges and normalizes its joint influences, and reconstructs every
shape-key position with the same barycentric weights. `maxInfluences`,
`minWeight` and `maxPositionError` are enforced before committing the new
SceneIR. Native smoke and Worker E2E verify output vertex counts, normalized
weights, stable joint IDs, bind matrices and shape-key array lengths.
`shapeKeys: REJECT` still returns `SHAPE_KEYS_REJECTED` before native work.
The reader now exports `ArmatureIR` bones with parent IDs, Blender `Bone.arm_mat`
rest matrices and matching `PoseChannel.pose_mat` matrices. A mesh Armature
modifier resolves its armature object, remaps vertex-group bone names to stable
joint IDs, and records the mesh object TRS as its non-identity bind matrix. The
generated rig fixture verifies all fields against local Blender data. This is
sufficient for GLB inverse-bind generation and the current triangle-bridge
remapping path.
UV seam and sharp-edge error metrics, tangent regeneration, native
vertex-group name selection in profiles, Armature deformation comparison,
seam/sharp constraints and Blender-evaluated shape-key error comparison remain
required before this task can be marked done.

46
docs/status/W-077.md Normal file
View File

@@ -0,0 +1,46 @@
# W-077 Status
status: in_progress
task: 多级 LOD 生成、缓存和视口选择
The cache boundary is now implemented without changing the native WASM ABI:
- `buildLODCacheKey` creates a deterministic, path-safe key from object ID,
source mesh revision, modifier-stack hash, profile hash and REST/POSE state.
- IndexedDB schema v3 adds `lod_manifest`; the StorageWorker can put/get a
validated manifest and writes transferable `.mesh` bytes to OPFS
`projects/<id>/cache/lod/<cacheKey>.mesh`. Read, delete and byte-budget
pruning update `lastAccessAt` and remove the least-recently-used entries.
- `ThreeLODAdapter` selects pre-generated levels from projected pixel height,
with a configurable hysteresis band, and is updated by the viewport render
loop without rebuilding the imported scene.
- `generateLOD` evaluates every requested profile independently from the source
`.blend` in a fresh WASM module instance, returns a validated manifest and
transferable geometry, and is exposed by the React inspector. A binary
length-table container round-trips the level buffers before OPFS persistence;
the React path reads that persisted container back before installing levels
into the Three.js viewport, while preserving source transforms and materials.
- Browser E2E covers the OPFS bytes, manifest round trip, stable key and
hysteresis behavior, multi-level generation and binary geometry round trips.
The React open path now lists all `lod_manifest` rows for the project after a
fresh application load, filters them by the authoritative SceneIR revision and
object IDs, reads the OPFS `.mesh` container, validates the level mesh IDs and
installs the cached levels into the Three.js LOD adapter. Applying any native
scene command also removes manifests from older source revisions, so a stale
cache cannot be selected after a mesh or modifier edit. Browser E2E covers
generation, reload, discovery and reuse.
Pose-aware skin joints are still not exported into the LOD manifest. The native
Collapse bridge now performs one Blender BMesh collapse pass at a time, creating
a fresh heap for each pass and recalculating the quadric choice from the current
mesh state. This removes the previous WASM memory failure at intermediate cube
targets; the LOD Worker keeps per-level isolation as a general fault boundary.
An active modifier stack must now have an `EVALUATED` dependency-graph report
before LOD generation. Metadata-only and unresolved stacks return a structured
capability error rather than caching output from an implicit base mesh.
Each isolated LOD profile applies the same skin/shape-key preflight as direct
Decimate, so a valid `skinPolicy` receives native remapping while a missing or
rejecting policy cannot create a partial cache entry.
Full Blender parity still requires pose-aware skin joints and broader Blender
golden coverage, so the task stays `in_progress`.

43
docs/status/W-078.md Normal file
View File

@@ -0,0 +1,43 @@
# W-078 Status
status: in_progress
task: 轻量化预算和导出策略
`protocol/budget.ts` now validates explicit integer budgets for triangles,
vertices, index bytes, material slots, texture bytes and estimated GPU bytes.
`evaluateLightweightBudget` returns every violation with limit, actual value and
excess while leaving the measured usage unchanged. Browser E2E covers a mixed
triangle/GPU over-budget report.
`aggregateLightweightUsage` and `evaluateLightweightBudgets` now produce
deterministic project, collection, object and LOD scopes. Project usage sums
each source object once; collection membership is reported per collection and
LOD usage is kept separate so generated levels are not double-counted. Browser
E2E covers all four scope reports.
`analyzeGLBExport` now runs before the UI export action and returns structured
warnings/errors for summary-only or missing geometry, unevaluated modifiers,
external/packed images, linked material inputs and unavailable skin remapping.
When no blocking condition is present, the local exporter writes a standards-
shaped GLB 2.0 binary containing hierarchy, mesh positions/normals/UVs/colors,
material groups, PBR material factors and shape-key morph targets. Blender
shape-key positions are converted to glTF deltas in the Z-up to Three.js/glTF
basis conversion. The top-bar action downloads this local binary; browser E2E
checks the GLB header and a non-zero morph delta.
`exportGLB` now accepts caller-provided local asset buffers, embeds each image
as a GLB buffer view, and connects it to exported PBR materials. It exports
`JOINTS_0`/`WEIGHTS_0`, bone nodes, inverse bind matrices and supported object
or bone animation channels for a resolved armature binding. The local test
worker parses the emitted GLB JSON again; browser E2E embeds a real local PNG
and verifies its byte length/signature, skin joints, MAT4 inverse-bind accessor,
animation channels and skin attributes without a network fetch.
The native reader now extracts the exact bytes of both legacy and list-based
Blender packed images through the local asset request ABI; the packed fixture
checks PNG signature, MIME and byte length without a network request. The GLB
worker strictly parses the emitted JSON/BIN chunks and compares mesh
attributes, embedded image byte counts/MIME, skin joints/inverse-bind MAT4s,
morph targets and grouped animation paths against the source SceneIR. The
browser round-trip test has no semantic mismatches. Blender-side unsupported
features remain explicit export warnings.

57
docs/status/W-079.md Normal file
View File

@@ -0,0 +1,57 @@
# W-079 Status
status: in_progress
task: 三角面简化和轻量化里程碑
The Blender golden fixture is checked by
`tools/web/run-blender-golden.mjs`. It opens the local
`tests/files/web/basic_scene.blend`, evaluates native Collapse in fresh WASM
instances at ratios `1.0`, `0.9`, `0.5` and `0.25`, and compares source object
identity, vertex counts and evaluated loop-triangle counts with
`tests/golden/W-079/basic-scene-decimate.json`. The test is runnable offline as
`npm run test:golden` and currently passes. The focused native memory regression
gate `npm run test:collapse-ratios` starts an isolated WASM instance for each
target and asserts a successful non-empty result. The matrix now also includes the
previously unstable intermediate ratios `0.80`, `0.75`, `0.70` and `0.65`.
LOD generation evaluates each requested profile in an isolated Worker/WASM
instance. A failure at one target records `requestedLevel`, `triangleBudget`
and the diagnostic message in `WebEngineLODResult.errors`; the remaining levels
are reindexed and cached. The native bridge now avoids the former multi-collapse
heap lifetime failure by running one native Collapse pass per fresh heap, so the
intermediate golden ratios no longer fault.
The reader retains 64-bit on-disk BHead pointer identities in WASM and resolves
duplicate `BLEND_DATA` attribute arrays in the owning Mesh block range. This
prevents a second mesh from inheriting the first mesh's attributes. The offline
`npm run test:topology-collapse` regression confirms successful native Collapse
for an open quad, an open n-gon and a non-manifold shared edge.
The rigged fixture also now runs Collapse with a preserving `skinPolicy`.
Native output projects to the source triangles, remaps fixed four-slot weights
and rebuilds shape-key positions, while a missing policy remains blocked. The
same path is covered through the browser WebEngine Worker.
The deformation fixture now records an actual Blender 5.2 evaluated-depsgraph
golden with shape-key value and armature pose applied. The Web deformation
worker compares linear bind/rest/pose evaluation against the declared max and
RMS error budget. Packed-image extraction, GLB semantic re-import and the Web
deformation golden are covered by focused browser tests.
The native Blender Depsgraph path now completes full loader, view-layer sync,
graph build, tagged update and frame evaluation in the headless WASM build for
the empty, basic and rigged/shape-key fixtures. A wasm32-specific DNA layout
adapter recursively restores 8-byte alignment for 64-bit members; this fixes
the `Scene.master_collection` offset and prevents the former memory trap. The
Web ID registry includes embedded NodeTree, Camera and Light data, and the
rigged output matches the Blender 5.2 deformation golden within its max/RMS
budgets. The report now exposes ordered native modifier entries, persistent UIDs,
mode flags, target object IDs, stack dependencies and explicit Blender evaluation errors; the
rigged fixture verifies Armature followed by a structured Decimate block on its
two-triangle input. N-001 is complete for the declared fixtures and N-002 has
started with this report contract; broader modifier, constraint and stress
coverage remains tracked by N-002/N-003/N-008.
The broad Decimate mode/performance matrix and Blender round-trip warnings are
still tracked as follow-up coverage; unsupported cases must remain structured
errors and cannot be treated as parity.

32
docs/status/W-080.md Normal file
View File

@@ -0,0 +1,32 @@
# W-080 Principled 材质映射
状态:`done_current_scope`
路线图中的 W-080 专指 Principled 材质映射。当前已完成默认输入、emission、alpha、IOR、
Image Texture 元数据和 warning 证据材质槽、选中面分配、UV Map 选择、限定
Principled/Image Texture/Normal Map 节点创作及 packed PNG/JPEG 写回归属于 W-080 的
bounded authoring slice。packed pixels、sRGB、基础 PBR/纹理 sampler 的 Blender GLB
round-trip 由 N-004/N-005 覆盖。N-013 现可把 Principled、Image Texture、Normal Map、
Material Output 四节点整图原子写回 Main 并保存重开RGB/Value/Mix/Math/Mapping/TexCoord/
Bump、任意节点求值和更多 Principled 扩展仍保持结构化边界。
2026-08-09 增加 `physical-v1` 渲染切片Specular IOR Level、Transmission Weight、
Coat Weight、Coat Roughness、Emission Strength 已贯通 Inspector、Blender Main、reader、
SceneIR、save/reopen 和主线程/Offscreen `MeshPhysicalMaterial`。两条视口现在统一使用线性
输入、sRGB 输出、ACES、World exposure、真实 SUN/SPOT 方向和有界 PCF shadow。
详细映射、风险和 PBR-001 至 PBR-012 后续任务见
`docs/PBR_RENDERING_IMPLEMENTATION_PLAN.md`。纹理像素 GPU 上传、UDIM、HDRI/IBL、任意
Shader Node、AREA 阴影等仍是 planned不包含在 `done_current_scope` 声明中。
`tests/golden/W-080/animation-depsgraph.json``pose-constraint-depsgraph.json`
历史 fixture 目录,语义上属于 N-003 的 frame/pose 回归,不改变 W-080 的任务定义。迁移
这些 fixture 前,验收脚本和 golden manifest 必须同步更新,禁止复用任务编号。
默认验收:
```bash
npm --prefix web run test:golden
npm --prefix web run test:frame-evaluation
npm --prefix web run test:pose-constraint-goldens
npm --prefix web run test:authoring-roundtrip
```

View File

@@ -0,0 +1,128 @@
{
"schemaVersion": 1,
"updatedAt": "2026-08-12",
"source": "docs/BLENDER_5_2_WEB_FEATURE_PARITY.md",
"statusEnum": ["LOCAL_EXACT", "LOCAL_BOUNDED", "SERVER", "BLOCKED"],
"families": [
{
"id": "N-015",
"name": "Non-mesh geometry",
"status": "LOCAL_BOUNDED",
"roadmapStatus": "in_progress",
"completedSlices": ["A1", "A2-malformed-binary-partial", "A2-integer-overflow-fixtures", "A2-multichunk-attribute-completeness", "B2", "B3-metadata", "C1-topology-partial", "C1-handle-points-partial", "C1-handle-preview-raycast-partial", "C1-handle-identity-gizmo-main-roundtrip", "C1-rename-partial", "C1-create-delete-poly-partial", "C1-poly-bezier-nurbs-1d-conversion", "C1-multispline-create-delete-bulk-handle-cyclic-transaction", "C1-surface-2d-topology-transaction", "C2-font-geometry-partial", "C2-font-layout-partial", "C2-font-character-style-textbox-roundtrip", "C2-existing-packed-vfont-style-links", "C2-builtin-font-evaluation-exact", "C3-roundtrip", "D1-raycast-partial", "D1-offscreen-vert-edge-partial", "D1-selection-history-partial", "D1-handle-identity-axis-gizmo", "D2-partial", "E1-partial", "E1-desktop-geometry-golden", "E1-true-2d-surface-desktop-golden", "E1-glb-evaluated-nonmesh-roundtrip-partial", "E1-glb-curve-line-surface-mesh-evaluated", "E1-usda-four-object-desktop-roundtrip", "E1-pointcloud-curves-hair-glb-usd-loss-fixture", "E2-1M-chromium", "E2-chromium-worker-recovery", "E2-opfs-quota-chromium"],
"blockedSlices": ["B3-vdb-renderer", "C1-multi-handle-selection-drag-gizmo", "C2-new-external-font-import", "D1-cross-object-history-range-patch", "E1-volume-loss-fixture"],
"acceptance": ["web:test:nonmesh-roundtrip", "web:test:nonmesh-desktop-golden", "web:test:nonmesh-glb-blender-roundtrip", "web:test:nonmesh-usd-serialization", "web:test:nonmesh-usd-blender-roundtrip", "web:test:nonmesh-binary", "web:test:vdb", "web:test:selection-history", "web:e2e:N-015|non-mesh", "web:e2e:real OPFS quota"],
"dependencies": []
},
{
"id": "N-016",
"name": "Grease Pencil",
"status": "BLOCKED",
"roadmapStatus": "planned",
"completedSlices": ["A-schema-budget", "A-main-reader", "B-layer-frame-stroke-transaction-partial", "C-point-radius-opacity-color-cyclic-material-partial", "D-current-frame-stroke-preview-main-offscreen-chromium"],
"blockedSlices": ["A", "B", "C", "D", "E"],
"acceptance": ["web:test:grease-pencil", "web:e2e:N-016 Grease Pencil"],
"dependencies": ["N-015"]
},
{
"id": "N-017",
"name": "Paint and weights",
"status": "BLOCKED",
"roadmapStatus": "planned",
"completedSlices": ["A-stroke-hit-weight-schema-budget-partial", "B-main-vertex-color-partial", "B-main-vertex-weight-normalize-partial"],
"blockedSlices": ["A", "B", "C", "D", "E"],
"acceptance": ["web:test:paint-roundtrip", "web:e2e:N-017 paint"],
"dependencies": ["N-016"]
},
{
"id": "N-018",
"name": "Physics and simulation",
"status": "BLOCKED",
"roadmapStatus": "planned",
"completedSlices": ["A-family-capability-inventory", "B-settings-dependency-cache-manifest-partial", "C-exact-frame-selection-gate"],
"blockedSlices": ["A", "B", "C", "D", "E"],
"acceptance": ["web:e2e:N-018 physics", "web:test:simulation-cache"],
"dependencies": ["N-017"]
},
{
"id": "N-019",
"name": "Lighting and render",
"status": "BLOCKED",
"roadmapStatus": "planned",
"completedSlices": ["A-camera-light-world-scene-reader-partial", "A-main-light-world-properties-partial", "B-three-exposure-shadow-mapping-partial", "A-white-balance-integrity-gate"],
"blockedSlices": ["A", "B", "C", "D", "E"],
"acceptance": ["web:test:lighting-roundtrip", "web:e2e:N-019 Scene exposure"],
"dependencies": ["N-018"]
},
{
"id": "N-020",
"name": "Compositor",
"status": "BLOCKED",
"roadmapStatus": "planned",
"completedSlices": ["A-graph-resource-cycle-schema", "B-bounded-cpu-executor-partial", "C-image-operation-budget-cancel-partial", "D-unsupported-node-preservation-gate"],
"blockedSlices": ["A", "B", "C", "D", "E"],
"acceptance": ["web:e2e:N-020 CPU compositor"],
"dependencies": ["N-019"]
},
{
"id": "N-021",
"name": "Sequencer and audio",
"status": "BLOCKED",
"roadmapStatus": "planned",
"completedSlices": ["A-strip-resource-schema", "B-deterministic-move-trim-split-partial", "B-source-frame-seek", "C-runtime-codec-probe-gate"],
"blockedSlices": ["A", "B", "C", "D", "E"],
"acceptance": ["web:e2e:N-021 sequencer"],
"dependencies": ["N-020"]
},
{
"id": "N-022",
"name": "Tracking and masks",
"status": "BLOCKED",
"roadmapStatus": "planned",
"completedSlices": ["A-clip-track-plane-mask-schema", "B-revision-marker-mask-edit-partial", "B-source-hash-binding-validation", "C-browser-probe-solve-gate"],
"blockedSlices": ["A", "B", "C", "D", "E"],
"acceptance": ["web:e2e:N-022 tracking"],
"dependencies": ["N-021"]
},
{
"id": "N-023",
"name": "Assets, libraries and IO",
"status": "BLOCKED",
"roadmapStatus": "planned",
"completedSlices": ["A-catalog-asset-license-source-schema", "A-content-addressed-opfs-capability", "B-library-dependency-order-partial", "C-glb-export-usd-analysis-gates", "E-archive-path-ratio-budget"],
"blockedSlices": ["A", "B", "C", "D", "E"],
"acceptance": ["web:e2e:N-023 asset"],
"dependencies": ["N-022"]
},
{
"id": "N-024",
"name": "Editors and workflow",
"status": "BLOCKED",
"roadmapStatus": "planned",
"completedSlices": ["A-unified-area-region-context", "B-read-only-editor-manifest-partial", "C-selection-sync-revision-partial", "D-keymap-layout-budget-gates"],
"blockedSlices": ["A", "B", "C", "D", "E"],
"acceptance": ["web:e2e:N-024 editor"],
"dependencies": ["N-023"]
},
{
"id": "N-025",
"name": "Scripting and platform",
"status": "BLOCKED",
"roadmapStatus": "planned",
"completedSlices": ["A-default-deny-script-policy", "B-signed-manifest-permission-budget", "C-server-source-hash-job-gate", "D-platform-capability-report"],
"blockedSlices": ["A", "B", "C", "D", "E"],
"acceptance": ["web:e2e:N-025 script"],
"dependencies": ["N-024"]
},
{
"id": "N-026",
"name": "Release gate",
"status": "BLOCKED",
"roadmapStatus": "planned",
"completedSlices": ["A-machine-readable-parity-manifest", "A-dependency-status-validation", "B-chromium-runtime-evidence-gate", "B-chromium-smoke-partial", "C-performance-fault-provenance-gate", "C-performance-1M-malicious-input-partial", "C-release-package-notices-partial", "D-deterministic-manifest-serialization", "D-offline-reproducible-source-archive-partial"],
"blockedSlices": ["A", "B", "C", "D", "E"],
"acceptance": ["web:e2e:N-026 release", "web:test:browser-smoke", "web:test:release-package", "web:release:offline", "web:test:release-performance", "web:test:malicious-blends"],
"dependencies": ["N-015", "N-016", "N-017", "N-018", "N-019", "N-020", "N-021", "N-022", "N-023", "N-024", "N-025"]
}
]
}