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