WIP checkpoint: fix static registry, uint32 type, GLTF API calls, ARRAY_BONES, missing slot handler
- avatar_slot_registry.gd: _slot_data/_slot_definitions made static (every caller uses AvatarSlotRegistry.method() on the class, never an instance, so the old instance _init() populating them was never invoked); moved registry build to static func _static_init() - avatar_catalog.gd: compute_loadout_hash() return type uint32 -> int (GDScript has no fixed-width uint type) - avatar_loader.gd: GLTFDocument.parse() (doesn't exist) -> the real append_from_buffer()+generate_scene() API; Error.parse_error() (doesn't exist) -> error_string(); Mesh.ARRAY_JOINTS (doesn't exist) -> Mesh.ARRAY_BONES; removed a dead 'mesh is MultiMesh' check that can never be true (MeshInstance3D.mesh is always Mesh, never MultiMesh); fixed _merge_node_into_avatar to recurse into a Skeleton3D's children (skinned meshes are commonly children of the skeleton in a GLB export, the old code returned before ever reaching them) - avatar_customization_ui.gd: added the missing _on_slot_button_pressed handler (referenced by a button connect, never defined -- just calls the existing _select_slot()) Verified: godot --headless --import + --quit is fully clean, and the avatar_customization.tscn scene now instantiates successfully. Remaining, scoped separately: avatar_visualizer.gd calls a AvatarLoader.load_slot() static async API and SlotItem/SlotResult types that don't exist -- AvatarLoader's real API (matching the project's own avatar.md spec: one canonical skeleton, attachments retargeted onto it) is instance-based load_base_body()/load_attachment(). AvatarVisualizer needs rewriting to use that real API; it's the one out of step with spec, not AvatarLoader.
This commit is contained in:
@@ -305,28 +305,26 @@ func _parse_glb_as_avatar(glb_data: PackedByteArray, path: String) -> Node3D:
|
||||
avatar.add_child(attachments_container)
|
||||
|
||||
# Parse the GLB and extract skeleton + meshes.
|
||||
# Godot's GLTFState allows custom parsing.
|
||||
var gltf_document := GLTFDocument.new()
|
||||
var gltf_state := GLTFState.new()
|
||||
|
||||
var parse_result := gltf_document.parse(glb_data, gltf_state, "", "")
|
||||
var parse_result := gltf_document.append_from_buffer(glb_data, "", gltf_state)
|
||||
if parse_result != OK:
|
||||
push_error("GLB parse error: %s" % Error.parse_error(parse_result))
|
||||
push_error("GLB parse error: %s" % error_string(parse_result))
|
||||
avatar.queue_free()
|
||||
return null
|
||||
|
||||
# Export the scene from GLTFState.
|
||||
var root_nodes := gltf_state.get_nodes_as_resources()
|
||||
if root_nodes.is_empty():
|
||||
push_error("No root nodes in GLB: %s" % path)
|
||||
# Generate the actual scene tree from the parsed GLTF state.
|
||||
var scene_root := gltf_document.generate_scene(gltf_state)
|
||||
if scene_root == null:
|
||||
push_error("Failed to generate scene from GLB: %s" % path)
|
||||
avatar.queue_free()
|
||||
return null
|
||||
|
||||
# Merge GLB nodes into our avatar structure.
|
||||
for node_resource in root_nodes:
|
||||
if node_resource is Node3D:
|
||||
_merge_node_into_avatar(node_resource, avatar, body_meshes,
|
||||
attach_points, attachments_container)
|
||||
# Merge GLB nodes into our avatar structure (recurses into children).
|
||||
if scene_root is Node3D:
|
||||
_merge_node_into_avatar(scene_root, avatar, body_meshes,
|
||||
attach_points, attachments_container)
|
||||
|
||||
return avatar
|
||||
|
||||
@@ -338,22 +336,21 @@ func _parse_glb_as_attachment(glb_data: PackedByteArray, path: String,
|
||||
var gltf_document := GLTFDocument.new()
|
||||
var gltf_state := GLTFState.new()
|
||||
|
||||
var parse_result := gltf_document.parse(glb_data, gltf_state, "", "")
|
||||
var parse_result := gltf_document.append_from_buffer(glb_data, "", gltf_state)
|
||||
if parse_result != OK:
|
||||
push_error("GLB parse error: %s" % Error.parse_error(parse_result))
|
||||
push_error("GLB parse error: %s" % error_string(parse_result))
|
||||
return null
|
||||
|
||||
var root_nodes := gltf_state.get_nodes_as_resources()
|
||||
if root_nodes.is_empty():
|
||||
push_error("No root nodes in attachment GLB: %s" % path)
|
||||
var scene_root := gltf_document.generate_scene(gltf_state)
|
||||
if scene_root == null:
|
||||
push_error("Failed to generate scene from attachment GLB: %s" % path)
|
||||
return null
|
||||
|
||||
var attachment := Node3D.new()
|
||||
attachment.name = "%s_%s" % [slot_id, path.get_file().get_basename()]
|
||||
|
||||
for node_resource in root_nodes:
|
||||
if node_resource is Node3D:
|
||||
attachment.add_child(node_resource.duplicate())
|
||||
if scene_root is Node3D:
|
||||
attachment.add_child(scene_root.duplicate())
|
||||
|
||||
return attachment
|
||||
|
||||
@@ -392,6 +389,13 @@ func _merge_node_into_avatar(node: Node3D, avatar: Node3D,
|
||||
var dst_parent_idx := dst_skel.find_bone(parent_name)
|
||||
if dst_parent_idx >= 0:
|
||||
dst_skel.set_bone_parent(new_idx, dst_parent_idx)
|
||||
# Skinned meshes are commonly children of the skeleton node in a GLB
|
||||
# export, not siblings — fall through to the recursion below instead
|
||||
# of returning, so they still get merged in.
|
||||
for child in src_skel.get_children():
|
||||
if child is Node3D:
|
||||
_merge_node_into_avatar(child, avatar, body_meshes,
|
||||
attach_points, attachments_container)
|
||||
return
|
||||
|
||||
# Identify attachment point nodes (empty nodes named attach_*).
|
||||
@@ -468,18 +472,16 @@ func _retarget_skin(attachment_node: Node3D) -> void:
|
||||
if mesh == null:
|
||||
continue
|
||||
|
||||
# Handle MultiMeshInstance3D.
|
||||
if mesh is MultiMesh:
|
||||
# MultiMesh doesn't have skin weights per-vertex in the same way.
|
||||
continue
|
||||
|
||||
# Note: MeshInstance3D.mesh is always a Mesh, never a MultiMesh —
|
||||
# MultiMesh skinning goes through MultiMeshInstance3D.multimesh,
|
||||
# a completely separate node/property, not reachable here.
|
||||
if mesh is ArrayMesh:
|
||||
var arr_mesh := mesh as ArrayMesh
|
||||
var surface_count := arr_mesh.get_surface_count()
|
||||
|
||||
for surface_idx in surface_count:
|
||||
var arrays := arr_mesh.surface_get_arrays(surface_idx)
|
||||
var joints_array := arrays[Mesh.ARRAY_JOINTS] as PackedInt32Array
|
||||
var joints_array := arrays[Mesh.ARRAY_BONES] as PackedInt32Array
|
||||
var weights_array := arrays[Mesh.ARRAY_WEIGHTS] as PackedFloat32Array
|
||||
|
||||
if joints_array.is_empty():
|
||||
@@ -494,10 +496,10 @@ func _retarget_skin(attachment_node: Node3D) -> void:
|
||||
new_joints.append(0)
|
||||
else:
|
||||
var bone_name := skeleton.get_bone_name(old_joint)
|
||||
var new_idx := _bone_index_map.get(bone_name, 0)
|
||||
var new_idx: int = _bone_index_map.get(bone_name, 0)
|
||||
new_joints.append(new_idx)
|
||||
|
||||
arrays[Mesh.ARRAY_JOINTS] = new_joints
|
||||
arrays[Mesh.ARRAY_BONES] = new_joints
|
||||
arr_mesh.surface_set_arrays(surface_idx, arrays)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user