feat(client): add BoneAttachment3D overhead anchor above Head bone (#712)
Infrastructure for floating UI elements (status indicators, thought bubbles, alert markers). Marker3D at Vector3(0, 0.3, 0) offset from Head bone, exposed via get_overhead_anchor() public API. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,7 @@ extends Node3D
|
|||||||
## get_clothing_node_count() — number of clothing MeshInstance3D nodes
|
## get_clothing_node_count() — number of clothing MeshInstance3D nodes
|
||||||
## get_accessory_node_count() — number of accessory BoneAttachment3D nodes
|
## get_accessory_node_count() — number of accessory BoneAttachment3D nodes
|
||||||
## get_skin_tone_texture_name(index) — skin tone texture filename key for index
|
## get_skin_tone_texture_name(index) — skin tone texture filename key for index
|
||||||
|
## get_overhead_anchor() — Marker3D above Head bone for floating UI (#712)
|
||||||
##
|
##
|
||||||
## D-159 (11 body types), D-160 (18 segments), D-161 (head separate),
|
## D-159 (11 body types), D-160 (18 segments), D-161 (head separate),
|
||||||
## D-162 (clothing pre-fitted), D-163 (heads via BoneAttachment3D), D-164 (skeleton fork)
|
## D-162 (clothing pre-fitted), D-163 (heads via BoneAttachment3D), D-164 (skeleton fork)
|
||||||
@@ -101,6 +102,8 @@ var _clothing_meshes: Array[MeshInstance3D] = []
|
|||||||
var _bone_attachments: Array[BoneAttachment3D] = []
|
var _bone_attachments: Array[BoneAttachment3D] = []
|
||||||
var _outline_nodes: Array[MeshInstance3D] = []
|
var _outline_nodes: Array[MeshInstance3D] = []
|
||||||
var _accessory_attachments: Array[BoneAttachment3D] = []
|
var _accessory_attachments: Array[BoneAttachment3D] = []
|
||||||
|
var _overhead_anchor: Marker3D = null
|
||||||
|
var _overhead_attachment: BoneAttachment3D = null
|
||||||
|
|
||||||
# Inspectable state for tests
|
# Inspectable state for tests
|
||||||
var _active_torso_variant: String = "full"
|
var _active_torso_variant: String = "full"
|
||||||
@@ -218,6 +221,13 @@ func get_skin_tone_texture_name(index: int) -> String:
|
|||||||
return SKIN_TONES[clampi(index, 0, SKIN_TONES.size() - 1)]["tex"]
|
return SKIN_TONES[clampi(index, 0, SKIN_TONES.size() - 1)]["tex"]
|
||||||
|
|
||||||
|
|
||||||
|
## Return the overhead anchor Marker3D (#712). Null if skeleton not loaded.
|
||||||
|
## Anchor point for floating UI elements: status indicators, thought bubbles,
|
||||||
|
## alert markers, speech icons. Positioned ~0.3m above the Head bone.
|
||||||
|
func get_overhead_anchor() -> Marker3D:
|
||||||
|
return _overhead_anchor
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Internal — teardown
|
# Internal — teardown
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -234,6 +244,13 @@ func _clear() -> void:
|
|||||||
node.free()
|
node.free()
|
||||||
_outline_nodes.clear()
|
_outline_nodes.clear()
|
||||||
|
|
||||||
|
# #712: free overhead anchor before general bone attachments
|
||||||
|
if is_instance_valid(_overhead_attachment) and _overhead_attachment.get_parent():
|
||||||
|
_overhead_attachment.get_parent().remove_child(_overhead_attachment)
|
||||||
|
_overhead_attachment.free()
|
||||||
|
_overhead_attachment = null
|
||||||
|
_overhead_anchor = null
|
||||||
|
|
||||||
for att in _bone_attachments:
|
for att in _bone_attachments:
|
||||||
if is_instance_valid(att) and att.get_parent():
|
if is_instance_valid(att) and att.get_parent():
|
||||||
att.get_parent().remove_child(att)
|
att.get_parent().remove_child(att)
|
||||||
@@ -291,6 +308,7 @@ func _load_skeleton() -> void:
|
|||||||
for m in armature_meshes:
|
for m in armature_meshes:
|
||||||
print(" armature mesh: ", m.name, " visible=", m.visible)
|
print(" armature mesh: ", m.name, " visible=", m.visible)
|
||||||
_validate_slot_bones()
|
_validate_slot_bones()
|
||||||
|
_create_overhead_anchor()
|
||||||
|
|
||||||
|
|
||||||
## Warn on any SLOT_TO_BONE entry that doesn't exist in the loaded skeleton.
|
## Warn on any SLOT_TO_BONE entry that doesn't exist in the loaded skeleton.
|
||||||
@@ -302,6 +320,27 @@ func _validate_slot_bones() -> void:
|
|||||||
push_warning("CharacterVisual: SLOT_TO_BONE['%s'] = '%s' — bone not found in skeleton" % [slot, bone_name])
|
push_warning("CharacterVisual: SLOT_TO_BONE['%s'] = '%s' — bone not found in skeleton" % [slot, bone_name])
|
||||||
|
|
||||||
|
|
||||||
|
## #712: Create a Marker3D anchored ~0.3m above the Head bone via BoneAttachment3D.
|
||||||
|
## Anchor point for floating UI elements (status indicators, thought bubbles, etc.).
|
||||||
|
func _create_overhead_anchor() -> void:
|
||||||
|
if _skeleton == null:
|
||||||
|
return
|
||||||
|
var bone_idx := _skeleton.find_bone("Head")
|
||||||
|
if bone_idx == -1:
|
||||||
|
push_warning("CharacterVisual: Head bone not found — overhead anchor not created")
|
||||||
|
return
|
||||||
|
_overhead_attachment = BoneAttachment3D.new()
|
||||||
|
_overhead_attachment.bone_name = "Head"
|
||||||
|
_overhead_attachment.bone_idx = bone_idx
|
||||||
|
_overhead_attachment.name = "OverheadAttachment"
|
||||||
|
_skeleton.add_child(_overhead_attachment)
|
||||||
|
|
||||||
|
_overhead_anchor = Marker3D.new()
|
||||||
|
_overhead_anchor.name = "OverheadAnchor"
|
||||||
|
_overhead_anchor.position = Vector3(0, 0.3, 0)
|
||||||
|
_overhead_attachment.add_child(_overhead_anchor)
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Internal — body segments (D-160)
|
# Internal — body segments (D-160)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user