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