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