"Spokes" script


#1

My thoughts are to use a script similar to this one:

var HingeConstraint = pc.createScript('hingeConstraint');

HingeConstraint.attributes.add('partA', { type: 'entity', title: 'Part A' });
HingeConstraint.attributes.add('partB', { type: 'entity', title: 'Part B' });
HingeConstraint.attributes.add('limits', { type: 'vec2', title: 'Joint Limits (degrees)' });

HingeConstraint.attributes.add('partOrientationOffsetA', { type: 'vec3', title: 'Part A Orientation Offset (degrees)' });
HingeConstraint.attributes.add('partOrientationOffsetB', { type: 'vec3', title: 'Part B Orientation Offset (degrees)' });

HingeConstraint.attributes.add('disableCollision', { type: 'boolean', default: false, title: 'Disable Collision' });

HingeConstraint.attributes.add('debugDraw', { type: 'boolean', default: false, title: 'Debug Draw' });

HingeConstraint.prototype.initialize = function() {
    this.createJoint();
    this.white = new pc.Color(1, 1, 1, 1);
    this.temp = new pc.Vec3();
    
    this.on('attr:limits', function (value, prev) {
        this.joint.setLimit(value.x * pc.math.DEG_TO_RAD, value.y * pc.math.DEG_TO_RAD, 0.9, 0.3, 1);
    });
};

HingeConstraint.prototype.draw = function () {
    this.app.renderLine(this.partA.getPosition(), this.entity.getPosition(), this.white);
    this.app.renderLine(this.partB.getPosition(), this.entity.getPosition(), this.white);
};

HingeConstraint.prototype.update = function (dt) {
    if (this.debugDraw) {
        this.draw();
    }
};

HingeConstraint.prototype.createJoint = function() {
    var rbA = this.partA.rigidbody.body;
    var rbB = this.partB.rigidbody.body;
    
    // Work out offset to the parts
    var offsetA = new pc.Vec3();
    var matrixA = this.partA.getWorldTransform().clone();
    matrixA.invert();
    matrixA.transformPoint(this.entity.getPosition(), offsetA);
        
    var offsetB = new pc.Vec3();
    var matrixB = this.partB.getWorldTransform().clone();
    console.log("Matrix: " + matrixB);
    console.log("World Transform: " + this.partB.worldTransform);
    matrixB.invert();    
    console.log("Inversse Matrix: " + matrixB);
    matrixB.transformPoint(this.entity.getPosition(), offsetB);
      
    var localA = new Ammo.btTransform();
    var localB = new Ammo.btTransform();
    
    localA.setIdentity();
    localB.setIdentity();

    var orientA = this.partOrientationOffsetA;
    var orientB = this.partOrientationOffsetB;

    localA.getBasis().setEulerZYX(orientA.x * pc.math.DEG_TO_RAD, orientA.y * pc.math.DEG_TO_RAD, orientA.z * pc.math.DEG_TO_RAD);
    localA.setOrigin(new Ammo.btVector3(offsetA.x, offsetA.y, offsetA.z));
    
    localB.getBasis().setEulerZYX(orientB.x * pc.math.DEG_TO_RAD, orientB.y * pc.math.DEG_TO_RAD, orientB.z * pc.math.DEG_TO_RAD);
    localB.setOrigin(new Ammo.btVector3(offsetB.x, offsetB.y, offsetB.z));

    this.joint = new Ammo.btHingeConstraint(rbA, rbB, localA, localB);

    this.joint.setLimit(this.limits.x * pc.math.DEG_TO_RAD, this.limits.y * pc.math.DEG_TO_RAD, 0.9, 0.3, 1);

    // Add joint to simulation
    this.app.systems.rigidbody.dynamicsWorld.addConstraint(this.joint, this.disableCollision);
};

To create something like the spokes on a bicycle wheel between several individual rigidbodies in my scene. They would need to stay at the position they are in, but rotate around a centralized point. I know this script can create that centralized point, but after playing around with it I get the feeling the rigidbodies will not move as a single entity with this setup. I have tried using a single model with several child rigidbodies, but they don’t interact with the other object I want them to when done this way. Compounding them together seems to be the only option.


#2

Here’s what I have so far:

var Spokes = pc.createScript('spokes');

Spokes.attributes.add('part1', {type: 'entity', title: 'Part 1'});
Spokes.attributes.add('part2', {type: 'entity', title: 'Part 2'});
Spokes.attributes.add('part3', {type: 'entity', title: 'Part 3'});
Spokes.attributes.add('part4', {type: 'entity', title: 'Part 4'});
Spokes.attributes.add('part5', {type: 'entity', title: 'Part 5'});
Spokes.attributes.add('part6', {type: 'entity', title: 'Part 6'});
Spokes.attributes.add('part7', {type: 'entity', title: 'Part 7'});
Spokes.attributes.add('part8', {type: 'entity', title: 'Part 8'});
Spokes.attributes.add('part9', {type: 'entity', title: 'Part 9'});
Spokes.attributes.add('part10', {type: 'entity', title: 'Part 10'});
Spokes.attributes.add('part11', {type: 'entity', title: 'Part 11'});
Spokes.attributes.add('part12', {type: 'entity', title: 'Part 12'});

Spokes.attributes.add('limits', {type: 'vec2'});

Spokes.attributes.add('disableCollision', {type: 'boolean', default: false});

Spokes.attributes.add('debugDraw', {type: 'boolean', default: false});

Spokes.prototype.initialize = function()
{
    this.createJoint();
    this.white = new pc.Color(1,1,1,1);
    this.temp = new pc.Vec3();
    this.pearls = this.app.root.findByTag('Pearl');
    
    this.on('attr:limits', function(value, prev)
    {
        this.joint.setLimit(value.x * pc.math.DEG_TO_RAD, value.y * pc.math.DEG_TO_RAD, 0.9, 0.3, 1);
    });
};

Spokes.prototype.draw = function()
{
    for(i=0;i<this.pearls.length;i++)
    {
        this.app.renderLine(this.pearls[i].getPosition, this.entity.getPosition(), this.white);
    }
};

Spokes.prototype.update = function(dt)
{
    if(this.debugDraw)
    {
        this.draw();
    }
};

Spokes.prototype.createjoint = function()
{
    for(i=0;i<this.pearls.length;i++)
    {
        
    }
};

What I’m wondering now is: is there a way to make an array of the 12 part attributes instead of creating the array from entities with a tag or name?

EDIT: and without making the array an attribute itself. (Did some reading up and attribute arrays of entities not implemented yet)


#3

Having many entity attributes you can build an array from it:

this.parts = [
    this.part1,
    this.part2,
    ...
];

or you can access them directly inside a loop like this:

for (var i = 1; i <= 12; i++) {
    var part = this['part' + i];
}

#4

I pretty much did this, but now I’m running into the same issue I had before with another constraint script.
For some reason, when I have multiple entities I’m trying to connect together with a script like this, the matrices I need to access mess up.

 var matrix = this.parts[i].getWorldTransform().clone();
 console.log("World Transform: " + this.parts[i].worldTransform);
 console.log("Matrix: " + matrix);
 matrix.invert();
 console.log("Inverse Matrix: " + matrix);
 matrix.transformPoint(this.entity.getPosition(), offset);

The World Transform and Matrix log match like they’re supposed to, but the Inverse Matrix is not inverse at all, it’s an entirely different matrix.
As shown here (these get printed 12 times, so I just grabbed the first 3):
World Transform:
[7.5, 0, 0, 0, 0, 1.7763568394002505e-15, 8, 0, 0, -7.5, 1.6653345369377348e-15, 0, 0.25502723455429077, 0.7520612478256226, -1.5518451929092407, 1]
Matrix:
[7.5, 0, 0, 0, 0, 1.7763568394002505e-15, 8, 0, 0, -7.5, 1.6653345369377348e-15, 0, 0.25502723455429077, 0.7520612478256226, -1.5518451929092407, 1]
Inverse Matrix:
[0.13333334028720856, 0, 0, 0, 0, 2.7755575615628914e-17, -0.13333334028720856, 0, 0, 0.125, 2.9605948867407985e-17, 0, -0.03400363028049469, 0.1939806491136551, 0.10027483105659485, 1]

Here’s a link to the scene so you can check it out yourself: https://playcanvas.com/editor/scene/593574