Hello !
- As far as I know, BabylonJS mesh constraints are physics oriented. (Please tell me if I’m wrong).
- In Blender, there is something that I use very often which is the
Transformation Constraint
which ables to map a transform from a mesh to another, without any physics :
- With these, in many case you can get rid of working with rigging, just adding a constraint, like “
This mesh rotates X when that mesh moves Y
”
I propose to discuss adding a mesh.link
method, very similar to the above constraint.
The code would be something like this :
pTR
= parent Transform (ex :"position"
)pAX
= parent Axis (ex :"x"
)pmin
= parent min (ex :-1
)pmax
= parent max (ex :1
)cTR
,cAX
,cmin
,cmax
etc with child
function link(parent, child, pTR, pAX, pmin, pmax, cTR, cAX, cmin, cmax){
parent.getScene().onBeforeRenderObservable.add(()=>{
const value = parent[pTR][pAX];
const param = (value-pmin)/(pmax-pmin);
child[cTR][cAX] = cmin+param*(cmax-cmin);
})
}
Also, in case we don’t want a linear mapping, we could add a custom function :
function link(parent, child, pTR, pAX, pmin, pmax, cTR, cAX, cmin=undefined, cmax=undefined, custom=undefined){
parent.getScene().onBeforeRenderObservable.add(()=>{
const value = parent[pTR][pAX];
if(custom){
child[cTR][cAX] = custom(value);
} else {
const param = (value-pmin)/(pmax-pmin);
child[cTR][cAX] = cmin+param*(cmax-cmin);
}
})
}
Of course I’m not detailing the final implementation here :
- Would be a new method of
TransformNode
, I guess - Would return the observable, with ability to delete with another
unlink
function. - etc…
As a Proof of Concept, I have done an example using the Buggy GLTF asset :
As you can see there are a total of 8 linkage, merging rotation, translation, etc.
The full “no rig” code is here :
// Wheeling
const wheeling = new BABYLON.TransformNode("wheeling");
// Link Wheels
link(wheeling, Axle_2, "rotation", "y", -1, 1, "rotation", "y", -1+Math.PI/2, 1+Math.PI/2);
link(Axle_2, Axle_3, "rotation", "y", -1, 1, "rotation", "y", -1, 1);
link(Axle_2, Axle_4, "rotation", "y", -1, 1, "rotation", "y", 1, -1);
link(Axle_2, Axle_5, "rotation", "y", -1, 1, "rotation", "y", 1, -1);
// Link Slider_0 (front)
const pos0 = Slider_0.position.clone();
link(Axle_2, Slider_0, "rotation", "y", -1, 1, "position", "x", null, null, (value)=>{
return pos0.x+0.5*Math.cos(value);
});
link(Axle_2, Slider_0, "rotation", "y", -1, 1, "position", "z", null, null, (value)=>{
return pos0.z+0.5-0.5*Math.sin(value);
});
// Link Slider_1 (back)
const pos1 = Slider_1.position.clone();
link(Slider_0, Slider_1, "position", "x", -1, 1, "position", "x", -1, 1);
link(Slider_0, Slider_1, "position", "z", pos0.z-1, pos0.z+1, "position", "z", pos1.z+1, pos1.z-1);
// Link Axles
link(Slider_0, Axle_1, "position", "x", -1, 1, "rotation", "z", 0.15+7.8, 0.15-7.8);
link(Axle_1, Axle_0, "rotation", "z", -1, 1, "rotation", "y", 0.1-1, 0.1+1);
What I like about this, is that since observable will be triggered in the same order than added, there in actually a perfect constraint chaining :
- I start from
wheeling
which is just aTransform Node
I plan to rotate - Passed to
wheel_0
with specific angle mapping- passed to all 3
wheels
as a copy (modulo revert)
- passed to all 3
- Passed toward translation with custom sinus function (
slider
) - Passed toward rotation (
horizontal axle
) - Passed toward rotation (
vertical axle
)
Here is the PG demo :
NB:
Please note that this custom GLB is just a renaming and remapping of hierarchy from the original GLB. All meshes are at the same pos, same transforms, no RIG
NB2:
Topic about reworking this Buggy !