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.BoundingVolume;
3759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.collision.Collidable;
3859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.collision.CollisionResults;
3959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.export.InputCapsule;
4059b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.export.JmeExporter;
4159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.export.JmeImporter;
4259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.export.OutputCapsule;
4359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.material.Material;
4459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.math.FastMath;
4559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.math.Ray;
4659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.math.Vector2f;
4759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.math.Vector3f;
4859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.Geometry;
4959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.Node;
5059b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.Spatial;
5159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.debug.WireBox;
5259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.terrain.ProgressMonitor;
5359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.terrain.Terrain;
5459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
5559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.terrain.geomipmap.picking.BresenhamTerrainPicker;
5659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.terrain.geomipmap.picking.TerrainPickData;
5759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.terrain.geomipmap.picking.TerrainPicker;
5859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.util.TangentBinormalGenerator;
5959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.io.IOException;
6059b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.ArrayList;
6159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.HashMap;
6259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.List;
6359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.Map;
6459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.concurrent.ExecutorService;
6559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.concurrent.Executors;
6659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.concurrent.ThreadFactory;
6759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.logging.Level;
6859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.logging.Logger;
6959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
7059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/**
7159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * A terrain quad is a node in the quad tree of the terrain system.
7259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * The root terrain quad will be the only one that receives the update() call every frame
7359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * and it will determine if there has been any LOD change.
7459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
7559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * The leaves of the terrain quad tree are Terrain Patches. These have the real geometry mesh.
7659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
7759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
7859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * Heightmap coordinates start from the bottom left of the world and work towards the
7959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * top right.
8059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
8159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  +x
8259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  ^
8359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  | ......N = length of heightmap
8459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  | :     :
8559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  | :     :
8659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  | 0.....:
8759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *  +---------> +z
8859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * (world coordinates)
8959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta *
9059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @author Brent Owens
9159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */
9259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapublic class TerrainQuad extends Node implements Terrain {
9359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
9459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected Vector2f offset;
9559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
9659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected int totalSize; // the size of this entire terrain tree (on one side)
9759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
9859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected int size; // size of this quad, can be between totalSize and patchSize
9959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
10059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected int patchSize; // size of the individual patches
10159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
10259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected Vector3f stepScale;
10359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
10459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected float offsetAmount;
10559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
10659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected int quadrant = 0; // 1=upper left, 2=lower left, 3=upper right, 4=lower right
10759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
10859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    //protected LodCalculatorFactory lodCalculatorFactory;
10959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    //protected LodCalculator lodCalculator;
11059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
11159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected List<Vector3f> lastCameraLocations; // used for LOD calc
11259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private boolean lodCalcRunning = false;
11359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private int lodOffCount = 0;
11459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private int maxLod = -1;
11559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private HashMap<String,UpdatedTerrainPatch> updatedPatches;
11659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private final Object updatePatchesLock = new Object();
11759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private BoundingBox affectedAreaBBox; // only set in the root quad
11859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
11959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private TerrainPicker picker;
12059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private Vector3f lastScale = Vector3f.UNIT_XYZ;
12159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
12259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected ExecutorService executor;
12359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
12459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected ExecutorService createExecutorService() {
12559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return Executors.newSingleThreadExecutor(new ThreadFactory() {
12659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            public Thread newThread(Runnable r) {
12759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Thread th = new Thread(r);
12859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                th.setName("jME Terrain Thread");
12959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                th.setDaemon(true);
13059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return th;
13159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
13259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        });
13359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
13459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
13559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public TerrainQuad() {
13659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        super("Terrain");
13759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
13859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
13959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
14059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
14159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param name the name of the scene element. This is required for
14259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * identification and comparison purposes.
14359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param patchSize size of the individual patches
14459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param totalSize the size of this entire terrain tree (on one side)
14559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param heightMap The height map to generate the terrain from (a flat
14659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * height map will be generated if this is null)
14759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
14859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public TerrainQuad(String name, int patchSize, int totalSize, float[] heightMap) {
14959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this(name, patchSize, totalSize, Vector3f.UNIT_XYZ, heightMap);
15059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
15159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
15259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
15359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
15459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param name the name of the scene element. This is required for
15559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * identification and comparison purposes.
15659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param patchSize size of the individual patches
15759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param quadSize
15859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param totalSize the size of this entire terrain tree (on one side)
15959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param heightMap The height map to generate the terrain from (a flat
16059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * height map will be generated if this is null)
16159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
16259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public TerrainQuad(String name, int patchSize, int quadSize, int totalSize, float[] heightMap) {
16359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this(name, patchSize, totalSize, quadSize, Vector3f.UNIT_XYZ, heightMap);
16459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
16559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
16659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
16759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
16859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param name the name of the scene element. This is required for
16959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * identification and comparison purposes.
17059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param patchSize size of the individual patches
17159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param size size of this quad, can be between totalSize and patchSize
17259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param scale
17359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param heightMap The height map to generate the terrain from (a flat
17459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * height map will be generated if this is null)
17559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
17659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public TerrainQuad(String name, int patchSize, int size, Vector3f scale, float[] heightMap) {
17759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this(name, patchSize, size, scale, heightMap, size, new Vector2f(), 0);
17859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size*2, Float.MAX_VALUE, size*2);
17959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        fixNormalEdges(affectedAreaBBox);
18059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        addControl(new NormalRecalcControl(this));
18159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
18259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
18359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
18459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
18559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param name the name of the scene element. This is required for
18659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * identification and comparison purposes.
18759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param patchSize size of the individual patches
18859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param totalSize the size of this entire terrain tree (on one side)
18959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param quadSize
19059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param scale
19159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param heightMap The height map to generate the terrain from (a flat
19259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * height map will be generated if this is null)
19359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
19459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public TerrainQuad(String name, int patchSize, int totalSize, int quadSize, Vector3f scale, float[] heightMap) {
19559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this(name, patchSize, quadSize, scale, heightMap, totalSize, new Vector2f(), 0);
19659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), totalSize*2, Float.MAX_VALUE, totalSize*2);
19759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        fixNormalEdges(affectedAreaBBox);
19859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        addControl(new NormalRecalcControl(this));
19959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
20059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
20159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected TerrainQuad(String name, int patchSize, int quadSize,
20259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            Vector3f scale, float[] heightMap, int totalSize,
20359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            Vector2f offset, float offsetAmount)
20459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    {
20559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        super(name);
20659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
20759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (heightMap == null)
20859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            heightMap = generateDefaultHeightMap(quadSize);
20959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
21059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (!FastMath.isPowerOfTwo(quadSize - 1)) {
21159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            throw new RuntimeException("size given: " + quadSize + "  Terrain quad sizes may only be (2^N + 1)");
21259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
21359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (FastMath.sqrt(heightMap.length) > quadSize) {
21459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Heightmap size is larger than the terrain size. Make sure your heightmap image is the same size as the terrain!");
21559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
21659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
21759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.offset = offset;
21859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.offsetAmount = offsetAmount;
21959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.totalSize = totalSize;
22059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.size = quadSize;
22159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.patchSize = patchSize;
22259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.stepScale = scale;
22359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //this.lodCalculatorFactory = lodCalculatorFactory;
22459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //this.lodCalculator = lodCalculator;
22559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        split(patchSize, heightMap);
22659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
22759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
22859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /*public void setLodCalculatorFactory(LodCalculatorFactory lodCalculatorFactory) {
22959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (children != null) {
23059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int i = children.size(); --i >= 0;) {
23159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Spatial child = children.get(i);
23259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (child instanceof TerrainQuad) {
23359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    ((TerrainQuad) child).setLodCalculatorFactory(lodCalculatorFactory);
23459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (child instanceof TerrainPatch) {
23559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    ((TerrainPatch) child).setLodCalculator(lodCalculatorFactory.createCalculator((TerrainPatch) child));
23659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
23759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
23859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
23959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }*/
24059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
24159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
24259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
24359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Create just a flat heightmap
24459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
24559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private float[] generateDefaultHeightMap(int size) {
24659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] heightMap = new float[size*size];
24759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return heightMap;
24859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
24959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
25059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     /**
25159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta      * Call from the update() method of a terrain controller to update
25259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta      * the LOD values of each patch.
25359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta      * This will perform the geometry calculation in a background thread and
25459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta      * do the actual update on the opengl thread.
25559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta      */
25659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void update(List<Vector3f> locations, LodCalculator lodCalculator) {
25759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        updateLOD(locations, lodCalculator);
25859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
25959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
26059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
26159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * update the normals if there were any height changes recently.
26259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Should only be called on the root quad
26359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
26459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void updateNormals() {
26559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
26659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (needToRecalculateNormals()) {
26759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            //TODO background-thread this if it ends up being expensive
26859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            fixNormals(affectedAreaBBox); // the affected patches
26959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            fixNormalEdges(affectedAreaBBox); // the edges between the patches
27059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
27159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            setNormalRecalcNeeded(null); // set to false
27259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
27359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
27459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
27559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    // do all of the LOD calculations
27659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void updateLOD(List<Vector3f> locations, LodCalculator lodCalculator) {
27759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // update any existing ones that need updating
27859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        updateQuadLODs();
27959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
28059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (lodCalculator.isLodOff()) {
28159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // we want to calculate the base lod at least once
28259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (lodOffCount == 1)
28359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return;
28459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            else
28559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                lodOffCount++;
28659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else
28759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            lodOffCount = 0;
28859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
28959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (lastCameraLocations != null) {
29059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (lastCameraLocationsTheSame(locations) && !lodCalculator.isLodOff())
29159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return; // don't update if in same spot
29259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            else
29359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                lastCameraLocations = cloneVectorList(locations);
29459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
29559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else {
29659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            lastCameraLocations = cloneVectorList(locations);
29759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return;
29859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
29959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
30059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (isLodCalcRunning()) {
30159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return;
30259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
30359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
30459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (getParent() instanceof TerrainQuad) {
30559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return; // we just want the root quad to perform this.
30659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
30759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
30859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (executor == null)
30959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            executor = createExecutorService();
31059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
31159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        UpdateLOD updateLodThread = new UpdateLOD(locations, lodCalculator);
31259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        executor.execute(updateLodThread);
31359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
31459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
31559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private synchronized boolean isLodCalcRunning() {
31659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return lodCalcRunning;
31759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
31859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
31959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private synchronized void setLodCalcRunning(boolean running) {
32059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        lodCalcRunning = running;
32159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
32259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
32359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private List<Vector3f> cloneVectorList(List<Vector3f> locations) {
32459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        List<Vector3f> cloned = new ArrayList<Vector3f>();
32559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for(Vector3f l : locations)
32659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            cloned.add(l.clone());
32759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return cloned;
32859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
32959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
33059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private boolean lastCameraLocationsTheSame(List<Vector3f> locations) {
33159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        boolean theSame = true;
33259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (Vector3f l : locations) {
33359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (Vector3f v : lastCameraLocations) {
33459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (!v.equals(l) ) {
33559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    theSame = false;
33659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    return false;
33759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
33859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
33959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
34059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return theSame;
34159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
34259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
34359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private int collideWithRay(Ray ray, CollisionResults results) {
34459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (picker == null)
34559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            picker = new BresenhamTerrainPicker(this);
34659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
34759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f intersection = picker.getTerrainIntersection(ray, results);
34859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (intersection != null)
34959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return 1;
35059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else
35159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return 0;
35259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
35359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
35459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
35559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Generate the entropy values for the terrain for the "perspective" LOD
35659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * calculator. This routine can take a long time to run!
35759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param progressMonitor optional
35859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
35959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void generateEntropy(ProgressMonitor progressMonitor) {
36059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // only check this on the root quad
36159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (isRootQuad())
36259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (progressMonitor != null) {
36359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                int numCalc = (totalSize-1)/(patchSize-1); // make it an even number
36459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                progressMonitor.setMonitorMax(numCalc*numCalc);
36559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
36659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
36759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (children != null) {
36859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int i = children.size(); --i >= 0;) {
36959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Spatial child = children.get(i);
37059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (child instanceof TerrainQuad) {
37159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        ((TerrainQuad) child).generateEntropy(progressMonitor);
37259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (child instanceof TerrainPatch) {
37359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    ((TerrainPatch) child).generateLodEntropies();
37459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if (progressMonitor != null)
37559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        progressMonitor.incrementProgress(1);
37659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
37759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
37859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
37959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
38059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // only do this on the root quad
38159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (isRootQuad())
38259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (progressMonitor != null)
38359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                progressMonitor.progressComplete();
38459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
38559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
38659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected boolean isRootQuad() {
38759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return (getParent() != null && !(getParent() instanceof TerrainQuad) );
38859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
38959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
39059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public Material getMaterial() {
39159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return getMaterial(null);
39259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
39359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
39459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public Material getMaterial(Vector3f worldLocation) {
39559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // get the material from one of the children. They all share the same material
39659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (children != null) {
39759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int i = children.size(); --i >= 0;) {
39859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Spatial child = children.get(i);
39959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (child instanceof TerrainQuad) {
40059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    return ((TerrainQuad)child).getMaterial(worldLocation);
40159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (child instanceof TerrainPatch) {
40259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    return ((TerrainPatch)child).getMaterial();
40359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
40459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
40559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
40659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return null;
40759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
40859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
40959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    //public float getTextureCoordinateScale() {
41059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    //    return 1f/(float)totalSize;
41159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    //}
41259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public int getNumMajorSubdivisions() {
41359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return 1;
41459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
41559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
41659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
41759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Calculates the LOD of all child terrain patches.
41859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
41959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private class UpdateLOD implements Runnable {
42059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        private List<Vector3f> camLocations;
42159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        private LodCalculator lodCalculator;
42259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
42359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        UpdateLOD(List<Vector3f> camLocations, LodCalculator lodCalculator) {
42459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            this.camLocations = camLocations;
42559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            this.lodCalculator = lodCalculator;
42659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
42759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
42859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        public void run() {
42959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            long start = System.currentTimeMillis();
43059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (isLodCalcRunning()) {
43159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                //System.out.println("thread already running");
43259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return;
43359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
43459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            //System.out.println("spawned thread "+toString());
43559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            setLodCalcRunning(true);
43659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
43759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // go through each patch and calculate its LOD based on camera distance
43859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            HashMap<String,UpdatedTerrainPatch> updated = new HashMap<String,UpdatedTerrainPatch>();
43959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            boolean lodChanged = calculateLod(camLocations, updated, lodCalculator); // 'updated' gets populated here
44059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
44159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (!lodChanged) {
44259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                // not worth updating anything else since no one's LOD changed
44359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                setLodCalcRunning(false);
44459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return;
44559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
44659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // then calculate its neighbour LOD values for seaming in the shader
44759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            findNeighboursLod(updated);
44859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
44959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            fixEdges(updated); // 'updated' can get added to here
45059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
45159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            reIndexPages(updated, lodCalculator.usesVariableLod());
45259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
45359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            setUpdateQuadLODs(updated); // set back to main ogl thread
45459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
45559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            setLodCalcRunning(false);
45659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            //double duration = (System.currentTimeMillis()-start);
45759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            //System.out.println("terminated in "+duration);
45859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
45959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
46059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
46159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private void setUpdateQuadLODs(HashMap<String,UpdatedTerrainPatch> updated) {
46259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        synchronized (updatePatchesLock) {
46359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            updatedPatches = updated;
46459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
46559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
46659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
46759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
46859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Back on the ogl thread: update the terrain patch geometries
46959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param updatedPatches to be updated
47059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
47159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private void updateQuadLODs() {
47259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        synchronized (updatePatchesLock) {
47359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
47459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (updatedPatches == null || updatedPatches.isEmpty())
47559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return;
47659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
47759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // do the actual geometry update here
47859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (UpdatedTerrainPatch utp : updatedPatches.values()) {
47959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                utp.updateAll();
48059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
48159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
48259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            updatedPatches.clear();
48359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
48459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
48559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
48659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public boolean hasPatchesToUpdate() {
48759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return updatedPatches != null && !updatedPatches.isEmpty();
48859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
48959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
49059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected boolean calculateLod(List<Vector3f> location, HashMap<String,UpdatedTerrainPatch> updates, LodCalculator lodCalculator) {
49159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
49259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        boolean lodChanged = false;
49359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
49459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (children != null) {
49559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int i = children.size(); --i >= 0;) {
49659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Spatial child = children.get(i);
49759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (child instanceof TerrainQuad) {
49859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    boolean b = ((TerrainQuad) child).calculateLod(location, updates, lodCalculator);
49959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if (b)
50059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        lodChanged = true;
50159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (child instanceof TerrainPatch) {
50259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    boolean b = lodCalculator.calculateLod((TerrainPatch) child, location, updates);
50359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if (b)
50459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        lodChanged = true;
50559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
50659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
50759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
50859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
50959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return lodChanged;
51059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
51159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
51259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected synchronized void findNeighboursLod(HashMap<String,UpdatedTerrainPatch> updated) {
51359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (children != null) {
51459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int x = children.size(); --x >= 0;) {
51559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Spatial child = children.get(x);
51659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (child instanceof TerrainQuad) {
51759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    ((TerrainQuad) child).findNeighboursLod(updated);
51859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (child instanceof TerrainPatch) {
51959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
52059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    TerrainPatch patch = (TerrainPatch) child;
52159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if (!patch.searchedForNeighboursAlready) {
52259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        // set the references to the neighbours
52359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        patch.rightNeighbour = findRightPatch(patch);
52459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        patch.bottomNeighbour = findDownPatch(patch);
52559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        patch.leftNeighbour = findLeftPatch(patch);
52659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        patch.topNeighbour = findTopPatch(patch);
52759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        patch.searchedForNeighboursAlready = true;
52859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    }
52959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    TerrainPatch right = patch.rightNeighbour;
53059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    TerrainPatch down = patch.bottomNeighbour;
53159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
53259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    UpdatedTerrainPatch utp = updated.get(patch.getName());
53359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if (utp == null) {
53459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        utp = new UpdatedTerrainPatch(patch, patch.lod);
53559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        updated.put(utp.getName(), utp);
53659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    }
53759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
53859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if (right != null) {
53959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        UpdatedTerrainPatch utpR = updated.get(right.getName());
54059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        if (utpR == null) {
54159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            utpR = new UpdatedTerrainPatch(right, right.lod);
54259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            updated.put(utpR.getName(), utpR);
54359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        }
54459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
54559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        utp.setRightLod(utpR.getNewLod());
54659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        utpR.setLeftLod(utp.getNewLod());
54759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    }
54859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if (down != null) {
54959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        UpdatedTerrainPatch utpD = updated.get(down.getName());
55059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        if (utpD == null) {
55159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            utpD = new UpdatedTerrainPatch(down, down.lod);
55259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            updated.put(utpD.getName(), utpD);
55359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        }
55459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
55559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        utp.setBottomLod(utpD.getNewLod());
55659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        utpD.setTopLod(utp.getNewLod());
55759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    }
55859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
55959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
56059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
56159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
56259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
56359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
56459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
56559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * TerrainQuad caches neighbours for faster LOD checks.
56659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Sometimes you might want to reset this cache (for instance in TerrainGrid)
56759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
56859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void resetCachedNeighbours() {
56959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (children != null) {
57059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int x = children.size(); --x >= 0;) {
57159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Spatial child = children.get(x);
57259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (child instanceof TerrainQuad) {
57359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    ((TerrainQuad) child).resetCachedNeighbours();
57459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (child instanceof TerrainPatch) {
57559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    TerrainPatch patch = (TerrainPatch) child;
57659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    patch.searchedForNeighboursAlready = false;
57759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
57859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
57959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
58059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
58159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
58259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
58359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Find any neighbours that should have their edges seamed because another neighbour
58459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * changed its LOD to a greater value (less detailed)
58559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
58659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected synchronized void fixEdges(HashMap<String,UpdatedTerrainPatch> updated) {
58759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (children != null) {
58859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int x = children.size(); --x >= 0;) {
58959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Spatial child = children.get(x);
59059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (child instanceof TerrainQuad) {
59159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    ((TerrainQuad) child).fixEdges(updated);
59259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (child instanceof TerrainPatch) {
59359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    TerrainPatch patch = (TerrainPatch) child;
59459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    UpdatedTerrainPatch utp = updated.get(patch.getName());
59559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
59659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if(utp != null && utp.lodChanged()) {
59759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        if (!patch.searchedForNeighboursAlready) {
59859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            // set the references to the neighbours
59959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            patch.rightNeighbour = findRightPatch(patch);
60059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            patch.bottomNeighbour = findDownPatch(patch);
60159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            patch.leftNeighbour = findLeftPatch(patch);
60259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            patch.topNeighbour = findTopPatch(patch);
60359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            patch.searchedForNeighboursAlready = true;
60459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        }
60559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        TerrainPatch right = patch.rightNeighbour;
60659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        TerrainPatch down = patch.bottomNeighbour;
60759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        TerrainPatch top = patch.topNeighbour;
60859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        TerrainPatch left = patch.leftNeighbour;
60959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        if (right != null) {
61059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            UpdatedTerrainPatch utpR = updated.get(right.getName());
61159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            if (utpR == null) {
61259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                utpR = new UpdatedTerrainPatch(right, right.lod);
61359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                updated.put(utpR.getName(), utpR);
61459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            }
61559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            utpR.setFixEdges(true);
61659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        }
61759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        if (down != null) {
61859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            UpdatedTerrainPatch utpD = updated.get(down.getName());
61959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            if (utpD == null) {
62059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                utpD = new UpdatedTerrainPatch(down, down.lod);
62159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                updated.put(utpD.getName(), utpD);
62259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            }
62359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            utpD.setFixEdges(true);
62459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        }
62559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        if (top != null){
62659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            UpdatedTerrainPatch utpT = updated.get(top.getName());
62759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            if (utpT == null) {
62859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                utpT = new UpdatedTerrainPatch(top, top.lod);
62959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                updated.put(utpT.getName(), utpT);
63059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            }
63159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            utpT.setFixEdges(true);
63259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        }
63359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        if (left != null){
63459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            UpdatedTerrainPatch utpL = updated.get(left.getName());
63559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            if (utpL == null) {
63659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                utpL = new UpdatedTerrainPatch(left, left.lod);
63759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                updated.put(utpL.getName(), utpL);
63859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            }
63959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            utpL.setFixEdges(true);
64059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        }
64159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    }
64259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
64359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
64459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
64559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
64659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
64759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected synchronized void reIndexPages(HashMap<String,UpdatedTerrainPatch> updated, boolean usesVariableLod) {
64859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (children != null) {
64959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int i = children.size(); --i >= 0;) {
65059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Spatial child = children.get(i);
65159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (child instanceof TerrainQuad) {
65259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    ((TerrainQuad) child).reIndexPages(updated, usesVariableLod);
65359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (child instanceof TerrainPatch) {
65459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    ((TerrainPatch) child).reIndexGeometry(updated, usesVariableLod);
65559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
65659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
65759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
65859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
65959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
66059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
66159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * <code>split</code> divides the heightmap data for four children. The
66259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * children are either quads or patches. This is dependent on the size of the
66359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * children. If the child's size is less than or equal to the set block
66459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * size, then patches are created, otherwise, quads are created.
66559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
66659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param blockSize
66759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			the blocks size to test against.
66859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param heightMap
66959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *			the height data.
67059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
67159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void split(int blockSize, float[] heightMap) {
67259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if ((size >> 1) + 1 <= blockSize) {
67359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            createQuadPatch(heightMap);
67459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else {
67559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            createQuad(blockSize, heightMap);
67659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
67759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
67859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
67959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
68059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
68159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Quadrants, world coordinates, and heightmap coordinates (Y-up):
68259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
68359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *         -z
68459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *      -u |
68559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *    -v  1|3
68659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *  -x ----+---- x
68759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *        2|4 u
68859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *         | v
68959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *         z
69059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * <code>createQuad</code> generates four new quads from this quad.
69159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * The heightmap's top left (0,0) coordinate is at the bottom, -x,-z
69259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * coordinate of the terrain, so it grows in the positive x.z direction.
69359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
69459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void createQuad(int blockSize, float[] heightMap) {
69559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // create 4 terrain quads
69659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int quarterSize = size >> 2;
69759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
69859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int split = (size + 1) >> 1;
69959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
70059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector2f tempOffset = new Vector2f();
70159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        offsetAmount += quarterSize;
70259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
70359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //if (lodCalculator == null)
70459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //    lodCalculator = createDefaultLodCalculator(); // set a default one
70559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
70659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // 1 upper left of heightmap, upper left quad
70759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] heightBlock1 = createHeightSubBlock(heightMap, 0, 0, split);
70859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
70959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f origin1 = new Vector3f(-quarterSize * stepScale.x, 0,
71059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        -quarterSize * stepScale.z);
71159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
71259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset.x = offset.x;
71359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset.y = offset.y;
71459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset.x += origin1.x;
71559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset.y += origin1.z;
71659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
71759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TerrainQuad quad1 = new TerrainQuad(getName() + "Quad1", blockSize,
71859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        split, stepScale, heightBlock1, totalSize, tempOffset,
71959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        offsetAmount);
72059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        quad1.setLocalTranslation(origin1);
72159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        quad1.quadrant = 1;
72259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.attachChild(quad1);
72359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
72459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // 2 lower left of heightmap, lower left quad
72559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] heightBlock2 = createHeightSubBlock(heightMap, 0, split - 1,
72659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        split);
72759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
72859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f origin2 = new Vector3f(-quarterSize * stepScale.x, 0,
72959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        quarterSize * stepScale.z);
73059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
73159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset = new Vector2f();
73259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset.x = offset.x;
73359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset.y = offset.y;
73459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset.x += origin2.x;
73559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset.y += origin2.z;
73659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
73759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TerrainQuad quad2 = new TerrainQuad(getName() + "Quad2", blockSize,
73859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        split, stepScale, heightBlock2, totalSize, tempOffset,
73959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        offsetAmount);
74059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        quad2.setLocalTranslation(origin2);
74159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        quad2.quadrant = 2;
74259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.attachChild(quad2);
74359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
74459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // 3 upper right of heightmap, upper right quad
74559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] heightBlock3 = createHeightSubBlock(heightMap, split - 1, 0,
74659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        split);
74759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
74859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f origin3 = new Vector3f(quarterSize * stepScale.x, 0,
74959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        -quarterSize * stepScale.z);
75059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
75159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset = new Vector2f();
75259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset.x = offset.x;
75359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset.y = offset.y;
75459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset.x += origin3.x;
75559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset.y += origin3.z;
75659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
75759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TerrainQuad quad3 = new TerrainQuad(getName() + "Quad3", blockSize,
75859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        split, stepScale, heightBlock3, totalSize, tempOffset,
75959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        offsetAmount);
76059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        quad3.setLocalTranslation(origin3);
76159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        quad3.quadrant = 3;
76259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.attachChild(quad3);
76359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
76459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // 4 lower right of heightmap, lower right quad
76559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] heightBlock4 = createHeightSubBlock(heightMap, split - 1,
76659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        split - 1, split);
76759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
76859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f origin4 = new Vector3f(quarterSize * stepScale.x, 0,
76959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        quarterSize * stepScale.z);
77059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
77159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset = new Vector2f();
77259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset.x = offset.x;
77359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset.y = offset.y;
77459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset.x += origin4.x;
77559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset.y += origin4.z;
77659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
77759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TerrainQuad quad4 = new TerrainQuad(getName() + "Quad4", blockSize,
77859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        split, stepScale, heightBlock4, totalSize, tempOffset,
77959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        offsetAmount);
78059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        quad4.setLocalTranslation(origin4);
78159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        quad4.quadrant = 4;
78259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.attachChild(quad4);
78359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
78459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
78559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
78659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void generateDebugTangents(Material mat) {
78759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int x = children.size(); --x >= 0;) {
78859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Spatial child = children.get(x);
78959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (child instanceof TerrainQuad) {
79059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                ((TerrainQuad)child).generateDebugTangents(mat);
79159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            } else if (child instanceof TerrainPatch) {
79259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Geometry debug = new Geometry( "Debug " + name,
79359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    TangentBinormalGenerator.genTbnLines( ((TerrainPatch)child).getMesh(), 0.8f));
79459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                attachChild(debug);
79559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                debug.setLocalTranslation(child.getLocalTranslation());
79659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                debug.setCullHint(CullHint.Never);
79759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                debug.setMaterial(mat);
79859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
79959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
80059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
80159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
80259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
80359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * <code>createQuadPatch</code> creates four child patches from this quad.
80459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
80559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void createQuadPatch(float[] heightMap) {
80659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // create 4 terrain patches
80759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int quarterSize = size >> 2;
80859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int halfSize = size >> 1;
80959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int split = (size + 1) >> 1;
81059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
81159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //if (lodCalculator == null)
81259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //    lodCalculator = createDefaultLodCalculator(); // set a default one
81359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
81459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        offsetAmount += quarterSize;
81559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
81659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // 1 lower left
81759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] heightBlock1 = createHeightSubBlock(heightMap, 0, 0, split);
81859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
81959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f origin1 = new Vector3f(-halfSize * stepScale.x, 0, -halfSize
82059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        * stepScale.z);
82159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
82259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector2f tempOffset1 = new Vector2f();
82359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset1.x = offset.x;
82459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset1.y = offset.y;
82559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset1.x += origin1.x / 2;
82659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset1.y += origin1.z / 2;
82759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
82859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TerrainPatch patch1 = new TerrainPatch(getName() + "Patch1", split,
82959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        stepScale, heightBlock1, origin1, totalSize, tempOffset1,
83059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        offsetAmount);
83159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        patch1.setQuadrant((short) 1);
83259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.attachChild(patch1);
83359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        patch1.setModelBound(new BoundingBox());
83459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        patch1.updateModelBound();
83559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //patch1.setLodCalculator(lodCalculator);
83659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //TangentBinormalGenerator.generate(patch1);
83759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
83859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // 2 upper left
83959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] heightBlock2 = createHeightSubBlock(heightMap, 0, split - 1,
84059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        split);
84159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
84259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f origin2 = new Vector3f(-halfSize * stepScale.x, 0, 0);
84359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
84459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector2f tempOffset2 = new Vector2f();
84559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset2.x = offset.x;
84659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset2.y = offset.y;
84759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset2.x += origin1.x / 2;
84859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset2.y += quarterSize * stepScale.z;
84959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
85059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TerrainPatch patch2 = new TerrainPatch(getName() + "Patch2", split,
85159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        stepScale, heightBlock2, origin2, totalSize, tempOffset2,
85259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        offsetAmount);
85359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        patch2.setQuadrant((short) 2);
85459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.attachChild(patch2);
85559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        patch2.setModelBound(new BoundingBox());
85659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        patch2.updateModelBound();
85759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //patch2.setLodCalculator(lodCalculator);
85859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //TangentBinormalGenerator.generate(patch2);
85959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
86059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // 3 lower right
86159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] heightBlock3 = createHeightSubBlock(heightMap, split - 1, 0,
86259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        split);
86359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
86459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f origin3 = new Vector3f(0, 0, -halfSize * stepScale.z);
86559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
86659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector2f tempOffset3 = new Vector2f();
86759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset3.x = offset.x;
86859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset3.y = offset.y;
86959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset3.x += quarterSize * stepScale.x;
87059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset3.y += origin3.z / 2;
87159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
87259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TerrainPatch patch3 = new TerrainPatch(getName() + "Patch3", split,
87359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        stepScale, heightBlock3, origin3, totalSize, tempOffset3,
87459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        offsetAmount);
87559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        patch3.setQuadrant((short) 3);
87659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.attachChild(patch3);
87759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        patch3.setModelBound(new BoundingBox());
87859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        patch3.updateModelBound();
87959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //patch3.setLodCalculator(lodCalculator);
88059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //TangentBinormalGenerator.generate(patch3);
88159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
88259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // 4 upper right
88359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] heightBlock4 = createHeightSubBlock(heightMap, split - 1,
88459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        split - 1, split);
88559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
88659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f origin4 = new Vector3f(0, 0, 0);
88759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
88859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector2f tempOffset4 = new Vector2f();
88959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset4.x = offset.x;
89059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset4.y = offset.y;
89159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset4.x += quarterSize * stepScale.x;
89259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        tempOffset4.y += quarterSize * stepScale.z;
89359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
89459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TerrainPatch patch4 = new TerrainPatch(getName() + "Patch4", split,
89559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        stepScale, heightBlock4, origin4, totalSize, tempOffset4,
89659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        offsetAmount);
89759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        patch4.setQuadrant((short) 4);
89859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.attachChild(patch4);
89959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        patch4.setModelBound(new BoundingBox());
90059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        patch4.updateModelBound();
90159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //patch4.setLodCalculator(lodCalculator);
90259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //TangentBinormalGenerator.generate(patch4);
90359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
90459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
90559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public float[] createHeightSubBlock(float[] heightMap, int x,
90659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    int y, int side) {
90759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] rVal = new float[side * side];
90859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int bsize = (int) FastMath.sqrt(heightMap.length);
90959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int count = 0;
91059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int i = y; i < side + y; i++) {
91159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int j = x; j < side + x; j++) {
91259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (j < bsize && i < bsize)
91359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    rVal[count] = heightMap[j + (i * bsize)];
91459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                count++;
91559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
91659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
91759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return rVal;
91859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
91959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
92059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
92159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * A handy method that will attach all bounding boxes of this terrain
92259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * to the node you supply.
92359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Useful to visualize the bounding boxes when debugging.
92459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
92559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param parent that will get the bounding box shapes of the terrain attached to
92659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
92759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void attachBoundChildren(Node parent) {
92859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int i = 0; i < this.getQuantity(); i++) {
92959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (this.getChild(i) instanceof TerrainQuad) {
93059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                ((TerrainQuad) getChild(i)).attachBoundChildren(parent);
93159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            } else if (this.getChild(i) instanceof TerrainPatch) {
93259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                BoundingVolume bv = getChild(i).getWorldBound();
93359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (bv instanceof BoundingBox) {
93459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    attachBoundingBox((BoundingBox)bv, parent);
93559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
93659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
93759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
93859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        BoundingVolume bv = getWorldBound();
93959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (bv instanceof BoundingBox) {
94059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            attachBoundingBox((BoundingBox)bv, parent);
94159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
94259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
94359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
94459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
94559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * used by attachBoundChildren()
94659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
94759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private void attachBoundingBox(BoundingBox bb, Node parent) {
94859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        WireBox wb = new WireBox(bb.getXExtent(), bb.getYExtent(), bb.getZExtent());
94959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Geometry g = new Geometry();
95059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        g.setMesh(wb);
95159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        g.setLocalTranslation(bb.getCenter());
95259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        parent.attachChild(g);
95359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
95459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
95559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
95659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Signal if the normal vectors for the terrain need to be recalculated.
95759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Does this by looking at the affectedAreaBBox bounding box. If the bbox
95859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * exists already, then it will grow the box to fit the new changedPoint.
95959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * If the affectedAreaBBox is null, then it will create one of unit size.
96059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     *
96159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param needToRecalculateNormals if null, will cause needToRecalculateNormals() to return false
96259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
96359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void setNormalRecalcNeeded(Vector2f changedPoint) {
96459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (changedPoint == null) { // set needToRecalculateNormals() to false
96559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            affectedAreaBBox = null;
96659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return;
96759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
96859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
96959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (affectedAreaBBox == null) {
97059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            affectedAreaBBox = new BoundingBox(new Vector3f(changedPoint.x, 0, changedPoint.y), 1f, Float.MAX_VALUE, 1f); // unit length
97159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else {
97259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // adjust size of box to be larger
97359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            affectedAreaBBox.mergeLocal(new BoundingBox(new Vector3f(changedPoint.x, 0, changedPoint.y), 1f, Float.MAX_VALUE, 1f));
97459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
97559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
97659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
97759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected boolean needToRecalculateNormals() {
97859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (affectedAreaBBox != null)
97959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return true;
98059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (!lastScale.equals(getWorldScale())) {
98159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size, Float.MAX_VALUE, size);
98259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            lastScale = getWorldScale();
98359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return true;
98459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
98559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return false;
98659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
98759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
98859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
98959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * This will cause all normals for this terrain quad to be recalculated
99059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
99159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void setNeedToRecalculateNormals() {
99259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size*2, Float.MAX_VALUE, size*2);
99359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
99459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
99559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public float getHeightmapHeight(Vector2f xz) {
99659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // offset
99759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int halfSize = totalSize / 2;
99859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int x = Math.round((xz.x / getWorldScale().x) + halfSize);
99959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int z = Math.round((xz.y / getWorldScale().z) + halfSize);
100059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
100159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return getHeightmapHeight(x, z);
100259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
100359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
100459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
100559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * This will just get the heightmap value at the supplied point,
100659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * not an interpolated (actual) height value.
100759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
100859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected float getHeightmapHeight(int x, int z) {
100959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int quad = findQuadrant(x, z);
101059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int split = (size + 1) >> 1;
101159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (children != null) {
101259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int i = children.size(); --i >= 0;) {
101359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Spatial spat = children.get(i);
101459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                int col = x;
101559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                int row = z;
101659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                boolean match = false;
101759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
101859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                // get the childs quadrant
101959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                int childQuadrant = 0;
102059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (spat instanceof TerrainQuad) {
102159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    childQuadrant = ((TerrainQuad) spat).getQuadrant();
102259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (spat instanceof TerrainPatch) {
102359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    childQuadrant = ((TerrainPatch) spat).getQuadrant();
102459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
102559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
102659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (childQuadrant == 1 && (quad & 1) != 0) {
102759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    match = true;
102859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (childQuadrant == 2 && (quad & 2) != 0) {
102959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    row = z - split + 1;
103059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    match = true;
103159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (childQuadrant == 3 && (quad & 4) != 0) {
103259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    col = x - split + 1;
103359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    match = true;
103459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (childQuadrant == 4 && (quad & 8) != 0) {
103559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    col = x - split + 1;
103659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    row = z - split + 1;
103759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    match = true;
103859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
103959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
104059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (match) {
104159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if (spat instanceof TerrainQuad) {
104259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        return ((TerrainQuad) spat).getHeightmapHeight(col, row);
104359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    } else if (spat instanceof TerrainPatch) {
104459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        return ((TerrainPatch) spat).getHeightmapHeight(col, row);
104559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    }
104659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
104759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
104859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
104959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
105059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return Float.NaN;
105159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
105259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
105359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected Vector3f getMeshNormal(int x, int z) {
105459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int quad = findQuadrant(x, z);
105559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int split = (size + 1) >> 1;
105659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (children != null) {
105759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int i = children.size(); --i >= 0;) {
105859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Spatial spat = children.get(i);
105959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                int col = x;
106059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                int row = z;
106159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                boolean match = false;
106259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
106359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                // get the childs quadrant
106459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                int childQuadrant = 0;
106559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (spat instanceof TerrainQuad) {
106659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    childQuadrant = ((TerrainQuad) spat).getQuadrant();
106759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (spat instanceof TerrainPatch) {
106859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    childQuadrant = ((TerrainPatch) spat).getQuadrant();
106959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
107059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
107159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (childQuadrant == 1 && (quad & 1) != 0) {
107259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    match = true;
107359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (childQuadrant == 2 && (quad & 2) != 0) {
107459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    row = z - split + 1;
107559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    match = true;
107659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (childQuadrant == 3 && (quad & 4) != 0) {
107759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    col = x - split + 1;
107859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    match = true;
107959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (childQuadrant == 4 && (quad & 8) != 0) {
108059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    col = x - split + 1;
108159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    row = z - split + 1;
108259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    match = true;
108359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
108459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
108559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (match) {
108659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if (spat instanceof TerrainQuad) {
108759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        return ((TerrainQuad) spat).getMeshNormal(col, row);
108859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    } else if (spat instanceof TerrainPatch) {
108959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        return ((TerrainPatch) spat).getMeshNormal(col, row);
109059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    }
109159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
109259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
109359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
109459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
109559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return null;
109659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
109759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
109859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public float getHeight(Vector2f xz) {
109959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // offset
110059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float x = (float)(((xz.x - getWorldTranslation().x) / getWorldScale().x) + (float)totalSize / 2f);
110159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float z = (float)(((xz.y - getWorldTranslation().z) / getWorldScale().z) + (float)totalSize / 2f);
110259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float height = getHeight(x, z);
110359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        height *= getWorldScale().y;
110459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return height;
110559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
110659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
110759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /*
110859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * gets an interpolated value at the specified point
110959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param x coordinate translated into actual (positive) terrain grid coordinates
111059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param y coordinate translated into actual (positive) terrain grid coordinates
111159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
111259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected float getHeight(float x, float z) {
111359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        x-=0.5f;
111459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        z-=0.5f;
111559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float col = FastMath.floor(x);
111659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float row = FastMath.floor(z);
111759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        boolean onX = false;
111859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if(1 - (x - col)-(z - row) < 0) // what triangle to interpolate on
111959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            onX = true;
112059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // v1--v2  ^
112159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // |  / |  |
112259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // | /  |  |
112359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // v3--v4  | Z
112459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //         |
112559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // <-------Y
112659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //     X
112759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float v1 = getHeightmapHeight((int) FastMath.ceil(x), (int) FastMath.ceil(z));
112859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float v2 = getHeightmapHeight((int) FastMath.floor(x), (int) FastMath.ceil(z));
112959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float v3 = getHeightmapHeight((int) FastMath.ceil(x), (int) FastMath.floor(z));
113059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float v4 = getHeightmapHeight((int) FastMath.floor(x), (int) FastMath.floor(z));
113159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (onX) {
113259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return ((x - col) + (z - row) - 1f)*v1 + (1f - (x - col))*v2 + (1f - (z - row))*v3;
113359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else {
113459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return (1f - (x - col) - (z - row))*v4 + (z - row)*v2 + (x - col)*v3;
113559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
113659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
113759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
113859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public Vector3f getNormal(Vector2f xz) {
113959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // offset
114059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float x = (float)(((xz.x - getWorldTranslation().x) / getWorldScale().x) + (float)totalSize / 2f);
114159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float z = (float)(((xz.y - getWorldTranslation().z) / getWorldScale().z) + (float)totalSize / 2f);
114259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f normal = getNormal(x, z, xz);
114359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
114459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return normal;
114559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
114659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
114759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected Vector3f getNormal(float x, float z, Vector2f xz) {
114859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        x-=0.5f;
114959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        z-=0.5f;
115059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float col = FastMath.floor(x);
115159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float row = FastMath.floor(z);
115259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        boolean onX = false;
115359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if(1 - (x - col)-(z - row) < 0) // what triangle to interpolate on
115459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            onX = true;
115559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // v1--v2  ^
115659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // |  / |  |
115759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // | /  |  |
115859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // v3--v4  | Z
115959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //         |
116059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // <-------Y
116159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //     X
116259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f n1 = getMeshNormal((int) FastMath.ceil(x), (int) FastMath.ceil(z));
116359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f n2 = getMeshNormal((int) FastMath.floor(x), (int) FastMath.ceil(z));
116459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f n3 = getMeshNormal((int) FastMath.ceil(x), (int) FastMath.floor(z));
116559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Vector3f n4 = getMeshNormal((int) FastMath.floor(x), (int) FastMath.floor(z));
116659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
116759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return n1.add(n2).add(n3).add(n4).normalize();
116859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
116959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
117059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void setHeight(Vector2f xz, float height) {
117159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        List<Vector2f> coord = new ArrayList<Vector2f>();
117259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        coord.add(xz);
117359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        List<Float> h = new ArrayList<Float>();
117459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        h.add(height);
117559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
117659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        setHeight(coord, h);
117759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
117859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
117959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void adjustHeight(Vector2f xz, float delta) {
118059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        List<Vector2f> coord = new ArrayList<Vector2f>();
118159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        coord.add(xz);
118259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        List<Float> h = new ArrayList<Float>();
118359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        h.add(delta);
118459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
118559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        adjustHeight(coord, h);
118659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
118759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
118859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void setHeight(List<Vector2f> xz, List<Float> height) {
118959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        setHeight(xz, height, true);
119059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
119159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
119259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void adjustHeight(List<Vector2f> xz, List<Float> height) {
119359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        setHeight(xz, height, false);
119459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
119559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
119659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void setHeight(List<Vector2f> xz, List<Float> height, boolean overrideHeight) {
119759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (xz.size() != height.size())
119859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            throw new IllegalArgumentException("Both lists must be the same length!");
119959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
120059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int halfSize = totalSize / 2;
120159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
120259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        List<LocationHeight> locations = new ArrayList<LocationHeight>();
120359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
120459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // offset
120559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int i=0; i<xz.size(); i++) {
120659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            int x = Math.round((xz.get(i).x / getWorldScale().x) + halfSize);
120759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            int z = Math.round((xz.get(i).y / getWorldScale().z) + halfSize);
120859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            locations.add(new LocationHeight(x,z,height.get(i)));
120959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
121059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
121159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        setHeight(locations, overrideHeight); // adjust height of the actual mesh
121259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
121359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // signal that the normals need updating
121459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int i=0; i<xz.size(); i++)
121559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            setNormalRecalcNeeded(xz.get(i) );
121659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
121759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
121859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected class LocationHeight {
121959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int x;
122059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int z;
122159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float h;
122259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
122359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        LocationHeight(){}
122459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
122559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        LocationHeight(int x, int z, float h){
122659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            this.x = x;
122759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            this.z = z;
122859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            this.h = h;
122959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
123059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
123159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
123259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void setHeight(List<LocationHeight> locations, boolean overrideHeight) {
123359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (children == null)
123459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return;
123559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
123659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        List<LocationHeight> quadLH1 = new ArrayList<LocationHeight>();
123759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        List<LocationHeight> quadLH2 = new ArrayList<LocationHeight>();
123859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        List<LocationHeight> quadLH3 = new ArrayList<LocationHeight>();
123959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        List<LocationHeight> quadLH4 = new ArrayList<LocationHeight>();
124059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Spatial quad1 = null;
124159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Spatial quad2 = null;
124259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Spatial quad3 = null;
124359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        Spatial quad4 = null;
124459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
124559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // get the child quadrants
124659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int i = children.size(); --i >= 0;) {
124759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Spatial spat = children.get(i);
124859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            int childQuadrant = 0;
124959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (spat instanceof TerrainQuad) {
125059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                childQuadrant = ((TerrainQuad) spat).getQuadrant();
125159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            } else if (spat instanceof TerrainPatch) {
125259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                childQuadrant = ((TerrainPatch) spat).getQuadrant();
125359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
125459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
125559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (childQuadrant == 1)
125659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                quad1 = spat;
125759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            else if (childQuadrant == 2)
125859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                quad2 = spat;
125959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            else if (childQuadrant == 3)
126059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                quad3 = spat;
126159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            else if (childQuadrant == 4)
126259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                quad4 = spat;
126359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
126459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
126559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int split = (size + 1) >> 1;
126659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
126759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // distribute each locationHeight into the quadrant it intersects
126859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (LocationHeight lh : locations) {
126959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            int quad = findQuadrant(lh.x, lh.z);
127059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
127159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            int col = lh.x;
127259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            int row = lh.z;
127359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
127459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if ((quad & 1) != 0) {
127559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                quadLH1.add(lh);
127659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
127759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if ((quad & 2) != 0) {
127859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                row = lh.z - split + 1;
127959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                quadLH2.add(new LocationHeight(lh.x, row, lh.h));
128059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
128159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if ((quad & 4) != 0) {
128259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                col = lh.x - split + 1;
128359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                quadLH3.add(new LocationHeight(col, lh.z, lh.h));
128459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
128559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if ((quad & 8) != 0) {
128659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                col = lh.x - split + 1;
128759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                row = lh.z - split + 1;
128859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                quadLH4.add(new LocationHeight(col, row, lh.h));
128959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
129059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
129159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
129259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // send the locations to the children
129359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (!quadLH1.isEmpty()) {
129459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad1 instanceof TerrainQuad)
129559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                ((TerrainQuad)quad1).setHeight(quadLH1, overrideHeight);
129659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            else if(quad1 instanceof TerrainPatch)
129759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                ((TerrainPatch)quad1).setHeight(quadLH1, overrideHeight);
129859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
129959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
130059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (!quadLH2.isEmpty()) {
130159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad2 instanceof TerrainQuad)
130259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                ((TerrainQuad)quad2).setHeight(quadLH2, overrideHeight);
130359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            else if(quad2 instanceof TerrainPatch)
130459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                ((TerrainPatch)quad2).setHeight(quadLH2, overrideHeight);
130559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
130659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
130759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (!quadLH3.isEmpty()) {
130859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad3 instanceof TerrainQuad)
130959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                ((TerrainQuad)quad3).setHeight(quadLH3, overrideHeight);
131059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            else if(quad3 instanceof TerrainPatch)
131159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                ((TerrainPatch)quad3).setHeight(quadLH3, overrideHeight);
131259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
131359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
131459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (!quadLH4.isEmpty()) {
131559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad4 instanceof TerrainQuad)
131659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                ((TerrainQuad)quad4).setHeight(quadLH4, overrideHeight);
131759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            else if(quad4 instanceof TerrainPatch)
131859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                ((TerrainPatch)quad4).setHeight(quadLH4, overrideHeight);
131959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
132059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
132159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
132259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected boolean isPointOnTerrain(int x, int z) {
132359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return (x >= 0 && x <= totalSize && z >= 0 && z <= totalSize);
132459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
132559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
132659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
132759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public int getTerrainSize() {
132859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return totalSize;
132959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
133059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
133159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
133259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    // a position can be in multiple quadrants, so use a bit anded value.
133359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    private int findQuadrant(int x, int y) {
133459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int split = (size + 1) >> 1;
133559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int quads = 0;
133659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (x < split && y < split)
133759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            quads |= 1;
133859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (x < split && y >= split - 1)
133959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            quads |= 2;
134059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (x >= split - 1 && y < split)
134159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            quads |= 4;
134259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (x >= split - 1 && y >= split - 1)
134359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            quads |= 8;
134459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return quads;
134559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
134659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
134759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
134859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * lock or unlock the meshes of this terrain.
134959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Locked meshes are uneditable but have better performance.
135059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param locked or unlocked
135159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
135259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void setLocked(boolean locked) {
135359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int i = 0; i < this.getQuantity(); i++) {
135459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (this.getChild(i) instanceof TerrainQuad) {
135559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                ((TerrainQuad) getChild(i)).setLocked(locked);
135659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            } else if (this.getChild(i) instanceof TerrainPatch) {
135759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (locked)
135859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    ((TerrainPatch) getChild(i)).lockMesh();
135959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                else
136059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    ((TerrainPatch) getChild(i)).unlockMesh();
136159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
136259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
136359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
136459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
136559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
136659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public int getQuadrant() {
136759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return quadrant;
136859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
136959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
137059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void setQuadrant(short quadrant) {
137159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        this.quadrant = quadrant;
137259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
137359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
137459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
137559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected TerrainPatch getPatch(int quad) {
137659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (children != null)
137759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int x = children.size(); --x >= 0;) {
137859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Spatial child = children.get(x);
137959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (child instanceof TerrainPatch) {
138059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    TerrainPatch tb = (TerrainPatch) child;
138159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if (tb.getQuadrant() == quad)
138259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        return tb;
138359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
138459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
138559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return null;
138659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
138759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
138859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected TerrainQuad getQuad(int quad) {
138959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (children != null)
139059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int x = children.size(); --x >= 0;) {
139159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Spatial child = children.get(x);
139259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (child instanceof TerrainQuad) {
139359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    TerrainQuad tq = (TerrainQuad) child;
139459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if (tq.getQuadrant() == quad)
139559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        return tq;
139659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
139759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
139859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return null;
139959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
140059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
140159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected TerrainPatch findRightPatch(TerrainPatch tp) {
140259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (tp.getQuadrant() == 1)
140359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return getPatch(3);
140459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else if (tp.getQuadrant() == 2)
140559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return getPatch(4);
140659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else if (tp.getQuadrant() == 3) {
140759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // find the patch to the right and ask it for child 1.
140859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            TerrainQuad quad = findRightQuad();
140959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad != null)
141059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return quad.getPatch(1);
141159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else if (tp.getQuadrant() == 4) {
141259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // find the patch to the right and ask it for child 2.
141359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            TerrainQuad quad = findRightQuad();
141459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad != null)
141559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return quad.getPatch(2);
141659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
141759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
141859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return null;
141959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
142059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
142159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected TerrainPatch findDownPatch(TerrainPatch tp) {
142259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (tp.getQuadrant() == 1)
142359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return getPatch(2);
142459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else if (tp.getQuadrant() == 3)
142559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return getPatch(4);
142659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else if (tp.getQuadrant() == 2) {
142759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // find the patch below and ask it for child 1.
142859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            TerrainQuad quad = findDownQuad();
142959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad != null)
143059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return quad.getPatch(1);
143159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else if (tp.getQuadrant() == 4) {
143259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            TerrainQuad quad = findDownQuad();
143359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad != null)
143459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return quad.getPatch(3);
143559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
143659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
143759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return null;
143859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
143959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
144059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
144159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected TerrainPatch findTopPatch(TerrainPatch tp) {
144259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (tp.getQuadrant() == 2)
144359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return getPatch(1);
144459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else if (tp.getQuadrant() == 4)
144559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return getPatch(3);
144659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else if (tp.getQuadrant() == 1) {
144759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // find the patch above and ask it for child 2.
144859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            TerrainQuad quad = findTopQuad();
144959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad != null)
145059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return quad.getPatch(2);
145159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else if (tp.getQuadrant() == 3) {
145259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            TerrainQuad quad = findTopQuad();
145359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad != null)
145459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return quad.getPatch(4);
145559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
145659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
145759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return null;
145859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
145959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
146059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected TerrainPatch findLeftPatch(TerrainPatch tp) {
146159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (tp.getQuadrant() == 3)
146259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return getPatch(1);
146359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else if (tp.getQuadrant() == 4)
146459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return getPatch(2);
146559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else if (tp.getQuadrant() == 1) {
146659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // find the patch above and ask it for child 2.
146759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            TerrainQuad quad = findLeftQuad();
146859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad != null)
146959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return quad.getPatch(3);
147059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else if (tp.getQuadrant() == 2) {
147159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            TerrainQuad quad = findLeftQuad();
147259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad != null)
147359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return quad.getPatch(4);
147459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
147559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
147659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return null;
147759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
147859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
147959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected TerrainQuad findRightQuad() {
148059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (getParent() == null || !(getParent() instanceof TerrainQuad))
148159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return null;
148259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
148359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TerrainQuad pQuad = (TerrainQuad) getParent();
148459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
148559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (quadrant == 1)
148659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return pQuad.getQuad(3);
148759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else if (quadrant == 2)
148859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return pQuad.getQuad(4);
148959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else if (quadrant == 3) {
149059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            TerrainQuad quad = pQuad.findRightQuad();
149159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad != null)
149259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return quad.getQuad(1);
149359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else if (quadrant == 4) {
149459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            TerrainQuad quad = pQuad.findRightQuad();
149559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad != null)
149659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return quad.getQuad(2);
149759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
149859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
149959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return null;
150059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
150159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
150259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected TerrainQuad findDownQuad() {
150359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (getParent() == null || !(getParent() instanceof TerrainQuad))
150459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return null;
150559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
150659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TerrainQuad pQuad = (TerrainQuad) getParent();
150759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
150859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (quadrant == 1)
150959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return pQuad.getQuad(2);
151059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else if (quadrant == 3)
151159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return pQuad.getQuad(4);
151259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else if (quadrant == 2) {
151359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            TerrainQuad quad = pQuad.findDownQuad();
151459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad != null)
151559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return quad.getQuad(1);
151659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else if (quadrant == 4) {
151759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            TerrainQuad quad = pQuad.findDownQuad();
151859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad != null)
151959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return quad.getQuad(3);
152059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
152159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
152259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return null;
152359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
152459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
152559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected TerrainQuad findTopQuad() {
152659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (getParent() == null || !(getParent() instanceof TerrainQuad))
152759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return null;
152859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
152959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TerrainQuad pQuad = (TerrainQuad) getParent();
153059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
153159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (quadrant == 2)
153259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return pQuad.getQuad(1);
153359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else if (quadrant == 4)
153459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return pQuad.getQuad(3);
153559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else if (quadrant == 1) {
153659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            TerrainQuad quad = pQuad.findTopQuad();
153759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad != null)
153859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return quad.getQuad(2);
153959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else if (quadrant == 3) {
154059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            TerrainQuad quad = pQuad.findTopQuad();
154159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad != null)
154259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return quad.getQuad(4);
154359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
154459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
154559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return null;
154659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
154759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
154859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected TerrainQuad findLeftQuad() {
154959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (getParent() == null || !(getParent() instanceof TerrainQuad))
155059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return null;
155159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
155259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TerrainQuad pQuad = (TerrainQuad) getParent();
155359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
155459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (quadrant == 3)
155559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return pQuad.getQuad(1);
155659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else if (quadrant == 4)
155759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return pQuad.getQuad(2);
155859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        else if (quadrant == 1) {
155959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            TerrainQuad quad = pQuad.findLeftQuad();
156059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad != null)
156159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return quad.getQuad(3);
156259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } else if (quadrant == 2) {
156359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            TerrainQuad quad = pQuad.findLeftQuad();
156459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (quad != null)
156559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return quad.getQuad(4);
156659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
156759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
156859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return null;
156959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
157059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
157159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
157259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Find what terrain patches need normal recalculations and update
157359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * their normals;
157459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
157559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void fixNormals(BoundingBox affectedArea) {
157659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (children == null)
157759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return;
157859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
157959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // go through the children and see if they collide with the affectedAreaBBox
158059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // if they do, then update their normals
158159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int x = children.size(); --x >= 0;) {
158259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Spatial child = children.get(x);
158359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (child instanceof TerrainQuad) {
158459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (affectedArea != null && affectedArea.intersects(((TerrainQuad) child).getWorldBound()) )
158559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    ((TerrainQuad) child).fixNormals(affectedArea);
158659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            } else if (child instanceof TerrainPatch) {
158759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (affectedArea != null && affectedArea.intersects(((TerrainPatch) child).getWorldBound()) )
158859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    ((TerrainPatch) child).updateNormals(); // recalculate the patch's normals
158959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
159059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
159159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
159259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
159359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
159459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * fix the normals on the edge of the terrain patches.
159559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
159659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    protected void fixNormalEdges(BoundingBox affectedArea) {
159759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (children == null)
159859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return;
159959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
160059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (int x = children.size(); --x >= 0;) {
160159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            Spatial child = children.get(x);
160259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (child instanceof TerrainQuad) {
160359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (affectedArea != null && affectedArea.intersects(((TerrainQuad) child).getWorldBound()) )
160459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    ((TerrainQuad) child).fixNormalEdges(affectedArea);
160559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            } else if (child instanceof TerrainPatch) {
160659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (affectedArea != null && !affectedArea.intersects(((TerrainPatch) child).getWorldBound()) ) // if doesn't intersect, continue
160759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    continue;
160859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
160959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                TerrainPatch tp = (TerrainPatch) child;
161059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                TerrainPatch right = findRightPatch(tp);
161159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                TerrainPatch bottom = findDownPatch(tp);
161259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                TerrainPatch top = findTopPatch(tp);
161359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                TerrainPatch left = findLeftPatch(tp);
161459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                TerrainPatch topLeft = null;
161559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (top != null)
161659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    topLeft = findLeftPatch(top);
161759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                TerrainPatch bottomRight = null;
161859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (right != null)
161959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    bottomRight = findDownPatch(right);
162059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                TerrainPatch topRight = null;
162159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (top != null)
162259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    topRight = findRightPatch(top);
162359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                TerrainPatch bottomLeft = null;
162459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (left != null)
162559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    bottomLeft = findDownPatch(left);
162659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
162759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                tp.fixNormalEdges(right, bottom, top, left, bottomRight, bottomLeft, topRight, topLeft);
162859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
162959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
163059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        } // for each child
163159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
163259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
163359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
163459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
163559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
163659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    @Override
163759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public int collideWith(Collidable other, CollisionResults results){
163859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int total = 0;
163959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
164059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (other instanceof Ray)
164159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            return collideWithRay((Ray)other, results);
164259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
164359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        // if it didn't collide with this bbox, return
164459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (other instanceof BoundingVolume)
164559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (!this.getWorldBound().intersects((BoundingVolume)other))
164659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                return total;
164759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
164859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        for (Spatial child : children){
164959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            total += child.collideWith(other, results);
165059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
165159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return total;
165259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
165359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
165459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
165559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Gather the terrain patches that intersect the given ray (toTest).
165659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * This only tests the bounding boxes
165759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param toTest
165859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param results
165959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
166059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void findPick(Ray toTest, List<TerrainPickData> results) {
166159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
166259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (getWorldBound() != null) {
166359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (getWorldBound().intersects(toTest)) {
166459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                // further checking needed.
166559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                for (int i = 0; i < getQuantity(); i++) {
166659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if (children.get(i) instanceof TerrainPatch) {
166759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        TerrainPatch tp = (TerrainPatch) children.get(i);
166859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        tp.ensurePositiveVolumeBBox();
166959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        if (tp.getWorldBound().intersects(toTest)) {
167059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            CollisionResults cr = new CollisionResults();
167159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            toTest.collideWith(tp.getWorldBound(), cr);
167259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            if (cr != null && cr.getClosestCollision() != null) {
167359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                cr.getClosestCollision().getDistance();
167459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                                results.add(new TerrainPickData(tp, cr.getClosestCollision()));
167559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                            }
167659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        }
167759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    }
1678a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    else if (children.get(i) instanceof TerrainQuad) {
167959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        ((TerrainQuad) children.get(i)).findPick(toTest, results);
1680a6b44658eb1c55295f132a36233a11aa2bd8f9cfScott Barta                    }
168159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
168259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
168359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
168459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
168559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
168659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
168759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    /**
168859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * Retrieve all Terrain Patches from all children and store them
168959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * in the 'holder' list
169059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     * @param holder must not be null, will be populated when returns
169159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta     */
169259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void getAllTerrainPatches(List<TerrainPatch> holder) {
169359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (children != null) {
169459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int i = children.size(); --i >= 0;) {
169559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Spatial child = children.get(i);
169659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (child instanceof TerrainQuad) {
169759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    ((TerrainQuad) child).getAllTerrainPatches(holder);
169859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (child instanceof TerrainPatch) {
169959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    holder.add((TerrainPatch)child);
170059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
170159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
170259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
170359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
170459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
170559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void getAllTerrainPatchesWithTranslation(Map<TerrainPatch,Vector3f> holder, Vector3f translation) {
170659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (children != null) {
170759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int i = children.size(); --i >= 0;) {
170859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                Spatial child = children.get(i);
170959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                if (child instanceof TerrainQuad) {
171059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    ((TerrainQuad) child).getAllTerrainPatchesWithTranslation(holder, translation.clone().add(child.getLocalTranslation()));
171159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                } else if (child instanceof TerrainPatch) {
171259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    //if (holder.size() < 4)
171359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    holder.put((TerrainPatch)child, translation.clone().add(child.getLocalTranslation()));
171459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
171559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
171659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
171759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
171859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
171959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    @Override
172059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void read(JmeImporter e) throws IOException {
172159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        super.read(e);
172259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        InputCapsule c = e.getCapsule(this);
172359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        size = c.readInt("size", 0);
172459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        stepScale = (Vector3f) c.readSavable("stepScale", null);
172559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        offset = (Vector2f) c.readSavable("offset", new Vector2f(0,0));
172659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        offsetAmount = c.readFloat("offsetAmount", 0);
172759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        quadrant = c.readInt("quadrant", 0);
172859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        totalSize = c.readInt("totalSize", 0);
172959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //lodCalculator = (LodCalculator) c.readSavable("lodCalculator", createDefaultLodCalculator());
173059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //lodCalculatorFactory = (LodCalculatorFactory) c.readSavable("lodCalculatorFactory", null);
173159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
173259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if ( !(getParent() instanceof TerrainQuad) ) {
173359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            BoundingBox all = new BoundingBox(getWorldTranslation(), totalSize, totalSize, totalSize);
173459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            affectedAreaBBox = all;
173559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            updateNormals();
173659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
173759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
173859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
173959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    @Override
174059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public void write(JmeExporter e) throws IOException {
174159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        super.write(e);
174259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        OutputCapsule c = e.getCapsule(this);
174359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        c.write(size, "size", 0);
174459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        c.write(totalSize, "totalSize", 0);
174559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        c.write(stepScale, "stepScale", null);
174659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        c.write(offset, "offset", new Vector2f(0,0));
174759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        c.write(offsetAmount, "offsetAmount", 0);
174859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        c.write(quadrant, "quadrant", 0);
174959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //c.write(lodCalculatorFactory, "lodCalculatorFactory", null);
175059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //c.write(lodCalculator, "lodCalculator", null);
175159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
175259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
175359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    @Override
175459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public TerrainQuad clone() {
175559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return this.clone(true);
175659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
175759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
175859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta	@Override
175959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public TerrainQuad clone(boolean cloneMaterials) {
176059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TerrainQuad quadClone = (TerrainQuad) super.clone(cloneMaterials);
176159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        quadClone.name = name.toString();
176259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        quadClone.size = size;
176359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        quadClone.totalSize = totalSize;
176459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (stepScale != null) {
176559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            quadClone.stepScale = stepScale.clone();
176659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
176759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (offset != null) {
176859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            quadClone.offset = offset.clone();
176959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
177059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        quadClone.offsetAmount = offsetAmount;
177159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        quadClone.quadrant = quadrant;
177259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //quadClone.lodCalculatorFactory = lodCalculatorFactory.clone();
177359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        //quadClone.lodCalculator = lodCalculator.clone();
177459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
177559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TerrainLodControl lodControlCloned = this.getControl(TerrainLodControl.class);
177659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        TerrainLodControl lodControl = quadClone.getControl(TerrainLodControl.class);
177759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
177859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (lodControlCloned != null && !(getParent() instanceof TerrainQuad)) {
177959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            //lodControlCloned.setLodCalculator(lodControl.getLodCalculator().clone());
178059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
178159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        NormalRecalcControl normalControl = getControl(NormalRecalcControl.class);
178259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (normalControl != null)
178359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            normalControl.setTerrain(this);
178459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
178559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return quadClone;
178659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
178759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
178859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public int getMaxLod() {
178959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (maxLod < 0)
179059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            maxLod = Math.max(1, (int) (FastMath.log(size-1)/FastMath.log(2)) -1); // -1 forces our minimum of 4 triangles wide
179159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
179259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return maxLod;
179359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
179459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
179559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public int getPatchSize() {
179659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return patchSize;
179759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
179859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
179959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public int getTotalSize() {
180059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return totalSize;
180159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
180259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
180359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    public float[] getHeightMap() {
180459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
180559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        float[] hm = null;
180659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int length = ((size-1)/2)+1;
180759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        int area = size*size;
180859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        hm = new float[area];
180959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
181059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        if (getChildren() != null && !getChildren().isEmpty()) {
181159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            float[] ul=null, ur=null, bl=null, br=null;
181259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // get the child heightmaps
181359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            if (getChild(0) instanceof TerrainPatch) {
181459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                for (Spatial s : getChildren()) {
181559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    if ( ((TerrainPatch)s).getQuadrant() == 1)
181659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        ul = ((TerrainPatch)s).getHeightMap();
181759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    else if(((TerrainPatch) s).getQuadrant() == 2)
181859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        bl = ((TerrainPatch)s).getHeightMap();
181959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    else if(((TerrainPatch) s).getQuadrant() == 3)
182059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        ur = ((TerrainPatch)s).getHeightMap();
182159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    else if(((TerrainPatch) s).getQuadrant() == 4)
182259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                        br = ((TerrainPatch)s).getHeightMap();
182359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
182459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
182559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            else {
182659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                ul = getQuad(1).getHeightMap();
182759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                bl = getQuad(2).getHeightMap();
182859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                ur = getQuad(3).getHeightMap();
182959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                br = getQuad(4).getHeightMap();
183059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
183159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
183259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // combine them into a single heightmap
183359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
183459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
183559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // first upper blocks
183659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int y=0; y<length; y++) { // rows
183759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                for (int x1=0; x1<length; x1++) {
183859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    int row = y*size;
183959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    hm[row+x1] = ul[y*length+x1];
184059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
184159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                for (int x2=1; x2<length; x2++) {
184259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    int row = y*size + length;
184359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    hm[row+x2-1] = ur[y*length + x2];
184459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
184559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
184659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            // second lower blocks
184759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            int rowOffset = size*length;
184859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            for (int y=1; y<length; y++) { // rows
184959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                for (int x1=0; x1<length; x1++) {
185059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    int row = (y-1)*size;
185159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    hm[rowOffset+row+x1] = bl[y*length+x1];
185259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
185359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                for (int x2=1; x2<length; x2++) {
185459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    int row = (y-1)*size + length;
185559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                    hm[rowOffset+row+x2-1] = br[y*length + x2];
185659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta                }
185759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta            }
185859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        }
185959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
186059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta        return hm;
186159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta    }
186259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta}
186359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta
1864