Well here we go. I wanted to create character face deformation controls, but cant understand why the bone positions are flipped. Also moving them is flipped.

Based on previous script: https://godotforums.org/d/41001-createscale-plane-to-field-of-view-camera-extents/3

I have blender scene import:

extends Node3D
@onready var skeleton_3d: Skeleton3D = $Armature_001/Skeleton3D
@onready var ball = preload("res://ball_node.tscn")
@export var cam:Camera3D
@onready var ball_nodes: Node3D = $BallNodes

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	get_bones()
	pass # Replace with function body.


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	pass


func get_bones():
	var bone_count = skeleton_3d.get_bone_count()
	
	if bone_count <1:
		return
	for b_index in range(0,bone_count-1):
		
		
		var b_pos = skeleton_3d.get_bone_global_pose(b_index)
		
		var pos =  skeleton_3d.global_transform * b_pos 
		
		
		var new_ball:Ball = ball.instantiate()
		new_ball.ball_position_changed.connect(_on_ball_position_changed)
		new_ball.bone_index = b_index
		new_ball.cam = cam
		
		new_ball.transform = pos
		#new_ball.transform = new_ball.transform.rotated(-Vector3.UP,0)
		ball_nodes.add_child(new_ball)
	pass
	
func _on_ball_position_changed(bone_index, bone_pos):
	skeleton_3d.set_bone_pose_position(bone_index,bone_pos)
	pass

But the balls spawn mirrored or 180° from the original skeleton. And when i try to move them, moving mouse right the ball goes left..

Not sure why i can only see few bones, (only nose)

Blender view

Video demo. Notice the top (detached ball) is not attached to any bone, and i can move it left right perfectly fine, while the other ones that are spawned based on bone position move opposite.

  • xyz replied to this.

    kuligs2

    new_ball.global_transform = pos

      xyz nope, already tried that.. the only way to rotate the balls into place of bones is to do this, but this wont solve the flipped action when you try to move them with mouse

      new_ball.global_transform = pos.rotated(Vector3.UP,PI)

      If you rotate from behind the face and try to move the ball then it moves but its flipped.

      • xyz replied to this.

        kuligs2 Do you use global_position instead of position in the movement code as I suggested in your previous post?

          xyz yeah, it makes no difference. here is project.

          camera-controls-rotation.zip
          3MB

          i deleted the blend files because they are too big. if you cant open the project without them let me know.

          main.tscn is the main world that holds garbanzo_anim3.tscn
          garbanzo_anim3.tscn spawns the balls inside it.
          ball_node.tscn holds info and movement about the ball.

          • xyz replied to this.

            kuligs2 Make a minimal example using minimal blender stuff, few joints skeleton, without a mesh.

              xyz what do you mean minimal? 😃 this is the minimal it has like 14 bones or something that i placed manually.
              without mesh? then whats the point if you cant see the deformation?.

              The problem is that somehow the skeleton3d global transform is rotated 180 for some reason. maybe its a bug?

              ill try to do that later but what am i testing in the minimal project? i will still have to have a ball node, a camera and a mesh with bones.

              • xyz replied to this.

                kuligs2 what do you mean minimal? 😃 this is the minimal it has like 14 bones or something that i placed manually.
                without mesh? then whats the point if you cant see the deformation?.

                Two bones, so the asset file size is minimal. Your problem is not with mesh deformation, it's with joint transforms. Nothing to do with the mesh. Cultivate the ability to fully isolate the problem. That's what "minimal" means in the "minimal reproduction project".

                kuligs2 maybe its a bug?

                Of course it's a bug. It's just much much more likely it's situated in your code, not in the engine code.

                  xyz idk.. well i recreated with simpler char body and few bones.
                  Kinda works, kinda doesnt.

                  Bone manipulation works but not all bones are located where they are in blender.

                  bone-manipulation-43.zip
                  149kB

                  The arm bones.. hmm. maybe the bone base is at the junction/joint? there are few balls that spawn at the same spot.

                  But the head/nose bone is not visible in godot but it is present in blender and i can move it around. Ofc.. blender does not allow them arm bones to be moved like i do in godot (only rotation is allowed)

                  • xyz replied to this.

                    kuligs2 Then it doesn't reproduce the initial problem. Create the exact same hierarchy above the joints as initially, with all the same transforms (in Blender and in Godot), just use few bones and no mesh. Some of those parent transforms may be messing the things when transforming from/to local joint spaces, if your code doesn't properly take them into account.

                    Replicate the initial problem in a minimal project, check if the project is self contained, then post it.

                      xyz Create the exact same hierarchy above the joints as initially, with all the same transforms (in Blender and in Godot)

                      i just did that, and i posted the project above. Not sure what you mean joints, but i created new skeleton armature with new bones and simple box mesh. I never rotated armature in blender, not in this or previous projects.

                      Not sure what you mean..

                      But later i will play more with this..

                      But i still dont understand why the additional bones (free floating) are not visible in godot, while they are in blender.

                      • xyz replied to this.

                        kuligs2 Your initial complaint was about things moving left-right in the direction opposite of mouse. In your attempt to reproduce that problem in a minimal project, this is apparently no more the case. So what's the actual problem here?

                          xyz problem is more of a question, why not all bones are visible in correct locations in skeleton3d and why as previously stated the left right movement is flipped in some cases?

                          I will do more tests.

                          But i will never be able to replicate the initial face armature because it was hand made (bone placement). And recreating simple skeleton, kinda fixed the left-right movement problem but the floating bone problem is still persistent.

                          • xyz replied to this.

                            kuligs2 The bone misplacement is likely due to improper setup in Blender or import/export process. So double check there and try a different file format. If you want to get it checked by somebody else, again make a minimal Blender file that still causes the problem.

                            When hunting bugs always proceed by cutting down their potential dwelling space little by little until they are completely cornered, hence easy to probe and eventually catch. Just like you'd do with an actual insect buzzing around your house.

                              xyz Interesting observation, godot cuts off last bone.
                              Previously in blender..

                              Added more bones

                              Now the root bones are visible in godot

                              Global transforms seem to be correct now, (new blend file, reimpored from scratch)

                              And now manipulating the root bone of that bone chain works kinda, still jumping around like mad sometimes, but manipulating the tip bone is still opposite,random.

                              So maybe my question is - is there a better way to create manipulators to pose the mesh in godot game? like for character creation, you create hooks/grabbing points for user to grab and stretch to create the face they want, record the position of the manipulations and then on model load apply these manipulations to have the consistent facial deform.

                              I thought the bone manipulation would be easy, it seem its not so...

                                @xyz also i noticed this, that the old project had the node rotated 180 deg, and this messes up the transforms. and manipulations.

                                  I think i know whats the problem. When the ball is clicked it rotates to look at camera, but when ball is moved the bone sets the ball global position and transform and so the ball rotates back facing away or whatever and so this messes up the position left-right, so in one frame it tries to go left in other right because it flips from looking at camera to the transform of the bone wich is different..

                                  Ill try to fix this tomorrow. 😃

                                  • xyz replied to this.

                                    kuligs2 So maybe my question is - is there a better way to create manipulators to pose the mesh in godot game?

                                    If I understand correctly, you're trying to use bones for what is normally done with shapekeys. That's a very interesting approach. I'll keep an eye on the topic. My plan is to start experimenting on this topic in about a month

                                    But i will never be able to replicate the initial face armature because it was hand made (bone placement).

                                    It's a strange approach to learnflow. Usually first, as a base, do something simple that can always be easily repeated.

                                      kuligs2 Why do you need to rotate the ball? Btw if a purpose of this ball gizmo is just to move a bone in the screen plane then you can use a 2D draggable gizmo, project its position onto the constrain plane and move a bone directly to that position.

                                        Tomcat It's a strange approach to learnflow. Usually first, as a base, do something simple that can always be easily repeated.

                                        imo this is as simple as it can get using sort of production mock up models.. i mean why would i try to do this on a cube mesh with one bone where if you start adding more bones it gets very complex?

                                        Also this way i learn how to and not to create bones in blender.

                                        Tomcat If I understand correctly, you're trying to use bones for what is normally done with shapekeys.

                                        whats a shape key?

                                        xyz uhm, i think i did that because i needed the x/y/z axis to be in aligned position with the mouse positive negative x/y axis so that it could translate without the trying to convert them somehow i cant even imagine right now..

                                          kuligs2 lol I was about to suggest that your origin axis or something to that effect was flipped in Blender

                                          kuligs2 xyz uhm, i think i did that because i needed the x/y/z axis to be in aligned position with the mouse positive negative x/y axis so that it could translate without the trying to convert them somehow i cant even imagine right now..

                                          Yeah but you don't need that at all. The orientation/basis of the gizmo or of the bone is not important as you're just need to set the position to a point calculated by constraining the mouse projection to a plane.

                                            xyz Continuing debugging, i noticed:

                                            When i click on the ball (i removed look_at() ) i print out positions of the ball and intersection.

                                            ball_node.gd

                                            if cam != null:
                                            	#look_at(cam.global_transform.origin,Vector3.UP)
                                            
                                            	print("interset click0: ", global_position)
                                            	
                                            	temp_plane = Plane(cam.global_transform.basis.z,global_position)
                                            
                                            	var RAY_LENGTH = (cam.global_position - global_position).length()*1.1
                                            	var mousepos = get_viewport().get_mouse_position()
                                            
                                            	var origin = cam.project_ray_origin(mousepos)
                                            	var end = origin + cam.project_ray_normal(mousepos) * RAY_LENGTH
                                            	var query = PhysicsRayQueryParameters3D.create(origin, end)
                                            	query.exclude = [self]
                                            	query.collide_with_areas = true
                                            	var intersect = temp_plane.intersects_ray(end, cam.global_transform.origin-global_position)
                                            	if intersect:
                                            
                                            		ball_position_changed.emit(bone_index, intersect)
                                            		print("interset click1: ", intersect)
                                            		ball_clicked.emit(position)

                                            Clicking the ball that is not related to bones prints out position that is close to real position of the ball.

                                            interset click0: (0, 0.959433, -1.63808)
                                            interset click1: (0.03866, 0.905366, -1.619112)

                                            But when i click the ball that is attached to bone, the ball jumps to some arbitrary location and prints out that wrong location.

                                            While i was writing this, i figured it out i think.

                                            Previously on garbanzo..

                                            func _on_ball_position_changed(bone_index, bone_pos):
                                            
                                            	skeleton_3d.set_bone_pose_position(bone_index,bone_pos)
                                            
                                            	pass

                                            Working code:

                                            func _on_ball_position_changed(bone_index, bone_pos):
                                            	
                                            	var pos =  bone_pos * skeleton_3d.global_transform
                                            	skeleton_3d.set_bone_pose_position(bone_index,pos)
                                            
                                            	pass

                                            So the problem was the transforms3d as per usual..
                                            Another culprit is that skeleton_3d.get_bone_global_pose and skeleton_3d.set_bone_pose_position work in local coord space relative to skeleton.

                                            But.. as per usual, some things are still not working.. 😃.. In the video i try to move the child-bone and this messes up everything.. mowing root-bone works as expected.

                                            Still need to work on the child node manipulations..

                                            • xyz replied to this.

                                              xyz

                                              func get_bones():
                                              	var bone_count = skeleton_3d.get_bone_count()
                                              	
                                              	if bone_count <1:
                                              		return
                                              	for b_index in range(0,bone_count-1):
                                              		
                                              		var b_pos = skeleton_3d.get_bone_global_pose(b_index)
                                              		var pos =  skeleton_3d.global_transform * b_pos * global_transform
                                              
                                              		var new_ball:Ball = ball.instantiate()
                                              		new_ball.ball_position_changed.connect(_on_ball_position_changed)
                                              		new_ball.bone_index = b_index
                                              		new_ball.cam = cam
                                              • xyz replied to this.

                                                kuligs2 This is initialization but balls of child bones are moving when you move a parent ball. How do you update this?

                                                  xyz

                                                  func _on_skeleton_updated():
                                                  	pass
                                                  	
                                                  	for child:Ball in ball_nodes.get_children():
                                                  		var b_pos = skeleton_3d.get_bone_global_pose(child.bone_index)
                                                  		var pos =  skeleton_3d.global_transform * b_pos * global_transform
                                                  		child.global_transform = pos

                                                  Tomcat Blend shapes are per-vertex animation technique. It has nothing to do with what is happening here. Even skeletal mesh animation is not relevant. The problem very likely boils down to faulty transform calculations.

                                                    xyz Blend shapes are per-vertex animation technique.

                                                    I mean, both are changes to the meshes. Yeah, in different ways. But they're similar enough in appearance. We don't know the end goal thoroughly. Somehow modifying by bones is supposed to be easier for the engine (so it's sometimes claimed). I want to try it, though I'm still vague about how it might be.

                                                      Tomcat so its a blender thing.. not really what im looking for unless the "end" goal i have in mind can be achieved with this.

                                                      I agree with xyz, its always transforms..

                                                      Tomcat well i didnt know any other way to do what i want - change for instance character shape.. i havent tested this with baked in animations, how it will all work, but the goal is to have character manipulation thingie, that players can set up their char face or any other bone/mesh the way they want.. with limits tho 😃..

                                                      I kinda hate when devs take a shortcut and just give few options to change the hair or have pre baked faces or body types..

                                                      I think something similar is in ARK survival game, tho i dont remember if you could adjust face.. i mean if you can adjust bone then face should be no problem.. ??

                                                        kuligs2 so its a blender thing.. not really what im looking for unless the "end" goal i have in mind can be achieved with this.

                                                        Well, those keys can be transferred to the engine.

                                                        well i didnt know any other way to do what i want - change for instance character shape..

                                                        Yep, that's exactly what the keys are for.

                                                        But character customization is a bit more complicated. Just to give you a very brief rundown on that: you have to be careful to match the rig and the mesh.

                                                          Tomcat Well, those keys can be transferred to the engine.

                                                          from what i seen keys are like linear manipulation, but idk.. yes i saw you can export to godot, but i dont like that you have to manually select vertices and move to a "maximum" distortion value rather than just modifying the root deformation bone somehow.. idk.. maybe the bone thing wont work out with animations etc.. need more testing..

                                                          Tomcat But character customization is a bit more complicated. Just to give you a very brief rundown on that: you have to be careful to match the rig and the mesh.

                                                          yeah this i learned, but you need to scale rig + mesh at the same time, and it should be ok, pretty easy to do in godot on node level.

                                                            kuligs2 need more testing..

                                                            Yeah, I want to try that too.

                                                            but you need to scale rig + mesh at the same time, and it should be ok, pretty easy to do in godot on node level.

                                                            No, it's not the size (not only the size) but the ratio of the different bones that changes, and this leads to severe distortions.

                                                            So i keep breaking things, not sure how and why..

                                                            Idea today was to create function that resets the bones to the initial positions. But somehow when i do that the mesh is mangled up.

                                                            As a bonus i figured out why the child bone didnt work properly. It was that function that get triggered when the skeleton is updated, so i disabled it and now i can move child bones and the bone/ball is not jumping away from my grip but the mesh is still deforming in not the bone direction but opposite (child bones)

                                                            Working code so far:

                                                            func get_bones():
                                                            	var bone_count = skeleton_3d.get_bone_count()
                                                            	
                                                            	if bone_count <1:
                                                            		return
                                                            	for b_index in range(0,bone_count-1):
                                                            		
                                                            		# works	
                                                            		var b_pos = skeleton_3d.get_bone_global_pose(b_index)
                                                            		var pos =  skeleton_3d.global_transform * b_pos
                                                            
                                                            		var new_ball:Ball = ball.instantiate()
                                                            		new_ball.ball_position_changed.connect(_on_ball_position_changed)
                                                            		new_ball.bone_index = b_index
                                                            		new_ball.cam = cam
                                                            	
                                                            		new_ball.global_transform = pos
                                                            		ball_nodes.add_child(new_ball)
                                                            	get_bone_reset_postions()
                                                            	pass
                                                            	
                                                            func _on_ball_position_changed(bone_index, bone_pos):
                                                            
                                                            	# works
                                                            	var pos =  bone_pos * skeleton_3d.global_transform 
                                                            	skeleton_3d.set_bone_pose_position(bone_index,pos)
                                                            
                                                            	pass
                                                            
                                                            func get_bone_reset_postions():
                                                            	var bone_count = skeleton_3d.get_bone_count()
                                                            	
                                                            	if bone_count <1:
                                                            		return
                                                            	for b_index in range(0,bone_count-1):
                                                            		# works	
                                                            		var b_pos = skeleton_3d.get_bone_global_pose(b_index)
                                                            		var pos =  skeleton_3d.global_transform * b_pos
                                                            		
                                                            		reset_bones_positions[b_index] = pos
                                                            	print("Reset position get")
                                                            		
                                                            func set_bone_reset_postions():
                                                            	
                                                            	for child:Ball in ball_nodes.get_children():
                                                            		child.global_transform = reset_bones_positions[child.bone_index]
                                                            		
                                                            		var pos =  reset_bones_positions[child.bone_index].origin * skeleton_3d.global_transform 
                                                            		skeleton_3d.set_bone_pose_position(child.bone_index,pos)
                                                            	print("Reset position set")

                                                            Its all inside the garbanzo - the face scene where i have access to skeleton and ball_node which contains balls that gets spawned by get_bones()

                                                            Continuing observations..
                                                            Godot does not import last added bone in blender..

                                                            I created new mesh and new bones from scratch. And now the movements are again flipped.. its not consistent.. script hasnt changed..

                                                            And same script with the other mesh

                                                            Not sure how and why this is happening and how to proceed..

                                                            Maybe its time to investigate shape keys??

                                                            EDIT:
                                                            One thing i noticed while here is that bone direction are different in blender, maybe thats the reason?

                                                            While the garbanzo works decent on the bones that are not the nose, the suzy bones do not at all..

                                                            EDIT2:

                                                            Ah yes.. yes yes yes.. this is quite peculiar.

                                                            TLDR:
                                                            Cannot chain bones if you want to deform mesh
                                                            Remember to add last sacrificial bone in blender because godot is too based to import it.
                                                            In blender, create bones that are Z-aligned (call em Z-bones) Z- axis in godot, in blender it could be either x or y

                                                            Note:
                                                            So for deformation in godot, you want to create separate armature that is based on the things i mentioned above.

                                                            Next:
                                                            Try to create idle animation for the mesh, then deform bones in godot and see how animation looks after deform.

                                                            Next - to last:
                                                            Try to save deform bone transforms to import them from either file or memory and apply it to the current session.