Model Localizer
把 FBX / GLB 拖进 Godot 4.7 编辑器后,自动生成独立的本地 .tscn 场景并从网格烘焙凸包碰撞体。
针对 Godot 4.7.2 编写并实测。
由 Chair_PC_04.glb 生成的 Chair_PC_04_local.tscn,几何、材质、贴图完整。
绿色为烘焙出的三角网格碰撞体叠加显示 —— 它逐面贴合模型(连椅子脚轮的细节都覆盖到了),这是 Trimesh 模式的效果。用 tests/capture_dbg.tscn 渲染。
它做什么
往 3D 视口里拖入一个模型,插件会:
- 感知拖入 —— 外部文件先被移动到
res://models/(可配置),不再散落在res://根目录; - 生成本地场景 —— 在模型旁边写出
模型名_local.tscn,节点树完全独立,不再引用模型的导入结果; - 烘焙碰撞 —— 每个网格生成一个
ConvexPolygonShape3D,挂到StaticBody3D下; - 替换实例 —— 把场景里那个模型实例换成生成好的本地场景(可 Ctrl+Z 撤销)。
已经自带碰撞的模型(-col / -colonly 后缀,或导入设置里生成过碰撞)会被原样跳过,绝不重复添加。
安装
- 把整个
addons/model_localizer/目录复制到你项目的addons/下; - 打开 项目 → 项目设置 → 插件,启用 Model Localizer;
- 插件菜单出现在 项目 → 工具 下。
设置项在 项目设置 → model_localizer。
设置
| 设置 | 默认 | 说明 |
|---|---|---|
auto_process_scene_instances |
true |
自动处理拖进场景的模型 |
auto_swap_to_local |
true |
用生成的本地场景替换拖入的实例 |
auto_import_dropped_files |
true |
把外部拖入的模型文件移进下面的目录 |
dropped_files_dir |
res://models/ |
外部文件的落地目录 |
output_dir |
presets |
相对路径 = 模型同目录下的子文件夹;res:// 开头 = 用该绝对目录;留空 = 与模型同目录 |
output_suffix |
_local |
生成文件名后缀 |
add_collision |
true |
是否烘焙碰撞 |
collision_shape |
Trimesh |
Convex Hull / Trimesh / Box / Sphere / Capsule / Cylinder |
collision_body |
StaticBody3D |
StaticBody3D / RigidBody3D / Area3D / CharacterBody3D |
force_convex_hull |
true |
仅 Convex Hull 有效:用真实三角面构建凸包 |
collision_layer / collision_mask |
1 |
碰撞层 / 掩码 |
skip_dirs |
addons/, .godot/ |
永不扫描的目录 |
默认布局
按默认设置,以 Chair_PC_04.glb 为例,文件和生成的场景是这样摆放的:
assets/models/
├─ Chair_PC_04.glb ← 你拖进来的模型
├─ Chair_PC_04_Textures_4.png ← 它带来的贴图
└─ presets/
└─ Chair_PC_04_local.tscn ← 自动生成:静态物体子节点 + 三角网格碰撞
也就是说场景永远生成在「模型所在目录的 presets 子文件夹」里,该目录不存在时插件会自动创建。
形状怎么选
- Trimesh(默认)—— 逐面精确,不做凸包简化。只能用在静态物体上,顶点多时开销较大。适合地形、建筑,以及任何不需要移动的模型。
- Convex Hull —— 每个网格一个凸包。性能好,可用于会移动的物体;凹形物体会被"填平"。
- Box / Sphere / Capsule / Cylinder —— 最省性能。这几种形状会按网格的实际位置自动偏移居中。
生成的场景根节点保持单位变换
生成的 .tscn 根节点变换固定为单位阵,拖放位置记录在场景实例上。这样做有两个原因:
- Godot 会对"根节点被变换"的场景弹出警告(
节点配置警告!建议不要变换场景的根节点); - 即使你不理这个警告,Godot 里实例自身的变换优先级高于场景内根节点的变换 —— 烘焙进根节点的位置会被静默丢弃。
所以位置信息放在它真正生效的地方:实例节点。序列化时 placement_root_transform 返回给插件,由插件负责把实例摆到正确位置。可以用 tests/root_transform_warning_test.gd 复核。
手动操作
项目 → 工具 菜单:
- 处理选中模型 —— 对场景里选中的模型实例执行一次转换;
- 重置记录 —— 清空本次会话的处理记录(想重新处理同一个模型时用);
- 设置 —— 跳到项目设置页。
行为细节
- 幂等:同一个模型在同一场景里只处理一次,不会反复生成。
- 模型重导出自动同步:模型的
.import变了(重新导入 / 换文件),生成场景会跟着刷新。 - 动画安全:骨骼、
Skeleton3D、AnimationPlayer、动画轨道路径都会保留并校验。实测一个 49 节点、8 个网格、2 段动画、434 条轨道的绑定模型,转换后轨道 0 条失效。 - 碰撞几何取自网格的绑定姿态几何,按节点累积变换放置。实测每个碰撞体与对应网格的顶点包围盒完全重合(偏差 0.000000)。
关于蒙皮(绑定)模型的限制
对骨骼绑定的模型,碰撞体是按它在静止姿态下的几何生成的,而不是按运行时骨骼变形后的形状。这是静态烘焙的固有性质(游戏里常见的做法是用胶囊/凸包代替,而不是让碰撞跟随骨骼)。
需要知道的一点:某些 FBX 的绑定姿态与静止姿态互相矛盾(导出器没有保持一致)。在这类资产上,骨骼静止姿态给出的位置和渲染出来的位置对不上,没有一个统一变换能同时匹配两者。遇到这种情况,插件的碰撞体会贴合绑定姿态几何;如果你发现碰撞和看到的位置不符,换用 Convex Hull 或 Box 通常更容易得到符合直觉的结果。
需要碰撞跟随骨骼运动时,请把 collision_body 改成 RigidBody3D 或 CharacterBody3D,并自行用骨骼驱动它。
- 贴图仍共享:生成场景通过
[ext_resource]引用贴图,所以同一模型的多个实例共享贴图,不会把贴图复制多份。 - 只在编辑器里运行,不进游戏包。
常见问题
生成场景很大?
网格数据(顶点、索引、UV)内嵌在 .tscn 里,这是"完全本地化"的代价。若模型本身不大而文件异常大,说明网格顶点很多,属于正常。想减小体积可以把 collision_shape 换成 Box,或把 add_collision 关掉后单独处理。
碰撞体不跟着动画动?
烘焙出来的是静态碰撞体。动画驱动的角色请把 collision_body 改成 RigidBody3D,或按需换成 CharacterBody3D 后自己接管移动。
模型已有碰撞,插件又加了一遍?
不会。检测到子树里已有 CollisionShape3D / CollisionPolygon3D 时整体跳过。如果你想重新烘焙,先删掉旧碰撞再执行一次「处理选中模型」(并先「重置记录」)。
拖动后没反应?
检查三件事:插件是否已在项目设置里启用;模型根节点是否直接挂在当前编辑场景根下;auto_process_scene_instances 是否为 true。也可以用「处理选中模型」手动触发。
Invalid array format for surface 警告?
FBX 的压缩网格在编辑器里重载时会打印这个。已实测生成的 .tscn 重新加载后顶点数与索引数与原始完全一致,几何无损,可忽略。
测试
tests/ 下是随附的验证脚本(跑在本目录的 demo 工程里)。
# 1) 核心逻辑自测:72 项断言,覆盖路径识别、结构保真、碰撞、动画、序列化往返
godot --headless --path . --script tests/self_test.gd
# 2) 模拟「拖入模型」:先造出带模型实例的场景,再让编辑器加载它
godot --headless --path . --script tests/build_drop_scene.gd
godot --headless --editor --quit --path . --scene res://tests/drop_test.tscn
# 3) 校验生成的场景是否完好(网格表面 / 顶点 / 索引 / 碰撞)
godot --headless --path . --script tests/verify_local.gd -- \
res://assets/models/Chair_PC_04_local.tscn
# 4) 复核生成的场景根节点没有变换(不触发 Godot 的根节点警告)
godot --headless --path . --script tests/root_transform_warning_test.gd
# 5) 看一眼模型(务必带 opengl3;本机 Vulkan 不可用时默认渲染器会崩)
godot --rendering-driver opengl3 --path . res://tests/capture.tscn -- \
res://assets/models/Chair_PC_04_local.tscn shot.png
# 6) 把碰撞体以半透明绿色叠加显示
godot --rendering-driver opengl3 --path . res://tests/capture_dbg.tscn -- \
res://assets/models/Chair_PC_04_local.tscn collision.png
# 7) 物理验证:小球落到碰撞体上(窗口模式)
godot --rendering-driver opengl3 --path . res://tests/physics_test.tscn -- \
res://assets/models/Chair_PC_04_local.tscn
注意:步骤 5/6/7 依赖真实的场景树。headless
--script模式下SceneTree.root本身不在树里,global_transform不会传播 —— 任何涉及变换传播的检查都会读到 单位阵而给出假结果(包括假通过)。所以这类验证必须走编辑器--editor, 或带--rendering-driver opengl3以窗口模式运行。
实测结果
在 Chair_PC_04.glb(8 670 面)与 GruffaloMouse.fbx(18 126 面、49 节点、骨骼 + 434 条动画轨道)上:
| 检查项 | GLB | FBX |
|---|---|---|
| 节点数(不含碰撞) | 2 → 2 | 49 → 49 |
| 网格实例 | 1 → 1 | 8 → 8 |
| 索引数 | 8 670 → 8 670 | 18 126 → 18 126 |
| 骨骼 / 动画播放器 | — | 1 → 1 / 1 → 1 |
| 动画轨道失效 | 0 | 0 / 434 |
| 碰撞体与网格顶点包围盒偏差 | 0.000000 | 0.000000(8/8 个网格) |
| 小球落到碰撞体上 | PASS | 见上方蒙皮限制说明 |
| 生成场景重量校验 | PASS | PASS |
工程结构
demo_project/
├─ addons/model_localizer/ ← 插件本体(复制这个目录即可)
│ ├─ plugin.cfg
│ ├─ model_localizer_plugin.gd 编辑器集成(探测 / 生成 / 替换)
│ ├─ model_localizer_core.gd 核心逻辑(纯逻辑,可 headless 自测)
│ ├─ localizer_settings.gd 项目设置
│ └─ README.md
├─ assets/models/ 测试模型(GLB + 带骨骼动画的 FBX)
├─ docs/ 截图
└─ tests/ 验证脚本
Model Localizer · 面向 Godot 4.7.2

