- update native web_engine sources and rebuilt wasm artifacts - add task cards, status notes and tooling scripts - add web e2e specs, tutorial UI sources and tutorial assets - sync golden task manifests and audit records Co-Authored-By: Claude Code <noreply@anthropic.com>
7.6 KiB
7.6 KiB
Web UI 对标 + PBR 真实案例实现状态(2026-11-11)
- 状态:
in_progress(实现证据,不宣称验收) - 范围:Outliner React treeview、着色器编辑器 React Flow、真实 PBR 资产链路与色彩管理
- 验收:
NOT_CLAIMED
已实现
Outliner
- 新增
web/app/src/app/ui/OutlinerTreeView.tsx、outliner-tree-data.ts、outliner-tree.css。 - 使用
react-accessible-treeview@2.11.2渲染原生tree.rows;保留选择、展开、拖放 payload、重命名、Remap、可见性、截断提示。 App.tsx原生行渲染改为 treeviewrenderRow,并保留role="treeitem"可访问名、data-node-id/data-active、.tree-node-name、展开按钮可访问名。- 修复 treeview 受控展开反馈环:不再由库自动触发
OUTLINER_TOGGLE_EXPAND,仅用户按钮/键盘触发。
着色器编辑器(React Flow)
- 新增
web/app/src/app/ui/ShaderNodeCanvas.tsx、ShaderNode.tsx、shader-flow-adapter.ts、shader-node-catalog.ts、shader-node.css。 WasmShaderEditor.tsx画布主体切换到@xyflow/react@12.12.1:- 节点拖动 →
MOVE - 端口拖拽 →
LINK,边删除 →UNLINK - 节点内 socket 编辑 →
SET_SOCKET - Shift+A / 工具栏添加节点 →
ADD - 节点属性(Mix data/blend、Mapping vector type、Normal Map space、Bump invert、UV Map 名称)→
SET_PROPERTY - Image Texture 换图 →
SET_IMAGE - 只读链接材质、revision 冲突、未知节点占位、全部端口折叠。
- 节点拖动 →
- 坐标约定
position={x, -y};读回fromSocket/toSocket与命令fromSocketId/toSocketId由适配器唯一转换。 - SSE/React Flow 要求:CSP 增加
style-src-attr 'unsafe-inline',脚本/Worker 仍严格;去掉 attribution 隐藏。
原生读回与编辑
web_engine_blend_reader.cpp:节点类型补齐 MIX/MAPPING/TEX_COORD/UV_MAP/BUMP/SEPARATE_COLOR/OBJECT_INFO;socketavailable/hidden;节点属性(dataType/blendType/vectorType/space/invert/projection/interpolation/extension/uvMap/mode);材质textureBindings;normalImageId真正赋值;ImagecolorSpace读取(当前资产未返回,见残余)。web_engine_main_state.cpp:editShaderNode新增SET_PROPERTY/SET_IMAGE,ADD 支持 UV_MAP/OBJECT_INFO/SEPARATE_COLOR;setShaderGraph类型扩展并按 socket identifier 绑定(修复 Mix 同名 socket)。- 新增
packImage:外部 PNG/JPEG 字节直接打包进已有 Image,保留节点链接,允许 0 尺寸由调用方按文件头提供。 - 新增
assignMaterialSlot:把已有材质装入对象槽(BKE_object_material_slot_add+BKE_object_material_assign),属性面板材质槽下拉已接入。 web_engine_api.cpp、web/protocol/web-engine.ts、web-engine.worker.ts、web-engine-capabilities.ts同步命令。
PBR 显示
three-adapter/pbr.ts:按场景colorManagement.viewTransform选择 AgX/Standard/Neutral/ACES;toneMappingExposure = 2 ** exposure;data-tone-mapping场景驱动。three-adapter/texture-assets.ts:应用map、normalMap+strength、metalnessMap、roughnessMap、emissiveMap、alphaMap、bumpMap;贴图驱动时中性化 factor。texture-assets.applyEnvironment使用WorldIR.environmentRotation。render-assets.ts纹理用途扩展;scene-ir.ts校验新节点类型/属性/textureBindings。- 外部贴图 UI:着色器编辑器“外部贴图”列表;文件先
StorageClient.putAsset写入 OPFS,再packImage;项目保存继续走StorageClient.saveProject(OPFS)。
Chromium 实测(手动探针,实现证据)
tests/files/web/m12_glb_main_v1/pbr.blend:Outliner 树、React Flow 2 节点/1 连线;拖动、连线、断开、改 Roughness、添加 Mapping 均发出正确原生命令;保存后重开 Roughness=0.7、Mapping 位置/连线保留;Ctrl+Z / Ctrl+Shift+Z 恢复 0.28 / 0.7。blender-5.2.0/tests/files/io_tests/blend_geometry/materials_pbr.blend:ImageTexture/Principled 读回;三张外部 PNG 经 OPFS→packImage 变为 PACKED;viewport 显示红/金属黑 PBR 立方体;data-tone-mapping场景驱动。cubes_with_textures.blend:读回 MAPPING/UV_MAP/SEPARATE_COLOR/IMAGE_TEXTURE;textureBindings 含 BASE_COLOR/ROUGHNESS/NORMAL/EMISSION,normalImageId已赋值。user-testing/WEB_Blender_Robot_Tool_20260916/outputs/robot-project.blend:18 对象、7 个 link 材质;着色器材质列表可见。
Playwright 用例
web/tests/e2e/outliner-treeview-production.spec.ts:通过。web/tests/e2e/shader-editor-flow-production.spec.ts:通过(headless 合成指针的节点拖动不稳定,拖动由手动 Chromium 探针覆盖;其余连线/删除/添加/参数/保存重开在用例内)。web/tests/e2e/pbr-real-assets-production.spec.ts:通过。三张外部 PNG 在 Web UI(?ui=web)下经 OPFS→packImage打包,SceneIR 依次 r7/r10/r13,外部贴图列表清空。- 三条用例合并运行:
3 passed (37.3s)。 - 注意:测试使用
?ui=web(React UI 模式)以避免 headless SwiftShader 下原生 GPU 节点编辑器绘制抛WASM_INIT_FAILED;这不是 React UI 路径的缺陷。
残余缺口
- Image
colorSpace未从.blend读出(undefined);当前按纹理用途推断 sRGB/Non-Color,颜色贴图默认正确,非默认色彩空间未覆盖。 robot-project.blend保存工作区中 Outliner 深层的展开按钮发送选择而非OUTLINER_TOGGLE_EXPAND;pbr.blend/basic_scene.blend展开正常。需继续定位该工程的 workspace/area 映射。- Headless Playwright 合成鼠标拖动 React Flow 节点不稳定;真实 Chromium 手动拖拽正常。
- Compositor 原生
setCompositorNodeGraph仍未实现;Geometry Nodes 尚未复用 React Flow 画布。 - 非 Principled BSDF(Glass/Hair/Anisotropic 等)、Subsurface/Volume/UDIM/HDRI 仍按 capability block 或后续批次处理。
- 本轮未运行全量回归、未生成正式 candidate receipt;构建哈希与证据需在后续批次绑定。
构建与构件
- 原生:
cmake --build build_web_blender6 --target web_engine -- -j8 - 同步:
node tools/web/sync-web-engine-runtime.mjs - 前端:
cd web && npm run build web_engine.wasmSHA-256:84fc21e7728b580579424f97495d27275d30c01f2898ccadf4e368ea37d646ea(与web/app/src/vendor/blender/web_engine.wasm一致)web/dist/index.htmlSHA-256:d36f002b7793b7684f3ac570537bd33527143a6ea64fb43b3dabff0d3d313a8e
CSP 与样式加载
- React Flow 需要内联 style 属性;部署 CSP 改为
style-src 'self'; style-src-attr 'unsafe-inline',脚本/Worker 指令保持严格。 - React Flow 与新增组件 CSS 通过
web/app/index.html的同源<link>引入(ui/xyflow.css、ui/outliner-tree.css、ui/shader-node.css),避免 Vite dev 的内联<style>注入被 CSP 阻断;生产构建提取为 CSS 文件。 tools/web/check-csp-policy.mjs已改为仅对脚本/Worker 指令拒绝'unsafe-inline',并断言style-src-attr的最小放宽。既有tests/golden/M13-05A/csp-report.json需在后续验收批次重新生成。
OPFS 资产策略
- 渲染用 packed 图像:
cachePackedAssets在每次快照后通过StorageClient.putAsset写入 OPFS。 - 外部贴图:用户选择文件后先
putAsset(OPFS),再packImage进工程;项目保存走StorageClient.saveProject(OPFS)。 - 打开工程时提供的库
.blend文件在openBlend前写入项目 OPFS(application/x-blender)。 - 资产库/OBJ/多分辨率/VDB 等既有导入路径继续使用
putAsset。