PC3 Phase 1.2: placeholder avatar asset pack + make the loader actually assemble

assets/avatars/catalog.json referenced 14 GLB files that were never in the
repo, so the avatar system could not load a single avatar. And the loader's
assembly path had never run against a real skeleton -- it called
Skeleton3D.has_bone() (no such method in Godot 4) and hand-copied bones in a
way that dropped the skin binding.

- scripts/tools/gen_placeholder_avatars.gd: generates the pack headlessly via
  GLTFDocument -- one rigged/skinned base body (the full 24-bone canonical
  rig from a T-pose) + 13 primitive attachments. Crude on purpose; real art
  swaps in as a pure content change (catalog paths unchanged).
- assets/avatars/*.glb: the generated pack (checked in).
- avatar_loader.gd: rewrote _parse_glb_as_avatar to lift the skin-bound
  Skeleton3D that generate_scene() already produces, instead of rebuilding it.
  Attachments now mount via BoneAttachment3D to the catalog's mount_bone and
  are actually added to the avatar tree (they were parsed then dropped).
  Fixed the has_bone() crash, the null ARRAY_BONES cast in _retarget_skin,
  and the Array[String] type mismatch on the accessory list.
- smoke_test_avatar_system.gd: replaced the "asserts it fails gracefully
  because the files don't exist" section with a real assembly check --
  Skeleton3D present, full 24-bone rig, canonical bones resolve by name,
  visible body mesh, equipped items mounted to bones. 14/14 from a cold cache.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015jtxAiu5dAW4WxCNdGkrkJ
This commit is contained in:
2026-09-05 21:38:00 +00:00
parent f51c49fcfa
commit d6cd3293ae
31 changed files with 1045 additions and 154 deletions
Binary file not shown.
@@ -0,0 +1,42 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bm01jsuomi50i"
path="res://.godot/imported/accessory_belt_01.glb-a2b6be72b4422067d290c84868489d2d.scn"
[deps]
source_file="res://assets/avatars/accessory_belt_01.glb"
dest_files=["res://.godot/imported/accessory_belt_01.glb-a2b6be72b4422067d290c84868489d2d.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
Binary file not shown.
@@ -0,0 +1,42 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://dvou487niwnjt"
path="res://.godot/imported/accessory_necklace_01.glb-75d713dbb1ff9579d6f0bf8b7ddde1e2.scn"
[deps]
source_file="res://assets/avatars/accessory_necklace_01.glb"
dest_files=["res://.godot/imported/accessory_necklace_01.glb-75d713dbb1ff9579d6f0bf8b7ddde1e2.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
Binary file not shown.
@@ -0,0 +1,42 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://dknbtq33pssf4"
path="res://.godot/imported/back_backpack_01.glb-d0ccc39aed8e86403d649c8d07008591.scn"
[deps]
source_file="res://assets/avatars/back_backpack_01.glb"
dest_files=["res://.godot/imported/back_backpack_01.glb-d0ccc39aed8e86403d649c8d07008591.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
Binary file not shown.
@@ -0,0 +1,42 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://brfp6g6pcbbw7"
path="res://.godot/imported/base_human_male_01.glb-c917b85c8f8ab5d1974812a6891f052f.scn"
[deps]
source_file="res://assets/avatars/base_human_male_01.glb"
dest_files=["res://.godot/imported/base_human_male_01.glb-c917b85c8f8ab5d1974812a6891f052f.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
Binary file not shown.
+42
View File
@@ -0,0 +1,42 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bva2etlpa8act"
path="res://.godot/imported/chest_bandit_01.glb-90867999feb79e02c469ef45d970f0e1.scn"
[deps]
source_file="res://assets/avatars/chest_bandit_01.glb"
dest_files=["res://.godot/imported/chest_bandit_01.glb-90867999feb79e02c469ef45d970f0e1.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
Binary file not shown.
+42
View File
@@ -0,0 +1,42 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://ctwcxf552y325"
path="res://.godot/imported/face_glasses_01.glb-fe06ebb12e6caf265dce06de37a74fc5.scn"
[deps]
source_file="res://assets/avatars/face_glasses_01.glb"
dest_files=["res://.godot/imported/face_glasses_01.glb-fe06ebb12e6caf265dce06de37a74fc5.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
Binary file not shown.
+42
View File
@@ -0,0 +1,42 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://j4dc6l3l0j6f"
path="res://.godot/imported/hair_mohawk_01.glb-f2a6883730b5d02facb56d1dbb0e371e.scn"
[deps]
source_file="res://assets/avatars/hair_mohawk_01.glb"
dest_files=["res://.godot/imported/hair_mohawk_01.glb-f2a6883730b5d02facb56d1dbb0e371e.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
Binary file not shown.
@@ -0,0 +1,42 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://dp4vqp2trrank"
path="res://.godot/imported/hair_ponytail_01.glb-cdf5913fc63278b1dd362cb640c2ecb4.scn"
[deps]
source_file="res://assets/avatars/hair_ponytail_01.glb"
dest_files=["res://.godot/imported/hair_ponytail_01.glb-cdf5913fc63278b1dd362cb640c2ecb4.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
Binary file not shown.
+42
View File
@@ -0,0 +1,42 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bkp35fnns36xb"
path="res://.godot/imported/head_cyber_01.glb-8081a99d6b31c121d208dd2f9629df5c.scn"
[deps]
source_file="res://assets/avatars/head_cyber_01.glb"
dest_files=["res://.godot/imported/head_cyber_01.glb-8081a99d6b31c121d208dd2f9629df5c.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
Binary file not shown.
@@ -0,0 +1,42 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bgfjpdu8kg0aj"
path="res://.godot/imported/left_foot_boot_01.glb-cab36c9e44befe0a3ff50ee651f2ff67.scn"
[deps]
source_file="res://assets/avatars/left_foot_boot_01.glb"
dest_files=["res://.godot/imported/left_foot_boot_01.glb-cab36c9e44befe0a3ff50ee651f2ff67.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
Binary file not shown.
@@ -0,0 +1,42 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://dtq7tgio1iymd"
path="res://.godot/imported/left_hand_glove_01.glb-4b237bea3299c6604547afa353a7b602.scn"
[deps]
source_file="res://assets/avatars/left_hand_glove_01.glb"
dest_files=["res://.godot/imported/left_hand_glove_01.glb-4b237bea3299c6604547afa353a7b602.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
Binary file not shown.
+42
View File
@@ -0,0 +1,42 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://yo204q1m6hs3"
path="res://.godot/imported/pants_cargo_01.glb-00f3e3b72fd692344cbb8bfd2b8449f9.scn"
[deps]
source_file="res://assets/avatars/pants_cargo_01.glb"
dest_files=["res://.godot/imported/pants_cargo_01.glb-00f3e3b72fd692344cbb8bfd2b8449f9.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
Binary file not shown.
@@ -0,0 +1,42 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://30ij0eapm6ti"
path="res://.godot/imported/right_foot_boot_01.glb-ffff27e561e5632596b7ba89fee5b5ea.scn"
[deps]
source_file="res://assets/avatars/right_foot_boot_01.glb"
dest_files=["res://.godot/imported/right_foot_boot_01.glb-ffff27e561e5632596b7ba89fee5b5ea.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
Binary file not shown.
@@ -0,0 +1,42 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bcrtxtp47dv8h"
path="res://.godot/imported/right_hand_glove_01.glb-a59875b630ac413350c3bdf6acc84bdf.scn"
[deps]
source_file="res://assets/avatars/right_hand_glove_01.glb"
dest_files=["res://.godot/imported/right_hand_glove_01.glb-a59875b630ac413350c3bdf6acc84bdf.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1