159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/*
259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * Copyright (c) 2009-2012 jMonkeyEngine
359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * All rights reserved.
459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * Redistribution and use in source and binary forms, with or without
659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * modification, are permitted provided that the following conditions are
759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * met:
859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * * Redistributions of source code must retain the above copyright
1059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *   notice, this list of conditions and the following disclaimer.
1159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
1259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * * Redistributions in binary form must reproduce the above copyright
1359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *   notice, this list of conditions and the following disclaimer in the
1459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *   documentation and/or other materials provided with the distribution.
1559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
1659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
1759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *   may be used to endorse or promote products derived from this software
1859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *   without specific prior written permission.
1959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
2059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
2459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
2559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
2759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
2859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
2959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */
3259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
3359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapackage com.jme3.terrain.geomipmap;
3459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
3559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.bounding.BoundingBox;
3659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.bounding.BoundingSphere;
3759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.bounding.BoundingVolume;
3859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.collision.Collidable;
3959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.collision.CollisionResults;
4059b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.collision.UnsupportedCollisionException;
4159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.export.InputCapsule;
4259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.export.JmeExporter;
4359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.export.JmeImporter;
4459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.export.OutputCapsule;
4559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.math.*;
4659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.Geometry;
4759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.Mesh;
4859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.VertexBuffer;
4959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.VertexBuffer.Type;
5059b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.terrain.geomipmap.TerrainQuad.LocationHeight;
5159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.terrain.geomipmap.lodcalc.util.EntropyComputeUtil;
5259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.util.BufferUtils;
5359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.io.IOException;
5459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.nio.FloatBuffer;
5559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.nio.IntBuffer;
5659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.HashMap;
5759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.List;
5859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
5959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
6059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/**
6159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * A terrain patch is a leaf in the terrain quad tree. It has a mesh that can change levels of detail (LOD)
6259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * whenever the view point, or camera, changes. The actual terrain mesh is created by the LODGeomap class.
6359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * That uses a geo-mipmapping algorithm to change the index buffer of the mesh.
6459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * The mesh is a triangle strip. In wireframe mode you might notice some strange lines, these are degenerate
6559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * triangles generated by the geoMipMap algorithm and can be ignored. The video card removes them at almost no cost.
6659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
6759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * Each patch needs to know its neighbour's LOD so it can seam its edges with them, in case the neighbour has a different
6859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * LOD. If this doesn't happen, you will see gaps.
6959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
7059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * The LOD value is most detailed at zero. It gets less detailed the higher the LOD value until you reach maxLod, which
7159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * is a mathematical limit on the number of times the 'size' of the patch can be divided by two. However there is a -1 to that
7259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * for now until I add in a custom index buffer calculation for that max level, the current algorithm does not go that far.
7359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
7459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * You can supply a LodThresholdCalculator for use in determining when the LOD should change. It's API will no doubt change
7559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * in the near future. Right now it defaults to just changing LOD every two patch sizes. So if a patch has a size of 65,
7659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * then the LOD changes every 130 units away.
7759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
7859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @author Brent Owens
7959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */
8059b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapublic class TerrainPatch extends Geometry {
8159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
8259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected LODGeomap geomap;
8359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected int lod = -1; // this terrain patch's LOD
8459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private int maxLod = -1;
8559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected int previousLod = -1;
8659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected int lodLeft, lodTop, lodRight, lodBottom; // it's neighbour's LODs
8759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
8859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected int size;
8959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
9059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected int totalSize;
9159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
9259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected short quadrant = 1;
9359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
9459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    // x/z step
9559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected Vector3f stepScale;
9659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
9759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    // center of the patch in relation to (0,0,0)
9859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected Vector2f offset;
9959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
10059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    // amount the patch has been shifted.
10159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected float offsetAmount;
10259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
10359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    //protected LodCalculator lodCalculator;
10459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    //protected LodCalculatorFactory lodCalculatorFactory;
10559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
10659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected TerrainPatch leftNeighbour, topNeighbour, rightNeighbour, bottomNeighbour;
10759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected boolean searchedForNeighboursAlready = false;
10859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
10959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
11059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected float[] lodEntropy;
11159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
11259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public TerrainPatch() {
11359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        super("TerrainPatch");
11459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
11559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
11659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public TerrainPatch(String name) {
11759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        super(name);
11859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
11959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
12059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public TerrainPatch(String name, int size) {
12159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this(name, size, new Vector3f(1,1,1), null, new Vector3f(0,0,0));
12259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
12359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
12459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
12559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Constructor instantiates a new <code>TerrainPatch</code> object. The
12659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * parameters and heightmap data are then processed to generate a
12759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * <code>TriMesh</code> object for rendering.
12859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
12959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param name
13059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			the name of the terrain patch.
13159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param size
13259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			the size of the heightmap.
13359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param stepScale
13459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			the scale for the axes.
13559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param heightMap
13659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			the height data.
13759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param origin
13859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			the origin offset of the patch.
13959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
14059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public TerrainPatch(String name, int size, Vector3f stepScale,
14159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    float[] heightMap, Vector3f origin) {
14259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this(name, size, stepScale, heightMap, origin, size, new Vector2f(), 0);
14359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
14459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
14559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
14659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Constructor instantiates a new <code>TerrainPatch</code> object. The
14759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * parameters and heightmap data are then processed to generate a
14859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * <code>TriMesh</code> object for renderering.
14959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
15059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param name
15159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			the name of the terrain patch.
15259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param size
15359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			the size of the patch.
15459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param stepScale
15559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			the scale for the axes.
15659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param heightMap
15759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			the height data.
15859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param origin
15959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			the origin offset of the patch.
16059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param totalSize
16159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			the total size of the terrain. (Higher if the patch is part of
16259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			a <code>TerrainQuad</code> tree.
16359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param offset
16459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			the offset for texture coordinates.
16559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param offsetAmount
16659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			the total offset amount. Used for texture coordinates.
16759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
16859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public TerrainPatch(String name, int size, Vector3f stepScale,
16959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    float[] heightMap, Vector3f origin, int totalSize,
17059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    Vector2f offset, float offsetAmount) {
17159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        super(name);
17259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.size = size;
17359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.stepScale = stepScale;
17459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.totalSize = totalSize;
17559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.offsetAmount = offsetAmount;
17659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.offset = offset;
17759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
17859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        setLocalTranslation(origin);
17959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
18059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        geomap = new LODGeomap(size, heightMap);
18159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Mesh m = geomap.createMesh(stepScale, new Vector2f(1,1), offset, offsetAmount, totalSize, false);
18259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        setMesh(m);
18359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
18459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
18559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
18659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
18759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * This calculation is slow, so don't use it often.
18859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
18959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void generateLodEntropies() {
19059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] entropies = new float[getMaxLod()+1];
19159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int i = 0; i <= getMaxLod(); i++){
19259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            int curLod = (int) Math.pow(2, i);
19359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            IntBuffer buf = geomap.writeIndexArrayLodDiff(null, curLod, false, false, false, false);
19459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            entropies[i] = EntropyComputeUtil.computeLodEntropy(mesh, buf);
19559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
19659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
19759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        lodEntropy = entropies;
19859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
19959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
20059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public float[] getLodEntropies(){
20159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (lodEntropy == null){
20259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            generateLodEntropies();
20359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
20459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return lodEntropy;
20559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
20659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
20759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    @Deprecated
20859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public FloatBuffer getHeightmap() {
20959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return BufferUtils.createFloatBuffer(geomap.getHeightArray());
21059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
21159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
21259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public float[] getHeightMap() {
21359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return geomap.getHeightArray();
21459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
21559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
21659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
21759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * The maximum lod supported by this terrain patch.
21859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * If the patch size is 32 then the returned value would be log2(32)-2 = 3
21959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * You can then use that value, 3, to see how many times you can divide 32 by 2
22059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * before the terrain gets too un-detailed (can't stitch it any further).
22159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return
22259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
22359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public int getMaxLod() {
22459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (maxLod < 0)
22559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            maxLod = Math.max(1, (int) (FastMath.log(size-1)/FastMath.log(2)) -1); // -1 forces our minimum of 4 triangles wide
22659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
22759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return maxLod;
22859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
22959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
23059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void reIndexGeometry(HashMap<String,UpdatedTerrainPatch> updated, boolean useVariableLod) {
23159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
23259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        UpdatedTerrainPatch utp = updated.get(getName());
23359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
23459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (utp != null && (utp.isReIndexNeeded() || utp.isFixEdges()) ) {
23559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            int pow = (int) Math.pow(2, utp.getNewLod());
23659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            boolean left = utp.getLeftLod() > utp.getNewLod();
23759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            boolean top = utp.getTopLod() > utp.getNewLod();
23859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            boolean right = utp.getRightLod() > utp.getNewLod();
23959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            boolean bottom = utp.getBottomLod() > utp.getNewLod();
24059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
24159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            IntBuffer ib = null;
24259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (useVariableLod)
24359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                ib = geomap.writeIndexArrayLodVariable(null, pow, (int) Math.pow(2, utp.getRightLod()), (int) Math.pow(2, utp.getTopLod()), (int) Math.pow(2, utp.getLeftLod()), (int) Math.pow(2, utp.getBottomLod()));
24459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            else
24559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                ib = geomap.writeIndexArrayLodDiff(null, pow, right, top, left, bottom);
24659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            utp.setNewIndexBuffer(ib);
24759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
24859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
24959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
25059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
25159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
25259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public Vector2f getTex(float x, float z, Vector2f store) {
25359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (x < 0 || z < 0 || x >= size || z >= size) {
25459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            store.set(Vector2f.ZERO);
25559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return store;
25659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
25759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int idx = (int) (z * size + x);
25859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return store.set(getMesh().getFloatBuffer(Type.TexCoord).get(idx*2),
25959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                         getMesh().getFloatBuffer(Type.TexCoord).get(idx*2+1) );
26059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
26159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
26259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public float getHeightmapHeight(float x, float z) {
26359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (x < 0 || z < 0 || x >= size || z >= size)
26459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return 0;
26559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int idx = (int) (z * size + x);
26659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return getMesh().getFloatBuffer(Type.Position).get(idx*3+1); // 3 floats per entry (x,y,z), the +1 is to get the Y
26759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
26859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
26959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
27059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Get the triangle of this geometry at the specified local coordinate.
27159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param x local to the terrain patch
27259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param z local to the terrain patch
27359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return the triangle in world coordinates, or null if the point does intersect this patch on the XZ axis
27459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
27559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public Triangle getTriangle(float x, float z) {
27659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return geomap.getTriangleAtPoint(x, z, getWorldScale() , getWorldTranslation());
27759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
27859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
27959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
28059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Get the triangles at the specified grid point. Probably only 2 triangles
28159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param x local to the terrain patch
28259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param z local to the terrain patch
28359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return the triangles in world coordinates, or null if the point does intersect this patch on the XZ axis
28459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
28559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public Triangle[] getGridTriangles(float x, float z) {
28659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return geomap.getGridTrianglesAtPoint(x, z, getWorldScale() , getWorldTranslation());
28759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
28859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
28959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void setHeight(List<LocationHeight> locationHeights, boolean overrideHeight) {
29059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
29159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (LocationHeight lh : locationHeights) {
29259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (lh.x < 0 || lh.z < 0 || lh.x >= size || lh.z >= size)
29359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                continue;
29459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            int idx = lh.z * size + lh.x;
29559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (overrideHeight) {
29659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                geomap.getHeightArray()[idx] = lh.h;
29759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            } else {
29859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                float h = getMesh().getFloatBuffer(Type.Position).get(idx*3+1);
29959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                geomap.getHeightArray()[idx] = h+lh.h;
30059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
30159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
30259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
30359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
30459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        FloatBuffer newVertexBuffer = geomap.writeVertexArray(null, stepScale, false);
30559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        getMesh().clearBuffer(Type.Position);
30659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        getMesh().setBuffer(Type.Position, 3, newVertexBuffer);
30759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
30859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
30959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
31059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * recalculate all of the normal vectors in this terrain patch
31159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
31259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void updateNormals() {
31359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        FloatBuffer newNormalBuffer = geomap.writeNormalArray(null, getWorldScale());
31459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        getMesh().getBuffer(Type.Normal).updateData(newNormalBuffer);
31559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        FloatBuffer newTangentBuffer = null;
31659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        FloatBuffer newBinormalBuffer = null;
317a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta        FloatBuffer[] tb = geomap.writeTangentArray(newNormalBuffer, newTangentBuffer, newBinormalBuffer, (FloatBuffer)getMesh().getBuffer(Type.TexCoord).getData(), getWorldScale());
31859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        newTangentBuffer = tb[0];
31959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        newBinormalBuffer = tb[1];
32059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        getMesh().getBuffer(Type.Tangent).updateData(newTangentBuffer);
32159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        getMesh().getBuffer(Type.Binormal).updateData(newBinormalBuffer);
32259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
32359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
324a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta    private void setInBuffer(Mesh mesh, int index, Vector3f normal, Vector3f tangent, Vector3f binormal) {
325a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta        VertexBuffer NB = mesh.getBuffer(Type.Normal);
326a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta        VertexBuffer TB = mesh.getBuffer(Type.Tangent);
327a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta        VertexBuffer BB = mesh.getBuffer(Type.Binormal);
328a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta        BufferUtils.setInBuffer(normal, (FloatBuffer)NB.getData(), index);
329a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta        BufferUtils.setInBuffer(tangent, (FloatBuffer)TB.getData(), index);
330a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta        BufferUtils.setInBuffer(binormal, (FloatBuffer)BB.getData(), index);
331a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta        NB.setUpdateNeeded();
332a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta        TB.setUpdateNeeded();
333a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta        BB.setUpdateNeeded();
334a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta    }
335a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta
33659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
33759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Matches the normals along the edge of the patch with the neighbours.
33859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Computes the normals for the right, bottom, left, and top edges of the
33959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * patch, and saves those normals in the neighbour's edges too.
34059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
34159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Takes 4 points (if has neighbour on that side) for each
34259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * point on the edge of the patch:
34359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *              *
34459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *              |
34559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *          *---x---*
34659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *              |
34759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *              *
34859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * It works across the right side of the patch, from the top down to
34959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * the bottom. Then it works on the bottom side of the patch, from the
35059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * left to the right.
35159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
35259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void fixNormalEdges(TerrainPatch right,
35359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                TerrainPatch bottom,
35459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                TerrainPatch top,
35559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                TerrainPatch left,
35659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                TerrainPatch bottomRight,
35759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                TerrainPatch bottomLeft,
35859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                TerrainPatch topRight,
35959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                TerrainPatch topLeft)
36059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    {
36159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f rootPoint = new Vector3f();
36259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f rightPoint = new Vector3f();
36359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f leftPoint = new Vector3f();
36459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f topPoint = new Vector3f();
36559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
36659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f bottomPoint = new Vector3f();
36759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
36859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f tangent = new Vector3f();
36959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f binormal = new Vector3f();
37059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f normal = new Vector3f();
37159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
372a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta
37359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int s = this.getSize()-1;
37459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
37559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (right != null) { // right side,    works its way down
37659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int i=0; i<s+1; i++) {
377a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                rootPoint.set(0, this.getHeightmapHeight(s,i), 0);
378a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                leftPoint.set(-1, this.getHeightmapHeight(s-1,i), 0);
379a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                rightPoint.set(1, right.getHeightmapHeight(1,i), 0);
38059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
38159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (i == 0) { // top point
382a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    bottomPoint.set(0, this.getHeightmapHeight(s,i+1), 1);
383a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta
38459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if (top == null) {
385a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint,  normal, tangent, binormal);
386a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(this.getMesh(), s, normal, tangent, binormal);
387a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(right.getMesh(), 0, normal, tangent, binormal);
38859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    } else {
389a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        topPoint.set(0, top.getHeightmapHeight(s,s-1), -1);
39059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
391a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint,normal, tangent, binormal);
392a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(this.getMesh(), s, normal, tangent, binormal);
393a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(right.getMesh(), 0, normal, tangent, binormal);
394a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(top.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);
39559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
39659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        if (topRight != null) {
397a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    //        setInBuffer(topRight.getMesh(), (s+1)*s, normal, tangent, binormal);
39859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        }
39959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    }
40059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (i == s) { // bottom point
401a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    topPoint.set(0, this.getHeightmapHeight(s,s-1), -1);
402a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta
40359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if (bottom == null) {
404a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, normal, tangent, binormal);
405a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(this.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);
406a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(right.getMesh(), (s+1)*(s), normal, tangent, binormal);
40759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    } else {
408a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        bottomPoint.set(0, bottom.getHeightmapHeight(s,1), 1);
409a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
410a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(this.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);
411a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(right.getMesh(), (s+1)*s, normal, tangent, binormal);
412a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(bottom.getMesh(), s, normal, tangent, binormal);
41359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
41459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        if (bottomRight != null) {
415a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                   //         setInBuffer(bottomRight.getMesh(), 0, normal, tangent, binormal);
41659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        }
41759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    }
41859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else { // all in the middle
419a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    topPoint.set(0, this.getHeightmapHeight(s,i-1), -1);
420a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    bottomPoint.set(0, this.getHeightmapHeight(s,i+1), 1);
421a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
422a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    setInBuffer(this.getMesh(), (s+1)*(i+1)-1, normal, tangent, binormal);
423a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    setInBuffer(right.getMesh(), (s+1)*(i), normal, tangent, binormal);
42459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
42559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
42659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
42759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
42859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (left != null) { // left side,    works its way down
42959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int i=0; i<s+1; i++) {
430a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                rootPoint.set(0, this.getHeightmapHeight(0,i), 0);
431a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                leftPoint.set(-1, left.getHeightmapHeight(s-1,i), 0);
432a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                rightPoint.set(1, this.getHeightmapHeight(1,i), 0);
43359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
43459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (i == 0) { // top point
435a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    bottomPoint.set(0, this.getHeightmapHeight(0,i+1), 1);
436a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta
43759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if (top == null) {
438a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
439a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(this.getMesh(), 0, normal, tangent, binormal);
440a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(left.getMesh(), s, normal, tangent, binormal);
44159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    } else {
442a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        topPoint.set(0, top.getHeightmapHeight(0,s-1), -1);
44359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
444a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
445a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(this.getMesh(), 0, normal, tangent, binormal);
446a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(left.getMesh(), s, normal, tangent, binormal);
447a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(top.getMesh(), (s+1)*s, normal, tangent, binormal);
44859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
44959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        if (topLeft != null) {
450a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                     //       setInBuffer(topLeft.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);
45159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        }
45259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    }
45359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (i == s) { // bottom point
454a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    topPoint.set(0, this.getHeightmapHeight(0,i-1), -1);
455a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta
45659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if (bottom == null) {
457a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, normal, tangent, binormal);
458a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(this.getMesh(), (s+1)*(s), normal, tangent, binormal);
459a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(left.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);
46059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    } else {
461a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        bottomPoint.set(0, bottom.getHeightmapHeight(0,1), 1);
46259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
463a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
464a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(this.getMesh(), (s+1)*(s), normal, tangent, binormal);
465a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(left.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal);
466a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                        setInBuffer(bottom.getMesh(), 0, normal, tangent, binormal);
46759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
46859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        if (bottomLeft != null) {
469a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                     //       setInBuffer(bottomLeft.getMesh(), s, normal, tangent, binormal);
47059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        }
47159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    }
47259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else { // all in the middle
473a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    topPoint.set(0, this.getHeightmapHeight(0,i-1), -1);
474a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    bottomPoint.set(0, this.getHeightmapHeight(0,i+1), 1);
47559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
476a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
477a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    setInBuffer(this.getMesh(), (s+1)*(i), normal, tangent, binormal);
478a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    setInBuffer(left.getMesh(), (s+1)*(i+1)-1, normal, tangent, binormal);
47959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
48059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
48159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
482a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta
48359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (top != null) { // top side,    works its way right
48459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int i=0; i<s+1; i++) {
485a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                rootPoint.set(0, this.getHeightmapHeight(i,0), 0);
486a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                topPoint.set(0, top.getHeightmapHeight(i,s-1), -1);
487a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                bottomPoint.set(0, this.getHeightmapHeight(i,1), 1);
48859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
48959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (i == 0) { // left corner
49059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    // handled by left side pass
49159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
49259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (i == s) { // right corner
49359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
49459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    // handled by this patch when it does its right side
49559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
49659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else { // all in the middle
497a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    leftPoint.set(-1, this.getHeightmapHeight(i-1,0), 0);
498a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    rightPoint.set(1, this.getHeightmapHeight(i+1,0), 0);
499a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
500a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    setInBuffer(this.getMesh(), i, normal, tangent, binormal);
501a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    setInBuffer(top.getMesh(), (s+1)*(s)+i, normal, tangent, binormal);
50259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
50359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
50459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
50559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
50659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
50759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (bottom != null) { // bottom side,    works its way right
50859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int i=0; i<s+1; i++) {
509a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                rootPoint.set(0, this.getHeightmapHeight(i,s), 0);
510a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                topPoint.set(0, this.getHeightmapHeight(i,s-1), -1);
511a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                bottomPoint.set(0, bottom.getHeightmapHeight(i,1), 1);
51259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
51359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (i == 0) { // left
51459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    // handled by the left side pass
51559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
51659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (i == s) { // right
51759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
518a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    // handled by the right side pass
51959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
52059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else { // all in the middle
521a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    leftPoint.set(-1, this.getHeightmapHeight(i-1,s), 0);
522a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    rightPoint.set(1, this.getHeightmapHeight(i+1,s), 0);
523a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal);
524a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    setInBuffer(this.getMesh(), (s+1)*(s)+i, normal, tangent, binormal);
525a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    setInBuffer(bottom.getMesh(), i, normal, tangent, binormal);
52659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
52759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
52859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
52959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
53059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
53159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
53259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void averageNormalsTangents(
53359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Vector3f topPoint,
53459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Vector3f rootPoint,
53559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Vector3f leftPoint,
53659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Vector3f bottomPoint,
53759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Vector3f rightPoint,
53859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Vector3f normal,
53959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Vector3f tangent,
54059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Vector3f binormal)
54159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    {
54259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f scale = getWorldScale();
54359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
544a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta        Vector3f n1 = new Vector3f(0,0,0);
54559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (topPoint != null && leftPoint != null) {
546a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta            n1.set(calculateNormal(topPoint.mult(scale), rootPoint.mult(scale), leftPoint.mult(scale)));
54759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
548a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta        Vector3f n2 = new Vector3f(0,0,0);
54959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (leftPoint != null && bottomPoint != null) {
550a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta            n2.set(calculateNormal(leftPoint.mult(scale), rootPoint.mult(scale), bottomPoint.mult(scale)));
55159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
552a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta        Vector3f n3 = new Vector3f(0,0,0);
55359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (rightPoint != null && bottomPoint != null) {
554a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta            n3.set(calculateNormal(bottomPoint.mult(scale), rootPoint.mult(scale), rightPoint.mult(scale)));
55559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
556a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta        Vector3f n4 = new Vector3f(0,0,0);
55759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (rightPoint != null && topPoint != null) {
558a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta            n4.set(calculateNormal(rightPoint.mult(scale), rootPoint.mult(scale), topPoint.mult(scale)));
55959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
56059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
561a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta        //if (bottomPoint != null && rightPoint != null && rootTex != null && rightTex != null && bottomTex != null)
562a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta        //    LODGeomap.calculateTangent(new Vector3f[]{rootPoint.mult(scale),rightPoint.mult(scale),bottomPoint.mult(scale)}, new Vector2f[]{rootTex,rightTex,bottomTex}, tangent, binormal);
56359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
564a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta        normal.set(n1.add(n2).add(n3).add(n4).normalize());
565a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta
566a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta        tangent.set(normal.cross(new Vector3f(0,0,1)).normalize());
567a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta        binormal.set(new Vector3f(1,0,0).cross(normal).normalize());
56859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
56959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
57059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private Vector3f calculateNormal(Vector3f firstPoint, Vector3f rootPoint, Vector3f secondPoint) {
57159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f normal = new Vector3f();
57259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        normal.set(firstPoint).subtractLocal(rootPoint)
57359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                  .crossLocal(secondPoint.subtract(rootPoint)).normalizeLocal();
57459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return normal;
57559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
57659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
57759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected Vector3f getMeshNormal(int x, int z) {
57859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (x >= size || z >= size)
57959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return null; // out of range
58059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
58159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int index = (z*size+x)*3;
58259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        FloatBuffer nb = (FloatBuffer)this.getMesh().getBuffer(Type.Normal).getData();
58359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f normal = new Vector3f();
58459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        normal.x = nb.get(index);
58559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        normal.y = nb.get(index+1);
58659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        normal.z = nb.get(index+2);
58759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return normal;
58859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
58959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
59059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
59159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Locks the mesh (sets it static) to improve performance.
59259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * But it it not editable then. Set unlock to make it editable.
59359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
59459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void lockMesh() {
59559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        getMesh().setStatic();
59659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
59759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
59859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
59959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Unlocks the mesh (sets it dynamic) to make it editable.
60059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * It will be editable but performance will be reduced.
60159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Call lockMesh to improve performance.
60259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
60359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void unlockMesh() {
60459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        getMesh().setDynamic();
60559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
60659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
60759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
60859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Returns the offset amount this terrain patch uses for textures.
60959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
61059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return The current offset amount.
61159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
61259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public float getOffsetAmount() {
61359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return offsetAmount;
61459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
61559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
61659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
61759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Returns the step scale that stretches the height map.
61859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
61959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return The current step scale.
62059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
62159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public Vector3f getStepScale() {
62259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return stepScale;
62359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
62459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
62559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
62659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Returns the total size of the terrain.
62759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
62859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return The terrain's total size.
62959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
63059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public int getTotalSize() {
63159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return totalSize;
63259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
63359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
63459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
63559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Returns the size of this terrain patch.
63659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
63759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return The current patch size.
63859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
63959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public int getSize() {
64059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return size;
64159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
64259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
64359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
64459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Returns the current offset amount. This is used when building texture
64559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * coordinates.
64659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
64759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return The current offset amount.
64859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
64959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public Vector2f getOffset() {
65059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return offset;
65159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
65259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
65359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
65459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Sets the value for the current offset amount to use when building texture
65559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * coordinates. Note that this does <b>NOT </b> rebuild the terrain at all.
65659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * This is mostly used for outside constructors of terrain patches.
65759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
65859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param offset
65959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			The new texture offset.
66059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
66159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void setOffset(Vector2f offset) {
66259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.offset = offset;
66359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
66459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
66559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
66659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Sets the size of this terrain patch. Note that this does <b>NOT </b>
66759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * rebuild the terrain at all. This is mostly used for outside constructors
66859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * of terrain patches.
66959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
67059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param size
67159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			The new size.
67259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
67359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void setSize(int size) {
67459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.size = size;
67559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
67659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        maxLod = -1; // reset it
67759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
67859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
67959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
68059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Sets the total size of the terrain . Note that this does <b>NOT </b>
68159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * rebuild the terrain at all. This is mostly used for outside constructors
68259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * of terrain patches.
68359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
68459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param totalSize
68559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			The new total size.
68659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
68759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void setTotalSize(int totalSize) {
68859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.totalSize = totalSize;
68959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
69059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
69159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
69259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Sets the step scale of this terrain patch's height map. Note that this
69359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * does <b>NOT </b> rebuild the terrain at all. This is mostly used for
69459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * outside constructors of terrain patches.
69559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
69659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param stepScale
69759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			The new step scale.
69859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
69959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void setStepScale(Vector3f stepScale) {
70059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.stepScale = stepScale;
70159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
70259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
70359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
70459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Sets the offset of this terrain texture map. Note that this does <b>NOT
70559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * </b> rebuild the terrain at all. This is mostly used for outside
70659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * constructors of terrain patches.
70759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
70859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param offsetAmount
70959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			The new texture offset.
71059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
71159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void setOffsetAmount(float offsetAmount) {
71259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.offsetAmount = offsetAmount;
71359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
71459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
71559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
71659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @return Returns the quadrant.
71759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
71859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public short getQuadrant() {
71959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return quadrant;
72059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
72159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
72259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
72359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param quadrant
72459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			The quadrant to set.
72559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
72659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void setQuadrant(short quadrant) {
72759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.quadrant = quadrant;
72859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
72959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
73059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public int getLod() {
73159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return lod;
73259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
73359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
73459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void setLod(int lod) {
73559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.lod = lod;
73659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
73759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
73859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public int getPreviousLod() {
73959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return previousLod;
74059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
74159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
74259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void setPreviousLod(int previousLod) {
74359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.previousLod = previousLod;
74459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
74559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
74659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected int getLodLeft() {
74759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return lodLeft;
74859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
74959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
75059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void setLodLeft(int lodLeft) {
75159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.lodLeft = lodLeft;
75259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
75359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
75459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected int getLodTop() {
75559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return lodTop;
75659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
75759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
75859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void setLodTop(int lodTop) {
75959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.lodTop = lodTop;
76059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
76159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
76259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected int getLodRight() {
76359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return lodRight;
76459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
76559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
76659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void setLodRight(int lodRight) {
76759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.lodRight = lodRight;
76859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
76959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
77059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected int getLodBottom() {
77159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return lodBottom;
77259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
77359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
77459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void setLodBottom(int lodBottom) {
77559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.lodBottom = lodBottom;
77659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
77759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
77859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /*public void setLodCalculator(LodCalculatorFactory lodCalculatorFactory) {
77959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.lodCalculatorFactory = lodCalculatorFactory;
78059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        setLodCalculator(lodCalculatorFactory.createCalculator(this));
78159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }*/
78259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
78359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    @Override
78459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException {
78559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (refreshFlags != 0)
78659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            throw new IllegalStateException("Scene graph must be updated" +
78759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                            " before checking collision");
78859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
78959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (other instanceof BoundingVolume)
79059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (!getWorldBound().intersects((BoundingVolume)other))
79159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return 0;
79259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
79359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if(other instanceof Ray)
79459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return collideWithRay((Ray)other, results);
79559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else if (other instanceof BoundingVolume)
79659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return collideWithBoundingVolume((BoundingVolume)other, results);
79759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else {
79859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            throw new UnsupportedCollisionException("TerrainPatch cannnot collide with "+other.getClass().getName());
79959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
80059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
80159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
80259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
80359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private int collideWithRay(Ray ray, CollisionResults results) {
80459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // This should be handled in the root terrain quad
80559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return 0;
80659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
80759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
80859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private int collideWithBoundingVolume(BoundingVolume boundingVolume, CollisionResults results) {
80959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (boundingVolume instanceof BoundingBox)
81059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return collideWithBoundingBox((BoundingBox)boundingVolume, results);
81159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else if(boundingVolume instanceof BoundingSphere) {
81259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            BoundingSphere sphere = (BoundingSphere) boundingVolume;
81359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            BoundingBox bbox = new BoundingBox(boundingVolume.getCenter().clone(), sphere.getRadius(),
81459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                                           sphere.getRadius(),
81559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                                           sphere.getRadius());
81659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return collideWithBoundingBox(bbox, results);
81759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
81859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return 0;
81959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
82059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
82159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected Vector3f worldCoordinateToLocal(Vector3f loc) {
82259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f translated = new Vector3f();
82359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        translated.x = loc.x/getWorldScale().x - getWorldTranslation().x;
82459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        translated.y = loc.y/getWorldScale().y - getWorldTranslation().y;
82559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        translated.z = loc.z/getWorldScale().z - getWorldTranslation().z;
82659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return translated;
82759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
82859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
82959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
83059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * This most definitely is not optimized.
83159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
83259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private int collideWithBoundingBox(BoundingBox bbox, CollisionResults results) {
83359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
83459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // test the four corners, for cases where the bbox dimensions are less than the terrain grid size, which is probably most of the time
83559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f topLeft = worldCoordinateToLocal(new Vector3f(bbox.getCenter().x-bbox.getXExtent(), 0, bbox.getCenter().z-bbox.getZExtent()));
83659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f topRight = worldCoordinateToLocal(new Vector3f(bbox.getCenter().x+bbox.getXExtent(), 0, bbox.getCenter().z-bbox.getZExtent()));
83759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f bottomLeft = worldCoordinateToLocal(new Vector3f(bbox.getCenter().x-bbox.getXExtent(), 0, bbox.getCenter().z+bbox.getZExtent()));
83859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f bottomRight = worldCoordinateToLocal(new Vector3f(bbox.getCenter().x+bbox.getXExtent(), 0, bbox.getCenter().z+bbox.getZExtent()));
83959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
84059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Triangle t = getTriangle(topLeft.x, topLeft.z);
84159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (t != null && bbox.collideWith(t, results) > 0)
84259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return 1;
84359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        t = getTriangle(topRight.x, topRight.z);
84459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (t != null && bbox.collideWith(t, results) > 0)
84559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return 1;
84659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        t = getTriangle(bottomLeft.x, bottomLeft.z);
84759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (t != null && bbox.collideWith(t, results) > 0)
84859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return 1;
84959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        t = getTriangle(bottomRight.x, bottomRight.z);
85059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (t != null && bbox.collideWith(t, results) > 0)
85159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return 1;
85259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
85359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // box is larger than the points on the terrain, so test against the points
85459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (float z=topLeft.z; z<bottomLeft.z; z+=1) {
85559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (float x=topLeft.x; x<topRight.x; x+=1) {
85659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
85759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (x < 0 || z < 0 || x >= size || z >= size)
85859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    continue;
85959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                t = getTriangle(x,z);
86059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (t != null && bbox.collideWith(t, results) > 0)
86159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    return 1;
86259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
86359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
86459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
86559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return 0;
86659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
86759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
86859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
86959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    @Override
87059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void write(JmeExporter ex) throws IOException {
87159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // the mesh is removed, and reloaded when read() is called
87259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // this reduces the save size to 10% by not saving the mesh
87359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Mesh temp = getMesh();
87459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        mesh = null;
87559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
87659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        super.write(ex);
87759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        OutputCapsule oc = ex.getCapsule(this);
87859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        oc.write(size, "size", 16);
87959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        oc.write(totalSize, "totalSize", 16);
88059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        oc.write(quadrant, "quadrant", (short)0);
88159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        oc.write(stepScale, "stepScale", Vector3f.UNIT_XYZ);
88259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        oc.write(offset, "offset", Vector3f.UNIT_XYZ);
88359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        oc.write(offsetAmount, "offsetAmount", 0);
88459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //oc.write(lodCalculator, "lodCalculator", null);
88559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //oc.write(lodCalculatorFactory, "lodCalculatorFactory", null);
88659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        oc.write(lodEntropy, "lodEntropy", null);
88759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        oc.write(geomap, "geomap", null);
88859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
88959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        setMesh(temp);
89059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
89159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
89259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    @Override
89359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void read(JmeImporter im) throws IOException {
89459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        super.read(im);
89559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        InputCapsule ic = im.getCapsule(this);
89659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        size = ic.readInt("size", 16);
89759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        totalSize = ic.readInt("totalSize", 16);
89859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        quadrant = ic.readShort("quadrant", (short)0);
89959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        stepScale = (Vector3f) ic.readSavable("stepScale", Vector3f.UNIT_XYZ);
90059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        offset = (Vector2f) ic.readSavable("offset", Vector3f.UNIT_XYZ);
90159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        offsetAmount = ic.readFloat("offsetAmount", 0);
90259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //lodCalculator = (LodCalculator) ic.readSavable("lodCalculator", new DistanceLodCalculator());
90359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //lodCalculator.setTerrainPatch(this);
90459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //lodCalculatorFactory = (LodCalculatorFactory) ic.readSavable("lodCalculatorFactory", null);
90559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        lodEntropy = ic.readFloatArray("lodEntropy", null);
90659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        geomap = (LODGeomap) ic.readSavable("geomap", null);
90759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
90859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Mesh regen = geomap.createMesh(stepScale, new Vector2f(1,1), offset, offsetAmount, totalSize, false);
90959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        setMesh(regen);
91059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //TangentBinormalGenerator.generate(this); // note that this will be removed
91159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        ensurePositiveVolumeBBox();
91259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
91359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
91459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    @Override
91559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public TerrainPatch clone() {
91659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TerrainPatch clone = new TerrainPatch();
91759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        clone.name = name.toString();
91859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        clone.size = size;
91959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        clone.totalSize = totalSize;
92059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        clone.quadrant = quadrant;
92159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        clone.stepScale = stepScale.clone();
92259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        clone.offset = offset.clone();
92359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        clone.offsetAmount = offsetAmount;
92459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //clone.lodCalculator = lodCalculator.clone();
92559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //clone.lodCalculator.setTerrainPatch(clone);
92659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //clone.setLodCalculator(lodCalculatorFactory.clone());
92759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        clone.geomap = new LODGeomap(size, geomap.getHeightArray());
92859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        clone.setLocalTranslation(getLocalTranslation().clone());
92959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Mesh m = clone.geomap.createMesh(clone.stepScale, Vector2f.UNIT_XY, clone.offset, clone.offsetAmount, clone.totalSize, false);
93059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        clone.setMesh(m);
93159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        clone.setMaterial(material.clone());
93259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return clone;
93359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
93459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
93559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void ensurePositiveVolumeBBox() {
93659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (getModelBound() instanceof BoundingBox) {
93759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (((BoundingBox)getModelBound()).getYExtent() < 0.001f) {
93859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                // a correction so the box always has a volume
93959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                ((BoundingBox)getModelBound()).setYExtent(0.001f);
94059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                updateWorldBound();
94159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
94259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
94359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
94459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
94559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
94659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
94759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta}
948