Feat/14 movement overhaul #20
@@ -56,7 +56,7 @@ func load_model(path: String) -> void:
|
|||||||
add_child(scene)
|
add_child(scene)
|
||||||
print("SkinnedPlayerModel: scene added: %s" % scene.name)
|
print("SkinnedPlayerModel: scene added: %s" % scene.name)
|
||||||
|
|
||||||
# Model scale — the Miku GLB is already exported at meter scale (~1.6m tall).
|
# Apply model scale (the Miku GLB is already in meters, ~1.6m tall)
|
||||||
# For models exported in centimeters (common from Mixamo/Blender), set scale_factor to 0.01.
|
# For models exported in centimeters (common from Mixamo/Blender), set scale_factor to 0.01.
|
||||||
scene.scale = Vector3(scale_factor, scale_factor, scale_factor)
|
scene.scale = Vector3(scale_factor, scale_factor, scale_factor)
|
||||||
print("SkinnedPlayerModel: applied scale %.4f" % scale_factor)
|
print("SkinnedPlayerModel: applied scale %.4f" % scale_factor)
|
||||||
@@ -68,6 +68,18 @@ func load_model(path: String) -> void:
|
|||||||
if mesh_instances.size() > 0:
|
if mesh_instances.size() > 0:
|
||||||
_mesh_instance = mesh_instances[0]
|
_mesh_instance = mesh_instances[0]
|
||||||
|
|
||||||
|
# Fix skeleton binding: GLTF imports set MeshInstance.skeleton to ".."
|
||||||
|
# which should work for a direct parent, but in our case the MeshInstance
|
||||||
|
# is a child of Skeleton3D which is a child of MikuRig. The skeleton
|
||||||
|
# property needs to point to the Skeleton3D node for skin deformation.
|
||||||
|
# Set it explicitly to the skeleton's path relative to MeshInstance.
|
||||||
|
if _mesh_instance and skeleton:
|
||||||
|
# Manually compute relative path since get_relative_path_to is not
|
||||||
|
# exposed for MeshInstance3D in Godot 4.2.
|
||||||
|
# MeshInstance is child of Skeleton3D, so ".." points to Skeleton3D.
|
||||||
|
_mesh_instance.skeleton = NodePath("..")
|
||||||
|
print("SkinnedPlayerModel: fixed mesh skeleton binding -> '%s'" % _mesh_instance.skeleton)
|
||||||
|
|
||||||
if first_person_mode and _mesh_instance:
|
if first_person_mode and _mesh_instance:
|
||||||
_setup_first_person()
|
_setup_first_person()
|
||||||
|
|
||||||
@@ -88,6 +100,10 @@ func load_model(path: String) -> void:
|
|||||||
# Find AnimationPlayer
|
# Find AnimationPlayer
|
||||||
animation_player = _find_animation_player(scene)
|
animation_player = _find_animation_player(scene)
|
||||||
if animation_player:
|
if animation_player:
|
||||||
|
# Ensure AnimationPlayer processes even if parent has process_mode disabled
|
||||||
|
animation_player.process_mode = Node.PROCESS_MODE_INHERIT
|
||||||
|
animation_player.active = true
|
||||||
|
print("SkinnedPlayerModel: AP process_mode=%d active=%s" % [animation_player.process_mode, animation_player.active])
|
||||||
var anim_list = animation_player.get_animation_list()
|
var anim_list = animation_player.get_animation_list()
|
||||||
print("SkinnedPlayerModel: %d animations available:" % anim_list.size())
|
print("SkinnedPlayerModel: %d animations available:" % anim_list.size())
|
||||||
for anim_name in anim_list:
|
for anim_name in anim_list:
|
||||||
@@ -286,12 +302,20 @@ func _add_animation_to_player(ap: AnimationPlayer, anim_name: String, anim: Anim
|
|||||||
lib.add_animation(anim_name, anim)
|
lib.add_animation(anim_name, anim)
|
||||||
print("SkinnedPlayerModel: added '%s' (%d tracks)" % [anim_name, anim.get_track_count()])
|
print("SkinnedPlayerModel: added '%s' (%d tracks)" % [anim_name, anim.get_track_count()])
|
||||||
|
|
||||||
|
func _update_skin() -> void:
|
||||||
|
## Force skeleton skin update by notifying the Skeleton3D that bones changed.
|
||||||
|
## GLTF runtime loaded models sometimes need this because the AnimationPlayer's
|
||||||
|
## internal bone pose updates don't always trigger the visual pipeline.
|
||||||
|
# Re-apply current bone poses to ensure dirty flags are set.
|
||||||
|
# This triggers NOTIFICATION_UPDATE_SKELETON on the Skeleton3D.
|
||||||
|
for i in range(skeleton.get_bone_count()):
|
||||||
|
skeleton.set_bone_pose(i, skeleton.get_bone_pose(i))
|
||||||
|
|
||||||
func _setup_first_person() -> void:
|
func _setup_first_person() -> void:
|
||||||
# In first person, hide the head and upper body so only arms/hands/legs show
|
# In first person, hide the head and upper body so only arms/hands/legs show
|
||||||
# This creates the classic FPS arms-only view
|
# This creates the classic FPS arms-only view
|
||||||
if not _mesh_instance:
|
if not _mesh_instance:
|
||||||
return
|
return
|
||||||
print("SkinnedPlayerModel: setting up first-person mode")
|
|
||||||
# The mesh has multiple sub-meshes (body parts). We need to hide the ones
|
# The mesh has multiple sub-meshes (body parts). We need to hide the ones
|
||||||
# that are above the chest (head, torso, skirt, etc.)
|
# that are above the chest (head, torso, skirt, etc.)
|
||||||
# For now, we hide the entire mesh and rely on the arms being separate
|
# For now, we hide the entire mesh and rely on the arms being separate
|
||||||
@@ -338,6 +362,7 @@ func play_animation(anim_name: String) -> void:
|
|||||||
|
|
||||||
var _last_played: String = ""
|
var _last_played: String = ""
|
||||||
var _missing_anim_warned: Array = []
|
var _missing_anim_warned: Array = []
|
||||||
|
var _skin_update_acc: float = 0.0
|
||||||
|
|
||||||
func _warn_missing(anim_name: String) -> void:
|
func _warn_missing(anim_name: String) -> void:
|
||||||
## Warn once per missing animation, not every frame.
|
## Warn once per missing animation, not every frame.
|
||||||
@@ -359,6 +384,15 @@ func _process(delta: float) -> void:
|
|||||||
elif _last_played == "":
|
elif _last_played == "":
|
||||||
print("SkinnedPlayerModel: NOT playing")
|
print("SkinnedPlayerModel: NOT playing")
|
||||||
|
|
||||||
|
# Force skeleton skin update — GLTF runtime loaded models need explicit
|
||||||
|
# bone pose re-application to trigger MeshInstance vertex deformation.
|
||||||
|
# Throttled to ~30fps to avoid per-frame overhead.
|
||||||
|
if skeleton and animation_player.is_playing():
|
||||||
|
_skin_update_acc += delta
|
||||||
|
if _skin_update_acc > 0.033:
|
||||||
|
_skin_update_acc = 0.0
|
||||||
|
_update_skin()
|
||||||
|
|
||||||
# Get movement state from parent PlayerMovementController
|
# Get movement state from parent PlayerMovementController
|
||||||
var parent = get_parent()
|
var parent = get_parent()
|
||||||
if not parent:
|
if not parent:
|
||||||
|
|||||||
Reference in New Issue
Block a user