159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/* 259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * To change this template, choose Tools | Templates 359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * and open the template in the editor. 459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapackage com.jme3.bullet.collision.shapes; 659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.export.InputCapsule; 859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.export.JmeExporter; 959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.export.JmeImporter; 1059b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.export.OutputCapsule; 1159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.math.FastMath; 1259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.math.Vector3f; 1359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.Mesh; 1459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.util.BufferUtils; 1559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.io.IOException; 1659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.nio.ByteBuffer; 1759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.logging.Level; 1859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.logging.Logger; 1959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 2059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/** 2159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * Uses Bullet Physics Heightfield terrain collision system. This is MUCH faster 2259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * than using a regular mesh. 2359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * There are a couple tricks though: 2459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * -No rotation or translation is supported. 2559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * -The collision bbox must be centered around 0,0,0 with the height above and below the y-axis being 2659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * equal on either side. If not, the whole collision box is shifted vertically and things don't collide 2759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * as they should. 2859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * 2959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @author Brent Owens 3059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 3159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapublic class HeightfieldCollisionShape extends CollisionShape { 3259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 3359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected int heightStickWidth; 3459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected int heightStickLength; 3559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected float[] heightfieldData; 3659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected float heightScale; 3759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected float minHeight; 3859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected float maxHeight; 3959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected int upAxis; 4059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected boolean flipQuadEdges; 4159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected ByteBuffer bbuf; 4259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta// protected FloatBuffer fbuf; 4359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 4459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public HeightfieldCollisionShape() { 4559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 4659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 4759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public HeightfieldCollisionShape(float[] heightmap) { 4859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta createCollisionHeightfield(heightmap, Vector3f.UNIT_XYZ); 4959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 5059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 5159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public HeightfieldCollisionShape(float[] heightmap, Vector3f scale) { 5259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta createCollisionHeightfield(heightmap, scale); 5359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 5459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 5559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected void createCollisionHeightfield(float[] heightmap, Vector3f worldScale) { 5659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.scale = worldScale; 5759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.heightScale = 1;//don't change away from 1, we use worldScale instead to scale 5859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 5959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.heightfieldData = heightmap; 6059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 6159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta float min = heightfieldData[0]; 6259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta float max = heightfieldData[0]; 6359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // calculate min and max height 6459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (int i = 0; i < heightfieldData.length; i++) { 6559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (heightfieldData[i] < min) { 6659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta min = heightfieldData[i]; 6759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 6859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (heightfieldData[i] > max) { 6959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta max = heightfieldData[i]; 7059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 7159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 7259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // we need to center the terrain collision box at 0,0,0 for BulletPhysics. And to do that we need to set the 7359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // min and max height to be equal on either side of the y axis, otherwise it gets shifted and collision is incorrect. 7459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (max < 0) { 7559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta max = -min; 7659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else { 7759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (Math.abs(max) > Math.abs(min)) { 7859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta min = -max; 7959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else { 8059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta max = -min; 8159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 8259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 8359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.minHeight = min; 8459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.maxHeight = max; 8559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 8659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.upAxis = 1; 8759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.flipQuadEdges = false; 8859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 8959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta heightStickWidth = (int) FastMath.sqrt(heightfieldData.length); 9059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta heightStickLength = heightStickWidth; 9159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 9259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 9359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta createShape(); 9459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 9559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 9659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected void createShape() { 9759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta bbuf = BufferUtils.createByteBuffer(heightfieldData.length * 4); 9859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta// fbuf = bbuf.asFloatBuffer();//FloatBuffer.wrap(heightfieldData); 9959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta// fbuf.rewind(); 10059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta// fbuf.put(heightfieldData); 10159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (int i = 0; i < heightfieldData.length; i++) { 10259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta float f = heightfieldData[i]; 10359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta bbuf.putFloat(f); 10459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 10559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta// fbuf.rewind(); 10659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta objectId = createShape(heightStickWidth, heightStickLength, bbuf, heightScale, minHeight, maxHeight, upAxis, flipQuadEdges); 10759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Shape {0}", Long.toHexString(objectId)); 10859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta setScale(scale); 10959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta setMargin(margin); 11059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 11159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 11259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private native long createShape(int heightStickWidth, int heightStickLength, ByteBuffer heightfieldData, float heightScale, float minHeight, float maxHeight, int upAxis, boolean flipQuadEdges); 11359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 11459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public Mesh createJmeMesh() { 11559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta //TODO return Converter.convert(bulletMesh); 11659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return null; 11759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 11859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 11959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void write(JmeExporter ex) throws IOException { 12059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta super.write(ex); 12159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta OutputCapsule capsule = ex.getCapsule(this); 12259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta capsule.write(heightStickWidth, "heightStickWidth", 0); 12359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta capsule.write(heightStickLength, "heightStickLength", 0); 12459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta capsule.write(heightScale, "heightScale", 0); 12559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta capsule.write(minHeight, "minHeight", 0); 12659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta capsule.write(maxHeight, "maxHeight", 0); 12759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta capsule.write(upAxis, "upAxis", 1); 12859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta capsule.write(heightfieldData, "heightfieldData", new float[0]); 12959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta capsule.write(flipQuadEdges, "flipQuadEdges", false); 13059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 13159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 13259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void read(JmeImporter im) throws IOException { 13359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta super.read(im); 13459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta InputCapsule capsule = im.getCapsule(this); 13559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta heightStickWidth = capsule.readInt("heightStickWidth", 0); 13659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta heightStickLength = capsule.readInt("heightStickLength", 0); 13759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta heightScale = capsule.readFloat("heightScale", 0); 13859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta minHeight = capsule.readFloat("minHeight", 0); 13959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta maxHeight = capsule.readFloat("maxHeight", 0); 14059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta upAxis = capsule.readInt("upAxis", 1); 14159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta heightfieldData = capsule.readFloatArray("heightfieldData", new float[0]); 14259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta flipQuadEdges = capsule.readBoolean("flipQuadEdges", false); 14359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta createShape(); 14459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 14559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta} 146