summaryrefslogtreecommitdiffstats
path: root/src/jbullet
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2008-07-21 08:03:46 +0000
committerSven Gothel <[email protected]>2008-07-21 08:03:46 +0000
commite3b90ddc82f92c3e10ee732b3b4d8019cbc94190 (patch)
treea09bdcd13d91575f4451d72075c1c59a7de8c135 /src/jbullet
parentbcd03ce42585f197c81dcc50e4b289494dbc7c65 (diff)
Initial jbullet commit - workin ragdoll for profiles (GLES1, GL2ES12 and GL2)
git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/../svn-server-sync/jogl-demos/branches/JOGL_2_SANDBOX@263 3298f667-5e0e-4b4a-8ed4-a3559d26a5f4
Diffstat (limited to 'src/jbullet')
-rw-r--r--src/jbullet/src/javabullet/demos/vehicle/VehicleDemo.java-nope576
-rw-r--r--src/jbullet/src/javabullet/dynamics/DiscreteDynamicsWorld.java985
-rw-r--r--src/jbullet/src/javabullet/dynamics/DynamicsWorld.java104
-rw-r--r--src/jbullet/src/javabullet/dynamics/DynamicsWorldType.java34
-rw-r--r--src/jbullet/src/javabullet/dynamics/RigidBody.java639
-rw-r--r--src/jbullet/src/javabullet/dynamics/RigidBodyConstructionInfo.java92
-rw-r--r--src/jbullet/src/javabullet/dynamics/SimpleDynamicsWorld.java236
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/ConeTwistConstraint.java419
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/ConstraintPersistentData.java90
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/ConstraintSolver.java55
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/ContactConstraint.java460
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/ContactConstraintEnum.java37
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/ContactSolverFunc.java37
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/ContactSolverInfo.java57
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/Generic6DofConstraint.java505
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/HingeConstraint.java619
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/JacobianEntry.java231
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/Point2PointConstraint.java197
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/RotationalLimitMotor.java211
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/SequentialImpulseConstraintSolver.java1233
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/SolverBody.java82
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/SolverConstraint.java55
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/SolverConstraintType.java33
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/SolverMode.java37
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/TranslationalLimitMotor.java155
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/TypedConstraint.java101
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/TypedConstraintType.java36
-rw-r--r--src/jbullet/src/javabullet/dynamics/constraintsolver/package-info.java28
-rw-r--r--src/jbullet/src/javabullet/dynamics/package-info.java28
-rw-r--r--src/jbullet/src/javabullet/dynamics/vehicle/DefaultVehicleRaycaster.java63
-rw-r--r--src/jbullet/src/javabullet/dynamics/vehicle/RaycastVehicle.java765
-rw-r--r--src/jbullet/src/javabullet/dynamics/vehicle/VehicleRaycaster.java37
-rw-r--r--src/jbullet/src/javabullet/dynamics/vehicle/VehicleRaycasterResult.java38
-rw-r--r--src/jbullet/src/javabullet/dynamics/vehicle/VehicleTuning.java38
-rw-r--r--src/jbullet/src/javabullet/dynamics/vehicle/WheelInfo.java145
-rw-r--r--src/jbullet/src/javabullet/dynamics/vehicle/WheelInfoConstructionInfo.java47
-rw-r--r--src/jbullet/src/javabullet/linearmath/AabbUtil2.java139
-rw-r--r--src/jbullet/src/javabullet/linearmath/Clock.java56
-rw-r--r--src/jbullet/src/javabullet/linearmath/DebugDrawModes.java46
-rw-r--r--src/jbullet/src/javabullet/linearmath/DefaultMotionState.java65
-rw-r--r--src/jbullet/src/javabullet/linearmath/GeometryUtil.java185
-rw-r--r--src/jbullet/src/javabullet/linearmath/IDebugDraw.java86
-rw-r--r--src/jbullet/src/javabullet/linearmath/MatrixUtil.java208
-rw-r--r--src/jbullet/src/javabullet/linearmath/MiscUtil.java156
-rw-r--r--src/jbullet/src/javabullet/linearmath/MotionState.java41
-rw-r--r--src/jbullet/src/javabullet/linearmath/QuaternionUtil.java104
-rw-r--r--src/jbullet/src/javabullet/linearmath/ScalarUtil.java58
-rw-r--r--src/jbullet/src/javabullet/linearmath/Transform.java148
-rw-r--r--src/jbullet/src/javabullet/linearmath/TransformUtil.java174
-rw-r--r--src/jbullet/src/javabullet/linearmath/VectorUtil.java189
-rw-r--r--src/jbullet/src/javabullet/linearmath/package-info.java28
-rw-r--r--src/jbullet/src/javabullet/util/FloatArrayList.java71
-rw-r--r--src/jbullet/src/javabullet/util/HashUtil.java126
-rw-r--r--src/jbullet/src/javabullet/util/HashUtil.java-orig126
-rw-r--r--src/jbullet/src/javabullet/util/IntArrayList.java71
-rw-r--r--src/jbullet/src/javabullet/util/package-info.java28
56 files changed, 10610 insertions, 0 deletions
diff --git a/src/jbullet/src/javabullet/demos/vehicle/VehicleDemo.java-nope b/src/jbullet/src/javabullet/demos/vehicle/VehicleDemo.java-nope
new file mode 100644
index 0000000..7c9efbd
--- /dev/null
+++ b/src/jbullet/src/javabullet/demos/vehicle/VehicleDemo.java-nope
@@ -0,0 +1,576 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.demos.vehicle;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.List;
+import javabullet.collision.broadphase.BroadphaseInterface;
+import javabullet.collision.broadphase.SimpleBroadphase;
+import javabullet.collision.dispatch.CollisionDispatcher;
+import javabullet.collision.dispatch.CollisionObject;
+import javabullet.collision.dispatch.DefaultCollisionConfiguration;
+import javabullet.collision.shapes.BoxShape;
+import javabullet.collision.shapes.BvhTriangleMeshShape;
+import javabullet.collision.shapes.CollisionShape;
+import javabullet.collision.shapes.CompoundShape;
+import javabullet.collision.shapes.CylinderShapeX;
+import javabullet.collision.shapes.TriangleIndexVertexArray;
+import javabullet.demos.opengl.JOGL;
+import javabullet.demos.opengl.DemoApplication;
+import javabullet.demos.opengl.GLShapeDrawer;
+import javabullet.dynamics.DiscreteDynamicsWorld;
+import javabullet.dynamics.RigidBody;
+import javabullet.dynamics.constraintsolver.ConstraintSolver;
+import javabullet.dynamics.constraintsolver.SequentialImpulseConstraintSolver;
+import javabullet.dynamics.vehicle.DefaultVehicleRaycaster;
+import javabullet.dynamics.vehicle.RaycastVehicle;
+import javabullet.dynamics.vehicle.VehicleRaycaster;
+import javabullet.dynamics.vehicle.VehicleTuning;
+import javabullet.dynamics.vehicle.WheelInfo;
+import javabullet.linearmath.Transform;
+import javax.vecmath.Vector3f;
+import javax.media.opengl.*;
+import java.awt.event.*;
+
+/**
+ * VehicleDemo shows how to setup and use the built-in raycast vehicle.
+ *
+ * @author jezek2
+ */
+public class VehicleDemo extends DemoApplication {
+
+ // By default, Bullet Vehicle uses Y as up axis.
+ // You can override the up axis, for example Z-axis up. Enable this define to see how to:
+ // //#define FORCE_ZAXIS_UP 1
+
+ //#ifdef FORCE_ZAXIS_UP
+ //int rightIndex = 0;
+ //int upIndex = 2;
+ //int forwardIndex = 1;
+ //btVector3 wheelDirectionCS0(0,0,-1);
+ //btVector3 wheelAxleCS(1,0,0);
+ //#else
+ private static final int rightIndex = 0;
+ private static final int upIndex = 1;
+ private static final int forwardIndex = 2;
+ private static final Vector3f wheelDirectionCS0 = new Vector3f(0,-1,0);
+ private static final Vector3f wheelAxleCS = new Vector3f(-1,0,0);
+ //#endif
+
+ private static final int maxProxies = 32766;
+ private static final int maxOverlap = 65535;
+
+ // RaycastVehicle is the interface for the constraint that implements the raycast vehicle
+ // notice that for higher-quality slow-moving vehicles, another approach might be better
+ // implementing explicit hinged-wheel constraints with cylinder collision, rather then raycasts
+ private static float gEngineForce = 0.f;
+ private static float gBreakingForce = 0.f;
+
+ private static float maxEngineForce = 1000.f;//this should be engine/velocity dependent
+ private static float maxBreakingForce = 100.f;
+
+ private static float gVehicleSteering = 0.f;
+ private static float steeringIncrement = 0.04f;
+ private static float steeringClamp = 0.3f;
+ private static float wheelRadius = 0.5f;
+ private static float wheelWidth = 0.4f;
+ private static float wheelFriction = 1000;//1e30f;
+ private static float suspensionStiffness = 20.f;
+ private static float suspensionDamping = 2.3f;
+ private static float suspensionCompression = 4.4f;
+ private static float rollInfluence = 0.1f;//1.0f;
+
+ private static final float suspensionRestLength = 0.6f;
+
+ private static final int CUBE_HALF_EXTENTS = 1;
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public RigidBody carChassis;
+ public List<CollisionShape> collisionShapes = new ArrayList<CollisionShape>();
+ public BroadphaseInterface overlappingPairCache;
+ public CollisionDispatcher dispatcher;
+ public ConstraintSolver constraintSolver;
+ public DefaultCollisionConfiguration collisionConfiguration;
+ public TriangleIndexVertexArray indexVertexArrays;
+
+ public ByteBuffer vertices;
+
+ public VehicleTuning tuning = new VehicleTuning();
+ public VehicleRaycaster vehicleRayCaster;
+ public RaycastVehicle vehicle;
+
+ public float cameraHeight;
+
+ public float minCameraDistance;
+ public float maxCameraDistance;
+
+ public VehicleDemo() {
+ super();
+ carChassis = null;
+ cameraHeight = 4f;
+ minCameraDistance = 3f;
+ maxCameraDistance = 10f;
+ indexVertexArrays = null;
+ vertices = null;
+ vehicle = null;
+ cameraPosition.set(30, 30, 30);
+ }
+
+ public void initPhysics() {
+ //#ifdef FORCE_ZAXIS_UP
+ //m_cameraUp = btVector3(0,0,1);
+ //m_forwardAxis = 1;
+ //#endif
+
+ CollisionShape groundShape = new BoxShape(new Vector3f(50, 3, 50));
+ collisionShapes.add(groundShape);
+ collisionConfiguration = new DefaultCollisionConfiguration();
+ dispatcher = new CollisionDispatcher(collisionConfiguration);
+ Vector3f worldMin = new Vector3f(-1000, -1000, -1000);
+ Vector3f worldMax = new Vector3f(1000, 1000, 1000);
+ //overlappingPairCache = new AxisSweep3(worldMin,worldMax);
+ overlappingPairCache = new SimpleBroadphase();
+ constraintSolver = new SequentialImpulseConstraintSolver();
+ dynamicsWorld = new DiscreteDynamicsWorld(dispatcher, overlappingPairCache, constraintSolver, collisionConfiguration);
+ //#ifdef FORCE_ZAXIS_UP
+ //dynamicsWorld.setGravity(new Vector3f(0, 0, -10));
+ //#endif
+
+ //m_dynamicsWorld->setGravity(btVector3(0,0,0));
+ Transform tr = new Transform();
+ tr.setIdentity();
+
+ // either use heightfield or triangle mesh
+ //#define USE_TRIMESH_GROUND 1
+ //#ifdef USE_TRIMESH_GROUND
+
+ final float TRIANGLE_SIZE = 20f;
+
+ // create a triangle-mesh ground
+ int vertStride = 4 * 3 /* sizeof(btVector3) */;
+ int indexStride = 3 * 4 /* 3*sizeof(int) */;
+
+ final int NUM_VERTS_X = 20;
+ final int NUM_VERTS_Y = 20;
+ final int totalVerts = NUM_VERTS_X * NUM_VERTS_Y;
+
+ final int totalTriangles = 2 * (NUM_VERTS_X - 1) * (NUM_VERTS_Y - 1);
+
+ vertices = ByteBuffer.allocateDirect(totalVerts * vertStride).order(ByteOrder.nativeOrder());
+ ByteBuffer gIndices = ByteBuffer.allocateDirect(totalTriangles * 3 * 4).order(ByteOrder.nativeOrder());
+
+ Vector3f tmp = new Vector3f();
+ for (int i = 0; i < NUM_VERTS_X; i++) {
+ for (int j = 0; j < NUM_VERTS_Y; j++) {
+ float wl = 0.2f;
+ // height set to zero, but can also use curved landscape, just uncomment out the code
+ float height = 0f; // 20f * (float)Math.sin(i * wl) * (float)Math.cos(j * wl);
+
+ //#ifdef FORCE_ZAXIS_UP
+ //m_vertices[i+j*NUM_VERTS_X].setValue(
+ // (i-NUM_VERTS_X*0.5f)*TRIANGLE_SIZE,
+ // (j-NUM_VERTS_Y*0.5f)*TRIANGLE_SIZE,
+ // height
+ // );
+ //#else
+ tmp.set(
+ (i - NUM_VERTS_X * 0.5f) * TRIANGLE_SIZE,
+ height,
+ (j - NUM_VERTS_Y * 0.5f) * TRIANGLE_SIZE);
+
+ int index = i + j * NUM_VERTS_X;
+ vertices.putFloat((index * 3 + 0) * 4, tmp.x);
+ vertices.putFloat((index * 3 + 1) * 4, tmp.y);
+ vertices.putFloat((index * 3 + 2) * 4, tmp.z);
+ //#endif
+ }
+ }
+
+ //int index=0;
+ gIndices.clear();
+ for (int i = 0; i < NUM_VERTS_X - 1; i++) {
+ for (int j = 0; j < NUM_VERTS_Y - 1; j++) {
+ gIndices.putInt(j * NUM_VERTS_X + i);
+ gIndices.putInt(j * NUM_VERTS_X + i + 1);
+ gIndices.putInt((j + 1) * NUM_VERTS_X + i + 1);
+
+ gIndices.putInt(j * NUM_VERTS_X + i);
+ gIndices.putInt((j + 1) * NUM_VERTS_X + i + 1);
+ gIndices.putInt((j + 1) * NUM_VERTS_X + i);
+ }
+ }
+ gIndices.flip();
+
+ indexVertexArrays = new TriangleIndexVertexArray(totalTriangles,
+ gIndices,
+ indexStride,
+ totalVerts, vertices, vertStride);
+
+ boolean useQuantizedAabbCompression = true;
+ groundShape = new BvhTriangleMeshShape(indexVertexArrays, useQuantizedAabbCompression);
+
+ tr.origin.set(0, -4.5f, 0);
+
+ //#else
+// //testing btHeightfieldTerrainShape
+// int width=128;
+// int length=128;
+// unsigned char* heightfieldData = new unsigned char[width*length];
+// {
+// for (int i=0;i<width*length;i++)
+// {
+// heightfieldData[i]=0;
+// }
+// }
+//
+// char* filename="heightfield128x128.raw";
+// FILE* heightfieldFile = fopen(filename,"r");
+// if (!heightfieldFile)
+// {
+// filename="../../heightfield128x128.raw";
+// heightfieldFile = fopen(filename,"r");
+// }
+// if (heightfieldFile)
+// {
+// int numBytes =fread(heightfieldData,1,width*length,heightfieldFile);
+// //btAssert(numBytes);
+// if (!numBytes)
+// {
+// printf("couldn't read heightfield at %s\n",filename);
+// }
+// fclose (heightfieldFile);
+// }
+//
+//
+// btScalar maxHeight = 20000.f;
+//
+// bool useFloatDatam=false;
+// bool flipQuadEdges=false;
+//
+// btHeightfieldTerrainShape* heightFieldShape = new btHeightfieldTerrainShape(width,length,heightfieldData,maxHeight,upIndex,useFloatDatam,flipQuadEdges);;
+// groundShape = heightFieldShape;
+//
+// heightFieldShape->setUseDiamondSubdivision(true);
+//
+// btVector3 localScaling(20,20,20);
+// localScaling[upIndex]=1.f;
+// groundShape->setLocalScaling(localScaling);
+//
+// tr.setOrigin(btVector3(0,-64.5f,0));
+//
+ //#endif
+
+ collisionShapes.add(groundShape);
+
+ // create ground object
+ localCreateRigidBody(0, tr, groundShape);
+
+ //#ifdef FORCE_ZAXIS_UP
+ // // indexRightAxis = 0;
+ // // indexUpAxis = 2;
+ // // indexForwardAxis = 1;
+ //btCollisionShape* chassisShape = new btBoxShape(btVector3(1.f,2.f, 0.5f));
+ //btCompoundShape* compound = new btCompoundShape();
+ //btTransform localTrans;
+ //localTrans.setIdentity();
+ // //localTrans effectively shifts the center of mass with respect to the chassis
+ //localTrans.setOrigin(btVector3(0,0,1));
+ //#else
+ CollisionShape chassisShape = new BoxShape(new Vector3f(1.0f, 0.5f, 2.0f));
+ collisionShapes.add(chassisShape);
+
+ CompoundShape compound = new CompoundShape();
+ collisionShapes.add(compound);
+ Transform localTrans = new Transform();
+ localTrans.setIdentity();
+ // localTrans effectively shifts the center of mass with respect to the chassis
+ localTrans.origin.set(0, 1, 0);
+ //#endif
+
+ compound.addChildShape(localTrans, chassisShape);
+
+ tr.origin.set(0, 0, 0);
+
+ carChassis = localCreateRigidBody(800, tr, compound); //chassisShape);
+ //m_carChassis->setDamping(0.2,0.2);
+
+ clientResetScene();
+
+ // create vehicle
+ {
+ vehicleRayCaster = new DefaultVehicleRaycaster(dynamicsWorld);
+ vehicle = new RaycastVehicle(tuning, carChassis, vehicleRayCaster);
+
+ // never deactivate the vehicle
+ carChassis.setActivationState(CollisionObject.DISABLE_DEACTIVATION);
+
+ dynamicsWorld.addVehicle(vehicle);
+
+ float connectionHeight = 1.2f;
+
+ boolean isFrontWheel = true;
+
+ // choose coordinate system
+ vehicle.setCoordinateSystem(rightIndex, upIndex, forwardIndex);
+
+ //#ifdef FORCE_ZAXIS_UP
+ //btVector3 connectionPointCS0(CUBE_HALF_EXTENTS-(0.3*wheelWidth),2*CUBE_HALF_EXTENTS-wheelRadius, connectionHeight);
+ //#else
+ Vector3f connectionPointCS0 = new Vector3f(CUBE_HALF_EXTENTS - (0.3f * wheelWidth), connectionHeight, 2f * CUBE_HALF_EXTENTS - wheelRadius);
+ //#endif
+
+ vehicle.addWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, isFrontWheel);
+ //#ifdef FORCE_ZAXIS_UP
+ //connectionPointCS0 = btVector3(-CUBE_HALF_EXTENTS+(0.3*wheelWidth),2*CUBE_HALF_EXTENTS-wheelRadius, connectionHeight);
+ //#else
+ connectionPointCS0.set(-CUBE_HALF_EXTENTS + (0.3f * wheelWidth), connectionHeight, 2f * CUBE_HALF_EXTENTS - wheelRadius);
+ //#endif
+
+ vehicle.addWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, isFrontWheel);
+ //#ifdef FORCE_ZAXIS_UP
+ //connectionPointCS0 = btVector3(-CUBE_HALF_EXTENTS+(0.3*wheelWidth),-2*CUBE_HALF_EXTENTS+wheelRadius, connectionHeight);
+ //#else
+ connectionPointCS0.set(-CUBE_HALF_EXTENTS + (0.3f * wheelWidth), connectionHeight, -2f * CUBE_HALF_EXTENTS + wheelRadius);
+ //#endif //FORCE_ZAXIS_UP
+ isFrontWheel = false;
+ vehicle.addWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, isFrontWheel);
+ //#ifdef FORCE_ZAXIS_UP
+ //connectionPointCS0 = btVector3(CUBE_HALF_EXTENTS-(0.3*wheelWidth),-2*CUBE_HALF_EXTENTS+wheelRadius, connectionHeight);
+ //#else
+ connectionPointCS0.set(CUBE_HALF_EXTENTS - (0.3f * wheelWidth), connectionHeight, -2f * CUBE_HALF_EXTENTS + wheelRadius);
+ //#endif
+ vehicle.addWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, isFrontWheel);
+
+ for (int i = 0; i < vehicle.getNumWheels(); i++) {
+ WheelInfo wheel = vehicle.getWheelInfo(i);
+ wheel.suspensionStiffness = suspensionStiffness;
+ wheel.wheelsDampingRelaxation = suspensionDamping;
+ wheel.wheelsDampingCompression = suspensionCompression;
+ wheel.frictionSlip = wheelFriction;
+ wheel.rollInfluence = rollInfluence;
+ }
+ }
+
+ setCameraDistance(26.f);
+ }
+
+ // to be implemented by the demo
+ @Override
+ public void renderme() {
+ updateCamera();
+
+ CylinderShapeX wheelShape = new CylinderShapeX(new Vector3f(wheelWidth, wheelRadius, wheelRadius));
+ Vector3f wheelColor = new Vector3f(1, 0, 0);
+
+ for (int i = 0; i < vehicle.getNumWheels(); i++) {
+ // synchronize the wheels with the (interpolated) chassis worldtransform
+ vehicle.updateWheelTransform(i, true);
+ // draw wheels (cylinders)
+ Transform trans = vehicle.getWheelInfo(i).worldTransform;
+ GLShapeDrawer.drawOpenGL(glsrt, gl, trans, wheelShape, wheelColor, getDebugMode());
+ }
+
+ super.renderme();
+ }
+
+ @Override
+ public void display(GLAutoDrawable drawable) {
+ gl = drawable.getGL();
+ gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT);
+
+ {
+ int wheelIndex = 2;
+ vehicle.applyEngineForce(gEngineForce,wheelIndex);
+ vehicle.setBrake(gBreakingForce,wheelIndex);
+ wheelIndex = 3;
+ vehicle.applyEngineForce(gEngineForce,wheelIndex);
+ vehicle.setBrake(gBreakingForce,wheelIndex);
+
+ wheelIndex = 0;
+ vehicle.setSteeringValue(gVehicleSteering,wheelIndex);
+ wheelIndex = 1;
+ vehicle.setSteeringValue(gVehicleSteering,wheelIndex);
+ }
+
+ float dt = clock.getTimeMicroseconds() * 0.000001f;
+ clock.reset();
+ if (dynamicsWorld != null)
+ {
+ // during idle mode, just run 1 simulation step maximum
+ int maxSimSubSteps = idle ? 1 : 2;
+ if (isIdle())
+ dt = 1f/420f;
+
+ int numSimSteps = dynamicsWorld.stepSimulation(dt,maxSimSubSteps);
+ // optional but useful: debug drawing
+ dynamicsWorld.debugDrawWorld();
+
+ //#define VERBOSE_FEEDBACK
+ //#ifdef VERBOSE_FEEDBACK
+ //if (!numSimSteps)
+ // printf("Interpolated transforms\n");
+ //else
+ //{
+ // if (numSimSteps > maxSimSubSteps)
+ // {
+ // //detect dropping frames
+ // printf("Dropped (%i) simulation steps out of %i\n",numSimSteps - maxSimSubSteps,numSimSteps);
+ // } else
+ // {
+ // printf("Simulated (%i) steps\n",numSimSteps);
+ // }
+ //}
+ //#endif //VERBOSE_FEEDBACK
+ }
+
+ //#ifdef USE_QUICKPROF
+ //btProfiler::beginBlock("render");
+ //#endif //USE_QUICKPROF
+
+ renderme();
+
+ //#ifdef USE_QUICKPROF
+ //btProfiler::endBlock("render");
+ //#endif
+ }
+
+ @Override
+ public void clientResetScene() {
+ gVehicleSteering = 0f;
+ Transform tr = new Transform();
+ tr.setIdentity();
+ carChassis.setCenterOfMassTransform(tr);
+ carChassis.setLinearVelocity(new Vector3f(0, 0, 0));
+ carChassis.setAngularVelocity(new Vector3f(0, 0, 0));
+ dynamicsWorld.getBroadphase().getOverlappingPairCache().cleanProxyFromPairs(carChassis.getBroadphaseHandle(), getDynamicsWorld().getDispatcher());
+ if (vehicle != null) {
+ vehicle.resetSuspension();
+ for (int i = 0; i < vehicle.getNumWheels(); i++) {
+ // synchronize the wheels with the (interpolated) chassis worldtransform
+ vehicle.updateWheelTransform(i, true);
+ }
+ }
+ }
+
+ @Override
+ public void specialKeyboard(int key) {
+ // printf("key = %i x=%i y=%i\n",key,x,y);
+
+ switch (key) {
+ case KeyEvent.VK_LEFT: {
+ gVehicleSteering += steeringIncrement;
+ if (gVehicleSteering > steeringClamp) {
+ gVehicleSteering = steeringClamp;
+ }
+ break;
+ }
+ case KeyEvent.VK_RIGHT: {
+ gVehicleSteering -= steeringIncrement;
+ if (gVehicleSteering < -steeringClamp) {
+ gVehicleSteering = -steeringClamp;
+ }
+ break;
+ }
+ case KeyEvent.VK_UP: {
+ gEngineForce = maxEngineForce;
+ gBreakingForce = 0.f;
+ break;
+ }
+ case KeyEvent.VK_DOWN: {
+ gBreakingForce = maxBreakingForce;
+ gEngineForce = 0.f;
+ break;
+ }
+ default:
+ super.specialKeyboard(key);
+ break;
+ }
+
+ //glutPostRedisplay();
+ }
+
+ @Override
+ public void updateCamera()
+ {
+
+ // //#define DISABLE_CAMERA 1
+ //#ifdef DISABLE_CAMERA
+ //DemoApplication::updateCamera();
+ //return;
+ //#endif //DISABLE_CAMERA
+
+ gl.glMatrixMode(gl.GL_PROJECTION);
+ gl.glLoadIdentity();
+
+ Transform chassisWorldTrans = new Transform();
+
+ // look at the vehicle
+ carChassis.getMotionState().getWorldTransform(chassisWorldTrans);
+ cameraTargetPosition.set(chassisWorldTrans.origin);
+
+ // interpolate the camera height
+ //#ifdef FORCE_ZAXIS_UP
+ //m_cameraPosition[2] = (15.0*m_cameraPosition[2] + m_cameraTargetPosition[2] + m_cameraHeight)/16.0;
+ //#else
+ cameraPosition.y = (15.0f*cameraPosition.y + cameraTargetPosition.y + cameraHeight) / 16.0f;
+ //#endif
+
+ Vector3f camToObject = new Vector3f();
+ camToObject.sub(cameraTargetPosition, cameraPosition);
+
+ // keep distance between min and max distance
+ float cameraDistance = camToObject.length();
+ float correctionFactor = 0f;
+ if (cameraDistance < minCameraDistance)
+ {
+ correctionFactor = 0.15f*(minCameraDistance-cameraDistance)/cameraDistance;
+ }
+ if (cameraDistance > maxCameraDistance)
+ {
+ correctionFactor = 0.15f*(maxCameraDistance-cameraDistance)/cameraDistance;
+ }
+ Vector3f tmp = new Vector3f();
+ tmp.scale(correctionFactor, camToObject);
+ cameraPosition.sub(tmp);
+
+ // update OpenGL camera settings
+ gl.glFrustum(-1.0, 1.0, -1.0, 1.0, 1.0, 10000.0);
+
+ glu.gluLookAt(cameraPosition.x,cameraPosition.y,cameraPosition.z,
+ cameraTargetPosition.x,cameraTargetPosition.y, cameraTargetPosition.z,
+ cameraUp.x,cameraUp.y,cameraUp.z);
+ gl.glMatrixMode(gl.GL_MODELVIEW);
+ }
+
+ public static void main(String[] args) {
+ VehicleDemo vehicleDemo = new VehicleDemo();
+ vehicleDemo.initPhysics();
+
+ JOGL.main(args, 640, 480, "Bullet Vehicle Demo", vehicleDemo);
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/DiscreteDynamicsWorld.java b/src/jbullet/src/javabullet/dynamics/DiscreteDynamicsWorld.java
new file mode 100644
index 0000000..237d4ec
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/DiscreteDynamicsWorld.java
@@ -0,0 +1,985 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import javabullet.BulletGlobals;
+import javabullet.collision.broadphase.BroadphaseInterface;
+import javabullet.collision.broadphase.CollisionFilterGroups;
+import javabullet.collision.broadphase.Dispatcher;
+import javabullet.collision.broadphase.DispatcherInfo;
+import javabullet.collision.dispatch.CollisionConfiguration;
+import javabullet.collision.dispatch.CollisionObject;
+import javabullet.collision.dispatch.CollisionWorld;
+import javabullet.collision.dispatch.SimulationIslandManager;
+import javabullet.collision.narrowphase.PersistentManifold;
+import javabullet.collision.shapes.CollisionShape;
+import javabullet.collision.shapes.InternalTriangleIndexCallback;
+import javabullet.collision.shapes.TriangleCallback;
+import javabullet.dynamics.constraintsolver.ConstraintSolver;
+import javabullet.dynamics.constraintsolver.ContactSolverInfo;
+import javabullet.dynamics.constraintsolver.SequentialImpulseConstraintSolver;
+import javabullet.dynamics.constraintsolver.TypedConstraint;
+import javabullet.dynamics.vehicle.RaycastVehicle;
+import javabullet.linearmath.DebugDrawModes;
+import javabullet.linearmath.IDebugDraw;
+import javabullet.linearmath.MiscUtil;
+import javabullet.linearmath.ScalarUtil;
+import javabullet.linearmath.Transform;
+import javabullet.linearmath.TransformUtil;
+import javax.vecmath.Vector3f;
+
+/**
+ * DiscreteDynamicsWorld provides discrete rigid body simulation.
+ * Those classes replace the obsolete CcdPhysicsEnvironment/CcdPhysicsController.
+ *
+ * @author jezek2
+ */
+public class DiscreteDynamicsWorld extends DynamicsWorld {
+
+ protected ConstraintSolver constraintSolver;
+ protected SimulationIslandManager islandManager;
+ protected final List<TypedConstraint> constraints = new ArrayList<TypedConstraint>();
+ protected final Vector3f gravity = new Vector3f(0f, -10f, 0f);
+
+ //for variable timesteps
+ protected float localTime = 1f / 60f;
+ //for variable timesteps
+
+ protected boolean ownsIslandManager;
+ protected boolean ownsConstraintSolver;
+
+ protected ContactSolverInfo solverInfo = new ContactSolverInfo();
+ protected List<RaycastVehicle> vehicles = new ArrayList<RaycastVehicle>();
+ protected int profileTimings = 0;
+
+ public DiscreteDynamicsWorld(Dispatcher dispatcher, BroadphaseInterface pairCache, ConstraintSolver constraintSolver, CollisionConfiguration collisionConfiguration) {
+ super(dispatcher, pairCache, collisionConfiguration);
+ this.constraintSolver = constraintSolver;
+
+ if (this.constraintSolver == null) {
+ this.constraintSolver = new SequentialImpulseConstraintSolver();
+ ownsConstraintSolver = true;
+ }
+ else {
+ ownsConstraintSolver = false;
+ }
+
+ {
+ islandManager = new SimulationIslandManager();
+ }
+
+ ownsIslandManager = true;
+ }
+
+ protected void saveKinematicState(float timeStep) {
+ for (int i = 0; i < collisionObjects.size(); i++) {
+ CollisionObject colObj = collisionObjects.get(i);
+ RigidBody body = RigidBody.upcast(colObj);
+ if (body != null) {
+ //Transform predictedTrans = new Transform();
+ if (body.getActivationState() != CollisionObject.ISLAND_SLEEPING) {
+ if (body.isKinematicObject()) {
+ // to calculate velocities next frame
+ body.saveKinematicState(timeStep);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void debugDrawWorld() {
+ stack.vectors.push();
+ try {
+ if (getDebugDrawer() != null && (getDebugDrawer().getDebugMode() & (DebugDrawModes.DRAW_WIREFRAME | DebugDrawModes.DRAW_AABB)) != 0) {
+ int i;
+
+ // todo: iterate over awake simulation islands!
+ for (i = 0; i < collisionObjects.size(); i++) {
+ CollisionObject colObj = collisionObjects.get(i);
+ if (getDebugDrawer() != null && (getDebugDrawer().getDebugMode() & DebugDrawModes.DRAW_WIREFRAME) != 0) {
+ Vector3f color = stack.vectors.get(255f, 255f, 255f);
+ switch (colObj.getActivationState()) {
+ case CollisionObject.ACTIVE_TAG:
+ color.set(255f, 255f, 255f);
+ break;
+ case CollisionObject.ISLAND_SLEEPING:
+ color.set(0f, 255f, 0f);
+ break;
+ case CollisionObject.WANTS_DEACTIVATION:
+ color.set(0f, 255f, 255f);
+ break;
+ case CollisionObject.DISABLE_DEACTIVATION:
+ color.set(255f, 0f, 0f);
+ break;
+ case CollisionObject.DISABLE_SIMULATION:
+ color.set(255f, 255f, 0f);
+ break;
+ default: {
+ color.set(255f, 0f, 0f);
+ }
+ }
+
+ debugDrawObject(colObj.getWorldTransform(), colObj.getCollisionShape(), color);
+ }
+ if (debugDrawer != null && (debugDrawer.getDebugMode() & DebugDrawModes.DRAW_AABB) != 0) {
+ Vector3f minAabb = stack.vectors.get(), maxAabb = stack.vectors.get();
+ Vector3f colorvec = stack.vectors.get(1f, 0f, 0f);
+ colObj.getCollisionShape().getAabb(colObj.getWorldTransform(), minAabb, maxAabb);
+ debugDrawer.drawAabb(minAabb, maxAabb, colorvec);
+ }
+ }
+
+ Vector3f wheelColor = stack.vectors.get();
+ Vector3f wheelPosWS = stack.vectors.get();
+ Vector3f axle = stack.vectors.get();
+ Vector3f tmp = stack.vectors.get();
+
+ for (i = 0; i < vehicles.size(); i++) {
+ for (int v = 0; v < vehicles.get(i).getNumWheels(); v++) {
+ wheelColor.set(0, 255, 255);
+ if (vehicles.get(i).getWheelInfo(v).raycastInfo.isInContact) {
+ wheelColor.set(0, 0, 255);
+ }
+ else {
+ wheelColor.set(255, 0, 255);
+ }
+
+ wheelPosWS.set(vehicles.get(i).getWheelInfo(v).worldTransform.origin);
+
+ axle.set(
+ vehicles.get(i).getWheelInfo(v).worldTransform.basis.getElement(0, vehicles.get(i).getRightAxis()),
+ vehicles.get(i).getWheelInfo(v).worldTransform.basis.getElement(1, vehicles.get(i).getRightAxis()),
+ vehicles.get(i).getWheelInfo(v).worldTransform.basis.getElement(2, vehicles.get(i).getRightAxis()));
+
+
+ //m_vehicles[i]->getWheelInfo(v).m_raycastInfo.m_wheelAxleWS
+ //debug wheels (cylinders)
+ tmp.add(wheelPosWS, axle);
+ debugDrawer.drawLine(wheelPosWS, tmp, wheelColor);
+ debugDrawer.drawLine(wheelPosWS, vehicles.get(i).getWheelInfo(v).raycastInfo.contactPointWS, wheelColor);
+ }
+ }
+ }
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ @Override
+ public void clearForces() {
+ // todo: iterate over awake simulation islands!
+ for (int i = 0; i < collisionObjects.size(); i++) {
+ CollisionObject colObj = collisionObjects.get(i);
+
+ RigidBody body = RigidBody.upcast(colObj);
+ if (body != null) {
+ body.clearForces();
+ }
+ }
+ }
+
+ /**
+ * Apply gravity, call this once per timestep.
+ */
+ public void applyGravity() {
+ // todo: iterate over awake simulation islands!
+ for (int i = 0; i < collisionObjects.size(); i++) {
+ CollisionObject colObj = collisionObjects.get(i);
+
+ RigidBody body = RigidBody.upcast(colObj);
+ if (body != null && body.isActive()) {
+ body.applyGravity();
+ }
+ }
+ }
+
+ protected void synchronizeMotionStates() {
+ stack.transforms.push();
+ try {
+ Transform interpolatedTransform = stack.transforms.get();
+
+ // todo: iterate over awake simulation islands!
+ for (int i = 0; i < collisionObjects.size(); i++) {
+ CollisionObject colObj = collisionObjects.get(i);
+
+ RigidBody body = RigidBody.upcast(colObj);
+ if (body != null && body.getMotionState() != null && !body.isStaticOrKinematicObject()) {
+ // we need to call the update at least once, even for sleeping objects
+ // otherwise the 'graphics' transform never updates properly
+ // so todo: add 'dirty' flag
+ //if (body->getActivationState() != ISLAND_SLEEPING)
+ {
+ TransformUtil.integrateTransform(body.getInterpolationWorldTransform(),
+ body.getInterpolationLinearVelocity(), body.getInterpolationAngularVelocity(), localTime, interpolatedTransform);
+ body.getMotionState().setWorldTransform(interpolatedTransform);
+ }
+ }
+ }
+
+ if (getDebugDrawer() != null && (getDebugDrawer().getDebugMode() & DebugDrawModes.DRAW_WIREFRAME) != 0) {
+ for (int i = 0; i < vehicles.size(); i++) {
+ for (int v = 0; v < vehicles.get(i).getNumWheels(); v++) {
+ // synchronize the wheels with the (interpolated) chassis worldtransform
+ vehicles.get(i).updateWheelTransform(v, true);
+ }
+ }
+ }
+ }
+ finally {
+ stack.transforms.pop();
+ }
+ }
+
+ public long nanoTime() {
+ // JAU: Orig: return System.nanoTime();
+ return System.currentTimeMillis()*1000000;
+ }
+
+ @Override
+ public int stepSimulation(float timeStep, int maxSubSteps, float fixedTimeStep) {
+ startProfiling(timeStep);
+
+ long t0 = nanoTime();
+
+ BulletGlobals.pushProfile("stepSimulation");
+ try {
+ int numSimulationSubSteps = 0;
+
+ if (maxSubSteps != 0) {
+ // fixed timestep with interpolation
+ localTime += timeStep;
+ if (localTime >= fixedTimeStep) {
+ numSimulationSubSteps = (int) (localTime / fixedTimeStep);
+ localTime -= numSimulationSubSteps * fixedTimeStep;
+ }
+ }
+ else {
+ //variable timestep
+ fixedTimeStep = timeStep;
+ localTime = timeStep;
+ if (ScalarUtil.fuzzyZero(timeStep)) {
+ numSimulationSubSteps = 0;
+ maxSubSteps = 0;
+ }
+ else {
+ numSimulationSubSteps = 1;
+ maxSubSteps = 1;
+ }
+ }
+
+ // process some debugging flags
+ if (getDebugDrawer() != null) {
+ BulletGlobals.gDisableDeactivation = (getDebugDrawer().getDebugMode() & DebugDrawModes.NO_DEACTIVATION) != 0;
+ }
+ if (numSimulationSubSteps != 0) {
+ saveKinematicState(fixedTimeStep);
+
+ applyGravity();
+
+ // clamp the number of substeps, to prevent simulation grinding spiralling down to a halt
+ int clampedSimulationSteps = (numSimulationSubSteps > maxSubSteps) ? maxSubSteps : numSimulationSubSteps;
+
+ for (int i = 0; i < clampedSimulationSteps; i++) {
+ internalSingleStepSimulation(fixedTimeStep);
+ synchronizeMotionStates();
+ }
+ }
+
+ synchronizeMotionStates();
+
+ clearForces();
+
+ // TODO: CProfileManager::Increment_Frame_Counter();
+
+ return numSimulationSubSteps;
+ }
+ finally {
+ BulletGlobals.popProfile();
+
+ BulletGlobals.stepSimulationTime = (nanoTime() - t0) / 1000000;
+ }
+ }
+
+ protected void internalSingleStepSimulation(float timeStep) {
+ BulletGlobals.pushProfile("internalSingleStepSimulation");
+ try {
+ // apply gravity, predict motion
+ predictUnconstraintMotion(timeStep);
+
+ DispatcherInfo dispatchInfo = getDispatchInfo();
+
+ dispatchInfo.timeStep = timeStep;
+ dispatchInfo.stepCount = 0;
+ dispatchInfo.debugDraw = getDebugDrawer();
+
+ // perform collision detection
+ performDiscreteCollisionDetection();
+
+ calculateSimulationIslands();
+
+ getSolverInfo().timeStep = timeStep;
+
+ // solve contact and other joint constraints
+ solveConstraints(getSolverInfo());
+
+ //CallbackTriggers();
+
+ // integrate transforms
+ integrateTransforms(timeStep);
+
+ // update vehicle simulation
+ updateVehicles(timeStep);
+
+ updateActivationState(timeStep);
+ }
+ finally {
+ BulletGlobals.popProfile();
+ }
+ }
+
+ @Override
+ public void setGravity(Vector3f gravity) {
+ this.gravity.set(gravity);
+ for (int i = 0; i < collisionObjects.size(); i++) {
+ CollisionObject colObj = collisionObjects.get(i);
+ RigidBody body = RigidBody.upcast(colObj);
+ if (body != null) {
+ body.setGravity(gravity);
+ }
+ }
+ }
+
+ @Override
+ public void removeRigidBody(RigidBody body) {
+ removeCollisionObject(body);
+ }
+
+ @Override
+ public void addRigidBody(RigidBody body) {
+ if (!body.isStaticOrKinematicObject()) {
+ body.setGravity(gravity);
+ }
+
+ if (body.getCollisionShape() != null) {
+ boolean isDynamic = !(body.isStaticObject() || body.isKinematicObject());
+ short collisionFilterGroup = isDynamic ? (short) CollisionFilterGroups.DEFAULT_FILTER : (short) CollisionFilterGroups.STATIC_FILTER;
+ short collisionFilterMask = isDynamic ? (short) CollisionFilterGroups.ALL_FILTER : (short) (CollisionFilterGroups.ALL_FILTER ^ CollisionFilterGroups.STATIC_FILTER);
+
+ addCollisionObject(body, collisionFilterGroup, collisionFilterMask);
+ }
+ }
+
+ public void addRigidBody(RigidBody body, short group, short mask) {
+ if (!body.isStaticOrKinematicObject()) {
+ body.setGravity(gravity);
+ }
+
+ if (body.getCollisionShape() != null) {
+ addCollisionObject(body, group, mask);
+ }
+ }
+
+ protected void updateVehicles(float timeStep) {
+ BulletGlobals.pushProfile("updateVehicles");
+ try {
+ for (int i = 0; i < vehicles.size(); i++) {
+ RaycastVehicle vehicle = vehicles.get(i);
+ vehicle.updateVehicle(timeStep);
+ }
+ }
+ finally {
+ BulletGlobals.popProfile();
+ }
+ }
+
+ protected void updateActivationState(float timeStep) {
+ BulletGlobals.pushProfile("updateActivationState");
+ try {
+ for (int i = 0; i < collisionObjects.size(); i++) {
+ CollisionObject colObj = collisionObjects.get(i);
+ RigidBody body = RigidBody.upcast(colObj);
+ if (body != null) {
+ body.updateDeactivation(timeStep);
+
+ if (body.wantsSleeping()) {
+ if (body.isStaticOrKinematicObject()) {
+ body.setActivationState(CollisionObject.ISLAND_SLEEPING);
+ }
+ else {
+ if (body.getActivationState() == CollisionObject.ACTIVE_TAG) {
+ body.setActivationState(CollisionObject.WANTS_DEACTIVATION);
+ }
+ }
+ }
+ else {
+ if (body.getActivationState() != CollisionObject.DISABLE_DEACTIVATION) {
+ body.setActivationState(CollisionObject.ACTIVE_TAG);
+ }
+ }
+ }
+ }
+ }
+ finally {
+ BulletGlobals.popProfile();
+ }
+ }
+
+ @Override
+ public void addConstraint(TypedConstraint constraint, boolean disableCollisionsBetweenLinkedBodies) {
+ constraints.add(constraint);
+ if (disableCollisionsBetweenLinkedBodies) {
+ constraint.getRigidBodyA().addConstraintRef(constraint);
+ constraint.getRigidBodyB().addConstraintRef(constraint);
+ }
+ }
+
+ @Override
+ public void removeConstraint(TypedConstraint constraint) {
+ constraints.remove(constraint);
+ constraint.getRigidBodyA().removeConstraintRef(constraint);
+ constraint.getRigidBodyB().removeConstraintRef(constraint);
+ }
+
+ @Override
+ public void addVehicle(RaycastVehicle vehicle) {
+ vehicles.add(vehicle);
+ }
+
+ @Override
+ public void removeVehicle(RaycastVehicle vehicle) {
+ vehicles.remove(vehicle);
+ }
+
+ private static int getConstraintIslandId(TypedConstraint lhs) {
+ int islandId;
+
+ CollisionObject rcolObj0 = lhs.getRigidBodyA();
+ CollisionObject rcolObj1 = lhs.getRigidBodyB();
+ islandId = rcolObj0.getIslandTag() >= 0 ? rcolObj0.getIslandTag() : rcolObj1.getIslandTag();
+ return islandId;
+ }
+
+ private static class InplaceSolverIslandCallback implements SimulationIslandManager.IslandCallback {
+ public ContactSolverInfo solverInfo;
+ public ConstraintSolver solver;
+ public List<TypedConstraint> sortedConstraints;
+ public int numConstraints;
+ public IDebugDraw debugDrawer;
+ //public StackAlloc* m_stackAlloc;
+ public Dispatcher dispatcher;
+
+ public void init(ContactSolverInfo solverInfo, ConstraintSolver solver, List<TypedConstraint> sortedConstraints, int numConstraints, IDebugDraw debugDrawer, Dispatcher dispatcher) {
+ this.solverInfo = solverInfo;
+ this.solver = solver;
+ this.sortedConstraints = sortedConstraints;
+ this.numConstraints = numConstraints;
+ this.debugDrawer = debugDrawer;
+ this.dispatcher = dispatcher;
+ }
+
+ public void processIsland(List<CollisionObject> bodies, int numBodies, List<PersistentManifold> manifolds, int manifolds_offset, int numManifolds, int islandId) {
+ if (islandId < 0) {
+ // we don't split islands, so all constraints/contact manifolds/bodies are passed into the solver regardless the island id
+ solver.solveGroup(bodies, numBodies, manifolds, manifolds_offset, numManifolds, sortedConstraints, 0, numConstraints, solverInfo, debugDrawer/*,m_stackAlloc*/, dispatcher);
+ }
+ else {
+ // also add all non-contact constraints/joints for this island
+ //List<TypedConstraint> startConstraint = null;
+ int startConstraint_idx = -1;
+ int numCurConstraints = 0;
+ int i;
+
+ // find the first constraint for this island
+ for (i = 0; i < numConstraints; i++) {
+ if (getConstraintIslandId(sortedConstraints.get(i)) == islandId) {
+ //startConstraint = &m_sortedConstraints[i];
+ //startConstraint = sortedConstraints.subList(i, sortedConstraints.size());
+ startConstraint_idx = i;
+ break;
+ }
+ }
+ // count the number of constraints in this island
+ for (; i < numConstraints; i++) {
+ if (getConstraintIslandId(sortedConstraints.get(i)) == islandId) {
+ numCurConstraints++;
+ }
+ }
+
+ solver.solveGroup(bodies, numBodies, manifolds, manifolds_offset, numManifolds, sortedConstraints, startConstraint_idx, numCurConstraints, solverInfo, debugDrawer/*,m_stackAlloc*/, dispatcher);
+ }
+ }
+ }
+
+ private List<TypedConstraint> sortedConstraints = new ArrayList<TypedConstraint>();
+ private InplaceSolverIslandCallback solverCallback = new InplaceSolverIslandCallback();
+
+ protected void solveConstraints(ContactSolverInfo solverInfo) {
+ BulletGlobals.pushProfile("solveConstraints");
+ try {
+ // sorted version of all btTypedConstraint, based on islandId
+ sortedConstraints.clear();
+ for (int i=0; i<constraints.size(); i++) {
+ sortedConstraints.add(constraints.get(i));
+ }
+ //Collections.sort(sortedConstraints, sortConstraintOnIslandPredicate);
+ MiscUtil.heapSort(sortedConstraints, sortConstraintOnIslandPredicate);
+
+ List<TypedConstraint> constraintsPtr = getNumConstraints() != 0 ? sortedConstraints : null;
+
+ solverCallback.init(solverInfo, constraintSolver, constraintsPtr, sortedConstraints.size(), debugDrawer/*,m_stackAlloc*/, dispatcher1);
+
+ constraintSolver.prepareSolve(getCollisionWorld().getNumCollisionObjects(), getCollisionWorld().getDispatcher().getNumManifolds());
+
+ // solve all the constraints for this island
+ islandManager.buildAndProcessIslands(getCollisionWorld().getDispatcher(), getCollisionWorld().getCollisionObjectArray(), solverCallback);
+
+ constraintSolver.allSolved(solverInfo, debugDrawer/*, m_stackAlloc*/);
+ }
+ finally {
+ BulletGlobals.popProfile();
+ }
+ }
+
+ protected void calculateSimulationIslands() {
+ BulletGlobals.pushProfile("calculateSimulationIslands");
+ try {
+ getSimulationIslandManager().updateActivationState(getCollisionWorld(), getCollisionWorld().getDispatcher());
+
+ {
+ int i;
+ int numConstraints = constraints.size();
+ for (i = 0; i < numConstraints; i++) {
+ TypedConstraint constraint = constraints.get(i);
+
+ RigidBody colObj0 = constraint.getRigidBodyA();
+ RigidBody colObj1 = constraint.getRigidBodyB();
+
+ if (((colObj0 != null) && ((colObj0).mergesSimulationIslands())) &&
+ ((colObj1 != null) && ((colObj1).mergesSimulationIslands()))) {
+ if (colObj0.isActive() || colObj1.isActive()) {
+
+ getSimulationIslandManager().getUnionFind().unite((colObj0).getIslandTag(),
+ (colObj1).getIslandTag());
+ }
+ }
+ }
+ }
+
+ // Store the island id in each body
+ getSimulationIslandManager().storeIslandActivationState(getCollisionWorld());
+ }
+ finally {
+ BulletGlobals.popProfile();
+ }
+ }
+
+ protected void integrateTransforms(float timeStep) {
+ BulletGlobals.pushProfile("integrateTransforms");
+ stack.transforms.push();
+ try {
+ Transform predictedTrans = stack.transforms.get();
+ for (int i = 0; i < collisionObjects.size(); i++) {
+ CollisionObject colObj = collisionObjects.get(i);
+ RigidBody body = RigidBody.upcast(colObj);
+ if (body != null) {
+ if (body.isActive() && (!body.isStaticOrKinematicObject())) {
+ body.predictIntegratedTransform(timeStep, predictedTrans);
+ body.proceedToTransform(predictedTrans);
+ }
+ }
+ }
+ }
+ finally {
+ stack.transforms.pop();
+ BulletGlobals.popProfile();
+ }
+ }
+
+ protected void predictUnconstraintMotion(float timeStep) {
+ BulletGlobals.pushProfile("predictUnconstraintMotion");
+ try {
+ for (int i = 0; i < collisionObjects.size(); i++) {
+ CollisionObject colObj = collisionObjects.get(i);
+ RigidBody body = RigidBody.upcast(colObj);
+ if (body != null) {
+ if (!body.isStaticOrKinematicObject()) {
+ if (body.isActive()) {
+ body.integrateVelocities(timeStep);
+ // damping
+ body.applyDamping(timeStep);
+
+ body.predictIntegratedTransform(timeStep, body.getInterpolationWorldTransform());
+ }
+ }
+ }
+ }
+ }
+ finally {
+ BulletGlobals.popProfile();
+ }
+ }
+
+ protected void startProfiling(float timeStep) {
+ // TODO
+ }
+
+ protected void debugDrawSphere(float radius, Transform transform, Vector3f color) {
+ stack.vectors.push();
+ try {
+ Vector3f start = stack.vectors.get(transform.origin);
+
+ Vector3f xoffs = stack.vectors.get(radius, 0, 0);
+ transform.basis.transform(xoffs);
+ Vector3f yoffs = stack.vectors.get(0, radius, 0);
+ transform.basis.transform(yoffs);
+ Vector3f zoffs = stack.vectors.get(0, 0, radius);
+ transform.basis.transform(zoffs);
+
+ Vector3f tmp1 = stack.vectors.get();
+ Vector3f tmp2 = stack.vectors.get();
+
+ // XY
+ tmp1.sub(start, xoffs);
+ tmp2.add(start, yoffs);
+ getDebugDrawer().drawLine(tmp1, tmp2, color);
+ tmp1.add(start, yoffs);
+ tmp2.add(start, xoffs);
+ getDebugDrawer().drawLine(tmp1, tmp2, color);
+ tmp1.add(start, xoffs);
+ tmp2.sub(start, yoffs);
+ getDebugDrawer().drawLine(tmp1, tmp2, color);
+ tmp1.sub(start, yoffs);
+ tmp2.sub(start, xoffs);
+ getDebugDrawer().drawLine(tmp1, tmp2, color);
+
+ // XZ
+ tmp1.sub(start, xoffs);
+ tmp2.add(start, zoffs);
+ getDebugDrawer().drawLine(tmp1, tmp2, color);
+ tmp1.add(start, zoffs);
+ tmp2.add(start, xoffs);
+ getDebugDrawer().drawLine(tmp1, tmp2, color);
+ tmp1.add(start, xoffs);
+ tmp2.sub(start, zoffs);
+ getDebugDrawer().drawLine(tmp1, tmp2, color);
+ tmp1.sub(start, zoffs);
+ tmp2.sub(start, xoffs);
+ getDebugDrawer().drawLine(tmp1, tmp2, color);
+
+ // YZ
+ tmp1.sub(start, yoffs);
+ tmp2.add(start, zoffs);
+ getDebugDrawer().drawLine(tmp1, tmp2, color);
+ tmp1.add(start, zoffs);
+ tmp2.add(start, yoffs);
+ getDebugDrawer().drawLine(tmp1, tmp2, color);
+ tmp1.add(start, yoffs);
+ tmp2.sub(start, zoffs);
+ getDebugDrawer().drawLine(tmp1, tmp2, color);
+ tmp1.sub(start, zoffs);
+ tmp2.sub(start, yoffs);
+ getDebugDrawer().drawLine(tmp1, tmp2, color);
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public void debugDrawObject(Transform worldTransform, CollisionShape shape, Vector3f color) {
+ stack.vectors.push();
+ try {
+ Vector3f tmp = stack.vectors.get();
+
+ // Draw a small simplex at the center of the object
+ {
+ Vector3f start = stack.vectors.get(worldTransform.origin);
+
+ tmp.set(1f, 0f, 0f);
+ worldTransform.basis.transform(tmp);
+ tmp.add(start);
+ getDebugDrawer().drawLine(start, tmp, stack.vectors.get(1f, 0f, 0f));
+
+ tmp.set(0f, 1f, 0f);
+ worldTransform.basis.transform(tmp);
+ tmp.add(start);
+ getDebugDrawer().drawLine(start, tmp, stack.vectors.get(0f, 1f, 0f));
+
+ tmp.set(0f, 0f, 1f);
+ worldTransform.basis.transform(tmp);
+ tmp.add(start);
+ getDebugDrawer().drawLine(start, tmp, stack.vectors.get(0f, 0f, 1f));
+ }
+
+ // TODO: debugDrawObject
+ // if (shape->getShapeType() == COMPOUND_SHAPE_PROXYTYPE)
+ // {
+ // const btCompoundShape* compoundShape = static_cast<const btCompoundShape*>(shape);
+ // for (int i=compoundShape->getNumChildShapes()-1;i>=0;i--)
+ // {
+ // btTransform childTrans = compoundShape->getChildTransform(i);
+ // const btCollisionShape* colShape = compoundShape->getChildShape(i);
+ // debugDrawObject(worldTransform*childTrans,colShape,color);
+ // }
+ //
+ // } else
+ // {
+ // switch (shape->getShapeType())
+ // {
+ //
+ // case SPHERE_SHAPE_PROXYTYPE:
+ // {
+ // const btSphereShape* sphereShape = static_cast<const btSphereShape*>(shape);
+ // btScalar radius = sphereShape->getMargin();//radius doesn't include the margin, so draw with margin
+ //
+ // debugDrawSphere(radius, worldTransform, color);
+ // break;
+ // }
+ // case MULTI_SPHERE_SHAPE_PROXYTYPE:
+ // {
+ // const btMultiSphereShape* multiSphereShape = static_cast<const btMultiSphereShape*>(shape);
+ //
+ // for (int i = multiSphereShape->getSphereCount()-1; i>=0;i--)
+ // {
+ // btTransform childTransform = worldTransform;
+ // childTransform.getOrigin() += multiSphereShape->getSpherePosition(i);
+ // debugDrawSphere(multiSphereShape->getSphereRadius(i), childTransform, color);
+ // }
+ //
+ // break;
+ // }
+ // case CAPSULE_SHAPE_PROXYTYPE:
+ // {
+ // const btCapsuleShape* capsuleShape = static_cast<const btCapsuleShape*>(shape);
+ //
+ // btScalar radius = capsuleShape->getRadius();
+ // btScalar halfHeight = capsuleShape->getHalfHeight();
+ //
+ // // Draw the ends
+ // {
+ // btTransform childTransform = worldTransform;
+ // childTransform.getOrigin() = worldTransform * btVector3(0,halfHeight,0);
+ // debugDrawSphere(radius, childTransform, color);
+ // }
+ //
+ // {
+ // btTransform childTransform = worldTransform;
+ // childTransform.getOrigin() = worldTransform * btVector3(0,-halfHeight,0);
+ // debugDrawSphere(radius, childTransform, color);
+ // }
+ //
+ // // Draw some additional lines
+ // btVector3 start = worldTransform.getOrigin();
+ // getDebugDrawer()->drawLine(start+worldTransform.getBasis() * btVector3(-radius,halfHeight,0),start+worldTransform.getBasis() * btVector3(-radius,-halfHeight,0), color);
+ // getDebugDrawer()->drawLine(start+worldTransform.getBasis() * btVector3(radius,halfHeight,0),start+worldTransform.getBasis() * btVector3(radius,-halfHeight,0), color);
+ // getDebugDrawer()->drawLine(start+worldTransform.getBasis() * btVector3(0,halfHeight,-radius),start+worldTransform.getBasis() * btVector3(0,-halfHeight,-radius), color);
+ // getDebugDrawer()->drawLine(start+worldTransform.getBasis() * btVector3(0,halfHeight,radius),start+worldTransform.getBasis() * btVector3(0,-halfHeight,radius), color);
+ //
+ // break;
+ // }
+ // case CONE_SHAPE_PROXYTYPE:
+ // {
+ // const btConeShape* coneShape = static_cast<const btConeShape*>(shape);
+ // btScalar radius = coneShape->getRadius();//+coneShape->getMargin();
+ // btScalar height = coneShape->getHeight();//+coneShape->getMargin();
+ // btVector3 start = worldTransform.getOrigin();
+ //
+ // int upAxis= coneShape->getConeUpIndex();
+ //
+ //
+ // btVector3 offsetHeight(0,0,0);
+ // offsetHeight[upAxis] = height * btScalar(0.5);
+ // btVector3 offsetRadius(0,0,0);
+ // offsetRadius[(upAxis+1)%3] = radius;
+ // btVector3 offset2Radius(0,0,0);
+ // offset2Radius[(upAxis+2)%3] = radius;
+ //
+ // getDebugDrawer()->drawLine(start+worldTransform.getBasis() * (offsetHeight),start+worldTransform.getBasis() * (-offsetHeight+offsetRadius),color);
+ // getDebugDrawer()->drawLine(start+worldTransform.getBasis() * (offsetHeight),start+worldTransform.getBasis() * (-offsetHeight-offsetRadius),color);
+ // getDebugDrawer()->drawLine(start+worldTransform.getBasis() * (offsetHeight),start+worldTransform.getBasis() * (-offsetHeight+offset2Radius),color);
+ // getDebugDrawer()->drawLine(start+worldTransform.getBasis() * (offsetHeight),start+worldTransform.getBasis() * (-offsetHeight-offset2Radius),color);
+ //
+ //
+ //
+ // break;
+ //
+ // }
+ // case CYLINDER_SHAPE_PROXYTYPE:
+ // {
+ // const btCylinderShape* cylinder = static_cast<const btCylinderShape*>(shape);
+ // int upAxis = cylinder->getUpAxis();
+ // btScalar radius = cylinder->getRadius();
+ // btScalar halfHeight = cylinder->getHalfExtentsWithMargin()[upAxis];
+ // btVector3 start = worldTransform.getOrigin();
+ // btVector3 offsetHeight(0,0,0);
+ // offsetHeight[upAxis] = halfHeight;
+ // btVector3 offsetRadius(0,0,0);
+ // offsetRadius[(upAxis+1)%3] = radius;
+ // getDebugDrawer()->drawLine(start+worldTransform.getBasis() * (offsetHeight+offsetRadius),start+worldTransform.getBasis() * (-offsetHeight+offsetRadius),color);
+ // getDebugDrawer()->drawLine(start+worldTransform.getBasis() * (offsetHeight-offsetRadius),start+worldTransform.getBasis() * (-offsetHeight-offsetRadius),color);
+ // break;
+ // }
+ // default:
+ // {
+ //
+ // if (shape->isConcave())
+ // {
+ // btConcaveShape* concaveMesh = (btConcaveShape*) shape;
+ //
+ // //todo pass camera, for some culling
+ // btVector3 aabbMax(btScalar(1e30),btScalar(1e30),btScalar(1e30));
+ // btVector3 aabbMin(btScalar(-1e30),btScalar(-1e30),btScalar(-1e30));
+ //
+ // DebugDrawcallback drawCallback(getDebugDrawer(),worldTransform,color);
+ // concaveMesh->processAllTriangles(&drawCallback,aabbMin,aabbMax);
+ //
+ // }
+ //
+ // if (shape->getShapeType() == CONVEX_TRIANGLEMESH_SHAPE_PROXYTYPE)
+ // {
+ // btConvexTriangleMeshShape* convexMesh = (btConvexTriangleMeshShape*) shape;
+ // //todo: pass camera for some culling
+ // btVector3 aabbMax(btScalar(1e30),btScalar(1e30),btScalar(1e30));
+ // btVector3 aabbMin(btScalar(-1e30),btScalar(-1e30),btScalar(-1e30));
+ // //DebugDrawcallback drawCallback;
+ // DebugDrawcallback drawCallback(getDebugDrawer(),worldTransform,color);
+ // convexMesh->getMeshInterface()->InternalProcessAllTriangles(&drawCallback,aabbMin,aabbMax);
+ // }
+ //
+ //
+ // /// for polyhedral shapes
+ // if (shape->isPolyhedral())
+ // {
+ // btPolyhedralConvexShape* polyshape = (btPolyhedralConvexShape*) shape;
+ //
+ // int i;
+ // for (i=0;i<polyshape->getNumEdges();i++)
+ // {
+ // btPoint3 a,b;
+ // polyshape->getEdge(i,a,b);
+ // btVector3 wa = worldTransform * a;
+ // btVector3 wb = worldTransform * b;
+ // getDebugDrawer()->drawLine(wa,wb,color);
+ //
+ // }
+ //
+ //
+ // }
+ // }
+ // }
+ // }
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ @Override
+ public void setConstraintSolver(ConstraintSolver solver) {
+ if (ownsConstraintSolver) {
+ //btAlignedFree( m_constraintSolver);
+ }
+ ownsConstraintSolver = false;
+ constraintSolver = solver;
+ }
+
+ @Override
+ public ConstraintSolver getConstraintSolver() {
+ return constraintSolver;
+ }
+
+ @Override
+ public int getNumConstraints() {
+ return constraints.size();
+ }
+
+ @Override
+ public TypedConstraint getConstraint(int index) {
+ return constraints.get(index);
+ }
+
+ public SimulationIslandManager getSimulationIslandManager() {
+ return islandManager;
+ }
+
+ public CollisionWorld getCollisionWorld() {
+ return this;
+ }
+
+ @Override
+ public DynamicsWorldType getWorldType() {
+ return DynamicsWorldType.DISCRETE_DYNAMICS_WORLD;
+ }
+
+ public ContactSolverInfo getSolverInfo() {
+ return solverInfo;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ private static final Comparator<TypedConstraint> sortConstraintOnIslandPredicate = new Comparator<TypedConstraint>() {
+ public int compare(TypedConstraint lhs, TypedConstraint rhs) {
+ int rIslandId0, lIslandId0;
+ rIslandId0 = getConstraintIslandId(rhs);
+ lIslandId0 = getConstraintIslandId(lhs);
+ return lIslandId0 < rIslandId0? -1 : +1;
+ }
+ };
+
+ private static class DebugDrawcallback implements TriangleCallback, InternalTriangleIndexCallback {
+ private IDebugDraw debugDrawer;
+ private final Vector3f color = new Vector3f();
+ private final Transform worldTrans = new Transform();
+
+ public DebugDrawcallback(IDebugDraw debugDrawer, Transform worldTrans, Vector3f color) {
+ this.debugDrawer = debugDrawer;
+ this.worldTrans.set(worldTrans);
+ this.color.set(color);
+ }
+
+ public void internalProcessTriangleIndex(Vector3f[] triangle, int partId, int triangleIndex) {
+ processTriangle(triangle,partId,triangleIndex);
+ }
+
+ private final Vector3f wv0 = new Vector3f(),wv1 = new Vector3f(),wv2 = new Vector3f();
+
+ public void processTriangle(Vector3f[] triangle, int partId, int triangleIndex) {
+ wv0.set(triangle[0]);
+ worldTrans.transform(wv0);
+ wv1.set(triangle[1]);
+ worldTrans.transform(wv1);
+ wv2.set(triangle[2]);
+ worldTrans.transform(wv2);
+
+ debugDrawer.drawLine(wv0, wv1, color);
+ debugDrawer.drawLine(wv1, wv2, color);
+ debugDrawer.drawLine(wv2, wv0, color);
+ }
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/DynamicsWorld.java b/src/jbullet/src/javabullet/dynamics/DynamicsWorld.java
new file mode 100644
index 0000000..ef9c324
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/DynamicsWorld.java
@@ -0,0 +1,104 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics;
+
+import javabullet.collision.broadphase.BroadphaseInterface;
+import javabullet.collision.broadphase.Dispatcher;
+import javabullet.collision.dispatch.CollisionConfiguration;
+import javabullet.collision.dispatch.CollisionWorld;
+import javabullet.dynamics.constraintsolver.ConstraintSolver;
+import javabullet.dynamics.constraintsolver.TypedConstraint;
+import javabullet.dynamics.vehicle.RaycastVehicle;
+import javax.vecmath.Vector3f;
+
+/**
+ * DynamicsWorld is the baseclass for several dynamics implementation, basic, discrete, parallel, and continuous.
+ *
+ * @author jezek2
+ */
+public abstract class DynamicsWorld extends CollisionWorld {
+
+ public DynamicsWorld(Dispatcher dispatcher, BroadphaseInterface broadphasePairCache, CollisionConfiguration collisionConfiguration) {
+ super(dispatcher, broadphasePairCache, collisionConfiguration);
+ }
+
+ public final int stepSimulation(float timeStep) {
+ return stepSimulation(timeStep, 1, 1f / 60f);
+ }
+
+ public final int stepSimulation(float timeStep, int maxSubSteps) {
+ return stepSimulation(timeStep, maxSubSteps, 1f / 60f);
+ }
+
+ /**
+ * stepSimulation proceeds the simulation over timeStep units.
+ * if maxSubSteps > 0, it will interpolate time steps.
+ */
+ public abstract int stepSimulation(float timeStep, int maxSubSteps, float fixedTimeStep);
+
+ public abstract void debugDrawWorld();
+
+ public final void addConstraint(TypedConstraint constraint) {
+ addConstraint(constraint, false);
+ }
+
+ public void addConstraint(TypedConstraint constraint, boolean disableCollisionsBetweenLinkedBodies) {
+ }
+
+ public void removeConstraint(TypedConstraint constraint) {
+ }
+
+ public void addVehicle(RaycastVehicle vehicle) {
+ }
+
+ public void removeVehicle(RaycastVehicle vehicle) {
+ }
+
+ /**
+ * Once a rigidbody is added to the dynamics world, it will get this gravity assigned.
+ * Existing rigidbodies in the world get gravity assigned too, during this method.
+ */
+ public abstract void setGravity(Vector3f gravity);
+
+ public abstract void addRigidBody(RigidBody body);
+
+ public abstract void removeRigidBody(RigidBody body);
+
+ public abstract void setConstraintSolver(ConstraintSolver solver);
+
+ public abstract ConstraintSolver getConstraintSolver();
+
+ public int getNumConstraints() {
+ return 0;
+ }
+
+ public TypedConstraint getConstraint(int index) {
+ return null;
+ }
+
+ public abstract DynamicsWorldType getWorldType();
+
+ public abstract void clearForces();
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/DynamicsWorldType.java b/src/jbullet/src/javabullet/dynamics/DynamicsWorldType.java
new file mode 100644
index 0000000..011977d
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/DynamicsWorldType.java
@@ -0,0 +1,34 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics;
+
+/**
+ *
+ * @author jezek2
+ */
+public enum DynamicsWorldType {
+ SIMPLE_DYNAMICS_WORLD,
+ DISCRETE_DYNAMICS_WORLD,
+ CONTINUOUS_DYNAMICS_WORLD
+}
diff --git a/src/jbullet/src/javabullet/dynamics/RigidBody.java b/src/jbullet/src/javabullet/dynamics/RigidBody.java
new file mode 100644
index 0000000..6e1a632
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/RigidBody.java
@@ -0,0 +1,639 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics;
+
+import java.util.ArrayList;
+import java.util.List;
+import javabullet.BulletGlobals;
+import javabullet.collision.broadphase.BroadphaseProxy;
+import javabullet.collision.dispatch.CollisionFlags;
+import javabullet.collision.dispatch.CollisionObject;
+import javabullet.collision.shapes.CollisionShape;
+import javabullet.dynamics.constraintsolver.TypedConstraint;
+import javabullet.linearmath.MatrixUtil;
+import javabullet.linearmath.MiscUtil;
+import javabullet.linearmath.MotionState;
+import javabullet.linearmath.Transform;
+import javabullet.linearmath.TransformUtil;
+import javax.vecmath.Matrix3f;
+import javax.vecmath.Quat4f;
+import javax.vecmath.Vector3f;
+
+/**
+ * RigidBody is the main class for rigid body objects. It is derived from btCollisionObject, so it keeps a pointer to a btCollisionShape.
+ * It is recommended for performance and memory use to share btCollisionShape objects whenever possible.<p>
+ *
+ * There are 3 types of rigid bodies:<br>
+ * - A) Dynamic rigid bodies, with positive mass. Motion is controlled by rigid body dynamics.<br>
+ * - B) Fixed objects with zero mass. They are not moving (basically collision objects)<br>
+ * - C) Kinematic objects, which are objects without mass, but the user can move them. There is on-way interaction, and Bullet calculates a velocity based on the timestep and previous and current world transform.<p>
+ *
+ * Bullet automatically deactivates dynamic rigid bodies, when the velocity is below a threshold for a given time.<p>
+ * Deactivated (sleeping) rigid bodies don't take any processing time, except a minor broadphase collision detection impact (to allow active objects to activate/wake up sleeping objects).
+ *
+ * @author jezek2
+ */
+public class RigidBody extends CollisionObject {
+
+ private static final float MAX_ANGVEL = BulletGlobals.SIMD_HALF_PI;
+
+ private final Matrix3f invInertiaTensorWorld = new Matrix3f();
+ private final Vector3f linearVelocity = new Vector3f();
+ private final Vector3f angularVelocity = new Vector3f();
+ private float inverseMass;
+ private float angularFactor;
+
+ private final Vector3f gravity = new Vector3f();
+ private final Vector3f invInertiaLocal = new Vector3f();
+ private final Vector3f totalForce = new Vector3f();
+ private final Vector3f totalTorque = new Vector3f();
+
+ private float linearDamping;
+ private float angularDamping;
+
+ private boolean additionalDamping;
+ private float additionalDampingFactor;
+ private float additionalLinearDampingThresholdSqr;
+ private float additionalAngularDampingThresholdSqr;
+ private float additionalAngularDampingFactor;
+
+ private float linearSleepingThreshold;
+ private float angularSleepingThreshold;
+
+ // optionalMotionState allows to automatic synchronize the world transform for active objects
+ private MotionState optionalMotionState;
+
+ // keep track of typed constraints referencing this rigid body
+ private final List<TypedConstraint> constraintRefs = new ArrayList<TypedConstraint>();
+
+ // for experimental overriding of friction/contact solver func
+ public int contactSolverType;
+ public int frictionSolverType;
+
+ private static int uniqueId = 0;
+ public int debugBodyId;
+
+ public RigidBody(RigidBodyConstructionInfo constructionInfo) {
+ setupRigidBody(constructionInfo);
+ }
+
+ public RigidBody(float mass, MotionState motionState, CollisionShape collisionShape) {
+ this(mass, motionState, collisionShape, BulletGlobals.ZERO_VECTOR3);
+ }
+
+ public RigidBody(float mass, MotionState motionState, CollisionShape collisionShape, Vector3f localInertia) {
+ RigidBodyConstructionInfo cinfo = new RigidBodyConstructionInfo(mass, motionState, collisionShape, localInertia);
+ setupRigidBody(cinfo);
+ }
+
+ private void setupRigidBody(RigidBodyConstructionInfo constructionInfo) {
+ linearVelocity.set(0f, 0f, 0f);
+ angularVelocity.set(0f, 0f, 0f);
+ angularFactor = 1f;
+ gravity.set(0f, 0f, 0f);
+ totalForce.set(0f, 0f, 0f);
+ totalTorque.set(0f, 0f, 0f);
+ linearDamping = 0f;
+ angularDamping = 0.5f;
+ linearSleepingThreshold = constructionInfo.linearSleepingThreshold;
+ angularSleepingThreshold = constructionInfo.angularSleepingThreshold;
+ optionalMotionState = constructionInfo.motionState;
+ contactSolverType = 0;
+ frictionSolverType = 0;
+ additionalDamping = constructionInfo.additionalDamping;
+
+ additionalLinearDampingThresholdSqr = constructionInfo.additionalLinearDampingThresholdSqr;
+ additionalAngularDampingThresholdSqr = constructionInfo.additionalAngularDampingThresholdSqr;
+ additionalAngularDampingFactor = constructionInfo.additionalAngularDampingFactor;
+
+ if (optionalMotionState != null)
+ {
+ optionalMotionState.getWorldTransform(worldTransform);
+ } else
+ {
+ worldTransform.set(constructionInfo.startWorldTransform);
+ }
+
+ interpolationWorldTransform.set(worldTransform);
+ interpolationLinearVelocity.set(0f, 0f, 0f);
+ interpolationAngularVelocity.set(0f, 0f, 0f);
+
+ // moved to CollisionObject
+ friction = constructionInfo.friction;
+ restitution = constructionInfo.restitution;
+
+ collisionShape = constructionInfo.collisionShape;
+ debugBodyId = uniqueId++;
+
+ // internalOwner is to allow upcasting from collision object to rigid body
+ internalOwner = this;
+
+ setMassProps(constructionInfo.mass, constructionInfo.localInertia);
+ setDamping(constructionInfo.linearDamping, constructionInfo.angularDamping);
+ updateInertiaTensor();
+ }
+
+ public void destroy() {
+ // No constraints should point to this rigidbody
+ // Remove constraints from the dynamics world before you delete the related rigidbodies.
+ assert (constraintRefs.size() == 0);
+ }
+
+ public void proceedToTransform(Transform newTrans) {
+ setCenterOfMassTransform(newTrans);
+ }
+
+ /**
+ * To keep collision detection and dynamics separate we don't store a rigidbody pointer,
+ * but a rigidbody is derived from CollisionObject, so we can safely perform an upcast.
+ */
+ public static RigidBody upcast(CollisionObject colObj) {
+ return (RigidBody) colObj.getInternalOwner();
+ }
+
+ /**
+ * Continuous collision detection needs prediction.
+ */
+ public void predictIntegratedTransform(float timeStep, Transform predictedTransform) {
+ TransformUtil.integrateTransform(worldTransform, linearVelocity, angularVelocity, timeStep, predictedTransform);
+ }
+
+ public void saveKinematicState(float timeStep) {
+ //todo: clamp to some (user definable) safe minimum timestep, to limit maximum angular/linear velocities
+ if (timeStep != 0f) {
+ //if we use motionstate to synchronize world transforms, get the new kinematic/animated world transform
+ if (getMotionState() != null) {
+ getMotionState().getWorldTransform(worldTransform);
+ }
+ //Vector3f linVel = new Vector3f(), angVel = new Vector3f();
+
+ TransformUtil.calculateVelocity(interpolationWorldTransform, worldTransform, timeStep, linearVelocity, angularVelocity);
+ interpolationLinearVelocity.set(linearVelocity);
+ interpolationAngularVelocity.set(angularVelocity);
+ interpolationWorldTransform.set(worldTransform);
+ //printf("angular = %f %f %f\n",m_angularVelocity.getX(),m_angularVelocity.getY(),m_angularVelocity.getZ());
+ }
+ }
+
+ public void applyGravity() {
+ if (isStaticOrKinematicObject())
+ return;
+
+ applyCentralForce(gravity);
+ }
+
+ public void setGravity(Vector3f acceleration) {
+ if (inverseMass != 0f) {
+ gravity.scale(1f / inverseMass, acceleration);
+ }
+ }
+
+ public Vector3f getGravity() {
+ return gravity;
+ }
+
+ public void setDamping(float lin_damping, float ang_damping) {
+ linearDamping = MiscUtil.GEN_clamped(lin_damping, 0f, 1f);
+ angularDamping = MiscUtil.GEN_clamped(ang_damping, 0f, 1f);
+ }
+
+ /**
+ * Damps the velocity, using the given linearDamping and angularDamping.
+ */
+ public void applyDamping(float timeStep) {
+ stack.vectors.push();
+ try {
+ linearVelocity.scale(MiscUtil.GEN_clamped((1f - timeStep * linearDamping), 0f, 1f));
+ angularVelocity.scale(MiscUtil.GEN_clamped((1f - timeStep * angularDamping), 0f, 1f));
+
+ if (additionalDamping) {
+ // Additional damping can help avoiding lowpass jitter motion, help stability for ragdolls etc.
+ // Such damping is undesirable, so once the overall simulation quality of the rigid body dynamics system has improved, this should become obsolete
+ if ((angularVelocity.lengthSquared() < additionalAngularDampingThresholdSqr) &&
+ (linearVelocity.lengthSquared() < additionalLinearDampingThresholdSqr)) {
+ angularVelocity.scale(additionalDampingFactor);
+ linearVelocity.scale(additionalDampingFactor);
+ }
+
+ float speed = linearVelocity.length();
+ if (speed < linearDamping) {
+ float dampVel = 0.005f;
+ if (speed > dampVel) {
+ Vector3f dir = stack.vectors.get(linearVelocity);
+ dir.normalize();
+ dir.scale(dampVel);
+ linearVelocity.sub(dir);
+ }
+ else {
+ linearVelocity.set(0f, 0f, 0f);
+ }
+ }
+
+ float angSpeed = angularVelocity.length();
+ if (angSpeed < angularDamping) {
+ float angDampVel = 0.005f;
+ if (angSpeed > angDampVel) {
+ Vector3f dir = stack.vectors.get(angularVelocity);
+ dir.normalize();
+ dir.scale(angDampVel);
+ angularVelocity.sub(dir);
+ }
+ else {
+ angularVelocity.set(0f, 0f, 0f);
+ }
+ }
+ }
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public void setMassProps(float mass, Vector3f inertia) {
+ if (mass == 0f) {
+ collisionFlags |= CollisionFlags.STATIC_OBJECT;
+ inverseMass = 0f;
+ }
+ else {
+ collisionFlags &= (~CollisionFlags.STATIC_OBJECT);
+ inverseMass = 1f / mass;
+ }
+
+ invInertiaLocal.set(inertia.x != 0f ? 1f / inertia.x : 0f,
+ inertia.y != 0f ? 1f / inertia.y : 0f,
+ inertia.z != 0f ? 1f / inertia.z : 0f);
+ }
+
+ public float getInvMass() {
+ return inverseMass;
+ }
+
+ public Matrix3f getInvInertiaTensorWorld() {
+ return invInertiaTensorWorld;
+ }
+
+ public void integrateVelocities(float step) {
+ if (isStaticOrKinematicObject()) {
+ return;
+ }
+
+ stack.vectors.push();
+ try {
+ linearVelocity.scaleAdd(inverseMass * step, totalForce, linearVelocity);
+ Vector3f tmp = stack.vectors.get(totalTorque);
+ invInertiaTensorWorld.transform(tmp);
+ angularVelocity.scaleAdd(step, tmp, angularVelocity);
+
+ // clamp angular velocity. collision calculations will fail on higher angular velocities
+ float angvel = angularVelocity.length();
+ if (angvel * step > MAX_ANGVEL) {
+ angularVelocity.scale((MAX_ANGVEL / step) / angvel);
+ }
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public void setCenterOfMassTransform(Transform xform) {
+ if (isStaticOrKinematicObject()) {
+ interpolationWorldTransform.set(worldTransform);
+ }
+ else {
+ interpolationWorldTransform.set(xform);
+ }
+ interpolationLinearVelocity.set(getLinearVelocity());
+ interpolationAngularVelocity.set(getAngularVelocity());
+ worldTransform.set(xform);
+ updateInertiaTensor();
+ }
+
+ public void applyCentralForce(Vector3f force) {
+ totalForce.add(force);
+ }
+
+ public Vector3f getInvInertiaDiagLocal() {
+ return invInertiaLocal;
+ }
+
+ public void setInvInertiaDiagLocal(Vector3f diagInvInertia) {
+ invInertiaLocal.set(diagInvInertia);
+ }
+
+ public void setSleepingThresholds(float linear, float angular) {
+ linearSleepingThreshold = linear;
+ angularSleepingThreshold = angular;
+ }
+
+ public void applyTorque(Vector3f torque) {
+ totalTorque.add(torque);
+ }
+
+ public void applyForce(Vector3f force, Vector3f rel_pos) {
+ stack.vectors.push();
+ try {
+ applyCentralForce(force);
+ Vector3f tmp = stack.vectors.get();
+ tmp.cross(rel_pos, force);
+ applyTorque(tmp);
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public void applyCentralImpulse(Vector3f impulse) {
+ linearVelocity.scaleAdd(inverseMass, impulse, linearVelocity);
+ }
+
+ public void applyTorqueImpulse(Vector3f torque) {
+ stack.vectors.push();
+ try {
+ Vector3f tmp = stack.vectors.get(torque);
+ invInertiaTensorWorld.transform(tmp);
+ angularVelocity.add(tmp);
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public void applyImpulse(Vector3f impulse, Vector3f rel_pos) {
+ stack.vectors.push();
+ try {
+ if (inverseMass != 0f) {
+ applyCentralImpulse(impulse);
+ if (angularFactor != 0f) {
+ Vector3f tmp = stack.vectors.get();
+ tmp.cross(rel_pos, impulse);
+ tmp.scale(angularFactor);
+ applyTorqueImpulse(tmp);
+ }
+ }
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ /**
+ * Optimization for the iterative solver: avoid calculating constant terms involving inertia, normal, relative position.
+ */
+ public void internalApplyImpulse(Vector3f linearComponent, Vector3f angularComponent, float impulseMagnitude) {
+ if (inverseMass != 0f) {
+ linearVelocity.scaleAdd(impulseMagnitude, linearComponent, linearVelocity);
+ if (angularFactor != 0f) {
+ angularVelocity.scaleAdd(impulseMagnitude * angularFactor, angularComponent, angularVelocity);
+ }
+ }
+ }
+
+ public void clearForces() {
+ totalForce.set(0f, 0f, 0f);
+ totalTorque.set(0f, 0f, 0f);
+ }
+
+ public void updateInertiaTensor() {
+ stack.matrices.push();
+ try {
+ Matrix3f mat1 = stack.matrices.get();
+ MatrixUtil.scale(mat1, worldTransform.basis, invInertiaLocal);
+
+ Matrix3f mat2 = stack.matrices.get(worldTransform.basis);
+ mat2.transpose();
+
+ invInertiaTensorWorld.mul(mat1, mat2);
+ }
+ finally {
+ stack.matrices.pop();
+ }
+ }
+
+ public Vector3f getCenterOfMassPosition() {
+ return worldTransform.origin;
+ }
+
+ public Quat4f getOrientation() {
+ stack.quats.push();
+ try {
+ Quat4f orn = stack.quats.get();
+ MatrixUtil.getRotation(worldTransform.basis, orn);
+ return stack.quats.returning(orn);
+ }
+ finally {
+ stack.quats.pop();
+ }
+ }
+
+ public Transform getCenterOfMassTransform() {
+ return worldTransform;
+ }
+
+ public Vector3f getLinearVelocity() {
+ return linearVelocity;
+ }
+
+ public Vector3f getAngularVelocity() {
+ return angularVelocity;
+ }
+
+ public void setLinearVelocity(Vector3f lin_vel) {
+ assert (collisionFlags != CollisionFlags.STATIC_OBJECT);
+ linearVelocity.set(lin_vel);
+ }
+
+ public void setAngularVelocity(Vector3f ang_vel) {
+ assert (collisionFlags != CollisionFlags.STATIC_OBJECT);
+ angularVelocity.set(ang_vel);
+ }
+
+ public Vector3f getVelocityInLocalPoint(Vector3f rel_pos) {
+ stack.vectors.push();
+ try {
+ // we also calculate lin/ang velocity for kinematic objects
+ Vector3f vec = stack.vectors.get();
+ vec.cross(angularVelocity, rel_pos);
+ vec.add(linearVelocity);
+ return stack.vectors.returning(vec);
+
+ //for kinematic objects, we could also use use:
+ // return (m_worldTransform(rel_pos) - m_interpolationWorldTransform(rel_pos)) / m_kinematicTimeStep;
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public void translate(Vector3f v) {
+ worldTransform.origin.add(v);
+ }
+
+ public void getAabb(Vector3f aabbMin, Vector3f aabbMax) {
+ getCollisionShape().getAabb(worldTransform, aabbMin, aabbMax);
+ }
+
+ public float computeImpulseDenominator(Vector3f pos, Vector3f normal) {
+ stack.vectors.push();
+ try {
+ Vector3f r0 = stack.vectors.get();
+ r0.sub(pos, getCenterOfMassPosition());
+
+ Vector3f c0 = stack.vectors.get();
+ c0.cross(r0, normal);
+
+ Vector3f tmp = stack.vectors.get();
+ MatrixUtil.transposeTransform(tmp, c0, getInvInertiaTensorWorld());
+
+ Vector3f vec = stack.vectors.get();
+ vec.cross(tmp, r0);
+
+ return inverseMass + normal.dot(vec);
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public float computeAngularImpulseDenominator(Vector3f axis) {
+ stack.vectors.push();
+ try {
+ Vector3f vec = stack.vectors.get();
+ MatrixUtil.transposeTransform(vec, axis, getInvInertiaTensorWorld());
+ return axis.dot(vec);
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public void updateDeactivation(float timeStep) {
+ if ((getActivationState() == ISLAND_SLEEPING) || (getActivationState() == DISABLE_DEACTIVATION)) {
+ return;
+ }
+
+ if ((getLinearVelocity().lengthSquared() < linearSleepingThreshold * linearSleepingThreshold) &&
+ (getAngularVelocity().lengthSquared() < angularSleepingThreshold * angularSleepingThreshold)) {
+ deactivationTime += timeStep;
+ }
+ else {
+ deactivationTime = 0f;
+ setActivationState(0);
+ }
+ }
+
+ public boolean wantsSleeping() {
+ if (getActivationState() == DISABLE_DEACTIVATION) {
+ return false;
+ }
+
+ // disable deactivation
+ if (BulletGlobals.gDisableDeactivation || (BulletGlobals.gDeactivationTime == 0f)) {
+ return false;
+ }
+
+ if ((getActivationState() == ISLAND_SLEEPING) || (getActivationState() == WANTS_DEACTIVATION)) {
+ return true;
+ }
+
+ if (deactivationTime > BulletGlobals.gDeactivationTime) {
+ return true;
+ }
+ return false;
+ }
+
+ public BroadphaseProxy getBroadphaseProxy() {
+ return broadphaseHandle;
+ }
+
+ public void setNewBroadphaseProxy(BroadphaseProxy broadphaseProxy) {
+ this.broadphaseHandle = broadphaseProxy;
+ }
+
+ public MotionState getMotionState() {
+ return optionalMotionState;
+ }
+
+ public void setMotionState(MotionState motionState) {
+ this.optionalMotionState = motionState;
+ if (optionalMotionState != null) {
+ motionState.getWorldTransform(worldTransform);
+ }
+ }
+
+ public void setAngularFactor(float angFac) {
+ angularFactor = angFac;
+ }
+
+ public float getAngularFactor() {
+ return angularFactor;
+ }
+
+ /**
+ * Is this rigidbody added to a CollisionWorld/DynamicsWorld/Broadphase?
+ */
+ public boolean isInWorld() {
+ return (getBroadphaseProxy() != null);
+ }
+
+ @Override
+ public boolean checkCollideWithOverride(CollisionObject co) {
+ // TODO: change to cast
+ RigidBody otherRb = RigidBody.upcast(co);
+ if (otherRb == null) {
+ return true;
+ }
+
+ for (int i = 0; i < constraintRefs.size(); ++i) {
+ TypedConstraint c = constraintRefs.get(i);
+ if (c.getRigidBodyA() == otherRb || c.getRigidBodyB() == otherRb) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public void addConstraintRef(TypedConstraint c) {
+ int index = constraintRefs.indexOf(c);
+ if (index == -1) {
+ constraintRefs.add(c);
+ }
+
+ checkCollideWith = true;
+ }
+
+ public void removeConstraintRef(TypedConstraint c) {
+ constraintRefs.remove(c);
+ checkCollideWith = (constraintRefs.size() > 0);
+ }
+
+ public TypedConstraint getConstraintRef(int index) {
+ return constraintRefs.get(index);
+ }
+
+ public int getNumConstraintRefs() {
+ return constraintRefs.size();
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/RigidBodyConstructionInfo.java b/src/jbullet/src/javabullet/dynamics/RigidBodyConstructionInfo.java
new file mode 100644
index 0000000..420eebc
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/RigidBodyConstructionInfo.java
@@ -0,0 +1,92 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics;
+
+import javabullet.BulletGlobals;
+import javabullet.collision.shapes.CollisionShape;
+import javabullet.linearmath.MotionState;
+import javabullet.linearmath.Transform;
+import javax.vecmath.Vector3f;
+
+/**
+ * RigidBodyConstructionInfo provides information to create a rigid body.<p>
+ *
+ * Setting mass to zero creates a fixed (non-dynamic) rigid body. For dynamic objects, you can
+ * use the collision shape to approximate the local inertia tensor, otherwise use the zero vector (default argument).<p>
+ *
+ * You can use the motion state to synchronize the world transform between physics and graphics objects.
+ * And if the motion state is provided, the rigid body will initialize its initial world transform from
+ * the motion state, startWorldTransform is only used when you don't provide a motion state.
+ *
+ * @author jezek2
+ */
+public class RigidBodyConstructionInfo {
+
+ public float mass;
+
+ /**
+ * When a motionState is provided, the rigid body will initialize its world transform
+ * from the motion state. In this case, startWorldTransform is ignored.
+ */
+ public MotionState motionState;
+ public final Transform startWorldTransform = new Transform();
+
+ public CollisionShape collisionShape;
+ public final Vector3f localInertia = new Vector3f();
+ public float linearDamping = 0f;
+ public float angularDamping = 0f;
+
+ /** Best simulation results when friction is non-zero. */
+ public float friction = 0.5f;
+ /** Best simulation results using zero restitution. */
+ public float restitution = 0f;
+
+ public float linearSleepingThreshold = 0.8f;
+ public float angularSleepingThreshold = 1.0f;
+
+ /**
+ * Additional damping can help avoiding lowpass jitter motion, help stability for ragdolls etc.
+ * Such damping is undesirable, so once the overall simulation quality of the rigid body dynamics
+ * system has improved, this should become obsolete.
+ */
+ public boolean additionalDamping = false;
+ public float additionalDampingFactor = 0.005f;
+ public float additionalLinearDampingThresholdSqr = 0.01f;
+ public float additionalAngularDampingThresholdSqr = 0.01f;
+ public float additionalAngularDampingFactor = 0.01f;
+
+ public RigidBodyConstructionInfo(float mass, MotionState motionState, CollisionShape collisionShape) {
+ this(mass, motionState, collisionShape, BulletGlobals.ZERO_VECTOR3);
+ }
+
+ public RigidBodyConstructionInfo(float mass, MotionState motionState, CollisionShape collisionShape, Vector3f localInertia) {
+ this.mass = mass;
+ this.motionState = motionState;
+ this.collisionShape = collisionShape;
+ this.localInertia.set(localInertia);
+
+ startWorldTransform.setIdentity();
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/SimpleDynamicsWorld.java b/src/jbullet/src/javabullet/dynamics/SimpleDynamicsWorld.java
new file mode 100644
index 0000000..33a4ca8
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/SimpleDynamicsWorld.java
@@ -0,0 +1,236 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics;
+
+import java.util.List;
+import javabullet.collision.broadphase.BroadphaseInterface;
+import javabullet.collision.broadphase.Dispatcher;
+import javabullet.collision.broadphase.DispatcherInfo;
+import javabullet.collision.dispatch.CollisionConfiguration;
+import javabullet.collision.dispatch.CollisionDispatcher;
+import javabullet.collision.dispatch.CollisionObject;
+import javabullet.collision.narrowphase.PersistentManifold;
+import javabullet.dynamics.constraintsolver.ConstraintSolver;
+import javabullet.dynamics.constraintsolver.ContactSolverInfo;
+import javabullet.linearmath.Transform;
+import javax.vecmath.Vector3f;
+
+/**
+ * SimpleDynamicsWorld serves as unit-test and to verify more complicated and optimized dynamics worlds.
+ * Please use DiscreteDynamicsWorld instead (or ContinuousDynamicsWorld once it is finished).
+ *
+ * @author jezek2
+ */
+public class SimpleDynamicsWorld extends DynamicsWorld {
+
+ protected ConstraintSolver constraintSolver;
+ protected boolean ownsConstraintSolver;
+ protected final Vector3f gravity = new Vector3f(0f, 0f, -10f);
+
+ public SimpleDynamicsWorld(Dispatcher dispatcher, BroadphaseInterface pairCache, ConstraintSolver constraintSolver, CollisionConfiguration collisionConfiguration) {
+ super(dispatcher, pairCache, collisionConfiguration);
+ this.constraintSolver = constraintSolver;
+ this.ownsConstraintSolver = false;
+ }
+
+ protected void predictUnconstraintMotion(float timeStep) {
+ for (int i = 0; i < collisionObjects.size(); i++) {
+ CollisionObject colObj = collisionObjects.get(i);
+ RigidBody body = RigidBody.upcast(colObj);
+ if (body != null) {
+ if (!body.isStaticObject()) {
+ if (body.isActive()) {
+ body.applyGravity();
+ body.integrateVelocities(timeStep);
+ body.applyDamping(timeStep);
+ body.predictIntegratedTransform(timeStep, body.getInterpolationWorldTransform());
+ }
+ }
+ }
+ }
+ }
+
+ protected void integrateTransforms(float timeStep) {
+ stack.transforms.push();
+ try {
+ Transform predictedTrans = stack.transforms.get();
+ for (int i = 0; i < collisionObjects.size(); i++) {
+ CollisionObject colObj = collisionObjects.get(i);
+ RigidBody body = RigidBody.upcast(colObj);
+ if (body != null) {
+ if (body.isActive() && (!body.isStaticObject())) {
+ body.predictIntegratedTransform(timeStep, predictedTrans);
+ body.proceedToTransform(predictedTrans);
+ }
+ }
+ }
+ }
+ finally {
+ stack.transforms.pop();
+ }
+ }
+
+ /**
+ * maxSubSteps/fixedTimeStep for interpolation is currently ignored for SimpleDynamicsWorld, use DiscreteDynamicsWorld instead.
+ */
+ @Override
+ public int stepSimulation(float timeStep, int maxSubSteps, float fixedTimeStep) {
+ // apply gravity, predict motion
+ predictUnconstraintMotion(timeStep);
+
+ DispatcherInfo dispatchInfo = getDispatchInfo();
+ dispatchInfo.timeStep = timeStep;
+ dispatchInfo.stepCount = 0;
+ dispatchInfo.debugDraw = getDebugDrawer();
+
+ // perform collision detection
+ performDiscreteCollisionDetection();
+
+ // solve contact constraints
+ int numManifolds = dispatcher1.getNumManifolds();
+ if (numManifolds != 0)
+ {
+ List<PersistentManifold> manifoldPtr = ((CollisionDispatcher)dispatcher1).getInternalManifoldPointer();
+
+ ContactSolverInfo infoGlobal = new ContactSolverInfo();
+ infoGlobal.timeStep = timeStep;
+ constraintSolver.prepareSolve(0,numManifolds);
+ constraintSolver.solveGroup(null,0,manifoldPtr, 0, numManifolds, null,0,0,infoGlobal,debugDrawer/*, m_stackAlloc*/,dispatcher1);
+ constraintSolver.allSolved(infoGlobal,debugDrawer/*, m_stackAlloc*/);
+ }
+
+ // integrate transforms
+ integrateTransforms(timeStep);
+
+ updateAabbs();
+
+ synchronizeMotionStates();
+
+ clearForces();
+
+ return 1;
+ }
+
+ @Override
+ public void clearForces() {
+ // todo: iterate over awake simulation islands!
+ for (int i = 0; i < collisionObjects.size(); i++) {
+ CollisionObject colObj = collisionObjects.get(i);
+
+ RigidBody body = RigidBody.upcast(colObj);
+ if (body != null) {
+ body.clearForces();
+ }
+ }
+ }
+
+ @Override
+ public void setGravity(Vector3f gravity) {
+ this.gravity.set(gravity);
+ for (int i = 0; i < collisionObjects.size(); i++) {
+ CollisionObject colObj = collisionObjects.get(i);
+ RigidBody body = RigidBody.upcast(colObj);
+ if (body != null) {
+ body.setGravity(gravity);
+ }
+ }
+ }
+
+ @Override
+ public void addRigidBody(RigidBody body) {
+ body.setGravity(gravity);
+
+ if (body.getCollisionShape() != null) {
+ addCollisionObject(body);
+ }
+ }
+
+ @Override
+ public void removeRigidBody(RigidBody body) {
+ removeCollisionObject(body);
+ }
+
+ @Override
+ public void updateAabbs() {
+ stack.pushCommonMath();
+ try {
+ Transform predictedTrans = stack.transforms.get();
+ Vector3f minAabb = stack.vectors.get(), maxAabb = stack.vectors.get();
+
+ for (int i = 0; i < collisionObjects.size(); i++) {
+ CollisionObject colObj = collisionObjects.get(i);
+ RigidBody body = RigidBody.upcast(colObj);
+ if (body != null) {
+ if (body.isActive() && (!body.isStaticObject())) {
+ colObj.getCollisionShape().getAabb(colObj.getWorldTransform(), minAabb, maxAabb);
+ BroadphaseInterface bp = getBroadphase();
+ bp.setAabb(body.getBroadphaseHandle(), minAabb, maxAabb, dispatcher1);
+ }
+ }
+ }
+ }
+ finally {
+ stack.popCommonMath();
+ }
+ }
+
+ public void synchronizeMotionStates() {
+ // todo: iterate over awake simulation islands!
+ for (int i = 0; i < collisionObjects.size(); i++) {
+ CollisionObject colObj = collisionObjects.get(i);
+ RigidBody body = RigidBody.upcast(colObj);
+ if (body != null && body.getMotionState() != null) {
+ if (body.getActivationState() != CollisionObject.ISLAND_SLEEPING) {
+ body.getMotionState().setWorldTransform(body.getWorldTransform());
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setConstraintSolver(ConstraintSolver solver) {
+ if (ownsConstraintSolver) {
+ //btAlignedFree(m_constraintSolver);
+ }
+
+ ownsConstraintSolver = false;
+ constraintSolver = solver;
+ }
+
+ @Override
+ public ConstraintSolver getConstraintSolver() {
+ return constraintSolver;
+ }
+
+ @Override
+ public void debugDrawWorld() {
+ // TODO: throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public DynamicsWorldType getWorldType() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/ConeTwistConstraint.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/ConeTwistConstraint.java
new file mode 100644
index 0000000..4bf81c1
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/ConeTwistConstraint.java
@@ -0,0 +1,419 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * btConeTwistConstraint is Copyright (c) 2007 Starbreeze Studios
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * Written by: Marcus Hennix
+ */
+
+package javabullet.dynamics.constraintsolver;
+
+import javabullet.BulletGlobals;
+import javabullet.dynamics.RigidBody;
+import javabullet.linearmath.QuaternionUtil;
+import javabullet.linearmath.ScalarUtil;
+import javabullet.linearmath.Transform;
+import javabullet.linearmath.TransformUtil;
+import javax.vecmath.Matrix3f;
+import javax.vecmath.Quat4f;
+import javax.vecmath.Vector3f;
+
+/**
+ * ConeTwistConstraint can be used to simulate ragdoll joints (upper arm, leg etc).
+ *
+ * @author jezek2
+ */
+public class ConeTwistConstraint extends TypedConstraint {
+
+ private JacobianEntry[] jac/*[3]*/ = new JacobianEntry[] { new JacobianEntry(), new JacobianEntry(), new JacobianEntry() }; //3 orthogonal linear constraints
+
+ private final Transform rbAFrame = new Transform();
+ private final Transform rbBFrame = new Transform();
+
+ private float limitSoftness;
+ private float biasFactor;
+ private float relaxationFactor;
+
+ private float swingSpan1;
+ private float swingSpan2;
+ private float twistSpan;
+
+ private final Vector3f swingAxis = new Vector3f();
+ private final Vector3f twistAxis = new Vector3f();
+
+ private float kSwing;
+ private float kTwist;
+
+ private float twistLimitSign;
+ private float swingCorrection;
+ private float twistCorrection;
+
+ private float accSwingLimitImpulse;
+ private float accTwistLimitImpulse;
+
+ private boolean angularOnly = false;
+ private boolean solveTwistLimit;
+ private boolean solveSwingLimit;
+
+ public ConeTwistConstraint() {
+ super(TypedConstraintType.CONETWIST_CONSTRAINT_TYPE);
+ }
+
+ public ConeTwistConstraint(RigidBody rbA, RigidBody rbB, Transform rbAFrame, Transform rbBFrame) {
+ super(TypedConstraintType.CONETWIST_CONSTRAINT_TYPE, rbA, rbB);
+ this.rbAFrame.set(rbAFrame);
+ this.rbBFrame.set(rbBFrame);
+
+ // flip axis for correct angles
+ this.rbBFrame.basis.m10 *= -1f;
+ this.rbBFrame.basis.m11 *= -1f;
+ this.rbBFrame.basis.m12 *= -1f;
+
+ swingSpan1 = 1e30f;
+ swingSpan2 = 1e30f;
+ twistSpan = 1e30f;
+ biasFactor = 0.3f;
+ relaxationFactor = 1.0f;
+
+ solveTwistLimit = false;
+ solveSwingLimit = false;
+ }
+
+ public ConeTwistConstraint(RigidBody rbA, Transform rbAFrame) {
+ super(TypedConstraintType.CONETWIST_CONSTRAINT_TYPE, rbA);
+ this.rbAFrame.set(rbAFrame);
+ this.rbBFrame.set(this.rbAFrame);
+
+ // flip axis for correct angles
+ this.rbBFrame.basis.m10 *= -1f;
+ this.rbBFrame.basis.m11 *= -1f;
+ this.rbBFrame.basis.m12 *= -1f;
+
+ this.rbBFrame.basis.m20 *= -1f;
+ this.rbBFrame.basis.m21 *= -1f;
+ this.rbBFrame.basis.m22 *= -1f;
+
+ swingSpan1 = 1e30f;
+ swingSpan2 = 1e30f;
+ twistSpan = 1e30f;
+ biasFactor = 0.3f;
+ relaxationFactor = 1.0f;
+
+ solveTwistLimit = false;
+ solveSwingLimit = false;
+ }
+
+ @Override
+ public void buildJacobian() {
+ stack.pushCommonMath();
+ stack.quats.push();
+ try {
+ Vector3f tmp = stack.vectors.get();
+ Vector3f tmp1 = stack.vectors.get();
+ Vector3f tmp2 = stack.vectors.get();
+
+ appliedImpulse = 0f;
+
+ // set bias, sign, clear accumulator
+ swingCorrection = 0f;
+ twistLimitSign = 0f;
+ solveTwistLimit = false;
+ solveSwingLimit = false;
+ accTwistLimitImpulse = 0f;
+ accSwingLimitImpulse = 0f;
+
+ if (!angularOnly) {
+ Vector3f pivotAInW = stack.vectors.get(rbAFrame.origin);
+ rbA.getCenterOfMassTransform().transform(pivotAInW);
+
+ Vector3f pivotBInW = stack.vectors.get(rbBFrame.origin);
+ rbB.getCenterOfMassTransform().transform(pivotBInW);
+
+ Vector3f relPos = stack.vectors.get();
+ relPos.sub(pivotBInW, pivotAInW);
+
+ // TODO: stack
+ Vector3f[] normal/*[3]*/ = new Vector3f[]{stack.vectors.get(), stack.vectors.get(), stack.vectors.get()};
+ if (relPos.lengthSquared() > BulletGlobals.FLT_EPSILON) {
+ normal[0].normalize(relPos);
+ }
+ else {
+ normal[0].set(1f, 0f, 0f);
+ }
+
+ TransformUtil.planeSpace1(normal[0], normal[1], normal[2]);
+
+ for (int i = 0; i < 3; i++) {
+ Matrix3f mat1 = stack.matrices.get(rbA.getCenterOfMassTransform().basis);
+ mat1.transpose();
+
+ Matrix3f mat2 = stack.matrices.get(rbB.getCenterOfMassTransform().basis);
+ mat2.transpose();
+
+ tmp1.sub(pivotAInW, rbA.getCenterOfMassPosition());
+ tmp2.sub(pivotBInW, rbB.getCenterOfMassPosition());
+
+ jac[i].init(
+ mat1,
+ mat2,
+ tmp1,
+ tmp2,
+ normal[i],
+ rbA.getInvInertiaDiagLocal(),
+ rbA.getInvMass(),
+ rbB.getInvInertiaDiagLocal(),
+ rbB.getInvMass());
+ }
+ }
+
+ Vector3f b1Axis1 = stack.vectors.get(), b1Axis2 = stack.vectors.get(), b1Axis3 = stack.vectors.get();
+ Vector3f b2Axis1 = stack.vectors.get(), b2Axis2 = stack.vectors.get();
+
+ rbAFrame.basis.getColumn(0, b1Axis1);
+ getRigidBodyA().getCenterOfMassTransform().basis.transform(b1Axis1);
+
+ rbBFrame.basis.getColumn(0, b2Axis1);
+ getRigidBodyB().getCenterOfMassTransform().basis.transform(b2Axis1);
+
+ float swing1 = 0f, swing2 = 0f;
+
+ // Get Frame into world space
+ if (swingSpan1 >= 0.05f) {
+ rbAFrame.basis.getColumn(1, b1Axis2);
+ getRigidBodyA().getCenterOfMassTransform().basis.transform(b1Axis2);
+ swing1 = ScalarUtil.atan2Fast(b2Axis1.dot(b1Axis2), b2Axis1.dot(b1Axis1));
+ }
+
+ if (swingSpan2 >= 0.05f) {
+ rbAFrame.basis.getColumn(2, b1Axis3);
+ getRigidBodyA().getCenterOfMassTransform().basis.transform(b1Axis3);
+ swing2 = ScalarUtil.atan2Fast(b2Axis1.dot(b1Axis3), b2Axis1.dot(b1Axis1));
+ }
+
+ float RMaxAngle1Sq = 1.0f / (swingSpan1 * swingSpan1);
+ float RMaxAngle2Sq = 1.0f / (swingSpan2 * swingSpan2);
+ float EllipseAngle = Math.abs(swing1) * RMaxAngle1Sq + Math.abs(swing2) * RMaxAngle2Sq;
+
+ if (EllipseAngle > 1.0f) {
+ swingCorrection = EllipseAngle - 1.0f;
+ solveSwingLimit = true;
+
+ // Calculate necessary axis & factors
+ tmp1.scale(b2Axis1.dot(b1Axis2), b1Axis2);
+ tmp2.scale(b2Axis1.dot(b1Axis3), b1Axis3);
+ tmp.add(tmp1, tmp2);
+ swingAxis.cross(b2Axis1, tmp);
+ swingAxis.normalize();
+
+ float swingAxisSign = (b2Axis1.dot(b1Axis1) >= 0.0f) ? 1.0f : -1.0f;
+ swingAxis.scale(swingAxisSign);
+
+ kSwing = 1f / (getRigidBodyA().computeAngularImpulseDenominator(swingAxis) +
+ getRigidBodyB().computeAngularImpulseDenominator(swingAxis));
+
+ }
+
+ // Twist limits
+ if (twistSpan >= 0f) {
+ //Vector3f b2Axis2 = stack.vectors.get();
+ rbBFrame.basis.getColumn(1, b2Axis2);
+ getRigidBodyB().getCenterOfMassTransform().basis.transform(b2Axis2);
+
+ Quat4f rotationArc = stack.quats.get(QuaternionUtil.shortestArcQuat(b2Axis1, b1Axis1));
+ Vector3f TwistRef = stack.vectors.get(QuaternionUtil.quatRotate(rotationArc, b2Axis2));
+ float twist = ScalarUtil.atan2Fast(TwistRef.dot(b1Axis3), TwistRef.dot(b1Axis2));
+
+ float lockedFreeFactor = (twistSpan > 0.05f) ? limitSoftness : 0f;
+ if (twist <= -twistSpan * lockedFreeFactor) {
+ twistCorrection = -(twist + twistSpan);
+ solveTwistLimit = true;
+
+ twistAxis.add(b2Axis1, b1Axis1);
+ twistAxis.scale(0.5f);
+ twistAxis.normalize();
+ twistAxis.scale(-1.0f);
+
+ kTwist = 1f / (getRigidBodyA().computeAngularImpulseDenominator(twistAxis) +
+ getRigidBodyB().computeAngularImpulseDenominator(twistAxis));
+
+ }
+ else if (twist > twistSpan * lockedFreeFactor) {
+ twistCorrection = (twist - twistSpan);
+ solveTwistLimit = true;
+
+ twistAxis.add(b2Axis1, b1Axis1);
+ twistAxis.scale(0.5f);
+ twistAxis.normalize();
+
+ kTwist = 1f / (getRigidBodyA().computeAngularImpulseDenominator(twistAxis) +
+ getRigidBodyB().computeAngularImpulseDenominator(twistAxis));
+ }
+ }
+ }
+ finally {
+ stack.popCommonMath();
+ stack.quats.pop();
+ }
+ }
+
+ @Override
+ public void solveConstraint(float timeStep) {
+ stack.vectors.push();
+ try {
+ Vector3f tmp = stack.vectors.get();
+ Vector3f tmp2 = stack.vectors.get();
+
+ Vector3f pivotAInW = stack.vectors.get(rbAFrame.origin);
+ rbA.getCenterOfMassTransform().transform(pivotAInW);
+
+ Vector3f pivotBInW = stack.vectors.get(rbBFrame.origin);
+ rbB.getCenterOfMassTransform().transform(pivotBInW);
+
+ float tau = 0.3f;
+
+ // linear part
+ if (!angularOnly) {
+ Vector3f rel_pos1 = stack.vectors.get();
+ rel_pos1.sub(pivotAInW, rbA.getCenterOfMassPosition());
+
+ Vector3f rel_pos2 = stack.vectors.get();
+ rel_pos2.sub(pivotBInW, rbB.getCenterOfMassPosition());
+
+ Vector3f vel1 = stack.vectors.get(rbA.getVelocityInLocalPoint(rel_pos1));
+ Vector3f vel2 = stack.vectors.get(rbB.getVelocityInLocalPoint(rel_pos2));
+ Vector3f vel = stack.vectors.get();
+ vel.sub(vel1, vel2);
+
+ for (int i = 0; i < 3; i++) {
+ Vector3f normal = jac[i].linearJointAxis;
+ float jacDiagABInv = 1f / jac[i].getDiagonal();
+
+ float rel_vel;
+ rel_vel = normal.dot(vel);
+ // positional error (zeroth order error)
+ tmp.sub(pivotAInW, pivotBInW);
+ float depth = -(tmp).dot(normal); // this is the error projected on the normal
+ float impulse = depth * tau / timeStep * jacDiagABInv - rel_vel * jacDiagABInv;
+ appliedImpulse += impulse;
+ Vector3f impulse_vector = stack.vectors.get();
+ impulse_vector.scale(impulse, normal);
+
+ tmp.sub(pivotAInW, rbA.getCenterOfMassPosition());
+ rbA.applyImpulse(impulse_vector, tmp);
+
+ tmp.negate(impulse_vector);
+ tmp2.sub(pivotBInW, rbB.getCenterOfMassPosition());
+ rbB.applyImpulse(tmp, tmp2);
+ }
+ }
+
+ {
+ // solve angular part
+ Vector3f angVelA = getRigidBodyA().getAngularVelocity();
+ Vector3f angVelB = getRigidBodyB().getAngularVelocity();
+
+ // solve swing limit
+ if (solveSwingLimit) {
+ tmp.sub(angVelB, angVelA);
+ float amplitude = ((tmp).dot(swingAxis) * relaxationFactor * relaxationFactor + swingCorrection * (1f / timeStep) * biasFactor);
+ float impulseMag = amplitude * kSwing;
+
+ // Clamp the accumulated impulse
+ float temp = accSwingLimitImpulse;
+ accSwingLimitImpulse = Math.max(accSwingLimitImpulse + impulseMag, 0.0f);
+ impulseMag = accSwingLimitImpulse - temp;
+
+ Vector3f impulse = stack.vectors.get();
+ impulse.scale(impulseMag, swingAxis);
+
+ rbA.applyTorqueImpulse(impulse);
+
+ tmp.negate(impulse);
+ rbB.applyTorqueImpulse(tmp);
+ }
+
+ // solve twist limit
+ if (solveTwistLimit) {
+ tmp.sub(angVelB, angVelA);
+ float amplitude = ((tmp).dot(twistAxis) * relaxationFactor * relaxationFactor + twistCorrection * (1f / timeStep) * biasFactor);
+ float impulseMag = amplitude * kTwist;
+
+ // Clamp the accumulated impulse
+ float temp = accTwistLimitImpulse;
+ accTwistLimitImpulse = Math.max(accTwistLimitImpulse + impulseMag, 0.0f);
+ impulseMag = accTwistLimitImpulse - temp;
+
+ Vector3f impulse = stack.vectors.get();
+ impulse.scale(impulseMag, twistAxis);
+
+ rbA.applyTorqueImpulse(impulse);
+
+ tmp.negate(impulse);
+ rbB.applyTorqueImpulse(tmp);
+ }
+ }
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public void updateRHS(float timeStep) {
+ }
+
+ public void setAngularOnly(boolean angularOnly) {
+ this.angularOnly = angularOnly;
+ }
+
+ public void setLimit(float _swingSpan1, float _swingSpan2, float _twistSpan) {
+ setLimit(_swingSpan1, _swingSpan2, _twistSpan, 0.8f, 0.3f, 1.0f);
+ }
+
+ public void setLimit(float _swingSpan1, float _swingSpan2, float _twistSpan, float _softness, float _biasFactor, float _relaxationFactor) {
+ swingSpan1 = _swingSpan1;
+ swingSpan2 = _swingSpan2;
+ twistSpan = _twistSpan;
+
+ limitSoftness = _softness;
+ biasFactor = _biasFactor;
+ relaxationFactor = _relaxationFactor;
+ }
+
+ public Transform getAFrame() {
+ return rbAFrame;
+ }
+
+ public Transform getBFrame() {
+ return rbBFrame;
+ }
+
+ public boolean getSolveTwistLimit() {
+ return solveTwistLimit;
+ }
+
+ public boolean getSolveSwingLimit() {
+ return solveTwistLimit;
+ }
+
+ public float getTwistLimitSign() {
+ return twistLimitSign;
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/ConstraintPersistentData.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/ConstraintPersistentData.java
new file mode 100644
index 0000000..b8e7220
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/ConstraintPersistentData.java
@@ -0,0 +1,90 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.constraintsolver;
+
+import javax.vecmath.Vector3f;
+
+/**
+ *
+ * @author jezek2
+ */
+public class ConstraintPersistentData {
+
+ /** total applied impulse during most recent frame */
+ public float appliedImpulse = 0f;
+ public float prevAppliedImpulse = 0f;
+ public float accumulatedTangentImpulse0 = 0f;
+ public float accumulatedTangentImpulse1 = 0f;
+
+ public float jacDiagABInv = 0f;
+ public float jacDiagABInvTangent0;
+ public float jacDiagABInvTangent1;
+ public int persistentLifeTime = 0;
+ public float restitution = 0f;
+ public float friction = 0f;
+ public float penetration = 0f;
+ public final Vector3f frictionWorldTangential0 = new Vector3f();
+ public final Vector3f frictionWorldTangential1 = new Vector3f();
+
+ public final Vector3f frictionAngularComponent0A = new Vector3f();
+ public final Vector3f frictionAngularComponent0B = new Vector3f();
+ public final Vector3f frictionAngularComponent1A = new Vector3f();
+ public final Vector3f frictionAngularComponent1B = new Vector3f();
+
+ //some data doesn't need to be persistent over frames: todo: clean/reuse this
+ public final Vector3f angularComponentA = new Vector3f();
+ public final Vector3f angularComponentB = new Vector3f();
+
+ public ContactSolverFunc contactSolverFunc = null;
+ public ContactSolverFunc frictionSolverFunc = null;
+
+ public void reset() {
+ appliedImpulse = 0f;
+ prevAppliedImpulse = 0f;
+ accumulatedTangentImpulse0 = 0f;
+ accumulatedTangentImpulse1 = 0f;
+
+ jacDiagABInv = 0f;
+ jacDiagABInvTangent0 = 0f;
+ jacDiagABInvTangent1 = 0f;
+ persistentLifeTime = 0;
+ restitution = 0f;
+ friction = 0f;
+ penetration = 0f;
+ frictionWorldTangential0.set(0f, 0f, 0f);
+ frictionWorldTangential1.set(0f, 0f, 0f);
+
+ frictionAngularComponent0A.set(0f, 0f, 0f);
+ frictionAngularComponent0B.set(0f, 0f, 0f);
+ frictionAngularComponent1A.set(0f, 0f, 0f);
+ frictionAngularComponent1B.set(0f, 0f, 0f);
+
+ angularComponentA.set(0f, 0f, 0f);
+ angularComponentB.set(0f, 0f, 0f);
+
+ contactSolverFunc = null;
+ frictionSolverFunc = null;
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/ConstraintSolver.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/ConstraintSolver.java
new file mode 100644
index 0000000..84062c3
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/ConstraintSolver.java
@@ -0,0 +1,55 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.constraintsolver;
+
+import java.util.List;
+import javabullet.BulletStack;
+import javabullet.collision.broadphase.Dispatcher;
+import javabullet.collision.dispatch.CollisionObject;
+import javabullet.collision.narrowphase.PersistentManifold;
+import javabullet.linearmath.IDebugDraw;
+
+/**
+ *
+ * @author jezek2
+ */
+public abstract class ConstraintSolver {
+
+ protected final BulletStack stack = BulletStack.get();
+
+ public void prepareSolve (int numBodies, int numManifolds) {}
+
+ /**
+ * Solve a group of constraints.
+ */
+ public abstract float solveGroup(List<CollisionObject> bodies, int numBodies, List<PersistentManifold> manifold, int manifold_offset, int numManifolds, List<TypedConstraint> constraints, int constraints_offset, int numConstraints, ContactSolverInfo info, IDebugDraw debugDrawer/*, btStackAlloc* stackAlloc*/, Dispatcher dispatcher);
+
+ public void allSolved(ContactSolverInfo info, IDebugDraw debugDrawer/*, btStackAlloc* stackAlloc*/) {}
+
+ /**
+ * Clear internal cached data and reset random seed.
+ */
+ public abstract void reset();
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/ContactConstraint.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/ContactConstraint.java
new file mode 100644
index 0000000..ea07897
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/ContactConstraint.java
@@ -0,0 +1,460 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.constraintsolver;
+
+import javabullet.BulletGlobals;
+import javabullet.BulletPool;
+import javabullet.BulletStack;
+import javabullet.ObjectPool;
+import javabullet.collision.narrowphase.ManifoldPoint;
+import javabullet.dynamics.RigidBody;
+import javax.vecmath.Matrix3f;
+import javax.vecmath.Vector3f;
+
+/**
+ *
+ * @author jezek2
+ */
+public class ContactConstraint {
+
+ public static final ContactSolverFunc resolveSingleCollision = new ContactSolverFunc() {
+ public float invoke(RigidBody body1, RigidBody body2, ManifoldPoint contactPoint, ContactSolverInfo info) {
+ return resolveSingleCollision(body1, body2, contactPoint, info);
+ }
+ };
+
+ public static final ContactSolverFunc resolveSingleFriction = new ContactSolverFunc() {
+ public float invoke(RigidBody body1, RigidBody body2, ManifoldPoint contactPoint, ContactSolverInfo info) {
+ return resolveSingleFriction(body1, body2, contactPoint, info);
+ }
+ };
+
+ public static final ContactSolverFunc resolveSingleCollisionCombined = new ContactSolverFunc() {
+ public float invoke(RigidBody body1, RigidBody body2, ManifoldPoint contactPoint, ContactSolverInfo info) {
+ return resolveSingleCollisionCombined(body1, body2, contactPoint, info);
+ }
+ };
+
+ /**
+ * Bilateral constraint between two dynamic objects.
+ */
+ public static void resolveSingleBilateral(RigidBody body1, Vector3f pos1,
+ RigidBody body2, Vector3f pos2,
+ float distance, Vector3f normal, float[] impulse, float timeStep) {
+ float normalLenSqr = normal.lengthSquared();
+ assert (Math.abs(normalLenSqr) < 1.1f);
+ if (normalLenSqr > 1.1f) {
+ impulse[0] = 0f;
+ return;
+ }
+
+ BulletStack stack = BulletStack.get();
+ ObjectPool<JacobianEntry> jacobiansPool = BulletPool.get(JacobianEntry.class);
+
+ stack.pushCommonMath();
+ try {
+ Vector3f rel_pos1 = stack.vectors.get();
+ rel_pos1.sub(pos1, body1.getCenterOfMassPosition());
+
+ Vector3f rel_pos2 = stack.vectors.get();
+ rel_pos2.sub(pos2, body2.getCenterOfMassPosition());
+
+ //this jacobian entry could be re-used for all iterations
+
+ Vector3f vel1 = stack.vectors.get();
+ vel1.set(body1.getVelocityInLocalPoint(rel_pos1));
+
+ Vector3f vel2 = stack.vectors.get();
+ vel2.set(body2.getVelocityInLocalPoint(rel_pos2));
+
+ Vector3f vel = stack.vectors.get();
+ vel.sub(vel1, vel2);
+
+ Matrix3f mat1 = stack.matrices.get(body1.getCenterOfMassTransform().basis);
+ mat1.transpose();
+
+ Matrix3f mat2 = stack.matrices.get(body2.getCenterOfMassTransform().basis);
+ mat2.transpose();
+
+ JacobianEntry jac = jacobiansPool.get();
+ jac.init(mat1, mat2,
+ rel_pos1, rel_pos2, normal, body1.getInvInertiaDiagLocal(), body1.getInvMass(),
+ body2.getInvInertiaDiagLocal(), body2.getInvMass());
+
+ float jacDiagAB = jac.getDiagonal();
+ float jacDiagABInv = 1f / jacDiagAB;
+
+ Vector3f tmp1 = stack.vectors.get(body1.getAngularVelocity());
+ mat1.transform(tmp1);
+
+ Vector3f tmp2 = stack.vectors.get(body2.getAngularVelocity());
+ mat2.transform(tmp2);
+
+ float rel_vel = jac.getRelativeVelocity(
+ body1.getLinearVelocity(),
+ tmp1,
+ body2.getLinearVelocity(),
+ tmp2);
+
+ jacobiansPool.release(jac);
+
+ float a;
+ a = jacDiagABInv;
+
+
+ rel_vel = normal.dot(vel);
+
+ // todo: move this into proper structure
+ float contactDamping = 0.2f;
+
+ //#ifdef ONLY_USE_LINEAR_MASS
+ // btScalar massTerm = btScalar(1.) / (body1.getInvMass() + body2.getInvMass());
+ // impulse = - contactDamping * rel_vel * massTerm;
+ //#else
+ float velocityImpulse = -contactDamping * rel_vel * jacDiagABInv;
+ impulse[0] = velocityImpulse;
+ //#endif
+ }
+ finally {
+ stack.popCommonMath();
+ }
+ }
+
+ /**
+ * Response between two dynamic objects with friction.
+ */
+ public static float resolveSingleCollision(
+ RigidBody body1,
+ RigidBody body2,
+ ManifoldPoint contactPoint,
+ ContactSolverInfo solverInfo) {
+
+ BulletStack stack = BulletStack.get();
+
+ stack.vectors.push();
+ try {
+ Vector3f pos1_ = contactPoint.getPositionWorldOnA();
+ Vector3f pos2_ = contactPoint.getPositionWorldOnB();
+ Vector3f normal = contactPoint.normalWorldOnB;
+
+ // constant over all iterations
+ Vector3f rel_pos1 = stack.vectors.get();
+ rel_pos1.sub(pos1_, body1.getCenterOfMassPosition());
+
+ Vector3f rel_pos2 = stack.vectors.get();
+ rel_pos2.sub(pos2_, body2.getCenterOfMassPosition());
+
+ Vector3f vel1 = stack.vectors.get(body1.getVelocityInLocalPoint(rel_pos1));
+ Vector3f vel2 = stack.vectors.get(body2.getVelocityInLocalPoint(rel_pos2));
+ Vector3f vel = stack.vectors.get();
+ vel.sub(vel1, vel2);
+
+ float rel_vel;
+ rel_vel = normal.dot(vel);
+
+ float Kfps = 1f / solverInfo.timeStep;
+
+ // btScalar damping = solverInfo.m_damping ;
+ float Kerp = solverInfo.erp;
+ float Kcor = Kerp * Kfps;
+
+ ConstraintPersistentData cpd = (ConstraintPersistentData) contactPoint.userPersistentData;
+ assert (cpd != null);
+ float distance = cpd.penetration;
+ float positionalError = Kcor * -distance;
+ float velocityError = cpd.restitution - rel_vel; // * damping;
+
+ float penetrationImpulse = positionalError * cpd.jacDiagABInv;
+
+ float velocityImpulse = velocityError * cpd.jacDiagABInv;
+
+ float normalImpulse = penetrationImpulse + velocityImpulse;
+
+ // See Erin Catto's GDC 2006 paper: Clamp the accumulated impulse
+ float oldNormalImpulse = cpd.appliedImpulse;
+ float sum = oldNormalImpulse + normalImpulse;
+ cpd.appliedImpulse = 0f > sum ? 0f : sum;
+
+ normalImpulse = cpd.appliedImpulse - oldNormalImpulse;
+
+ //#ifdef USE_INTERNAL_APPLY_IMPULSE
+ Vector3f tmp = stack.vectors.get();
+ if (body1.getInvMass() != 0f) {
+ tmp.scale(body1.getInvMass(), contactPoint.normalWorldOnB);
+ body1.internalApplyImpulse(tmp, cpd.angularComponentA, normalImpulse);
+ }
+ if (body2.getInvMass() != 0f) {
+ tmp.scale(body2.getInvMass(), contactPoint.normalWorldOnB);
+ body2.internalApplyImpulse(tmp, cpd.angularComponentB, -normalImpulse);
+ }
+ //#else //USE_INTERNAL_APPLY_IMPULSE
+ // body1.applyImpulse(normal*(normalImpulse), rel_pos1);
+ // body2.applyImpulse(-normal*(normalImpulse), rel_pos2);
+ //#endif //USE_INTERNAL_APPLY_IMPULSE
+
+ return normalImpulse;
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public static float resolveSingleFriction(
+ RigidBody body1,
+ RigidBody body2,
+ ManifoldPoint contactPoint,
+ ContactSolverInfo solverInfo) {
+
+ BulletStack stack = BulletStack.get();
+
+ stack.vectors.push();
+ try {
+ Vector3f pos1 = contactPoint.getPositionWorldOnA();
+ Vector3f pos2 = contactPoint.getPositionWorldOnB();
+
+ Vector3f rel_pos1 = stack.vectors.get();
+ rel_pos1.sub(pos1, body1.getCenterOfMassPosition());
+
+ Vector3f rel_pos2 = stack.vectors.get();
+ rel_pos2.sub(pos2, body2.getCenterOfMassPosition());
+
+ ConstraintPersistentData cpd = (ConstraintPersistentData) contactPoint.userPersistentData;
+ assert (cpd != null);
+
+ float combinedFriction = cpd.friction;
+
+ float limit = cpd.appliedImpulse * combinedFriction;
+
+ if (cpd.appliedImpulse > 0f) //friction
+ {
+ //apply friction in the 2 tangential directions
+
+ // 1st tangent
+ Vector3f vel1 = stack.vectors.get();
+ vel1.set(body1.getVelocityInLocalPoint(rel_pos1));
+
+ Vector3f vel2 = stack.vectors.get();
+ vel2.set(body2.getVelocityInLocalPoint(rel_pos2));
+
+ Vector3f vel = stack.vectors.get();
+ vel.sub(vel1, vel2);
+
+ float j1, j2;
+
+ {
+ float vrel = cpd.frictionWorldTangential0.dot(vel);
+
+ // calculate j that moves us to zero relative velocity
+ j1 = -vrel * cpd.jacDiagABInvTangent0;
+ float oldTangentImpulse = cpd.accumulatedTangentImpulse0;
+ cpd.accumulatedTangentImpulse0 = oldTangentImpulse + j1;
+
+ cpd.accumulatedTangentImpulse0 = Math.min(cpd.accumulatedTangentImpulse0, limit);
+ cpd.accumulatedTangentImpulse0 = Math.max(cpd.accumulatedTangentImpulse0, -limit);
+ j1 = cpd.accumulatedTangentImpulse0 - oldTangentImpulse;
+ }
+ {
+ // 2nd tangent
+
+ float vrel = cpd.frictionWorldTangential1.dot(vel);
+
+ // calculate j that moves us to zero relative velocity
+ j2 = -vrel * cpd.jacDiagABInvTangent1;
+ float oldTangentImpulse = cpd.accumulatedTangentImpulse1;
+ cpd.accumulatedTangentImpulse1 = oldTangentImpulse + j2;
+
+ cpd.accumulatedTangentImpulse1 = Math.min(cpd.accumulatedTangentImpulse1, limit);
+ cpd.accumulatedTangentImpulse1 = Math.max(cpd.accumulatedTangentImpulse1, -limit);
+ j2 = cpd.accumulatedTangentImpulse1 - oldTangentImpulse;
+ }
+
+ //#ifdef USE_INTERNAL_APPLY_IMPULSE
+ Vector3f tmp = stack.vectors.get();
+
+ if (body1.getInvMass() != 0f) {
+ tmp.scale(body1.getInvMass(), cpd.frictionWorldTangential0);
+ body1.internalApplyImpulse(tmp, cpd.frictionAngularComponent0A, j1);
+
+ tmp.scale(body1.getInvMass(), cpd.frictionWorldTangential1);
+ body1.internalApplyImpulse(tmp, cpd.frictionAngularComponent1A, j2);
+ }
+ if (body2.getInvMass() != 0f) {
+ tmp.scale(body2.getInvMass(), cpd.frictionWorldTangential0);
+ body2.internalApplyImpulse(tmp, cpd.frictionAngularComponent0B, -j1);
+
+ tmp.scale(body2.getInvMass(), cpd.frictionWorldTangential1);
+ body2.internalApplyImpulse(tmp, cpd.frictionAngularComponent1B, -j2);
+ }
+ //#else //USE_INTERNAL_APPLY_IMPULSE
+ // body1.applyImpulse((j1 * cpd->m_frictionWorldTangential0)+(j2 * cpd->m_frictionWorldTangential1), rel_pos1);
+ // body2.applyImpulse((j1 * -cpd->m_frictionWorldTangential0)+(j2 * -cpd->m_frictionWorldTangential1), rel_pos2);
+ //#endif //USE_INTERNAL_APPLY_IMPULSE
+ }
+ return cpd.appliedImpulse;
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ /**
+ * velocity + friction<br>
+ * response between two dynamic objects with friction
+ */
+ public static float resolveSingleCollisionCombined(
+ RigidBody body1,
+ RigidBody body2,
+ ManifoldPoint contactPoint,
+ ContactSolverInfo solverInfo) {
+
+ BulletStack stack = BulletStack.get();
+
+ stack.vectors.push();
+ try {
+ Vector3f pos1 = contactPoint.getPositionWorldOnA();
+ Vector3f pos2 = contactPoint.getPositionWorldOnB();
+ Vector3f normal = contactPoint.normalWorldOnB;
+
+ Vector3f rel_pos1 = stack.vectors.get();
+ rel_pos1.sub(pos1, body1.getCenterOfMassPosition());
+
+ Vector3f rel_pos2 = stack.vectors.get();
+ rel_pos2.sub(pos2, body2.getCenterOfMassPosition());
+
+ Vector3f vel1 = stack.vectors.get(body1.getVelocityInLocalPoint(rel_pos1));
+ Vector3f vel2 = stack.vectors.get(body2.getVelocityInLocalPoint(rel_pos2));
+ Vector3f vel = stack.vectors.get();
+ vel.sub(vel1, vel2);
+
+ float rel_vel;
+ rel_vel = normal.dot(vel);
+
+ float Kfps = 1f / solverInfo.timeStep;
+
+ //btScalar damping = solverInfo.m_damping ;
+ float Kerp = solverInfo.erp;
+ float Kcor = Kerp * Kfps;
+
+ ConstraintPersistentData cpd = (ConstraintPersistentData) contactPoint.userPersistentData;
+ assert (cpd != null);
+ float distance = cpd.penetration;
+ float positionalError = Kcor * -distance;
+ float velocityError = cpd.restitution - rel_vel;// * damping;
+
+ float penetrationImpulse = positionalError * cpd.jacDiagABInv;
+
+ float velocityImpulse = velocityError * cpd.jacDiagABInv;
+
+ float normalImpulse = penetrationImpulse + velocityImpulse;
+
+ // See Erin Catto's GDC 2006 paper: Clamp the accumulated impulse
+ float oldNormalImpulse = cpd.appliedImpulse;
+ float sum = oldNormalImpulse + normalImpulse;
+ cpd.appliedImpulse = 0f > sum ? 0f : sum;
+
+ normalImpulse = cpd.appliedImpulse - oldNormalImpulse;
+
+
+ //#ifdef USE_INTERNAL_APPLY_IMPULSE
+ Vector3f tmp = stack.vectors.get();
+ if (body1.getInvMass() != 0f) {
+ tmp.scale(body1.getInvMass(), contactPoint.normalWorldOnB);
+ body1.internalApplyImpulse(tmp, cpd.angularComponentA, normalImpulse);
+ }
+ if (body2.getInvMass() != 0f) {
+ tmp.scale(body2.getInvMass(), contactPoint.normalWorldOnB);
+ body2.internalApplyImpulse(tmp, cpd.angularComponentB, -normalImpulse);
+ }
+ //#else //USE_INTERNAL_APPLY_IMPULSE
+ // body1.applyImpulse(normal*(normalImpulse), rel_pos1);
+ // body2.applyImpulse(-normal*(normalImpulse), rel_pos2);
+ //#endif //USE_INTERNAL_APPLY_IMPULSE
+
+ {
+ //friction
+ vel1.set(body1.getVelocityInLocalPoint(rel_pos1));
+ vel2.set(body2.getVelocityInLocalPoint(rel_pos2));
+ vel.sub(vel1, vel2);
+
+ rel_vel = normal.dot(vel);
+
+ tmp.scale(rel_vel, normal);
+ Vector3f lat_vel = stack.vectors.get();
+ lat_vel.sub(vel, tmp);
+ float lat_rel_vel = lat_vel.length();
+
+ float combinedFriction = cpd.friction;
+
+ if (cpd.appliedImpulse > 0) {
+ if (lat_rel_vel > BulletGlobals.FLT_EPSILON) {
+ lat_vel.scale(1f / lat_rel_vel);
+
+ Vector3f temp1 = stack.vectors.get();
+ temp1.cross(rel_pos1, lat_vel);
+ body1.getInvInertiaTensorWorld().transform(temp1);
+
+ Vector3f temp2 = stack.vectors.get();
+ temp2.cross(rel_pos2, lat_vel);
+ body2.getInvInertiaTensorWorld().transform(temp2);
+
+ Vector3f java_tmp1 = stack.vectors.get();
+ java_tmp1.cross(temp1, rel_pos1);
+
+ Vector3f java_tmp2 = stack.vectors.get();
+ java_tmp2.cross(temp2, rel_pos2);
+
+ tmp.add(java_tmp1, java_tmp2);
+
+ float friction_impulse = lat_rel_vel /
+ (body1.getInvMass() + body2.getInvMass() + lat_vel.dot(tmp));
+ float normal_impulse = cpd.appliedImpulse * combinedFriction;
+
+ friction_impulse = Math.min(friction_impulse, normal_impulse);
+ friction_impulse = Math.max(friction_impulse, -normal_impulse);
+
+ tmp.scale(-friction_impulse, lat_vel);
+ body1.applyImpulse(tmp, rel_pos1);
+
+ tmp.scale(friction_impulse, lat_vel);
+ body2.applyImpulse(tmp, rel_pos2);
+ }
+ }
+ }
+
+ return normalImpulse;
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public static float resolveSingleFrictionEmpty(
+ RigidBody body1,
+ RigidBody body2,
+ ManifoldPoint contactPoint,
+ ContactSolverInfo solverInfo) {
+ return 0f;
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/ContactConstraintEnum.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/ContactConstraintEnum.java
new file mode 100644
index 0000000..3382637
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/ContactConstraintEnum.java
@@ -0,0 +1,37 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.constraintsolver;
+
+/**
+ * TODO: name
+ *
+ * @author jezek2
+ */
+public enum ContactConstraintEnum {
+ DEFAULT_CONTACT_SOLVER_TYPE,
+ CONTACT_SOLVER_TYPE1,
+ CONTACT_SOLVER_TYPE2,
+ USER_CONTACT_SOLVER_TYPE1,
+ MAX_CONTACT_SOLVER_TYPES
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/ContactSolverFunc.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/ContactSolverFunc.java
new file mode 100644
index 0000000..97799e1
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/ContactSolverFunc.java
@@ -0,0 +1,37 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.constraintsolver;
+
+import javabullet.collision.narrowphase.ManifoldPoint;
+import javabullet.dynamics.RigidBody;
+
+/**
+ *
+ * @author jezek2
+ */
+public interface ContactSolverFunc {
+
+ public float invoke(RigidBody body1, RigidBody body2, ManifoldPoint contactPoint, ContactSolverInfo info);
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/ContactSolverInfo.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/ContactSolverInfo.java
new file mode 100644
index 0000000..7915516
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/ContactSolverInfo.java
@@ -0,0 +1,57 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.constraintsolver;
+
+/**
+ *
+ * @author jezek2
+ */
+public class ContactSolverInfo {
+
+ public float tau = 0.6f;
+ public float damping = 1f;
+ public float friction = 0.3f;
+ public float timeStep;
+ public float restitution = 0f;
+ public int numIterations = 10;
+ public float maxErrorReduction = 20f;
+ public float sor = 1.3f;
+ public float erp = 0.4f;
+
+ public ContactSolverInfo() {
+ }
+
+ public ContactSolverInfo(ContactSolverInfo g) {
+ tau = g.tau;
+ damping = g.damping;
+ friction = g.friction;
+ timeStep = g.timeStep;
+ restitution = g.restitution;
+ numIterations = g.numIterations;
+ maxErrorReduction = g.maxErrorReduction;
+ sor = g.sor;
+ erp = g.erp;
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/Generic6DofConstraint.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/Generic6DofConstraint.java
new file mode 100644
index 0000000..46e9b93
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/Generic6DofConstraint.java
@@ -0,0 +1,505 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+/*
+2007-09-09
+btGeneric6DofConstraint Refactored by Francisco Le�n
+http://gimpact.sf.net
+*/
+
+package javabullet.dynamics.constraintsolver;
+
+import javabullet.BulletGlobals;
+import javabullet.dynamics.RigidBody;
+import javabullet.linearmath.MatrixUtil;
+import javabullet.linearmath.Transform;
+
+
+///
+import javabullet.linearmath.VectorUtil;
+import javax.vecmath.Matrix3f;
+import javax.vecmath.Vector3f;
+/*!
+
+*/
+/**
+ * Generic6DofConstraint between two rigidbodies each with a pivotpoint that descibes the axis location in local space.<p>
+ * Generic6DofConstraint can leave any of the 6 degree of freedom 'free' or 'locked'.
+ * Currently this limit supports rotational motors.<br>
+ * <ul>
+ * <li>For Linear limits, use Generic6DofConstraint.setLinearUpperLimit, Generic6DofConstraint.setLinearLowerLimit. You can set the parameters with the TranslationalLimitMotor structure accsesible through the Generic6DofConstraint.getTranslationalLimitMotor method.
+ * At this moment translational motors are not supported. May be in the future.</li>
+ *
+ * <li>For Angular limits, use the RotationalLimitMotor structure for configuring the limit.
+ * This is accessible through Generic6DofConstraint.getLimitMotor method,
+ * This brings support for limit parameters and motors.</li>
+ *
+ * <li>Angulars limits have these possible ranges:
+ * <table border=1 >
+ * <tr>
+ * <td><b>AXIS</b></td>
+ * <td><b>MIN ANGLE</b></td>
+ * <td><b>MAX ANGLE</b></td>
+ * </tr><tr>
+ * <td>X</td>
+ * <td>-PI</td>
+ * <td>PI</td>
+ * </tr><tr>
+ * <td>Y</td>
+ * <td>-PI/2</td>
+ * <td>PI/2</td>
+ * </tr><tr>
+ * <td>Z</td>
+ * <td>-PI/2</td>
+ * <td>PI/2</td>
+ * </tr>
+ * </table>
+ * </li>
+ * </ul>
+ *
+ * @author jezek2
+ */
+public class Generic6DofConstraint extends TypedConstraint {
+
+ protected final Transform frameInA = new Transform(); //!< the constraint space w.r.t body A
+ protected final Transform frameInB = new Transform(); //!< the constraint space w.r.t body B
+
+ protected final JacobianEntry[] jacLinear/*[3]*/ = new JacobianEntry[] { new JacobianEntry(), new JacobianEntry(), new JacobianEntry() }; //!< 3 orthogonal linear constraints
+ protected final JacobianEntry[] jacAng/*[3]*/ = new JacobianEntry[] { new JacobianEntry(), new JacobianEntry(), new JacobianEntry() }; //!< 3 orthogonal angular constraints
+
+ protected final TranslationalLimitMotor linearLimits = new TranslationalLimitMotor();
+
+ protected final RotationalLimitMotor[] angularLimits/*[3]*/ = new RotationalLimitMotor[] { new RotationalLimitMotor(), new RotationalLimitMotor(), new RotationalLimitMotor() };
+
+ protected float timeStep;
+ protected final Transform calculatedTransformA = new Transform();
+ protected final Transform calculatedTransformB = new Transform();
+ protected final Vector3f calculatedAxisAngleDiff = new Vector3f();
+ protected final Vector3f[] calculatedAxis/*[3]*/ = new Vector3f[] { new Vector3f(), new Vector3f(), new Vector3f() };
+
+ protected boolean useLinearReferenceFrameA;
+
+ public Generic6DofConstraint() {
+ super(TypedConstraintType.D6_CONSTRAINT_TYPE);
+ useLinearReferenceFrameA = true;
+ }
+
+ public Generic6DofConstraint(RigidBody rbA, RigidBody rbB, Transform frameInA, Transform frameInB, boolean useLinearReferenceFrameA) {
+ super(TypedConstraintType.D6_CONSTRAINT_TYPE, rbA, rbB);
+ this.frameInA.set(frameInA);
+ this.frameInB.set(frameInB);
+ this.useLinearReferenceFrameA = useLinearReferenceFrameA;
+ }
+
+ private static float getMatrixElem(Matrix3f mat, int index) {
+ int i = index % 3;
+ int j = index / 3;
+ return mat.getElement(i, j);
+ }
+
+ /**
+ * MatrixToEulerXYZ from http://www.geometrictools.com/LibFoundation/Mathematics/Wm4Matrix3.inl.html
+ */
+ private static boolean matrixToEulerXYZ(Matrix3f mat, Vector3f xyz) {
+ // // rot = cy*cz -cy*sz sy
+ // // cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx
+ // // -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy
+ //
+
+ if (getMatrixElem(mat, 2) < 1.0f) {
+ if (getMatrixElem(mat, 2) > -1.0f) {
+ xyz.x = (float) Math.atan2(-getMatrixElem(mat, 5), getMatrixElem(mat, 8));
+ xyz.y = (float) Math.asin(getMatrixElem(mat, 2));
+ xyz.z = (float) Math.atan2(-getMatrixElem(mat, 1), getMatrixElem(mat, 0));
+ return true;
+ }
+ else {
+ // WARNING. Not unique. XA - ZA = -atan2(r10,r11)
+ xyz.x = -(float) Math.atan2(getMatrixElem(mat, 3), getMatrixElem(mat, 4));
+ xyz.y = -BulletGlobals.SIMD_HALF_PI;
+ xyz.z = 0.0f;
+ return false;
+ }
+ }
+ else {
+ // WARNING. Not unique. XAngle + ZAngle = atan2(r10,r11)
+ xyz.x = (float) Math.atan2(getMatrixElem(mat, 3), getMatrixElem(mat, 4));
+ xyz.y = BulletGlobals.SIMD_HALF_PI;
+ xyz.z = 0.0f;
+ }
+
+ return false;
+ }
+
+ /**
+ * Calcs the euler angles between the two bodies.
+ */
+ protected void calculateAngleInfo() {
+ stack.pushCommonMath();
+ try {
+ Matrix3f mat = stack.matrices.get();
+
+ Matrix3f relative_frame = stack.matrices.get();
+ mat.set(calculatedTransformA.basis);
+ MatrixUtil.invert(mat);
+ relative_frame.mul(mat, calculatedTransformB.basis);
+
+ matrixToEulerXYZ(relative_frame, calculatedAxisAngleDiff);
+
+ // in euler angle mode we do not actually constrain the angular velocity
+ // along the axes axis[0] and axis[2] (although we do use axis[1]) :
+ //
+ // to get constrain w2-w1 along ...not
+ // ------ --------------------- ------
+ // d(angle[0])/dt = 0 ax[1] x ax[2] ax[0]
+ // d(angle[1])/dt = 0 ax[1]
+ // d(angle[2])/dt = 0 ax[0] x ax[1] ax[2]
+ //
+ // constraining w2-w1 along an axis 'a' means that a'*(w2-w1)=0.
+ // to prove the result for angle[0], write the expression for angle[0] from
+ // GetInfo1 then take the derivative. to prove this for angle[2] it is
+ // easier to take the euler rate expression for d(angle[2])/dt with respect
+ // to the components of w and set that to 0.
+
+ Vector3f axis0 = stack.vectors.get();
+ calculatedTransformB.basis.getColumn(0, axis0);
+
+ Vector3f axis2 = stack.vectors.get();
+ calculatedTransformA.basis.getColumn(2, axis2);
+
+ calculatedAxis[1].cross(axis2, axis0);
+ calculatedAxis[0].cross(calculatedAxis[1], axis2);
+ calculatedAxis[2].cross(axis0, calculatedAxis[1]);
+
+ // if(m_debugDrawer)
+ // {
+ //
+ // char buff[300];
+ // sprintf(buff,"\n X: %.2f ; Y: %.2f ; Z: %.2f ",
+ // m_calculatedAxisAngleDiff[0],
+ // m_calculatedAxisAngleDiff[1],
+ // m_calculatedAxisAngleDiff[2]);
+ // m_debugDrawer->reportErrorWarning(buff);
+ // }
+ }
+ finally {
+ stack.popCommonMath();
+ }
+ }
+
+ /**
+ * Calcs global transform of the offsets.<p>
+ * Calcs the global transform for the joint offset for body A an B, and also calcs the agle differences between the bodies.
+ *
+ * See also: Generic6DofConstraint.getCalculatedTransformA, Generic6DofConstraint.getCalculatedTransformB, Generic6DofConstraint.calculateAngleInfo
+ */
+ public void calculateTransforms() {
+ calculatedTransformA.set(rbA.getCenterOfMassTransform());
+ calculatedTransformA.mul(frameInA);
+
+ calculatedTransformB.set(rbB.getCenterOfMassTransform());
+ calculatedTransformB.mul(frameInB);
+
+ calculateAngleInfo();
+ }
+
+ protected void buildLinearJacobian(/*JacobianEntry jacLinear*/int jacLinear_index, Vector3f normalWorld, Vector3f pivotAInW, Vector3f pivotBInW) {
+ stack.pushCommonMath();
+ try {
+ Matrix3f mat1 = stack.matrices.get(rbA.getCenterOfMassTransform().basis);
+ mat1.transpose();
+
+ Matrix3f mat2 = stack.matrices.get(rbB.getCenterOfMassTransform().basis);
+ mat2.transpose();
+
+ Vector3f tmp1 = stack.vectors.get();
+ tmp1.sub(pivotAInW, rbA.getCenterOfMassPosition());
+
+ Vector3f tmp2 = stack.vectors.get();
+ tmp2.sub(pivotBInW, rbB.getCenterOfMassPosition());
+
+ jacLinear[jacLinear_index].init(
+ mat1,
+ mat2,
+ tmp1,
+ tmp2,
+ normalWorld,
+ rbA.getInvInertiaDiagLocal(),
+ rbA.getInvMass(),
+ rbB.getInvInertiaDiagLocal(),
+ rbB.getInvMass());
+ }
+ finally {
+ stack.popCommonMath();
+ }
+ }
+
+ protected void buildAngularJacobian(/*JacobianEntry jacAngular*/int jacAngular_index, Vector3f jointAxisW) {
+ stack.matrices.push();
+ try {
+ Matrix3f mat1 = stack.matrices.get(rbA.getCenterOfMassTransform().basis);
+ mat1.transpose();
+
+ Matrix3f mat2 = stack.matrices.get(rbB.getCenterOfMassTransform().basis);
+ mat2.transpose();
+
+ jacAng[jacAngular_index].init(jointAxisW,
+ mat1,
+ mat2,
+ rbA.getInvInertiaDiagLocal(),
+ rbB.getInvInertiaDiagLocal());
+ }
+ finally {
+ stack.matrices.pop();
+ }
+ }
+
+ /**
+ * Test angular limit.<p>
+ * Calculates angular correction and returns true if limit needs to be corrected.
+ * Generic6DofConstraint.buildJacobian must be called previously.
+ */
+ public boolean testAngularLimitMotor(int axis_index) {
+ float angle = VectorUtil.getCoord(calculatedAxisAngleDiff, axis_index);
+
+ // test limits
+ angularLimits[axis_index].testLimitValue(angle);
+ return angularLimits[axis_index].needApplyTorques();
+ }
+
+ @Override
+ public void buildJacobian() {
+ stack.vectors.push();
+ try {
+ // calculates transform
+ calculateTransforms();
+
+ Vector3f pivotAInW = calculatedTransformA.origin;
+ Vector3f pivotBInW = calculatedTransformB.origin;
+
+ Vector3f rel_pos1 = stack.vectors.get();
+ rel_pos1.sub(pivotAInW, rbA.getCenterOfMassPosition());
+
+ Vector3f rel_pos2 = stack.vectors.get();
+ rel_pos2.sub(pivotBInW, rbB.getCenterOfMassPosition());
+
+ Vector3f normalWorld = stack.vectors.get();
+ int i;
+ // linear part
+ for (i = 0; i < 3; i++) {
+ if (linearLimits.isLimited(i)) {
+ if (useLinearReferenceFrameA) {
+ calculatedTransformA.basis.getColumn(i, normalWorld);
+ }
+ else {
+ calculatedTransformB.basis.getColumn(i, normalWorld);
+ }
+
+ buildLinearJacobian(
+ /*jacLinear[i]*/i, normalWorld,
+ pivotAInW, pivotBInW);
+
+ }
+ }
+
+ // angular part
+ for (i = 0; i < 3; i++) {
+ // calculates error angle
+ if (testAngularLimitMotor(i)) {
+ normalWorld.set(this.getAxis(i));
+ // Create angular atom
+ buildAngularJacobian(/*jacAng[i]*/i, normalWorld);
+ }
+ }
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ @Override
+ public void solveConstraint(float timeStep) {
+ stack.vectors.push();
+ try {
+ this.timeStep = timeStep;
+
+ //calculateTransforms();
+
+ int i;
+
+ // linear
+
+ Vector3f pointInA = stack.vectors.get(calculatedTransformA.origin);
+ Vector3f pointInB = stack.vectors.get(calculatedTransformB.origin);
+
+ float jacDiagABInv;
+ Vector3f linear_axis = stack.vectors.get();
+ for (i = 0; i < 3; i++) {
+ if (linearLimits.isLimited(i)) {
+ jacDiagABInv = 1f / jacLinear[i].getDiagonal();
+
+ if (useLinearReferenceFrameA) {
+ calculatedTransformA.basis.getColumn(i, linear_axis);
+ }
+ else {
+ calculatedTransformB.basis.getColumn(i, linear_axis);
+ }
+
+ linearLimits.solveLinearAxis(
+ this.timeStep,
+ jacDiagABInv,
+ rbA, pointInA,
+ rbB, pointInB,
+ i, linear_axis);
+
+ }
+ }
+
+ // angular
+ Vector3f angular_axis = stack.vectors.get();
+ float angularJacDiagABInv;
+ for (i = 0; i < 3; i++) {
+ if (angularLimits[i].needApplyTorques()) {
+ // get axis
+ angular_axis.set(getAxis(i));
+
+ angularJacDiagABInv = 1f / jacAng[i].getDiagonal();
+
+ angularLimits[i].solveAngularLimits(this.timeStep, angular_axis, angularJacDiagABInv, rbA, rbB);
+ }
+ }
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+
+ public void updateRHS(float timeStep) {
+ }
+
+ /**
+ * Get the rotation axis in global coordinates.
+ * Generic6DofConstraint.buildJacobian must be called previously.
+ */
+ public Vector3f getAxis(int axis_index) {
+ return calculatedAxis[axis_index];
+ }
+
+ /**
+ * Get the relative Euler angle.
+ * Generic6DofConstraint.buildJacobian must be called previously.
+ */
+ public float getAngle(int axis_index) {
+ return VectorUtil.getCoord(calculatedAxisAngleDiff, axis_index);
+ }
+
+ /**
+ * Gets the global transform of the offset for body A.<p>
+ * See also: Generic6DofConstraint.getFrameOffsetA, Generic6DofConstraint.getFrameOffsetB, Generic6DofConstraint.calculateAngleInfo.
+ */
+ public Transform getCalculatedTransformA() {
+ return calculatedTransformA;
+ }
+
+ /**
+ * Gets the global transform of the offset for body B.<p>
+ * See also: Generic6DofConstraint.getFrameOffsetA, Generic6DofConstraint.getFrameOffsetB, Generic6DofConstraint.calculateAngleInfo.
+ */
+ public Transform getCalculatedTransformB() {
+ return calculatedTransformB;
+ }
+
+ public Transform getFrameOffsetA() {
+ return frameInA;
+ }
+
+ public Transform getFrameOffsetB() {
+ return frameInB;
+ }
+
+ public void setLinearLowerLimit(Vector3f linearLower) {
+ linearLimits.lowerLimit.set(linearLower);
+ }
+
+ public void setLinearUpperLimit(Vector3f linearUpper) {
+ linearLimits.upperLimit.set(linearUpper);
+ }
+
+ public void setAngularLowerLimit(Vector3f angularLower) {
+ angularLimits[0].loLimit = angularLower.x;
+ angularLimits[1].loLimit = angularLower.y;
+ angularLimits[2].loLimit = angularLower.z;
+ }
+
+ public void setAngularUpperLimit(Vector3f angularUpper) {
+ angularLimits[0].hiLimit = angularUpper.x;
+ angularLimits[1].hiLimit = angularUpper.y;
+ angularLimits[2].hiLimit = angularUpper.z;
+ }
+
+ /**
+ * Retrieves the angular limit informacion.
+ */
+ public RotationalLimitMotor getRotationalLimitMotor(int index) {
+ return angularLimits[index];
+ }
+
+ /**
+ * Retrieves the limit informacion.
+ */
+ public TranslationalLimitMotor getTranslationalLimitMotor() {
+ return linearLimits;
+ }
+
+ /**
+ * first 3 are linear, next 3 are angular
+ */
+ public void setLimit(int axis, float lo, float hi) {
+ if (axis < 3) {
+ VectorUtil.setCoord(linearLimits.lowerLimit, axis, lo);
+ VectorUtil.setCoord(linearLimits.upperLimit, axis, hi);
+ }
+ else {
+ angularLimits[axis - 3].loLimit = lo;
+ angularLimits[axis - 3].hiLimit = hi;
+ }
+ }
+
+ /**
+ * Test limit.<p>
+ * - free means upper &lt; lower,<br>
+ * - locked means upper == lower<br>
+ * - limited means upper &gt; lower<br>
+ * - limitIndex: first 3 are linear, next 3 are angular
+ */
+ public boolean isLimited(int limitIndex) {
+ if (limitIndex < 3) {
+ return linearLimits.isLimited(limitIndex);
+
+ }
+ return angularLimits[limitIndex - 3].isLimited();
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/HingeConstraint.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/HingeConstraint.java
new file mode 100644
index 0000000..a514c3e
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/HingeConstraint.java
@@ -0,0 +1,619 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+/* Hinge Constraint by Dirk Gregorius. Limits added by Marcus Hennix at Starbreeze Studios */
+
+package javabullet.dynamics.constraintsolver;
+
+import javabullet.BulletGlobals;
+import javabullet.dynamics.RigidBody;
+import javabullet.linearmath.QuaternionUtil;
+import javabullet.linearmath.ScalarUtil;
+import javabullet.linearmath.Transform;
+import javabullet.linearmath.TransformUtil;
+import javax.vecmath.Matrix3f;
+import javax.vecmath.Quat4f;
+import javax.vecmath.Vector3f;
+
+/**
+ * Hinge constraint between two rigidbodies each with a pivotpoint that descibes
+ * the axis location in local space. Axis defines the orientation of the hinge axis.
+ *
+ * @author jezek2
+ */
+public class HingeConstraint extends TypedConstraint {
+
+ private JacobianEntry[] jac/*[3]*/ = new JacobianEntry[] { new JacobianEntry(), new JacobianEntry(), new JacobianEntry() }; // 3 orthogonal linear constraints
+ private JacobianEntry[] jacAng/*[3]*/ = new JacobianEntry[] { new JacobianEntry(), new JacobianEntry(), new JacobianEntry() }; // 2 orthogonal angular constraints+ 1 for limit/motor
+
+ private final Transform rbAFrame = new Transform(); // constraint axii. Assumes z is hinge axis.
+ private final Transform rbBFrame = new Transform();
+
+ private float motorTargetVelocity;
+ private float maxMotorImpulse;
+
+ private float limitSoftness;
+ private float biasFactor;
+ private float relaxationFactor;
+
+ private float lowerLimit;
+ private float upperLimit;
+
+ private float kHinge;
+
+ private float limitSign;
+ private float correction;
+
+ private float accLimitImpulse;
+
+ private boolean angularOnly;
+ private boolean enableAngularMotor;
+ private boolean solveLimit;
+
+ public HingeConstraint() {
+ super(TypedConstraintType.HINGE_CONSTRAINT_TYPE);
+ enableAngularMotor = false;
+ }
+
+ public HingeConstraint(RigidBody rbA, RigidBody rbB, Vector3f pivotInA, Vector3f pivotInB, Vector3f axisInA, Vector3f axisInB) {
+ super(TypedConstraintType.HINGE_CONSTRAINT_TYPE, rbA, rbB);
+ angularOnly = false;
+ enableAngularMotor = false;
+
+ stack.vectors.push();
+ stack.quats.push();
+ try {
+ rbAFrame.origin.set(pivotInA);
+
+ // since no frame is given, assume this to be zero angle and just pick rb transform axis
+ Vector3f rbAxisA1 = stack.vectors.get();
+ rbA.getCenterOfMassTransform().basis.getColumn(0, rbAxisA1);
+
+ float projection = rbAxisA1.dot(axisInA);
+ if (projection > BulletGlobals.FLT_EPSILON) {
+ rbAxisA1.scale(projection);
+ rbAxisA1.sub(axisInA);
+ }
+ else {
+ rbA.getCenterOfMassTransform().basis.getColumn(1, rbAxisA1);
+ }
+
+ Vector3f rbAxisA2 = stack.vectors.get();
+ rbAxisA2.cross(rbAxisA1, axisInA);
+
+ rbAFrame.basis.setRow(0, rbAxisA1.x, rbAxisA2.x, axisInA.x);
+ rbAFrame.basis.setRow(1, rbAxisA1.y, rbAxisA2.y, axisInA.y);
+ rbAFrame.basis.setRow(2, rbAxisA1.z, rbAxisA2.z, axisInA.z);
+
+ Quat4f rotationArc = stack.quats.get(QuaternionUtil.shortestArcQuat(axisInA, axisInB));
+ Vector3f rbAxisB1 = stack.vectors.get(QuaternionUtil.quatRotate(rotationArc, rbAxisA1));
+ Vector3f rbAxisB2 = stack.vectors.get();
+ rbAxisB2.cross(rbAxisB1, axisInB);
+
+ rbBFrame.origin.set(pivotInB);
+ rbBFrame.basis.setRow(0, rbAxisB1.x, rbAxisB2.x, -axisInB.x);
+ rbBFrame.basis.setRow(1, rbAxisB1.y, rbAxisB2.y, -axisInB.y);
+ rbBFrame.basis.setRow(2, rbAxisB1.z, rbAxisB2.z, -axisInB.z);
+
+ // start with free
+ lowerLimit = 1e30f;
+ upperLimit = -1e30f;
+ biasFactor = 0.3f;
+ relaxationFactor = 1.0f;
+ limitSoftness = 0.9f;
+ solveLimit = false;
+ }
+ finally {
+ stack.vectors.pop();
+ stack.quats.pop();
+ }
+ }
+
+ public HingeConstraint(RigidBody rbA, Vector3f pivotInA, Vector3f axisInA) {
+ super(TypedConstraintType.HINGE_CONSTRAINT_TYPE, rbA);
+ angularOnly = false;
+ enableAngularMotor = false;
+
+ stack.vectors.push();
+ stack.quats.push();
+ try {
+ // since no frame is given, assume this to be zero angle and just pick rb transform axis
+ // fixed axis in worldspace
+ Vector3f rbAxisA1 = stack.vectors.get();
+ rbA.getCenterOfMassTransform().basis.getColumn(0, rbAxisA1);
+
+ float projection = rbAxisA1.dot(axisInA);
+ if (projection > BulletGlobals.FLT_EPSILON) {
+ rbAxisA1.scale(projection);
+ rbAxisA1.sub(axisInA);
+ }
+ else {
+ rbA.getCenterOfMassTransform().basis.getColumn(1, rbAxisA1);
+ }
+
+ Vector3f rbAxisA2 = stack.vectors.get();
+ rbAxisA2.cross(axisInA, rbAxisA1);
+
+ rbAFrame.origin.set(pivotInA);
+ rbAFrame.basis.setRow(0, rbAxisA1.x, rbAxisA2.x, axisInA.x);
+ rbAFrame.basis.setRow(1, rbAxisA1.y, rbAxisA2.y, axisInA.y);
+ rbAFrame.basis.setRow(2, rbAxisA1.z, rbAxisA2.z, axisInA.z);
+
+ Vector3f axisInB = stack.vectors.get();
+ axisInB.negate(axisInA);
+ rbA.getCenterOfMassTransform().basis.transform(axisInB);
+
+ Quat4f rotationArc = stack.quats.get(QuaternionUtil.shortestArcQuat(axisInA, axisInB));
+ Vector3f rbAxisB1 = stack.vectors.get(QuaternionUtil.quatRotate(rotationArc, rbAxisA1));
+ Vector3f rbAxisB2 = stack.vectors.get();
+ rbAxisB2.cross(axisInB, rbAxisB1);
+
+ rbBFrame.origin.set(pivotInA);
+ rbA.getCenterOfMassTransform().transform(rbBFrame.origin);
+ rbBFrame.basis.setRow(0, rbAxisB1.x, rbAxisB2.x, axisInB.x);
+ rbBFrame.basis.setRow(1, rbAxisB1.y, rbAxisB2.y, axisInB.y);
+ rbBFrame.basis.setRow(2, rbAxisB1.z, rbAxisB2.z, axisInB.z);
+
+ // start with free
+ lowerLimit = 1e30f;
+ upperLimit = -1e30f;
+ biasFactor = 0.3f;
+ relaxationFactor = 1.0f;
+ limitSoftness = 0.9f;
+ solveLimit = false;
+ }
+ finally {
+ stack.vectors.pop();
+ stack.quats.pop();
+ }
+ }
+
+ public HingeConstraint(RigidBody rbA, RigidBody rbB, Transform rbAFrame, Transform rbBFrame) {
+ super(TypedConstraintType.HINGE_CONSTRAINT_TYPE, rbA, rbB);
+ this.rbAFrame.set(rbAFrame);
+ this.rbBFrame.set(rbBFrame);
+ angularOnly = false;
+ enableAngularMotor = false;
+
+ // flip axis
+ this.rbBFrame.basis.m02 *= -1f;
+ this.rbBFrame.basis.m12 *= -1f;
+ this.rbBFrame.basis.m22 *= -1f;
+
+ // start with free
+ lowerLimit = 1e30f;
+ upperLimit = -1e30f;
+ biasFactor = 0.3f;
+ relaxationFactor = 1.0f;
+ limitSoftness = 0.9f;
+ solveLimit = false;
+ }
+
+ public HingeConstraint(RigidBody rbA, Transform rbAFrame) {
+ super(TypedConstraintType.HINGE_CONSTRAINT_TYPE, rbA);
+ this.rbAFrame.set(rbAFrame);
+ this.rbBFrame.set(rbAFrame);
+ angularOnly = false;
+ enableAngularMotor = false;
+
+ // not providing rigidbody B means implicitly using worldspace for body B
+
+ // flip axis
+ this.rbBFrame.basis.m02 *= -1f;
+ this.rbBFrame.basis.m12 *= -1f;
+ this.rbBFrame.basis.m22 *= -1f;
+
+ this.rbBFrame.origin.set(this.rbAFrame.origin);
+ rbA.getCenterOfMassTransform().transform(this.rbBFrame.origin);
+
+ // start with free
+ lowerLimit = 1e30f;
+ upperLimit = -1e30f;
+ biasFactor = 0.3f;
+ relaxationFactor = 1.0f;
+ limitSoftness = 0.9f;
+ solveLimit = false;
+ }
+
+ @Override
+ public void buildJacobian() {
+ stack.pushCommonMath();
+ try {
+ Vector3f tmp = stack.vectors.get();
+ Vector3f tmp1 = stack.vectors.get();
+ Vector3f tmp2 = stack.vectors.get();
+ Matrix3f mat1 = stack.matrices.get();
+ Matrix3f mat2 = stack.matrices.get();
+
+ appliedImpulse = 0f;
+
+ if (!angularOnly) {
+ Vector3f pivotAInW = stack.vectors.get(rbAFrame.origin);
+ rbA.getCenterOfMassTransform().transform(pivotAInW);
+
+ Vector3f pivotBInW = stack.vectors.get(rbBFrame.origin);
+ rbB.getCenterOfMassTransform().transform(pivotBInW);
+
+ Vector3f relPos = stack.vectors.get();
+ relPos.sub(pivotBInW, pivotAInW);
+
+ Vector3f[] normal/*[3]*/ = new Vector3f[]{stack.vectors.get(), stack.vectors.get(), stack.vectors.get()};
+ if (relPos.lengthSquared() > BulletGlobals.FLT_EPSILON) {
+ normal[0].set(relPos);
+ normal[0].normalize();
+ }
+ else {
+ normal[0].set(1f, 0f, 0f);
+ }
+
+ TransformUtil.planeSpace1(normal[0], normal[1], normal[2]);
+
+ for (int i = 0; i < 3; i++) {
+ mat1.transpose(rbA.getCenterOfMassTransform().basis);
+ mat2.transpose(rbB.getCenterOfMassTransform().basis);
+
+ tmp1.sub(pivotAInW, rbA.getCenterOfMassPosition());
+ tmp2.sub(pivotBInW, rbB.getCenterOfMassPosition());
+
+ jac[i].init(
+ mat1,
+ mat2,
+ tmp1,
+ tmp2,
+ normal[i],
+ rbA.getInvInertiaDiagLocal(),
+ rbA.getInvMass(),
+ rbB.getInvInertiaDiagLocal(),
+ rbB.getInvMass());
+ }
+ }
+
+ // calculate two perpendicular jointAxis, orthogonal to hingeAxis
+ // these two jointAxis require equal angular velocities for both bodies
+
+ // this is unused for now, it's a todo
+ Vector3f jointAxis0local = stack.vectors.get();
+ Vector3f jointAxis1local = stack.vectors.get();
+
+ rbAFrame.basis.getColumn(2, tmp);
+ TransformUtil.planeSpace1(tmp, jointAxis0local, jointAxis1local);
+
+ // TODO: check this
+ //getRigidBodyA().getCenterOfMassTransform().getBasis() * m_rbAFrame.getBasis().getColumn(2);
+
+ Vector3f jointAxis0 = stack.vectors.get(jointAxis0local);
+ getRigidBodyA().getCenterOfMassTransform().basis.transform(jointAxis0);
+
+ Vector3f jointAxis1 = stack.vectors.get(jointAxis1local);
+ getRigidBodyA().getCenterOfMassTransform().basis.transform(jointAxis1);
+
+ Vector3f hingeAxisWorld = stack.vectors.get();
+ rbAFrame.basis.getColumn(2, hingeAxisWorld);
+ getRigidBodyA().getCenterOfMassTransform().basis.transform(hingeAxisWorld);
+
+ mat1.transpose(rbA.getCenterOfMassTransform().basis);
+ mat2.transpose(rbB.getCenterOfMassTransform().basis);
+ jacAng[0].init(jointAxis0,
+ mat1,
+ mat2,
+ rbA.getInvInertiaDiagLocal(),
+ rbB.getInvInertiaDiagLocal());
+
+ // JAVA NOTE: reused mat1 and mat2, as recomputation is not needed
+ jacAng[1].init(jointAxis1,
+ mat1,
+ mat2,
+ rbA.getInvInertiaDiagLocal(),
+ rbB.getInvInertiaDiagLocal());
+
+ // JAVA NOTE: reused mat1 and mat2, as recomputation is not needed
+ jacAng[2].init(hingeAxisWorld,
+ mat1,
+ mat2,
+ rbA.getInvInertiaDiagLocal(),
+ rbB.getInvInertiaDiagLocal());
+
+ // Compute limit information
+ float hingeAngle = getHingeAngle();
+
+ //set bias, sign, clear accumulator
+ correction = 0f;
+ limitSign = 0f;
+ solveLimit = false;
+ accLimitImpulse = 0f;
+
+ if (lowerLimit < upperLimit) {
+ if (hingeAngle <= lowerLimit * limitSoftness) {
+ correction = (lowerLimit - hingeAngle);
+ limitSign = 1.0f;
+ solveLimit = true;
+ }
+ else if (hingeAngle >= upperLimit * limitSoftness) {
+ correction = upperLimit - hingeAngle;
+ limitSign = -1.0f;
+ solveLimit = true;
+ }
+ }
+
+ // Compute K = J*W*J' for hinge axis
+ Vector3f axisA = stack.vectors.get();
+ rbAFrame.basis.getColumn(2, axisA);
+ getRigidBodyA().getCenterOfMassTransform().basis.transform(axisA);
+
+ kHinge = 1.0f / (getRigidBodyA().computeAngularImpulseDenominator(axisA) +
+ getRigidBodyB().computeAngularImpulseDenominator(axisA));
+ }
+ finally {
+ stack.popCommonMath();
+ }
+ }
+
+ @Override
+ public void solveConstraint(float timeStep) {
+ stack.vectors.push();
+ try {
+ Vector3f tmp = stack.vectors.get();
+ Vector3f tmp2 = stack.vectors.get();
+
+ Vector3f pivotAInW = stack.vectors.get(rbAFrame.origin);
+ rbA.getCenterOfMassTransform().transform(pivotAInW);
+
+ Vector3f pivotBInW = stack.vectors.get(rbBFrame.origin);
+ rbB.getCenterOfMassTransform().transform(pivotBInW);
+
+ float tau = 0.3f;
+
+ // linear part
+ if (!angularOnly) {
+ Vector3f rel_pos1 = stack.vectors.get();
+ rel_pos1.sub(pivotAInW, rbA.getCenterOfMassPosition());
+
+ Vector3f rel_pos2 = stack.vectors.get();
+ rel_pos2.sub(pivotBInW, rbB.getCenterOfMassPosition());
+
+ Vector3f vel1 = stack.vectors.get(rbA.getVelocityInLocalPoint(rel_pos1));
+ Vector3f vel2 = stack.vectors.get(rbB.getVelocityInLocalPoint(rel_pos2));
+ Vector3f vel = stack.vectors.get();
+ vel.sub(vel1, vel2);
+
+ for (int i = 0; i < 3; i++) {
+ Vector3f normal = jac[i].linearJointAxis;
+ float jacDiagABInv = 1f / jac[i].getDiagonal();
+
+ float rel_vel;
+ rel_vel = normal.dot(vel);
+ // positional error (zeroth order error)
+ tmp.sub(pivotAInW, pivotBInW);
+ float depth = -(tmp).dot(normal); // this is the error projected on the normal
+ float impulse = depth * tau / timeStep * jacDiagABInv - rel_vel * jacDiagABInv;
+ appliedImpulse += impulse;
+ Vector3f impulse_vector = stack.vectors.get();
+ impulse_vector.scale(impulse, normal);
+
+ tmp.sub(pivotAInW, rbA.getCenterOfMassPosition());
+ rbA.applyImpulse(impulse_vector, tmp);
+
+ tmp.negate(impulse_vector);
+ tmp2.sub(pivotBInW, rbB.getCenterOfMassPosition());
+ rbB.applyImpulse(tmp, tmp2);
+ }
+ }
+
+
+ {
+ // solve angular part
+
+ // get axes in world space
+ Vector3f axisA = stack.vectors.get();
+ rbAFrame.basis.getColumn(2, axisA);
+ getRigidBodyA().getCenterOfMassTransform().basis.transform(axisA);
+
+ Vector3f axisB = stack.vectors.get();
+ rbBFrame.basis.getColumn(2, axisB);
+ getRigidBodyB().getCenterOfMassTransform().basis.transform(axisB);
+
+ Vector3f angVelA = getRigidBodyA().getAngularVelocity();
+ Vector3f angVelB = getRigidBodyB().getAngularVelocity();
+
+ Vector3f angVelAroundHingeAxisA = stack.vectors.get();
+ angVelAroundHingeAxisA.scale(axisA.dot(angVelA), axisA);
+
+ Vector3f angVelAroundHingeAxisB = stack.vectors.get();
+ angVelAroundHingeAxisB.scale(axisB.dot(angVelB), axisB);
+
+ Vector3f angAorthog = stack.vectors.get();
+ angAorthog.sub(angVelA, angVelAroundHingeAxisA);
+
+ Vector3f angBorthog = stack.vectors.get();
+ angBorthog.sub(angVelB, angVelAroundHingeAxisB);
+
+ Vector3f velrelOrthog = stack.vectors.get();
+ velrelOrthog.sub(angAorthog, angBorthog);
+
+ {
+ // solve orthogonal angular velocity correction
+ float relaxation = 1f;
+ float len = velrelOrthog.length();
+ if (len > 0.00001f) {
+ Vector3f normal = stack.vectors.get();
+ normal.normalize(velrelOrthog);
+
+ float denom = getRigidBodyA().computeAngularImpulseDenominator(normal) +
+ getRigidBodyB().computeAngularImpulseDenominator(normal);
+ // scale for mass and relaxation
+ // todo: expose this 0.9 factor to developer
+ velrelOrthog.scale((1f / denom) * relaxationFactor);
+ }
+
+ // solve angular positional correction
+ // TODO: check
+ //Vector3f angularError = -axisA.cross(axisB) *(btScalar(1.)/timeStep);
+ Vector3f angularError = stack.vectors.get();
+ angularError.cross(axisA, axisB);
+ angularError.negate();
+ angularError.scale(1f / timeStep);
+ float len2 = angularError.length();
+ if (len2 > 0.00001f) {
+ Vector3f normal2 = stack.vectors.get();
+ normal2.normalize(angularError);
+
+ float denom2 = getRigidBodyA().computeAngularImpulseDenominator(normal2) +
+ getRigidBodyB().computeAngularImpulseDenominator(normal2);
+ angularError.scale((1f / denom2) * relaxation);
+ }
+
+ tmp.negate(velrelOrthog);
+ tmp.add(angularError);
+ rbA.applyTorqueImpulse(tmp);
+
+ tmp.sub(velrelOrthog, angularError);
+ rbB.applyTorqueImpulse(tmp);
+
+ // solve limit
+ if (solveLimit) {
+ tmp.sub(angVelB, angVelA);
+ float amplitude = ((tmp).dot(axisA) * relaxationFactor + correction * (1f / timeStep) * biasFactor) * limitSign;
+
+ float impulseMag = amplitude * kHinge;
+
+ // Clamp the accumulated impulse
+ float temp = accLimitImpulse;
+ accLimitImpulse = Math.max(accLimitImpulse + impulseMag, 0f);
+ impulseMag = accLimitImpulse - temp;
+
+ Vector3f impulse = stack.vectors.get();
+ impulse.scale(impulseMag * limitSign, axisA);
+
+ rbA.applyTorqueImpulse(impulse);
+
+ tmp.negate(impulse);
+ rbB.applyTorqueImpulse(tmp);
+ }
+ }
+
+ // apply motor
+ if (enableAngularMotor) {
+ // todo: add limits too
+ Vector3f angularLimit = stack.vectors.get(0f, 0f, 0f);
+
+ Vector3f velrel = stack.vectors.get();
+ velrel.sub(angVelAroundHingeAxisA, angVelAroundHingeAxisB);
+ float projRelVel = velrel.dot(axisA);
+
+ float desiredMotorVel = motorTargetVelocity;
+ float motor_relvel = desiredMotorVel - projRelVel;
+
+ float unclippedMotorImpulse = kHinge * motor_relvel;
+ // todo: should clip against accumulated impulse
+ float clippedMotorImpulse = unclippedMotorImpulse > maxMotorImpulse ? maxMotorImpulse : unclippedMotorImpulse;
+ clippedMotorImpulse = clippedMotorImpulse < -maxMotorImpulse ? -maxMotorImpulse : clippedMotorImpulse;
+ Vector3f motorImp = stack.vectors.get();
+ motorImp.scale(clippedMotorImpulse, axisA);
+
+ tmp.add(motorImp, angularLimit);
+ rbA.applyTorqueImpulse(tmp);
+
+ tmp.negate(motorImp);
+ tmp.sub(angularLimit);
+ rbB.applyTorqueImpulse(tmp);
+ }
+ }
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public void updateRHS(float timeStep) {
+ }
+
+ public float getHingeAngle() {
+ stack.vectors.push();
+ try {
+ Vector3f refAxis0 = stack.vectors.get();
+ rbAFrame.basis.getColumn(0, refAxis0);
+ getRigidBodyA().getCenterOfMassTransform().basis.transform(refAxis0);
+
+ Vector3f refAxis1 = stack.vectors.get();
+ rbAFrame.basis.getColumn(1, refAxis1);
+ getRigidBodyA().getCenterOfMassTransform().basis.transform(refAxis1);
+
+ Vector3f swingAxis = stack.vectors.get();
+ rbBFrame.basis.getColumn(1, swingAxis);
+ getRigidBodyB().getCenterOfMassTransform().basis.transform(swingAxis);
+
+ return ScalarUtil.atan2Fast(swingAxis.dot(refAxis0), swingAxis.dot(refAxis1));
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public void setAngularOnly(boolean angularOnly) {
+ this.angularOnly = angularOnly;
+ }
+
+ public void enableAngularMotor(boolean enableMotor, float targetVelocity, float maxMotorImpulse) {
+ this.enableAngularMotor = enableMotor;
+ this.motorTargetVelocity = targetVelocity;
+ this.maxMotorImpulse = maxMotorImpulse;
+ }
+
+ public void setLimit(float low, float high) {
+ setLimit(low, high, 0.9f, 0.3f, 1.0f);
+ }
+
+ public void setLimit(float low, float high, float _softness, float _biasFactor, float _relaxationFactor) {
+ lowerLimit = low;
+ upperLimit = high;
+
+ limitSoftness = _softness;
+ biasFactor = _biasFactor;
+ relaxationFactor = _relaxationFactor;
+ }
+
+ public float getLowerLimit() {
+ return lowerLimit;
+ }
+
+ public float getUpperLimit() {
+ return upperLimit;
+ }
+
+ public Transform getAFrame() {
+ return rbAFrame;
+ }
+
+ public Transform getBFrame() {
+ return rbBFrame;
+ }
+
+ public boolean getSolveLimit() {
+ return solveLimit;
+ }
+
+ public float getLimitSign() {
+ return limitSign;
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/JacobianEntry.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/JacobianEntry.java
new file mode 100644
index 0000000..aca6ccc
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/JacobianEntry.java
@@ -0,0 +1,231 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.constraintsolver;
+
+import javabullet.BulletGlobals;
+import javabullet.BulletStack;
+import javabullet.linearmath.VectorUtil;
+import javax.vecmath.Matrix3f;
+import javax.vecmath.Vector3f;
+
+//notes:
+// Another memory optimization would be to store m_1MinvJt in the remaining 3 w components
+// which makes the btJacobianEntry memory layout 16 bytes
+// if you only are interested in angular part, just feed massInvA and massInvB zero
+
+/**
+ * Jacobian entry is an abstraction that allows to describe constraints.
+ * It can be used in combination with a constraint solver.
+ * Can be used to relate the effect of an impulse to the constraint error.
+ *
+ * @author jezek2
+ */
+public class JacobianEntry {
+
+ protected final BulletStack stack = BulletStack.get();
+
+ public final Vector3f linearJointAxis = new Vector3f();
+ public final Vector3f aJ = new Vector3f();
+ public final Vector3f bJ = new Vector3f();
+ public final Vector3f m_0MinvJt = new Vector3f();
+ public final Vector3f m_1MinvJt = new Vector3f();
+ // Optimization: can be stored in the w/last component of one of the vectors
+ public float Adiag;
+
+ public JacobianEntry() {
+ }
+
+ /**
+ * Constraint between two different rigidbodies.
+ */
+ public void init(Matrix3f world2A,
+ Matrix3f world2B,
+ Vector3f rel_pos1, Vector3f rel_pos2,
+ Vector3f jointAxis,
+ Vector3f inertiaInvA,
+ float massInvA,
+ Vector3f inertiaInvB,
+ float massInvB)
+ {
+ linearJointAxis.set(jointAxis);
+
+ aJ.cross(rel_pos1, linearJointAxis);
+ world2A.transform(aJ);
+
+ bJ.set(linearJointAxis);
+ bJ.negate();
+ bJ.cross(rel_pos2, bJ);
+ world2B.transform(bJ);
+
+ VectorUtil.mul(m_0MinvJt, inertiaInvA, aJ);
+ VectorUtil.mul(m_1MinvJt, inertiaInvB, bJ);
+ Adiag = massInvA + m_0MinvJt.dot(aJ) + massInvB + m_1MinvJt.dot(bJ);
+
+ assert (Adiag > 0f);
+ }
+
+ /**
+ * Angular constraint between two different rigidbodies.
+ */
+ public void init(Vector3f jointAxis,
+ Matrix3f world2A,
+ Matrix3f world2B,
+ Vector3f inertiaInvA,
+ Vector3f inertiaInvB)
+ {
+ linearJointAxis.set(0f, 0f, 0f);
+
+ aJ.set(jointAxis);
+ world2A.transform(aJ);
+
+ bJ.set(jointAxis);
+ bJ.negate();
+ world2B.transform(bJ);
+
+ VectorUtil.mul(m_0MinvJt, inertiaInvA, aJ);
+ VectorUtil.mul(m_1MinvJt, inertiaInvB, bJ);
+ Adiag = m_0MinvJt.dot(aJ) + m_1MinvJt.dot(bJ);
+
+ assert (Adiag > 0f);
+ }
+
+ /**
+ * Angular constraint between two different rigidbodies.
+ */
+ public void init(Vector3f axisInA,
+ Vector3f axisInB,
+ Vector3f inertiaInvA,
+ Vector3f inertiaInvB)
+ {
+ linearJointAxis.set(0f, 0f, 0f);
+ aJ.set(axisInA);
+
+ bJ.set(axisInB);
+ bJ.negate();
+
+ VectorUtil.mul(m_0MinvJt, inertiaInvA, aJ);
+ VectorUtil.mul(m_1MinvJt, inertiaInvB, bJ);
+ Adiag = m_0MinvJt.dot(aJ) + m_1MinvJt.dot(bJ);
+
+ assert (Adiag > 0f);
+ }
+
+ /**
+ * Constraint on one rigidbody.
+ */
+ public void init(
+ Matrix3f world2A,
+ Vector3f rel_pos1, Vector3f rel_pos2,
+ Vector3f jointAxis,
+ Vector3f inertiaInvA,
+ float massInvA)
+ {
+ linearJointAxis.set(jointAxis);
+
+ aJ.cross(rel_pos1, jointAxis);
+ world2A.transform(aJ);
+
+ bJ.set(jointAxis);
+ bJ.negate();
+ bJ.cross(rel_pos2, bJ);
+ world2A.transform(bJ);
+
+ VectorUtil.mul(m_0MinvJt, inertiaInvA, aJ);
+ m_1MinvJt.set(0f, 0f, 0f);
+ Adiag = massInvA + m_0MinvJt.dot(aJ);
+
+ assert (Adiag > 0f);
+ }
+
+ public float getDiagonal() { return Adiag; }
+
+ /**
+ * For two constraints on the same rigidbody (for example vehicle friction).
+ */
+ public float getNonDiagonal(JacobianEntry jacB, float massInvA) {
+ JacobianEntry jacA = this;
+ float lin = massInvA * jacA.linearJointAxis.dot(jacB.linearJointAxis);
+ float ang = jacA.m_0MinvJt.dot(jacB.aJ);
+ return lin + ang;
+ }
+
+ /**
+ * For two constraints on sharing two same rigidbodies (for example two contact points between two rigidbodies).
+ */
+ public float getNonDiagonal(JacobianEntry jacB, float massInvA, float massInvB) {
+ stack.vectors.push();
+ try {
+ JacobianEntry jacA = this;
+
+ Vector3f lin = stack.vectors.get();
+ VectorUtil.mul(lin, jacA.linearJointAxis, jacB.linearJointAxis);
+
+ Vector3f ang0 = stack.vectors.get();
+ VectorUtil.mul(ang0, jacA.m_0MinvJt, jacB.aJ);
+
+ Vector3f ang1 = stack.vectors.get();
+ VectorUtil.mul(ang1, jacA.m_1MinvJt, jacB.bJ);
+
+ Vector3f lin0 = stack.vectors.get();
+ lin0.scale(massInvA, lin);
+
+ Vector3f lin1 = stack.vectors.get();
+ lin1.scale(massInvB, lin);
+
+ Vector3f sum = stack.vectors.get();
+ VectorUtil.add(sum, ang0, ang1, lin0, lin1);
+
+ return sum.x + sum.y + sum.z;
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public float getRelativeVelocity(Vector3f linvelA, Vector3f angvelA, Vector3f linvelB, Vector3f angvelB) {
+ stack.vectors.push();
+ try {
+ Vector3f linrel = stack.vectors.get();
+ linrel.sub(linvelA, linvelB);
+
+ Vector3f angvela = stack.vectors.get();
+ VectorUtil.mul(angvela, angvelA, aJ);
+
+ Vector3f angvelb = stack.vectors.get();
+ VectorUtil.mul(angvelb, angvelB, bJ);
+
+ VectorUtil.mul(linrel, linrel, linearJointAxis);
+
+ angvela.add(angvelb);
+ angvela.add(linrel);
+
+ float rel_vel2 = angvela.x + angvela.y + angvela.z;
+ return rel_vel2 + BulletGlobals.FLT_EPSILON;
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/Point2PointConstraint.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/Point2PointConstraint.java
new file mode 100644
index 0000000..0d64c25
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/Point2PointConstraint.java
@@ -0,0 +1,197 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.constraintsolver;
+
+import javabullet.dynamics.RigidBody;
+import javabullet.linearmath.VectorUtil;
+import javax.vecmath.Matrix3f;
+import javax.vecmath.Vector3f;
+
+/**
+ * Point to point constraint between two rigidbodies each with a pivotpoint that descibes the 'ballsocket' location in local space.
+ *
+ * @author jezek2
+ */
+public class Point2PointConstraint extends TypedConstraint {
+
+ private final JacobianEntry[] jac = new JacobianEntry[]/*[3]*/ { new JacobianEntry(), new JacobianEntry(), new JacobianEntry() }; // 3 orthogonal linear constraints
+
+ private final Vector3f pivotInA = new Vector3f();
+ private final Vector3f pivotInB = new Vector3f();
+
+ public ConstraintSetting setting = new ConstraintSetting();
+
+ public Point2PointConstraint() {
+ super(TypedConstraintType.POINT2POINT_CONSTRAINT_TYPE);
+ }
+
+ public Point2PointConstraint(RigidBody rbA, RigidBody rbB, Vector3f pivotInA, Vector3f pivotInB) {
+ super(TypedConstraintType.POINT2POINT_CONSTRAINT_TYPE, rbA, rbB);
+ this.pivotInA.set(pivotInA);
+ this.pivotInB.set(pivotInB);
+ }
+
+ public Point2PointConstraint(RigidBody rbA, Vector3f pivotInA) {
+ super(TypedConstraintType.POINT2POINT_CONSTRAINT_TYPE, rbA);
+ this.pivotInA.set(pivotInA);
+ this.pivotInB.set(pivotInA);
+ rbA.getCenterOfMassTransform().transform(this.pivotInB);
+ }
+
+ @Override
+ public void buildJacobian() {
+ stack.pushCommonMath();
+ try {
+ appliedImpulse = 0f;
+
+ Vector3f normal = stack.vectors.get(0f, 0f, 0f);
+
+ Matrix3f tmpMat1 = stack.matrices.get();
+ Matrix3f tmpMat2 = stack.matrices.get();
+ Vector3f tmp1 = stack.vectors.get();
+ Vector3f tmp2 = stack.vectors.get();
+
+ for (int i = 0; i < 3; i++) {
+ VectorUtil.setCoord(normal, i, 1f);
+
+ tmpMat1.transpose(rbA.getCenterOfMassTransform().basis);
+ tmpMat2.transpose(rbB.getCenterOfMassTransform().basis);
+
+ tmp1.set(pivotInA);
+ rbA.getCenterOfMassTransform().transform(tmp1);
+ tmp1.sub(rbA.getCenterOfMassPosition());
+
+ tmp2.set(pivotInB);
+ rbB.getCenterOfMassTransform().transform(tmp2);
+ tmp2.sub(rbB.getCenterOfMassPosition());
+
+ jac[i].init(
+ tmpMat1,
+ tmpMat2,
+ tmp1,
+ tmp2,
+ normal,
+ rbA.getInvInertiaDiagLocal(),
+ rbA.getInvMass(),
+ rbB.getInvInertiaDiagLocal(),
+ rbB.getInvMass());
+ VectorUtil.setCoord(normal, i, 0f);
+ }
+ }
+ finally {
+ stack.popCommonMath();
+ }
+ }
+
+ @Override
+ public void solveConstraint(float timeStep) {
+ stack.vectors.push();
+ try {
+ Vector3f tmp = stack.vectors.get();
+ Vector3f tmp2 = stack.vectors.get();
+
+ Vector3f pivotAInW = stack.vectors.get(pivotInA);
+ rbA.getCenterOfMassTransform().transform(pivotAInW);
+
+ Vector3f pivotBInW = stack.vectors.get(pivotInB);
+ rbB.getCenterOfMassTransform().transform(pivotBInW);
+
+ Vector3f normal = stack.vectors.get(0f, 0f, 0f);
+
+ //btVector3 angvelA = m_rbA.getCenterOfMassTransform().getBasis().transpose() * m_rbA.getAngularVelocity();
+ //btVector3 angvelB = m_rbB.getCenterOfMassTransform().getBasis().transpose() * m_rbB.getAngularVelocity();
+
+ for (int i = 0; i < 3; i++) {
+ VectorUtil.setCoord(normal, i, 1f);
+ float jacDiagABInv = 1f / jac[i].getDiagonal();
+
+ Vector3f rel_pos1 = stack.vectors.get();
+ rel_pos1.sub(pivotAInW, rbA.getCenterOfMassPosition());
+ Vector3f rel_pos2 = stack.vectors.get();
+ rel_pos2.sub(pivotBInW, rbB.getCenterOfMassPosition());
+ // this jacobian entry could be re-used for all iterations
+
+ Vector3f vel1 = stack.vectors.get(rbA.getVelocityInLocalPoint(rel_pos1));
+ Vector3f vel2 = stack.vectors.get(rbB.getVelocityInLocalPoint(rel_pos2));
+ Vector3f vel = stack.vectors.get();
+ vel.sub(vel1, vel2);
+
+ float rel_vel;
+ rel_vel = normal.dot(vel);
+
+ /*
+ //velocity error (first order error)
+ btScalar rel_vel = m_jac[i].getRelativeVelocity(m_rbA.getLinearVelocity(),angvelA,
+ m_rbB.getLinearVelocity(),angvelB);
+ */
+
+ // positional error (zeroth order error)
+ tmp.sub(pivotAInW, pivotBInW);
+ float depth = -tmp.dot(normal); //this is the error projected on the normal
+
+ float impulse = depth * setting.tau / timeStep * jacDiagABInv - setting.damping * rel_vel * jacDiagABInv;
+ appliedImpulse += impulse;
+ Vector3f impulse_vector = stack.vectors.get();
+ impulse_vector.scale(impulse, normal);
+ tmp.sub(pivotAInW, rbA.getCenterOfMassPosition());
+ rbA.applyImpulse(impulse_vector, tmp);
+ tmp.negate(impulse_vector);
+ tmp2.sub(pivotBInW, rbB.getCenterOfMassPosition());
+ rbB.applyImpulse(tmp, tmp2);
+
+ VectorUtil.setCoord(normal, i, 0f);
+ }
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public void updateRHS(float timeStep) {
+ }
+
+ public void setPivotA(Vector3f pivotA) {
+ pivotInA.set(pivotA);
+ }
+
+ public void setPivotB(Vector3f pivotB) {
+ pivotInB.set(pivotB);
+ }
+
+ public Vector3f getPivotInA() {
+ return pivotInA;
+ }
+
+ public Vector3f getPivotInB() {
+ return pivotInB;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public static class ConstraintSetting {
+ public float tau = 0.3f;
+ public float damping = 1f;
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/RotationalLimitMotor.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/RotationalLimitMotor.java
new file mode 100644
index 0000000..73fa92e
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/RotationalLimitMotor.java
@@ -0,0 +1,211 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+/*
+2007-09-09
+btGeneric6DofConstraint Refactored by Francisco Le�n
+http://gimpact.sf.net
+*/
+
+package javabullet.dynamics.constraintsolver;
+
+import javabullet.BulletGlobals;
+import javabullet.BulletStack;
+import javabullet.dynamics.RigidBody;
+import javax.vecmath.Vector3f;
+
+/**
+ * Rotation Limit structure for generic joints.
+ *
+ * @author jezek2
+ */
+public class RotationalLimitMotor {
+
+ protected final BulletStack stack = BulletStack.get();
+
+ public float loLimit; //!< joint limit
+ public float hiLimit; //!< joint limit
+ public float targetVelocity; //!< target motor velocity
+ public float maxMotorForce; //!< max force on motor
+ public float maxLimitForce; //!< max force on limit
+ public float damping; //!< Damping.
+ public float limitSoftness; //! Relaxation factor
+ public float ERP; //!< Error tolerance factor when joint is at limit
+ public float bounce; //!< restitution factor
+ public boolean enableMotor;
+
+ public float currentLimitError;//! How much is violated this limit
+ public int currentLimit;//!< 0=free, 1=at lo limit, 2=at hi limit
+ public float accumulatedImpulse;
+
+ public RotationalLimitMotor() {
+ accumulatedImpulse = 0.f;
+ targetVelocity = 0;
+ maxMotorForce = 0.1f;
+ maxLimitForce = 300.0f;
+ loLimit = -BulletGlobals.SIMD_INFINITY;
+ hiLimit = BulletGlobals.SIMD_INFINITY;
+ ERP = 0.5f;
+ bounce = 0.0f;
+ damping = 1.0f;
+ limitSoftness = 0.5f;
+ currentLimit = 0;
+ currentLimitError = 0;
+ enableMotor = false;
+ }
+
+ public RotationalLimitMotor(RotationalLimitMotor limot) {
+ targetVelocity = limot.targetVelocity;
+ maxMotorForce = limot.maxMotorForce;
+ limitSoftness = limot.limitSoftness;
+ loLimit = limot.loLimit;
+ hiLimit = limot.hiLimit;
+ ERP = limot.ERP;
+ bounce = limot.bounce;
+ currentLimit = limot.currentLimit;
+ currentLimitError = limot.currentLimitError;
+ enableMotor = limot.enableMotor;
+ }
+
+ /**
+ * Is limited?
+ */
+ public boolean isLimited()
+ {
+ if(loLimit>=hiLimit) return false;
+ return true;
+ }
+
+ /**
+ * Need apply correction?
+ */
+ public boolean needApplyTorques()
+ {
+ if(currentLimit == 0 && enableMotor == false) return false;
+ return true;
+ }
+
+ /**
+ * Calculates error. Calculates currentLimit and currentLimitError.
+ */
+ public int testLimitValue(float test_value) {
+ if (loLimit > hiLimit) {
+ currentLimit = 0; // Free from violation
+ return 0;
+ }
+
+ if (test_value < loLimit) {
+ currentLimit = 1; // low limit violation
+ currentLimitError = test_value - loLimit;
+ return 1;
+ }
+ else if (test_value > hiLimit) {
+ currentLimit = 2; // High limit violation
+ currentLimitError = test_value - hiLimit;
+ return 2;
+ }
+ else {
+ currentLimit = 0; // Free from violation
+ return 0;
+ }
+ //return 0;
+ }
+
+ /**
+ * Apply the correction impulses for two bodies.
+ */
+ public float solveAngularLimits(float timeStep, Vector3f axis, float jacDiagABInv, RigidBody body0, RigidBody body1) {
+ if (needApplyTorques() == false) {
+ return 0.0f;
+ }
+
+ stack.vectors.push();
+ try {
+ float target_velocity = this.targetVelocity;
+ float maxMotorForce = this.maxMotorForce;
+
+ // current error correction
+ if (currentLimit != 0) {
+ target_velocity = -ERP * currentLimitError / (timeStep);
+ maxMotorForce = maxLimitForce;
+ }
+
+ maxMotorForce *= timeStep;
+
+ // current velocity difference
+ Vector3f vel_diff = stack.vectors.get(body0.getAngularVelocity());
+ if (body1 != null) {
+ vel_diff.sub(body1.getAngularVelocity());
+ }
+
+ float rel_vel = axis.dot(vel_diff);
+
+ // correction velocity
+ float motor_relvel = limitSoftness * (target_velocity - damping * rel_vel);
+
+ if (motor_relvel < BulletGlobals.FLT_EPSILON && motor_relvel > -BulletGlobals.FLT_EPSILON) {
+ return 0.0f; // no need for applying force
+ }
+
+ // correction impulse
+ float unclippedMotorImpulse = (1 + bounce) * motor_relvel * jacDiagABInv;
+
+ // clip correction impulse
+ float clippedMotorImpulse;
+
+ // todo: should clip against accumulated impulse
+ if (unclippedMotorImpulse > 0.0f) {
+ clippedMotorImpulse = unclippedMotorImpulse > maxMotorForce ? maxMotorForce : unclippedMotorImpulse;
+ }
+ else {
+ clippedMotorImpulse = unclippedMotorImpulse < -maxMotorForce ? -maxMotorForce : unclippedMotorImpulse;
+ }
+
+ // sort with accumulated impulses
+ float lo = -1e30f;
+ float hi = 1e30f;
+
+ float oldaccumImpulse = accumulatedImpulse;
+ float sum = oldaccumImpulse + clippedMotorImpulse;
+ accumulatedImpulse = sum > hi ? 0f : sum < lo ? 0f : sum;
+
+ clippedMotorImpulse = accumulatedImpulse - oldaccumImpulse;
+
+ Vector3f motorImp = stack.vectors.get();
+ motorImp.scale(clippedMotorImpulse, axis);
+
+ body0.applyTorqueImpulse(motorImp);
+ if (body1 != null) {
+ motorImp.negate();
+ body1.applyTorqueImpulse(motorImp);
+ }
+
+ return clippedMotorImpulse;
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/SequentialImpulseConstraintSolver.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/SequentialImpulseConstraintSolver.java
new file mode 100644
index 0000000..ba52ff0
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/SequentialImpulseConstraintSolver.java
@@ -0,0 +1,1233 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.constraintsolver;
+
+import java.util.ArrayList;
+import java.util.List;
+import javabullet.BulletGlobals;
+import javabullet.BulletPool;
+import javabullet.ContactDestroyedCallback;
+import javabullet.ObjectPool;
+import javabullet.collision.broadphase.Dispatcher;
+import javabullet.collision.dispatch.CollisionObject;
+import javabullet.collision.narrowphase.ManifoldPoint;
+import javabullet.collision.narrowphase.PersistentManifold;
+import javabullet.dynamics.RigidBody;
+import javabullet.linearmath.IDebugDraw;
+import javabullet.linearmath.MiscUtil;
+import javabullet.linearmath.TransformUtil;
+import javabullet.util.IntArrayList;
+import javax.vecmath.Matrix3f;
+import javax.vecmath.Vector3f;
+
+/**
+ * SequentialImpulseConstraintSolver uses a Propagation Method and Sequentially applies impulses.
+ * The approach is the 3D version of Erin Catto's GDC 2006 tutorial. See http://www.gphysics.com<p>
+ *
+ * Although Sequential Impulse is more intuitive, it is mathematically equivalent to Projected Successive Overrelaxation (iterative LCP).
+ * Applies impulses for combined restitution and penetration recovery and to simulate friction.
+ *
+ * @author jezek2
+ */
+public class SequentialImpulseConstraintSolver extends ConstraintSolver {
+
+ private static final int MAX_CONTACT_SOLVER_TYPES = ContactConstraintEnum.MAX_CONTACT_SOLVER_TYPES.ordinal();
+
+ private static final int SEQUENTIAL_IMPULSE_MAX_SOLVER_POINTS = 16384;
+ private static OrderIndex[] gOrder = new OrderIndex[SEQUENTIAL_IMPULSE_MAX_SOLVER_POINTS];
+
+ private static int totalCpd = 0;
+
+ static {
+ for (int i=0; i<gOrder.length; i++) {
+ gOrder[i] = new OrderIndex();
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ private final ObjectPool<SolverBody> bodiesPool = BulletPool.get(SolverBody.class);
+ private final ObjectPool<SolverConstraint> constraintsPool = BulletPool.get(SolverConstraint.class);
+ private final ObjectPool<JacobianEntry> jacobiansPool = BulletPool.get(JacobianEntry.class);
+
+ private final List<SolverBody> tmpSolverBodyPool = new ArrayList<SolverBody>();
+ private final List<SolverConstraint> tmpSolverConstraintPool = new ArrayList<SolverConstraint>();
+ private final List<SolverConstraint> tmpSolverFrictionConstraintPool = new ArrayList<SolverConstraint>();
+ private final IntArrayList orderTmpConstraintPool = new IntArrayList();
+ private final IntArrayList orderFrictionConstraintPool = new IntArrayList();
+
+ protected final ContactSolverFunc[][] contactDispatch = new ContactSolverFunc[MAX_CONTACT_SOLVER_TYPES][MAX_CONTACT_SOLVER_TYPES];
+ protected final ContactSolverFunc[][] frictionDispatch = new ContactSolverFunc[MAX_CONTACT_SOLVER_TYPES][MAX_CONTACT_SOLVER_TYPES];
+
+ // choose between several modes, different friction model etc.
+ protected int solverMode = SolverMode.SOLVER_RANDMIZE_ORDER | SolverMode.SOLVER_CACHE_FRIENDLY; // not using SOLVER_USE_WARMSTARTING,
+ // btSeed2 is used for re-arranging the constraint rows. improves convergence/quality of friction
+ protected long btSeed2 = 0L;
+
+ public SequentialImpulseConstraintSolver() {
+ BulletGlobals.gContactDestroyedCallback = new ContactDestroyedCallback() {
+ public boolean invoke(Object userPersistentData) {
+ assert (userPersistentData != null);
+ ConstraintPersistentData cpd = (ConstraintPersistentData) userPersistentData;
+ //btAlignedFree(cpd);
+ totalCpd--;
+ //printf("totalCpd = %i. DELETED Ptr %x\n",totalCpd,userPersistentData);
+ return true;
+ }
+ };
+
+ // initialize default friction/contact funcs
+ int i, j;
+ for (i = 0; i < MAX_CONTACT_SOLVER_TYPES; i++) {
+ for (j = 0; j < MAX_CONTACT_SOLVER_TYPES; j++) {
+ contactDispatch[i][j] = ContactConstraint.resolveSingleCollision;
+ frictionDispatch[i][j] = ContactConstraint.resolveSingleFriction;
+ }
+ }
+ }
+
+ public long rand2() {
+ btSeed2 = (1664525L * btSeed2 + 1013904223L) & 0xffffffff;
+ return btSeed2;
+ }
+
+ // See ODE: adam's all-int straightforward(?) dRandInt (0..n-1)
+ public int randInt2(int n) {
+ // seems good; xor-fold and modulus
+ long un = n;
+ long r = rand2();
+
+ // note: probably more aggressive than it needs to be -- might be
+ // able to get away without one or two of the innermost branches.
+ if (un <= 0x00010000L) {
+ r ^= (r >>> 16);
+ if (un <= 0x00000100L) {
+ r ^= (r >>> 8);
+ if (un <= 0x00000010L) {
+ r ^= (r >>> 4);
+ if (un <= 0x00000004L) {
+ r ^= (r >>> 2);
+ if (un <= 0x00000002L) {
+ r ^= (r >>> 1);
+ }
+ }
+ }
+ }
+ }
+
+ // TODO: check modulo C vs Java mismatch
+ return (int) Math.abs(r % un);
+ }
+
+ private void initSolverBody(SolverBody solverBody, CollisionObject collisionObject) {
+ RigidBody rb = RigidBody.upcast(collisionObject);
+ if (rb != null) {
+ solverBody.angularVelocity.set(rb.getAngularVelocity());
+ solverBody.centerOfMassPosition.set(collisionObject.getWorldTransform().origin);
+ solverBody.friction = collisionObject.getFriction();
+ solverBody.invMass = rb.getInvMass();
+ solverBody.linearVelocity.set(rb.getLinearVelocity());
+ solverBody.originalBody = rb;
+ solverBody.angularFactor = rb.getAngularFactor();
+ }
+ else {
+ solverBody.angularVelocity.set(0f, 0f, 0f);
+ solverBody.centerOfMassPosition.set(collisionObject.getWorldTransform().origin);
+ solverBody.friction = collisionObject.getFriction();
+ solverBody.invMass = 0f;
+ solverBody.linearVelocity.set(0f, 0f, 0f);
+ solverBody.originalBody = null;
+ solverBody.angularFactor = 1f;
+ }
+ }
+
+ private float restitutionCurve(float rel_vel, float restitution) {
+ float rest = restitution * -rel_vel;
+ return rest;
+ }
+
+ /**
+ * velocity + friction
+ * response between two dynamic objects with friction
+ */
+ private float resolveSingleCollisionCombinedCacheFriendly(
+ SolverBody body1,
+ SolverBody body2,
+ SolverConstraint contactConstraint,
+ ContactSolverInfo solverInfo) {
+ stack.vectors.push();
+ try {
+ float normalImpulse;
+
+ // Optimized version of projected relative velocity, use precomputed cross products with normal
+ // body1.getVelocityInLocalPoint(contactConstraint.m_rel_posA,vel1);
+ // body2.getVelocityInLocalPoint(contactConstraint.m_rel_posB,vel2);
+ // btVector3 vel = vel1 - vel2;
+ // btScalar rel_vel = contactConstraint.m_contactNormal.dot(vel);
+
+ float rel_vel;
+ float vel1Dotn = contactConstraint.contactNormal.dot(body1.linearVelocity) + contactConstraint.relpos1CrossNormal.dot(body1.angularVelocity);
+ float vel2Dotn = contactConstraint.contactNormal.dot(body2.linearVelocity) + contactConstraint.relpos2CrossNormal.dot(body2.angularVelocity);
+
+ rel_vel = vel1Dotn - vel2Dotn;
+
+
+ float positionalError = contactConstraint.penetration;
+ float velocityError = contactConstraint.restitution - rel_vel; // * damping;
+
+ float penetrationImpulse = positionalError * contactConstraint.jacDiagABInv;
+ float velocityImpulse = velocityError * contactConstraint.jacDiagABInv;
+ normalImpulse = penetrationImpulse + velocityImpulse;
+
+ // See Erin Catto's GDC 2006 paper: Clamp the accumulated impulse
+ float oldNormalImpulse = contactConstraint.appliedImpulse;
+ float sum = oldNormalImpulse + normalImpulse;
+ contactConstraint.appliedImpulse = 0f > sum ? 0f : sum;
+
+ float oldVelocityImpulse = contactConstraint.appliedVelocityImpulse;
+ float velocitySum = oldVelocityImpulse + velocityImpulse;
+ contactConstraint.appliedVelocityImpulse = 0f > velocitySum ? 0f : velocitySum;
+
+ normalImpulse = contactConstraint.appliedImpulse - oldNormalImpulse;
+
+ Vector3f tmp = stack.vectors.get();
+ if (body1.invMass != 0f) {
+ tmp.scale(body1.invMass, contactConstraint.contactNormal);
+ body1.internalApplyImpulse(tmp, contactConstraint.angularComponentA, normalImpulse);
+ }
+ if (body2.invMass != 0f) {
+ tmp.scale(body2.invMass, contactConstraint.contactNormal);
+ body2.internalApplyImpulse(tmp, contactConstraint.angularComponentB, -normalImpulse);
+ }
+
+ return normalImpulse;
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ private float resolveSingleFrictionCacheFriendly(
+ SolverBody body1,
+ SolverBody body2,
+ SolverConstraint contactConstraint,
+ ContactSolverInfo solverInfo,
+ float appliedNormalImpulse) {
+ stack.vectors.push();
+ try {
+ float combinedFriction = contactConstraint.friction;
+
+ float limit = appliedNormalImpulse * combinedFriction;
+
+ if (appliedNormalImpulse > 0f) //friction
+ {
+
+ float j1;
+ {
+
+ float rel_vel;
+ float vel1Dotn = contactConstraint.contactNormal.dot(body1.linearVelocity) + contactConstraint.relpos1CrossNormal.dot(body1.angularVelocity);
+ float vel2Dotn = contactConstraint.contactNormal.dot(body2.linearVelocity) + contactConstraint.relpos2CrossNormal.dot(body2.angularVelocity);
+ rel_vel = vel1Dotn - vel2Dotn;
+
+ // calculate j that moves us to zero relative velocity
+ j1 = -rel_vel * contactConstraint.jacDiagABInv;
+ //#define CLAMP_ACCUMULATED_FRICTION_IMPULSE 1
+ //#ifdef CLAMP_ACCUMULATED_FRICTION_IMPULSE
+ float oldTangentImpulse = contactConstraint.appliedImpulse;
+ contactConstraint.appliedImpulse = oldTangentImpulse + j1;
+
+ if (limit < contactConstraint.appliedImpulse) {
+ contactConstraint.appliedImpulse = limit;
+ }
+ else {
+ if (contactConstraint.appliedImpulse < -limit) {
+ contactConstraint.appliedImpulse = -limit;
+ }
+ }
+ j1 = contactConstraint.appliedImpulse - oldTangentImpulse;
+ // #else
+ // if (limit < j1)
+ // {
+ // j1 = limit;
+ // } else
+ // {
+ // if (j1 < -limit)
+ // j1 = -limit;
+ // }
+ // #endif
+
+ //GEN_set_min(contactConstraint.m_appliedImpulse, limit);
+ //GEN_set_max(contactConstraint.m_appliedImpulse, -limit);
+ }
+
+ Vector3f tmp = stack.vectors.get();
+ if (body1.invMass != 0f) {
+ tmp.scale(body1.invMass, contactConstraint.contactNormal);
+ body1.internalApplyImpulse(tmp, contactConstraint.angularComponentA, j1);
+ }
+ if (body2.invMass != 0f) {
+ tmp.scale(body2.invMass, contactConstraint.contactNormal);
+ body2.internalApplyImpulse(tmp, contactConstraint.angularComponentB, -j1);
+ }
+
+ }
+ return 0f;
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ protected void addFrictionConstraint(Vector3f normalAxis, int solverBodyIdA, int solverBodyIdB, int frictionIndex, ManifoldPoint cp, Vector3f rel_pos1, Vector3f rel_pos2, CollisionObject colObj0, CollisionObject colObj1, float relaxation) {
+ stack.vectors.push();
+ try {
+ RigidBody body0 = RigidBody.upcast(colObj0);
+ RigidBody body1 = RigidBody.upcast(colObj1);
+
+ SolverConstraint solverConstraint = constraintsPool.get();
+ tmpSolverFrictionConstraintPool.add(solverConstraint);
+
+ solverConstraint.contactNormal.set(normalAxis);
+
+ solverConstraint.solverBodyIdA = solverBodyIdA;
+ solverConstraint.solverBodyIdB = solverBodyIdB;
+ solverConstraint.constraintType = SolverConstraintType.SOLVER_FRICTION_1D;
+ solverConstraint.frictionIndex = frictionIndex;
+
+ solverConstraint.friction = cp.combinedFriction;
+
+ solverConstraint.appliedImpulse = 0f;
+ solverConstraint.appliedVelocityImpulse = 0f;
+ solverConstraint.penetration = 0f;
+ {
+ Vector3f ftorqueAxis1 = stack.vectors.get();
+ ftorqueAxis1.cross(rel_pos1, solverConstraint.contactNormal);
+ solverConstraint.relpos1CrossNormal.set(ftorqueAxis1);
+ if (body0 != null) {
+ solverConstraint.angularComponentA.set(ftorqueAxis1);
+ body0.getInvInertiaTensorWorld().transform(solverConstraint.angularComponentA);
+ }
+ else {
+ solverConstraint.angularComponentA.set(0f, 0f, 0f);
+ }
+ }
+ {
+ Vector3f ftorqueAxis1 = stack.vectors.get();
+ ftorqueAxis1.cross(rel_pos2, solverConstraint.contactNormal);
+ solverConstraint.relpos2CrossNormal.set(ftorqueAxis1);
+ if (body1 != null) {
+ solverConstraint.angularComponentB.set(ftorqueAxis1);
+ body1.getInvInertiaTensorWorld().transform(solverConstraint.angularComponentB);
+ }
+ else {
+ solverConstraint.angularComponentB.set(0f, 0f, 0f);
+ }
+ }
+
+ //#ifdef COMPUTE_IMPULSE_DENOM
+ // btScalar denom0 = rb0->computeImpulseDenominator(pos1,solverConstraint.m_contactNormal);
+ // btScalar denom1 = rb1->computeImpulseDenominator(pos2,solverConstraint.m_contactNormal);
+ //#else
+ Vector3f vec = stack.vectors.get();
+ float denom0 = 0f;
+ float denom1 = 0f;
+ if (body0 != null) {
+ vec.cross(solverConstraint.angularComponentA, rel_pos1);
+ denom0 = body0.getInvMass() + normalAxis.dot(vec);
+ }
+ if (body1 != null) {
+ vec.cross(solverConstraint.angularComponentB, rel_pos2);
+ denom1 = body1.getInvMass() + normalAxis.dot(vec);
+ }
+ //#endif //COMPUTE_IMPULSE_DENOM
+
+ float denom = relaxation / (denom0 + denom1);
+ solverConstraint.jacDiagABInv = denom;
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public float solveGroupCacheFriendlySetup(List<CollisionObject> bodies, int numBodies, List<PersistentManifold> manifoldPtr, int manifold_offset, int numManifolds, List<TypedConstraint> constraints, int constraints_offset, int numConstraints, ContactSolverInfo infoGlobal, IDebugDraw debugDrawer/*,btStackAlloc* stackAlloc*/) {
+ BulletGlobals.pushProfile("solveGroupCacheFriendlySetup");
+ stack.vectors.push();
+ try {
+
+ if ((numConstraints + numManifolds) == 0) {
+ // printf("empty\n");
+ return 0f;
+ }
+ PersistentManifold manifold = null;
+ CollisionObject colObj0 = null, colObj1 = null;
+
+ //btRigidBody* rb0=0,*rb1=0;
+
+ // //#ifdef FORCE_REFESH_CONTACT_MANIFOLDS
+ //
+ // BEGIN_PROFILE("refreshManifolds");
+ //
+ // int i;
+ //
+ //
+ //
+ // for (i=0;i<numManifolds;i++)
+ // {
+ // manifold = manifoldPtr[i];
+ // rb1 = (btRigidBody*)manifold->getBody1();
+ // rb0 = (btRigidBody*)manifold->getBody0();
+ //
+ // manifold->refreshContactPoints(rb0->getCenterOfMassTransform(),rb1->getCenterOfMassTransform());
+ //
+ // }
+ //
+ // END_PROFILE("refreshManifolds");
+ // //#endif //FORCE_REFESH_CONTACT_MANIFOLDS
+
+ Vector3f color = stack.vectors.get(0f, 1f, 0f);
+
+ //int sizeofSB = sizeof(btSolverBody);
+ //int sizeofSC = sizeof(btSolverConstraint);
+
+ //if (1)
+ {
+ //if m_stackAlloc, try to pack bodies/constraints to speed up solving
+ // btBlock* sablock;
+ // sablock = stackAlloc->beginBlock();
+
+ // int memsize = 16;
+ // unsigned char* stackMemory = stackAlloc->allocate(memsize);
+
+
+ // todo: use stack allocator for this temp memory
+ int minReservation = numManifolds * 2;
+
+ //m_tmpSolverBodyPool.reserve(minReservation);
+
+ //don't convert all bodies, only the one we need so solver the constraints
+ /*
+ {
+ for (int i=0;i<numBodies;i++)
+ {
+ btRigidBody* rb = btRigidBody::upcast(bodies[i]);
+ if (rb && (rb->getIslandTag() >= 0))
+ {
+ btAssert(rb->getCompanionId() < 0);
+ int solverBodyId = m_tmpSolverBodyPool.size();
+ btSolverBody& solverBody = m_tmpSolverBodyPool.expand();
+ initSolverBody(&solverBody,rb);
+ rb->setCompanionId(solverBodyId);
+ }
+ }
+ }
+ */
+
+ //m_tmpSolverConstraintPool.reserve(minReservation);
+ //m_tmpSolverFrictionConstraintPool.reserve(minReservation);
+
+ {
+ int i;
+
+ for (i = 0; i < numManifolds; i++) {
+ manifold = manifoldPtr.get(manifold_offset+i);
+ colObj0 = (CollisionObject) manifold.getBody0();
+ colObj1 = (CollisionObject) manifold.getBody1();
+
+ int solverBodyIdA = -1;
+ int solverBodyIdB = -1;
+
+ if (manifold.getNumContacts() != 0) {
+ if (colObj0.getIslandTag() >= 0) {
+ if (colObj0.getCompanionId() >= 0) {
+ // body has already been converted
+ solverBodyIdA = colObj0.getCompanionId();
+ }
+ else {
+ solverBodyIdA = tmpSolverBodyPool.size();
+ SolverBody solverBody = bodiesPool.get();
+ tmpSolverBodyPool.add(solverBody);
+ initSolverBody(solverBody, colObj0);
+ colObj0.setCompanionId(solverBodyIdA);
+ }
+ }
+ else {
+ // create a static body
+ solverBodyIdA = tmpSolverBodyPool.size();
+ SolverBody solverBody = bodiesPool.get();
+ tmpSolverBodyPool.add(solverBody);
+ initSolverBody(solverBody, colObj0);
+ }
+
+ if (colObj1.getIslandTag() >= 0) {
+ if (colObj1.getCompanionId() >= 0) {
+ solverBodyIdB = colObj1.getCompanionId();
+ }
+ else {
+ solverBodyIdB = tmpSolverBodyPool.size();
+ SolverBody solverBody = bodiesPool.get();
+ tmpSolverBodyPool.add(solverBody);
+ initSolverBody(solverBody, colObj1);
+ colObj1.setCompanionId(solverBodyIdB);
+ }
+ }
+ else {
+ // create a static body
+ solverBodyIdB = tmpSolverBodyPool.size();
+ SolverBody solverBody = bodiesPool.get();
+ tmpSolverBodyPool.add(solverBody);
+ initSolverBody(solverBody, colObj1);
+ }
+ }
+
+ Vector3f rel_pos1 = stack.vectors.get();
+ Vector3f rel_pos2 = stack.vectors.get();
+ float relaxation;
+
+ for (int j = 0; j < manifold.getNumContacts(); j++) {
+
+ ManifoldPoint cp = manifold.getContactPoint(j);
+
+ if (debugDrawer != null) {
+ debugDrawer.drawContactPoint(cp.positionWorldOnB, cp.normalWorldOnB, cp.getDistance(), cp.getLifeTime(), color);
+ }
+
+ if (cp.getDistance() <= 0f) {
+ Vector3f pos1 = cp.getPositionWorldOnA();
+ Vector3f pos2 = cp.getPositionWorldOnB();
+
+ rel_pos1.sub(pos1, colObj0.getWorldTransform().origin);
+ rel_pos2.sub(pos2, colObj1.getWorldTransform().origin);
+
+ relaxation = 1f;
+ float rel_vel;
+ Vector3f vel = stack.vectors.get();
+
+ int frictionIndex = tmpSolverConstraintPool.size();
+
+ {
+ SolverConstraint solverConstraint = constraintsPool.get();
+ tmpSolverConstraintPool.add(solverConstraint);
+ RigidBody rb0 = RigidBody.upcast(colObj0);
+ RigidBody rb1 = RigidBody.upcast(colObj1);
+
+ solverConstraint.solverBodyIdA = solverBodyIdA;
+ solverConstraint.solverBodyIdB = solverBodyIdB;
+ solverConstraint.constraintType = SolverConstraintType.SOLVER_CONTACT_1D;
+
+ Vector3f torqueAxis0 = stack.vectors.get();
+ torqueAxis0.cross(rel_pos1, cp.normalWorldOnB);
+
+ if (rb0 != null) {
+ solverConstraint.angularComponentA.set(torqueAxis0);
+ rb0.getInvInertiaTensorWorld().transform(solverConstraint.angularComponentA);
+ }
+ else {
+ solverConstraint.angularComponentA.set(0f, 0f, 0f);
+ }
+
+ Vector3f torqueAxis1 = stack.vectors.get();
+ torqueAxis1.cross(rel_pos2, cp.normalWorldOnB);
+
+ if (rb1 != null) {
+ solverConstraint.angularComponentB.set(torqueAxis1);
+ rb1.getInvInertiaTensorWorld().transform(solverConstraint.angularComponentB);
+ }
+ else {
+ solverConstraint.angularComponentB.set(0f, 0f, 0f);
+ }
+
+ {
+ //#ifdef COMPUTE_IMPULSE_DENOM
+ //btScalar denom0 = rb0->computeImpulseDenominator(pos1,cp.m_normalWorldOnB);
+ //btScalar denom1 = rb1->computeImpulseDenominator(pos2,cp.m_normalWorldOnB);
+ //#else
+ Vector3f vec = stack.vectors.get();
+ float denom0 = 0f;
+ float denom1 = 0f;
+ if (rb0 != null) {
+ vec.cross(solverConstraint.angularComponentA, rel_pos1);
+ denom0 = rb0.getInvMass() + cp.normalWorldOnB.dot(vec);
+ }
+ if (rb1 != null) {
+ vec.cross(solverConstraint.angularComponentB, rel_pos2);
+ denom1 = rb1.getInvMass() + cp.normalWorldOnB.dot(vec);
+ }
+ //#endif //COMPUTE_IMPULSE_DENOM
+
+ float denom = relaxation / (denom0 + denom1);
+ solverConstraint.jacDiagABInv = denom;
+ }
+
+ solverConstraint.contactNormal.set(cp.normalWorldOnB);
+ solverConstraint.relpos1CrossNormal.cross(rel_pos1, cp.normalWorldOnB);
+ solverConstraint.relpos2CrossNormal.cross(rel_pos2, cp.normalWorldOnB);
+
+ Vector3f vel1 = rb0 != null ? stack.vectors.get(rb0.getVelocityInLocalPoint(rel_pos1)) : stack.vectors.get(0f, 0f, 0f);
+ Vector3f vel2 = rb1 != null ? stack.vectors.get(rb1.getVelocityInLocalPoint(rel_pos2)) : stack.vectors.get(0f, 0f, 0f);
+
+ vel.sub(vel1, vel2);
+
+ rel_vel = cp.normalWorldOnB.dot(vel);
+
+ solverConstraint.penetration = cp.getDistance();///btScalar(infoGlobal.m_numIterations);
+ solverConstraint.friction = cp.combinedFriction;
+ solverConstraint.restitution = restitutionCurve(rel_vel, cp.combinedRestitution);
+ if (solverConstraint.restitution <= 0f) {
+ solverConstraint.restitution = 0f;
+ }
+
+ float penVel = -solverConstraint.penetration / infoGlobal.timeStep;
+ solverConstraint.penetration *= -(infoGlobal.erp / infoGlobal.timeStep);
+
+ if (solverConstraint.restitution > penVel) {
+ solverConstraint.penetration = 0f;
+ }
+
+ solverConstraint.appliedImpulse = 0f;
+ solverConstraint.appliedVelocityImpulse = 0f;
+ }
+
+ {
+ Vector3f frictionDir1 = stack.vectors.get();
+ frictionDir1.scale(rel_vel, cp.normalWorldOnB);
+ frictionDir1.sub(vel, frictionDir1);
+
+ float lat_rel_vel = frictionDir1.lengthSquared();
+ if (lat_rel_vel > BulletGlobals.FLT_EPSILON)//0.0f)
+ {
+ frictionDir1.scale(1f / (float) Math.sqrt(lat_rel_vel));
+ addFrictionConstraint(frictionDir1, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation);
+ Vector3f frictionDir2 = stack.vectors.get();
+ frictionDir2.cross(frictionDir1, cp.normalWorldOnB);
+ frictionDir2.normalize();//??
+ addFrictionConstraint(frictionDir2, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation);
+ }
+ else {
+ // re-calculate friction direction every frame, todo: check if this is really needed
+ Vector3f /*frictionDir1 = stack.vectors.get(),*/ frictionDir2 = stack.vectors.get();
+ TransformUtil.planeSpace1(cp.normalWorldOnB, frictionDir1, frictionDir2);
+ addFrictionConstraint(frictionDir1, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation);
+ addFrictionConstraint(frictionDir2, solverBodyIdA, solverBodyIdB, frictionIndex, cp, rel_pos1, rel_pos2, colObj0, colObj1, relaxation);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // TODO: btContactSolverInfo info = infoGlobal;
+
+ {
+ int j;
+ for (j = 0; j < numConstraints; j++) {
+ TypedConstraint constraint = constraints.get(constraints_offset+j);
+ constraint.buildJacobian();
+ }
+ }
+
+
+
+ int numConstraintPool = tmpSolverConstraintPool.size();
+ int numFrictionPool = tmpSolverFrictionConstraintPool.size();
+
+ // todo: use stack allocator for such temporarily memory, same for solver bodies/constraints
+ MiscUtil.resize(orderTmpConstraintPool, numConstraintPool, 0);
+ MiscUtil.resize(orderFrictionConstraintPool, numFrictionPool, 0);
+ {
+ int i;
+ for (i = 0; i < numConstraintPool; i++) {
+ orderTmpConstraintPool.set(i, i);
+ }
+ for (i = 0; i < numFrictionPool; i++) {
+ orderFrictionConstraintPool.set(i, i);
+ }
+ }
+
+ return 0f;
+ }
+ finally {
+ stack.vectors.pop();
+ BulletGlobals.popProfile();
+ }
+ }
+
+ public float solveGroupCacheFriendlyIterations(List<CollisionObject> bodies, int numBodies, List<PersistentManifold> manifoldPtr, int manifold_offset, int numManifolds, List<TypedConstraint> constraints, int constraints_offset, int numConstraints, ContactSolverInfo infoGlobal, IDebugDraw debugDrawer/*,btStackAlloc* stackAlloc*/) {
+ BulletGlobals.pushProfile("solveGroupCacheFriendlyIterations");
+ try {
+ int numConstraintPool = tmpSolverConstraintPool.size();
+ int numFrictionPool = tmpSolverFrictionConstraintPool.size();
+
+ // should traverse the contacts random order...
+ int iteration;
+ {
+ for (iteration = 0; iteration < infoGlobal.numIterations; iteration++) {
+
+ int j;
+ if ((solverMode & SolverMode.SOLVER_RANDMIZE_ORDER) != 0) {
+ if ((iteration & 7) == 0) {
+ for (j = 0; j < numConstraintPool; ++j) {
+ int tmp = orderTmpConstraintPool.get(j);
+ int swapi = randInt2(j + 1);
+ orderTmpConstraintPool.set(j, orderTmpConstraintPool.get(swapi));
+ orderTmpConstraintPool.set(swapi, tmp);
+ }
+
+ for (j = 0; j < numFrictionPool; ++j) {
+ int tmp = orderFrictionConstraintPool.get(j);
+ int swapi = randInt2(j + 1);
+ orderFrictionConstraintPool.set(j, orderFrictionConstraintPool.get(swapi));
+ orderFrictionConstraintPool.set(swapi, tmp);
+ }
+ }
+ }
+
+ for (j = 0; j < numConstraints; j++) {
+ BulletGlobals.pushProfile("solveConstraint");
+ try {
+ TypedConstraint constraint = constraints.get(constraints_offset+j);
+ // todo: use solver bodies, so we don't need to copy from/to btRigidBody
+
+ if ((constraint.getRigidBodyA().getIslandTag() >= 0) && (constraint.getRigidBodyA().getCompanionId() >= 0)) {
+ tmpSolverBodyPool.get(constraint.getRigidBodyA().getCompanionId()).writebackVelocity();
+ }
+ if ((constraint.getRigidBodyB().getIslandTag() >= 0) && (constraint.getRigidBodyB().getCompanionId() >= 0)) {
+ tmpSolverBodyPool.get(constraint.getRigidBodyB().getCompanionId()).writebackVelocity();
+ }
+
+ constraint.solveConstraint(infoGlobal.timeStep);
+
+ if ((constraint.getRigidBodyA().getIslandTag() >= 0) && (constraint.getRigidBodyA().getCompanionId() >= 0)) {
+ tmpSolverBodyPool.get(constraint.getRigidBodyA().getCompanionId()).readVelocity();
+ }
+ if ((constraint.getRigidBodyB().getIslandTag() >= 0) && (constraint.getRigidBodyB().getCompanionId() >= 0)) {
+ tmpSolverBodyPool.get(constraint.getRigidBodyB().getCompanionId()).readVelocity();
+ }
+ }
+ finally {
+ BulletGlobals.popProfile();
+ }
+ }
+
+ {
+ BulletGlobals.pushProfile("resolveSingleCollisionCombinedCacheFriendly");
+ try {
+ int numPoolConstraints = tmpSolverConstraintPool.size();
+ for (j = 0; j < numPoolConstraints; j++) {
+ SolverConstraint solveManifold = tmpSolverConstraintPool.get(orderTmpConstraintPool.get(j));
+ resolveSingleCollisionCombinedCacheFriendly(tmpSolverBodyPool.get(solveManifold.solverBodyIdA),
+ tmpSolverBodyPool.get(solveManifold.solverBodyIdB), solveManifold, infoGlobal);
+ }
+ }
+ finally {
+ BulletGlobals.popProfile();
+ }
+ }
+
+ {
+ BulletGlobals.pushProfile("resolveSingleFrictionCacheFriendly");
+ try {
+ int numFrictionPoolConstraints = tmpSolverFrictionConstraintPool.size();
+
+ for (j = 0; j < numFrictionPoolConstraints; j++) {
+ SolverConstraint solveManifold = tmpSolverFrictionConstraintPool.get(orderFrictionConstraintPool.get(j));
+ resolveSingleFrictionCacheFriendly(tmpSolverBodyPool.get(solveManifold.solverBodyIdA),
+ tmpSolverBodyPool.get(solveManifold.solverBodyIdB), solveManifold, infoGlobal,
+ tmpSolverConstraintPool.get(solveManifold.frictionIndex).appliedImpulse);
+ }
+ }
+ finally {
+ BulletGlobals.popProfile();
+ }
+ }
+ }
+ }
+
+ return 0f;
+ }
+ finally {
+ BulletGlobals.popProfile();
+ }
+ }
+
+ public float solveGroupCacheFriendly(List<CollisionObject> bodies, int numBodies, List<PersistentManifold> manifoldPtr, int manifold_offset, int numManifolds, List<TypedConstraint> constraints, int constraints_offset, int numConstraints, ContactSolverInfo infoGlobal, IDebugDraw debugDrawer/*,btStackAlloc* stackAlloc*/) {
+ int i;
+
+ solveGroupCacheFriendlySetup(bodies, numBodies, manifoldPtr, manifold_offset, numManifolds, constraints, constraints_offset, numConstraints, infoGlobal, debugDrawer/*, stackAlloc*/);
+ solveGroupCacheFriendlyIterations(bodies, numBodies, manifoldPtr, manifold_offset, numManifolds, constraints, constraints_offset, numConstraints, infoGlobal, debugDrawer/*, stackAlloc*/);
+
+ for (i = 0; i < tmpSolverBodyPool.size(); i++) {
+ SolverBody body = tmpSolverBodyPool.get(i);
+ body.writebackVelocity();
+ bodiesPool.release(body);
+ }
+
+ // printf("m_tmpSolverConstraintPool.size() = %i\n",m_tmpSolverConstraintPool.size());
+
+ /*
+ printf("m_tmpSolverBodyPool.size() = %i\n",m_tmpSolverBodyPool.size());
+ printf("m_tmpSolverConstraintPool.size() = %i\n",m_tmpSolverConstraintPool.size());
+ printf("m_tmpSolverFrictionConstraintPool.size() = %i\n",m_tmpSolverFrictionConstraintPool.size());
+ printf("m_tmpSolverBodyPool.capacity() = %i\n",m_tmpSolverBodyPool.capacity());
+ printf("m_tmpSolverConstraintPool.capacity() = %i\n",m_tmpSolverConstraintPool.capacity());
+ printf("m_tmpSolverFrictionConstraintPool.capacity() = %i\n",m_tmpSolverFrictionConstraintPool.capacity());
+ */
+
+ tmpSolverBodyPool.clear();
+
+ for (i=0; i<tmpSolverConstraintPool.size(); i++) {
+ constraintsPool.release(tmpSolverConstraintPool.get(i));
+ }
+ tmpSolverConstraintPool.clear();
+
+ for (i=0; i<tmpSolverFrictionConstraintPool.size(); i++) {
+ constraintsPool.release(tmpSolverFrictionConstraintPool.get(i));
+ }
+ tmpSolverFrictionConstraintPool.clear();
+
+ return 0f;
+ }
+
+ /**
+ * Sequentially applies impulses.
+ */
+ @Override
+ public float solveGroup(List<CollisionObject> bodies, int numBodies, List<PersistentManifold> manifoldPtr, int manifold_offset, int numManifolds, List<TypedConstraint> constraints, int constraints_offset, int numConstraints, ContactSolverInfo infoGlobal, IDebugDraw debugDrawer, Dispatcher dispatcher) {
+ BulletGlobals.pushProfile("solveGroup");
+ try {
+ // TODO: solver cache friendly
+ if ((getSolverMode() & SolverMode.SOLVER_CACHE_FRIENDLY) != 0) {
+ // you need to provide at least some bodies
+ // SimpleDynamicsWorld needs to switch off SOLVER_CACHE_FRIENDLY
+ assert (bodies != null);
+ assert (numBodies != 0);
+ float value = solveGroupCacheFriendly(bodies, numBodies, manifoldPtr, manifold_offset, numManifolds, constraints, constraints_offset, numConstraints, infoGlobal, debugDrawer/*,stackAlloc*/);
+ return value;
+ }
+
+ ContactSolverInfo info = new ContactSolverInfo(infoGlobal);
+
+ int numiter = infoGlobal.numIterations;
+
+ int totalPoints = 0;
+ {
+ short j;
+ for (j = 0; j < numManifolds; j++) {
+ PersistentManifold manifold = manifoldPtr.get(manifold_offset+j);
+ prepareConstraints(manifold, info, debugDrawer);
+
+ for (short p = 0; p < manifoldPtr.get(manifold_offset+j).getNumContacts(); p++) {
+ gOrder[totalPoints].manifoldIndex = j;
+ gOrder[totalPoints].pointIndex = p;
+ totalPoints++;
+ }
+ }
+ }
+
+ {
+ int j;
+ for (j = 0; j < numConstraints; j++) {
+ TypedConstraint constraint = constraints.get(constraints_offset+j);
+ constraint.buildJacobian();
+ }
+ }
+
+ // should traverse the contacts random order...
+ int iteration;
+ {
+ for (iteration = 0; iteration < numiter; iteration++) {
+ int j;
+ if ((solverMode & SolverMode.SOLVER_RANDMIZE_ORDER) != 0) {
+ if ((iteration & 7) == 0) {
+ for (j = 0; j < totalPoints; ++j) {
+ // JAVA NOTE: swaps references instead of copying values (but that's fine in this context)
+ OrderIndex tmp = gOrder[j];
+ int swapi = randInt2(j + 1);
+ gOrder[j] = gOrder[swapi];
+ gOrder[swapi] = tmp;
+ }
+ }
+ }
+
+ for (j = 0; j < numConstraints; j++) {
+ TypedConstraint constraint = constraints.get(constraints_offset+j);
+ constraint.solveConstraint(info.timeStep);
+ }
+
+ for (j = 0; j < totalPoints; j++) {
+ PersistentManifold manifold = manifoldPtr.get(manifold_offset+gOrder[j].manifoldIndex);
+ solve((RigidBody) manifold.getBody0(),
+ (RigidBody) manifold.getBody1(), manifold.getContactPoint(gOrder[j].pointIndex), info, iteration, debugDrawer);
+ }
+
+ for (j = 0; j < totalPoints; j++) {
+ PersistentManifold manifold = manifoldPtr.get(manifold_offset+gOrder[j].manifoldIndex);
+ solveFriction((RigidBody) manifold.getBody0(),
+ (RigidBody) manifold.getBody1(), manifold.getContactPoint(gOrder[j].pointIndex), info, iteration, debugDrawer);
+ }
+
+ }
+ }
+
+ return 0f;
+ }
+ finally {
+ BulletGlobals.popProfile();
+ }
+ }
+
+ protected void prepareConstraints(PersistentManifold manifoldPtr, ContactSolverInfo info, IDebugDraw debugDrawer) {
+ stack.pushCommonMath();
+ try {
+ RigidBody body0 = (RigidBody) manifoldPtr.getBody0();
+ RigidBody body1 = (RigidBody) manifoldPtr.getBody1();
+
+ // only necessary to refresh the manifold once (first iteration). The integration is done outside the loop
+ {
+ //#ifdef FORCE_REFESH_CONTACT_MANIFOLDS
+ //manifoldPtr->refreshContactPoints(body0->getCenterOfMassTransform(),body1->getCenterOfMassTransform());
+ //#endif //FORCE_REFESH_CONTACT_MANIFOLDS
+ int numpoints = manifoldPtr.getNumContacts();
+
+ BulletGlobals.gTotalContactPoints += numpoints;
+
+ Vector3f color = stack.vectors.get(0f, 1f, 0f);
+ for (int i = 0; i < numpoints; i++) {
+ ManifoldPoint cp = manifoldPtr.getContactPoint(i);
+ if (cp.getDistance() <= 0f) {
+ Vector3f pos1 = cp.getPositionWorldOnA();
+ Vector3f pos2 = cp.getPositionWorldOnB();
+
+ Vector3f rel_pos1 = stack.vectors.get();
+ rel_pos1.sub(pos1, body0.getCenterOfMassPosition());
+
+ Vector3f rel_pos2 = stack.vectors.get();
+ rel_pos2.sub(pos2, body1.getCenterOfMassPosition());
+
+ // this jacobian entry is re-used for all iterations
+ Matrix3f mat1 = stack.matrices.get(body0.getCenterOfMassTransform().basis);
+ mat1.transpose();
+
+ Matrix3f mat2 = stack.matrices.get(body1.getCenterOfMassTransform().basis);
+ mat2.transpose();
+
+ JacobianEntry jac = jacobiansPool.get();
+ jac.init(mat1, mat2,
+ rel_pos1, rel_pos2, cp.normalWorldOnB, body0.getInvInertiaDiagLocal(), body0.getInvMass(),
+ body1.getInvInertiaDiagLocal(), body1.getInvMass());
+
+ float jacDiagAB = jac.getDiagonal();
+ jacobiansPool.release(jac);
+
+ ConstraintPersistentData cpd = (ConstraintPersistentData) cp.userPersistentData;
+ if (cpd != null) {
+ // might be invalid
+ cpd.persistentLifeTime++;
+ if (cpd.persistentLifeTime != cp.getLifeTime()) {
+ //printf("Invalid: cpd->m_persistentLifeTime = %i cp.getLifeTime() = %i\n",cpd->m_persistentLifeTime,cp.getLifeTime());
+ //new (cpd) btConstraintPersistentData;
+ cpd.reset();
+ cpd.persistentLifeTime = cp.getLifeTime();
+
+ }
+ else {
+ //printf("Persistent: cpd->m_persistentLifeTime = %i cp.getLifeTime() = %i\n",cpd->m_persistentLifeTime,cp.getLifeTime());
+ }
+ }
+ else {
+ // todo: should this be in a pool?
+ //void* mem = btAlignedAlloc(sizeof(btConstraintPersistentData),16);
+ //cpd = new (mem)btConstraintPersistentData;
+ cpd = new ConstraintPersistentData();
+ //assert(cpd != null);
+
+ totalCpd++;
+ //printf("totalCpd = %i Created Ptr %x\n",totalCpd,cpd);
+ cp.userPersistentData = cpd;
+ cpd.persistentLifeTime = cp.getLifeTime();
+ //printf("CREATED: %x . cpd->m_persistentLifeTime = %i cp.getLifeTime() = %i\n",cpd,cpd->m_persistentLifeTime,cp.getLifeTime());
+ }
+ assert (cpd != null);
+
+ cpd.jacDiagABInv = 1f / jacDiagAB;
+
+ // Dependent on Rigidbody A and B types, fetch the contact/friction response func
+ // perhaps do a similar thing for friction/restutution combiner funcs...
+
+ cpd.frictionSolverFunc = frictionDispatch[body0.frictionSolverType][body1.frictionSolverType];
+ cpd.contactSolverFunc = contactDispatch[body0.contactSolverType][body1.contactSolverType];
+
+ Vector3f vel1 = stack.vectors.get(body0.getVelocityInLocalPoint(rel_pos1));
+ Vector3f vel2 = stack.vectors.get(body1.getVelocityInLocalPoint(rel_pos2));
+ Vector3f vel = stack.vectors.get();
+ vel.sub(vel1, vel2);
+
+ float rel_vel;
+ rel_vel = cp.normalWorldOnB.dot(vel);
+
+ float combinedRestitution = cp.combinedRestitution;
+
+ cpd.penetration = cp.getDistance(); ///btScalar(info.m_numIterations);
+ cpd.friction = cp.combinedFriction;
+ cpd.restitution = restitutionCurve(rel_vel, combinedRestitution);
+ if (cpd.restitution <= 0f) {
+ cpd.restitution = 0f;
+ }
+
+ // restitution and penetration work in same direction so
+ // rel_vel
+
+ float penVel = -cpd.penetration / info.timeStep;
+
+ if (cpd.restitution > penVel) {
+ cpd.penetration = 0f;
+ }
+
+ float relaxation = info.damping;
+ if ((solverMode & SolverMode.SOLVER_USE_WARMSTARTING) != 0) {
+ cpd.appliedImpulse *= relaxation;
+ }
+ else {
+ cpd.appliedImpulse = 0f;
+ }
+
+ // for friction
+ cpd.prevAppliedImpulse = cpd.appliedImpulse;
+
+ // re-calculate friction direction every frame, todo: check if this is really needed
+ TransformUtil.planeSpace1(cp.normalWorldOnB, cpd.frictionWorldTangential0, cpd.frictionWorldTangential1);
+
+ //#define NO_FRICTION_WARMSTART 1
+ //#ifdef NO_FRICTION_WARMSTART
+ cpd.accumulatedTangentImpulse0 = 0f;
+ cpd.accumulatedTangentImpulse1 = 0f;
+ //#endif //NO_FRICTION_WARMSTART
+ float denom0 = body0.computeImpulseDenominator(pos1, cpd.frictionWorldTangential0);
+ float denom1 = body1.computeImpulseDenominator(pos2, cpd.frictionWorldTangential0);
+ float denom = relaxation / (denom0 + denom1);
+ cpd.jacDiagABInvTangent0 = denom;
+
+ denom0 = body0.computeImpulseDenominator(pos1, cpd.frictionWorldTangential1);
+ denom1 = body1.computeImpulseDenominator(pos2, cpd.frictionWorldTangential1);
+ denom = relaxation / (denom0 + denom1);
+ cpd.jacDiagABInvTangent1 = denom;
+
+ Vector3f totalImpulse = stack.vectors.get();
+ //btVector3 totalImpulse =
+ // //#ifndef NO_FRICTION_WARMSTART
+ // //cpd->m_frictionWorldTangential0*cpd->m_accumulatedTangentImpulse0+
+ // //cpd->m_frictionWorldTangential1*cpd->m_accumulatedTangentImpulse1+
+ // //#endif //NO_FRICTION_WARMSTART
+ // cp.normalWorldOnB*cpd.appliedImpulse;
+ totalImpulse.scale(cpd.appliedImpulse, cp.normalWorldOnB);
+
+ ///
+ {
+ Vector3f torqueAxis0 = stack.vectors.get();
+ torqueAxis0.cross(rel_pos1, cp.normalWorldOnB);
+
+ cpd.angularComponentA.set(torqueAxis0);
+ body0.getInvInertiaTensorWorld().transform(cpd.angularComponentA);
+
+ Vector3f torqueAxis1 = stack.vectors.get();
+ torqueAxis1.cross(rel_pos2, cp.normalWorldOnB);
+
+ cpd.angularComponentB.set(torqueAxis1);
+ body1.getInvInertiaTensorWorld().transform(cpd.angularComponentB);
+ }
+ {
+ Vector3f ftorqueAxis0 = stack.vectors.get();
+ ftorqueAxis0.cross(rel_pos1, cpd.frictionWorldTangential0);
+
+ cpd.frictionAngularComponent0A.set(ftorqueAxis0);
+ body0.getInvInertiaTensorWorld().transform(cpd.frictionAngularComponent0A);
+ }
+ {
+ Vector3f ftorqueAxis1 = stack.vectors.get();
+ ftorqueAxis1.cross(rel_pos1, cpd.frictionWorldTangential1);
+
+ cpd.frictionAngularComponent1A.set(ftorqueAxis1);
+ body0.getInvInertiaTensorWorld().transform(cpd.frictionAngularComponent1A);
+ }
+ {
+ Vector3f ftorqueAxis0 = stack.vectors.get();
+ ftorqueAxis0.cross(rel_pos2, cpd.frictionWorldTangential0);
+
+ cpd.frictionAngularComponent0B.set(ftorqueAxis0);
+ body1.getInvInertiaTensorWorld().transform(cpd.frictionAngularComponent0B);
+ }
+ {
+ Vector3f ftorqueAxis1 = stack.vectors.get();
+ ftorqueAxis1.cross(rel_pos2, cpd.frictionWorldTangential1);
+
+ cpd.frictionAngularComponent1B.set(ftorqueAxis1);
+ body1.getInvInertiaTensorWorld().transform(cpd.frictionAngularComponent1B);
+ }
+
+ ///
+
+ // apply previous frames impulse on both bodies
+ body0.applyImpulse(totalImpulse, rel_pos1);
+
+ Vector3f tmp = stack.vectors.get(totalImpulse);
+ tmp.negate();
+ body1.applyImpulse(tmp, rel_pos2);
+ }
+
+ }
+ }
+ }
+ finally {
+ stack.popCommonMath();
+ }
+ }
+
+ public float solveCombinedContactFriction(RigidBody body0, RigidBody body1, ManifoldPoint cp, ContactSolverInfo info, int iter, IDebugDraw debugDrawer) {
+ stack.vectors.push();
+ try {
+ float maxImpulse = 0f;
+
+ Vector3f color = stack.vectors.get(0f, 1f, 0f);
+ {
+ if (cp.getDistance() <= 0f) {
+
+ if (iter == 0) {
+ if (debugDrawer != null) {
+ debugDrawer.drawContactPoint(cp.positionWorldOnB, cp.normalWorldOnB, cp.getDistance(), cp.getLifeTime(), color);
+ }
+ }
+
+ {
+ //btConstraintPersistentData* cpd = (btConstraintPersistentData*) cp.m_userPersistentData;
+ float impulse = ContactConstraint.resolveSingleCollisionCombined(body0, body1, cp, info);
+
+ if (maxImpulse < impulse) {
+ maxImpulse = impulse;
+ }
+ }
+ }
+ }
+ return maxImpulse;
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ protected float solve(RigidBody body0, RigidBody body1, ManifoldPoint cp, ContactSolverInfo info, int iter, IDebugDraw debugDrawer) {
+ stack.vectors.push();
+ try {
+ float maxImpulse = 0f;
+
+ Vector3f color = stack.vectors.get(0f, 1f, 0f);
+ {
+ if (cp.getDistance() <= 0f) {
+
+ if (iter == 0) {
+ if (debugDrawer != null) {
+ debugDrawer.drawContactPoint(cp.positionWorldOnB, cp.normalWorldOnB, cp.getDistance(), cp.getLifeTime(), color);
+ }
+ }
+
+ {
+ ConstraintPersistentData cpd = (ConstraintPersistentData) cp.userPersistentData;
+ float impulse = cpd.contactSolverFunc.invoke(body0, body1, cp, info);
+
+ if (maxImpulse < impulse) {
+ maxImpulse = impulse;
+ }
+ }
+ }
+ }
+
+ return maxImpulse;
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ protected float solveFriction(RigidBody body0, RigidBody body1, ManifoldPoint cp, ContactSolverInfo info, int iter, IDebugDraw debugDrawer) {
+ stack.vectors.push();
+ try {
+ Vector3f color = stack.vectors.get(0f, 1f, 0f);
+ {
+ if (cp.getDistance() <= 0f) {
+ ConstraintPersistentData cpd = (ConstraintPersistentData) cp.userPersistentData;
+ cpd.frictionSolverFunc.invoke(body0, body1, cp, info);
+ }
+ }
+ return 0f;
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ @Override
+ public void reset() {
+ btSeed2 = 0;
+ }
+
+ /**
+ * Advanced: Override the default contact solving function for contacts, for certain types of rigidbody<br>
+ * See RigidBody.contactSolverType and RigidBody.frictionSolverType
+ */
+ public void setContactSolverFunc(ContactSolverFunc func, int type0, int type1) {
+ contactDispatch[type0][type1] = func;
+ }
+
+ /**
+ * Advanced: Override the default friction solving function for contacts, for certain types of rigidbody<br>
+ * See RigidBody.contactSolverType and RigidBody.frictionSolverType
+ */
+ public void setFrictionSolverFunc(ContactSolverFunc func, int type0, int type1) {
+ frictionDispatch[type0][type1] = func;
+ }
+
+ public int getSolverMode() {
+ return solverMode;
+ }
+
+ public void setSolverMode(int solverMode) {
+ this.solverMode = solverMode;
+ }
+
+ public void setRandSeed(long seed) {
+ btSeed2 = seed;
+ }
+
+ public long getRandSeed() {
+ return btSeed2;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ private static class OrderIndex {
+ public int manifoldIndex;
+ public int pointIndex;
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/SolverBody.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/SolverBody.java
new file mode 100644
index 0000000..92f59f7
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/SolverBody.java
@@ -0,0 +1,82 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.constraintsolver;
+
+import javabullet.BulletStack;
+import javabullet.dynamics.RigidBody;
+import javax.vecmath.Vector3f;
+
+/**
+ * SolverBody is an internal datastructure for the constraint solver. Only necessary data is packed to increase cache coherence/performance.
+ *
+ * @author jezek2
+ */
+public class SolverBody {
+
+ protected final BulletStack stack = BulletStack.get();
+
+ public final Vector3f angularVelocity = new Vector3f();
+ public float angularFactor;
+ public float invMass;
+ public float friction;
+ public RigidBody originalBody;
+ public final Vector3f linearVelocity = new Vector3f();
+ public final Vector3f centerOfMassPosition = new Vector3f();
+
+ public void getVelocityInLocalPoint(Vector3f rel_pos, Vector3f velocity) {
+ stack.vectors.push();
+ try {
+ Vector3f tmp = stack.vectors.get();
+ tmp.cross(angularVelocity, rel_pos);
+ velocity.add(linearVelocity, tmp);
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ /**
+ * Optimization for the iterative solver: avoid calculating constant terms involving inertia, normal, relative position.
+ */
+ public void internalApplyImpulse(Vector3f linearComponent, Vector3f angularComponent, float impulseMagnitude) {
+ linearVelocity.scaleAdd(impulseMagnitude, linearComponent, linearVelocity);
+ angularVelocity.scaleAdd(impulseMagnitude * angularFactor, angularComponent, angularVelocity);
+ }
+
+ public void writebackVelocity() {
+ if (invMass != 0f) {
+ originalBody.setLinearVelocity(linearVelocity);
+ originalBody.setAngularVelocity(angularVelocity);
+ //m_originalBody->setCompanionId(-1);
+ }
+ }
+
+ public void readVelocity() {
+ if (invMass != 0f) {
+ linearVelocity.set(originalBody.getLinearVelocity());
+ angularVelocity.set(originalBody.getAngularVelocity());
+ }
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/SolverConstraint.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/SolverConstraint.java
new file mode 100644
index 0000000..70c37af
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/SolverConstraint.java
@@ -0,0 +1,55 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.constraintsolver;
+
+import javax.vecmath.Vector3f;
+
+/**
+ * 1D constraint along a normal axis between bodyA and bodyB. It can be combined to solve contact and friction constraints.
+ *
+ * @author jezek2
+ */
+public class SolverConstraint {
+
+ public final Vector3f relpos1CrossNormal = new Vector3f();
+ public final Vector3f contactNormal = new Vector3f();
+
+ public final Vector3f relpos2CrossNormal = new Vector3f();
+ public final Vector3f angularComponentA = new Vector3f();
+
+ public final Vector3f angularComponentB = new Vector3f();
+ public float appliedVelocityImpulse;
+ public float appliedImpulse;
+ public int solverBodyIdA;
+ public int solverBodyIdB;
+
+ public float friction;
+ public float restitution;
+ public float jacDiagABInv;
+ public float penetration;
+
+ public SolverConstraintType constraintType;
+ public int frictionIndex;
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/SolverConstraintType.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/SolverConstraintType.java
new file mode 100644
index 0000000..eaf6286
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/SolverConstraintType.java
@@ -0,0 +1,33 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.constraintsolver;
+
+/**
+ *
+ * @author jezek2
+ */
+public enum SolverConstraintType {
+ SOLVER_CONTACT_1D,
+ SOLVER_FRICTION_1D
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/SolverMode.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/SolverMode.java
new file mode 100644
index 0000000..bd90d9b
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/SolverMode.java
@@ -0,0 +1,37 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.constraintsolver;
+
+/**
+ *
+ * @author jezek2
+ */
+public class SolverMode {
+
+ public static final int SOLVER_RANDMIZE_ORDER = 1;
+ public static final int SOLVER_FRICTION_SEPARATE = 2;
+ public static final int SOLVER_USE_WARMSTARTING = 4;
+ public static final int SOLVER_CACHE_FRIENDLY = 8;
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/TranslationalLimitMotor.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/TranslationalLimitMotor.java
new file mode 100644
index 0000000..d8dafbc
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/TranslationalLimitMotor.java
@@ -0,0 +1,155 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+/*
+2007-09-09
+btGeneric6DofConstraint Refactored by Francisco Le�n
+http://gimpact.sf.net
+*/
+
+package javabullet.dynamics.constraintsolver;
+
+import javabullet.BulletStack;
+import javabullet.dynamics.RigidBody;
+import javabullet.linearmath.VectorUtil;
+import javax.vecmath.Vector3f;
+
+/**
+ *
+ * @author jezek2
+ */
+public class TranslationalLimitMotor {
+
+ protected final BulletStack stack = BulletStack.get();
+
+ public final Vector3f lowerLimit = new Vector3f(); //!< the constraint lower limits
+ public final Vector3f upperLimit = new Vector3f(); //!< the constraint upper limits
+ public final Vector3f accumulatedImpulse = new Vector3f();
+
+ public float limitSoftness; //!< Softness for linear limit
+ public float damping; //!< Damping for linear limit
+ public float restitution; //! Bounce parameter for linear limit
+
+ public TranslationalLimitMotor() {
+ lowerLimit.set(0f, 0f, 0f);
+ upperLimit.set(0f, 0f, 0f);
+ accumulatedImpulse.set(0f, 0f, 0f);
+
+ limitSoftness = 0.7f;
+ damping = 1.0f;
+ restitution = 0.5f;
+ }
+
+ public TranslationalLimitMotor(TranslationalLimitMotor other) {
+ lowerLimit.set(other.lowerLimit);
+ upperLimit.set(other.upperLimit);
+ accumulatedImpulse.set(other.accumulatedImpulse);
+
+ limitSoftness = other.limitSoftness;
+ damping = other.damping;
+ restitution = other.restitution;
+ }
+
+ /**
+ * Test limit.<p>
+ * - free means upper &lt; lower,<br>
+ * - locked means upper == lower<br>
+ * - limited means upper &gt; lower<br>
+ * - limitIndex: first 3 are linear, next 3 are angular
+ */
+ public boolean isLimited(int limitIndex) {
+ return (VectorUtil.getCoord(upperLimit, limitIndex) >= VectorUtil.getCoord(lowerLimit, limitIndex));
+ }
+
+ public float solveLinearAxis(float timeStep, float jacDiagABInv, RigidBody body1, Vector3f pointInA, RigidBody body2, Vector3f pointInB, int limit_index, Vector3f axis_normal_on_a) {
+ stack.vectors.push();
+ try {
+ Vector3f tmp = stack.vectors.get();
+
+ // find relative velocity
+ Vector3f rel_pos1 = stack.vectors.get();
+ rel_pos1.sub(pointInA, body1.getCenterOfMassPosition());
+
+ Vector3f rel_pos2 = stack.vectors.get();
+ rel_pos2.sub(pointInB, body2.getCenterOfMassPosition());
+
+ Vector3f vel1 = stack.vectors.get(body1.getVelocityInLocalPoint(rel_pos1));
+ Vector3f vel2 = stack.vectors.get(body2.getVelocityInLocalPoint(rel_pos2));
+ Vector3f vel = stack.vectors.get();
+ vel.sub(vel1, vel2);
+
+ float rel_vel = axis_normal_on_a.dot(vel);
+
+ // apply displacement correction
+
+ // positional error (zeroth order error)
+ tmp.sub(pointInA, pointInB);
+ float depth = -(tmp).dot(axis_normal_on_a);
+ float lo = -1e30f;
+ float hi = 1e30f;
+
+ float minLimit = VectorUtil.getCoord(lowerLimit, limit_index);
+ float maxLimit = VectorUtil.getCoord(upperLimit, limit_index);
+
+ // handle the limits
+ if (minLimit < maxLimit) {
+ {
+ if (depth > maxLimit) {
+ depth -= maxLimit;
+ lo = 0f;
+
+ }
+ else {
+ if (depth < minLimit) {
+ depth -= minLimit;
+ hi = 0f;
+ }
+ else {
+ return 0.0f;
+ }
+ }
+ }
+ }
+
+ float normalImpulse = limitSoftness * (restitution * depth / timeStep - damping * rel_vel) * jacDiagABInv;
+
+ float oldNormalImpulse = VectorUtil.getCoord(accumulatedImpulse, limit_index);
+ float sum = oldNormalImpulse + normalImpulse;
+ VectorUtil.setCoord(accumulatedImpulse, limit_index, sum > hi ? 0f : sum < lo ? 0f : sum);
+ normalImpulse = VectorUtil.getCoord(accumulatedImpulse, limit_index) - oldNormalImpulse;
+
+ Vector3f impulse_vector = stack.vectors.get();
+ impulse_vector.scale(normalImpulse, axis_normal_on_a);
+ body1.applyImpulse(impulse_vector, rel_pos1);
+
+ tmp.negate(impulse_vector);
+ body2.applyImpulse(tmp, rel_pos2);
+ return normalImpulse;
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/TypedConstraint.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/TypedConstraint.java
new file mode 100644
index 0000000..e68d0a0
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/TypedConstraint.java
@@ -0,0 +1,101 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.constraintsolver;
+
+import javabullet.BulletGlobals;
+import javabullet.BulletStack;
+import javabullet.dynamics.RigidBody;
+
+/**
+ * TypedConstraint is the baseclass for Bullet constraints and vehicles.
+ *
+ * @author jezek2
+ */
+public abstract class TypedConstraint {
+
+ protected final BulletStack stack = BulletStack.get();
+
+ private static final RigidBody s_fixed = new RigidBody(0, null, null);
+
+ private int userConstraintType = -1;
+ private int userConstraintId = -1;
+
+ private TypedConstraintType constraintType;
+
+ protected RigidBody rbA;
+ protected RigidBody rbB;
+ protected float appliedImpulse = 0f;
+
+ public TypedConstraint(TypedConstraintType type) {
+ this(type, s_fixed, s_fixed);
+ }
+
+ public TypedConstraint(TypedConstraintType type, RigidBody rbA) {
+ this(type, rbA, s_fixed);
+ }
+
+ public TypedConstraint(TypedConstraintType type, RigidBody rbA, RigidBody rbB) {
+ this.constraintType = type;
+ this.rbA = rbA;
+ this.rbB = rbB;
+ s_fixed.setMassProps(0f, BulletGlobals.ZERO_VECTOR3);
+ }
+
+ public abstract void buildJacobian();
+
+ public abstract void solveConstraint(float timeStep);
+
+ public RigidBody getRigidBodyA() {
+ return rbA;
+ }
+
+ public RigidBody getRigidBodyB() {
+ return rbB;
+ }
+
+ public int getUserConstraintType() {
+ return userConstraintType;
+ }
+
+ public void setUserConstraintType(int userConstraintType) {
+ this.userConstraintType = userConstraintType;
+ }
+
+ public int getUserConstraintId() {
+ return userConstraintId;
+ }
+
+ public void setUserConstraintId(int userConstraintId) {
+ this.userConstraintId = userConstraintId;
+ }
+
+ public float getAppliedImpulse() {
+ return appliedImpulse;
+ }
+
+ public TypedConstraintType getConstraintType() {
+ return constraintType;
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/TypedConstraintType.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/TypedConstraintType.java
new file mode 100644
index 0000000..c2f08c0
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/TypedConstraintType.java
@@ -0,0 +1,36 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.constraintsolver;
+
+/**
+ *
+ * @author jezek2
+ */
+public enum TypedConstraintType {
+ POINT2POINT_CONSTRAINT_TYPE,
+ HINGE_CONSTRAINT_TYPE,
+ CONETWIST_CONSTRAINT_TYPE,
+ D6_CONSTRAINT_TYPE,
+ VEHICLE_CONSTRAINT_TYPE
+}
diff --git a/src/jbullet/src/javabullet/dynamics/constraintsolver/package-info.java b/src/jbullet/src/javabullet/dynamics/constraintsolver/package-info.java
new file mode 100644
index 0000000..08aa9fd
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/constraintsolver/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+/**
+ * Constraints.
+ */
+package javabullet.dynamics.constraintsolver;
+
diff --git a/src/jbullet/src/javabullet/dynamics/package-info.java b/src/jbullet/src/javabullet/dynamics/package-info.java
new file mode 100644
index 0000000..394cf74
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+/**
+ * DynamicsWorld and RigidBody.
+ */
+package javabullet.dynamics;
+
diff --git a/src/jbullet/src/javabullet/dynamics/vehicle/DefaultVehicleRaycaster.java b/src/jbullet/src/javabullet/dynamics/vehicle/DefaultVehicleRaycaster.java
new file mode 100644
index 0000000..c14949e
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/vehicle/DefaultVehicleRaycaster.java
@@ -0,0 +1,63 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.vehicle;
+
+import javabullet.collision.dispatch.CollisionWorld.ClosestRayResultCallback;
+import javabullet.dynamics.DynamicsWorld;
+import javabullet.dynamics.RigidBody;
+import javax.vecmath.Vector3f;
+
+/**
+ *
+ * @author jezek2
+ */
+public class DefaultVehicleRaycaster implements VehicleRaycaster {
+
+ protected DynamicsWorld dynamicsWorld;
+
+ public DefaultVehicleRaycaster(DynamicsWorld world) {
+ this.dynamicsWorld = world;
+ }
+
+ public Object castRay(Vector3f from, Vector3f to, VehicleRaycasterResult result) {
+ //RayResultCallback& resultCallback;
+
+ ClosestRayResultCallback rayCallback = new ClosestRayResultCallback(from, to);
+
+ dynamicsWorld.rayTest(from, to, rayCallback);
+
+ if (rayCallback.hasHit()) {
+ RigidBody body = RigidBody.upcast(rayCallback.collisionObject);
+ if (body != null) {
+ result.hitPointInWorld.set(rayCallback.hitPointWorld);
+ result.hitNormalInWorld.set(rayCallback.hitNormalWorld);
+ result.hitNormalInWorld.normalize();
+ result.distFraction = rayCallback.closestHitFraction;
+ return body;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/vehicle/RaycastVehicle.java b/src/jbullet/src/javabullet/dynamics/vehicle/RaycastVehicle.java
new file mode 100644
index 0000000..c13817f
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/vehicle/RaycastVehicle.java
@@ -0,0 +1,765 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.vehicle;
+
+import java.util.ArrayList;
+import java.util.List;
+import javabullet.dynamics.RigidBody;
+import javabullet.dynamics.constraintsolver.ContactConstraint;
+import javabullet.dynamics.constraintsolver.TypedConstraint;
+import javabullet.dynamics.constraintsolver.TypedConstraintType;
+import javabullet.linearmath.MatrixUtil;
+import javabullet.linearmath.MiscUtil;
+import javabullet.linearmath.QuaternionUtil;
+import javabullet.linearmath.Transform;
+import javabullet.util.FloatArrayList;
+import javax.vecmath.Matrix3f;
+import javax.vecmath.Quat4f;
+import javax.vecmath.Vector3f;
+
+/**
+ * Raycast vehicle, very special constraint that turn a rigidbody into a vehicle.
+ *
+ * @author jezek2
+ */
+public class RaycastVehicle extends TypedConstraint {
+
+ private static RigidBody s_fixedObject = new RigidBody(0, null, null);
+ private static final float sideFrictionStiffness2 = 1.0f;
+
+ protected List<Vector3f> forwardWS = new ArrayList<Vector3f>();
+ protected List<Vector3f> axle = new ArrayList<Vector3f>();
+ protected FloatArrayList forwardImpulse = new FloatArrayList();
+ protected FloatArrayList sideImpulse = new FloatArrayList();
+
+ private float tau;
+ private float damping;
+ private VehicleRaycaster vehicleRaycaster;
+ private float pitchControl = 0f;
+ private float steeringValue;
+ private float currentVehicleSpeedKmHour;
+
+ private RigidBody chassisBody;
+
+ private int indexRightAxis = 0;
+ private int indexUpAxis = 2;
+ private int indexForwardAxis = 1;
+
+ public List<WheelInfo> wheelInfo = new ArrayList<WheelInfo>();
+
+ // constructor to create a car from an existing rigidbody
+ public RaycastVehicle(VehicleTuning tuning, RigidBody chassis, VehicleRaycaster raycaster) {
+ super(TypedConstraintType.VEHICLE_CONSTRAINT_TYPE);
+ this.vehicleRaycaster = raycaster;
+ this.chassisBody = chassis;
+ defaultInit(tuning);
+ }
+
+ private void defaultInit(VehicleTuning tuning) {
+ currentVehicleSpeedKmHour = 0f;
+ steeringValue = 0f;
+ }
+
+ /**
+ * Basically most of the code is general for 2 or 4 wheel vehicles, but some of it needs to be reviewed.
+ */
+ public WheelInfo addWheel(Vector3f connectionPointCS, Vector3f wheelDirectionCS0, Vector3f wheelAxleCS, float suspensionRestLength, float wheelRadius, VehicleTuning tuning, boolean isFrontWheel) {
+ WheelInfoConstructionInfo ci = new WheelInfoConstructionInfo();
+
+ ci.chassisConnectionCS.set(connectionPointCS);
+ ci.wheelDirectionCS.set(wheelDirectionCS0);
+ ci.wheelAxleCS.set(wheelAxleCS);
+ ci.suspensionRestLength = suspensionRestLength;
+ ci.wheelRadius = wheelRadius;
+ ci.suspensionStiffness = tuning.suspensionStiffness;
+ ci.wheelsDampingCompression = tuning.suspensionCompression;
+ ci.wheelsDampingRelaxation = tuning.suspensionDamping;
+ ci.frictionSlip = tuning.frictionSlip;
+ ci.bIsFrontWheel = isFrontWheel;
+ ci.maxSuspensionTravelCm = tuning.maxSuspensionTravelCm;
+
+ wheelInfo.add(new WheelInfo(ci));
+
+ WheelInfo wheel = wheelInfo.get(getNumWheels() - 1);
+
+ updateWheelTransformsWS(wheel, false);
+ updateWheelTransform(getNumWheels() - 1, false);
+ return wheel;
+ }
+
+ public Transform getWheelTransformWS(int wheelIndex) {
+ assert (wheelIndex < getNumWheels());
+ WheelInfo wheel = wheelInfo.get(wheelIndex);
+ return wheel.worldTransform;
+ }
+
+ public void updateWheelTransform(int wheelIndex) {
+ updateWheelTransform(wheelIndex, true);
+ }
+
+ public void updateWheelTransform(int wheelIndex, boolean interpolatedTransform) {
+ stack.vectors.push();
+ stack.quats.push();
+ stack.matrices.push();
+ try {
+ WheelInfo wheel = wheelInfo.get(wheelIndex);
+ updateWheelTransformsWS(wheel, interpolatedTransform);
+ Vector3f up = stack.vectors.get();
+ up.negate(wheel.raycastInfo.wheelDirectionWS);
+ Vector3f right = wheel.raycastInfo.wheelAxleWS;
+ Vector3f fwd = stack.vectors.get();
+ fwd.cross(up, right);
+ fwd.normalize();
+ // up = right.cross(fwd);
+ // up.normalize();
+
+ // rotate around steering over de wheelAxleWS
+ float steering = wheel.steering;
+
+ Quat4f steeringOrn = stack.quats.get();
+ QuaternionUtil.setRotation(steeringOrn, up, steering); //wheel.m_steering);
+ Matrix3f steeringMat = stack.matrices.get();
+ MatrixUtil.setRotation(steeringMat, steeringOrn);
+
+ Quat4f rotatingOrn = stack.quats.get();
+ QuaternionUtil.setRotation(rotatingOrn, right, -wheel.rotation);
+ Matrix3f rotatingMat = stack.matrices.get();
+ MatrixUtil.setRotation(rotatingMat, rotatingOrn);
+
+ Matrix3f basis2 = stack.matrices.get();
+ basis2.setRow(0, right.x, fwd.x, up.x);
+ basis2.setRow(1, right.y, fwd.y, up.y);
+ basis2.setRow(2, right.z, fwd.z, up.z);
+
+ Matrix3f wheelBasis = wheel.worldTransform.basis;
+ wheelBasis.mul(steeringMat, rotatingMat);
+ wheelBasis.mul(basis2);
+
+ wheel.worldTransform.origin.scaleAdd(wheel.raycastInfo.suspensionLength, wheel.raycastInfo.wheelDirectionWS, wheel.raycastInfo.hardPointWS);
+ }
+ finally {
+ stack.vectors.pop();
+ stack.quats.pop();
+ stack.matrices.pop();
+ }
+ }
+
+ public void resetSuspension() {
+ int i;
+ for (i = 0; i < wheelInfo.size(); i++) {
+ WheelInfo wheel = wheelInfo.get(i);
+ wheel.raycastInfo.suspensionLength = wheel.getSuspensionRestLength();
+ wheel.suspensionRelativeVelocity = 0f;
+
+ wheel.raycastInfo.contactNormalWS.negate(wheel.raycastInfo.wheelDirectionWS);
+ //wheel_info.setContactFriction(btScalar(0.0));
+ wheel.clippedInvContactDotSuspension = 1f;
+ }
+ }
+
+ public void updateWheelTransformsWS(WheelInfo wheel) {
+ updateWheelTransformsWS(wheel, true);
+ }
+
+ public void updateWheelTransformsWS(WheelInfo wheel, boolean interpolatedTransform) {
+ stack.transforms.push();
+ try {
+ wheel.raycastInfo.isInContact = false;
+
+ Transform chassisTrans = stack.transforms.get(getChassisWorldTransform());
+ if (interpolatedTransform && (getRigidBody().getMotionState() != null)) {
+ getRigidBody().getMotionState().getWorldTransform(chassisTrans);
+ }
+
+ wheel.raycastInfo.hardPointWS.set(wheel.chassisConnectionPointCS);
+ chassisTrans.transform(wheel.raycastInfo.hardPointWS);
+
+ wheel.raycastInfo.wheelDirectionWS.set(wheel.wheelDirectionCS);
+ chassisTrans.basis.transform(wheel.raycastInfo.wheelDirectionWS);
+
+ wheel.raycastInfo.wheelAxleWS.set(wheel.wheelAxleCS);
+ chassisTrans.basis.transform(wheel.raycastInfo.wheelAxleWS);
+ }
+ finally {
+ stack.transforms.pop();
+ }
+ }
+
+ public float rayCast(WheelInfo wheel) {
+ stack.vectors.push();
+ try {
+ updateWheelTransformsWS(wheel, false);
+
+ float depth = -1f;
+
+ float raylen = wheel.getSuspensionRestLength() + wheel.wheelsRadius;
+
+ Vector3f rayvector = stack.vectors.get();
+ rayvector.scale(raylen, wheel.raycastInfo.wheelDirectionWS);
+ Vector3f source = wheel.raycastInfo.hardPointWS;
+ wheel.raycastInfo.contactPointWS.add(source, rayvector);
+ Vector3f target = wheel.raycastInfo.contactPointWS;
+
+ float param = 0f;
+
+ VehicleRaycasterResult rayResults = new VehicleRaycasterResult();
+
+ assert (vehicleRaycaster != null);
+
+ Object object = vehicleRaycaster.castRay(source, target, rayResults);
+
+ wheel.raycastInfo.groundObject = null;
+
+ if (object != null) {
+ param = rayResults.distFraction;
+ depth = raylen * rayResults.distFraction;
+ wheel.raycastInfo.contactNormalWS.set(rayResults.hitNormalInWorld);
+ wheel.raycastInfo.isInContact = true;
+
+ wheel.raycastInfo.groundObject = s_fixedObject; // todo for driving on dynamic/movable objects!;
+ //wheel.m_raycastInfo.m_groundObject = object;
+
+ float hitDistance = param * raylen;
+ wheel.raycastInfo.suspensionLength = hitDistance - wheel.wheelsRadius;
+ // clamp on max suspension travel
+
+ float minSuspensionLength = wheel.getSuspensionRestLength() - wheel.maxSuspensionTravelCm * 0.01f;
+ float maxSuspensionLength = wheel.getSuspensionRestLength() + wheel.maxSuspensionTravelCm * 0.01f;
+ if (wheel.raycastInfo.suspensionLength < minSuspensionLength) {
+ wheel.raycastInfo.suspensionLength = minSuspensionLength;
+ }
+ if (wheel.raycastInfo.suspensionLength > maxSuspensionLength) {
+ wheel.raycastInfo.suspensionLength = maxSuspensionLength;
+ }
+
+ wheel.raycastInfo.contactPointWS.set(rayResults.hitPointInWorld);
+
+ float denominator = wheel.raycastInfo.contactNormalWS.dot(wheel.raycastInfo.wheelDirectionWS);
+
+ Vector3f chassis_velocity_at_contactPoint = stack.vectors.get();
+ Vector3f relpos = stack.vectors.get();
+ relpos.sub(wheel.raycastInfo.contactPointWS, getRigidBody().getCenterOfMassPosition());
+
+ chassis_velocity_at_contactPoint.set(getRigidBody().getVelocityInLocalPoint(relpos));
+
+ float projVel = wheel.raycastInfo.contactNormalWS.dot(chassis_velocity_at_contactPoint);
+
+ if (denominator >= -0.1f) {
+ wheel.suspensionRelativeVelocity = 0f;
+ wheel.clippedInvContactDotSuspension = 1f / 0.1f;
+ }
+ else {
+ float inv = -1f / denominator;
+ wheel.suspensionRelativeVelocity = projVel * inv;
+ wheel.clippedInvContactDotSuspension = inv;
+ }
+
+ }
+ else {
+ // put wheel info as in rest position
+ wheel.raycastInfo.suspensionLength = wheel.getSuspensionRestLength();
+ wheel.suspensionRelativeVelocity = 0f;
+ wheel.raycastInfo.contactNormalWS.negate(wheel.raycastInfo.wheelDirectionWS);
+ wheel.clippedInvContactDotSuspension = 1f;
+ }
+
+ return depth;
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public Transform getChassisWorldTransform() {
+ /*
+ if (getRigidBody()->getMotionState())
+ {
+ btTransform chassisWorldTrans;
+ getRigidBody()->getMotionState()->getWorldTransform(chassisWorldTrans);
+ return chassisWorldTrans;
+ }
+ */
+
+ return getRigidBody().getCenterOfMassTransform();
+ }
+
+ public void updateVehicle(float step) {
+ stack.vectors.push();
+ stack.transforms.push();
+ try {
+ for (int i = 0; i < getNumWheels(); i++) {
+ updateWheelTransform(i, false);
+ }
+
+ currentVehicleSpeedKmHour = 3.6f * getRigidBody().getLinearVelocity().length();
+
+ Transform chassisTrans = stack.transforms.get(getChassisWorldTransform());
+
+ Vector3f forwardW = stack.vectors.get(
+ chassisTrans.basis.getElement(0, indexForwardAxis),
+ chassisTrans.basis.getElement(1, indexForwardAxis),
+ chassisTrans.basis.getElement(2, indexForwardAxis));
+
+ if (forwardW.dot(getRigidBody().getLinearVelocity()) < 0f) {
+ currentVehicleSpeedKmHour *= -1f;
+ }
+
+ //
+ // simulate suspension
+ //
+
+ int i = 0;
+ for (i = 0; i < wheelInfo.size(); i++) {
+ float depth;
+ depth = rayCast(wheelInfo.get(i));
+ }
+
+ updateSuspension(step);
+
+ for (i = 0; i < wheelInfo.size(); i++) {
+ // apply suspension force
+ WheelInfo wheel = wheelInfo.get(i);
+
+ float suspensionForce = wheel.wheelsSuspensionForce;
+
+ float gMaxSuspensionForce = 6000f;
+ if (suspensionForce > gMaxSuspensionForce) {
+ suspensionForce = gMaxSuspensionForce;
+ }
+ Vector3f impulse = stack.vectors.get();
+ impulse.scale(suspensionForce * step, wheel.raycastInfo.contactNormalWS);
+ Vector3f relpos = stack.vectors.get();
+ relpos.sub(wheel.raycastInfo.contactPointWS, getRigidBody().getCenterOfMassPosition());
+
+ getRigidBody().applyImpulse(impulse, relpos);
+ }
+
+ updateFriction(step);
+
+ for (i = 0; i < wheelInfo.size(); i++) {
+ WheelInfo wheel = wheelInfo.get(i);
+ Vector3f relpos = stack.vectors.get();
+ relpos.sub(wheel.raycastInfo.hardPointWS, getRigidBody().getCenterOfMassPosition());
+ Vector3f vel = stack.vectors.get(getRigidBody().getVelocityInLocalPoint(relpos));
+
+ if (wheel.raycastInfo.isInContact) {
+ Transform chassisWorldTransform = stack.transforms.get(getChassisWorldTransform());
+
+ Vector3f fwd = stack.vectors.get(
+ chassisWorldTransform.basis.getElement(0, indexForwardAxis),
+ chassisWorldTransform.basis.getElement(1, indexForwardAxis),
+ chassisWorldTransform.basis.getElement(2, indexForwardAxis));
+
+ float proj = fwd.dot(wheel.raycastInfo.contactNormalWS);
+ Vector3f tmp = stack.vectors.get();
+ tmp.scale(proj, wheel.raycastInfo.contactNormalWS);
+ fwd.sub(tmp);
+
+ float proj2 = fwd.dot(vel);
+
+ wheel.deltaRotation = (proj2 * step) / (wheel.wheelsRadius);
+ wheel.rotation += wheel.deltaRotation;
+
+ }
+ else {
+ wheel.rotation += wheel.deltaRotation;
+ }
+
+ wheel.deltaRotation *= 0.99f; // damping of rotation when not in contact
+ }
+ }
+ finally {
+ stack.vectors.pop();
+ stack.transforms.pop();
+ }
+ }
+
+ public void setSteeringValue(float steering, int wheel) {
+ assert (wheel >= 0 && wheel < getNumWheels());
+
+ WheelInfo wheel_info = getWheelInfo(wheel);
+ wheel_info.steering = steering;
+ }
+
+ public float getSteeringValue(int wheel) {
+ return getWheelInfo(wheel).steering;
+ }
+
+ public void applyEngineForce(float force, int wheel) {
+ assert (wheel >= 0 && wheel < getNumWheels());
+ WheelInfo wheel_info = getWheelInfo(wheel);
+ wheel_info.engineForce = force;
+ }
+
+ public WheelInfo getWheelInfo(int index) {
+ assert ((index >= 0) && (index < getNumWheels()));
+
+ return wheelInfo.get(index);
+ }
+
+ public void setBrake(float brake, int wheelIndex) {
+ assert ((wheelIndex >= 0) && (wheelIndex < getNumWheels()));
+ getWheelInfo(wheelIndex).brake = brake;
+ }
+
+ public void updateSuspension(float deltaTime) {
+ float chassisMass = 1f / chassisBody.getInvMass();
+
+ for (int w_it = 0; w_it < getNumWheels(); w_it++) {
+ WheelInfo wheel_info = wheelInfo.get(w_it);
+
+ if (wheel_info.raycastInfo.isInContact) {
+ float force;
+ // Spring
+ {
+ float susp_length = wheel_info.getSuspensionRestLength();
+ float current_length = wheel_info.raycastInfo.suspensionLength;
+
+ float length_diff = (susp_length - current_length);
+
+ force = wheel_info.suspensionStiffness * length_diff * wheel_info.clippedInvContactDotSuspension;
+ }
+
+ // Damper
+ {
+ float projected_rel_vel = wheel_info.suspensionRelativeVelocity;
+ {
+ float susp_damping;
+ if (projected_rel_vel < 0f) {
+ susp_damping = wheel_info.wheelsDampingCompression;
+ }
+ else {
+ susp_damping = wheel_info.wheelsDampingRelaxation;
+ }
+ force -= susp_damping * projected_rel_vel;
+ }
+ }
+
+ // RESULT
+ wheel_info.wheelsSuspensionForce = force * chassisMass;
+ if (wheel_info.wheelsSuspensionForce < 0f) {
+ wheel_info.wheelsSuspensionForce = 0f;
+ }
+ }
+ else {
+ wheel_info.wheelsSuspensionForce = 0f;
+ }
+ }
+ }
+
+ private float calcRollingFriction(WheelContactPoint contactPoint) {
+ stack.vectors.push();
+ try {
+ float j1 = 0f;
+
+ Vector3f contactPosWorld = contactPoint.frictionPositionWorld;
+
+ Vector3f rel_pos1 = stack.vectors.get();
+ rel_pos1.sub(contactPosWorld, contactPoint.body0.getCenterOfMassPosition());
+ Vector3f rel_pos2 = stack.vectors.get();
+ rel_pos2.sub(contactPosWorld, contactPoint.body1.getCenterOfMassPosition());
+
+ float maxImpulse = contactPoint.maxImpulse;
+
+ Vector3f vel1 = stack.vectors.get(contactPoint.body0.getVelocityInLocalPoint(rel_pos1));
+ Vector3f vel2 = stack.vectors.get(contactPoint.body1.getVelocityInLocalPoint(rel_pos2));
+ Vector3f vel = stack.vectors.get();
+ vel.sub(vel1, vel2);
+
+ float vrel = contactPoint.frictionDirectionWorld.dot(vel);
+
+ // calculate j that moves us to zero relative velocity
+ j1 = -vrel * contactPoint.jacDiagABInv;
+ j1 = Math.min(j1, maxImpulse);
+ j1 = Math.max(j1, -maxImpulse);
+
+ return j1;
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public void updateFriction(float timeStep) {
+ stack.vectors.push();
+ stack.matrices.push();
+ try {
+ // calculate the impulse, so that the wheels don't move sidewards
+ int numWheel = getNumWheels();
+ if (numWheel == 0) {
+ return;
+ }
+
+ MiscUtil.resize(forwardWS, numWheel, Vector3f.class);
+ MiscUtil.resize(axle, numWheel, Vector3f.class);
+ MiscUtil.resize(forwardImpulse, numWheel, 0f);
+ MiscUtil.resize(sideImpulse, numWheel, 0f);
+
+ Vector3f tmp = stack.vectors.get();
+
+ int numWheelsOnGround = 0;
+
+ // collapse all those loops into one!
+ for (int i = 0; i < getNumWheels(); i++) {
+ WheelInfo wheel_info = wheelInfo.get(i);
+ RigidBody groundObject = (RigidBody) wheel_info.raycastInfo.groundObject;
+ if (groundObject != null) {
+ numWheelsOnGround++;
+ }
+ sideImpulse.set(i, 0f);
+ forwardImpulse.set(i, 0f);
+ }
+
+ {
+ for (int i = 0; i < getNumWheels(); i++) {
+
+ WheelInfo wheel_info = wheelInfo.get(i);
+
+ RigidBody groundObject = (RigidBody) wheel_info.raycastInfo.groundObject;
+
+ if (groundObject != null) {
+ Transform wheelTrans = getWheelTransformWS(i);
+
+ Matrix3f wheelBasis0 = stack.matrices.get(wheelTrans.basis);
+ axle.get(i).set(
+ wheelBasis0.getElement(0, indexRightAxis),
+ wheelBasis0.getElement(1, indexRightAxis),
+ wheelBasis0.getElement(2, indexRightAxis));
+
+ Vector3f surfNormalWS = wheel_info.raycastInfo.contactNormalWS;
+ float proj = axle.get(i).dot(surfNormalWS);
+ tmp.scale(proj, surfNormalWS);
+ axle.get(i).sub(tmp);
+ axle.get(i).normalize();
+
+ forwardWS.get(i).cross(surfNormalWS, axle.get(i));
+ forwardWS.get(i).normalize();
+
+ float[] floatPtr = stack.floatArrays.getFixed(1);
+ ContactConstraint.resolveSingleBilateral(chassisBody, wheel_info.raycastInfo.contactPointWS,
+ groundObject, wheel_info.raycastInfo.contactPointWS,
+ 0f, axle.get(i), floatPtr, timeStep);
+ sideImpulse.set(i, floatPtr[0]);
+ stack.floatArrays.release(floatPtr);
+
+ sideImpulse.set(i, sideImpulse.get(i) * sideFrictionStiffness2);
+ }
+ }
+ }
+
+ float sideFactor = 1f;
+ float fwdFactor = 0.5f;
+
+ boolean sliding = false;
+ {
+ for (int wheel = 0; wheel < getNumWheels(); wheel++) {
+ WheelInfo wheel_info = wheelInfo.get(wheel);
+ RigidBody groundObject = (RigidBody) wheel_info.raycastInfo.groundObject;
+
+ float rollingFriction = 0f;
+
+ if (groundObject != null) {
+ if (wheel_info.engineForce != 0f) {
+ rollingFriction = wheel_info.engineForce * timeStep;
+ }
+ else {
+ float defaultRollingFrictionImpulse = 0f;
+ float maxImpulse = wheel_info.brake != 0f ? wheel_info.brake : defaultRollingFrictionImpulse;
+ WheelContactPoint contactPt = new WheelContactPoint(chassisBody, groundObject, wheel_info.raycastInfo.contactPointWS, forwardWS.get(wheel), maxImpulse);
+ rollingFriction = calcRollingFriction(contactPt);
+ }
+ }
+
+ // switch between active rolling (throttle), braking and non-active rolling friction (no throttle/break)
+
+ forwardImpulse.set(wheel, 0f);
+ wheelInfo.get(wheel).skidInfo = 1f;
+
+ if (groundObject != null) {
+ wheelInfo.get(wheel).skidInfo = 1f;
+
+ float maximp = wheel_info.wheelsSuspensionForce * timeStep * wheel_info.frictionSlip;
+ float maximpSide = maximp;
+
+ float maximpSquared = maximp * maximpSide;
+
+ forwardImpulse.set(wheel, rollingFriction); //wheelInfo.m_engineForce* timeStep;
+
+ float x = (forwardImpulse.get(wheel)) * fwdFactor;
+ float y = (sideImpulse.get(wheel)) * sideFactor;
+
+ float impulseSquared = (x * x + y * y);
+
+ if (impulseSquared > maximpSquared) {
+ sliding = true;
+
+ float factor = maximp / (float) Math.sqrt(impulseSquared);
+
+ wheelInfo.get(wheel).skidInfo *= factor;
+ }
+ }
+
+ }
+ }
+
+ if (sliding) {
+ for (int wheel = 0; wheel < getNumWheels(); wheel++) {
+ if (sideImpulse.get(wheel) != 0f) {
+ if (wheelInfo.get(wheel).skidInfo < 1f) {
+ forwardImpulse.set(wheel, forwardImpulse.get(wheel) * wheelInfo.get(wheel).skidInfo);
+ sideImpulse.set(wheel, sideImpulse.get(wheel) * wheelInfo.get(wheel).skidInfo);
+ }
+ }
+ }
+ }
+
+ // apply the impulses
+ {
+ for (int wheel = 0; wheel < getNumWheels(); wheel++) {
+ WheelInfo wheel_info = wheelInfo.get(wheel);
+
+ Vector3f rel_pos = stack.vectors.get();
+ rel_pos.sub(wheel_info.raycastInfo.contactPointWS, chassisBody.getCenterOfMassPosition());
+
+ if (forwardImpulse.get(wheel) != 0f) {
+ tmp.scale(forwardImpulse.get(wheel), forwardWS.get(wheel));
+ chassisBody.applyImpulse(tmp, rel_pos);
+ }
+ if (sideImpulse.get(wheel) != 0f) {
+ RigidBody groundObject = (RigidBody) wheelInfo.get(wheel).raycastInfo.groundObject;
+
+ Vector3f rel_pos2 = stack.vectors.get();
+ rel_pos2.sub(wheel_info.raycastInfo.contactPointWS, groundObject.getCenterOfMassPosition());
+
+ Vector3f sideImp = stack.vectors.get();
+ sideImp.scale(sideImpulse.get(wheel), axle.get(wheel));
+
+ rel_pos.z *= wheel_info.rollInfluence;
+ chassisBody.applyImpulse(sideImp, rel_pos);
+
+ // apply friction impulse on the ground
+ tmp.negate(sideImp);
+ groundObject.applyImpulse(tmp, rel_pos2);
+ }
+ }
+ }
+ }
+ finally {
+ stack.vectors.pop();
+ stack.matrices.pop();
+ }
+ }
+
+ @Override
+ public void buildJacobian() {
+ // not yet
+ }
+
+ @Override
+ public void solveConstraint(float timeStep) {
+ // not yet
+ }
+
+ public int getNumWheels() {
+ return wheelInfo.size();
+ }
+
+ public void setPitchControl(float pitch) {
+ this.pitchControl = pitch;
+ }
+
+ public RigidBody getRigidBody() {
+ return chassisBody;
+ }
+
+ public int getRightAxis() {
+ return indexRightAxis;
+ }
+
+ public int getUpAxis() {
+ return indexUpAxis;
+ }
+
+ public int getForwardAxis() {
+ return indexForwardAxis;
+ }
+
+ /**
+ * Worldspace forward vector.
+ */
+ public Vector3f getForwardVector() {
+ stack.vectors.push();
+ try {
+ Transform chassisTrans = getChassisWorldTransform();
+
+ Vector3f forwardW = stack.vectors.get(
+ chassisTrans.basis.getElement(0, indexForwardAxis),
+ chassisTrans.basis.getElement(1, indexForwardAxis),
+ chassisTrans.basis.getElement(2, indexForwardAxis));
+
+ return stack.vectors.returning(forwardW);
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ /**
+ * Velocity of vehicle (positive if velocity vector has same direction as foward vector).
+ */
+ public float getCurrentSpeedKmHour() {
+ return currentVehicleSpeedKmHour;
+ }
+
+ public void setCoordinateSystem(int rightIndex, int upIndex, int forwardIndex) {
+ this.indexRightAxis = rightIndex;
+ this.indexUpAxis = upIndex;
+ this.indexForwardAxis = forwardIndex;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ private static class WheelContactPoint {
+ public RigidBody body0;
+ public RigidBody body1;
+ public final Vector3f frictionPositionWorld = new Vector3f();
+ public final Vector3f frictionDirectionWorld = new Vector3f();
+ public float jacDiagABInv;
+ public float maxImpulse;
+
+ public WheelContactPoint(RigidBody body0, RigidBody body1, Vector3f frictionPosWorld, Vector3f frictionDirectionWorld, float maxImpulse) {
+ this.body0 = body0;
+ this.body1 = body1;
+ this.frictionPositionWorld.set(frictionPosWorld);
+ this.frictionDirectionWorld.set(frictionDirectionWorld);
+ this.maxImpulse = maxImpulse;
+
+ float denom0 = body0.computeImpulseDenominator(frictionPosWorld, frictionDirectionWorld);
+ float denom1 = body1.computeImpulseDenominator(frictionPosWorld, frictionDirectionWorld);
+ float relaxation = 1f;
+ jacDiagABInv = relaxation / (denom0 + denom1);
+ }
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/vehicle/VehicleRaycaster.java b/src/jbullet/src/javabullet/dynamics/vehicle/VehicleRaycaster.java
new file mode 100644
index 0000000..5fff289
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/vehicle/VehicleRaycaster.java
@@ -0,0 +1,37 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.vehicle;
+
+import javax.vecmath.Vector3f;
+
+/**
+ * VehicleRaycaster is provides interface for between vehicle simulation and raycasting.
+ *
+ * @author jezek2
+ */
+public interface VehicleRaycaster {
+
+ public Object castRay(Vector3f from, Vector3f to, VehicleRaycasterResult result);
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/vehicle/VehicleRaycasterResult.java b/src/jbullet/src/javabullet/dynamics/vehicle/VehicleRaycasterResult.java
new file mode 100644
index 0000000..b948341
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/vehicle/VehicleRaycasterResult.java
@@ -0,0 +1,38 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.vehicle;
+
+import javax.vecmath.Vector3f;
+
+/**
+ *
+ * @author jezek2
+ */
+public class VehicleRaycasterResult {
+
+ public final Vector3f hitPointInWorld = new Vector3f();
+ public final Vector3f hitNormalInWorld = new Vector3f();
+ public float distFraction = -1f;
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/vehicle/VehicleTuning.java b/src/jbullet/src/javabullet/dynamics/vehicle/VehicleTuning.java
new file mode 100644
index 0000000..47a6f29
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/vehicle/VehicleTuning.java
@@ -0,0 +1,38 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.vehicle;
+
+/**
+ *
+ * @author jezek2
+ */
+public class VehicleTuning {
+
+ public float suspensionStiffness = 5.88f;
+ public float suspensionCompression = 0.83f;
+ public float suspensionDamping = 0.88f;
+ public float maxSuspensionTravelCm = 500f;
+ public float frictionSlip = 10.5f;
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/vehicle/WheelInfo.java b/src/jbullet/src/javabullet/dynamics/vehicle/WheelInfo.java
new file mode 100644
index 0000000..d8f9643
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/vehicle/WheelInfo.java
@@ -0,0 +1,145 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.vehicle;
+
+import javabullet.BulletStack;
+import javabullet.dynamics.RigidBody;
+import javabullet.linearmath.Transform;
+import javax.vecmath.Vector3f;
+
+/**
+ * WheelInfo contains information per wheel about friction and suspension.
+ *
+ * @author jezek2
+ */
+public class WheelInfo {
+
+ protected final BulletStack stack = BulletStack.get();
+
+ public final RaycastInfo raycastInfo = new RaycastInfo();
+
+ public final Transform worldTransform = new Transform();
+
+ public final Vector3f chassisConnectionPointCS = new Vector3f(); // const
+ public final Vector3f wheelDirectionCS = new Vector3f(); // const
+ public final Vector3f wheelAxleCS = new Vector3f(); // const or modified by steering
+ public float suspensionRestLength1; // const
+ public float maxSuspensionTravelCm;
+ public float wheelsRadius; // const
+ public float suspensionStiffness; // const
+ public float wheelsDampingCompression; // const
+ public float wheelsDampingRelaxation; // const
+ public float frictionSlip;
+ public float steering;
+ public float rotation;
+ public float deltaRotation;
+ public float rollInfluence;
+
+ public float engineForce;
+
+ public float brake;
+
+ public boolean bIsFrontWheel;
+
+ public Object clientInfo; // can be used to store pointer to sync transforms...
+
+ public float clippedInvContactDotSuspension;
+ public float suspensionRelativeVelocity;
+ // calculated by suspension
+ public float wheelsSuspensionForce;
+ public float skidInfo;
+
+ public WheelInfo(WheelInfoConstructionInfo ci) {
+ suspensionRestLength1 = ci.suspensionRestLength;
+ maxSuspensionTravelCm = ci.maxSuspensionTravelCm;
+
+ wheelsRadius = ci.wheelRadius;
+ suspensionStiffness = ci.suspensionStiffness;
+ wheelsDampingCompression = ci.wheelsDampingCompression;
+ wheelsDampingRelaxation = ci.wheelsDampingRelaxation;
+ chassisConnectionPointCS.set(ci.chassisConnectionCS);
+ wheelDirectionCS.set(ci.wheelDirectionCS);
+ wheelAxleCS.set(ci.wheelAxleCS);
+ frictionSlip = ci.frictionSlip;
+ steering = 0f;
+ engineForce = 0f;
+ rotation = 0f;
+ deltaRotation = 0f;
+ brake = 0f;
+ rollInfluence = 0.1f;
+ bIsFrontWheel = ci.bIsFrontWheel;
+ }
+
+ public float getSuspensionRestLength() {
+ return suspensionRestLength1;
+ }
+
+ public void updateWheel(RigidBody chassis, RaycastInfo raycastInfo) {
+ stack.vectors.push();
+ try {
+ if (raycastInfo.isInContact) {
+ float project = raycastInfo.contactNormalWS.dot(raycastInfo.wheelDirectionWS);
+ Vector3f chassis_velocity_at_contactPoint = stack.vectors.get();
+ Vector3f relpos = stack.vectors.get();
+ relpos.sub(raycastInfo.contactPointWS, chassis.getCenterOfMassPosition());
+ chassis_velocity_at_contactPoint.set(chassis.getVelocityInLocalPoint(relpos));
+ float projVel = raycastInfo.contactNormalWS.dot(chassis_velocity_at_contactPoint);
+ if (project >= -0.1f) {
+ suspensionRelativeVelocity = 0f;
+ clippedInvContactDotSuspension = 1f / 0.1f;
+ }
+ else {
+ float inv = -1f / project;
+ suspensionRelativeVelocity = projVel * inv;
+ clippedInvContactDotSuspension = inv;
+ }
+ }
+ else {
+ // Not in contact : position wheel in a nice (rest length) position
+ raycastInfo.suspensionLength = getSuspensionRestLength();
+ suspensionRelativeVelocity = 0f;
+ raycastInfo.contactNormalWS.negate(raycastInfo.wheelDirectionWS);
+ clippedInvContactDotSuspension = 1f;
+ }
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ public static class RaycastInfo {
+ // set by raycaster
+ public final Vector3f contactNormalWS = new Vector3f(); // contactnormal
+ public final Vector3f contactPointWS = new Vector3f(); // raycast hitpoint
+ public float suspensionLength;
+ public final Vector3f hardPointWS = new Vector3f(); // raycast starting point
+ public final Vector3f wheelDirectionWS = new Vector3f(); // direction in worldspace
+ public final Vector3f wheelAxleWS = new Vector3f(); // axle in worldspace
+ public boolean isInContact;
+ public Object groundObject; // could be general void* ptr
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/dynamics/vehicle/WheelInfoConstructionInfo.java b/src/jbullet/src/javabullet/dynamics/vehicle/WheelInfoConstructionInfo.java
new file mode 100644
index 0000000..2824bdb
--- /dev/null
+++ b/src/jbullet/src/javabullet/dynamics/vehicle/WheelInfoConstructionInfo.java
@@ -0,0 +1,47 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.dynamics.vehicle;
+
+import javax.vecmath.Vector3f;
+
+/**
+ *
+ * @author jezek2
+ */
+public class WheelInfoConstructionInfo {
+
+ public final Vector3f chassisConnectionCS = new Vector3f();
+ public final Vector3f wheelDirectionCS = new Vector3f();
+ public final Vector3f wheelAxleCS = new Vector3f();
+ public float suspensionRestLength;
+ public float maxSuspensionTravelCm;
+ public float wheelRadius;
+
+ public float suspensionStiffness;
+ public float wheelsDampingCompression;
+ public float wheelsDampingRelaxation;
+ public float frictionSlip;
+ public boolean bIsFrontWheel;
+
+}
diff --git a/src/jbullet/src/javabullet/linearmath/AabbUtil2.java b/src/jbullet/src/javabullet/linearmath/AabbUtil2.java
new file mode 100644
index 0000000..978dcdf
--- /dev/null
+++ b/src/jbullet/src/javabullet/linearmath/AabbUtil2.java
@@ -0,0 +1,139 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.linearmath;
+
+import javabullet.BulletStack;
+import javax.vecmath.Vector3f;
+
+/**
+ *
+ * @author jezek2
+ */
+public class AabbUtil2 {
+
+ public static int outcode(Vector3f p, Vector3f halfExtent) {
+ return (p.x < -halfExtent.x ? 0x01 : 0x0) |
+ (p.x > halfExtent.x ? 0x08 : 0x0) |
+ (p.y < -halfExtent.y ? 0x02 : 0x0) |
+ (p.y > halfExtent.y ? 0x10 : 0x0) |
+ (p.z < -halfExtent.z ? 0x4 : 0x0) |
+ (p.z > halfExtent.z ? 0x20 : 0x0);
+ }
+
+ public static boolean rayAabb(Vector3f rayFrom, Vector3f rayTo, Vector3f aabbMin, Vector3f aabbMax, float[] param, Vector3f normal) {
+ BulletStack stack = BulletStack.get();
+
+ stack.vectors.push();
+ try {
+ Vector3f aabbHalfExtent = stack.vectors.get();
+ Vector3f aabbCenter = stack.vectors.get();
+ Vector3f source = stack.vectors.get();
+ Vector3f target = stack.vectors.get();
+ Vector3f r = stack.vectors.get();
+ Vector3f hitNormal = stack.vectors.get();
+
+ aabbHalfExtent.sub(aabbMax, aabbMin);
+ aabbHalfExtent.scale(0.5f);
+
+ aabbCenter.add(aabbMax, aabbMin);
+ aabbCenter.scale(0.5f);
+
+ source.sub(rayFrom, aabbCenter);
+ target.sub(rayTo, aabbCenter);
+
+ int sourceOutcode = outcode(source, aabbHalfExtent);
+ int targetOutcode = outcode(target, aabbHalfExtent);
+ if ((sourceOutcode & targetOutcode) == 0x0) {
+ float lambda_enter = 0f;
+ float lambda_exit = param[0];
+ r.sub(target, source);
+
+ float normSign = 1f;
+ hitNormal.set(0f, 0f, 0f);
+ int bit = 1;
+
+ for (int j = 0; j < 2; j++) {
+ for (int i = 0; i != 3; ++i) {
+ if ((sourceOutcode & bit) != 0) {
+ float lambda = (-VectorUtil.getCoord(source, i) - VectorUtil.getCoord(aabbHalfExtent, i) * normSign) / VectorUtil.getCoord(r, i);
+ if (lambda_enter <= lambda) {
+ lambda_enter = lambda;
+ hitNormal.set(0f, 0f, 0f);
+ VectorUtil.setCoord(hitNormal, i, normSign);
+ }
+ }
+ else if ((targetOutcode & bit) != 0) {
+ float lambda = (-VectorUtil.getCoord(source, i) - VectorUtil.getCoord(aabbHalfExtent, i) * normSign) / VectorUtil.getCoord(r, i);
+ //btSetMin(lambda_exit, lambda);
+ lambda_exit = Math.min(lambda_exit, lambda);
+ }
+ bit <<= 1;
+ }
+ normSign = -1f;
+ }
+ if (lambda_enter <= lambda_exit) {
+ param[0] = lambda_enter;
+ normal.set(hitNormal);
+ return true;
+ }
+ }
+ return false;
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ /**
+ * Conservative test for overlap between two aabbs.
+ */
+ public static boolean testAabbAgainstAabb2(Vector3f aabbMin1, Vector3f aabbMax1, Vector3f aabbMin2, Vector3f aabbMax2) {
+ boolean overlap = true;
+ overlap = (aabbMin1.x > aabbMax2.x || aabbMax1.x < aabbMin2.x) ? false : overlap;
+ overlap = (aabbMin1.z > aabbMax2.z || aabbMax1.z < aabbMin2.z) ? false : overlap;
+ overlap = (aabbMin1.y > aabbMax2.y || aabbMax1.y < aabbMin2.y) ? false : overlap;
+ return overlap;
+ }
+
+ /**
+ * Conservative test for overlap between triangle and aabb.
+ */
+ public static boolean testTriangleAgainstAabb2(Vector3f[] vertices, Vector3f aabbMin, Vector3f aabbMax) {
+ Vector3f p1 = vertices[0];
+ Vector3f p2 = vertices[1];
+ Vector3f p3 = vertices[2];
+
+ if (Math.min(Math.min(p1.x, p2.x), p3.x) > aabbMax.x) return false;
+ if (Math.max(Math.max(p1.x, p2.x), p3.x) < aabbMin.x) return false;
+
+ if (Math.min(Math.min(p1.z, p2.z), p3.z) > aabbMax.z) return false;
+ if (Math.max(Math.max(p1.z, p2.z), p3.z) < aabbMin.z) return false;
+
+ if (Math.min(Math.min(p1.y, p2.y), p3.y) > aabbMax.y) return false;
+ if (Math.max(Math.max(p1.y, p2.y), p3.y) < aabbMin.y) return false;
+
+ return true;
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/linearmath/Clock.java b/src/jbullet/src/javabullet/linearmath/Clock.java
new file mode 100644
index 0000000..14207bb
--- /dev/null
+++ b/src/jbullet/src/javabullet/linearmath/Clock.java
@@ -0,0 +1,56 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.linearmath;
+
+/**
+ *
+ * @author jezek2
+ */
+public class Clock {
+
+ private long mStartTime;
+
+ public Clock() {
+ reset();
+ }
+
+ public void reset() {
+ mStartTime = System.currentTimeMillis();
+ }
+
+ /**
+ * Returns the time in ms since the last call to reset or since the Clock was created.
+ */
+ public long getTimeMilliseconds() {
+ return System.currentTimeMillis() - mStartTime;
+ }
+
+ /**
+ * Returns the time in us since the last call to reset or since the Clock was created.
+ */
+ public long getTimeMicroseconds() {
+ return getTimeMilliseconds() * 1000;
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/linearmath/DebugDrawModes.java b/src/jbullet/src/javabullet/linearmath/DebugDrawModes.java
new file mode 100644
index 0000000..e159a3d
--- /dev/null
+++ b/src/jbullet/src/javabullet/linearmath/DebugDrawModes.java
@@ -0,0 +1,46 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.linearmath;
+
+/**
+ *
+ * @author jezek2
+ */
+public class DebugDrawModes {
+
+ public static final int NO_DEBUG = 0;
+ public static final int DRAW_WIREFRAME = 1;
+ public static final int DRAW_AABB = 2;
+ public static final int DRAW_FEATURES_TEXT = 4;
+ public static final int DRAW_CONTACT_POINTS = 8;
+ public static final int NO_DEACTIVATION = 16;
+ public static final int NO_HELP_TEXT = 32;
+ public static final int DRAW_TEXT = 64;
+ public static final int PROFILE_TIMINGS = 128;
+ public static final int ENABLE_SAT_COMPARISON = 256;
+ public static final int DISABLE_BULLET_LCP = 512;
+ public static final int ENABLE_CCD = 1024;
+ public static final int MAX_DEBUG_DRAW_MODE = 1025;
+
+}
diff --git a/src/jbullet/src/javabullet/linearmath/DefaultMotionState.java b/src/jbullet/src/javabullet/linearmath/DefaultMotionState.java
new file mode 100644
index 0000000..e3bbe7e
--- /dev/null
+++ b/src/jbullet/src/javabullet/linearmath/DefaultMotionState.java
@@ -0,0 +1,65 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.linearmath;
+
+/**
+ * DefaultMotionState provides a common implementation to synchronize world transforms with offsets.
+ *
+ * @author jezek2
+ */
+public class DefaultMotionState implements MotionState {
+
+ public final Transform graphicsWorldTrans = new Transform();
+ public final Transform centerOfMassOffset = new Transform();
+ public final Transform startWorldTrans = new Transform();
+
+ public DefaultMotionState() {
+ graphicsWorldTrans.setIdentity();
+ centerOfMassOffset.setIdentity();
+ startWorldTrans.setIdentity();
+ }
+
+ public DefaultMotionState(Transform startTrans) {
+ this.graphicsWorldTrans.set(startTrans);
+ centerOfMassOffset.setIdentity();
+ this.startWorldTrans.set(startTrans);
+ }
+
+ public DefaultMotionState(Transform startTrans, Transform centerOfMassOffset) {
+ this.graphicsWorldTrans.set(startTrans);
+ this.centerOfMassOffset.set(centerOfMassOffset);
+ this.startWorldTrans.set(startTrans);
+ }
+
+ public void getWorldTransform(Transform centerOfMassWorldTrans) {
+ centerOfMassWorldTrans.inverse(centerOfMassOffset);
+ centerOfMassWorldTrans.mul(graphicsWorldTrans);
+ }
+
+ public void setWorldTransform(Transform centerOfMassWorldTrans) {
+ graphicsWorldTrans.set(centerOfMassWorldTrans);
+ graphicsWorldTrans.mul(centerOfMassOffset);
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/linearmath/GeometryUtil.java b/src/jbullet/src/javabullet/linearmath/GeometryUtil.java
new file mode 100644
index 0000000..354203c
--- /dev/null
+++ b/src/jbullet/src/javabullet/linearmath/GeometryUtil.java
@@ -0,0 +1,185 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.linearmath;
+
+import java.util.List;
+import javabullet.BulletStack;
+import javax.vecmath.Vector3f;
+import javax.vecmath.Vector4f;
+
+/**
+ *
+ * @author jezek2
+ */
+public class GeometryUtil {
+
+ public static boolean isPointInsidePlanes(List<Vector4f> planeEquations, Vector3f point, float margin) {
+ int numbrushes = planeEquations.size();
+ for (int i = 0; i < numbrushes; i++) {
+ Vector4f N1 = planeEquations.get(i);
+ float dist = VectorUtil.dot3(N1, point) + N1.w - margin;
+ if (dist > 0f) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean areVerticesBehindPlane(Vector4f planeNormal, List<Vector3f> vertices, float margin) {
+ int numvertices = vertices.size();
+ for (int i = 0; i < numvertices; i++) {
+ Vector3f N1 = vertices.get(i);
+ float dist = VectorUtil.dot3(planeNormal, N1) + planeNormal.w - margin;
+ if (dist > 0f) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean notExist(Vector4f planeEquation, List<Vector4f> planeEquations) {
+ int numbrushes = planeEquations.size();
+ for (int i = 0; i < numbrushes; i++) {
+ Vector4f N1 = planeEquations.get(i);
+ if (VectorUtil.dot3(planeEquation, N1) > 0.999f) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static void getPlaneEquationsFromVertices(List<Vector3f> vertices, List<Vector4f> planeEquationsOut) {
+ BulletStack stack = BulletStack.get();
+
+ stack.vectors.push();
+ stack.vectors4.push();
+ try {
+ Vector4f planeEquation = stack.vectors4.get();
+ Vector3f edge0 = stack.vectors.get(), edge1 = stack.vectors.get();
+ Vector3f tmp = stack.vectors.get();
+
+ int numvertices = vertices.size();
+ // brute force:
+ for (int i = 0; i < numvertices; i++) {
+ Vector3f N1 = vertices.get(i);
+
+ for (int j = i + 1; j < numvertices; j++) {
+ Vector3f N2 = vertices.get(j);
+
+ for (int k = j + 1; k < numvertices; k++) {
+ Vector3f N3 = vertices.get(k);
+
+ edge0.sub(N2, N1);
+ edge1.sub(N3, N1);
+ float normalSign = 1f;
+ for (int ww = 0; ww < 2; ww++) {
+ tmp.cross(edge0, edge1);
+ planeEquation.x = normalSign * tmp.x;
+ planeEquation.y = normalSign * tmp.y;
+ planeEquation.z = normalSign * tmp.z;
+
+ if (VectorUtil.lengthSquared3(planeEquation) > 0.0001f) {
+ VectorUtil.normalize3(planeEquation);
+ if (notExist(planeEquation, planeEquationsOut)) {
+ planeEquation.w = -VectorUtil.dot3(planeEquation, N1);
+
+ // check if inside, and replace supportingVertexOut if needed
+ if (areVerticesBehindPlane(planeEquation, vertices, 0.01f)) {
+ planeEquationsOut.add(planeEquation);
+ }
+ }
+ }
+ normalSign = -1f;
+ }
+ }
+ }
+ }
+ }
+ finally {
+ stack.vectors.pop();
+ stack.vectors4.pop();
+ }
+ }
+
+ public static void getVerticesFromPlaneEquations(List<Vector4f> planeEquations, List<Vector3f> verticesOut) {
+ BulletStack stack = BulletStack.get();
+
+ stack.vectors.push();
+ try {
+ Vector3f n2n3 = stack.vectors.get();
+ Vector3f n3n1 = stack.vectors.get();
+ Vector3f n1n2 = stack.vectors.get();
+ Vector3f potentialVertex = stack.vectors.get();
+
+ int numbrushes = planeEquations.size();
+ // brute force:
+ for (int i = 0; i < numbrushes; i++) {
+ Vector4f N1 = planeEquations.get(i);
+
+ for (int j = i + 1; j < numbrushes; j++) {
+ Vector4f N2 = planeEquations.get(j);
+
+ for (int k = j + 1; k < numbrushes; k++) {
+ Vector4f N3 = planeEquations.get(k);
+
+ VectorUtil.cross3(n2n3, N2, N3);
+ VectorUtil.cross3(n3n1, N3, N1);
+ VectorUtil.cross3(n1n2, N1, N2);
+
+ if ((n2n3.lengthSquared() > 0.0001f) &&
+ (n3n1.lengthSquared() > 0.0001f) &&
+ (n1n2.lengthSquared() > 0.0001f)) {
+ // point P out of 3 plane equations:
+
+ // d1 ( N2 * N3 ) + d2 ( N3 * N1 ) + d3 ( N1 * N2 )
+ // P = -------------------------------------------------------------------------
+ // N1 . ( N2 * N3 )
+
+ float quotient = VectorUtil.dot3(N1, n2n3);
+ if (Math.abs(quotient) > 0.000001f) {
+ quotient = -1f / quotient;
+ n2n3.scale(N1.w);
+ n3n1.scale(N2.w);
+ n1n2.scale(N3.w);
+ potentialVertex.set(n2n3);
+ potentialVertex.add(n3n1);
+ potentialVertex.add(n1n2);
+ potentialVertex.scale(quotient);
+
+ // check if inside, and replace supportingVertexOut if needed
+ if (isPointInsidePlanes(planeEquations, potentialVertex, 0.01f)) {
+ verticesOut.add(potentialVertex);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/linearmath/IDebugDraw.java b/src/jbullet/src/javabullet/linearmath/IDebugDraw.java
new file mode 100644
index 0000000..407e4c6
--- /dev/null
+++ b/src/jbullet/src/javabullet/linearmath/IDebugDraw.java
@@ -0,0 +1,86 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.linearmath;
+
+import javabullet.BulletStack;
+import javax.vecmath.Vector3f;
+
+/**
+ *
+ * @author jezek2
+ */
+public abstract class IDebugDraw {
+
+ protected final BulletStack stack = BulletStack.get();
+
+ public abstract void drawLine(Vector3f from, Vector3f to, Vector3f color);
+
+ public abstract void drawContactPoint(Vector3f PointOnB, Vector3f normalOnB, float distance, int lifeTime, Vector3f color);
+
+ public abstract void reportErrorWarning(String warningString);
+
+ public abstract void draw3dText(Vector3f location, String textString);
+
+ public abstract void setDebugMode(int debugMode);
+
+ public abstract int getDebugMode();
+
+ public void drawAabb(Vector3f from, Vector3f to, Vector3f color) {
+ stack.vectors.push();
+ try {
+ Vector3f halfExtents = stack.vectors.get(to);
+ halfExtents.sub(from);
+ halfExtents.scale(0.5f);
+
+ Vector3f center = stack.vectors.get(to);
+ center.add(from);
+ center.scale(0.5f);
+
+ int i, j;
+
+ Vector3f edgecoord = stack.vectors.get(1f, 1f, 1f), pa = stack.vectors.get(), pb = stack.vectors.get();
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 3; j++) {
+ pa.set(edgecoord.x * halfExtents.x, edgecoord.y * halfExtents.y, edgecoord.z * halfExtents.z);
+ pa.add(center);
+
+ int othercoord = j % 3;
+
+ VectorUtil.mulCoord(edgecoord, othercoord, -1f);
+ pb.set(edgecoord.x * halfExtents.x, edgecoord.y * halfExtents.y, edgecoord.z * halfExtents.z);
+ pb.add(center);
+
+ drawLine(pa, pb, color);
+ }
+ edgecoord.set(-1f, -1f, -1f);
+ if (i < 3) {
+ VectorUtil.mulCoord(edgecoord, i, -1f);
+ }
+ }
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+}
diff --git a/src/jbullet/src/javabullet/linearmath/MatrixUtil.java b/src/jbullet/src/javabullet/linearmath/MatrixUtil.java
new file mode 100644
index 0000000..f5c9774
--- /dev/null
+++ b/src/jbullet/src/javabullet/linearmath/MatrixUtil.java
@@ -0,0 +1,208 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.linearmath;
+
+import javabullet.BulletStack;
+import javax.vecmath.Matrix3f;
+import javax.vecmath.Quat4f;
+import javax.vecmath.Vector3f;
+
+/**
+ *
+ * @author jezek2
+ */
+public class MatrixUtil {
+
+ public static void scale(Matrix3f dest, Matrix3f mat, Vector3f s) {
+ dest.m00 = mat.m00 * s.x; dest.m01 = mat.m01 * s.y; dest.m02 = mat.m02 * s.z;
+ dest.m10 = mat.m10 * s.x; dest.m11 = mat.m11 * s.y; dest.m12 = mat.m12 * s.z;
+ dest.m20 = mat.m20 * s.x; dest.m21 = mat.m21 * s.y; dest.m22 = mat.m22 * s.z;
+ }
+
+ public static void absolute(Matrix3f mat) {
+ mat.m00 = Math.abs(mat.m00);
+ mat.m01 = Math.abs(mat.m01);
+ mat.m02 = Math.abs(mat.m02);
+ mat.m10 = Math.abs(mat.m10);
+ mat.m11 = Math.abs(mat.m11);
+ mat.m12 = Math.abs(mat.m12);
+ mat.m20 = Math.abs(mat.m20);
+ mat.m21 = Math.abs(mat.m21);
+ mat.m22 = Math.abs(mat.m22);
+ }
+
+ public static void setFromOpenGLSubMatrix(Matrix3f mat, float[] m) {
+ mat.m00 = m[0]; mat.m01 = m[4]; mat.m02 = m[8];
+ mat.m10 = m[1]; mat.m11 = m[5]; mat.m12 = m[9];
+ mat.m20 = m[2]; mat.m21 = m[6]; mat.m22 = m[10];
+ }
+
+ public static void getOpenGLSubMatrix(Matrix3f mat, float[] m) {
+ m[0] = mat.m00;
+ m[1] = mat.m10;
+ m[2] = mat.m20;
+ m[3] = 0f;
+ m[4] = mat.m01;
+ m[5] = mat.m11;
+ m[6] = mat.m21;
+ m[7] = 0f;
+ m[8] = mat.m02;
+ m[9] = mat.m12;
+ m[10] = mat.m22;
+ m[11] = 0f;
+ }
+
+ /**
+ * setEulerZYX
+ *
+ * @param euler a const reference to a btVector3 of euler angles
+ * These angles are used to produce a rotation matrix. The euler
+ * angles are applied in ZYX order. I.e a vector is first rotated
+ * about X then Y and then Z
+ */
+ public static void setEulerZYX(Matrix3f mat, float eulerX, float eulerY, float eulerZ) {
+ float ci = (float) Math.cos(eulerX);
+ float cj = (float) Math.cos(eulerY);
+ float ch = (float) Math.cos(eulerZ);
+ float si = (float) Math.sin(eulerX);
+ float sj = (float) Math.sin(eulerY);
+ float sh = (float) Math.sin(eulerZ);
+ float cc = ci * ch;
+ float cs = ci * sh;
+ float sc = si * ch;
+ float ss = si * sh;
+
+ mat.setRow(0, cj * ch, sj * sc - cs, sj * cc + ss);
+ mat.setRow(1, cj * sh, sj * ss + cc, sj * cs - sc);
+ mat.setRow(2, -sj, cj * si, cj * ci);
+ }
+
+ private static float tdotx(Matrix3f mat, Vector3f vec) {
+ return mat.m00 * vec.x + mat.m10 * vec.y + mat.m20 * vec.z;
+ }
+
+ private static float tdoty(Matrix3f mat, Vector3f vec) {
+ return mat.m01 * vec.x + mat.m11 * vec.y + mat.m21 * vec.z;
+ }
+
+ private static float tdotz(Matrix3f mat, Vector3f vec) {
+ return mat.m02 * vec.x + mat.m12 * vec.y + mat.m22 * vec.z;
+ }
+
+ public static void transposeTransform(Vector3f dest, Vector3f vec, Matrix3f mat) {
+ float x = tdotx(mat, vec);
+ float y = tdoty(mat, vec);
+ float z = tdotz(mat, vec);
+ dest.x = x;
+ dest.y = y;
+ dest.z = z;
+ }
+
+ public static void setRotation(Matrix3f dest, Quat4f q) {
+ float d = q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w;
+ assert (d != 0f);
+ float s = 2f / d;
+ float xs = q.x * s, ys = q.y * s, zs = q.z * s;
+ float wx = q.w * xs, wy = q.w * ys, wz = q.w * zs;
+ float xx = q.x * xs, xy = q.x * ys, xz = q.x * zs;
+ float yy = q.y * ys, yz = q.y * zs, zz = q.z * zs;
+ dest.m00 = 1f - (yy + zz);
+ dest.m01 = xy - wz;
+ dest.m02 = xz + wy;
+ dest.m10 = xy + wz;
+ dest.m11 = 1f - (xx + zz);
+ dest.m12 = yz - wx;
+ dest.m20 = xz - wy;
+ dest.m21 = yz + wx;
+ dest.m22 = 1f - (xx + yy);
+ }
+
+ public static void getRotation(Matrix3f mat, Quat4f dest) {
+ BulletStack stack = BulletStack.get();
+
+ float trace = mat.m00 + mat.m11 + mat.m22;
+ float[] temp = stack.floatArrays.getFixed(4);
+
+ if (trace > 0f) {
+ float s = (float) Math.sqrt(trace + 1f);
+ temp[3] = (s * 0.5f);
+ s = 0.5f / s;
+
+ temp[0] = ((mat.m21 - mat.m12) * s);
+ temp[1] = ((mat.m02 - mat.m20) * s);
+ temp[2] = ((mat.m10 - mat.m01) * s);
+ }
+ else {
+ int i = mat.m00 < mat.m11 ? (mat.m11 < mat.m22 ? 2 : 1) : (mat.m00 < mat.m22 ? 2 : 0);
+ int j = (i + 1) % 3;
+ int k = (i + 2) % 3;
+
+ float s = (float) Math.sqrt(mat.getElement(i, i) - mat.getElement(j, j) - mat.getElement(k, k) + 1f);
+ temp[i] = s * 0.5f;
+ s = 0.5f / s;
+
+ temp[3] = (mat.getElement(k, j) - mat.getElement(j, k)) * s;
+ temp[j] = (mat.getElement(j, i) + mat.getElement(i, j)) * s;
+ temp[k] = (mat.getElement(k, i) + mat.getElement(i, k)) * s;
+ }
+ dest.set(temp[0], temp[1], temp[2], temp[3]);
+
+ stack.floatArrays.release(temp);
+ }
+
+ private static float cofac(Matrix3f mat, int r1, int c1, int r2, int c2) {
+ return mat.getElement(r1, c1) * mat.getElement(r2, c2) - mat.getElement(r1, c2) * mat.getElement(r2, c1);
+ }
+
+ public static void invert(Matrix3f mat) {
+ float co_x = cofac(mat, 1, 1, 2, 2);
+ float co_y = cofac(mat, 1, 2, 2, 0);
+ float co_z = cofac(mat, 1, 0, 2, 1);
+
+ float det = mat.m00*co_x + mat.m01*co_y + mat.m02*co_z;
+ assert (det != 0f);
+
+ float s = 1f / det;
+ float m00 = co_x * s;
+ float m01 = cofac(mat, 0, 2, 2, 1) * s;
+ float m02 = cofac(mat, 0, 1, 1, 2) * s;
+ float m10 = co_y * s;
+ float m11 = cofac(mat, 0, 0, 2, 2) * s;
+ float m12 = cofac(mat, 0, 2, 1, 0) * s;
+ float m20 = co_z * s;
+ float m21 = cofac(mat, 0, 1, 2, 0) * s;
+ float m22 = cofac(mat, 0, 0, 1, 1) * s;
+
+ mat.m00 = m00;
+ mat.m01 = m01;
+ mat.m02 = m02;
+ mat.m10 = m10;
+ mat.m11 = m11;
+ mat.m12 = m12;
+ mat.m20 = m20;
+ mat.m21 = m21;
+ mat.m22 = m22;
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/linearmath/MiscUtil.java b/src/jbullet/src/javabullet/linearmath/MiscUtil.java
new file mode 100644
index 0000000..e522fb2
--- /dev/null
+++ b/src/jbullet/src/javabullet/linearmath/MiscUtil.java
@@ -0,0 +1,156 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.linearmath;
+
+import java.util.Comparator;
+import java.util.List;
+import javabullet.util.FloatArrayList;
+import javabullet.util.IntArrayList;
+
+/**
+ *
+ * @author jezek2
+ */
+public class MiscUtil {
+
+ public static int getListCapacityForHash(List<?> list) {
+ return getListCapacityForHash(list.size());
+ }
+
+ public static int getListCapacityForHash(int size) {
+ int n = 2;
+ while (n < size) {
+ n <<= 1;
+ }
+ return n;
+ }
+
+ public static <T> void ensureIndex(List<T> list, int index, T value) {
+ while (list.size() <= index) {
+ list.add(value);
+ }
+ }
+
+ public static void resize(IntArrayList list, int size, int value) {
+ while (list.size() < size) {
+ list.add(value);
+ }
+
+ while (list.size() > size) {
+ list.remove(list.size() - 1);
+ }
+ }
+
+ public static void resize(FloatArrayList list, int size, float value) {
+ while (list.size() < size) {
+ list.add(value);
+ }
+
+ while (list.size() > size) {
+ list.remove(list.size() - 1);
+ }
+ }
+
+ public static <T> void resize(List<T> list, int size, Class<T> valueCls) {
+ try {
+ while (list.size() < size) {
+ list.add(valueCls != null? valueCls.newInstance() : null);
+ }
+
+ while (list.size() > size) {
+ list.remove(list.size() - 1);
+ }
+ }
+ catch (IllegalAccessException e) {
+ throw new IllegalStateException(e);
+ }
+ catch (InstantiationException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public static <T> int indexOf(T[] array, T obj) {
+ for (int i=0; i<array.length; i++) {
+ if (array[i] == obj) return i;
+ }
+ return -1;
+ }
+
+ public static float GEN_clamped(float a, float lb, float ub) {
+ return a < lb ? lb : (ub < a ? ub : a);
+ }
+
+ /**
+ * Heap sort from http://www.csse.monash.edu.au/~lloyd/tildeAlgDS/Sort/Heap/
+ */
+ private static <T> void downHeap(List<T> pArr, int k, int n, Comparator<T> comparator) {
+ /* PRE: a[k+1..N] is a heap */
+ /* POST: a[k..N] is a heap */
+
+ T temp = pArr.get(k - 1);
+ /* k has child(s) */
+ while (k <= n / 2) {
+ int child = 2 * k;
+
+ if ((child < n) && comparator.compare(pArr.get(child - 1), pArr.get(child)) < 0) {
+ child++;
+ }
+ /* pick larger child */
+ if (comparator.compare(temp, pArr.get(child - 1)) < 0) {
+ /* move child up */
+ pArr.set(k - 1, pArr.get(child - 1));
+ k = child;
+ }
+ else {
+ break;
+ }
+ }
+ pArr.set(k - 1, temp);
+ }
+
+ public static <T> void heapSort(List<T> list, Comparator<T> comparator) {
+ /* sort a[0..N-1], N.B. 0 to N-1 */
+ int k;
+ int n = list.size();
+ for (k = n / 2; k > 0; k--) {
+ downHeap(list, k, n, comparator);
+ }
+
+ /* a[1..N] is now a heap */
+ while (n >= 1) {
+ swap(list, 0, n - 1); /* largest of a[0..n-1] */
+
+ n = n - 1;
+ /* restore a[1..i-1] heap */
+ downHeap(list, 1, n, comparator);
+ }
+ }
+
+ private static <T> void swap(List<T> list, int index0, int index1) {
+ T temp = list.get(index0);
+ list.set(index0, list.get(index1));
+ list.set(index1, temp);
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/linearmath/MotionState.java b/src/jbullet/src/javabullet/linearmath/MotionState.java
new file mode 100644
index 0000000..964df4a
--- /dev/null
+++ b/src/jbullet/src/javabullet/linearmath/MotionState.java
@@ -0,0 +1,41 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.linearmath;
+
+/**
+ * MotionState allows the dynamics world to synchronize the updated world transforms with graphics.
+ * For optimizations, potentially only moving objects get synchronized (using setWorldPosition/setWorldOrientation).
+ *
+ * @author jezek2
+ */
+public interface MotionState {
+
+ public void getWorldTransform(Transform worldTrans);
+
+ /**
+ * Note: Bullet only calls the update of worldtransform for active objects.
+ */
+ public void setWorldTransform(Transform worldTrans);
+
+}
diff --git a/src/jbullet/src/javabullet/linearmath/QuaternionUtil.java b/src/jbullet/src/javabullet/linearmath/QuaternionUtil.java
new file mode 100644
index 0000000..65fece4
--- /dev/null
+++ b/src/jbullet/src/javabullet/linearmath/QuaternionUtil.java
@@ -0,0 +1,104 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.linearmath;
+
+import javabullet.BulletGlobals;
+import javabullet.BulletStack;
+import javax.vecmath.Quat4f;
+import javax.vecmath.Vector3f;
+
+/**
+ *
+ * @author jezek2
+ */
+public class QuaternionUtil {
+
+ public static float getAngle(Quat4f q) {
+ float s = 2f * (float) Math.acos(q.w);
+ return s;
+ }
+
+ public static void setRotation(Quat4f q, Vector3f axis, float angle) {
+ float d = axis.length();
+ assert (d != 0f);
+ float s = (float)Math.sin(angle * 0.5f) / d;
+ q.set(axis.x * s, axis.y * s, axis.z * s, (float) Math.cos(angle * 0.5f));
+ }
+
+ // Game Programming Gems 2.10. make sure v0,v1 are normalized
+ public static Quat4f shortestArcQuat(Vector3f v0, Vector3f v1) {
+ BulletStack stack = BulletStack.get();
+
+ stack.vectors.push();
+ stack.quats.push();
+ try {
+ Vector3f c = stack.vectors.get();
+ c.cross(v0, v1);
+ float d = v0.dot(v1);
+
+ if (d < -1.0 + BulletGlobals.FLT_EPSILON) {
+ // just pick any vector
+ return stack.quats.returning(stack.quats.get(0.0f, 1.0f, 0.0f, 0.0f));
+ }
+
+ float s = (float) Math.sqrt((1.0f + d) * 2.0f);
+ float rs = 1.0f / s;
+
+ return stack.quats.returning(stack.quats.get(c.x * rs, c.y * rs, c.z * rs, s * 0.5f));
+ }
+ finally {
+ stack.vectors.pop();
+ stack.quats.pop();
+ }
+ }
+
+ public static void mul(Quat4f q, Vector3f w) {
+ float rx = q.w * w.x + q.y * w.z - q.z * w.y;
+ float ry = q.w * w.y + q.z * w.x - q.x * w.z;
+ float rz = q.w * w.z + q.x * w.y - q.y * w.x;
+ float rw = -q.x * w.x - q.y * w.y - q.z * w.z;
+ q.set(rx, ry, rz, rw);
+ }
+
+ public static Vector3f quatRotate(Quat4f rotation, Vector3f v) {
+ BulletStack stack = BulletStack.get();
+
+ stack.vectors.push();
+ stack.quats.push();
+ try {
+ Quat4f q = stack.quats.get(rotation);
+ QuaternionUtil.mul(q, v);
+
+ Quat4f tmp = stack.quats.get();
+ tmp.inverse(rotation);
+ q.mul(tmp);
+ return stack.vectors.returning(stack.vectors.get(q.x, q.y, q.z));
+ }
+ finally {
+ stack.vectors.pop();
+ stack.quats.pop();
+ }
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/linearmath/ScalarUtil.java b/src/jbullet/src/javabullet/linearmath/ScalarUtil.java
new file mode 100644
index 0000000..c898ebb
--- /dev/null
+++ b/src/jbullet/src/javabullet/linearmath/ScalarUtil.java
@@ -0,0 +1,58 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.linearmath;
+
+import javabullet.BulletGlobals;
+
+/**
+ *
+ * @author jezek2
+ */
+public class ScalarUtil {
+
+ public static float fsel(float a, float b, float c) {
+ return a >= 0 ? b : c;
+ }
+
+ public static boolean fuzzyZero(float x) {
+ return Math.abs(x) < BulletGlobals.FLT_EPSILON;
+ }
+
+ public static float atan2Fast(float y, float x) {
+ float coeff_1 = BulletGlobals.SIMD_PI / 4.0f;
+ float coeff_2 = 3.0f * coeff_1;
+ float abs_y = Math.abs(y);
+ float angle;
+ if (x >= 0.0f) {
+ float r = (x - abs_y) / (x + abs_y);
+ angle = coeff_1 - coeff_1 * r;
+ }
+ else {
+ float r = (x + abs_y) / (abs_y - x);
+ angle = coeff_2 - coeff_1 * r;
+ }
+ return (y < 0.0f) ? -angle : angle;
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/linearmath/Transform.java b/src/jbullet/src/javabullet/linearmath/Transform.java
new file mode 100644
index 0000000..1b44958
--- /dev/null
+++ b/src/jbullet/src/javabullet/linearmath/Transform.java
@@ -0,0 +1,148 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.linearmath;
+
+import javabullet.BulletStack;
+import javax.vecmath.Matrix3f;
+import javax.vecmath.Quat4f;
+import javax.vecmath.Vector3f;
+
+/**
+ * Transform supports rigid transforms (only translation and rotation, no scaling/shear).
+ *
+ * @author jezek2
+ */
+public class Transform {
+
+ protected BulletStack stack;
+
+ public final Matrix3f basis = new Matrix3f();
+ public final Vector3f origin = new Vector3f();
+
+ public Transform() {
+ }
+
+ public Transform(Matrix3f mat) {
+ basis.set(mat);
+ }
+
+ public Transform(Transform tr) {
+ set(tr);
+ }
+
+ public void set(Transform tr) {
+ basis.set(tr.basis);
+ origin.set(tr.origin);
+ }
+
+ public void transform(Vector3f v) {
+ basis.transform(v);
+ v.add(origin);
+ }
+
+ public void setIdentity() {
+ basis.setIdentity();
+ origin.set(0f, 0f, 0f);
+ }
+
+ public void inverse() {
+ basis.transpose();
+ origin.scale(-1f);
+ basis.transform(origin);
+ }
+
+ public void inverse(Transform tr) {
+ set(tr);
+ inverse();
+ }
+
+ public void mul(Transform tr) {
+ if (stack == null) stack = BulletStack.get();
+
+ stack.vectors.push();
+ try {
+ Vector3f vec = stack.vectors.get(tr.origin);
+ transform(vec);
+
+ basis.mul(tr.basis);
+ origin.set(vec);
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public void mul(Transform tr1, Transform tr2) {
+ set(tr1);
+ mul(tr2);
+ }
+
+ public void invXform(Vector3f inVec, Vector3f out) {
+ if (stack == null) stack = BulletStack.get();
+
+ stack.matrices.push();
+ try {
+ out.sub(inVec, origin);
+
+ Matrix3f mat = stack.matrices.get(basis);
+ mat.transpose();
+ mat.transform(out);
+ }
+ finally {
+ stack.matrices.pop();
+ }
+ }
+
+ public Quat4f getRotation() {
+ if (stack == null) stack = BulletStack.get();
+
+ stack.quats.push();
+ try {
+ Quat4f q = stack.quats.get();
+ MatrixUtil.getRotation(basis, q);
+ return stack.quats.returning(q);
+ }
+ finally {
+ stack.quats.pop();
+ }
+ }
+
+ public void setRotation(Quat4f q) {
+ MatrixUtil.setRotation(basis, q);
+ }
+
+ public void setFromOpenGLMatrix(float[] m) {
+ MatrixUtil.setFromOpenGLSubMatrix(basis, m);
+ origin.set(m[12], m[13], m[14]);
+ }
+
+ public void getOpenGLMatrix(float[] m) {
+ MatrixUtil.getOpenGLSubMatrix(basis, m);
+ m[12] = origin.x;
+ m[13] = origin.y;
+ m[14] = origin.z;
+ m[15] = 1f;
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/linearmath/TransformUtil.java b/src/jbullet/src/javabullet/linearmath/TransformUtil.java
new file mode 100644
index 0000000..25acf80
--- /dev/null
+++ b/src/jbullet/src/javabullet/linearmath/TransformUtil.java
@@ -0,0 +1,174 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.linearmath;
+
+import javabullet.BulletGlobals;
+import javabullet.BulletStack;
+import javax.vecmath.Matrix3f;
+import javax.vecmath.Quat4f;
+import javax.vecmath.Vector3f;
+
+/**
+ *
+ * @author jezek2
+ */
+public class TransformUtil {
+
+ public static final float SIMDSQRT12 = 0.7071067811865475244008443621048490f;
+ public static final float ANGULAR_MOTION_THRESHOLD = 0.5f*BulletGlobals.SIMD_HALF_PI;
+
+ public static float recipSqrt(float x) {
+ return 1f / (float)Math.sqrt(x); /* reciprocal square root */
+ }
+
+ public static void planeSpace1(Vector3f n, Vector3f p, Vector3f q) {
+ if (Math.abs(n.z) > SIMDSQRT12) {
+ // choose p in y-z plane
+ float a = n.y * n.y + n.z * n.z;
+ float k = recipSqrt(a);
+ p.set(0, -n.z * k, n.y * k);
+ // set q = n x p
+ q.set(a * k, -n.x * p.z, n.x * p.y);
+ }
+ else {
+ // choose p in x-y plane
+ float a = n.x * n.x + n.y * n.y;
+ float k = recipSqrt(a);
+ p.set(-n.y * k, n.x * k, 0);
+ // set q = n x p
+ q.set(-n.z * p.y, n.z * p.x, a * k);
+ }
+ }
+
+ public static void integrateTransform(Transform curTrans, Vector3f linvel, Vector3f angvel, float timeStep, Transform predictedTransform) {
+ BulletStack stack = BulletStack.get();
+
+ stack.vectors.push();
+ stack.quats.push();
+ try {
+ predictedTransform.origin.scaleAdd(timeStep, linvel, curTrans.origin);
+ // //#define QUATERNION_DERIVATIVE
+ // #ifdef QUATERNION_DERIVATIVE
+ // btQuaternion predictedOrn = curTrans.getRotation();
+ // predictedOrn += (angvel * predictedOrn) * (timeStep * btScalar(0.5));
+ // predictedOrn.normalize();
+ // #else
+ // exponential map
+ Vector3f axis = stack.vectors.get();
+ float fAngle = angvel.length();
+
+ // limit the angular motion
+ if (fAngle * timeStep > ANGULAR_MOTION_THRESHOLD) {
+ fAngle = ANGULAR_MOTION_THRESHOLD / timeStep;
+ }
+
+ if (fAngle < 0.001f) {
+ // use Taylor's expansions of sync function
+ axis.scale(0.5f * timeStep - (timeStep * timeStep * timeStep) * (0.020833333333f) * fAngle * fAngle, angvel);
+ }
+ else {
+ // sync(fAngle) = sin(c*fAngle)/t
+ axis.scale((float) Math.sin(0.5f * fAngle * timeStep) / fAngle, angvel);
+ }
+ Quat4f dorn = stack.quats.get(axis.x, axis.y, axis.z, (float) Math.cos(fAngle * timeStep * 0.5f));
+ Quat4f orn0 = stack.quats.get(curTrans.getRotation());
+
+ Quat4f predictedOrn = stack.quats.get();
+ predictedOrn.mul(dorn, orn0);
+ predictedOrn.normalize();
+ // #endif
+ predictedTransform.setRotation(predictedOrn);
+ }
+ finally {
+ stack.vectors.pop();
+ stack.quats.pop();
+ }
+ }
+
+ public static void calculateVelocity(Transform transform0, Transform transform1, float timeStep, Vector3f linVel, Vector3f angVel) {
+ BulletStack stack = BulletStack.get();
+
+ stack.vectors.push();
+ try {
+ linVel.sub(transform1.origin, transform0.origin);
+ linVel.scale(1f / timeStep);
+
+ Vector3f axis = stack.vectors.get();
+ float[] angle = new float[1];
+ calculateDiffAxisAngle(transform0, transform1, axis, angle);
+ angVel.scale(angle[0] / timeStep, axis);
+ }
+ finally {
+ stack.vectors.pop();
+ }
+ }
+
+ public static void calculateDiffAxisAngle(Transform transform0, Transform transform1, Vector3f axis, float[] angle) {
+ BulletStack stack = BulletStack.get();
+
+ stack.matrices.push();
+ stack.quats.push();
+ try {
+ // #ifdef USE_QUATERNION_DIFF
+ // btQuaternion orn0 = transform0.getRotation();
+ // btQuaternion orn1a = transform1.getRotation();
+ // btQuaternion orn1 = orn0.farthest(orn1a);
+ // btQuaternion dorn = orn1 * orn0.inverse();
+ // #else
+ Matrix3f tmp = stack.matrices.get();
+ tmp.set(transform0.basis);
+ MatrixUtil.invert(tmp);
+
+ Matrix3f dmat = stack.matrices.get();
+ dmat.mul(transform1.basis, tmp);
+
+ Quat4f dorn = stack.quats.get();
+ MatrixUtil.getRotation(dmat, dorn);
+ // #endif
+
+ // floating point inaccuracy can lead to w component > 1..., which breaks
+
+ dorn.normalize();
+
+ angle[0] = QuaternionUtil.getAngle(dorn);
+ axis.set(dorn.x, dorn.y, dorn.z);
+ // TODO: probably not needed
+ //axis[3] = btScalar(0.);
+
+ // check for axis length
+ float len = axis.lengthSquared();
+ if (len < BulletGlobals.FLT_EPSILON * BulletGlobals.FLT_EPSILON) {
+ axis.set(1f, 0f, 0f);
+ }
+ else {
+ axis.scale(1f / (float) Math.sqrt(len));
+ }
+ }
+ finally {
+ stack.matrices.pop();
+ stack.quats.pop();
+ }
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/linearmath/VectorUtil.java b/src/jbullet/src/javabullet/linearmath/VectorUtil.java
new file mode 100644
index 0000000..b0ba6ca
--- /dev/null
+++ b/src/jbullet/src/javabullet/linearmath/VectorUtil.java
@@ -0,0 +1,189 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.linearmath;
+
+import javax.vecmath.Vector3f;
+import javax.vecmath.Vector4f;
+
+/**
+ *
+ * @author jezek2
+ */
+public class VectorUtil {
+
+ public static int maxAxis(Vector3f v) {
+ int maxIndex = -1;
+ float maxVal = -1e30f;
+ if (v.x > maxVal) {
+ maxIndex = 0;
+ maxVal = v.x;
+ }
+ if (v.y > maxVal) {
+ maxIndex = 1;
+ maxVal = v.y;
+ }
+ if (v.z > maxVal) {
+ maxIndex = 2;
+ maxVal = v.z;
+ }
+
+ return maxIndex;
+ }
+
+ public static int maxAxis4(Vector4f v) {
+ int maxIndex = -1;
+ float maxVal = -1e30f;
+ if (v.x > maxVal) {
+ maxIndex = 0;
+ maxVal = v.x;
+ }
+ if (v.y > maxVal) {
+ maxIndex = 1;
+ maxVal = v.y;
+ }
+ if (v.z > maxVal) {
+ maxIndex = 2;
+ maxVal = v.z;
+ }
+ if (v.w > maxVal) {
+ maxIndex = 3;
+ maxVal = v.w;
+ }
+
+ return maxIndex;
+ }
+
+ public static int closestAxis4(Vector4f vec) {
+ Vector4f tmp = new Vector4f(vec);
+ tmp.absolute();
+ return maxAxis4(tmp);
+ }
+
+ public static float getCoord(Vector3f vec, int num) {
+ switch (num) {
+ case 0: return vec.x;
+ case 1: return vec.y;
+ case 2: return vec.z;
+ default: throw new InternalError();
+ }
+ }
+
+ public static void setCoord(Vector3f vec, int num, float value) {
+ switch (num) {
+ case 0: vec.x = value; break;
+ case 1: vec.y = value; break;
+ case 2: vec.z = value; break;
+ default: throw new InternalError();
+ }
+ }
+
+ public static void mulCoord(Vector3f vec, int num, float value) {
+ switch (num) {
+ case 0: vec.x *= value; break;
+ case 1: vec.y *= value; break;
+ case 2: vec.z *= value; break;
+ default: throw new InternalError();
+ }
+ }
+
+ public static void setInterpolate3(Vector3f dest, Vector3f v0, Vector3f v1, float rt) {
+ float s = 1f - rt;
+ dest.x = s * v0.x + rt * v1.x;
+ dest.y = s * v0.y + rt * v1.y;
+ dest.z = s * v0.z + rt * v1.z;
+ // don't do the unused w component
+ // m_co[3] = s * v0[3] + rt * v1[3];
+ }
+
+ public static void add(Vector3f dest, Vector3f v1, Vector3f v2) {
+ dest.x = v1.x + v2.x;
+ dest.y = v1.y + v2.y;
+ dest.z = v1.z + v2.z;
+ }
+
+ public static void add(Vector3f dest, Vector3f v1, Vector3f v2, Vector3f v3) {
+ dest.x = v1.x + v2.x + v3.x;
+ dest.y = v1.y + v2.y + v3.y;
+ dest.z = v1.z + v2.z + v3.z;
+ }
+
+ public static void add(Vector3f dest, Vector3f v1, Vector3f v2, Vector3f v3, Vector3f v4) {
+ dest.x = v1.x + v2.x + v3.x + v4.x;
+ dest.y = v1.y + v2.y + v3.y + v4.y;
+ dest.z = v1.z + v2.z + v3.z + v4.z;
+ }
+
+ public static void mul(Vector3f dest, Vector3f v1, Vector3f v2) {
+ dest.x = v1.x * v2.x;
+ dest.y = v1.y * v2.y;
+ dest.z = v1.z * v2.z;
+ }
+
+ public static void div(Vector3f dest, Vector3f v1, Vector3f v2) {
+ dest.x = v1.x / v2.x;
+ dest.y = v1.y / v2.y;
+ dest.z = v1.z / v2.z;
+ }
+
+ public static void setMin(Vector3f a, Vector3f b) {
+ a.x = Math.min(a.x, b.x);
+ a.y = Math.min(a.y, b.y);
+ a.z = Math.min(a.z, b.z);
+ }
+
+ public static void setMax(Vector3f a, Vector3f b) {
+ a.x = Math.max(a.x, b.x);
+ a.y = Math.max(a.y, b.y);
+ a.z = Math.max(a.z, b.z);
+ }
+
+ public static float dot3(Vector4f v0, Vector3f v1) {
+ return (v0.x*v1.x + v0.y*v1.y + v0.z*v1.z);
+ }
+
+ public static float dot3(Vector4f v0, Vector4f v1) {
+ return (v0.x*v1.x + v0.y*v1.y + v0.z*v1.z);
+ }
+
+ public static float lengthSquared3(Vector4f v) {
+ return (v.x*v.x + v.y*v.y + v.z*v.z);
+ }
+
+ public static void normalize3(Vector4f v) {
+ float norm = (float)(1.0/Math.sqrt(v.x*v.x + v.y*v.y + v.z*v.z));
+ v.x *= norm;
+ v.y *= norm;
+ v.z *= norm;
+ }
+
+ public static void cross3(Vector3f dest, Vector4f v1, Vector4f v2) {
+ float x,y;
+ x = v1.y*v2.z - v1.z*v2.y;
+ y = v2.x*v1.z - v2.z*v1.x;
+ dest.z = v1.x*v2.y - v1.y*v2.x;
+ dest.x = x;
+ dest.y = y;
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/linearmath/package-info.java b/src/jbullet/src/javabullet/linearmath/package-info.java
new file mode 100644
index 0000000..3c33279
--- /dev/null
+++ b/src/jbullet/src/javabullet/linearmath/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+/**
+ * Vector math library.
+ */
+package javabullet.linearmath;
+
diff --git a/src/jbullet/src/javabullet/util/FloatArrayList.java b/src/jbullet/src/javabullet/util/FloatArrayList.java
new file mode 100644
index 0000000..ab8fe41
--- /dev/null
+++ b/src/jbullet/src/javabullet/util/FloatArrayList.java
@@ -0,0 +1,71 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.util;
+
+/**
+ *
+ * @author jezek2
+ */
+public class FloatArrayList {
+
+ private float[] array = new float[16];
+ private int size;
+
+ public void add(float value) {
+ if (size == array.length) {
+ expand();
+ }
+
+ array[size++] = value;
+ }
+
+ private void expand() {
+ float[] newArray = new float[array.length << 1];
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ array = newArray;
+ }
+
+ public float remove(int index) {
+ if (index >= size) throw new IndexOutOfBoundsException();
+ float old = array[index];
+ System.arraycopy(array, index+1, array, index, size - index - 1);
+ size--;
+ return old;
+ }
+
+ public float get(int index) {
+ if (index >= size) throw new IndexOutOfBoundsException();
+ return array[index];
+ }
+
+ public void set(int index, float value) {
+ if (index >= size) throw new IndexOutOfBoundsException();
+ array[index] = value;
+ }
+
+ public int size() {
+ return size;
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/util/HashUtil.java b/src/jbullet/src/javabullet/util/HashUtil.java
new file mode 100644
index 0000000..10e0008
--- /dev/null
+++ b/src/jbullet/src/javabullet/util/HashUtil.java
@@ -0,0 +1,126 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.util;
+
+import gnu.trove.THashMap;
+import gnu.trove.TObjectObjectProcedure;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * Wrapper for THashMap (from GNU Trove), with fallback to less effective original HashMap.
+ *
+ * @author jezek2
+ */
+public class HashUtil {
+
+ private HashUtil() {}
+
+ public interface IMap<K,V> {
+ public V get(K key);
+ public V put(K key, V value);
+ public V remove(K key);
+ public int size();
+ public boolean forEachValue(IObjectProcedure<V> proc);
+ public boolean retainEntries(IObjectProcedure<V> proc);
+ }
+
+ public interface IObjectProcedure<T> {
+ public boolean execute(T value);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ private static Class mapCls;
+
+ static {
+ try {
+ mapCls = TroveHashMapImpl.class;
+ }
+ catch (Throwable t) {
+ mapCls = JavaHashMapImpl.class;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <K,V> IMap<K,V> createMap() {
+ try {
+ return (IMap<K,V>)mapCls.newInstance();
+ }
+ catch (IllegalAccessException e) {
+ throw new IllegalStateException(e);
+ }
+ catch (InstantiationException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ protected static class JavaHashMapImpl<K,V> extends HashMap<K,V> implements IMap<K,V> {
+ public boolean forEachValue(IObjectProcedure<V> proc) {
+ for (V value : values()) {
+ if (!proc.execute(value)) return false;
+ }
+ return true;
+ }
+
+ public boolean retainEntries(IObjectProcedure<V> proc) {
+ boolean mod = false;
+ for (Iterator<V> it = values().iterator(); it.hasNext(); ) {
+ V value = it.next();
+ if (!proc.execute(value)) {
+ it.remove();
+ mod = true;
+ }
+ }
+ return mod;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ protected static class TroveHashMapImpl<K,V> extends THashMap<K,V> implements IMap<K,V> {
+ private TroveObjectObjectProcedureWrapper<K,V> valueWrapper = new TroveObjectObjectProcedureWrapper<K,V>();
+
+ public boolean forEachValue(IObjectProcedure<V> proc) {
+ valueWrapper.proc = proc;
+ return forEachEntry(valueWrapper);
+ }
+
+ public boolean retainEntries(IObjectProcedure<V> proc) {
+ valueWrapper.proc = proc;
+ return retainEntries(valueWrapper);
+ }
+ }
+
+ protected static class TroveObjectObjectProcedureWrapper<K,V> implements TObjectObjectProcedure<K,V> {
+ public IObjectProcedure<V> proc;
+
+ public boolean execute(K key, V value) {
+ return proc.execute(value);
+ }
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/util/HashUtil.java-orig b/src/jbullet/src/javabullet/util/HashUtil.java-orig
new file mode 100644
index 0000000..10e0008
--- /dev/null
+++ b/src/jbullet/src/javabullet/util/HashUtil.java-orig
@@ -0,0 +1,126 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.util;
+
+import gnu.trove.THashMap;
+import gnu.trove.TObjectObjectProcedure;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * Wrapper for THashMap (from GNU Trove), with fallback to less effective original HashMap.
+ *
+ * @author jezek2
+ */
+public class HashUtil {
+
+ private HashUtil() {}
+
+ public interface IMap<K,V> {
+ public V get(K key);
+ public V put(K key, V value);
+ public V remove(K key);
+ public int size();
+ public boolean forEachValue(IObjectProcedure<V> proc);
+ public boolean retainEntries(IObjectProcedure<V> proc);
+ }
+
+ public interface IObjectProcedure<T> {
+ public boolean execute(T value);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ private static Class mapCls;
+
+ static {
+ try {
+ mapCls = TroveHashMapImpl.class;
+ }
+ catch (Throwable t) {
+ mapCls = JavaHashMapImpl.class;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <K,V> IMap<K,V> createMap() {
+ try {
+ return (IMap<K,V>)mapCls.newInstance();
+ }
+ catch (IllegalAccessException e) {
+ throw new IllegalStateException(e);
+ }
+ catch (InstantiationException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ protected static class JavaHashMapImpl<K,V> extends HashMap<K,V> implements IMap<K,V> {
+ public boolean forEachValue(IObjectProcedure<V> proc) {
+ for (V value : values()) {
+ if (!proc.execute(value)) return false;
+ }
+ return true;
+ }
+
+ public boolean retainEntries(IObjectProcedure<V> proc) {
+ boolean mod = false;
+ for (Iterator<V> it = values().iterator(); it.hasNext(); ) {
+ V value = it.next();
+ if (!proc.execute(value)) {
+ it.remove();
+ mod = true;
+ }
+ }
+ return mod;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ protected static class TroveHashMapImpl<K,V> extends THashMap<K,V> implements IMap<K,V> {
+ private TroveObjectObjectProcedureWrapper<K,V> valueWrapper = new TroveObjectObjectProcedureWrapper<K,V>();
+
+ public boolean forEachValue(IObjectProcedure<V> proc) {
+ valueWrapper.proc = proc;
+ return forEachEntry(valueWrapper);
+ }
+
+ public boolean retainEntries(IObjectProcedure<V> proc) {
+ valueWrapper.proc = proc;
+ return retainEntries(valueWrapper);
+ }
+ }
+
+ protected static class TroveObjectObjectProcedureWrapper<K,V> implements TObjectObjectProcedure<K,V> {
+ public IObjectProcedure<V> proc;
+
+ public boolean execute(K key, V value) {
+ return proc.execute(value);
+ }
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/util/IntArrayList.java b/src/jbullet/src/javabullet/util/IntArrayList.java
new file mode 100644
index 0000000..aeab7ba
--- /dev/null
+++ b/src/jbullet/src/javabullet/util/IntArrayList.java
@@ -0,0 +1,71 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+package javabullet.util;
+
+/**
+ *
+ * @author jezek2
+ */
+public class IntArrayList {
+
+ private int[] array = new int[16];
+ private int size;
+
+ public void add(int value) {
+ if (size == array.length) {
+ expand();
+ }
+
+ array[size++] = value;
+ }
+
+ private void expand() {
+ int[] newArray = new int[array.length << 1];
+ System.arraycopy(array, 0, newArray, 0, array.length);
+ array = newArray;
+ }
+
+ public int remove(int index) {
+ if (index >= size) throw new IndexOutOfBoundsException();
+ int old = array[index];
+ System.arraycopy(array, index+1, array, index, size - index - 1);
+ size--;
+ return old;
+ }
+
+ public int get(int index) {
+ if (index >= size) throw new IndexOutOfBoundsException();
+ return array[index];
+ }
+
+ public void set(int index, int value) {
+ if (index >= size) throw new IndexOutOfBoundsException();
+ array[index] = value;
+ }
+
+ public int size() {
+ return size;
+ }
+
+}
diff --git a/src/jbullet/src/javabullet/util/package-info.java b/src/jbullet/src/javabullet/util/package-info.java
new file mode 100644
index 0000000..1e9db35
--- /dev/null
+++ b/src/jbullet/src/javabullet/util/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * Java port of Bullet (c) 2008 Martin Dvorak <[email protected]>
+ *
+ * Bullet Continuous Collision Detection and Physics Library
+ * Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+/**
+ * Java-specific utility classes.
+ */
+package javabullet.util;
+