How to re-position the joint location on a collision shape?

In my spare time I’ve been trying to understand how the joints and objects in JMonkey and JBullet work, and after a lot of trials, I’ve finally managed to create this (Image 1):

final
But i want the joints to be positioned like in the image 2 so that the 6dof joint’s could be constrained in a way that allows me to create evolutionary algorithms which rotate the motors to create a walking motion.

Code given below.

The question:
The joint connections begin from the center of the capsule and box, and I’m unable to figure out how to move the joint connections to the edge of the objects. Could you help with that?
I’ve already seen the super classes of the meshed vehicle in Jme3Tests and saw how the chassis is positioned lower than the vehicle body, but it isn’t clear from the code, how that was accomplished.

Background:
The 2D version of what I’m trying to achieve, I was able to do in Pymunk as shown here, but Python is slow and does not support true parallelism (due to Global Interpreter Lock), which is why I want to create a four legged version of the robot shown in this video. I know JMonkey was built for gaming, but it’d be great if such examples would be available for programmers to try their AI algorithms like in OpenAI gym. At least for starters, I’d like to create a program and contribute it to the JMonkey community. But it’s rather hard to figure out the nuances. The documentation on 6dof is scanty, there are hardly any examples and the local and world coordinates are confusing.

Code:
package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.joints.SixDofJoint;
import com.jme3.bullet.joints.PhysicsJoint;
import com.jme3.bullet.joints.motors.RotationalLimitMotor;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.system.AppSettings;

public class Main extends SimpleApplication implements ActionListener {

    private BulletAppState bulletAppState = new BulletAppState();
    private final Node robot = new Node();
    private final Vector3f upforce = new Vector3f(0, 200, 0);
    private final float motorVel = 10.0f;
    private final float motorForce = 10.0f;

    public static void main(String[] args) {
        Main app = new Main();
        AppSettings as = new AppSettings(true);
        as.setWidth(1020);
        as.setHeight(600);
        as.setVSync(false);
        app.setSettings(as);
        app.setShowSettings(false);
        app.start();
    }

    @Override
    public void simpleInitApp() {
        bulletAppState = new BulletAppState();
        stateManager.attach(bulletAppState);
        bulletAppState.setDebugEnabled(true);
        inputManager.addMapping("Pull ragdoll up", new MouseButtonTrigger(0));
        inputManager.addListener(this, "Pull ragdoll up");
        PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
        createRobot();
    }

    private void createRobot() {
        Vector3f chassisLoc = new Vector3f(0, 0, 0);
        Vector3f chassisSize = new Vector3f(0.2f, 0.2f, 1);    
        float limbHeight = 0.5f;
        float limbWidth = 0.05f;
        
        //---create chassis
        BoxCollisionShape shape = new BoxCollisionShape(chassisSize);
        Node chassisNode = new Node("chassis"); 
        RigidBodyControl rigidBodyControl = new RigidBodyControl(shape, 1); 
        chassisNode.setLocalTranslation(chassisLoc); chassisNode.addControl(rigidBodyControl); robot.attachChild(chassisNode);
        //bulletAppState.getPhysicsSpace().addAll(robot); 
        
        Node limbRight1A = createLimb(chassisLoc, chassisSize, limbHeight, limbWidth, "right");
        Node limbLeft1A = createLimb(chassisLoc, chassisSize, limbHeight, limbWidth, "left");
        Vector3f chassis_limbRight1A_JoinLoc = new Vector3f(chassisLoc.x+chassisSize.x/2+limbHeight/2, chassisLoc.y, chassisLoc.z);      
        Vector3f chassis_limbLeft1A_JoinLoc = new Vector3f(chassisLoc.x-(chassisSize.x/2+limbHeight/2), chassisLoc.y, chassisLoc.z);              
        join(chassisNode, limbRight1A, chassis_limbRight1A_JoinLoc); 
        join(chassisNode, limbLeft1A, chassis_limbLeft1A_JoinLoc); 
        robot.attachChild(chassisNode);
        robot.attachChild(limbRight1A);
        robot.attachChild(limbLeft1A);
        rootNode.attachChild(robot);
        bulletAppState.getPhysicsSpace().addAll(robot);        
    }
    
    private Node createLimb(final Vector3f chassisLoc, final Vector3f chassisSize, final float limbHeight, final float limbWidth, final String rightLeft) {
        int axis = PhysicsSpace.AXIS_Z;
        Vector3f offset;
        if ("right".equals(rightLeft)) {offset = new Vector3f(chassisSize.x/2+limbWidth/2, 0, 0);}
        else {offset = new Vector3f(-(chassisSize.x/2+limbWidth/2), 0, 0);}
        Vector3f limbPos = new Vector3f(chassisLoc.x+offset.x, chassisLoc.y+offset.y, chassisLoc.z+offset.z);
        CapsuleCollisionShape shape = new CapsuleCollisionShape(limbWidth, limbHeight, axis);
        Node limbNode = new Node("limb");
        RigidBodyControl rigidBodyControl = new RigidBodyControl(shape, 1); 
        limbNode.setLocalTranslation(limbPos); limbNode.addControl(rigidBodyControl); robot.attachChild(limbNode);
        bulletAppState.getPhysicsSpace().addAll(robot);  
        return limbNode;
    }

    private PhysicsJoint join(Node A, Node B, Vector3f connectionPoint) {
        Vector3f pivotA = A.worldToLocal(connectionPoint, new Vector3f());
        Vector3f pivotB = B.worldToLocal(connectionPoint, new Vector3f());
        SixDofJoint joint = new SixDofJoint(A.getControl(RigidBodyControl.class), B.getControl(RigidBodyControl.class), pivotA, pivotB, true);
        RotationalLimitMotor m = joint.getRotationalLimitMotor(PhysicsSpace.AXIS_Z);
        m.setEnableMotor(true);
        m.setTargetVelocity(motorVel);
        m.setMaxMotorForce(motorForce);
        //joint.setAngularLowerLimit(new Vector3f(1,1,1));
        //joint.setAngularLowerLimit(new Vector3f(10,10,10));
        
        return joint;
    }

    @Override
    public void onAction(String string, boolean bln, float tpf) {
//        if ("Pull ragdoll up".equals(string)) {if (bln) {limb1.getControl(RigidBodyControl.class).activate();applyForce = true;} else {applyForce = false;}}
    }

    @Override
    public void simpleUpdate(float tpf) {
//        if (applyForce) {limb1.getControl(RigidBodyControl.class).applyForce(upforce, Vector3f.ZERO);}
    }
}
1 Like

The joints are positioned at Vector3f(0, 0, 0) so build your meshes with that in mind. You dont “move” the joint. You “position” your mesh arount the joint.

If memory serves, you can just add the model to a node and move the model around in the parent.

So if your “leg” is 1x10x1 in size built from the center, and you want the joint at the top, add the leg to a node and set the local translation of the leg. Then use the parent node as the model.

leg.setLocalTranslation(0, -5, 0);

When you instantiate a PhysicsJoint, you typically specify two bodies, A and B. For most subclasses, you can also specify the offset of the “pivot” from the center of each body.

@jayfella: But setLocalTranslation is already there in my code. It doesn’t work. The joint origin always starts from the center of the object, and that’s not how I want it to work.
@sgold: I looked at the classes, but there’s no option to specify a pivot offset.
I post on a forum only when I’ve already examined the documentation and tried out various options. Some working code would be a big help.

example

As I said, add it to a node and position/rotate the model inside the parent where you want it relative to zero. Then use the parent as if it’s the model.

Spatial myModel = ...
Node parent = new Node("parent");
myModel.addChild(myModel);

// set the local position and rotation of the model inside the parent.
myModel.setLocalTranslation(new Vector3f(5, 5, 5));
myModel.setLocalRotation(...);

// now treat "parent" as your "leg" or whatever it is.

So you probably want to rotate the model 90 degrees (half-pi radians) along whichever axis, looks like Z from your drawing, and move it half the size of the model.

Now use “parent” as you would the model instead of the model itself.

The pivot offsets are the pivotA and pivotB arguments in the SixDofJoint constructor of the code you posted.

Ok, so using a 6dof doesn’t seem appropriate here, since if I move the y axis motor to make a limb move upward, the moment I disable the motor, the leg would fall down. I wanted more of a servo motor functionality. Once I rotate a joint to a position, it stays there.

So I went through the SceneGraph tutorial again and although it made sense, there seem to be more complications.

Eg: If I create this it works:

    BoxCollisionShape chassisShape = new BoxCollisionShape(chassisSize);
    Node chassisNode = new Node("chassis"); 
    RigidBodyControl chassisRigidCtrl = new RigidBodyControl(chassisShape, 1); 
    chassisNode.move(chassisLoc); //chassisNode.setLocalTranslation(chassisLoc); 
    chassisNode.addControl(chassisRigidCtrl); 

But if I try to do the moving after chassisNode.addControl, it doesn’t work.

    BoxCollisionShape chassisShape = new BoxCollisionShape(chassisSize);
    Node chassisNode = new Node("chassis"); 
    RigidBodyControl chassisRigidCtrl = new RigidBodyControl(chassisShape, 1);  
    chassisNode.addControl(chassisRigidCtrl); 
    chassisNode.move(chassisLoc);

Also, when I tried adding a sphere as the child node of the chassis, the sphere just rolled off. I expected it to stay in it’s specified position with respect to the chassis. Since the Sixdof didn’t suit the application, I thought I’d create a sphere and attach a limb to it. This way I’d be able to rotate the sphere and that’d automatically rotate the limb which is a child of the sphere. But nothing is working as planned.
I’d be very grateful if y’all could post a working example. I can see that y’all are trying to teach me to fish, rather than give me the fish, but please understand that I posted on this forum because the documentation, tutorials and actual code trials I did were hitting a dead end.
At this point, explanations won’t help. Only an actual working demo will.

ps: For some reason, the code formatting for the code snippets I posted above, didn’t work.

The code I tried:

    package mygame;

    import com.jme3.app.SimpleApplication;
    import com.jme3.bounding.BoundingVolume;
    import com.jme3.bullet.BulletAppState;
    import com.jme3.bullet.PhysicsSpace;
    import com.jme3.bullet.collision.shapes.BoxCollisionShape;
    import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
    import com.jme3.bullet.collision.shapes.SphereCollisionShape;
    import com.jme3.bullet.control.RigidBodyControl;
    import com.jme3.bullet.joints.SixDofJoint;
    import com.jme3.bullet.joints.PhysicsJoint;
    import com.jme3.bullet.joints.motors.RotationalLimitMotor;
    import com.jme3.collision.Collidable;
    import com.jme3.collision.CollisionResults;
    import com.jme3.collision.UnsupportedCollisionException;
    import com.jme3.input.controls.ActionListener;
    import com.jme3.input.controls.MouseButtonTrigger;
    import com.jme3.math.Vector3f;
    import com.jme3.scene.Node;
    import com.jme3.scene.SceneGraphVisitor;
    import com.jme3.scene.Spatial;
    import com.jme3.system.AppSettings;
    import java.util.Queue;

    public class Main extends SimpleApplication implements ActionListener {

        private BulletAppState bulletAppState = new BulletAppState();
        private final Node robot = new Node();
        private final Vector3f upforce = new Vector3f(0, 200, 0);
        private final float motorVel = 10.0f;
        private final float motorForce = 10.0f;

        public static void main(String[] args) {
            Main app = new Main();
            AppSettings as = new AppSettings(true);
            as.setWidth(1020);
            as.setHeight(600);
            as.setVSync(false);
            app.setSettings(as);
            app.setShowSettings(false);
            app.start();
        }

        @Override
        public void simpleInitApp() {
            bulletAppState = new BulletAppState();
            stateManager.attach(bulletAppState);
            bulletAppState.setDebugEnabled(true);
            inputManager.addMapping("Pull ragdoll up", new MouseButtonTrigger(0));
            inputManager.addListener(this, "Pull ragdoll up");
            PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
            createRobot();
        }

        private void createRobot() {
            Vector3f chassisLoc = new Vector3f(0, 0, 5);
            Vector3f chassisSize = new Vector3f(0.2f, 0.2f, 1);    
            float limbHeight = 0.5f;
            float limbWidth = 0.05f;
            float casterRadius = 0.05f;
            
            //---create chassis
            BoxCollisionShape chassisShape = new BoxCollisionShape(chassisSize);
            Node chassisNode = new Node("chassis"); 
            RigidBodyControl chassisRigidCtrl = new RigidBodyControl(chassisShape, 1); 
            chassisNode.move(chassisLoc); //chassisNode.setLocalTranslation(chassisLoc); 
            chassisNode.addControl(chassisRigidCtrl); 
            
            //---create left limb A1 caster
            SphereCollisionShape leftA1_casterShape = new SphereCollisionShape(casterRadius);
            Node leftA1_casterNode = new Node("leftA1_caster"); 
            RigidBodyControl leftA1_rigidCtrl = new RigidBodyControl(leftA1_casterShape, 1);
            leftA1_casterNode.move(chassisLoc.x+chassisSize.x/2+casterRadius/2, chassisLoc.y+chassisSize.y/2, chassisLoc.z+chassisSize.z/2);
            leftA1_casterNode.addControl(leftA1_rigidCtrl);
            
            chassisNode.attachChild(leftA1_casterNode);
            
            //chassisNode.move(chassisLoc.x, chassisLoc.y, chassisLoc.z+10);
            
            robot.attachChild(chassisNode);
            
            rootNode.attachChild(robot);
            bulletAppState.getPhysicsSpace().addAll(robot);        
        }

        @Override
        public void onAction(String string, boolean bln, float tpf) {
        }

        @Override
        public void simpleUpdate(float tpf) {
        }
    }

Here you go:

package jme3test.bullet;

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.joints.SixDofJoint;
import com.jme3.bullet.joints.motors.RotationalLimitMotor;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;

/**
 * Test/demonstrate double-ended 6-DOF joints.
 *
 * @author Stephen Gold sgold@sonic.net
 */
public class JointDemo extends SimpleApplication {

    private BulletAppState bulletAppState;
    private PhysicsSpace physicsSpace;

    public static void main(String[] ignored) {
        new JointDemo().start();
    }

    @Override
    public void simpleInitApp() {
        configureCamera();
        configurePhysics();
        addBox();
        addRobot();
    }

    /**
     * Add a large static box to serve as a platform.
     */
    private void addBox() {
        float halfExtent = 50f;
        Node boxNode = new Node("box");
        rootNode.attachChild(boxNode);

        boxNode.move(0f, -halfExtent, 0f);

        BoxCollisionShape shape = new BoxCollisionShape(
                new Vector3f(halfExtent, halfExtent, halfExtent));
        float mass = 0f;
        RigidBodyControl boxBody = new RigidBodyControl(shape, mass);
        boxNode.addControl(boxBody);
        boxBody.setPhysicsSpace(physicsSpace);
    }

    /**
     * Add a rectangular leg to the robot chassis.
     */
    private void addLeg(Node legGeom, Vector3f legInWorld,
            Vector3f pivotInChassis, Vector3f chassisInWorld,
            RigidBodyControl chassisRbc) {
        rootNode.attachChild(legGeom);

        CollisionShape shape
                = new BoxCollisionShape(new Vector3f(0.1f, 0.4f, 0.1f));
        legGeom.move(legInWorld);

        float mass = 0.1f;
        RigidBodyControl legRbc = new RigidBodyControl(shape, mass);
        legGeom.addControl(legRbc);
        legRbc.setPhysicsSpace(physicsSpace);

        Vector3f pivotInLeg
                = pivotInChassis.add(chassisInWorld).subtractLocal(legInWorld);
        SixDofJoint joint = new SixDofJoint(chassisRbc, legRbc, pivotInChassis,
                pivotInLeg, false);

        joint.setAngularLowerLimit(new Vector3f(0f, 0f, -9e9f));
        joint.setAngularUpperLimit(new Vector3f(0f, 0f, 9e9f));
        /*
         * Enable the motor for Z-axis rotation and return a reference to it.
         */
        RotationalLimitMotor motor
                = joint.getRotationalLimitMotor(PhysicsSpace.AXIS_Z);
        motor.setEnableMotor(true);
        motor.setHiLimit(9e9f);
        motor.setLoLimit(-9e9f);
        motor.setMaxMotorForce(9e9f);
        motor.setTargetVelocity(1f);

        physicsSpace.add(joint);
    }

    /**
     * Add a 4-legged robot to the scene.
     */
    private void addRobot() {
        CollisionShape chassisShape
                = new BoxCollisionShape(new Vector3f(1f, 0.3f, 0.3f));
        Vector3f chassisInWorld = new Vector3f(0f, 1f, 0f);
        float chassisMass = 1f;

        Node chassisNode = new Node("chassis");
        rootNode.attachChild(chassisNode);
        chassisNode.move(chassisInWorld);
        RigidBodyControl chassisRbc
                = new RigidBodyControl(chassisShape, chassisMass);
        chassisNode.addControl(chassisRbc);
        chassisRbc.setPhysicsSpace(physicsSpace);

        Node lfGeom = new Node("lf leg");
        Vector3f lfLegInWorld = new Vector3f(-0.8f, 0.6f, 0.5f);
        Vector3f lfPivotInChassis = new Vector3f(-0.8f, 0f, 0.4f);
        addLeg(lfGeom, lfLegInWorld, lfPivotInChassis, chassisInWorld,
                chassisRbc);

        Node rfGeom = new Node("rf leg");
        Vector3f rfLegInWorld = new Vector3f(-0.8f, 0.6f, -0.5f);
        Vector3f rfPivotInChassis = new Vector3f(-0.8f, 0f, -0.4f);
        addLeg(rfGeom, rfLegInWorld, rfPivotInChassis, chassisInWorld,
                chassisRbc);

        Node lrGeom = new Node("lr leg");
        Vector3f lrLegInWorld = new Vector3f(0.8f, 0.6f, 0.5f);
        Vector3f lrPivotInChassis = new Vector3f(0.8f, 0f, 0.4f);
        addLeg(lrGeom, lrLegInWorld, lrPivotInChassis, chassisInWorld,
                chassisRbc);

        Node rrGeom = new Node("rr leg");
        Vector3f rrLegInWorld = new Vector3f(0.8f, 0.6f, -0.5f);
        Vector3f rrPivotInChassis = new Vector3f(0.8f, 0f, -0.4f);
        addLeg(rrGeom, rrLegInWorld, rrPivotInChassis, chassisInWorld,
                chassisRbc);
    }

    /**
     * Configure the camera during startup.
     */
    private void configureCamera() {
        flyCam.setDragToRotate(true);
        flyCam.setMoveSpeed(4f);
        cam.setLocation(new Vector3f(3f, 2f, 10f));
        cam.setRotation(new Quaternion(0f, 0.9759f, -0.04f, -0.2136f));
    }

    /**
     * Configure physics during startup.
     */
    private void configurePhysics() {
        bulletAppState = new BulletAppState();
        bulletAppState.setDebugEnabled(true);
        stateManager.attach(bulletAppState);
        physicsSpace = bulletAppState.getPhysicsSpace();
    }
}

P.S.: My consulting rate is $1,000/hour.

Thank you Stephen. I’m aware creating this program has taken a good amount of your time. It has been embarrassing for me to ask for help in this manner. I normally put in a lot more effort before asking for ready-made code. During my prime, I’ve put in a lot of my time to help newbies on various forums and contributed to open source. Now I can’t put in as much work due to chronic RSI.
Anyway, for a moment I thought the program you gave was a perfect solution, until I made the motors stop and noticed that the joint could not hold the bodies in position. They keep leaning due to gravity (I know I can stop them by putting the bodies to sleep, but that’s not the only problem), and if the mass of the limbs is increased, there’s a weird shaking behaviour which is worsened by the fact that the joints jostle and move away from the chassis much more than I intend it to. This is certainly not the kind of behaviour I want from a robot. So it looks like I’ll be back to using Pymunk.

Am very grateful for all of your help. The help offered by this community, the dedication and the expertise y’all have is par-excellence!

As a gesture of thanks, I’ve made the code a bit more generic. Hope this thread will serve as a reference for any future enthusiast who comes looking for an example on how to create a walking robot using 6dof joints.

package main;

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.joints.PhysicsJoint;
import com.jme3.bullet.joints.SixDofJoint;
import com.jme3.bullet.joints.motors.RotationalLimitMotor;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.system.AppSettings;
import java.time.Instant;
import java.util.Collection;

/**
 * Test/demonstrate double-ended 6-DOF joints.
 *
 * @author Stephen Gold sgold@sonic.net
 * @made generic by: Navin 
 */
public class Main extends SimpleApplication {

    private BulletAppState bulletAppState;
    private PhysicsSpace physicsSpace;
    private final long startTime = Instant.now().getEpochSecond();

    public static void main(String[] ignored) {
        Main app = new Main();
        AppSettings as = new AppSettings(true);
        as.setWidth(1020);
        as.setHeight(600);
        as.setVSync(false);
        app.setSettings(as);
        app.setShowSettings(false);
        app.start();
        //new Main().start();
    }

    @Override
    public void simpleInitApp() {        
        configureCamera();
        configurePhysics();
        addPlatform();
        addRobot();
    }
    
    /**
     * Add a large static box to serve as a platform.
     */
    private void addPlatform() {
        float halfExtent = 50f;
        Node boxNode = new Node("box");
        rootNode.attachChild(boxNode);
        boxNode.move(0f, -halfExtent, 0f);
        BoxCollisionShape shape = new BoxCollisionShape(new Vector3f(halfExtent, halfExtent, halfExtent));
        float mass = 0f;
        RigidBodyControl boxBody = new RigidBodyControl(shape, mass);
        boxNode.addControl(boxBody);
        boxBody.setPhysicsSpace(physicsSpace);
    }

     /**
     * Add a rectangular limb to the robot chassis.
     */
    private void addLimb(Node limbGeom, Vector3f limbInWorld, Vector3f pivotInChassis, Vector3f chassisInWorld, RigidBodyControl chassisRbc, Vector3f limbSize) {
        rootNode.attachChild(limbGeom);

        CollisionShape shape = new BoxCollisionShape(limbSize);
        limbGeom.move(limbInWorld);

        float mass = 10f;//this mass can be a much smaller value
        RigidBodyControl limbRbc = new RigidBodyControl(shape, mass);
        limbRbc.setSleepingThresholds(0f, 0f);
        limbGeom.addControl(limbRbc);
        limbRbc.setPhysicsSpace(physicsSpace);

        Vector3f pivotInLimb = pivotInChassis.add(chassisInWorld).subtractLocal(limbInWorld);
        SixDofJoint joint = new SixDofJoint(chassisRbc, limbRbc, pivotInChassis, pivotInLimb, false);

        joint.setAngularLowerLimit(new Vector3f(0f, 0f, -9e9f));
        joint.setAngularUpperLimit(new Vector3f(0f, 0f, 9e9f));

        //---Enable the motor for Z-axis rotation and return a reference to it.
        RotationalLimitMotor motor = joint.getRotationalLimitMotor(PhysicsSpace.AXIS_Z);
        motor.setEnableMotor(true);
        motor.setHiLimit(9e9f);
        motor.setLoLimit(-9e9f);
        motor.setMaxMotorForce(9e9f);
        motor.setTargetVelocity(1);

        physicsSpace.add(joint);
    }

     /**
     * Add a 4-legged robot to the scene.
     */
    private void addRobot() {
        Vector3f chassisSize = new Vector3f(2f, 0.3f, 0.3f);
        Vector3f chassisInWorld = new Vector3f(0f, 1f, 0f);
        Vector3f limbSize = new Vector3f(0.05f, 0.6f, 0.05f);
        CollisionShape chassisShape = new BoxCollisionShape(chassisSize);        
        float chassisMass = 1f;

        Node chassisNode = new Node("chassis");
        rootNode.attachChild(chassisNode);
        chassisNode.move(chassisInWorld);
        RigidBodyControl chassisRbc = new RigidBodyControl(chassisShape, chassisMass);
        chassisRbc.setSleepingThresholds(0f, 0f);
        chassisNode.addControl(chassisRbc);
        chassisRbc.setPhysicsSpace(physicsSpace);
        float limbOffset = 4 * limbSize.z;
        
        Node lfGeom = new Node("lf limb");
        Vector3f lfLimbInWorld = new Vector3f(chassisInWorld.x-chassisSize.x+limbSize.x, chassisInWorld.y+limbSize.y, chassisInWorld.z+chassisSize.z/2+limbSize.z/2+limbOffset);
        Vector3f lfPivotInChassis = new Vector3f(-chassisSize.x+limbSize.x, 0f, chassisInWorld.z+chassisSize.z/2-limbSize.z/2+limbOffset);
        addLimb(lfGeom, lfLimbInWorld, lfPivotInChassis, chassisInWorld, chassisRbc, limbSize);

        Node rfGeom = new Node("rf limb");
        Vector3f rfLimbInWorld = new Vector3f(chassisInWorld.x-chassisSize.x+limbSize.x, chassisInWorld.y+limbSize.y, chassisInWorld.z-chassisSize.z/2-limbSize.z/2-limbOffset);
        Vector3f rfPivotInChassis = new Vector3f(-chassisSize.x+limbSize.x, 0f, chassisInWorld.z-chassisSize.z/2+limbSize.z/2-limbOffset);
        addLimb(rfGeom, rfLimbInWorld, rfPivotInChassis, chassisInWorld, chassisRbc, limbSize);

        Node lrGeom = new Node("lr limb");
        Vector3f lrLimbInWorld = new Vector3f(chassisInWorld.x+chassisSize.x-limbSize.x, chassisInWorld.y+limbSize.y, chassisInWorld.z+chassisSize.z/2+limbSize.z/2+limbOffset);
        Vector3f lrPivotInChassis = new Vector3f(chassisSize.x-limbSize.x, 0f, chassisInWorld.z+chassisSize.z/2-limbSize.z/2+limbOffset);
        addLimb(lrGeom, lrLimbInWorld, lrPivotInChassis, chassisInWorld, chassisRbc, limbSize);

        Node rrGeom = new Node("rr limb");
        Vector3f rrLimbInWorld = new Vector3f(chassisInWorld.x+chassisSize.x-limbSize.x, chassisInWorld.y+limbSize.y, chassisInWorld.z-chassisSize.z/2-limbSize.z/2-limbOffset);
        Vector3f rrPivotInChassis = new Vector3f(chassisSize.x-limbSize.x, 0f, chassisInWorld.z-chassisSize.z/2+limbSize.z/2-limbOffset);
        addLimb(rrGeom, rrLimbInWorld, rrPivotInChassis, chassisInWorld, chassisRbc, limbSize);
    }

    @Override
    public void simpleUpdate(float tpf) {
        //stopping the motors after a while to see what happens
        if (Instant.now().getEpochSecond() - startTime > 6) {stopMotors();}
    }    
    
    private void stopMotors() {
        Collection<PhysicsJoint> joints = physicsSpace.getJointList();
        for (PhysicsJoint p: joints) {
            SixDofJoint j = (SixDofJoint) p;
            //j.getRotationalLimitMotor(PhysicsSpace.AXIS_Z).setEnableMotor(false);
            j.getRotationalLimitMotor(PhysicsSpace.AXIS_Z).setTargetVelocity(0);
        }           
    }

     /**
     * Configure the camera during startup.
     */
    private void configureCamera() {
        flyCam.setDragToRotate(true);
        flyCam.setMoveSpeed(4f);
        cam.setLocation(new Vector3f(0f, 1f, 10f));
        //cam.setRotation(new Quaternion(0f, 0.9759f, -0.04f, -0.2136f));
    }

    /**
     * Configure physics during startup.
     */
    private void configurePhysics() {
        bulletAppState = new BulletAppState();
        bulletAppState.setDebugEnabled(true);
        stateManager.attach(bulletAppState);
        physicsSpace = bulletAppState.getPhysicsSpace();        
    }
}
1 Like