I am dynamically updating the bones on gltf model I have loaded in my scene. This is the function I’m using to rotate the bones and update them:
THREE.Bone.prototype.lookAtWorld = function(position) {
const parentBone = this.parent;
scene.attach(this)
this.lookAtLocal(position)
parentBone.attach(this)
}
THREE.Bone.prototype.lookAtLocal = function(position) {
this.updateMatrix()
this.updateMatrixWorld()
let direction = new THREE.Vector3().fromArray(position).normalize()
let pitch = Math.asin(-direction.y)
let yaw = Math.atan2(-direction.x, direction.z); // Beware cos(pitch)==0, catch this exception!
let roll = Math.PI;
this.rotation.set(roll, yaw, pitch);
// console.log(roll, yaw, pitch);
// console.log(THREE.MathUtils.radToDeg(roll), THREE.MathUtils.radToDeg(yaw), THREE.MathUtils.radToDeg(pitch));
}
So for each bone, I’m calling lookAtWorld() to transform them relative to world space.
It seems that these transformations eventually cause my model to explode after some time:
I believe this is the cause because I found that if I update the bones 5 per frame instead of just once, the blow up happens much faster.
Why is this happening? is there some kind of update I should be running on the skinned mesh, skeleton, or bones after each bone transformation?
all code:
async function loadRigg(model_path) {
async function loadModel(model_path) {
const loader = new GLTFLoader();
return new Promise(
(resolve, reject) =>
loader.load(model_path, model => resolve(model), null, reject)
);
}
function mapModel(model) {
let rigg = {}
rigg.scene = model.scene;
rigg.Object3D = model.scene.children[0];
rigg.skinned_mesh = rigg.Object3D.children[1];
rigg.skeleton = rigg.skinned_mesh.skeleton;
rigg.helper = new THREE.SkeletonHelper(rigg.skeleton.bones[0]);
let bones = rigg.skeleton.bones;
rigg.forearm = bones[0];
rigg.wrist = bones[1];
rigg.palm = bones[2];
rigg.thumb = bones.slice(3, 7);
rigg.index = bones.slice(7, 11);
rigg.middle = bones.slice(11, 15);
rigg.ring = bones.slice(15, 19);
rigg.pinky = bones.slice(19, 23);
THREE.Bone.prototype.lookAtLocal = function(position) {
this.updateMatrix()
this.updateMatrixWorld()
let direction = new THREE.Vector3().fromArray(position).normalize()
let pitch = Math.asin(-direction.y)
let yaw = Math.atan2(-direction.x, direction.z); // Beware cos(pitch)==0, catch this exception!
let roll = Math.PI;
this.rotation.set(roll, yaw, pitch);
// console.log(roll, yaw, pitch);
// console.log(THREE.MathUtils.radToDeg(roll), THREE.MathUtils.radToDeg(yaw), THREE.MathUtils.radToDeg(pitch));
}
THREE.Bone.prototype.lookAtWorld = function(position) {
const parentBone = this.parent;
scene.attach(this)
this.lookAtLocal(position)
parentBone.attach(this)
}
rigg.resetOrientation = function() {
rigg.Object3D.rotation.z = Math.PI / 2
rigg.forearm.position.set(0, 0, 0)
rigg.forearm.quaternion.set(0.44647279381752014, -0.5598294138908386, -0.498911589384079, 0.48820120096206665)
}
rigg.setOrientation = function(q) {
rigg.forearm.quaternion.set(q.y, q.z, q.x, q.w)
rigg.forearm.rotation.x += Math.PI / 2
}
rigg.setPose = function(pose) {
rigg.resetOrientation()
rigg.palm.lookAtWorld(pose["palm-direction"])
for (let finger of ["index", "middle", "ring", "pinky", "thumb"]) {
for (let i = 0; i < 3; i++) {
rigg[finger][i].lookAtWorld(pose[finger + "-direction"][i])
}
}
}
rigg.resetOrientation()
return rigg;
}
let gltf = await loadModel(model_path);
let rigg = mapModel(gltf);
return rigg;
}