Hi, hdietrich!
Thanks for such a quick reply. I modified the test code as you proposed and it seems to work fine. Threfore, I suggest there’s something wrong with my code. I think it’s better to paste some of my code, even though it doesn’t run as such. You’ll probably get a grasp of what I’m trying to to. Actually, If you ever need pure java test classes (without any bindings), I’d be happy to contribute my code bits (2d scenegraph with picking, dragging etc.)
So a few explanations of the code below:
- I’m binding JOODE to my 2d scenegraph, where “Model” actually refers to geometry of an object
- JOODEPhysicsAnimator is run in the normal “update world, handle paint” simulation loop (yes, it’s not yet thread-safe, and this probably will cause some problems as UI events are handled in a separate thread)
- I’m trying to make red box, black box movable in the x axis direction of seesaw, support movable in the x axis of world (fixed in y axis of the world)
- I haven’t implemented any collision callbacks, I suppose I don’t even need them if all elements are connected by some joints
- When I get this working, I probably need to add some geometry for ground to limit the rotation of seesaw
- Later on, I probably isolate all bits of JOODE to my JOODEPhysicsAnimator class
Here are some open questions:
- When attaching a geometry to body, is the mass, by default equally divided throughout the body (constant density)?
- … Um… there’s got to be more open thingies, I’ll return them to later on after writing some new bits of test code…
// Setup physics
JOODEPhysicsAnimator physics = new JOODEPhysicsAnimator();
physics.setController(controller);
bluePhysics = physics.createJOODEPhysics(blue, 1);
redPhysics = physics.createJOODEPhysics(red, 1);
seesawPhysics = physics.createJOODEPhysics(seesaw, 1);
supportPhysics = physics.createJOODEPhysics(support, 1);
physics.addJOODEPhysics(redPhysics);
physics.addJOODEPhysics(bluePhysics);
physics.addJOODEPhysics(seesawPhysics);
physics.addJOODEPhysics(supportPhysics);
// Attach support to the floor, make it slideable
World world = physics.getWorld();
JointSlider js = world.createSlider(null);
js.attach(supportPhysics.getBody(), null);
js.dJointSetSliderAxis(1, 0, 0);
// Attach seesaw to support, make it a hinge to allow rotation
// Make hinge slideable by attaching a slider on top of that
JointHinge jh = world.createHinge(null);
Body tempBody = world.createBody();
Vector2D v = seesaw.getTranslation();
tempBody.pos.set((float) v.getX(), (float) v.getY(), 0f);
jh.attach(supportPhysics.getBody(), tempBody);
jh.dJointSetHingeAnchor((float) v.getX(), (float) v.getY(), 0f);
jh.dJointSetHingeAxis(0, 0, 1);
JointSlider jhs = world.createSlider(null);
jhs.attach(tempBody, seesawPhysics.getBody());
jhs.dJointSetSliderAxis(1, 0, 0);
And for reference, here’s the functions of my physics class that get called
import java.util.ArrayList;
import fi.tammi.math.Physics;
import fi.tammi.math.Vector2D;
import fi.tammi.util.Model;
import java.awt.geom.Rectangle2D;
import net.java.dev.joode.world.*;
import net.java.dev.joode.body.*;
import net.java.dev.joode.util.Vector3;
import net.java.dev.joode.geom.Box;
import net.java.dev.joode.geom.Geom;
import net.java.dev.joode.space.NearCallback;
import net.java.dev.joode.space.SimpleSpace;
import net.java.dev.joode.space.Space;
import net.java.dev.joode.util.Quaternion;
public class JOODEPhysicsAnimator implements AnimationListener {
private ArrayList joodeObjects;
private AbstractAnimationController controller;
private double updated;
private Vector2D temp;
// Joode objects
private World world;
private Space space;
private Vector3 tempVect;
public JOODEPhysicsAnimator() {
joodeObjects = new ArrayList();
updated = 0;
temp = new Vector2D();
tempVect = new Vector3();
world = new World();
world.gravity.set(0, 9.81f, 0);
space = new SimpleSpace(null);
}
public JOODEPhysics createJOODEPhysics(Model m, double mass) {
JOODEPhysics p = new JOODEPhysics();
p.setModel(m);
p.setMass(mass);
return p;
}
public void addJOODEPhysics(JOODEPhysics p) {
joodeObjects.add(p);
}
public void removeJOODEPhysics(JOODEPhysics p) {
joodeObjects.remove(p);
}
public void animationFinished() {
}
public void animationStarted() {
updated = 0;
}
/**
* Update physics every frame
*/
public void frameChanged(double now) {
double timeDiff = now - updated;
updated = now;
// Update geometry positions & translations
for (int i = 0; i < joodeObjects.size(); i++) {
// Update geometry positions based on physics
updatePhysics((JOODEPhysics) joodeObjects.get(i));
}
// Update physics
//space.collide(null, null);
// TODO Get rid of this kludge - instead,
// put JOODE to handle zero difference
world.step((float) timeDiff + (0.0001f));
// Update positions for all the models
for (int i = 0; i < joodeObjects.size(); i++) {
// Update geometry positions based on physics
updateGeometryPosition((JOODEPhysics) joodeObjects.get(i));
}
}
private void updateGeometryPosition(JOODEPhysics p) {
Model m = p.getModel();
Body b = p.getBody();
// Fetch the translation & rotation from geometry
temp.set((double) b.pos.getX(),(double) b.pos.getY());
m.setTranslation(temp);
double rot = toAxisAngle(b.q, tempVect);
m.setRotation(rot);
}
private void updatePhysics(JOODEPhysics p) {
Model m = p.getModel();
Body b = p.getBody();
Vector2D xlat = m.getTranslation();
double angle = m.getRotation();
// Fetch the translation & rotation from geometry
b.pos.set((float) xlat.getX(),(float) xlat.getY(), 0f);
tempVect.set(0f, 0f, 1f);
toQuaternion(tempVect, angle, b.q);
}
public AbstractAnimationController getController() {
return controller;
}
public void setController(AbstractAnimationController controller) {
if (controller == this.controller)
return;
if (this.controller != null) {
this.controller.removeAnimationListener(this);
}
if (controller != null) {
controller.addAnimationListener(this);
}
this.controller = controller;
}
/**
* Conversion from quaternion to axis angle, see the link below for formulas.
*
* http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm
*
* @param q Source quaternion
* @param dst Destination axis
* @return Destination angle
*/
public double toAxisAngle(Quaternion q, Vector3 dst) {
double angle = 2 * Math.acos(q.m[0]);
dst.setX((float) (q.m[1] / Math.sqrt(1-q.m[3]*q.m[0])));
dst.setY((float) (q.m[2] / Math.sqrt(1-q.m[3]*q.m[0])));
dst.setZ((float) (q.m[3] / Math.sqrt(1-q.m[3]*q.m[0])));
return angle;
}
/**
* Conversion from axis angle to quaternion, see the link below for formulas.
*
* http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm
*
* @param v Source vector axis
* @param angle Source angle
* @param q Destination quaternion
* @return The quaternion transformed (q)
*/
public Quaternion toQuaternion(Vector3 v, double angle, Quaternion q) {
q.m[1] = v.getX() * (float) Math.sin(angle/2);
q.m[2] = v.getY() * (float) Math.sin(angle/2);
q.m[3] = v.getZ() * (float) Math.sin(angle/2);
q.m[0] = (float) Math.cos(angle/2);
return q;
}
public class JOODEPhysics implements Physics {
public static final double CONSTANT_DEPTH = 1.0;
private Body body;
private Vector2D velocityCache;
private Vector2D accelerationCache;
private double massCache;
private Model model;
private Geom geometry;
public JOODEPhysics() {
body = world.createBody();
velocityCache = new Vector2D();
accelerationCache = new Vector2D();
massCache = 0;
}
public Vector2D getVelocity() {
velocityCache.set(body.avel.getX(), body.avel.getY());
return velocityCache;
}
public void setMass(double mass) {
massCache = mass;
body.mass.adjust((float) mass);
}
public double getMass() {
return massCache;
}
public Vector2D getAcceleration() {
accelerationCache.set(body.facc.getX(), body.facc.getY());
return accelerationCache;
}
public void setAcceleration() {
}
public Model getModel() {
return model;
}
public void setModel(Model model) {
this.model = model;
// TODO Handle setting this to null
// Update physics parameters
// For now, update geometry as with bounding box
Rectangle2D rect = model.getBoundingBox();
float width = (float) rect.getWidth();
float height = (float) rect.getHeight();
float depth = (float) CONSTANT_DEPTH;
geometry = new Box(space, width, height, depth);
geometry.setBody(body);
// Fetch the translation & rotation from model
Vector2D xlat = model.getTranslation();
double rot = model.getRotation();
geometry.pos.set((float) xlat.getX(), (float) xlat.getY(), 0);
body.pos.set((float) xlat.getX(), (float) xlat.getY(), 0);
// Set to pointing upwards from screen (z = 1, others 0)
tempVect.set(0,0,1);
toQuaternion(tempVect, rot, body.q);
}
public Body getBody() {
return body;
}
public Geom getGeometry() {
return geometry;
}
}
public Space getSpace() {
return space;
}
public World getWorld() {
return world;
}
}