BABYLON.BoneIKController With GLTF Mesh

That’s kinda of what I was thinking after you said something. I guess it’s down to who wants to do it.

Nose goes.

Can i please see whats going on in the other prototypes you must have created like:
Matrix.getQuaterion
TransformNode.getLocalRotation
TransformNode.setLocalRotation
Quaternion.inverse

And any other that you use here… Please :slight_smile:

@mrlooi

1 Like

@MackeyK24

TransformNode.prototype.getLocalRotation = function () {
	if (this.rotationQuaternion) return this.rotationQuaternion.clone();
	return this.rotation.toQuaternion();
};

TransformNode.prototype.setLocalRotation = function (R) {
	if (R instanceof Quaternion) this.rotationQuaternion = R.clone();
	else if (R instanceof Vector3)
		// euler
		this.rotationQuaternion = R.toQuaternion();
	else throw new Error('Input rotation must be instanceof Quaternion or Vector3 (euler angles)');
	// this.markAsDirty('rotationQuaternion'); // this line causes gizmo + IK disconnect glitch
};

Matrix.prototype.getQuaternion = function () {
	let q = new Quaternion();
	this.decompose(undefined, q, undefined);
	return q;
};

Quaternion.inverse is just Quaternion.Inverse(x)

Definitely in favor of bones being extended from TransformNode!
Just like three.js
@sebavan @Pryme8 @JCPalmer @Deltakosh @Evgeni_Popov

Thanks @mrlooi

@Deltakosh any thoughts ?

I don’t see why not but let’s have a look at a PR to see how it looks like (Devil is in the details)

3 Likes

So now for the hard question :slight_smile: Who is game for the PR ???

I can say that would not be me. Think someone who uses GLTF should. I use embedded geometry exclusively in my own work. Using data files (.babylon / .glb / etc) are more than just asynchronous, which sucks. It means BABYLON.Mesh & BABYLON.InstancedMesh classes will be what you get. Those are the base classes for me. I have gone as low as 4 sub-classes below BABYLON.Mesh. OO rules!

The changes in BABYLON.Bone are probably minimal. Think GLTF loader changes, and shutting off where the linked transform node’s matrix is copied every frame, are the more important parts. Once accomplished both IK & the bone look controller should work without change, I think.

1 Like

Ok… So i got my custom GltfBoneIKController test class. I am not going to keep that class i am just using to figure out what needs to be modified on the original BoneIKController class. So far there are only three spots we need to change to work with GLTF Transform Nodes as Bones.

But… BabylonJS NEEDS a setAbsoluteRotationQuaternion function. For now, i added it to my UTILS class, and i am using that in my test GltfBoneIKController.

But all in all, it seems to be working fine. I need to get with @Pryme8 so we can go over all the rhyme and reason with the pole target mesh, pole angle and bendAxis… seeing is how i am new to the whole IK setup.

But again, working great so far. These are the three small chnages made to BoneIKCOntroller to make it work with GLTF meshes:

Yo @mrlooi … can you please look over these implementation changes one last time :slight_smile:

@MackeyK24 would you mind sharing your setabsoluterotation… utils ? As I guess I could at least integrate this solution as a first unblocking step in the framework

lgtm, you’ll just need to test it :slight_smile:

My Get And Set Absolute Rotation… Thanks to @mrlooi

        /** Sets the transform node abosulte Rotation */
        public static SetAbsoluteRotation(transform:BABYLON.TransformNode, rotation:BABYLON.Quaternion):void {
            if (transform.rotationQuaternion == null) {
                transform.rotationQuaternion = transform.rotation.toQuaternion();
            }
            if (transform.parent != null && transform.parent instanceof TransformNode) {
                BABYLON.Utilities.TempQuaternion2.set(0,0,0,1);
                BABYLON.Utilities.GetAbsoluteRotationToRef(transform.parent, BABYLON.Utilities.TempQuaternion2);
                BABYLON.Quaternion.InverseToRef(BABYLON.Utilities.TempQuaternion2, BABYLON.Utilities.TempQuaternion2);
                BABYLON.Utilities.TempQuaternion2.multiplyInPlace(rotation);
                transform.rotationQuaternion.copyFrom(BABYLON.Utilities.TempQuaternion2);
            } else {
                transform.rotationQuaternion.copyFrom(rotation);
            }
        }
        /** Gets the transform node abosulte rotation */
        public static GetAbsoluteRotation(transform:BABYLON.TransformNode):BABYLON.Quaternion {
            const result:BABYLON.Quaternion = new BABYLON.Quaternion(0,0,0,1);
            BABYLON.Utilities.GetAbsoluteRotationToRef(transform, result);
            return result;
        }
        /** Gets the transform node abosulte rotation */
        public static GetAbsoluteRotationToRef(transform:BABYLON.TransformNode, result:BABYLON.Quaternion):void {
            if (transform.parent != null && transform.parent instanceof TransformNode) {
                BABYLON.Utilities.GetAbsoluteRotationToRef(transform.parent, result);
                if (transform.rotationQuaternion == null) {
                    transform.rotationQuaternion = transform.rotation.toQuaternion();
                }
                result.multiplyInPlace(transform.rotationQuaternion);
                return;
            }
            const scale:BABYLON.Vector3 = transform.scaling;
            if (scale.x == 1 && scale.y == 1 && scale.z == 1) {
                result.copyFrom(transform.absoluteRotationQuaternion);
            } else {
                const sx:number = 1 / scale.x, sy:number = 1 / scale.y, sz:number = 1 / scale.z;
                //@ts-ignore Access a private field
                const m = transform.getWorldMatrix()._m;
                BABYLON.Utilities.TempMatrix2.reset();                
                BABYLON.Matrix.FromValuesToRef(
                    m[0] * sx,
                    m[1] * sx,
                    m[2] * sx,
                    0.0,
                    m[4] * sy,
                    m[5] * sy,
                    m[6] * sy,
                    0.0,
                    m[8] * sz,
                    m[9] * sz,
                    m[10] * sz,
                    0.0,
                    0.0,
                    0.0,
                    0.0,
                    1.0,
                    BABYLON.Utilities.TempMatrix2
                );
                BABYLON.Utilities.TempQuaternion3.set(0,0,0,1);
                BABYLON.Utilities.TempMatrix2.decompose(undefined, BABYLON.Utilities.TempQuaternion3, undefined);
                result.copyFrom(BABYLON.Utilities.TempQuaternion3);
            }
        }

I use alot to TOREF… above… I dont want any new instances on function i use in the render loop… So she may look verbose… but its fast :slight_smile:

1 Like

GLTF BoneIKController Demo: https://playground.babylonjs.com/#D0T14K#2?UnityToolkit

Uses embedded GltfBoneIKController class and MyUtils SetAbsoluteRotation Helper class

I have no idea (YET) about all the proper params for the target and pole target meshes.

But it is rotating the 2 bone chain … That is BoneIKController working on GLTF meshes :slight_smile:

5 Likes

Yo @adam … can you please take at look at this playground:

https://playground.babylonjs.com/#D0T14K#4?UnityToolkit

I think the targetMesh might be making the knee rotate the opposite direction.The playground
should be making the right foot reach down when the targetMesh is lower… but for some reason it is bending the knee the wrong way

Just to double check i am getting the same results, here is a version of the playground that uses
a native .babylon json file and the original BABYLON.BoneIKController (that does not support GLTF)

The result are the same. Which is promising (even if wrong right now) … it at least is doing the exact same thing the original BoneIKController is doing… So the port using the GLTF bone.linkedTransformNode seems to be working as expected.

It would seem its all in the setup and again, i just dont know what the heck im doing with the setup of all the proper target mesh, pole mesh, angles, positions, etc… for proper Foot placement to be aligned with the ground.

So thats where i am at so far

Something sounds really weird in GetAbsoluteRotationToRef, @MackeyK24 , @mrlooi why would we use only the scale of the root and not the intermediate ones ?

Also why not relying on the parentTransform.absoluteRotationQuaternion ??? I wonder what would the issue be ?

like this:

private static _SetAbsoluteRotation(transform: TransformNode, rotation: Quaternion): void {
        if (transform.rotationQuaternion == null) {
            transform.rotationQuaternion = new Quaternion();
        }
        if (transform.parent != null && this._IsTransformNode(transform.parent)) {
            const tmpQuat = TmpVectors.Quaternion[0];
            Quaternion.InverseToRef(transform.parent.absoluteRotationQuaternion, tmpQuat);
            tmpQuat.multiplyToRef(rotation, transform.rotationQuaternion);
        } else {
            transform.rotationQuaternion.copyFrom(rotation);
        }
    }

Yeah… I got that code from @mrlooi (Thanks again for your help, i really appreciate all you guys) :slight_smile:

@mrlooi why would we use only the scale of the root and not the intermediate ones ?

I remember the intermediate ones scaled the quaternions along with the actual scale. I wanted a function that only took the raw quaternions without any scaling. Not sure if the API has changed, this was done like a year ago