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