mcpField
A
add-3d-models

add-3d-models

github.com/dcl-regenesislabs创意与媒体1742 次安装21 周前更新
代码仓库

关于 add-3d-models

面向 Decentraland 场景创作者的 3D 模型配置技能,介绍静态与运行时实体、碰撞器、模型目录搜索和常见加载问题。

主要功能

  1. 01在 main-entities.ts 放置静态模型
  2. 02配置 GLTF 与网格碰撞器
  3. 03搜索并下载目录模型
  4. 04设置模型变换与父实体
  5. 05排查模型加载和显示问题

适用场景

  1. 01为 Decentraland 场景添加静态道具
  2. 02在运行时动态生成模型
  3. 03查找场景所需模型资源
  4. 04诊断模型不可见或无法点击

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dcl-regenesislabs/opendcl --skill add-3d-models

安装后在 Claude Code 中输入/add-3d-models确认已加载。

详细介绍

向 Decentraland 场景中添加 3D 模型

模型放在哪里:main-entities.ts

在创作时放置的 3D 模型是静态可见实体。请在 main-entities.ts 中声明它,不要在 src/index.ts 中通过 engine.addEntity() 添加。构建时会将 main-entities.ts 编译为 main.crdt,引擎会在 main() 运行前预加载它,编辑器也可以交互式地拖动和旋转模型。

// main-entities.ts
import type { Scene } from '@dcl/sdk/scene-types'

export const scene = {
  my_model: {
    components: {
      Transform: { position: { x: 8, y: 0, z: 8 } },
      GltfContainer: {
        src: 'models/myModel.glb',
        visibleMeshesCollisionMask: 3 // CL_PHYSICS | CL_POINTER
      }
    }
  }
} satisfies Scene

始终为 GltfContainer 设置 visibleMeshesCollisionMask。目录中的模型不包含单独的碰撞体网格;使用可见网格作为碰撞体可确保模型具有实体碰撞效果且可点击。在 main-entities.ts 中使用整数值(ColliderLayer.CL_PHYSICS = 1、CL_POINTER = 2,两者都启用时为 3),因为字面量中不允许使用枚举。

改为在 src/index.ts 中使用 engine.addEntity() 的情况:仅当模型是动态生成的(在循环中程序化放置、在事件触发时生成、由 NFT 所有权控制等)。对于静态道具,始终使用 main-entities.ts。

文件组织

将模型文件放在项目根目录下的 models/ 目录中:

project/
├── models/
│   ├── building.glb
│   ├── tree.glb
│   └── furniture/
│       ├── chair.glb
│       └── table.glb
├── src/
│   └── index.ts
└── scene.json

碰撞体

使用模型自带的碰撞体
// main-entities.ts — declared inline with GltfContainer
building: {
  components: {
    Transform: { position: { x: 8, y: 0, z: 8 } },
    GltfContainer: {
      src: 'models/building.glb',
      visibleMeshesCollisionMask: 3,    // CL_PHYSICS | CL_POINTER
      invisibleMeshesCollisionMask: 1   // CL_PHYSICS
    }
  }
}
添加简单碰撞体

对于基本形状(非 GLTF),请添加 MeshCollider:

// main-entities.ts
invisible_wall: {
  components: {
    Transform: { position: { x: 0, y: 1, z: 8 }, scale: { x: 0.1, y: 2, z: 16 } },
    MeshCollider: { mesh: { $case: 'box', box: { uvs: [] } } }
  }
}

其他形状:{ $case: 'sphere', sphere: {} }、{ $case: 'plane', plane: { uvs: [] } }、{ $case: 'cylinder', cylinder: {} }。

⚠️ 重要:切勿在 Transform 字段中传入 undefined

只要 Transform 字段(position、rotation、scale)存在但值为 undefined,SDK 序列化器就会崩溃。请直接省略整个键,无论是在 main-entities.ts 字面量中还是在运行时辅助函数中都如此。在 main-entities.ts 中这很简单(不写该字段即可)。

常见模型操作

缩放
// main-entities.ts
big_statue: {
  components: {
    Transform: { position: { x: 8, y: 0, z: 8 }, scale: { x: 2, y: 2, z: 2 } },
    GltfContainer: { src: 'models/statue.glb' }
  }
}
旋转(在创作时将欧拉角转换为四元数)

四元数格式为 { x, y, z, w }。Quaternion.fromEulerDegrees(0, 90, 0) 对应的字面量是 { x: 0, y: 0.7071, z: 0, w: 0.7071 }。如果需要精确角度的旋转,又不想手动计算,请在 main-entities.ts 中将 rotation 设为单位旋转,并在运行时于 src/index.ts 中使用 Transform.getMutable(entity).rotation = Quaternion.fromEulerDegrees(0, 90, 0) 进行旋转。

父子关系

通过名称引用父实体(同一 scene 对象中的字符串键)。构建过程会在第二次遍历时将名称解析为实体 ID。

// main-entities.ts
character: {
  components: {
    Transform: { position: { x: 8, y: 0, z: 8 } },
    GltfContainer: { src: 'models/character.glb' }
  }
},
hat: {
  components: {
    Transform: {
      position: { x: 0, y: 2, z: 0 },  // 2m above parent's origin
      parent: 'character'
    },
    GltfContainer: { src: 'models/hat.glb' }
  }
}

免费 3D 模型——OpenDCL 目录(5,700+ 个模型)

目录文件位于 {baseDir}/references/model-catalog.md。每行的格式如下:

slug | dims | tris | size | category/sub | description [tags] [anim: clips] | curl command | preview: thumbnail_url
如何搜索

每次使用一个关键词搜索——先尝试最具体的词:

grep -i "zombie" {baseDir}/references/model-catalog.md

如果没有结果,请尝试同义词、更宽泛的词或相关词:

  • “sofa” → “couch” → “seat” → “furniture”
  • “car” → “vehicle” → “truck” → “van”
  • “wall” → “fence” → “barrier” → “structure”

浏览所有类别,了解有哪些可用内容:

grep "^##" {baseDir}/references/model-catalog.md

在特定类别中搜索:

grep "^##\|chair" {baseDir}/references/model-catalog.md
如何使用模型
  1. 使用不同关键词搜索目录,直到找到匹配的模型
  2. 查看搜索结果——检查尺寸、三角形数量、动画和描述
  3. 使用 curl 命令将模型下载到 models/
  4. 在代码中通过 GltfContainer.create(entity, { src: 'models/{slug}.glb' }) 引用
  5. 如果模型带有动画(列在 [anim: ...] 中),请使用 Animator 组件播放动画
  6. 放置模型后,可以获取其预览缩略图(preview: URL),查看模型外观
示例工作流
# Search for zombie models
grep -i "zombie" {baseDir}/references/model-catalog.md

# Found: zombie-purple | 2.8×2.9×0.5m | 1472 tri | 271KB | character/zombie | ...
#   [anim: Tpose, ZombieAttack, ZombieUP, ZombieWalk]
#   preview: https://models.dclregenesislabs.xyz/blobs/bafkrei...

# Download the model
curl -o models/zombie-purple.glb "https://models.dclregenesislabs.xyz/blobs/bafybeiffc..."
// main-entities.ts — declare the entity with all its initial state
import type { Scene } from '@dcl/sdk/scene-types'

export const scene = {
  zombie: {
    components: {
      Transform: { position: { x: 8, y: 0, z: 8 } },
      GltfContainer: { src: 'models/zombie-purple.glb' },
      Animator: {
        states: [
          { clip: 'ZombieWalk', playing: true, loop: true },
          { clip: 'ZombieAttack', playing: false, loop: false }
        ]
      }
    }
  }
} satisfies Scene

要在运行时切换动画(例如,点击时触发攻击),请使用 src/index.ts:

import { engine, Animator } from '@dcl/sdk/ecs'

export function main() {
  const zombie = engine.getEntityOrNullByName('zombie')
  if (zombie) Animator.playSingleAnimation(zombie, 'ZombieAttack')
}

重要:GltfContainer 仅适用于本地文件。模型的 src 字段绝不要使用外部 URL。请先将模型下载到 models/。 切勿 cd 进入 models 目录。始终在项目根目录中运行 curl:curl -o models/slug.glb "URL"。不要使用 cd models && curl -o slug.glb。

检查模型加载状态

加载状态轮询属于运行时行为——将其放入 src/index.ts,并通过名称引用实体:

import { engine, GltfContainerLoadingState, LoadingState } from '@dcl/sdk/ecs'

export function main() {
  engine.addSystem(() => {
    const model = engine.getEntityOrNullByName('zombie')
    if (!model) return
    const state = GltfContainerLoadingState.getOrNull(model)
    if (state?.currentState === LoadingState.FINISHED) {
      console.log('Model loaded successfully')
    } else if (state?.currentState === LoadingState.FINISHED_WITH_ERROR) {
      console.log('Model failed to load')
    }
  })
}

故障排除

问题原因解决方案
看不到模型文件路径错误确认文件位于相对于项目根目录的准确路径下(例如,models/myModel.glb)
看不到模型位置超出场景边界检查 Transform 位置是否在每块地块的 0-16 范围内。单块地块场景的中心为 (8, 0, 8)
看不到模型缩放值为 0 或非常小检查 Transform.scale——默认值为 (1,1,1)。如果模型导出时非常小,请尝试使用更大的值
看不到模型模型位于摄像机后方移动化身或旋转视角,使其朝向模型
模型已加载,但外观异常Y-up 与 Z-up 不匹配Decentraland 使用 Y-up。请从 Blender 重新导出,并勾选 "Y Up"
加载状态为 "FINISHED_WITH_ERROR".glb 文件已损坏或不受支持重新导出模型。使用 .glb(二进制 GLTF)格式。确保没有不受支持的扩展
点击模型没有反应缺少碰撞体在 GltfContainer 中添加 visibleMeshesCollisionMask: ColliderLayer.CL_POINTER,或添加 MeshCollider

需要针对场景限制优化模型? 请参阅 optimize-scene 技能,了解三角形数量预算和 LOD 模式。 需要为模型添加动画? 请参阅 animations-tweens 技能,了解如何使用 Animator 播放 GLTF 动画片段。

模型最佳实践

  • 为确保加载速度良好,请将每个文件中的模型控制在 50MB 以下
  • 使用 .glb 格式(二进制 GLTF)——比 .gltf 更小
  • 优化三角形数量:小型道具的每个模型尽量控制在 1,500 个三角形以下
  • 尽可能使用纹理图集来减少绘制调用
  • 含有嵌入式动画的模型可使用 Animator 组件播放
  • 测试模型方向——Decentraland 使用 Y-up 坐标系
  • 模型中的材质应使用 PBR(基于物理的渲染),以获得最佳效果

评论

支持 Markdown