And here it comes. But please keep in mind that especially JPCTGL.java does some strange things to make jPCT behave like the state machine that OpenGL is. Normally, one wouldn't write a jPCT based application in that way.
/*
* Java port of Bullet (c) 2008 Martin Dvorak <jezek2@advel.cz>
*
* 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.basic;
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.DefaultCollisionConfiguration;
import javabullet.collision.shapes.BoxShape;
import javabullet.collision.shapes.CollisionShape;
import javabullet.collision.shapes.StaticPlaneShape;
import javabullet.demos.opengl.DemoApplication;
import javabullet.demos.opengl.GLDebugDrawer;
import javabullet.demos.opengl.IGL;
import javabullet.dynamics.DiscreteDynamicsWorld;
import javabullet.dynamics.RigidBody;
import javabullet.dynamics.RigidBodyConstructionInfo;
import javabullet.dynamics.constraintsolver.ConstraintSolver;
import javabullet.dynamics.constraintsolver.SequentialImpulseConstraintSolver;
import javabullet.linearmath.DefaultMotionState;
import javabullet.linearmath.Transform;
import javax.vecmath.Vector3f;
import static javabullet.demos.opengl.IGL.*;
import javabullet.demos.opengl.jpct.*;
/**
* BasicDemo is good starting point for learning the code base and porting.
*
* @author jezek2
*/
public class BasicDemoJPCT extends DemoApplication {
// create 125 (5x5x5) dynamic object
private static final int ARRAY_SIZE_X = 5;
private static final int ARRAY_SIZE_Y = 5;
private static final int ARRAY_SIZE_Z = 5;
// maximum number of objects (and allow user to shoot additional boxes)
private static final int MAX_PROXIES = (ARRAY_SIZE_X*ARRAY_SIZE_Y*ARRAY_SIZE_Z + 1024);
private static final int START_POS_X = -5;
private static final int START_POS_Y = -5;
private static final int START_POS_Z = -3;
// keep the collision shapes, for deletion/cleanup
private List<CollisionShape> collisionShapes = new ArrayList<CollisionShape>();
private BroadphaseInterface overlappingPairCache;
private CollisionDispatcher dispatcher;
private ConstraintSolver solver;
private DefaultCollisionConfiguration collisionConfiguration;
public BasicDemoJPCT(IGL gl) {
super(gl);
}
@Override
public void clientMoveAndDisplay() {
gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// simple dynamics world doesn't handle fixed-time-stepping
float ms = clock.getTimeMicroseconds();
clock.reset();
// step the simulation
if (dynamicsWorld != null) {
dynamicsWorld.stepSimulation(ms / 1000000f);
// optional but useful: debug drawing
dynamicsWorld.debugDrawWorld();
}
renderme();
//glFlush();
//glutSwapBuffers();
}
@Override
public void displayCallback() {
renderme();
// optional but useful: debug drawing to detect problems
if (dynamicsWorld != null) {
dynamicsWorld.debugDrawWorld();
}
}
public void initPhysics() {
setCameraDistance(50f);
// collision configuration contains default setup for memory, collision setup
collisionConfiguration = new DefaultCollisionConfiguration();
// use the default collision dispatcher. For parallel processing you can use a diffent dispatcher (see Extras/BulletMultiThreaded)
dispatcher = new CollisionDispatcher(collisionConfiguration);
// the maximum size of the collision world. Make sure objects stay within these boundaries
// TODO: AxisSweep3
// Don't make the world AABB size too large, it will harm simulation quality and performance
Vector3f worldAabbMin = new Vector3f(-10000,-10000,-10000);
Vector3f worldAabbMax = new Vector3f(10000,10000,10000);
//overlappingPairCache = new AxisSweep3(worldAabbMin,worldAabbMax,MAX_PROXIES);
overlappingPairCache = new SimpleBroadphase(MAX_PROXIES);
//overlappingPairCache = new _BrutalforceBroadphase();
// the default constraint solver. For parallel processing you can use a different solver (see Extras/BulletMultiThreaded)
SequentialImpulseConstraintSolver sol = new SequentialImpulseConstraintSolver();
solver = sol;
// TODO: needed for SimpleDynamicsWorld
//sol.setSolverMode(sol.getSolverMode() & ~SolverMode.SOLVER_CACHE_FRIENDLY.getMask());
dynamicsWorld = new DiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration);
//dynamicsWorld = new SimpleDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration);
dynamicsWorld.setGravity(new Vector3f(0f, -10f, 0f));
// create a few basic rigid bodies
//CollisionShape groundShape = new BoxShape(new Vector3f(50f, 50f, 50f));
CollisionShape groundShape = new StaticPlaneShape(new Vector3f(0, 1, 0), 50);
collisionShapes.add(groundShape);
Transform groundTransform = new Transform();
groundTransform.setIdentity();
groundTransform.origin.set(0, -56, 0);
// We can also use DemoApplication::localCreateRigidBody, but for clarity it is provided here:
{
float mass = 0f;
// rigidbody is dynamic if and only if mass is non zero, otherwise static
boolean isDynamic = (mass != 0f);
Vector3f localInertia = new Vector3f(0, 0, 0);
if (isDynamic) {
groundShape.calculateLocalInertia(mass, localInertia);
}
// using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects
DefaultMotionState myMotionState = new DefaultMotionState(groundTransform);
RigidBodyConstructionInfo rbInfo = new RigidBodyConstructionInfo(mass, myMotionState, groundShape, localInertia);
RigidBody body = new RigidBody(rbInfo);
// add the body to the dynamics world
dynamicsWorld.addRigidBody(body);
}
{
// create a few dynamic rigidbodies
// Re-using the same collision is better for memory usage and performance
CollisionShape colShape = new BoxShape(new Vector3f(1, 1, 1));
//CollisionShape colShape = new SphereShape(1f);
collisionShapes.add(colShape);
// Create Dynamic Objects
Transform startTransform = new Transform();
startTransform.setIdentity();
float mass = 1f;
// rigidbody is dynamic if and only if mass is non zero, otherwise static
boolean isDynamic = (mass != 0f);
Vector3f localInertia = new Vector3f(0, 0, 0);
if (isDynamic) {
colShape.calculateLocalInertia(mass, localInertia);
}
float start_x = START_POS_X - ARRAY_SIZE_X / 2;
float start_y = START_POS_Y;
float start_z = START_POS_Z - ARRAY_SIZE_Z / 2;
for (int k = 0; k < ARRAY_SIZE_Y; k++) {
for (int i = 0; i < ARRAY_SIZE_X; i++) {
for (int j = 0; j < ARRAY_SIZE_Z; j++) {
startTransform.origin.set(
2f * i + start_x,
2f * k + start_y,
2f * j + start_z);
// using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects
DefaultMotionState myMotionState = new DefaultMotionState(startTransform);
RigidBodyConstructionInfo rbInfo = new RigidBodyConstructionInfo(mass, myMotionState, colShape, localInertia);
RigidBody body = new RigidBody(rbInfo);
dynamicsWorld.addRigidBody(body);
}
}
}
}
clientResetScene();
}
public static void main(String[] args) {
BasicDemoJPCT ccdDemo = new BasicDemoJPCT(JPCT.getGL());
ccdDemo.initPhysics();
ccdDemo.getDynamicsWorld().setDebugDrawer(new GLDebugDrawer(JPCT.getGL()));
JPCT.main(args, 640, 480, "Bullet Physics Demo. http://bullet.sf.net", ccdDemo);
}
}
/*
* Java port of Bullet (c) 2008 Martin Dvorak <jezek2@advel.cz>
*
* 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.opengl.jpct;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.*;
import org.lwjgl.opengl.*;
import javabullet.demos.opengl.*;
import com.threed.jpct.*;
/**
*
* @author EgonOlsen
*/
public class JPCT {
private static boolean redisplay = false;
private static JPCTGL gl = new JPCTGL();
private static FrameBuffer buffer = null;
public static void postRedisplay() {
redisplay = true;
}
public static IGL getGL() {
return gl;
}
public static int main(String[] args, int width, int height, String title, DemoApplication demoApp) {
buffer = new FrameBuffer(width, height, FrameBuffer.SAMPLINGMODE_NORMAL);
Config.glWindowName = title;
Config.lightMul=3;
Config.glColorDepth = 16; // Should work on EEEPC
buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
buffer.enableRenderer(IRenderer.RENDERER_OPENGL);
gl.setBuffer(buffer);
Keyboard.enableRepeatEvents(true);
gl.init();
demoApp.myinit();
demoApp.reshape(width, height);
boolean quit = false;
long lastTime = System.currentTimeMillis();
int frames = 0;
while (!Display.isCloseRequested() && !quit) {
demoApp.moveAndDisplay();
gl.update();
while (Keyboard.next()) {
if (Keyboard.getEventCharacter() != '\0') {
demoApp.keyboardCallback(Keyboard.getEventCharacter(), Mouse.getX(), Mouse.getY());
}
if (Keyboard.getEventKeyState()) {
demoApp.specialKeyboard(Keyboard.getEventKey(), Mouse.getX(), Mouse.getY());
} else {
demoApp.specialKeyboardUp(Keyboard.getEventKey(), Mouse.getX(), Mouse.getY());
}
if (Keyboard.getEventKey() == Keyboard.KEY_ESCAPE) {
quit = true;
}
if (Keyboard.getEventKey() == Keyboard.KEY_Q) {
quit = true;
}
}
while (Mouse.next()) {
if (Mouse.getEventButton() != -1) {
int btn = Mouse.getEventButton();
if (btn == 1) {
btn = 2;
} else if (btn == 2) {
btn = 1;
}
demoApp.mouseFunc(btn, Mouse.getEventButtonState() ? 0 : 1, Mouse.getEventX(), Display.getDisplayMode().getHeight() - 1 - Mouse.getEventY());
}
demoApp.mouseMotionFunc(Mouse.getEventX(), Display.getDisplayMode().getHeight() - 1 - Mouse.getEventY());
}
long time = System.currentTimeMillis();
if (time - lastTime < 1000) {
frames++;
} else {
Display.setTitle(title + " | FPS: " + frames);
lastTime = time;
frames = 0;
}
}
System.exit(0);
return 0;
}
}
package javabullet.demos.opengl.jpct;
/*
* Java port of Bullet (c) 2008 Martin Dvorak <jezek2@advel.cz>
*
* 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.
*/
import com.threed.jpct.*;
import com.threed.jpct.util.*;
import javabullet.demos.opengl.*;
import java.io.*;
import java.util.*;
import java.util.List;
import java.awt.*;
import org.lwjgl.opengl.*;
/**
*
* @author EgonOlsen
*/
public class JPCTGL implements IGL {
{
Config.maxPolysVisible=20000;
Config.useMultipleThreads=false;
}
private World world=new World();
private Object3D plane=Primitives.getPlane(10,6);
private FrameBuffer buffer=null;
private Texture font;
private int shadeModel = Object3D.SHADING_GOURAUD;
private Color clearColor = Color.WHITE;
private Light light=null;
private Color color=Color.BLUE;
private SimpleVector translate=new SimpleVector();
private Matrix rot=new Matrix();
private float scale=1f;
Map<Float, List<Object3D>> cubes=new HashMap<Float, List<Object3D>>();
private boolean shadows=true;
private ShadowHelper sh=null;
private boolean lines=false;
public void setBuffer(FrameBuffer buffer) {
this.buffer=buffer;
if (shadows) {
Projector proj=new Projector();
proj.setPosition(1, -30, -1);
proj.setFOV(1.5f);
proj.setYFOV(1.5f);
proj.lookAt(SimpleVector.ORIGIN);
sh=new ShadowHelper(world,buffer,proj,1024);
sh.addReceiver(plane);
sh.setCullingMode(false);
sh.setAmbientLight(new Color(20,20,20));
}
}
public void init() {
if (shadows) {
world.setAmbientLight(30, 20, 20);
} else {
plane.setAdditionalColor(Color.GREEN);
}
TextureManager tm=TextureManager.getInstance();
Texture t=new Texture("box.jpg");
tm.addTexture("plane",t);
plane.setTexture("plane");
plane.build();
plane.translate(0,7,0);
plane.rotateX((float) Math.PI/2f);
world.addObject(plane);
}
public void glLight(int light, int pname, float[] params) {
if (pname==GL11.GL_AMBIENT) {
this.light=new Light(world);
SimpleVector ins=new SimpleVector(params[0]*255,params[1]*255, params[2]*255);
this.light.setIntensity(ins);
}
if (pname==GL11.GL_POSITION) {
this.light.setPosition(new SimpleVector(params[0],-params[1], -params[2]));
}
}
public void glEnable(int cap) {
}
public void glDisable(int cap) {
}
public void glShadeModel(int mode) {
if (mode == GL11.GL_SMOOTH) {
shadeModel = Object3D.SHADING_GOURAUD;
} else {
shadeModel = Object3D.SHADING_FAKED_FLAT;
}
}
public void glDepthFunc(int func) {
}
public void glClearColor(float red, float green, float blue, float alpha) {
clearColor = new Color(red, green, blue, alpha);
}
public void glMatrixMode(int mode) {
}
public void glLoadIdentity() {
}
public void glFrustum(double left, double right, double bottom, double top, double zNear, double zFar) {
}
public void gluLookAt(float eyex, float eyey, float eyez, float centerx, float centery, float centerz, float upx, float upy, float upz) {
world.getCamera().setPosition(new SimpleVector(eyex, -eyey,-eyez));
world.getCamera().lookAt(new SimpleVector(centerx,-centery,-centerz));
}
public void glViewport(int x, int y, int width, int height) {
}
public void glPushMatrix() {
rot.setIdentity();
translate.x=0;
translate.y=0;
translate.z=0;
}
public void glPopMatrix() {
}
public void gluOrtho2D(float left, float right, float bottom, float top) {
}
public void glScalef(float x, float y, float z) {
scale=x;
}
public void glTranslatef(float x, float y, float z) {
}
public void glColor3f(float red, float green, float blue) {
color=new Color(Math.min(1f,red), Math.min(1f,green), Math.min(1f,blue));
}
public void glClear(int mask) {
buffer.clear(clearColor);
}
public void glBegin(int mode) {
if (mode==GL11.GL_LINES) {
lines=true;
} else {
lines=false;
}
}
public void glEnd() {
}
public void glVertex3f(float x, float y, float z) {
}
public void glLineWidth(float width) {
}
public void glPointSize(float size) {
}
public void glNormal3f(float nx, float ny, float nz) {
}
public void glMultMatrix(float[] m) {
translate.x=m[12];
translate.y=-m[13];
translate.z=-m[14];
m[12]=0;
m[13]=0;
m[14]=0;
rot.setDump(m);
}
////////////////////////////////////////////////////////////////////////////
public void drawCube(float extent) {
extent = extent * 0.5f;
Float key=new Float(extent);
List<Object3D> cubeLst=cubes.get(key);
if (cubeLst==null) {
cubeLst=new ArrayList<Object3D>();
cubes.put(key, cubeLst);
}
Object3D next=null;
for (Object3D obj:cubeLst) {
if (obj.getVisibility()==Object3D.OBJ_INVISIBLE) {
next=obj;
break;
}
}
if (next==null) {
next=Primitives.getBox(extent, 1);
cubeLst.add(next);
world.addObject(next);
next.rotateY((float) Math.PI/4f);
next.rotateMesh();
next.build();
if (shadows) {
sh.addCaster(next);
//sh.addReceiver(next);
}
}
next.setVisibility(Object3D.OBJ_VISIBLE);
next.setAdditionalColor(color);
next.getTranslationMatrix().setIdentity();
next.translate(new SimpleVector(translate));
next.setScale(1);
next.getRotationMatrix().setTo(rot);
next.setScale(scale);
//System.out.println(next.getTransformedCenter());
}
/*
private static final Cylinder quadObj = new Cylinder();
private static final Sphere sphere = new Sphere();
private static class SphereKey {
public float radius;
public SphereKey() {
}
public SphereKey(SphereKey key) {
radius = key.radius;
}
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof SphereKey)) {
return false;
}
SphereKey other = (SphereKey) obj;
return radius == other.radius;
}
@Override
public int hashCode() {
return Float.floatToIntBits(radius);
}
}
private static Map<SphereKey, Integer> sphereDisplayLists = new HashMap<SphereKey, Integer>();
private static SphereKey sphereKey = new SphereKey();
public void drawSphere(float radius, int slices, int stacks) {
sphereKey.radius = radius;
Integer glList = sphereDisplayLists.get(sphereKey);
if (glList == null) {
glList = glGenLists(1);
glNewList(glList, GL_COMPILE);
sphere.draw(radius, 8, 8);
glEndList();
sphereDisplayLists.put(new SphereKey(sphereKey), glList);
}
glCallList(glList);
}
////////////////////////////////////////////////////////////////////////////
private static class CylinderKey {
public float radius;
public float halfHeight;
public CylinderKey() {
}
public CylinderKey(CylinderKey key) {
radius = key.radius;
halfHeight = key.halfHeight;
}
public void set(float radius, float halfHeight) {
this.radius = radius;
this.halfHeight = halfHeight;
}
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof CylinderKey)) {
return false;
}
CylinderKey other = (CylinderKey) obj;
if (radius != other.radius) {
return false;
}
if (halfHeight != other.halfHeight) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 7;
hash = 23 * hash + Float.floatToIntBits(radius);
hash = 23 * hash + Float.floatToIntBits(halfHeight);
return hash;
}
}
private static Map<CylinderKey, Integer> cylinderDisplayLists = new HashMap<CylinderKey, Integer>();
private static CylinderKey cylinderKey = new CylinderKey();
public void drawCylinder(float radius, float halfHeight, int upAxis) {
glPushMatrix();
switch (upAxis) {
case 0:
glRotatef(-90f, 0.0f, 1.0f, 0.0f);
glTranslatef(0.0f, 0.0f, -halfHeight);
break;
case 1:
glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
glTranslatef(0.0f, 0.0f, -halfHeight);
break;
case 2:
glTranslatef(0.0f, 0.0f, -halfHeight);
break;
default: {
assert (false);
}
}
// The gluCylinder subroutine draws a cylinder that is oriented along the z axis.
// The base of the cylinder is placed at z = 0; the top of the cylinder is placed at z=height.
// Like a sphere, the cylinder is subdivided around the z axis into slices and along the z axis into stacks.
cylinderKey.set(radius, halfHeight);
Integer glList = cylinderDisplayLists.get(cylinderKey);
if (glList == null) {
glList = glGenLists(1);
glNewList(glList, GL_COMPILE);
quadObj.setDrawStyle(GLU_FILL);
quadObj.setNormals(GLU_SMOOTH);
quadObj.draw(radius, radius, 2f * halfHeight, 15, 10);
glEndList();
cylinderDisplayLists.put(new CylinderKey(cylinderKey), glList);
}
glCallList(glList);
glPopMatrix();
}
*/
public void drawCylinder(float radius, float halfHeight, int upAxis) {
}
public void drawSphere(float radius, int slices, int stacks) {
}
////////////////////////////////////////////////////////////////////////////
public void drawString(CharSequence s, int x, int y, float red, float green, float blue) {
if (font != null) {
//
}
}
public void update() {
plane.setVisibility(Object3D.OBJ_VISIBLE);
if (!shadows) {
buffer.clear(clearColor);
world.renderScene(buffer);
if (lines) {
world.drawWireframe(buffer, Color.WHITE);
} else {
world.draw(buffer);
}
buffer.update();
buffer.displayGLOnly();
} else {
sh.updateShadowMap();
buffer.clear(clearColor);
sh.drawScene();
buffer.update();
buffer.displayGLOnly();
}
for (Enumeration e=world.getObjects(); e.hasMoreElements();) {
Object3D obj=(Object3D)e.nextElement();
obj.setVisibility(false);
}
}
}