纹渊 HarmonyOS 7 工程实战(03):GLTF/GLB 模型库的数据目录设计
一、问题与目标
纹渊的 3D 预览需要面对多个载体模型:头盔、动物、杯体或其他商品形态。模型如果只是散落在资源目录里,页面很快会变成一堆硬编码路径;后续新增载体、替换模型或做降级提示时,维护成本会越来越高。
更稳的做法是把模型当成目录数据来管理。每个模型都应该有名称、资源路径、默认缩略图、适用纹样区域和失败提示。页面只消费目录项,不直接关心底层资源怎么组织。这样模型库扩展时,页面状态不会跟着重写。
二、实现思路
模型目录可以先从最小字段开始:id、displayName、resource、previewImage、materialSlot 和 fallbackText。id 用来保存选择状态,resource 指向 GLTF/GLB 资源,materialSlot 标记纹样最终要进入哪个材质节点,fallbackText 用于模型加载失败时的界面提示。
加载流程也要分层:目录读取负责提供候选项,模型加载负责创建 Scene,材质绑定负责把纹样结果放进节点,页面只处理选中态和错误态。这样做的好处是模型数量增加时,业务链路仍然保持稳定。
| 模型字段 | 处理方式 |
|---|---|
| id | 保存用户选择,避免显示名变更影响状态恢复 |
| resource | 指向 GLTF/GLB 资源,统一交给模型加载服务处理 |
| materialSlot | 标记纹样需要替换的材质节点或贴图槽位 |
| fallbackText | 加载失败时给出可理解的降级说明 |
三、关键实现
模型加载服务只暴露一个入口:接收资源路径,返回可绑定材质的场景对象。页面不直接创建底层资源,也不把节点查找逻辑写进组件。
async function loadModel(path: ResourceStr): Promise<Scene> { const scene = await Scene.load(path) const factory = scene.getResourceFactory() if (scene.root !== undefined) { scene.root.position = { x: 0, y: 0, z: 0 } } if (factory !== null) { const camera = await factory.createCamera({ name: 'main-camera' }) scene.root?.addChild(camera) } return scene }这种写法把 3D 资源的不确定性包在服务层里。模型文件缺失、节点名称变化、材质槽位不匹配,都可以在服务层返回明确错误,页面只需要显示降级状态或切换到二维预览。
目录化还有一个实际好处:不同载体可以共享同一套页面组件。新增一个 GLB 模型时,优先改目录数据和材质槽位,不需要改创作页的交互结构。
四、失败场景与取舍
模型库最容易踩的坑是把资源路径和页面选择写死。短期看能跑,长期看每新增一个载体都要改页面。另一个问题是材质节点没有稳定标识,模型换版后贴图失效,但页面仍然显示“生成成功”。
降级策略要提前准备。模型加载失败时,不应该让页面停在空白 3D 容器;可以保留 Canvas 预览、显示模型资源不可用提示,并允许用户继续导出平面纹样。这样 3D 能力异常不会拖垮整个创作流程。
五、验证步骤
- 打开创作入口,确认标题、主要卡片、底部操作区和预览区域显示正常。
- 走一遍主流程,观察加载态、成功态和下一步按钮是否同步变化。
- 人为制造空输入、重复点击或不可用条件,确认页面给出可理解的提示。
- 返回上一页再进入,确认选中态、预览结果和关键状态没有漂移。
- 在较窄窗口和较宽窗口各看一遍,确认文字、卡片和按钮没有重叠。
六、总结
GLTF/GLB 模型库的数据目录设计,核心是把资源、材质槽位和失败提示从页面里拿出来。页面只处理选中态,模型服务处理资源细节,后续扩展载体时会更稳。
