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):
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);}
}
}