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

View File

@@ -0,0 +1,209 @@
# Blender 5.2 Web 功能对标与全量迁移台账
更新时间2026-08-12
本文件以仓库内 `blender-5.2.0/source/blender/` 为基线,记录 Blender 功能域在 Web
项目中的迁移方式和唯一领取顺序。`completed_current_scope` 只表示声明子集通过验收,
不表示桌面 Blender 全量等价。任何未列入白名单的 operator、node、strip、physics
类型或数据块都必须返回机器可读阻断,不能由 Three.js 静默近似。
## 完成定义
一个功能只有同时满足以下条件,才能从 `planned` 转为 `done_current_scope`
1. 版本化协议和输入预算linked/missing/corrupt/cycle 等安全门有稳定错误码。
2. 修改 Blender Main而不是只修改 React/Three.js 状态;支持 undo/redo。
3. SceneIR/二进制 buffer/SceneDelta 可读回,保存、关闭、重开语义一致。
4. Blender 5.2 desktop 与 WASM fixture/golden 比较通过。
5. Chromium 主线程/OffscreenCanvas、Worker restart、OPFS quota、malformed input 和内存门通过。
6. UI 只发命令并消费权威快照;不支持输入有可诊断状态。
7. 导出前报告语义损失;不能映射时拒绝导出,不静默删除数据。
## 功能域矩阵
| Blender 5.2 功能域 | 当前 Web 状态 | Web 目标 | 后续任务 |
| --- | --- | --- | --- |
| `.blend` read/write、Main、undo、崩溃恢复 | `done_current_scope` | 浏览器本地 WASM + OPFS 权威闭环 | N-001/N-006/N-009/N-010 持续回归 |
| Scene/Object/Collection/Parent/Transform | `done_current_scope` | 本地 Main 编辑和增量视口 | N-007 持续扩 fixture |
| Mesh Object/Edit Mode | `done_current_scope` 有限 operator | 补选择历史、knife、rip、bridge、spin、normals、data transfer | N-007 后续矩阵 |
| Modifier/Depsgraph | `done` 能力矩阵,求值为有限白名单 | 扩 modifier family、GN、simulation 和循环/目标资源门 | N-002/N-012 |
| UV/Image/Material slot | `done_current_scope` | seam/unwrap/pack、多 UV、UDIM、paint image lifecycle | N-004/N-013/N-017 |
| Sculpt | `in_progress` 有界 CPU/Main stroke | PBVH、四笔刷 desktop 等价、Mask/Face Set、恢复和 UI | N-011 |
| Geometry Nodes | `in_progress` 协议/资源/cache | Blender lazy-function/field、实例、属性域和 group 闭包 | N-012 |
| Simulation Zone | `in_progress` cache 安全层 | desktop bake 消费,再启用可取消的浏览器 bake | N-012 |
| Shader Nodes | `in_progress` 七节点 Main 子集 | Mix/Mapping/TexCoord/Bump、受限 compiler、颜色空间 | N-013 |
| Object Action/FCurve/Timeline | `done_current_scope` | handle/tangent、channel/group、driver 安全子集 | N-014/N-024 |
| Armature/Pose/Constraint/Shape Key | `done_current_scope` 读取/求值 | edit bones、pose tools、weight paint、更多约束和驱动 | N-003/N-014/N-017 |
| NLA | `in_progress` Action Clip/reverse/repeat 子集 | 多轨混合、Transition/Meta、Animated Time、UI 和导出 | N-014 |
| Curve/Curves/Surface/Text/Metaball | `in_progress` reader/预览子集 | 数据块 IR、Main operator、tessellation 和保存闭环 | N-015 |
| Point Cloud/Volume/Hair | `in_progress` 摘要/资源门 | 有界数据读取、分块 buffer、视口和资源预算 | N-015 |
| Grease Pencil/Drawing | `planned` | layer/frame/drawing/stroke、onion skin 和基础 modifier | N-016 |
| Vertex/Weight/Texture Paint | `planned` | Main/PBVH image transaction、颜色管理和恢复 | N-017 |
| Rigid/Soft Body、Cloth、Fluid、Particles | `planned` | 优先消费 desktop bakeWASM 求值逐 family feature probe | N-018 |
| Camera/Light/World/Color Management | `metadata/viewport subset` | SceneIR/Three/WebGPU 映射、阴影、曝光和差异报告 | N-019 |
| Eevee/Cycles/Freestyle/Render Result | `planned` | WebGPU 实时子集Cycles/硬件后端采用服务端 Blender | N-019 |
| Compositor | `planned` | 受限 node graph + WebGPU/CPU executor完整图可服务端执行 | N-020 |
| Video Sequencer/Audio | `planned` | strip/time/edit proxycodec 能力探测;最终编码可服务端 | N-021 |
| Movie Tracking/Mask | `planned` | 数据/编辑协议和基础浏览器工具;重计算按能力门 | N-022 |
| Asset Browser/Library/Override | `partial` | catalog、preview、append/link/override、路径与来源沙箱 | N-023 |
| Import/Export formats | `GLB subset` | glTF/OBJ/PLY/STL/USD/Alembic 能力矩阵和 desktop round-trip | N-023 |
| Blender editors/workspaces/keymap | `partial shell` | View3D/Outliner/Properties/UV/Node/Graph/Dope/NLA/Spreadsheet | N-024 |
| Python Console/Text/Add-ons | `blocked_by_security` | 默认禁用;签名脚本沙箱或服务端 Blender绝不执行任意代码 | N-025 |
| Native window/GPU device/CUDA/Metal/HIP | `platform_not_equivalent` | 浏览器 API 适配或服务端执行,保持明确 capability report | N-025 |
| 国际化、无障碍、输入设备、帮助系统 | `partial` | locale/keymap/accessibility 和可测试的偏好设置 | N-026 |
## 连续执行顺序
必须先完成当前运行时语义,再扩大数据块和编辑器:
```text
N-011 PBVH Sculpt
-> N-012 GN/Simulation
-> N-013 Shader/compiler
-> N-014 NLA
-> N-015 非 Mesh 几何数据块
-> N-016 Grease Pencil
-> N-017 Paint/Weight
-> N-018 Physics/Simulation
-> N-019 Lighting/Render
-> N-020 Compositor
-> N-021 Sequencer/Audio
-> N-022 Tracking/Mask
-> N-023 Asset/IO
-> N-024 Editors/UI
-> N-025 Scripting/Platform adapters
-> N-026 全域发布门
```
UI 任务可以提前制作只读视图,但不得在对应 Main writer 和保存语义完成前开放编辑。
## N-015 非 Mesh 几何数据块
1. N-015-A`done_current_scope``NonMeshDataIR` 和 WNM schema 1 已覆盖 Curve、Curves、
Surface、Font、Metaball、PointCloud、Volume、Hair提供 stable ID、状态、属性 domain、
65,536 点 chunks、SHA-256、256 MB 场景预算和 1M 点性能门。
2. N-015-B`done_current_scope`native reader 已读取真实 PointCloud/Curves position、
radius、curve offset 和属性depsgraph 对 Curve/Surface/Font/Metaball 输出真实 mesh、UV、
材质槽和 source face mappingVolume 保持项目路径与 VDB grid/decoder 安全门。
3. N-015-C`in_progress`Curve/Surface control point/resolution、有界 cyclic/handle metadata、
bounded Poly Curve create/delete、Poly/Bezier conversion、Curve/Surface/Font/Metaball 数据块 rename、
Font body/geometry properties、逐字符样式/textbox、已有/packed VFont 四样式链接和
Metaball element Main command 已支持并通过 undo/save/reopen一维多 spline 创建/删除/重排、
批量 handle/cyclic 和二维 Surface U/V topology/order/rational weight transaction 已通过
undo/save/reopen 与 depsgraph 求值;多 handle 选择拖拽/gizmo UI 和任意外部路径字体导入仍推进。
4. N-015-D`in_progress`Three.js 主线程和 OffscreenCanvas Worker 共享 WNM chunks真实
PointCloud/Curves 可预览Point/Curve bounded raycast、Metaball proxy 与 evaluated mesh 分层;
左右 handle identity raycast/单 handle 轴向 gizmo 已完成WebGPU、跨对象 selection history、
多 handle 拖拽和 SceneDelta range patch 仍待完成。
5. N-015-E`in_progress`Blender 5.2 fixture、Chromium 双 renderer、depsgraph 真求值、GLB
拒绝/映射和 USD loss report、Main roundtrip、desktop golden、7 对象 GLB/USD round-trip、
1M binary gate、Worker restart 与 Chromium 真实 OPFS quota 已接入VDB renderer 仍是
当前阻断。后续浏览器验收仅覆盖 Chromium不配置 Firefox/WebKit。
专项字段、验收命令和停止条件见 `docs/status/N-015.md`
## N-016 Grease Pencil
1. N-016-ALayer/Frame/Drawing/Stroke/Point/Attribute schema 与最大 layer/frame/point 预算。
2. N-016-BMain create/remove/reorder layerinsert/remove framedraw/erase stroke transaction。
3. N-016-Cmaterial、onion skin、cyclic stroke、radius/opacity/color 和基础 modifier reader。
4. N-016-DChromium 主线程/Offscreen 的 3D current-frame stroke preview 已完成2D editor、
stroke/point selection、timeline/dope integration、gizmo 和 worker restart 继续推进。
5. N-016-Edesktop drawing hash、像素 golden、save/reopen 和 GLB/USD loss report。
## N-017 Paint 与权重
1. N-017-AVertex Color/Weight/Texture Paint stroke schema、PBVH/UV hit 和 brush 预算。
2. N-017-Bvertex color 与 vertex group Main transaction、normalize/limit/mirror 权重。
3. N-017-Cpacked/UDIM image tile transaction、色彩空间、dirty tile 和原子保存。
4. N-017-Darmature deform golden、seam bleed、mask/face selection 和 undo/redo。
5. N-017-EGPU texture upload/dispose、large image quota、malformed image 和 UI。
## N-018 Physics 与 Simulation
1. N-018-ARigid Body、Soft Body、Cloth、Fluid、Dynamic Paint、Particle/Hair 能力清单。
2. N-018-B每 family 的 settings/dependency/cache manifest目标对象和 collection 防循环。
3. N-018-C先消费 Blender desktop deterministic bakeframe seek 不允许复用错误帧。
4. N-018-D逐 family 探测 WASM solver初始化、线程或内存门失败时保持 bake-only。
5. N-018-E浏览器 bake start/cancel/commit、故障恢复、长任务进度和 100 帧 golden。
## N-019 灯光与渲染
1. N-019-ACamera、Light、World、View Transform、Exposure、Mist、Shadow 的完整 SceneIR。
2. N-019-BThree/WebGPU 映射和 capability report材质/灯光变化使用增量更新。
3. N-019-CEevee 有限特性矩阵、shadow map、transparent sorting、probe 和后处理。
4. N-019-DCycles/Freestyle/硬件 denoise 使用服务端 Blender job 协议与结果 hash。
5. N-019-Edesktop/Web 像素误差、色彩管理、设备丢失和 1M 三角形性能门。
## N-020 Compositor
1. N-020-ACompositor GraphIR、socket/link、image/render-layer 资源和 cycle 校验。
2. N-020-BTransform/Color/Alpha/Blur/Mix 等有限 node 的 CPU/WebGPU executor。
3. N-020-Cviewer/composite output、frame cache、tile budget 和 worker cancellation。
4. N-020-Dunsupported node 保留 metadata 并可提交服务端 Blender不删图继续执行。
5. N-020-Edesktop image golden、HDR/alpha/color-space 和 OOM/fault 门。
## N-021 Sequencer 与音频
1. N-021-AScene/Movie/Image/Sound/Effect/Meta strip schema、channel、range 和 proxy metadata。
2. N-021-BMain add/remove/move/trim/split、transition 和 modifier 白名单。
3. N-021-CWebCodecs/HTMLMedia capability probe、waveform/proxy 和精确 timeline seek。
4. N-021-D浏览器不支持的 codec/混音/编码提交服务端;资源路径必须沙箱化。
5. N-021-EA/V sync、frame hash、save/reopen、丢帧和损坏媒体测试。
## N-022 Tracking 与 Mask
1. N-022-AMovieClip、Track/Marker、Plane Track、Mask Layer/Spline/Point schema。
2. N-022-BMain marker/mask edit、selection、keyframe 和 camera solve metadata。
3. N-022-C浏览器 tracking feature probe完整 solve 可使用服务端 Blender。
4. N-022-DClip/Mask editors、overlay 和 compositor/scene resource binding。
5. N-022-Edesktop solve/error golden、媒体故障、保存和重开。
## N-023 Asset、Library 与 IO
1. N-023-AAsset catalog、tag、preview、author/license/source hash 和 OPFS index。
2. N-023-BAppend/Link/Library Override Main transaction、reload/relocate 和循环依赖门。
3. N-023-CglTF/OBJ/PLY/STL 首批本地 IOUSD/Alembic 按编译能力或服务端执行。
4. N-023-D每格式 import -> save -> reopen -> export -> desktop reimport 语义比较。
5. N-023-Ezip bomb、路径穿越、外部 URI、许可证和大文件流式预算。
## N-024 Editors 与工作流
1. N-024-A统一 Area/Region/Workspace context不复制 Main state。
2. N-024-BProperties、UV/Image、Node、Graph、Dope Sheet、NLA、Spreadsheet 的只读面。
3. N-024-C对应 writer 完成后开放编辑、selection sync、operator search 和 context menu。
4. N-024-DBlender-compatible keymap、可配置 shortcut、gizmo、drag preview/commit。
5. N-024-Edesktop/mobile layout、键盘/触控/笔、无重叠截图和 accessibility 门。
## N-025 Scripting 与平台适配
1. N-025-A默认拒绝任意 Python、Text autorun、driver expression 和 add-on 安装。
2. N-025-B可选签名脚本 manifest、权限、CPU/内存/时间预算和无网络沙箱。
3. N-025-C需要 CPython/native module 的作业走服务端 Blender结果绑定 source hash。
4. N-025-DCUDA/Metal/HIP/OptiX、native window、file watcher 等返回平台能力报告。
5. N-025-E恶意脚本、依赖混淆、逃逸、重放和审计日志发布门。
## N-026 全域发布门
1. 生成 machine-readable parity manifest每个 Blender family 为
`LOCAL_EXACT``LOCAL_BOUNDED``SERVER``BLOCKED`
2. 全部正例和阻断 fixture 在 Chromium 主线程/OffscreenCanvas、离线包和 Worker restart 下运行。
3. 100k/1M/10M 几何、4k/8k texture、100/1000 frame、长媒体和 simulation cache 基准。
4. `.blend`/image/media/script fuzz、OPFS quota、OOM、设备丢失、网络中断和恢复验证。
5. 许可证/source offer/SBOM、确定性包、文档状态与测试报告一致后才能发布。
## 默认验收入口
新增任务必须把专项命令接入 `web/package.json`,并至少执行:
```bash
source tools/web/emscripten-env.sh
cmake --build build_web_blender6 --target web_engine -- -j8
bash tools/web/install-web-engine-assets.sh
npm --prefix web run typecheck
npm --prefix web run lint
npm --prefix web test
npm --prefix web run test:authoring-roundtrip
npm --prefix web run test:capability-gates
npm --prefix web run test:e2e
```

View File

@@ -0,0 +1,196 @@
# Web Blender PBR 渲染实现与任务分解
更新日期2026-08-09
## 1. 目标和声明边界
目标是在浏览器 3D Viewport 中提供与 Blender Principled 工作流一致、可保存、可重开的
PBR 预览。材质参数必须来自 Blender Main经 SceneIR 传输并由主线程和 OffscreenCanvas
Worker 使用相同映射,禁止只修改 Three.js 临时状态。
当前能力称为 `physical-v1`,不是 Cycles、EEVEE 或 Blender Shader Node 的逐像素等价实现。
它保证的是有界 Principled 参数、线性色彩输入、sRGB 输出、ACES Filmic tone mapping、
World exposure、场景灯光和有界 PCF 阴影的一致 WebGL 渲染。未声明节点、谱渲染、光程、体积、
次表面、多次散射和离线 path tracing 必须继续结构化阻断或降级并给出 warning。
## 2. 权威数据流
```text
Blender Main
-> .blend writer/save/reopen
-> native blend reader
-> SceneIR MaterialIR/LightIR/WorldIR
-> transferable mesh buffers
-> shared pbr.ts mapping
-> main-thread WebGLRenderer
-> OffscreenCanvas render Worker
```
编辑链路为Inspector 控件 -> `setMaterialPrincipled` -> EngineWorker -> Blender Main 节点
socket -> 新 revision SceneIR -> 视口重建。任何新增参数必须同时通过命令校验、Main 写回、
reader、SceneIR parser、保存重开和双后端验收。
## 3. Blender 到 Web 的材质映射
| Blender 5.2 Principled 输入 | SceneIR | Three.js | 范围/说明 |
| --- | --- | --- | --- |
| Base Color | `baseColor` | `color` | 线性 RGBalpha 独立处理 |
| Roughness | `roughness` | `roughness` | `[0,1]` |
| Metallic | `metallic` | `metalness` | `[0,1]` |
| IOR | `ior` | `ior` | 当前限定 `[1,2.333]` |
| Specular IOR Level | `specularIORLevel` | `specularIntensity` | Blender 中性值 0.5 映射到 Three 中性值 1.0;高半区有界截断 |
| Transmission Weight | `transmissionWeight` | `transmission` | `[0,1]`,使用物理透射 pass |
| Coat Weight | `coatWeight` | `clearcoat` | `[0,1]` |
| Coat Roughness | `coatRoughness` | `clearcoatRoughness` | `[0,1]` |
| Emission Color | `emissionColor` | `emissive` | 线性 RGB |
| Emission Strength | `emissionStrength` | `emissiveIntensity` | `[0,1000000]` |
| Alpha | `alpha` | `opacity/transparent/depthWrite` | alpha blend 时关闭 depth write |
材质选择高亮保存原始 emission 和强度,取消选择后恢复,不再破坏自发光材质。所有网格、
实例和 LOD 都启用一致的 cast/receive shadow 标志。
## 4. 色彩、灯光和阴影
- Blender/SceneIR 数值按线性工作空间进入材质和灯光。
- WebGL 输出固定为 `SRGBColorSpace`tone mapping 固定为 `ACESFilmicToneMapping`
- `WorldIR.exposure` 按摄影 stop 转换为 `2 ** exposure`,并限制在 `[-8,8]`
- 无场景灯时使用低强度中性 Hemisphere fill 和有阴影的 key light确保空场景可检查。
- POINT、SUN、SPOT、AREA 均从 `LightIR` 创建。SUN/SPOT 根据 Blender 本地 `-Z`
方向创建真实 Three target不再错误地全部指向世界原点。
- SUN、SPOT、POINT 使用 `PCFShadowMap`,第一质量档为 1024 平方 shadow map并设置
有界 bias、normal bias、near/far。AREA 受 Three WebGL 能力限制,当前不声明阴影等价。
- 主线程与 Worker 暴露 `data-pbr-profile``data-tone-mapping``data-shadow-map`,供
自动化测试核对真实后端配置。
## 5. 资产和纹理边界
当前 SceneIR 已表达 UV、Image Texture、Normal Map、packed image 和资源状态,但纹理像素
进入 GPU 的链路必须继续遵守 EngineWorker/StorageWorker 权威模型。浏览器渲染器不得根据
`.blend` 内路径自行访问文件系统或网络。
PBR-007 已建立 `GPUTextureAsset` 只读载荷:`assetId``imageId`、SHA-256、MIME、尺寸、
颜色空间、用途、UDIM tile 和 `ArrayBuffer` 必须通过字节数、64 MiB 单图、16384 维度和
SHA-256 校验后才可进入 `ImageBitmap`/Three `Texture`。主线程与 Offscreen Worker 共享同一
`GPUTextureStore` 逻辑,载荷状态和拒绝字节数可观测;路径仍只能由 EngineWorker/StorageWorker
解析,浏览器不得访问 `.blend` 内路径。当前不把 context-loss 自动恢复、mipmap/anisotropy
策略和完整 UV Map 节点映射误报为完成。
## 6. 质量档和性能预算
| 档位 | 阴影 | DPR | 透射 | 目标 |
| --- | --- | --- | --- | --- |
| Low | 关闭或 512 | 1 | 关闭 | 低内存移动设备 |
| Medium | PCF 1024 | `min(device,1.5)` | 0.5 分辨率 | 默认移动/集显 |
| High | PCF 1024/2048 | `min(device,2)` | 全分辨率 | 默认桌面 |
`physical-v1` 当前实现 High 的有界子集。后续自动降档的输入包括 GPU tier、draw call、
transmission material 数、shadow caster 数、WASM/GPU 内存压力和帧时间 P95。质量切换只能
改变表现质量,不得改变 Main 或保存语义。
## 7. 任务分解与执行顺序
### PBR-001 协议与安全门done
- PBR-001-A扩展 `MaterialIR` 五个物理参数,保持旧 snapshot 可解析。
- PBR-001-B对比例参数验证 `[0,1]`,对 emission strength 设硬上限。
- PBR-001-C扩展 `setMaterialPrincipled`,所有字段保持 optional 兼容旧客户端。
- PBR-001-Dlinked material 继续拒绝写回NaN/Infinity 继续由 native 阻断。
### PBR-002 Blender Main authoringdone
- PBR-002-A自建 Principled 节点补齐五个 Blender 5.2 socket。
- PBR-002-B命令写入 Base/Roughness/Metallic/IOR/Specular/Transmission/Coat/Emission。
- PBR-002-Cnative reader 从真实 socket 读取并生成 SceneIR。
- PBR-002-DMain round-trip 和 save/reopen 核对每个物理参数误差小于 `1e-5`
### PBR-003 共享物理材质适配器done
- PBR-003-A`MeshPhysicalMaterial` 替代两条路径的 Standard/fixed material。
- PBR-003-B集中 clamp、默认值、透明深度策略和 selection emission 恢复。
- PBR-003-C主线程、Worker、实例和 LOD 使用同一映射。
- PBR-003-DWorker 恢复 UV、corner color 和多材质 triangle group。
### PBR-004 色彩管理和曝光done
- PBR-004-A线性输入、sRGB output、ACES Filmic。
- PBR-004-BWorld exposure 按 stop 映射并限制极端值。
- PBR-004-C通过 canvas profile 属性和像素非空门验证双后端。
### PBR-005 灯光和阴影done_current_scope
- PBR-005-APOINT/SUN/SPOT/AREA 类型映射。
- PBR-005-B修复 SUN/SPOT 方向 target。
- PBR-005-CSUN/SPOT/POINT PCF 阴影、bias 和 shadow camera 边界。
- PBR-005-D待补能量单位校准 fixture、级联太阳阴影、AREA 阴影替代和 caster budget。
### PBR-006 材质 Inspectordone_current_scope
- PBR-006-A增加 IOR、Specular、Transmission、Coat、Coat Roughness、Emission Strength。
- PBR-006-B应用命令保留 alpha 和 emission color。
- PBR-006-C待补材质槽选择、emission color、alpha mode、双面和 blend mode 面板。
### PBR-007 纹理像素闭环in_progressAssetPayload/解码已完成GPU 生命周期仍有安全门)
- PBR-007-Adone_current_scope`render-assets.ts` 定义 schema v1、SHA-256、MIME/尺寸/字节预算和稳定错误码App 从 `requestAsset` 生成载荷并同时写入 OPFS。
- PBR-007-Bdone_current_scopeBase Color/Emission 使用 sRGBNormal/Data 使用 non-color/linear主线程和 Worker 使用相同 `GPUTextureStore`
- PBR-007-Cin_progressImageBitmap 解码、旧 Texture dispose、状态回传已完成mipmap、anisotropy、GPU context-loss 恢复和全局显存预算仍未开放。
- PBR-007-DNormal Map 切线空间、UV Map 选择、wrap/filter 和缺失纹理 fallback。
- PBR-007-Ein_progress双后端同一载荷正例已覆盖context-loss/Worker crash 后重新上传仍属于 PBR-011 故障注入门。
### PBR-008 UDIM 与外部资源in_progressmanifest/阻断已完成,多 tile 采样未声明)
- PBR-008-Adone_current_scope`UDIMManifest` 校验 1001..1999、u/v 坐标、重复 tile、数量上限和 packed 状态;每个 tile 进入同一 SHA-256 载荷。
- PBR-008-Bin_progress当前只允许单 tile 安全上传,多 tile 返回 `UDIM_MULTI_TILE_UNAVAILABLE`尚未实现分页、LRU 和 GPU atlas/array 采样。
- PBR-008-Cdone_current_scopelinked library、外部路径和缺 tile 使用结构化门,禁止任意路径读取。
- PBR-008-Ddone_current_scopegenerated/sequence/movie 不生成静态 GPU 载荷,保持独立 capability。
### PBR-009 IBL 和 World Nodesin_progress有限 raster equirectangular IBL
- PBR-009-Ain_progresspacked PNG/JPEG equirectangular 环境可经 GPU 载荷、PMREMGenerator 生成反射环境HDR/EXR 解码尚未实现并返回 `IBL_FORMAT_UNSUPPORTED`
- PBR-009-Bin_progressWorldIR 已携带 environment image/strength/rotation 字段,当前只消费 strength旋转、跨场景缓存和完整 HDR 色域仍待安全门。
- PBR-009-Cdone_current_scope`backgroundVisible``scene.environment` 生命周期分离,旧 PMREM target 会释放。
- PBR-009-Ddone_current_scope任意 World Shader Node 不进入纹理上传,保持结构化阻断。
### PBR-010 GLB PBR 扩展done_current_scope
- PBR-010-A已导出 `KHR_materials_ior``transmission``clearcoat``specular``emissive_strength`,并保留无完整节点图时的有界 `imageIds` Base Color/Normal 路径。
- PBR-010-B桌面 Blender 再导入比较覆盖 8x8 packed texture、骨骼、shape key、动作和材质基础值。
- PBR-010-C不支持的 Blender 图返回 `SHADER_GRAPH_UNMAPPABLE`,禁止静默烘焙。
- PBR-010-Dadvanced extension fixture 已验证桌面 Blender 对 IOR、Specular IOR Level、Transmission、Coat 和 Emission Strength 的导入数值。
### PBR-011 性能、容错和跨浏览器planned
- PBR-011-A100k/1M 三角形下记录 CPU/GPU 帧时、draw call、shader compile 和显存。
- PBR-011-BChromium 主线程/OffscreenCanvas 的 ACES、transmission 与 shadow 差异矩阵。
- PBR-011-CWebGL context loss、Worker crash、纹理解码失败和 GPU OOM 故障注入。
- PBR-011-D自动降档必须可观测且不修改权威数据。
### PBR-012 高级渲染in_progress能力查询/阻断协议,运行时家族仍未声明)
- PBR-012-Adone_current_scope`queryRenderCapability` 明确返回 `WEBGPU_RENDERER_UNAVAILABLE`,没有 WebGPU renderer bundle 时不伪装为 WebGPU。
- PBR-012-Bdone_current_scope安全门BLOOM/SSAO/SSR/TAA/DOF/MOTION_BLUR 未有真实 pass 时返回 `POSTPROCESS_PASS_UNAVAILABLE`,未声明假实现。
- PBR-012-Cdone_current_scope安全门Volume/SSS 返回独立 `VOLUME_SHADER_UNAVAILABLE`/`SUBSURFACE_SHADER_UNAVAILABLE`,任意 Shader node 返回 `SHADER_NODE_UNSUPPORTED`
- PBR-012-D浏览器 path tracer 若引入,作为独立 Render backend不声称 Cycles 等价。
## 8. 当前验收
```bash
source tools/web/emscripten-env.sh
cmake --build build_web_blender6 --target web_engine -- -j8
bash tools/web/install-web-engine-assets.sh
npm --prefix web run typecheck
npm --prefix web run lint
npm --prefix web test
npm --prefix web run test:authoring-roundtrip
WEB_TEST_PORT=5209 npm --prefix web run test:e2e -- -g "PBR|Principled|local UV|packed raster|OffscreenCanvas"
```
发布前还要运行完整 E2E、跨浏览器、GLB Blender round-trip 和离线包门。桌面与移动截图必须
检查 canvas 非空、模型构图、材质高光/阴影可见、控件不遮挡以及页面无异常横向溢出。
## 9. 完成定义
一个 PBR 子任务只有在以下条件全部满足后才能标记 done协议字段可验证Main 可写;
保存重开等价;主线程和 Worker 可渲染;资源可释放;错误稳定且结构化;至少一个正例和一个
边界反例;桌面/移动像素与截图门通过;状态文档同步。仅识别 Blender socket、仅构造
SceneIR 或仅显示 UI 控件都不构成完成。

View File

@@ -0,0 +1,237 @@
# Web Blender 项目现状与后续连续任务
更新时间2026-08-12
## 1. 当前结论
项目已经形成可运行的 Web 三维编辑器工程骨架但还不是“Web 版完整
Blender”。当前成熟度是Web 基础设施和数据通路已成形,网格轻量化、骨骼
数据、packed image 和 GLB 已有专项实现;完整 Blender Depsgraph、完整 modifier
语义和 Blender 的全部工具仍不是当前承诺;已声明的 Mesh Edit、材质/UV、动画和对象层级
工作流均经过 Blender Main、撤销、原子保存和重开闭环。
浏览器端技术边界保持不变React 负责界面Three.js 负责全部实时渲染,
Blender C/C++ 子集编译为 WASMOPFS 保存大文件IndexedDB 保存小型元数据。
SQLite WASM 和 Bitbybit/OCCT 均不在当前依赖范围内。Three.js、WASM 和其他
运行时资源全部使用项目本地文件,不依赖 CDN。
## 2. 已完成并有测试覆盖的能力
| 能力 | 当前证据 | 状态 |
| --- | --- | --- |
| React + Blender 风格工作区壳 | Topbar、Viewport、Outliner、Properties、Timeline、状态栏和截图 golden | 已完成基础壳 |
| Three.js 本地渲染 | 本地 vendor、SceneIR 适配、选择/相机/网格显示 | 已完成基础链路 |
| Worker/C ABI/WASM 启动 | EngineWorker、错误协议、内存统计、离线 manifest 哈希 | 已完成 |
| `.blend` 数据读取 | 场景、对象、Mesh、非 Mesh 摘要、材质、图片、相机、灯光、动画和稳定 ID | 已完成已声明子集 |
| OPFS/IndexedDB | 项目、快照、LOD、SHA-256 资产、原子保存 journal 和 Worker 重启恢复 | 已完成当前存储链路 |
| Decimate/LOD | native Collapse、多比例 golden、边界和非流形专项、LOD 缓存发现 | 已完成已声明子集 |
| 蒙皮轻量化 | 四权重重映射、归一化、bind matrix 保留、shape key 重映射和策略阻断 | 已完成专项链路 |
| Blender 变形 golden | Blender 5.2 shape key + armature 基准及 Web 误差检查 | 已完成当前 fixture |
| Packed image | PackedFile、双 tile UDIM、generated、linked library、损坏签名、路径沙箱和 OPFS SHA-256 去重/重发现 | 已完成当前资源矩阵 |
| GLB | 本地导出、严格解析,以及 Blender 5.2 回导比较网格/PBR/纹理/morph/skin/animation | 已完成当前 SceneIR 子集 |
| Modifier/undo | modifier enable 状态、命令 revision、undo/redo 和专项 smoke | 已完成基础命令链路 |
| Mesh Edit | 点/边/面选择Merge/Dissolve/Extrude/Inset/Bevel/Loop CutMain 保存重开 | 已完成当前操作集 |
| 材质与 UV | 材质槽/面分配、UV Map、planar/cube unwrap、限定 Principled/Image/Normal 图、图片打包 | 已完成当前节点集 |
| 动画编辑 | TRS 关键帧、Timeline/Dope Sheet、插值、Action 选择和约束开关/影响 | 已完成当前操作集 |
| 对象层级 | Parent/Unparent、Collection、rename、Join/Separate、Apply Transform、Origin | 已完成当前操作集 |
| SceneIR 扩展 | tangent/split normal/seam/sharp/vertex group/attribute/UVnative WBG1、资源 Delta 和 mesh byte-range patch | 已完成当前数据契约 |
| Sculpt 属性 | Mask/Face Set 的 SceneIR、WBG1 header v2、Delta、Main、undo 和保存重开 | 已完成属性子集;有界四笔刷 Main stroke 已开放PBVH/完整求值未开放 |
| Simulation cache | Blender 5.2/source blend/hash/frame manifest、OPFS 内容寻址、schema 6、重启逐帧复验 | 已完成缓存安全层Simulation evaluator 未开放 |
| Shader Main | RGB/Value/Principled/Image Texture/Normal Map/Output 整图事务、资源门和保存重开 | 已完成六节点写回子集;任意图/编译仍阻断 |
| NLA Main | 单对象 Action Clip 整栈写回、SceneIR 重开和 native frame 求值 | 已完成有限 Action Clip 子集 |
| 非 Mesh 数据块 | Curve/Surface/Font/Metaball Main 权威写回与保存重开、真实 evaluated mesh、PointCloud/Curves/Hair 属性、WNM chunks、双 viewport、desktop golden 与 7 对象 GLB/USD round-trip | 有界本地切片 in_progressVDB renderer、完整 topology editor/跨对象 history/range patch、外部字体导入、跨浏览器 OPFS 性能仍阻断 |
## 3. 部分完成或仍有边界的能力
| 能力 | 已达到 | 尚未达到 |
| --- | --- | --- |
| Native Blender Depsgraph | Blender 5.2 full loader、ID 注册、viewport graph、tagged/frame 求值、复杂 rig 和 100 次重复门已在 wasm32 通过 | 新增 Blender 类型时继续扩大矩阵 |
| Modifier dependency graph | Blender 5.2 类型 086 已完整识别27 类 native 白名单,其余 60 类身份保留并结构化阻断 | 后续扩白名单必须逐类增加 Blender golden不属于当前矩阵完成条件 |
| 骨骼与层级 | Bone/parent/rest/pose、权重、joint ID、非单位 bind、四骨、父级缩放、IK、Copy Rotation 和 1/5/10/15 golden 已通过 | 新约束类型属于增量 fixture |
| GLB round-trip | Web 严格解析与 Blender 5.2 importer 双门已覆盖网格、变换、PBR、PNG/sRGB、sampler、morph、双骨骼 skin 和动画帧 | 新增相机/灯光、插值和材质扩展时继续扩大独立摘要 |
| `.blend` 保存 | 场景状态、modifier、Mesh Edit、材质/UV、动画/约束和对象层级命令均写 Main历史、原子保存、快照和日志重放可恢复 | skin/shape-key decimate 保持结构化阻断 |
| Blender UI 对标 | 默认区域、当前 Mesh Edit、raycast、多选、拖拽 gizmo、层级/材质/动画面板和核心快捷键已闭环 | Sculpt、Geometry Nodes 编辑器等未声明能力继续结构化阻断 |
| 未声明能力扩展 | 四项协议/安全门均已细分Sculpt 属性/有界 Main stroke、Simulation cache、六节点 Shader Main 和 Action Clip NLA Main 已有正例 | PBVH、GN lazy-function/Simulation 求值、其余 Shader Node、Web compiler、完整 NLA/UI/GLB 图映射仍 planned/结构化阻断 |
| 非 Mesh 数据块 | 1M 点 reader budget、ID_MB/PT/VO/VF 注册、Main control-point/topology/body/element/Font style link commands、一维多 spline 与批量 handle/cyclic、二维 Surface topology transaction、undo/save/reopen、真实 evaluated mesh、WNM SHA chunks、handle raycast/gizmo、7 对象 GLB/USD 和 Chromium quota | 多 handle 选择拖拽/gizmo UI、跨对象 history/range delta、新外部字体导入、VDB renderer/voxel upload 仍 planned/阻断 |
| 大场景 | Worker 二进制解码、per-mesh/range transferable、linked Mesh 实例化、LOD/OPFS cache、frustum culling、能力门 OffscreenCanvas Worker、100k/1M 内存门 | 超出当前 WBG1 的网络式渐进流送仍属于后续性能扩展 |
| Decimate 全对标 | Collapse 的当前网格集合已覆盖,错误路径结构化 | Un-Subdivide、Dissolve、所有 delimiter/权重/对称组合及大模型性能矩阵未完成 |
## 4. 关键安全门
`web_engine_evaluate_depsgraph` 现在直接执行 Blender 5.2 native loader、viewport
Depsgraph、tagged update 和 frame change。wasm32 DNA 重建器会递归恢复 64-bit
字段的 8-byte ABI 对齐,避免 `Scene.master_collection` 等成员错位Web ID 注册表
覆盖 Scene、Collection、Object、Mesh、Material、World、NodeTree、Camera、Light、
Lattice、Curves 和 Grease Pencil。
空场景、基础场景和骨骼/Shape Key 场景均返回 `BlenderDepsgraph/EVALUATED`,并通过
Blender 5.2 变形 golden。任何 loader、graph 或输出结构错误仍必须返回结构化错误,
禁止伪造 Three.js 求值。
## 5. 后续连续执行顺序
以下任务按顺序领取。一个任务的验收未通过,不得把下一个任务标记为完成。
### N-001 恢复 native Depsgraph 安全求值(已完成,转入回归)
1. 建立最小 `empty/basic/rigged` full-loader fixture单独验证 Main 所有权和释放。
2. 补齐 headless 初始化闭包ID/CPP type、task scheduler、RNA 必要子集、NodeTree
数据类型、sequencer modifier 和 image runtime禁止链接桌面 WindowManager/GPU。
3. 修复 embedded NodeTree 的 ID/DNA 读取,加载阶段不得再产生 invalid embedded ID。
4. 分别验证 graph build、tagged update、frame change、graph free 和 Main free。
5. 连续求值 100 次,检查 live handles、WASM heap 增长和 Worker 重启恢复。
6. 重新启用 native capability并将重复求值和 golden 纳入持续回归。
验收:三个 fixture 无 trap最终 positions/indices 合法rigged 最大/RMS 误差通过;
重复求值无持续泄漏。当前 fixture 已满足frame/pose 回归已恢复,完整类型矩阵继续由
N-002/N-003 覆盖。
### N-002 Modifier/Depsgraph 能力矩阵(已完成)
1. 建立 Blender modifier 类型清单和 Web 支持矩阵;原生报告已先覆盖 UID、堆栈顺序、模式开关、跨对象依赖边、`EVALUATED/DISABLED/BLOCKED` 以及稳定错误码/建议。
2. 按数据依赖加入 Generate、Deform、Physics、Nodes 类,不按 UI 名称盲目链接;当前 27 类进入 native 白名单。simulation zone 无确定缓存时使用专项阻断deterministic cache、更多节点、GP 和其余家族属于未来逐批扩白名单,不影响矩阵契约完成。
3. 对 modifier 顺序、viewport/render/edit/cage 开关、对象 target 和循环依赖做 golden当前 category golden 已覆盖顺序/模式/target专项 fixture 已覆盖双对象循环和缺 target。
4. modifier 参数编辑、apply、undo/redo、保存和重新打开必须使用同一 revision 协议。
5. 不支持的 modifier 返回类型、对象、参数和建议处理方式,不得静默跳过。
验收:白名单内结果与 Blender 5.2 golden 一致;白名单外全部结构化阻断。
### N-003 骨骼、Shape Key 与变形误差扩展
1. 覆盖多骨骼、多权重、非单位 bind matrix、父级缩放和多个 shape key。
2. 覆盖关键帧区间、Pose constraint、IK 前后的每帧矩阵和最终顶点。
3. 验证简化前后 joint 映射、权重归一化、shape key 相对基准和误差预算。
4. Blender desktop golden 与 native WASM 同时比较Three.js 消费同一通过校验的 SceneIR/geometry buffer并由跨浏览器非空像素门验证呈现链路。
验收:每个 fixture 同时满足拓扑、层级、bind matrix、最大误差和 RMS 误差门。
### N-004 Packed image 完整覆盖
1. 单 PackedFile、PackedFile 列表、UDIM/tile、generated image 和 library-linked resource 矩阵已覆盖。
2. 校验 MIME、原始文件名、相对路径、SHA-256、重复资产去重和损坏字节错误。
3. packed 数据进入 OPFS content-addressed asset并验证刷新后可重新发现。
当前验收:`npm --prefix web run test:image-resources`,以及资产 E2E。SHA-256 去重、路径不可
逃逸项目目录和 OPFS Worker 重启发现均已覆盖。
### N-005 GLB Blender 级 round-trip当前 SceneIR 子集已完成)
1. 扩展材质、纹理 sampler/色彩空间、morph、skin、animation interpolation 和 hierarchy。
2. 保留当前 Web 严格解析比较作为快速门。
3. 使用桌面 Blender 5.2 导入导出的 GLB生成独立语义摘要。
4. 比较 node parent、mesh primitive/material、image、skin/joint/inverse bind、动画 channel。
5. warning 分成阻断错误、可接受近似和信息,不允许一条字符串混合多种严重度。
验收Web 快速门和 Blender 再导入门均通过;所有差异进入机器可读报告。当前命令为
`npm --prefix web run test:glb-blender-roundtrip`,已覆盖显式 sampler、sRGB、morph、双骨骼
层级/权重和帧 1/25 动画。
### N-006 `.blend` 真正回写与恢复
1. 把对象/层级/modifier/mesh/armature/animation 命令写入权威 Blender Main。
2. 实现内存 `.blend` 写出、取消、OOM、OPFS 原子替换和校验和。
3. 保存后清空 Worker重新打开文件并比较 SceneIR、Depsgraph 和 GLB 摘要。
4. 将 undo/redo 与保存点、自动保存和崩溃恢复统一到 revision 日志。
当前协议命令集已覆盖 frame/visibility/modifier、普通 decimate 的 Main 写入、内存保存和清空
后重开比较;标准 write-file callback 不使用 undo MemFile动画与 Pose constraint 已通过两代
写出后 animation 1/5/10 与 pose 1/5/10/15 帧等价回归。历史栈恢复 MainOPFS 以 staging/journal/manifest/SHA-256 原子提交,
并在两个故障点完成新 Worker 恢复。skin/shape-key decimate 继续以
`BLEND_WRITE_REQUIRES_MAIN_AUTHORITY` 阻断N-007 的 primitive/duplicate/delete/TRS/顶点平移
已复用同一 Main/history/save 契约。
验收:编辑不再只存在于 JSON snapshot已支持命令重新打开 `.blend` 后语义一致,
其余命令必须明确阻断,不能伪造成功。
### N-007 建模和 UI 对标扩展
当前验收切片已完成 primitive、duplicate、delete、TRS、点/边/面选择、Merge、Dissolve、
Extrude、Inset、Bevel、Loop Cut、raycast、多选、拖拽 gizmo、层级/Collection/Join/Separate、
Apply Transform/Origin 和核心快捷键,并通过 Main/history/save/reopen 门。
### N-008 质量与发布门
100k/1M 性能与内存、Worker 故障、OPFS quota、恶意 `.blend`、许可证、本地依赖和离线
dist 门已完成。N-008 记录的多引擎结果只作为历史证据;后续验收统一以 Chromium 为唯一
浏览器基线。
### N-009 快照、日志与崩溃重放
Schema 6含 Simulation manifest、SHA-256 快照保留、命令阈值、revision 排序、损坏隔离、保存点清理,以及提交后
命令重放已完成。验收见 `docs/status/N-009.md`
### N-010 可复现离线交付
确定性二进制包、对应源码包、source offer、license/notice、文件 manifest 和顶层
`SHA256SUMS.txt` 已纳入连续两次生成摘要一致门。验收见 `docs/status/N-010.md`
### N-011 至 N-014 未声明能力扩展(有限子集 in_progress
Sculpt、Geometry Nodes/Simulation、Shader Node 图和 NLA 的详细任务分解、依赖顺序、
协议字段、稳定错误码、停止条件和默认验收命令统一维护在
`docs/UNDECLARED_CAPABILITIES_EXECUTION_PLAN.md`。当前四项均进入
`in_progress`:四项都已超过纯协议阶段,但只开放经过 native/存储闭环验证的有限切片:
Sculpt Mask/Face Set 与有界 Main stroke、GN 外部资源门与 deterministic cache 存储、七节点
Shader Main 写回(含有限 Math和常量 PBR 映射、单对象 Action Clip NLA Main/Depsgraph reverse/repeat 求值。详细完成项和 planned 项见专项计划;
这些正例不扩大到 PBVH Sculpt、任意 GN/Simulation、任意 Shader Node/Web compiler 或完整 NLA。
Blender 5.2 全域功能矩阵、浏览器/服务端边界和 N-015 至 N-026 后续顺序见
`docs/BLENDER_5_2_WEB_FEATURE_PARITY.md`
### N-015 非 Mesh 几何数据块(有界本地切片 in_progress
`NonMeshDataIR`、native SDNA reader、Main 权威 Curve/Surface/Font/Metaball 写回、bounded Poly Curve create/delete、Poly/Bezier conversion、非 Mesh 数据块 rename、Curve 有界 topology/Font geometry、undo/redo、
save/reopen、真实 legacy evaluated mesh、PointCloud/Curves 属性和 WNM 二进制分块已经接通。
主线程与 OffscreenCanvas 消费同一 chunksGLB 只接受真实 evaluated meshUSD 提供
`UsdGeomMesh``UsdGeomPoints``UsdGeomBasisCurves``OpenVDBAsset` 的机器可读映射/损失。
VDB renderer、完整 Curve handle/cyclic topology editor、二维 Surface mutation、跨对象 history/range patch、新字体导入和跨浏览器 OPFS quota 仍按
`docs/status/N-015.md` 的阻断项推进。
### PBR-001 至 PBR-012 模型物理渲染(核心切片 done_current_scope资产/高级渲染安全门 in_progress
`physical-v1` 已完成扩展 Principled 参数的 Main 写回/保存重开、双后端
`MeshPhysicalMaterial`、ACES/sRGB、World exposure、场景灯方向和有界 PCF 阴影。PBR-007
现已接通 SHA-256 GPU 纹理载荷、颜色空间、尺寸预算、ImageBitmap 解码和双后端状态PBR-008
现已接通 UDIM manifest/缺失资源/多 tile 结构化阻断PBR-009 已支持有限 packed raster
equirectangular PMREM/IBL 生命周期PBR-012 已提供 WebGPU、任意 Shader、Volume/SSS 和
高级后处理的能力查询门。HDR/EXR、多 tile 采样、context-loss 恢复、真实 WebGPU renderer、
高级 pass 和未声明 Shader 家族仍不得声称完成。架构映射、细分状态、停止条件和验收命令统一维护在
`docs/PBR_RENDERING_IMPLEMENTATION_PLAN.md`
## 6. 当前验收命令
```bash
source tools/web/emscripten-env.sh
cmake --build build_web_blender6 --target web_engine -- -j8
bash tools/web/install-web-engine-assets.sh
bash tools/web/check-dual-engine.sh
node tools/web/run-web-engine-smoke.mjs
npm --prefix web run typecheck
npm --prefix web run lint
npm --prefix web test
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:main-roundtrip
npm --prefix web run test:authoring-roundtrip
npm --prefix web run test:modifier-goldens
npm --prefix web run test:modifier-cycle
npm --prefix web run test:modifier-matrix
npm --prefix web run test:image-resources
npm --prefix web run test:glb-blender-roundtrip
npm --prefix web run test:depsgraph
npm --prefix web run test:collapse-ratios
npm --prefix web run test:topology-collapse
npm --prefix web run test:capability-gates
npm --prefix web run test:simulation-cache
npm --prefix web run test:release-performance
npm --prefix web run test:malicious-blends
npm --prefix web run test:browser
npm --prefix web run release:offline
WEB_TEST_PORT=5193 npm --prefix web run test:e2e # 5173 被占用时指定可用端口
WEB_TEST_PORT=5193 npm --prefix web run test:e2e -- -g "N-015|non-mesh"
```
`test:depsgraph` 默认执行 `DEPSGRAPH_REPEAT=100`;本地快速迭代可显式设置为较小的
正整数,但提交前必须使用默认值。
每次完成 N-001 至 N-015 的一个任务切片,都必须更新本文件、对应 `docs/status/`
记录、fixture/golden 和验证输出。

View File

@@ -0,0 +1,318 @@
# 未声明 Blender 能力执行计划
更新时间2026-08-09
本计划把当前未声明的 Sculpt、Geometry Nodes/Simulation、任意 Shader Node 和完整
NLA 拆成四个可独立验收的连续任务。每个任务都必须满足Blender Main 是唯一写入
权威SceneIR 只承载通过校验的数据Three.js 不执行 Blender 语义;不支持或不确定
的输入返回稳定的结构化阻断。任务没有通过自己的 desktop golden、WASM golden、保存
重开和故障门前,不得扩大下一阶段的能力白名单。
## 总体依赖
| 顺序 | 任务 | 依赖 | 交付物 |
| --- | --- | --- | --- |
| 1 | N-011 Sculpt 子集 | MeshIR 属性、BMesh/Main history、N-003 变形门 | 固定采样笔刷事务、Mask/Face Set、可重开的 Sculpt fixture |
| 2 | N-012 Geometry Nodes 与 Simulation | Modifier graph、NodeTree reader、N-001 Depsgraph | 可证明的字段/实例闭包、内容寻址 simulation cache |
| 3 | N-013 Shader Node 图 | MaterialIR、图片/色彩空间、W-080 节点写回 | 校验过的 ShaderIR、受限图编译与桌面材质 golden |
| 4 | N-014 NLA | Action/FCurve、frame/pose 求值、N-006 保存日志 | Strip/Track 子集、跨 Action 评估和恢复 golden |
共享基础工作在 N-011 前先完成capability manifest、协议版本、资源预算、错误码
注册表、golden runner 模板和不支持输入 fixture。它们不改变任何既有白名单。
## N-011 Sculpt 子集
### 目标边界
第一版只承诺单对象、单用户 Mesh、Object Mode 到 Sculpt Mode 的受控笔刷事务。支持
Draw、Inflate、Grab、Smooth 四种确定性笔刷,固定半径/强度/对称轴和离散 stroke
采样;支持每顶点 Sculpt Mask 与每面 Face Set 的读写。多分辨率、Dyntopo、Voxel Remesh、
cloth/pose/elastic deform、纹理笔刷、破坏性拓扑笔刷和雕刻贴图在本任务中继续阻断。
### 分解
1. **N-011-A 能力和资源门**
- 增加 `SCULPT_STROKE``SCULPT_MASK``SCULPT_FACE_SET` 命令类型和 payload schema。
- 校验 object/mesh 单用户、可编辑、无 linked library、无 shape-key/激活 modifier
拓扑冲突;失败码分别为 `SCULPT_MESH_NOT_SINGLE_USER``SCULPT_TOPOLOGY_UNSUPPORTED`
`LINKED_DATA_MUTATION_BLOCKED`
- 建立 stroke 点数、总位移、影响顶点数和每 revision 内存预算;超限不得部分提交。
2. **N-011-B MeshIR 属性**
- SceneIR 增加 `sculptMask`vertex float32`faceSets`face int32、active
face set、mask revision 和 attribute domain。
- WBG1 增加可选 mask/face-set字段Worker 校验长度、有限值和域范围;旧 WBG1
没有这些字段时保持兼容。
- `MeshGeometryDelta` 支持上述属性的范围 patch并将属性变化计入资源 hash。
3. **N-011-C Blender Main 笔刷事务**
- 在 native Main writer 中建立一次 stroke 的临时 BMesh/PBVH 查询,按固定顺序处理
sample所有顶点写入在一个 history transaction 内完成。
- Draw/Inflate 使用命中法线方向Grab 使用投影平面位移Smooth 使用固定邻接
平均;算法参数和 Blender 5.2 desktop script 必须相同,不能由 Three.js 重新实现。
- 每次 stroke 完成后更新 normals、split normals、tangents、bounds 和 SceneDelta。
4. **N-011-D UI 与恢复**
- 增加 Sculpt Mode、brush selector、radius/strength、mask/face-set 操作和单击/拖拽
stroke 收集;界面只发送 sample不保存独立 Three 状态。
- undo/redo、autosave、OPFS journal、Worker 重启和 `.blend` 下载都使用同一命令。
- linked Mesh、shape-key topology、modifier evaluated mesh 必须显示结构化阻断而非灰掉后
静默忽略。
5. **N-011-E 验收**
- 新 fixture平面、非均匀变换 Mesh、带 seam/sharp、带 mask/face set 的 1k/50k 网格。
- desktop golden 比较 positions、normals、mask、face sets、bounds门限为 max/RMS
误差和精确属性 hash。
- 多帧相同 stroke、重复打开、取消中途 stroke、OOM 和 malformed payload 都有测试。
- 命令:`npm --prefix web run test:sculpt-goldens``test:sculpt-roundtrip`
`test:sculpt-faults``test:sculpt-e2e`
### 停止条件
无法在 wasm32 headless 环境稳定初始化 PBVH/笔刷依赖时PBVH Sculpt 能力保持 `BLOCKED`
当前有界 native Main CPU stroke 只能作为独立有限子集 C1a 验收,不能满足 C1、不能声明
Blender PBVH 等价,也不能借由 Three.js 近似扩大能力。
## N-012 Geometry Nodes 与 Simulation
### 目标边界
先扩大无时间状态的字段和实例闭包,再处理 Simulation Zone。第一阶段承诺 Group Input/
Output、Transform Geometry、Set Position、Join Geometry、Separate Geometry、Realize
Instances、Store/Named Attribute、基础数学/比较/选择字段;不承诺任意 node group、外部
对象资源、Python node、硬件依赖或隐式时间状态。
### 分解
1. **N-012-A GraphIR 和校验器**
- 建立 node UUID、bl_idname、socket type/domain/default/link、group interface、
node group 引用和 field dependency 的版本化 `GeometryNodeGraphIR`
- 解析时检查 duplicate UUID、悬空 link、socket 类型/域不匹配、group recursion 和
anonymous attribute 生命周期,错误码包含 `GN_SOCKET_TYPE_MISMATCH`
`GN_GROUP_RECURSION``GN_EXTERNAL_RESOURCE_MISSING`
- 将 modifier stack order、viewport/render/edit 开关和 graph hash 纳入 depsgraph report。
2. **N-012-B 无时间字段求值**
- native 只按 Blender lazy-function/field 语义执行声明闭包;输出 MeshIR 的 topology、
instances、attributes 和 bounds。
- 每个 node family 单独 desktop golden域转换point/edge/face/corner/instance
有固定 fixture不允许把 unsupported socket 当常量 0。
- Three.js 只消费 evaluated SceneIR未求值 graph 返回 `METADATA_ONLY``BLOCKED`
3. **N-012-C 实例和外部依赖**
- 定义 object info、collection info、image/volume 等外部资源的 stable ID 和沙箱检查。
- 先支持同文件、非循环、只读外部对象linked library、循环 collection、缺失对象和
非确定性数据返回 `GN_EXTERNAL_RESOURCE_MISSING``GN_DEPENDENCY_CYCLE`
- 实例变换和属性必须能进入 WBG1/GLB 摘要,不能在 Web 端展开后丢失 ID 语义。
4. **N-012-D Simulation cache**
- 定义 `SimulationCacheManifest`graph hash、source blend hash、frame range、inputs
hash、Blender version、schema、每帧 byte range 和 SHA-256。
- 只接受桌面 Blender 生成并校验过的 deterministic bakeWorker 按 OPFS content-addressed
方式读取frame seek 不得隐式重新计算。
- cache 缺帧、hash 不符、版本不兼容、quota/OOM 和损坏字节都必须可恢复地阻断;禁止返回
上一帧冒充当前帧。
5. **N-012-E 验收**
- graph fixtures 覆盖每个支持 family、字段域、实例、group recursion、循环依赖和
modifier 开关simulation fixtures 覆盖 1/5/10/25 帧、seek、cache 重启和损坏。
- 命令:`test-gn-goldens``test-gn-graph-validation``test-simulation-cache`
`test-gn-e2e``test-gn-blender-roundtrip`
### 停止条件
Blender lazy-function runtime 或 simulation cache 不能在 wasm32 完整初始化时,只扩大
metadata/graph validation保持现有 simulation structured block不得把输入几何复用成
“求值成功”。
## N-013 Shader Node 图
### 目标边界
把当前 Principled/Image/Normal/UV 读写子集升级为可验证的 ShaderIR 图,而不是承诺任意
Blender 节点。首批支持 RGB、Value、Mix、Math限定运算、Mapping、Texture Coordinate、
Image Texture、Normal Map、Bump、Principled、Material OutputVolumetric、OSL、任意
自定义 shader、Cycles-only 节点和未实现色彩管理继续阻断。
### 分解
1. **N-013-A ShaderIR**
- node UUID/type/label/location、typed sockets/defaults, links、material output、
image/sampler/color-space、group boundary 和 graph hash 全部版本化。
- graph validator 检查单输出、无环、socket 类型、颜色/向量隐式转换、未连接输入和
texture resource错误码包含 `SHADER_NODE_UNSUPPORTED``SHADER_GRAPH_CYCLE`
`SHADER_SOCKET_TYPE_MISMATCH`
2. **N-013-B Blender Main 写回**
- 为每个支持 node family 建立最小 native node/socket/storage 注册和 create/update/remove
command操作在一个 Main history transaction 中完成。
- linked material、library image、packed asset 和 color-management profile 经过同一
资源/路径沙箱;无法写回的图返回稳定阻断。
3. **N-013-C Web 消费和编译**
- 从 ShaderIR 生成受限 Three.js material/GLSL 或 WebGPU backend编译结果必须带 graph
hash 和 capability report。
- Web 端不能把未知节点删掉后继续显示“成功”;未知节点显示阻断并保留原图 metadata。
- 透明、alpha clip、normal/tangent、sRGB/Non-Color 和 sampler 状态分别测试。
4. **N-013-D 导出和回导**
- GLB 只导出能映射到 glTF PBR 的闭包;不可映射节点进入机器可读 warning/error不能
静默烘焙。
- Blender desktop 再导入比较节点摘要、材质值、纹理色彩空间、sampler 和渲染摘要。
5. **N-013-E 验收**
- 命令:`test-shader-graph-goldens``test-shader-node-roundtrip`
`test-shader-compile``test-shader-glb-roundtrip``test-shader-faults`
- 每个 node family 至少一个正例、一个错误图和一个 save/reopen fixture图 hash、
asset hash、desktop/Web 语义报告必须可复现。
## N-014 NLA
### 目标边界
在现有 Action/FCurve 子集上增加单个对象的 Track/Strip、Action Clip、frame range、
scale/repeat、blend in/out、influence、extrapolation 和 Replace/Add/Multiply/Combine
四种有限混合。Drivers、完整 transition strips、meta strips、time warp 和复杂约束驱动
仍然单独阻断。
### 分解
1. **N-014-A AnimationIR 扩展**
- 增加 Action、slot、NlaTrack、NlaStrip、strip source range、scene range、blend/extend、
muted/solo/active 和 stable owner IDs。
- 校验 track/strip 排序、重叠、负 scale、repeat、循环引用和 FCurve path compatibility。
2. **N-014-B Blender Main 编辑**
- 新增 track/strip create/remove/move/resize/blend/solo/mute commands全部进入 Main
historyAction 选择和已有 keyframe 命令共享 revision。
- 保存后重新打开必须恢复 strip UUID、Action assignment、active track 和 frame state。
3. **N-014-C 原生求值**
- 每帧交给 Blender Depsgraph/NLA evaluator输出 object/bone matrices、FCurve values 和
evaluated MeshIRThree.js 不自行混合 Action。
- 覆盖 strip 边界、负向播放、repeat、blend overlap、constraint order 和 frame seek。
4. **N-014-D UI 与导出**
- Dope Sheet 增加 NLA track/strip lane、拖动边界、blend handles、mute/solo、Action menu。
- GLB 导出只承诺可线性展开的 clip不可展开的 NLA 结构返回 warning/error 并保留摘要。
5. **N-014-E 验收**
- 命令:`test-nla-goldens``test-nla-frame-evaluation``test-nla-roundtrip`
`test-nla-faults``test-nla-glb-roundtrip`
- 以 frame 1/5/10/15/25 比较 transforms、pose matrices、vertex max/RMS error、strip
metadata 和 save/reopen hash。
## 共享发布门
四个任务完成前,每个新能力必须补齐:
1. SceneIR/WBG schema 和 capability manifest 的版本迁移。
2. native Main、undo/redo、save/reopen、OPFS journal、Worker restart 和 malformed input。
3. Blender 5.2 desktop golden、WASM golden和 Chromium 主线程/OffscreenCanvas 非空像素。
4. 100k/1M 内存预算、命令 payload 上限、超时/OOM/quota、恶意图/图形数据和路径沙箱。
5. 对应 `docs/status/N-011.md``N-014.md`、fixture、默认 npm 验收命令和机器可读报告。
## 领取顺序
先做 N-011-A/N-011-B 与 N-012-A/N-013-A/N-014-A 这四个协议和安全门,随后按
N-011 -> N-012 -> N-013 -> N-014 执行。任一任务的正例、阻断例、保存恢复或发布门失败,
都只能修复当前任务,不得把下一任务的实现标记为完成。
## 当前执行切片2026-08-09
本轮把四个协议阶段再切成可独立回归的小步骤;每一步都先在 Worker 入口验证,再决定
是否允许进入下一层:
| 切片 | 内容 | 状态 | 证据/下一步 |
| --- | --- | --- | --- |
| N-011-A1 | `SculptStrokeIR` schema、笔刷枚举、sample 数量/路径长度/数值/对称校验 | 已完成 | `web/protocol/sculpt.ts`;超 2048 点或路径预算返回 `SCULPT_STROKE_BUDGET_EXCEEDED` |
| N-011-A2 | 单用户、linked、Shape Key、拓扑 modifier、空网格和 object-space mirror 能力门 | 已完成 | `gateSculptCapability` + Worker/native mirror gate不满足条件结构化阻断 |
| N-011-B1 | SceneIR Mask/Face Set 字段、域长度、范围、revision 校验 | 已完成 | `scene-ir.ts` 与 Worker binary validator |
| N-011-B2 | WBG1/geometry delta 属性编码和 hash | 已完成 | WBG1 magic/header v2 向后兼容 v1mask/face set 支持 binary transfer、replacement/range delta |
| N-011-B3 | Mask/Face Set Blender Main 写回、undo、保存重开 | 已完成 | `setSculptMeshAttributes``test:authoring-roundtrip`active Face Set runtime state 继续阻断 |
| N-011-C1a | 有界 native Main 顶点 stroke四笔刷、压力、Mask、对称、预算 | 已完成有限切片 | C++ Main 单事务;`test:authoring-roundtrip` 验证 save/reopen明确不是 PBVH/BMesh 等价 |
| N-011-C1 | Blender PBVH/BMesh 命中、法线/切线和桌面等价求值 | planned/阻断 | 当前只允许 C1aPBVH、Dyntopo、多分辨率和 UI 仍返回结构化阻断 |
| N-011-C2 | Draw/Inflate/Grab/Smooth desktop/WASM golden | planned | 依赖 C1每种笔刷独立 max/RMS 门 |
| N-012-A1 | GraphIR schema、节点白名单、typed socket/link/domain 校验 | 已完成 | `geometry-nodes.ts`Worker cycle gate 已有 E2E |
| N-012-A2 | 跨 group 的递归闭包、外部 object/collection/image ID 沙箱 | 已完成 | stable ID 类型、owner cycle、linked/missing/corrupt 资源门和 E2E 已覆盖 |
| N-012-A3a | Simulation manifest/schema、Blender 版本、frame byte range | 已完成 | `simulation-cache.ts`;完整帧、连续范围、总/逐帧 SHA-256 |
| N-012-A3b | committed blend 绑定、OPFS 内容寻址、schema 6 索引和 Worker 重启 | 已完成 | `simulation_manifest` + asset SHA-256`test:simulation-cache` |
| N-012-A3c | Blender desktop bake 生成、版本迁移、GN frame seek 消费 | planned | Simulation Zone 仍返回 `GEOMETRY_NODES_SIMULATION_UNAVAILABLE` |
| N-012-B1 | 无时间 GN lazy-function/field Main evaluator | planned | 当前合法 GraphIR 仍返回 `CAPABILITY_MISSING` |
| N-013-A1 | ShaderIR schema、单输出、typed socket/隐式转换、无环 | 已完成 | `shader-graph.ts`;未进入 writer 白名单的节点返回 `SHADER_NODE_UNSUPPORTED` |
| N-013-A2 | material/image stable ID 与 linked/missing/corrupt 资源门 | 已完成当前子集 | packed image 可引用linked/missing/corrupt image 在 Worker 阻断 |
| N-013-B1 | RGB/Value/Principled/Image Texture/Normal Map/Output 整图 Main transaction | 已完成当前子集 | native create/remove/link/default、history、save/reopen 回归 |
| N-013-B2a | Math 六运算 Main `custom1`、原生 socket identifier 和 operation reader | 已完成有限切片 | ADD/SUBTRACT/MULTIPLY/DIVIDE/MINIMUM/MAXIMUM其他运算阻断 |
| N-013-B2 | Mix/Mapping/TexCoord/Bump storage/property 写回 | planned | 未声明节点和 properties 仍 `SHADER_NODE_UNSUPPORTED` |
| N-013-C1a | RGB/Value 常量到 glTF PBR factor 的严格映射 | 已完成有限切片 | 缺值、越界、重复输入和未知链接返回 `SHADER_GRAPH_UNMAPPABLE`mapping E2E 覆盖 |
| N-013-C1 | 受限 Web material compiler、graph hash、色彩空间/sampler | planned | C1a 不代表 Web shader compiler任意图仍未启用 |
| N-014-A1 | Track/Strip schema、Action/range/blend/time-warp 校验 | 已完成 | `nla.ts`;缺失 Action 返回 `NLA_ACTION_MISSING` |
| N-014-A2 | strip 排序/重叠、owner/path compatibility、循环引用校验 | 已完成当前子集 | 同轨重叠、duration mapping、blend range 和 unsupported strip type 均阻断 |
| N-014-B1 | Action Clip 整栈替换 Main transaction、reader、保存重开 | 已完成当前子集 | `setNLAStack`Track/Strip/Action 摘要重开一致 |
| N-014-B2 | 细粒度 create/remove/move/resize/active 命令 | planned | UI 前先扩命令和 revision 回归 |
| N-014-C1 | 正 scale Action Clip native frame 求值 | 已完成当前子集 | authoritative Main bytes 进入 Blender depsgraphframe 5 矩阵回归通过 |
| N-014-C2a | Action Clip reverse Main 标志、reader、Depsgraph、保存重开 | 已完成有限切片 | `NLASTRIP_FLAG_REVERSE`frame 5 反向矩阵回归 |
| N-014-C2b1 | Action Clip repeat 与跨周期 frame seek | 已完成有限切片 | `repeat=2`frame 5/14 native 矩阵一致 |
| N-014-C2 | blend overlap、骨骼、约束顺序、transition/meta | planned | 未声明族继续 `NLA_*` 结构化阻断 |
| 共享 | 四类命令联合类型、Worker 统一 gate、浏览器稳定错误码回归 | 已完成 | `web-engine.ts``web-engine.worker.ts``test:capability-gates` |
### 阻断家族的可领取细分
以下切片一次只扩大一个可证明的能力;前一切片未通过 Main/save-reopen/golden/fault 门时,
后一切片不得转为 `in_progress`
| Sculpt 切片 | 交付与验收 | 状态 |
| --- | --- | --- |
| N-011-C1b | wasm32 PBVH/session/context feature probe初始化失败返回独立错误码且不修改 Main | planned/阻断 |
| N-011-C1c | PBVH object-space 命中、半径查询、对称副本去重和最大影响顶点预算 | planned依赖 C1b |
| N-011-C1d | Draw/Inflate/Grab/Smooth 分别接 Blender brush kernel每笔刷独立事务和取消回滚 | planned依赖 C1c |
| N-011-C1e | normals/split normals/tangents/bounds 与 mesh range deltarevision/hash 精确变化 | planned依赖 C1d |
| N-011-C2 | 1k/50k、非单位对象矩阵、Mask/Face Set、seam/sharp desktop/WASM max/RMS golden | planned依赖 C1e |
| N-011-D1 | Sculpt Mode、四笔刷、半径/强度和 object-space symmetry UIUI 不保存副本 | planned依赖 C2 |
| N-011-D2 | undo/redo、取消、Worker restart、OOM/malformed stroke 和关闭重开故障注入 | planned依赖 D1 |
| GN/Simulation 切片 | 交付与验收 | 状态 |
| --- | --- | --- |
| N-012-B0 | wasm32 lazy-function/field runtime feature probe、超时和内存上限 | planned/阻断 |
| N-012-B1a | Group Input/Output + Transform Geometry 最小闭包,输出 topology/attributes/bounds | planned依赖 B0 |
| N-012-B1b | Set Position、Math/Compare/Selection field 及 point/edge/face/corner 域转换 | planned依赖 B1a |
| N-012-B1c | Join/Separate/Realize Instances 和 instance stable ID/transform | planned依赖 B1b |
| N-012-B1d | Store/Named Attribute 生命周期、anonymous attribute 泄漏和 deterministic graph hash | planned依赖 B1c |
| N-012-D1 | desktop bake 生成器、Blender 版本迁移和逐帧 cache 消费 | planned依赖现有 A3b |
| N-012-D2 | Simulation Zone wasm frame step、状态所有权和 frame seek feature probe | planned/阻断,依赖 B1d/D1 |
| N-012-D3 | 浏览器 bake start/cancel/commit 原子事务;中断时不发布半成品 manifest | planned依赖 D2 |
| N-012-D4 | cache invalidation、缺帧/hash/version/quota/OOM/Worker restart 故障门 | planned依赖 D3 |
| Shader 切片 | 交付与验收 | 状态 |
| --- | --- | --- |
| N-013-B2a | Math 白名单逐运算 Main 写回和 operation readerGLB/Web 编译仍阻断 | 已完成有限切片 |
| N-013-B2b | Mix 的 data type、factor clamp 和颜色/向量 socket 变体 Main 写回 | planned依赖 B2a |
| N-013-B2c | Texture Coordinate -> Mapping -> Image TextureUV 名称/sampler/色彩空间闭环 | planned依赖 B2b |
| N-013-B2d | Normal Map/Bump 串联、强度/距离、切线前置条件和 desktop golden | planned依赖 B2c |
| N-013-C1b | 受限 Web compiler IR、graph hash cache、未知节点 fail-closed | planned依赖 B2d |
| N-013-C1c | sRGB/Non-Color、alpha、sampler、normal/tangent 和 WebGL/WebGPU 编译报告 | planned依赖 C1b |
| N-013-D1 | 可证明 PBR 闭包的纹理/常量完整映射;其余保持 `SHADER_GRAPH_UNMAPPABLE` | plannedC1a 已覆盖常量子集 |
| N-013-D2 | 桌面 Blender GLB 再导入,比较 factor、节点摘要、图片、sampler 与渲染摘要 | planned依赖 D1 |
| NLA 切片 | 交付与验收 | 状态 |
| --- | --- | --- |
| N-014-B2a | create/remove/move/resize/active 细粒度命令与 revision 冲突回归 | planned |
| N-014-C2b1 | repeat 和跨周期 frame seek Main/Depsgraph/save-reopen | 已完成有限切片 |
| N-014-C2b2 | strip 边界、NOTHING/HOLD/HOLD_FORWARD 和多周期 desktop golden | planned依赖 C2b1 |
| N-014-C2c | 多轨 overlap、blend in/out、influence 与四种 blend mode 的对象/骨骼 golden | planned依赖 C2b2 |
| N-014-C2d | constraint order、父级缩放和 pose matrix 的 NLA/Depsgraph 误差门 | planned依赖 C2c |
| N-014-C3 | Transition strip schema/Main/reader/evaluator只连接兼容相邻 Clip | planned/阻断 |
| N-014-C4 | Meta strip 递归深度、子 strip 所有权和循环检测 | planned/阻断,依赖 C3 |
| N-014-C5 | Sound strip 资源沙箱、时间摘要;不伪装为几何/姿态求值 | planned/阻断 |
| N-014-C6 | Animated Time、reverse+time-warp 组合、Drivers 依赖图和循环检测 | planned/阻断 |
| N-014-D1 | NLA lanes、selection、drag/resize、blend handles、mute/solo 与 Action menu | planned依赖 B2a/C2d |
| N-014-D2 | 可线性展开 Clip 的 GLB 导出;其他 strip 返回机器可读错误并保留摘要 | planned依赖 D1 |
当前默认验收命令:
```bash
npm --prefix web run typecheck
WEB_TEST_PORT=5200 npm --prefix web run test:capability-gates
npm --prefix web run test:simulation-cache
npm --prefix web run test:authoring-roundtrip
```

View File

@@ -0,0 +1,47 @@
# Web Blender Baseline
## Scope
This baseline fixes the source and toolchain for the first Web implementation. The
Blender source tree remains an upstream dependency; Web-specific code belongs in
new `web/`, `tools/web/`, `docs/`, and `tests/` paths.
## Source
| Item | Value |
| --- | --- |
| Web source directory | `blender-5.2.0/` |
| Source archive | `blender-5.2.0.tar.xz` |
| Source archive MD5 | `ec40a0ad895131a0fd3d360a59fd599a` |
| Reference Git checkout | `blender/` |
| Reference Git commit | `fbe6228777e7d9afefcd61a413844e790ae75db7` |
| Reference checkout state | dirty before Web work; pre-existing changes are out of scope |
| Existing native build | `build_blender_5.2.0/` |
The reference checkout is intentionally not clean. No existing Blender source,
asset, or test file is reverted by the Web implementation.
## Toolchain
| Tool | Version |
| --- | --- |
| OS | Linux x86_64, kernel 6.12.95+deb13-amd64 |
| CMake | 3.31.6 |
| Ninja | 1.12.1 |
| Emscripten | 3.1.69 |
| Node.js | 20.19.2 |
| npm | 9.2.0 |
| pnpm | unavailable at baseline; npm is the bootstrap fallback |
## Build isolation
- Native builds use `build_blender_5.2.0/` or a separately named native build tree.
- Web builds use `build_web/` and must not reuse native CMake cache files.
- Browser packages are emitted under `web/dist/` and are not copied into the
Blender source tree.
## Baseline checks
The baseline is accepted when the native binary reports Blender 5.2.0, the source
and archive paths exist, and the Web toolchain versions above are recorded in the
task status file.

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"]
}
]
}

View File

@@ -0,0 +1,33 @@
# Coordinate and Color Contract v1
## Coordinate spaces
Blender scene data is the authority. The browser adapter performs one explicit
conversion at the SceneIR -> Three.js boundary; React components never convert
coordinates independently.
| Data | Blender source | Web representation | Rule |
| --- | --- | --- | --- |
| Object transform | local/world matrices | column-major `Float32Array(16)` | preserve parent inverse and negative scale |
| Position/vector | Blender Z-up | Three.js Y-up adapter space | convert once in `three-adapter` |
| Normal/tangent | evaluated mesh | normalized buffer attributes | inverse-transpose for object scale |
| UV | mesh loop corner | `uv` buffer attribute | preserve V direction from source contract |
| Distance | Blender unit | meters only at import/export boundary | do not silently rescale scene values |
| Camera | Blender camera | Three.js camera | preserve perspective/orthographic parameters |
## Color spaces
- Base color and emission textures are decoded as sRGB unless Blender metadata
explicitly says otherwise.
- Metallic, roughness, normal, mask and weight textures remain linear data.
- Vertex colors are converted according to their Blender color attribute domain and
storage type; no implicit gamma conversion is allowed in the SceneIR serializer.
- Exposure and tone mapping belong to the Three.js renderer settings, not to
material data.
## Test fixtures
The contract is validated with an axis object, a non-uniformly scaled cube, a
negative-scale mirror, a perspective camera, an orthographic camera, an animated
object and an sRGB/linear texture pair. Every fixture records source values and
adapter values in `tests/golden/coordinate-color/`.

View File

@@ -0,0 +1,27 @@
# Web Dependency Matrix
This is the initial dependency decision for the browser build. A dependency may
move from `待验证` only after a task adds a build or browser regression test.
| Dependency or module | Decision | Web role | Evidence or next task |
| --- | --- | --- | --- |
| `blenlib`, `guardedalloc` | 保留 | Blender base/runtime utilities | W-022 |
| `makesdna`, `makesrna` | 保留 | DNA/RNA data and property schema | W-022 |
| `blenloader`, `blenkernel` | 保留 | `.blend` read/write and data blocks | W-022, W-030 |
| `bmesh` | 保留 | mesh editing and Decimate primitives | W-022, W-074 |
| `depsgraph` | 保留 | hierarchy, modifiers, evaluated mesh | W-067, W-093 |
| selected `modifiers` | 保留白名单 | Blender 5.2 types are identity-complete; evaluated parity remains fixture-gated | [modifier support matrix](modifier-support-matrix.md), W-066, W-074 |
| `windowmanager`, `editors`, `ghost` | React replacement | Blender-style UI semantics, not desktop window backend | W-018, W-019 |
| Blender `draw/gpu` viewport | 关闭首期 | Three.js is the browser renderer | W-013, W-050 |
| Python runtime and automatic `.blend` scripts | 关闭首期 | security and package-size boundary | W-117 |
| Cycles, CUDA/OptiX, Embree | 关闭首期 | server-side or native rendering only | roadmap non-goal |
| OpenVDB, USD, FFmpeg | 关闭首期 | server-side or later format modules | W-021 |
| React + TypeScript | 保留 | application UI and state | W-010 |
| Three.js (vendored) | 保留 | WebGL2 viewport and scene adapter; runtime has no CDN dependency | W-013, W-050 |
| Emscripten runtime | 保留 | Blender C/C++ to WebAssembly | W-020~W-025 |
| zlib 1.3.1 (vendored) | 保留 | gzip `.blend` decompression in Blender file reader | W-030 |
| zstd 1.5.7 (vendored) | 保留 | zstd `.blend` decompression in Blender file reader | W-030 |
| IndexedDB | 保留 | project metadata and small persistent records in StorageWorker | W-014, W-041 |
| OPFS | 保留 | large `.blend`, assets, snapshots and LOD cache | W-040, W-077 |
No CAD kernel or Bitbybit/OCCT module is part of this matrix.

View File

@@ -0,0 +1,71 @@
# Blender Depsgraph Evaluation Contract
`evaluateDepsgraph` is an explicit WebEngine Worker command intended to evaluate
the currently opened `.blend` with Blender 5.2's native loader and dependency
graph. It does not reinterpret the SceneIR modifier metadata in TypeScript.
Current status: the native module executes Blender 5.2 full loading, view-layer
synchronization, graph build, tagged update and frame evaluation in the headless
WASM runtime for the declared fixtures. The wasm32 DNA bridge recursively aligns
64-bit members, and the Web ID registry includes embedded NodeTree, Camera,
Light, Lattice and Grease Pencil data. The command returns the native
`BlenderDepsgraph/EVALUATED` report;
loader, graph or schema failures remain structured errors and never become
Three.js approximations.
The native evaluation sequence is:
1. Initialize Blender's headless data runtime once per WASM module.
2. Load the saved buffer with `BLO_read_from_memory`.
3. Synchronize view layers, build the viewport Depsgraph and apply tagged and
frame evaluation.
4. Return modifier identity/status for every evaluated object and final mesh
vertex positions and triangle indices for every mesh
object, keyed by stable SceneIR object/Mesh IDs. Each mesh also returns its
original modifier stack in order. Entries carry Blender's persistent UID,
registered type name, viewport/render/edit/cage flags, target object IDs,
the previous-stack and cross-object dependency edges, and an explicit
status: `EVALUATED`, `DISABLED` or `BLOCKED`. Blocked entries carry a stable
`errorCode`, human-readable `error` and actionable `suggestion`; they are
never silently treated as successful evaluation.
The result is validated by `parseDepsgraphEvaluation`. Every mesh must have a
16-value world matrix, exactly `vertexCount * 3` position values and exactly
`triangleCount * 3` in-range integer indices. A load, initialization or graph
failure returns the existing structured WebEngine error; it must never fall
back to a Three.js approximation.
The rigged fixture is checked against a golden written by desktop Blender 5.2.
The golden disables the fixture's Preview Decimate modifier solely to isolate
shape-key plus armature deformation. The current Web deformation evaluator
already compares final positions under the declared maximum and RMS error
budget. Native WASM Depsgraph evaluation must pass the same comparison and
repeated memory-safety runs before this contract is considered complete.
The offline regression `npm --prefix web run test:depsgraph` checks this report
schema for all declared fixtures and verifies the rigged stack order
`Armature -> Decimate`, its stack edge, the Armature target object edge and
Blender's structured "requires more than 3 input faces" Decimate block.
Drivers and constraints remain separate matrix entries. Geometry Nodes now has
two explicitly bounded native closures described below; it is not a claim of
general node-graph evaluation.
The category fixture suite adds Generate, Deform, Physics, Geometry Nodes and
Grease Pencil desktop goldens. Twenty-four category types execute native Web
implementations and compare topology/positions, including serialized
Mesh/Surface Deform bind caches and frame-6 Build/Wave output. Subdivision uses
the locally linked OpenSubdiv 3.7.0 CPU evaluator; exact Boolean uses local GMP
6.3.0; Curve evaluates through Blender's native modifier after registering the
Curves ID type. Their category goldens now pass in wasm32.
Geometry Nodes is intentionally narrower than Blender's full lazy-function
runtime. The evaluator accepts a three-node graph containing either Transform
Geometry in Components mode with literal defaults, or Set Position with an
unlinked constant selection/offset. Both use Blender mesh APIs and match desktop
goldens. Other graph shapes produce `BLENDER_MODIFIER_ERROR`; simulation/bake
state produces `GEOMETRY_NODES_SIMULATION_UNAVAILABLE` and preserves input geometry.
Other unsupported implementations retain their original DNA type code through
a disabled placeholder and report `UNSUPPORTED_MODIFIER_TYPE`; they are not
rewritten to `None`. Unsupported resource closures are validated before their
implementation can mutate geometry or enter an unavailable runtime path.

View File

@@ -0,0 +1,31 @@
# 本地依赖策略
浏览器运行时禁止从 CDN、远程 ESM 或第三方脚本地址加载功能模块。依赖分为
三类:
| 类别 | 当前模块 | 本地实现 |
| --- | --- | --- |
| 项目 vendored 运行时 | Three.js 0.185.1、OrbitControls、WebEngine glue/WASM | `web/app/src/vendor/``web/app/public/vendor/` |
| WASM 构建压缩库 | zlib 1.3.1、zstd 1.5.7 | `blender-5.2.0/extern/zlib/``blender-5.2.0/extern/zstd/`,由 Web CMake 的本地静态 target 使用 |
| npm 构建依赖 | React、ReactDOM、Vite、TypeScript | `web/package-lock.json` 锁定Vite 打入本地 `dist/assets/index-*.js` |
| 测试/工具依赖 | Playwright、ESLint、Emscripten | `web/node_modules`、系统 Emscripten 和本机 Chrome不进入浏览器产物 |
`tools/web/check-local-deps.sh` 是发布前门禁:检查 runtime dependencies 在本地
安装、Three.js 没有回退到 npm runtime import、vendor 资源存在,并扫描源码和
`dist/` 中的 CDN/远程 ESM 地址,同时按 `engine-manifest.json` 校验本地 WASM
SHA-256。npm registry 只发生在开发机首次安装阶段;发布/浏览器运行只提供
`web/dist` 的同源静态文件。离线开发需要预先保留 `node_modules` 或 npm
缓存,随后可用 `npm ci --offline` 重建,运行时不再访问 registry。
Blender Web CMake 不使用 `ExternalProject`、FetchContent 或构建时下载。zlib
和 zstd 的源码、许可证和 CMake 输入已放入 `blender-5.2.0/extern/`;浏览器只
加载 `web_engine.js` 与同源 `web_engine.wasm`,不加载 SQLite WASM、OCCT 或
任何远程模块。
可复现的离线检查命令:
```text
cd web
npm ci --offline --ignore-scripts
npm run check:local-deps
```

View File

@@ -0,0 +1,66 @@
# MeshIR Buffer Layout
This document is the versioned boundary between the Blender reader and the
Three.js renderer. The compatibility C ABI returns a JSON snapshot. The
preferred ABI separates JSON metadata from a native WBG1 geometry stream, and
the Worker transfers each typed mesh buffer without reconstructing large JSON
arrays. JSON arrays remain a fallback for older engine binaries.
## Coordinate and topology contract
- Blender source coordinates are right-handed, Z-up, with forward `-Y`.
- Positions and normals use packed little-endian IEEE-754 `float32` values.
- `indices` are packed little-endian unsigned 32-bit integers and reference
`positions` vertices.
- `topology` is `triangles`; every three indices form one triangle.
- `triangleCornerIndices` maps every index entry to the original Blender mesh
corner. It is required when UV or color attributes are present because a
single position can have multiple corner values.
- `triangleMaterialIndices` has one non-negative integer per triangle and
indexes `materialSlotIds`.
## SceneIR fields
| Field | Element count | Type | Domain |
| --- | ---: | --- | --- |
| `positions` | `vertexCount * 3` | `float32` | Point |
| `normals` | `vertexCount * 3` | `float32` | Point, normalized |
| `indices` | `triangleCount * 3` | `uint32` | Triangle corners |
| `triangleCornerIndices` | `triangleCount * 3` | `uint32` | Blender Corner |
| `uvs` | `cornerCount * 2` | `float32` | Corner, optional |
| `colors` | `cornerCount * 4` | `float32` RGBA | Corner, optional |
| `triangleMaterialIndices` | `triangleCount` | `uint32` | Face expanded to triangles |
| `triangleFaceIndices` | `triangleCount` | `uint32` | Source face for edit selection |
| `edgeVertexIndices` | `edgeCount * 2` | `uint32` | Edge endpoints |
| `tangents` | `cornerCount * 4` | `float32` | Corner tangent/sign, optional |
| `splitNormals` | `cornerCount * 3` | `float32` | Corner normal, optional |
`bounds.min` and `bounds.max` are three finite JSON numbers in source object
space. `materialSlotIds` are stable SceneIR IDs and never contain embedded
material data.
## Native WBG1 stream
The stream starts with `WBG1`, a little-endian schema version and mesh count.
Each mesh contains an ID length, eleven element counts, UTF-8 ID bytes, then
the following arrays in order:
```text
positions:f32, indices:u32, normals:f32, triangleCornerIndices:u32,
uvs:f32, colors:f32, triangleMaterialIndices:u32,
triangleFaceIndices:u32, edgeVertexIndices:u32, tangents:f32,
splitNormals:f32
```
Every count is element-based and every scalar is 4 bytes, so array starts stay
4-byte aligned. The decoder rejects truncation/trailing bytes, unknown mesh
IDs, count/summary mismatches and indices outside `vertexCount`. Topology
changes must regenerate triangle-face and edge mappings; stale source arrays
are never accepted.
`geometryStatus: "binary"` means metadata references a transferred per-mesh
buffer through `geometryBufferId`. `geometryStatus: "available"` is the JSON
fallback. After the initial WBG1 generation, `MeshGeometryDelta` carries
`meshId`, field, byte offset and transferable replacement bytes for stable-size
changes; topology or attribute-layout changes replace only that mesh envelope.
Network-style progressive sub-mesh streaming is not implied by WBG1.

View File

@@ -0,0 +1,143 @@
# Blender 5.2 Modifier Web Support Matrix
This matrix separates file recognition, dependency extraction and evaluated
result parity. Loading a modifier type is not evidence that its Web result
matches Blender desktop.
## Status levels
| Level | Meaning |
| --- | --- |
| `IDENTITY` | Type code/name, UUID, stack order and mode flags are preserved. |
| `PARAMETERS` | Declared parameters are present in SceneIR. No evaluated mesh is implied. |
| `TARGET_EDGE` | Cross-object target IDs are present in the native Depsgraph report. |
| `NATIVE_FIXTURE` | Native WASM Depsgraph behavior is covered by a Blender fixture/golden. |
| `COMMAND` | A revisioned Web command can execute and has undo/redo or isolated-preview coverage. |
| `BLOCKED` | No parity claim. An enabled unsupported/invalid entry must return a structured block. |
## Current verified set
| Modifier | Identity | Parameters | Target edge | Native fixture | Command |
| --- | --- | --- | --- | --- | --- |
| Subdivision Surface | yes | levels/type | no | desktop/WASM topology and geometry parity through local OpenSubdiv 3.7.0 CPU evaluator | no |
| Mirror | yes | axis/merge flags | optional mirror object | desktop/WASM geometry parity | no |
| Boolean (Exact) | yes | operation/solver | target object | desktop/WASM surface parity through local GMP 6.3.0 | no |
| Decimate | yes | mode/ratio/iterations/delimit | no | structured small-mesh error | Collapse/Un-Subdivide/Dissolve command |
| Armature | yes | target/group/deform flags | yes | rigged shape-key golden | enable/disable + undo/redo |
| Bevel | yes | width/segments/profile | no | desktop/WASM geometry parity | no |
| Lattice | yes | target/group/strength/flags | yes | desktop/WASM geometry parity | no |
| Curve | yes | target/group/axis/flags | yes | desktop/WASM topology and geometry parity | no |
| Hook | yes | target/subtarget/group/falloff/force | yes | desktop/WASM geometry parity | no |
| Mesh Deform | yes | target/group/grid/flags | yes | bound-cache desktop/WASM geometry parity | no |
| Shrinkwrap | yes | targets/group/distance/type/mode/limit | primary/aux targets | desktop/WASM geometry parity | no |
| Surface Deform | yes | target/group/falloff/strength/flags | yes | bound-cache desktop/WASM geometry parity | no |
| Array/Solidify | yes | fixture parameters | object edges where used | desktop/WASM geometry parity | no |
| Triangulate/Weld | yes | fixture parameters | no | desktop/WASM topology and geometry parity | no |
| Build/Wave | yes | frame/time parameters | object/texture edges where used | frame-6 desktop/WASM geometry parity | no |
| Geometry Nodes | yes | node group/default socket values | node-group edge | bounded Transform Geometry and Set Position closures match desktop | no |
| Edge Split | yes | angle/sharp flags | no | desktop/WASM topology and geometry parity | no |
| Screw | yes | axis/angle/steps/merge | optional axis object | desktop/WASM topology and geometry parity | no |
| Displace | yes | direction/strength/mid-level/space | optional mapping object | constant local-space desktop/WASM parity; resource configurations block | no |
Native `targetObjectIds` currently covers Array, Mirror, Boolean, Lattice, Curve, Armature, Hook,
Mesh Deform, Shrinkwrap, Surface Deform, Screw and Displace mapping objects.
The ordered `dependsOn` list contains the prior stack entry followed by those
external object IDs. These SceneIR entries remain `METADATA_ONLY`; the Worker
still blocks topology/LOD commands until evaluated input is available.
## Complete Blender 5.2 identity coverage
The lightweight reader now covers every DNA slot from `0` through `86`; a C++
`static_assert` fails the build when `NUM_MODIFIER_TYPES` grows without a table
update. The category-golden native whitelist contains 24 evaluated types:
`SUBSURF`, `MIRROR`, `ARRAY`, exact `BOOLEAN`, `BEVEL`, `SOLIDIFY`,
`TRIANGULATE`, `WELD`, `LATTICE`, `HOOK`, `SHRINKWRAP`, `SIMPLE_DEFORM`,
`MESH_DEFORM`, `SURFACE_DEFORM`, `CURVE`, `BUILD`, `WAVE` and the bounded
`NODES` closures, plus `SMOOTH`, `CAST`, `WIREFRAME`, `EDGE_SPLIT`, `SCREW` and
bounded `DISPLACE`.
Armature, Shape Key and Decimate have earlier dedicated
fixtures. The remaining identity-only types are:
`NONE`, `SOFT_BODY`, `UV_PROJECT`,
`PARTICLE_SYSTEM`, `PARTICLE_INSTANCE`, `EXPLODE`, `CLOTH`,
`COLLISION`, `FLUID_SIM`, `MASK`, `MULTIRES`, `SURFACE`, reserved slot `31`,
`WARP`, `WEIGHT_VG_EDIT`, `WEIGHT_VG_MIX`, `WEIGHT_VG_PROXIMITY`,
`OCEAN`, `DYNAMIC_PAINT`, `REMESH`, `SKIN`, `LAPLACIAN_SMOOTH`, `UV_WARP`,
`MESH_CACHE`, `LAPLACIAN_DEFORM`, `DATA_TRANSFER`,
`NORMAL_EDIT`, `CORRECTIVE_SMOOTH`, `MESH_SEQUENCE_CACHE`, `WEIGHTED_NORMAL`,
`FLUID`, `MESH_TO_VOLUME`, `VOLUME_DISPLACE`, `VOLUME_TO_MESH`,
`GREASE_PENCIL_OPACITY`, `GREASE_PENCIL_SUBDIV`, `GREASE_PENCIL_COLOR`,
`GREASE_PENCIL_TINT`, `GREASE_PENCIL_SMOOTH`, `GREASE_PENCIL_OFFSET`,
`GREASE_PENCIL_NOISE`, `GREASE_PENCIL_MIRROR`, `GREASE_PENCIL_THICKNESS`,
`GREASE_PENCIL_LATTICE`, `GREASE_PENCIL_DASH`, `GREASE_PENCIL_MULTIPLY`,
`GREASE_PENCIL_LENGTH`, `GREASE_PENCIL_WEIGHT_ANGLE`,
`GREASE_PENCIL_ARRAY`, `GREASE_PENCIL_WEIGHT_PROXIMITY`,
`GREASE_PENCIL_HOOK`, `GREASE_PENCIL_LINEART`, `GREASE_PENCIL_ARMATURE`,
`GREASE_PENCIL_TIME`, `GREASE_PENCIL_ENVELOPE`, `GREASE_PENCIL_OUTLINE`,
`GREASE_PENCIL_SHRINKWRAP`, `GREASE_PENCIL_BUILD`,
`GREASE_PENCIL_SIMPLIFY`, `GREASE_PENCIL_TEXTURE`.
## Blocking contract
An enabled unregistered native modifier returns `UNSUPPORTED_MODIFIER_TYPE`.
An enabled target-based modifier without a target returns
`MODIFIER_TARGET_MISSING`. An error raised by Blender itself returns
`BLENDER_MODIFIER_ERROR`. Resource/configuration-dependent Displace returns
`MODIFIER_CONFIGURATION_UNSUPPORTED`; simulation/bake GN returns
`GEOMETRY_NODES_SIMULATION_UNAVAILABLE`. A target path that reaches the owner returns
`MODIFIER_DEPENDENCY_CYCLE`. All blockers include the modifier object/name/type,
`error`, and an actionable `suggestion`. Viewport-disabled modifiers remain
`DISABLED` and do not inherit stale evaluated errors.
## Category fixtures
| Category | Blender 5.2 golden | Current Web result |
| --- | --- | --- |
| Generate | Subdivision, Mirror, Array, Boolean, Bevel, Solidify, Triangulate, Weld | All eight execute native Blender code. Subdivision matches the 26-vertex/48-triangle OpenSubdiv golden; Exact Boolean matches the 12-vertex/20-triangle surface golden. |
| Deform | Lattice, Hook, Shrinkwrap, Simple Deform, Mesh Deform, Surface Deform | All six match desktop geometry; Mesh/Surface fixtures contain real bind caches and target deformation. |
| Curve | Curve target and evaluated output in a dedicated fixture | Native Curve evaluation matches desktop topology and positions within the declared floating-point tolerance. |
| Physics/time | Build and Wave at frame 6; disabled Cloth, Soft Body and Collision | Build and Wave match desktop geometry at frame 6; simulation entries remain `DISABLED`. |
| Geometry Nodes | Self-contained Transform Geometry, Set Position and simulation-zone graphs | Constant Transform and Set Position match desktop positions/topology. Simulation zones preserve input and return `GEOMETRY_NODES_SIMULATION_UNAVAILABLE` without consuming temporal state. |
| Grease Pencil | One drawing plus all 26 GP modifier types exposed by Blender 5.2 Python | GP data loads; all modifier codes/modes are preserved and remain `DISABLED`. |
| Extended native | Edge Split, Screw and bounded Displace | All match desktop topology/positions; RGB/resource Displace preserves input and returns `MODIFIER_CONFIGURATION_UNSUPPORTED`. |
Line Art is exposed as `LINEART` rather than with the `GREASE_PENCIL_` prefix;
the generator includes it explicitly. The fixtures, desktop goldens and
comparison runner are generated by
`tools/web/generate-modifier-fixtures.py`,
`tools/web/generate-modifier-goldens.py` and checked offline with
`npm --prefix web run test:modifier-goldens`.
## Local native dependencies
Subdivision uses the local OpenSubdiv 3.7.0 source and archive under
`blender-5.2.0/extern/opensubdiv-source`. The Web build enables its CPU-only
static evaluator and disables GPU, examples, documentation and tests. The
archive MD5 is `470d53c4d4335a601c33a052ce7c33b4`; SHA-256 is
`f843eb49daf20264007d807cbc64516a1fed9cdb1149aaf84ff47691d97491f9`.
Exact Boolean uses the local GMP 6.3.0 DFSG source plus Emscripten static
archives under `blender-5.2.0/extern/gmp-source` and
`blender-5.2.0/extern/gmp-wasm`. The source archive MD5 is
`d4a3890b5e28df535b653b07798b11b2`; SHA-256 is
`bd2966e6d277f79328e894a5a9f3ba3fbf2ed2be81def5f48623e30c23fb1572`.
`WITH_MANIFOLD` remains off: Blender 5.2's verified Exact solver path uses GMP,
so Manifold is not part of the tested runtime closure. wasm32 and desktop may
choose different valid diagonals on coplanar faces; the golden therefore
compares oriented surface area and geometry instead of requiring identical
triangle indices.
`npm --prefix web run check:local-deps` verifies both source archive hashes,
the OpenSubdiv CMake source and the local GMP/GMPXX headers and WASM archives.
## Future whitelist expansion
1. Add deterministic physics cache stepping for Cloth/Soft Body/Collision;
Build and Wave are complete for the current frame golden.
2. Expand Geometry Nodes from the verified Transform Geometry and Set Position closures in small
node/dependency batches. Simulation zones, bake data, fields, instances and
the full lazy-function graph remain outside the current whitelist.
3. Enable Grease Pencil modifiers in small dependency batches; keep target and
simulation types disabled until their external resources are validated.
4. Add native geometry goldens for the remaining Generate/Deform families,
including Cast, Remesh, Multires and volume conversions.

View File

@@ -0,0 +1,78 @@
{
"schemaVersion": 1,
"packages": [
{
"name": "Blender",
"version": "5.2.0",
"license": "GPL-2.0-or-later",
"source": "blender-5.2.0/",
"notice": "Blender source and license files remain in the upstream source tree."
},
{
"name": "React",
"version": "19.2.8",
"license": "MIT",
"source": "web/node_modules/react (installed from locked npm package)"
},
{
"name": "ReactDOM",
"version": "19.2.8",
"license": "MIT",
"source": "web/node_modules/react-dom (installed from locked npm package)"
},
{
"name": "Three.js",
"version": "0.185.1",
"license": "MIT",
"source": "web/app/src/vendor/three/"
},
{
"name": "Emscripten runtime",
"version": "3.1.69",
"license": "MIT",
"source": "local emsdk"
},
{
"name": "Vite",
"version": "8.2.0",
"license": "MIT",
"source": "web/node_modules/vite (build-time only)"
},
{
"name": "TypeScript",
"version": "5.9.3",
"license": "Apache-2.0",
"source": "web/node_modules/typescript (build-time only)"
},
{
"name": "Playwright",
"version": "1.62.1",
"license": "Apache-2.0",
"source": "web/node_modules/@playwright/test (test-time only)"
},
{
"name": "OpenSubdiv",
"version": "3.7.0",
"license": "Apache-2.0",
"source": "blender-5.2.0/extern/opensubdiv-source"
},
{
"name": "GMP",
"version": "6.3.0",
"license": "LGPL-3.0-or-later",
"source": "blender-5.2.0/extern/gmp-source"
},
{
"name": "zlib",
"version": "1.3.1",
"license": "Zlib",
"source": "blender-5.2.0/extern/zlib/ (vendored build dependency)"
},
{
"name": "zstd",
"version": "1.5.7",
"license": "BSD-3-Clause",
"source": "blender-5.2.0/extern/zstd/ (vendored build dependency)"
}
]
}

19
docs/web/three-vendor.md Normal file
View File

@@ -0,0 +1,19 @@
# Three.js 本地资源
当前使用 Three.js `0.185.1`。运行时模块已从锁定的 npm 包复制到项目源码:
- `web/app/src/vendor/three/three.module.js`
- `web/app/src/vendor/three/three.core.js`
- `web/app/src/vendor/three/addons/controls/OrbitControls.js`
视口适配器只引用上述相对路径OrbitControls 的裸 `three` 导入也已改为
本地相对导入。构建产物把代码打进本地 `index` chunk不从 CDN 或网页运行时
网络加载 Three.js。`@types/three` 仅作为开发期类型依赖。
## 校验
```text
three.module.js bbf5ed13fe4373f5bd38b14ea8e62e9f157327da5638edc6d3863e08b167c9c7
three.core.js 3718df126d69c125362a03340913204470d8c50238605150e57f808840fb7759
OrbitControls.js 1e08bf297c9062aa5055a9d649d8a8458c3871aa31c7d600f535a06f567e7cad
```