Home | History | Annotate | Download | only in geomipmap
      1 /*
      2  * Copyright (c) 2009-2010 jMonkeyEngine
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  * * Redistributions of source code must retain the above copyright
     10  *   notice, this list of conditions and the following disclaimer.
     11  *
     12  * * Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  *
     16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
     17  *   may be used to endorse or promote products derived from this software
     18  *   without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 package com.jme3.terrain.geomipmap;
     33 
     34 import com.jme3.export.JmeExporter;
     35 import com.jme3.export.JmeImporter;
     36 import com.jme3.math.FastMath;
     37 import com.jme3.math.Plane;
     38 import com.jme3.math.Triangle;
     39 import com.jme3.math.Vector2f;
     40 import com.jme3.math.Vector3f;
     41 import com.jme3.scene.Mesh;
     42 import com.jme3.scene.Mesh.Mode;
     43 import com.jme3.scene.VertexBuffer.Type;
     44 import com.jme3.terrain.GeoMap;
     45 import com.jme3.util.BufferUtils;
     46 import com.jme3.util.TempVars;
     47 import java.io.IOException;
     48 import java.nio.BufferOverflowException;
     49 import java.nio.BufferUnderflowException;
     50 import java.nio.FloatBuffer;
     51 import java.nio.IntBuffer;
     52 
     53 /**
     54  * Produces the mesh for the TerrainPatch.
     55  * This LOD algorithm generates a single triangle strip by first building the center of the
     56  * mesh, minus one outer edge around it. Then it builds the edges in counter-clockwise order,
     57  * starting at the bottom right and working up, then left across the top, then down across the
     58  * left, then right across the bottom.
     59  * It needs to know what its neighbour's LOD's are so it can stitch the edges.
     60  * It creates degenerate polygons in order to keep the winding order of the polygons and to move
     61  * the strip to a new position while still maintaining the continuity of the overall mesh. These
     62  * degenerates are removed quickly by the video card.
     63  *
     64  * @author Brent Owens
     65  */
     66 public class LODGeomap extends GeoMap {
     67 
     68     public LODGeomap() {
     69     }
     70 
     71     @Deprecated
     72     public LODGeomap(int size, FloatBuffer heightMap) {
     73         super(heightMap, size, size, 1);
     74     }
     75 
     76     public LODGeomap(int size, float[] heightMap) {
     77         super(heightMap, size, size, 1);
     78     }
     79 
     80     public Mesh createMesh(Vector3f scale, Vector2f tcScale, Vector2f tcOffset, float offsetAmount, int totalSize, boolean center) {
     81         return this.createMesh(scale, tcScale, tcOffset, offsetAmount, totalSize, center, 1, false, false, false, false);
     82     }
     83 
     84     public Mesh createMesh(Vector3f scale, Vector2f tcScale, Vector2f tcOffset, float offsetAmount, int totalSize, boolean center, int lod, boolean rightLod, boolean topLod, boolean leftLod, boolean bottomLod) {
     85         FloatBuffer pb = writeVertexArray(null, scale, center);
     86         FloatBuffer texb = writeTexCoordArray(null, tcOffset, tcScale, offsetAmount, totalSize);
     87         FloatBuffer nb = writeNormalArray(null, scale);
     88         IntBuffer ib = writeIndexArrayLodDiff(null, lod, rightLod, topLod, leftLod, bottomLod);
     89         FloatBuffer bb = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3);
     90         FloatBuffer tanb = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3);
     91         writeTangentArray(nb, tanb, bb, texb, scale);
     92         Mesh m = new Mesh();
     93         m.setMode(Mode.TriangleStrip);
     94         m.setBuffer(Type.Position, 3, pb);
     95         m.setBuffer(Type.Normal, 3, nb);
     96         m.setBuffer(Type.Tangent, 3, tanb);
     97         m.setBuffer(Type.Binormal, 3, bb);
     98         m.setBuffer(Type.TexCoord, 2, texb);
     99         m.setBuffer(Type.Index, 3, ib);
    100         m.setStatic();
    101         m.updateBound();
    102         return m;
    103     }
    104 
    105     public FloatBuffer writeTexCoordArray(FloatBuffer store, Vector2f offset, Vector2f scale, float offsetAmount, int totalSize) {
    106         if (store != null) {
    107             if (store.remaining() < getWidth() * getHeight() * 2) {
    108                 throw new BufferUnderflowException();
    109             }
    110         } else {
    111             store = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 2);
    112         }
    113 
    114         if (offset == null) {
    115             offset = new Vector2f();
    116         }
    117 
    118         Vector2f tcStore = new Vector2f();
    119 
    120         // work from bottom of heightmap up, so we don't flip the coords
    121         for (int y = getHeight() - 1; y >= 0; y--) {
    122             for (int x = 0; x < getWidth(); x++) {
    123                 getUV(x, y, tcStore, offset, offsetAmount, totalSize);
    124                 float tx = tcStore.x * scale.x;
    125                 float ty = tcStore.y * scale.y;
    126                 store.put(tx);
    127                 store.put(ty);
    128             }
    129         }
    130 
    131         return store;
    132     }
    133 
    134     public Vector2f getUV(int x, int y, Vector2f store, Vector2f offset, float offsetAmount, int totalSize) {
    135         float offsetX = offset.x + (offsetAmount * 1.0f);
    136         float offsetY = -offset.y + (offsetAmount * 1.0f);//note the -, we flip the tex coords
    137 
    138         store.set((((float) x) + offsetX) / (float) (totalSize - 1), // calculates percentage of texture here
    139                 (((float) y) + offsetY) / (float) (totalSize - 1));
    140         return store;
    141     }
    142 
    143     /**
    144      * Create the LOD index array that will seam its edges with its neighbour's LOD.
    145      * This is a scary method!!! It will break your mind.
    146      *
    147      * @param store to store the index buffer
    148      * @param lod level of detail of the mesh
    149      * @param rightLod LOD of the right neighbour
    150      * @param topLod LOD of the top neighbour
    151      * @param leftLod LOD of the left neighbour
    152      * @param bottomLod LOD of the bottom neighbour
    153      * @return the LOD-ified index buffer
    154      */
    155     public IntBuffer writeIndexArrayLodDiff(IntBuffer store, int lod, boolean rightLod, boolean topLod, boolean leftLod, boolean bottomLod) {
    156 
    157         IntBuffer buffer2 = store;
    158         int numIndexes = calculateNumIndexesLodDiff(lod);
    159         if (store == null) {
    160             buffer2 = BufferUtils.createIntBuffer(numIndexes);
    161         }
    162         VerboseIntBuffer buffer = new VerboseIntBuffer(buffer2);
    163 
    164 
    165         // generate center squares minus the edges
    166         //System.out.println("for (x="+lod+"; x<"+(getWidth()-(2*lod))+"; x+="+lod+")");
    167         //System.out.println("	for (z="+lod+"; z<"+(getWidth()-(1*lod))+"; z+="+lod+")");
    168         for (int r = lod; r < getWidth() - (2 * lod); r += lod) { // row
    169             int rowIdx = r * getWidth();
    170             int nextRowIdx = (r + 1 * lod) * getWidth();
    171             for (int c = lod; c < getWidth() - (1 * lod); c += lod) { // column
    172                 int idx = rowIdx + c;
    173                 buffer.put(idx);
    174                 idx = nextRowIdx + c;
    175                 buffer.put(idx);
    176             }
    177 
    178             // add degenerate triangles
    179             if (r < getWidth() - (3 * lod)) {
    180                 int idx = nextRowIdx + getWidth() - (1 * lod) - 1;
    181                 buffer.put(idx);
    182                 idx = nextRowIdx + (1 * lod); // inset by 1
    183                 buffer.put(idx);
    184                 //System.out.println("");
    185             }
    186         }
    187         //System.out.println("\nright:");
    188 
    189         //int runningBufferCount = buffer.getCount();
    190         //System.out.println("buffer start: "+runningBufferCount);
    191 
    192 
    193         // right
    194         int br = getWidth() * (getWidth() - lod) - 1 - lod;
    195         buffer.put(br); // bottom right -1
    196         int corner = getWidth() * getWidth() - 1;
    197         buffer.put(corner);	// bottom right corner
    198         if (rightLod) { // if lower LOD
    199             for (int row = getWidth() - lod; row >= 1 + lod; row -= 2 * lod) {
    200                 int idx = (row) * getWidth() - 1 - lod;
    201                 buffer.put(idx);
    202                 idx = (row - lod) * getWidth() - 1;
    203                 buffer.put(idx);
    204                 if (row > lod + 1) { //if not the last one
    205                     idx = (row - lod) * getWidth() - 1 - lod;
    206                     buffer.put(idx);
    207                     idx = (row - lod) * getWidth() - 1;
    208                     buffer.put(idx);
    209                 } else {
    210                 }
    211             }
    212         } else {
    213             buffer.put(corner);//br+1);//degenerate to flip winding order
    214             for (int row = getWidth() - lod; row > lod; row -= lod) {
    215                 int idx = row * getWidth() - 1; // mult to get row
    216                 buffer.put(idx);
    217                 buffer.put(idx - lod);
    218             }
    219 
    220         }
    221 
    222         buffer.put(getWidth() - 1);
    223 
    224 
    225         //System.out.println("\nbuffer right: "+(buffer.getCount()-runningBufferCount));
    226         //runningBufferCount = buffer.getCount();
    227 
    228 
    229         //System.out.println("\ntop:");
    230 
    231         // top 			(the order gets reversed here so the diagonals line up)
    232         if (topLod) { // if lower LOD
    233             if (rightLod) {
    234                 buffer.put(getWidth() - 1);
    235             }
    236             for (int col = getWidth() - 1; col >= lod; col -= 2 * lod) {
    237                 int idx = (lod * getWidth()) + col - lod; // next row
    238                 buffer.put(idx);
    239                 idx = col - 2 * lod;
    240                 buffer.put(idx);
    241                 if (col > lod * 2) { //if not the last one
    242                     idx = (lod * getWidth()) + col - 2 * lod;
    243                     buffer.put(idx);
    244                     idx = col - 2 * lod;
    245                     buffer.put(idx);
    246                 } else {
    247                 }
    248             }
    249         } else {
    250             if (rightLod) {
    251                 buffer.put(getWidth() - 1);
    252             }
    253             for (int col = getWidth() - 1 - lod; col > 0; col -= lod) {
    254                 int idx = col + (lod * getWidth());
    255                 buffer.put(idx);
    256                 idx = col;
    257                 buffer.put(idx);
    258             }
    259             buffer.put(0);
    260         }
    261         buffer.put(0);
    262 
    263         //System.out.println("\nbuffer top: "+(buffer.getCount()-runningBufferCount));
    264         //runningBufferCount = buffer.getCount();
    265 
    266         //System.out.println("\nleft:");
    267 
    268         // left
    269         if (leftLod) { // if lower LOD
    270             if (topLod) {
    271                 buffer.put(0);
    272             }
    273             for (int row = 0; row < getWidth() - lod; row += 2 * lod) {
    274                 int idx = (row + lod) * getWidth() + lod;
    275                 buffer.put(idx);
    276                 idx = (row + 2 * lod) * getWidth();
    277                 buffer.put(idx);
    278                 if (row < getWidth() - lod - 2 - 1) { //if not the last one
    279                     idx = (row + 2 * lod) * getWidth() + lod;
    280                     buffer.put(idx);
    281                     idx = (row + 2 * lod) * getWidth();
    282                     buffer.put(idx);
    283                 } else {
    284                 }
    285             }
    286         } else {
    287             if (!topLod) {
    288                 buffer.put(0);
    289             }
    290             //buffer.put(getWidth()+1); // degenerate
    291             //buffer.put(0); // degenerate winding-flip
    292             for (int row = lod; row < getWidth() - lod; row += lod) {
    293                 int idx = row * getWidth();
    294                 buffer.put(idx);
    295                 idx = row * getWidth() + lod;
    296                 buffer.put(idx);
    297             }
    298 
    299         }
    300         buffer.put(getWidth() * (getWidth() - 1));
    301 
    302 
    303         //System.out.println("\nbuffer left: "+(buffer.getCount()-runningBufferCount));
    304         //runningBufferCount = buffer.getCount();
    305 
    306         //if (true) return buffer.delegate;
    307         //System.out.println("\nbottom");
    308 
    309         // bottom
    310         if (bottomLod) { // if lower LOD
    311             if (leftLod) {
    312                 buffer.put(getWidth() * (getWidth() - 1));
    313             }
    314             // there was a slight bug here when really high LOD near maxLod
    315             // far right has extra index one row up and all the way to the right, need to skip last index entered
    316             // seemed to be fixed by making "getWidth()-1-2-lod" this: "getWidth()-1-2*lod", which seems more correct
    317             for (int col = 0; col < getWidth() - lod; col += 2 * lod) {
    318                 int idx = getWidth() * (getWidth() - 1 - lod) + col + lod;
    319                 buffer.put(idx);
    320                 idx = getWidth() * (getWidth() - 1) + col + 2 * lod;
    321                 buffer.put(idx);
    322                 if (col < getWidth() - 1 - 2 * lod) { //if not the last one
    323                     idx = getWidth() * (getWidth() - 1 - lod) + col + 2 * lod;
    324                     buffer.put(idx);
    325                     idx = getWidth() * (getWidth() - 1) + col + 2 * lod;
    326                     buffer.put(idx);
    327                 } else {
    328                 }
    329             }
    330         } else {
    331             if (leftLod) {
    332                 buffer.put(getWidth() * (getWidth() - 1));
    333             }
    334             for (int col = lod; col < getWidth() - lod; col += lod) {
    335                 int idx = getWidth() * (getWidth() - 1 - lod) + col; // up
    336                 buffer.put(idx);
    337                 idx = getWidth() * (getWidth() - 1) + col; // down
    338                 buffer.put(idx);
    339             }
    340             //buffer.put(getWidth()*getWidth()-1-lod); // <-- THIS caused holes at the end!
    341         }
    342 
    343         buffer.put(getWidth() * getWidth() - 1);
    344 
    345         //System.out.println("\nbuffer bottom: "+(buffer.getCount()-runningBufferCount));
    346         //runningBufferCount = buffer.getCount();
    347 
    348         //System.out.println("\nBuffer size: "+buffer.getCount());
    349 
    350         // fill in the rest of the buffer with degenerates, there should only be a couple
    351         for (int i = buffer.getCount(); i < numIndexes; i++) {
    352             buffer.put(getWidth() * getWidth() - 1);
    353         }
    354 
    355         return buffer.delegate;
    356     }
    357 
    358     public IntBuffer writeIndexArrayLodVariable(IntBuffer store, int lod, int rightLod, int topLod, int leftLod, int bottomLod) {
    359 
    360         IntBuffer buffer2 = store;
    361         int numIndexes = calculateNumIndexesLodDiff(lod);
    362         if (store == null) {
    363             buffer2 = BufferUtils.createIntBuffer(numIndexes);
    364         }
    365         VerboseIntBuffer buffer = new VerboseIntBuffer(buffer2);
    366 
    367 
    368         // generate center squares minus the edges
    369         //System.out.println("for (x="+lod+"; x<"+(getWidth()-(2*lod))+"; x+="+lod+")");
    370         //System.out.println("	for (z="+lod+"; z<"+(getWidth()-(1*lod))+"; z+="+lod+")");
    371         for (int r = lod; r < getWidth() - (2 * lod); r += lod) { // row
    372             int rowIdx = r * getWidth();
    373             int nextRowIdx = (r + 1 * lod) * getWidth();
    374             for (int c = lod; c < getWidth() - (1 * lod); c += lod) { // column
    375                 int idx = rowIdx + c;
    376                 buffer.put(idx);
    377                 idx = nextRowIdx + c;
    378                 buffer.put(idx);
    379             }
    380 
    381             // add degenerate triangles
    382             if (r < getWidth() - (3 * lod)) {
    383                 int idx = nextRowIdx + getWidth() - (1 * lod) - 1;
    384                 buffer.put(idx);
    385                 idx = nextRowIdx + (1 * lod); // inset by 1
    386                 buffer.put(idx);
    387                 //System.out.println("");
    388             }
    389         }
    390         //System.out.println("\nright:");
    391 
    392         //int runningBufferCount = buffer.getCount();
    393         //System.out.println("buffer start: "+runningBufferCount);
    394 
    395 
    396         // right
    397         int br = getWidth() * (getWidth() - lod) - 1 - lod;
    398         buffer.put(br); // bottom right -1
    399         int corner = getWidth() * getWidth() - 1;
    400         buffer.put(corner);	// bottom right corner
    401         if (rightLod > lod) { // if lower LOD
    402             int idx = corner;
    403             int it = (getWidth() - 1) / rightLod; // iterations
    404             int lodDiff = rightLod / lod;
    405             for (int i = it; i > 0; i--) { // for each lod level of the neighbour
    406                 idx = getWidth() * (i * rightLod + 1) - 1;
    407                 for (int j = 1; j <= lodDiff; j++) { // for each section in that lod level
    408                     int idxB = idx - (getWidth() * (j * lod)) - lod;
    409 
    410                     if (j == lodDiff && i == 1) {// the last one
    411                         buffer.put(getWidth() - 1);
    412                     } else if (j == lodDiff) {
    413                         buffer.put(idxB);
    414                         buffer.put(idxB + lod);
    415                     } else {
    416                         buffer.put(idxB);
    417                         buffer.put(idx);
    418                     }
    419                 }
    420             }
    421             // reset winding order
    422             buffer.put(getWidth() * (lod + 1) - lod - 1); // top-right +1row
    423             buffer.put(getWidth() - 1);// top-right
    424 
    425         } else {
    426             buffer.put(corner);//br+1);//degenerate to flip winding order
    427             for (int row = getWidth() - lod; row > lod; row -= lod) {
    428                 int idx = row * getWidth() - 1; // mult to get row
    429                 buffer.put(idx);
    430                 buffer.put(idx - lod);
    431             }
    432             buffer.put(getWidth() - 1);
    433         }
    434 
    435 
    436         //System.out.println("\nbuffer right: "+(buffer.getCount()-runningBufferCount));
    437         //runningBufferCount = buffer.getCount();
    438 
    439 
    440         //System.out.println("\ntop:");
    441 
    442         // top 			(the order gets reversed here so the diagonals line up)
    443         if (topLod > lod) { // if lower LOD
    444             if (rightLod > lod) {
    445                 // need to flip winding order
    446                 buffer.put(getWidth() - 1);
    447                 buffer.put(getWidth() * lod - 1);
    448                 buffer.put(getWidth() - 1);
    449             }
    450             int idx = getWidth() - 1;
    451             int it = (getWidth() - 1) / topLod; // iterations
    452             int lodDiff = topLod / lod;
    453             for (int i = it; i > 0; i--) { // for each lod level of the neighbour
    454                 idx = (i * topLod);
    455                 for (int j = 1; j <= lodDiff; j++) { // for each section in that lod level
    456                     int idxB = lod * getWidth() + (i * topLod) - (j * lod);
    457 
    458                     if (j == lodDiff && i == 1) {// the last one
    459                         buffer.put(0);
    460                     } else if (j == lodDiff) {
    461                         buffer.put(idxB);
    462                         buffer.put(idx - topLod);
    463                     } else {
    464                         buffer.put(idxB);
    465                         buffer.put(idx);
    466                     }
    467                 }
    468             }
    469         } else {
    470             if (rightLod > lod) {
    471                 buffer.put(getWidth() - 1);
    472             }
    473             for (int col = getWidth() - 1 - lod; col > 0; col -= lod) {
    474                 int idx = col + (lod * getWidth());
    475                 buffer.put(idx);
    476                 idx = col;
    477                 buffer.put(idx);
    478             }
    479             buffer.put(0);
    480         }
    481         buffer.put(0);
    482 
    483         //System.out.println("\nbuffer top: "+(buffer.getCount()-runningBufferCount));
    484         //runningBufferCount = buffer.getCount();
    485 
    486         //System.out.println("\nleft:");
    487 
    488         // left
    489         if (leftLod > lod) { // if lower LOD
    490 
    491             int idx = 0;
    492             int it = (getWidth() - 1) / leftLod; // iterations
    493             int lodDiff = leftLod / lod;
    494             for (int i = 0; i < it; i++) { // for each lod level of the neighbour
    495                 idx = getWidth() * (i * leftLod);
    496                 for (int j = 1; j <= lodDiff; j++) { // for each section in that lod level
    497                     int idxB = idx + (getWidth() * (j * lod)) + lod;
    498 
    499                     if (j == lodDiff && i == it - 1) {// the last one
    500                         buffer.put(getWidth() * getWidth() - getWidth());
    501                     } else if (j == lodDiff) {
    502                         buffer.put(idxB);
    503                         buffer.put(idxB - lod);
    504                     } else {
    505                         buffer.put(idxB);
    506                         buffer.put(idx);
    507                     }
    508                 }
    509             }
    510 
    511         } else {
    512             buffer.put(0);
    513             buffer.put(getWidth() * lod + lod);
    514             buffer.put(0);
    515             for (int row = lod; row < getWidth() - lod; row += lod) {
    516                 int idx = row * getWidth();
    517                 buffer.put(idx);
    518                 idx = row * getWidth() + lod;
    519                 buffer.put(idx);
    520             }
    521             buffer.put(getWidth() * (getWidth() - 1));
    522         }
    523         //buffer.put(getWidth()*(getWidth()-1));
    524 
    525 
    526         //System.out.println("\nbuffer left: "+(buffer.getCount()-runningBufferCount));
    527         //runningBufferCount = buffer.getCount();
    528 
    529         //if (true) return buffer.delegate;
    530         //System.out.println("\nbottom");
    531 
    532         // bottom
    533         if (bottomLod > lod) { // if lower LOD
    534             if (leftLod > lod) {
    535                 buffer.put(getWidth() * (getWidth() - 1));
    536                 buffer.put(getWidth() * (getWidth() - lod));
    537                 buffer.put(getWidth() * (getWidth() - 1));
    538             }
    539 
    540             int idx = getWidth() * getWidth() - getWidth();
    541             int it = (getWidth() - 1) / bottomLod; // iterations
    542             int lodDiff = bottomLod / lod;
    543             for (int i = 0; i < it; i++) { // for each lod level of the neighbour
    544                 idx = getWidth() * getWidth() - getWidth() + (i * bottomLod);
    545                 for (int j = 1; j <= lodDiff; j++) { // for each section in that lod level
    546                     int idxB = idx - (getWidth() * lod) + j * lod;
    547 
    548                     if (j == lodDiff && i == it - 1) {// the last one
    549                         buffer.put(getWidth() * getWidth() - 1);
    550                     } else if (j == lodDiff) {
    551                         buffer.put(idxB);
    552                         buffer.put(idx + bottomLod);
    553                     } else {
    554                         buffer.put(idxB);
    555                         buffer.put(idx);
    556                     }
    557                 }
    558             }
    559         } else {
    560             if (leftLod > lod) {
    561                 buffer.put(getWidth() * (getWidth() - 1));
    562                 buffer.put(getWidth() * getWidth() - (getWidth() * lod) + lod);
    563                 buffer.put(getWidth() * (getWidth() - 1));
    564             }
    565             for (int col = lod; col < getWidth() - lod; col += lod) {
    566                 int idx = getWidth() * (getWidth() - 1 - lod) + col; // up
    567                 buffer.put(idx);
    568                 idx = getWidth() * (getWidth() - 1) + col; // down
    569                 buffer.put(idx);
    570             }
    571             //buffer.put(getWidth()*getWidth()-1-lod); // <-- THIS caused holes at the end!
    572         }
    573 
    574         buffer.put(getWidth() * getWidth() - 1);
    575 
    576         //System.out.println("\nbuffer bottom: "+(buffer.getCount()-runningBufferCount));
    577         //runningBufferCount = buffer.getCount();
    578 
    579         //System.out.println("\nBuffer size: "+buffer.getCount());
    580 
    581         // fill in the rest of the buffer with degenerates, there should only be a couple
    582         for (int i = buffer.getCount(); i < numIndexes; i++) {
    583             buffer.put(getWidth() * getWidth() - 1);
    584         }
    585 
    586         return buffer.delegate;
    587     }
    588 
    589 
    590     /*private int calculateNumIndexesNormal(int lod) {
    591     int length = getWidth()-1;
    592     int num = ((length/lod)+1)*((length/lod)+1)*2;
    593     System.out.println("num: "+num);
    594     num -= 2*((length/lod)+1);
    595     System.out.println("num2: "+num);
    596     // now get the degenerate indexes that exist between strip rows
    597     num += 2*(((length/lod)+1)-2); // every row except the first and last
    598     System.out.println("Index buffer size: "+num);
    599     return num;
    600     }*/
    601     /**
    602      * calculate how many indexes there will be.
    603      * This isn't that precise and there might be a couple extra.
    604      */
    605     private int calculateNumIndexesLodDiff(int lod) {
    606         if (lod == 0) {
    607             lod = 1;
    608         }
    609         int length = getWidth() - 1; // make it even for lod calc
    610         int side = (length / lod) + 1 - (2);
    611         //System.out.println("side: "+side);
    612         int num = side * side * 2;
    613         //System.out.println("num: "+num);
    614         num -= 2 * side;	// remove one first row and one last row (they are only hit once each)
    615         //System.out.println("num2: "+num);
    616         // now get the degenerate indexes that exist between strip rows
    617         int degenerates = 2 * (side - (2)); // every row except the first and last
    618         num += degenerates;
    619         //System.out.println("degenerates: "+degenerates);
    620 
    621         //System.out.println("center, before edges: "+num);
    622 
    623         num += (getWidth() / lod) * 2 * 4;
    624         num++;
    625 
    626         num += 10;// TODO remove me: extra
    627         //System.out.println("Index buffer size: "+num);
    628         return num;
    629     }
    630 
    631     public FloatBuffer[] writeTangentArray(FloatBuffer normalBuffer, FloatBuffer tangentStore, FloatBuffer binormalStore, FloatBuffer textureBuffer, Vector3f scale) {
    632         if (!isLoaded()) {
    633             throw new NullPointerException();
    634         }
    635 
    636         if (tangentStore != null) {
    637             if (tangentStore.remaining() < getWidth() * getHeight() * 3) {
    638                 throw new BufferUnderflowException();
    639             }
    640         } else {
    641             tangentStore = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3);
    642         }
    643         tangentStore.rewind();
    644 
    645         if (binormalStore != null) {
    646             if (binormalStore.remaining() < getWidth() * getHeight() * 3) {
    647                 throw new BufferUnderflowException();
    648             }
    649         } else {
    650             binormalStore = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3);
    651         }
    652         binormalStore.rewind();
    653 
    654         Vector3f normal = new Vector3f();
    655         Vector3f tangent = new Vector3f();
    656         Vector3f binormal = new Vector3f();
    657         /*Vector3f v1 = new Vector3f();
    658         Vector3f v2 = new Vector3f();
    659         Vector3f v3 = new Vector3f();
    660         Vector2f t1 = new Vector2f();
    661         Vector2f t2 = new Vector2f();
    662         Vector2f t3 = new Vector2f();*/
    663 
    664         for (int r = 0; r < getHeight(); r++) {
    665             for (int c = 0; c < getWidth(); c++) {
    666 
    667                 int idx = (r * getWidth() + c) * 3;
    668                 normal.set(normalBuffer.get(idx), normalBuffer.get(idx+1), normalBuffer.get(idx+2));
    669                 tangent.set(normal.cross(new Vector3f(0,0,1)));
    670                 binormal.set(new Vector3f(1,0,0).cross(normal));
    671 
    672                 BufferUtils.setInBuffer(tangent.normalizeLocal(), tangentStore, (r * getWidth() + c)); // save the tangent
    673                 BufferUtils.setInBuffer(binormal.normalizeLocal(), binormalStore, (r * getWidth() + c)); // save the binormal
    674             }
    675         }
    676 
    677 /*        for (int r = 0; r < getHeight(); r++) {
    678             for (int c = 0; c < getWidth(); c++) {
    679 
    680                 int texIdx = ((getHeight() - 1 - r) * getWidth() + c) * 2; // pull from the end
    681                 int texIdxAbove = ((getHeight() - 1 - (r - 1)) * getWidth() + c) * 2; // pull from the end
    682                 int texIdxNext = ((getHeight() - 1 - (r + 1)) * getWidth() + c) * 2; // pull from the end
    683 
    684                 v1.set(c, getValue(c, r), r);
    685                 t1.set(textureBuffer.get(texIdx), textureBuffer.get(texIdx + 1));
    686 
    687                 // below
    688                 if (r == getHeight()-1) { // last row
    689                     v3.set(c, getValue(c, r), r + 1);
    690                     float u = textureBuffer.get(texIdx) - textureBuffer.get(texIdxAbove);
    691                     u += textureBuffer.get(texIdx);
    692                     float v = textureBuffer.get(texIdx + 1) - textureBuffer.get(texIdxAbove + 1);
    693                     v += textureBuffer.get(texIdx + 1);
    694                     t3.set(u, v);
    695                 } else {
    696                     v3.set(c, getValue(c, r + 1), r + 1);
    697                     t3.set(textureBuffer.get(texIdxNext), textureBuffer.get(texIdxNext + 1));
    698                 }
    699 
    700                 //right
    701                 if (c == getWidth()-1) { // last column
    702                     v2.set(c + 1, getValue(c, r), r);
    703                     float u = textureBuffer.get(texIdx) - textureBuffer.get(texIdx - 2);
    704                     u += textureBuffer.get(texIdx);
    705                     float v = textureBuffer.get(texIdx + 1) - textureBuffer.get(texIdx - 1);
    706                     v += textureBuffer.get(texIdx - 1);
    707                     t2.set(u, v);
    708                 } else {
    709                     v2.set(c + 1, getValue(c + 1, r), r); // one to the right
    710                     t2.set(textureBuffer.get(texIdx + 2), textureBuffer.get(texIdx + 3));
    711                 }
    712 
    713                 calculateTangent(new Vector3f[]{v1.mult(scale), v2.mult(scale), v3.mult(scale)}, new Vector2f[]{t1, t2, t3}, tangent, binormal);
    714                 BufferUtils.setInBuffer(tangent, tangentStore, (r * getWidth() + c)); // save the tangent
    715                 BufferUtils.setInBuffer(binormal, binormalStore, (r * getWidth() + c)); // save the binormal
    716             }
    717         }
    718         */
    719         return new FloatBuffer[]{tangentStore, binormalStore};
    720     }
    721 
    722     /**
    723      *
    724      * @param v Takes 3 vertices: root, right, bottom
    725      * @param t Takes 3 tex coords: root, right, bottom
    726      * @param tangent that will store the result
    727      * @return the tangent store
    728      */
    729     public static Vector3f calculateTangent(Vector3f[] v, Vector2f[] t, Vector3f tangent, Vector3f binormal) {
    730         Vector3f edge1 = new Vector3f(); // y=0
    731         Vector3f edge2 = new Vector3f(); // x=0
    732         Vector2f edge1uv = new Vector2f(); // y=0
    733         Vector2f edge2uv = new Vector2f(); // x=0
    734 
    735         t[2].subtract(t[0], edge2uv);
    736         t[1].subtract(t[0], edge1uv);
    737 
    738         float det = edge1uv.x * edge2uv.y;// - edge1uv.y*edge2uv.x;  = 0
    739 
    740         boolean normalize = true;
    741         if (Math.abs(det) < 0.0000001f) {
    742             det = 1;
    743             normalize = true;
    744         }
    745 
    746         v[1].subtract(v[0], edge1);
    747         v[2].subtract(v[0], edge2);
    748 
    749         tangent.set(edge1);
    750         tangent.normalizeLocal();
    751         binormal.set(edge2);
    752         binormal.normalizeLocal();
    753 
    754         float factor = 1 / det;
    755         tangent.x = (edge2uv.y * edge1.x) * factor;
    756         tangent.y = 0;
    757         tangent.z = (edge2uv.y * edge1.z) * factor;
    758         if (normalize) {
    759             tangent.normalizeLocal();
    760         }
    761 
    762         binormal.x = 0;
    763         binormal.y = (edge1uv.x * edge2.y) * factor;
    764         binormal.z = (edge1uv.x * edge2.z) * factor;
    765         if (normalize) {
    766             binormal.normalizeLocal();
    767         }
    768 
    769         return tangent;
    770     }
    771 
    772     @Override
    773     public FloatBuffer writeNormalArray(FloatBuffer store, Vector3f scale) {
    774         if (!isLoaded()) {
    775             throw new NullPointerException();
    776         }
    777 
    778         if (store != null) {
    779             if (store.remaining() < getWidth() * getHeight() * 3) {
    780                 throw new BufferUnderflowException();
    781             }
    782         } else {
    783             store = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3);
    784         }
    785         store.rewind();
    786 
    787         TempVars vars = TempVars.get();
    788 
    789         Vector3f rootPoint = vars.vect1;
    790         Vector3f rightPoint = vars.vect2;
    791         Vector3f leftPoint = vars.vect3;
    792         Vector3f topPoint = vars.vect4;
    793         Vector3f bottomPoint = vars.vect5;
    794 
    795         Vector3f tmp1 = vars.vect6;
    796 
    797         // calculate normals for each polygon
    798         for (int r = 0; r < getHeight(); r++) {
    799             for (int c = 0; c < getWidth(); c++) {
    800 
    801                 rootPoint.set(c, getValue(c, r), r);
    802                 Vector3f normal = vars.vect8;
    803 
    804                 if (r == 0) { // first row
    805                     if (c == 0) { // first column
    806                         rightPoint.set(c + 1, getValue(c + 1, r), r);
    807                         bottomPoint.set(c, getValue(c, r + 1), r + 1);
    808                         getNormal(bottomPoint, rootPoint, rightPoint, scale, normal);
    809                     } else if (c == getWidth() - 1) { // last column
    810                         leftPoint.set(c - 1, getValue(c - 1, r), r);
    811                         bottomPoint.set(c, getValue(c, r + 1), r + 1);
    812                         getNormal(leftPoint, rootPoint, bottomPoint, scale, normal);
    813                     } else { // all middle columns
    814                         leftPoint.set(c - 1, getValue(c - 1, r), r);
    815                         rightPoint.set(c + 1, getValue(c + 1, r), r);
    816                         bottomPoint.set(c, getValue(c, r + 1), r + 1);
    817 
    818                         normal.set( getNormal(leftPoint, rootPoint, bottomPoint, scale, tmp1) );
    819                         normal.add( getNormal(bottomPoint, rootPoint, rightPoint, scale, tmp1) );
    820                         normal.normalizeLocal();
    821                     }
    822                 } else if (r == getHeight() - 1) { // last row
    823                     if (c == 0) { // first column
    824                         topPoint.set(c, getValue(c, r - 1), r - 1);
    825                         rightPoint.set(c + 1, getValue(c + 1, r), r);
    826                         getNormal(rightPoint, rootPoint, topPoint, scale, normal);
    827                     } else if (c == getWidth() - 1) { // last column
    828                         topPoint.set(c, getValue(c, r - 1), r - 1);
    829                         leftPoint.set(c - 1, getValue(c - 1, r), r);
    830                         getNormal(topPoint, rootPoint, leftPoint, scale, normal);
    831                     } else { // all middle columns
    832                         topPoint.set(c, getValue(c, r - 1), r - 1);
    833                         leftPoint.set(c - 1, getValue(c - 1, r), r);
    834                         rightPoint.set(c + 1, getValue(c + 1, r), r);
    835 
    836                         normal.set( getNormal(topPoint, rootPoint, leftPoint, scale, tmp1) );
    837                         normal.add( getNormal(rightPoint, rootPoint, topPoint, scale, tmp1) );
    838                         normal.normalizeLocal();
    839                     }
    840                 } else { // all middle rows
    841                     if (c == 0) { // first column
    842                         topPoint.set(c, getValue(c, r - 1), r - 1);
    843                         rightPoint.set(c + 1, getValue(c + 1, r), r);
    844                         bottomPoint.set(c, getValue(c, r + 1), r + 1);
    845 
    846                         normal.set( getNormal(rightPoint, rootPoint, topPoint, scale, tmp1) );
    847                         normal.add( getNormal(bottomPoint, rootPoint, rightPoint, scale, tmp1) );
    848                         normal.normalizeLocal();
    849                     } else if (c == getWidth() - 1) { // last column
    850                         topPoint.set(c, getValue(c, r - 1), r - 1);
    851                         leftPoint.set(c - 1, getValue(c - 1, r), r);
    852                         bottomPoint.set(c, getValue(c, r + 1), r + 1); //XXX wrong
    853 
    854                         normal.set( getNormal(topPoint, rootPoint, leftPoint, scale, tmp1) );
    855                         normal.add( getNormal(leftPoint, rootPoint, bottomPoint, scale, tmp1) );
    856                         normal.normalizeLocal();
    857                     } else { // all middle columns
    858                         topPoint.set(c, getValue(c, r - 1), r - 1);
    859                         leftPoint.set(c - 1, getValue(c - 1, r), r);
    860                         rightPoint.set(c + 1, getValue(c + 1, r), r);
    861                         bottomPoint.set(c, getValue(c, r + 1), r + 1);
    862 
    863                         normal.set( getNormal(topPoint,  rootPoint, leftPoint, scale, tmp1 ) );
    864                         normal.add( getNormal(leftPoint, rootPoint, bottomPoint, scale, tmp1) );
    865                         normal.add( getNormal(bottomPoint, rootPoint, rightPoint, scale, tmp1) );
    866                         normal.add( getNormal(rightPoint, rootPoint, topPoint, scale, tmp1) );
    867                         normal.normalizeLocal();
    868                     }
    869                 }
    870 
    871                 BufferUtils.setInBuffer(normal, store, (r * getWidth() + c)); // save the normal
    872             }
    873         }
    874         vars.release();
    875 
    876         return store;
    877     }
    878 
    879     private Vector3f getNormal(Vector3f firstPoint, Vector3f rootPoint, Vector3f secondPoint, Vector3f scale, Vector3f store) {
    880         float x1 = firstPoint.x - rootPoint.x;
    881         float y1 = firstPoint.y - rootPoint.y;
    882         float z1 = firstPoint.z - rootPoint.z;
    883         x1 *= scale.x;
    884         y1 *= scale.y;
    885         z1 *= scale.z;
    886         float x2 = secondPoint.x - rootPoint.x;
    887         float y2 = secondPoint.y - rootPoint.y;
    888         float z2 = secondPoint.z - rootPoint.z;
    889         x2 *= scale.x;
    890         y2 *= scale.y;
    891         z2 *= scale.z;
    892         float x3 = (y1 * z2) - (z1 * y2);
    893         float y3 = (z1 * x2) - (x1 * z2);
    894         float z3 = (x1 * y2) - (y1 * x2);
    895 
    896         float inv = 1.0f / FastMath.sqrt(x3 * x3 + y3 * y3 + z3 * z3);
    897         store.x = x3 * inv;
    898         store.y = y3 * inv;
    899         store.z = z3 * inv;
    900         return store;
    901 
    902         /*store.set( firstPoint.subtractLocal(rootPoint).multLocal(scale).crossLocal(secondPoint.subtractLocal(rootPoint).multLocal(scale)).normalizeLocal() );
    903         return store;*/
    904 
    905     }
    906 
    907     /**
    908      * Keeps a count of the number of indexes, good for debugging
    909      */
    910     public class VerboseIntBuffer {
    911 
    912         private IntBuffer delegate;
    913         int count = 0;
    914 
    915         public VerboseIntBuffer(IntBuffer d) {
    916             delegate = d;
    917         }
    918 
    919         public void put(int value) {
    920             try {
    921                 delegate.put(value);
    922                 count++;
    923             } catch (BufferOverflowException e) {
    924                 //System.out.println("err buffer size: "+delegate.capacity());
    925             }
    926         }
    927 
    928         public int getCount() {
    929             return count;
    930         }
    931     }
    932 
    933     /**
    934      * Get a representation of the underlying triangle at the given point,
    935      * translated to world coordinates.
    936      *
    937      * @param x local x coordinate
    938      * @param z local z coordinate
    939      * @return a triangle in world space not local space
    940      */
    941     protected Triangle getTriangleAtPoint(float x, float z, Vector3f scale, Vector3f translation) {
    942         Triangle tri = getTriangleAtPoint(x, z);
    943         if (tri != null) {
    944             tri.get1().multLocal(scale).addLocal(translation);
    945             tri.get2().multLocal(scale).addLocal(translation);
    946             tri.get3().multLocal(scale).addLocal(translation);
    947         }
    948         return tri;
    949     }
    950 
    951     /**
    952      * Get the two triangles that make up the grid section at the specified point,
    953      * translated to world coordinates.
    954      *
    955      * @param x local x coordinate
    956      * @param z local z coordinate
    957      * @param scale
    958      * @param translation
    959      * @return two triangles in world space not local space
    960      */
    961     protected Triangle[] getGridTrianglesAtPoint(float x, float z, Vector3f scale, Vector3f translation) {
    962         Triangle[] tris = getGridTrianglesAtPoint(x, z);
    963         if (tris != null) {
    964             tris[0].get1().multLocal(scale).addLocal(translation);
    965             tris[0].get2().multLocal(scale).addLocal(translation);
    966             tris[0].get3().multLocal(scale).addLocal(translation);
    967             tris[1].get1().multLocal(scale).addLocal(translation);
    968             tris[1].get2().multLocal(scale).addLocal(translation);
    969             tris[1].get3().multLocal(scale).addLocal(translation);
    970         }
    971         return tris;
    972     }
    973 
    974     /**
    975      * Get the two triangles that make up the grid section at the specified point.
    976      *
    977      * For every grid space there are two triangles oriented like this:
    978      *  *----*
    979      *  |a / |
    980      *  | / b|
    981      *  *----*
    982      * The corners of the mesh have differently oriented triangles. The two
    983      * corners that we have to special-case are the top left and bottom right
    984      * corners. They are oriented inversely:
    985      *  *----*
    986      *  | \ b|
    987      *  |a \ |
    988      *  *----*
    989      *
    990      * @param x local x coordinate
    991      * @param z local z coordinate
    992      * @param scale
    993      * @param translation
    994      * @return
    995      */
    996     protected Triangle[] getGridTrianglesAtPoint(float x, float z) {
    997         int gridX = (int) x;
    998         int gridY = (int) z;
    999 
   1000         int index = findClosestHeightIndex(gridX, gridY);
   1001         if (index < 0) {
   1002             return null;
   1003         }
   1004 
   1005         Triangle t = new Triangle(new Vector3f(), new Vector3f(), new Vector3f());
   1006         Triangle t2 = new Triangle(new Vector3f(), new Vector3f(), new Vector3f());
   1007 
   1008         float h1 = hdata[index];                // top left
   1009         float h2 = hdata[index + 1];            // top right
   1010         float h3 = hdata[index + width];        // bottom left
   1011         float h4 = hdata[index + width + 1];    // bottom right
   1012 
   1013 
   1014         if ((gridX == 0 && gridY == 0) || (gridX == width - 1 && gridY == width - 1)) {
   1015             // top left or bottom right grid point
   1016             t.get(0).x = (gridX);
   1017             t.get(0).y = (h1);
   1018             t.get(0).z = (gridY);
   1019 
   1020             t.get(1).x = (gridX);
   1021             t.get(1).y = (h3);
   1022             t.get(1).z = (gridY + 1);
   1023 
   1024             t.get(2).x = (gridX + 1);
   1025             t.get(2).y = (h4);
   1026             t.get(2).z = (gridY + 1);
   1027 
   1028             t2.get(0).x = (gridX);
   1029             t2.get(0).y = (h1);
   1030             t2.get(0).z = (gridY);
   1031 
   1032             t2.get(1).x = (gridX + 1);
   1033             t2.get(1).y = (h4);
   1034             t2.get(1).z = (gridY + 1);
   1035 
   1036             t2.get(2).x = (gridX + 1);
   1037             t2.get(2).y = (h2);
   1038             t2.get(2).z = (gridY);
   1039         } else {
   1040             // all other grid points
   1041             t.get(0).x = (gridX);
   1042             t.get(0).y = (h1);
   1043             t.get(0).z = (gridY);
   1044 
   1045             t.get(1).x = (gridX);
   1046             t.get(1).y = (h3);
   1047             t.get(1).z = (gridY + 1);
   1048 
   1049             t.get(2).x = (gridX + 1);
   1050             t.get(2).y = (h2);
   1051             t.get(2).z = (gridY);
   1052 
   1053             t2.get(0).x = (gridX + 1);
   1054             t2.get(0).y = (h2);
   1055             t2.get(0).z = (gridY);
   1056 
   1057             t2.get(1).x = (gridX);
   1058             t2.get(1).y = (h3);
   1059             t2.get(1).z = (gridY + 1);
   1060 
   1061             t2.get(2).x = (gridX + 1);
   1062             t2.get(2).y = (h4);
   1063             t2.get(2).z = (gridY + 1);
   1064         }
   1065 
   1066         return new Triangle[]{t, t2};
   1067     }
   1068 
   1069     /**
   1070      * Get the triangle that the point is on.
   1071      *
   1072      * @param x coordinate in local space to the geomap
   1073      * @param z coordinate in local space to the geomap
   1074      * @return triangle in local space to the geomap
   1075      */
   1076     protected Triangle getTriangleAtPoint(float x, float z) {
   1077         Triangle[] triangles = getGridTrianglesAtPoint(x, z);
   1078         if (triangles == null) {
   1079             //System.out.println("x,z: " + x + "," + z);
   1080             return null;
   1081         }
   1082         Vector2f point = new Vector2f(x, z);
   1083         Vector2f t1 = new Vector2f(triangles[0].get1().x, triangles[0].get1().z);
   1084         Vector2f t2 = new Vector2f(triangles[0].get2().x, triangles[0].get2().z);
   1085         Vector2f t3 = new Vector2f(triangles[0].get3().x, triangles[0].get3().z);
   1086 
   1087         if (0 != FastMath.pointInsideTriangle(t1, t2, t3, point)) {
   1088             return triangles[0];
   1089         }
   1090 
   1091         t1.set(triangles[1].get1().x, triangles[1].get1().z);
   1092         t1.set(triangles[1].get2().x, triangles[1].get2().z);
   1093         t1.set(triangles[1].get3().x, triangles[1].get3().z);
   1094 
   1095         if (0 != FastMath.pointInsideTriangle(t1, t2, t3, point)) {
   1096             return triangles[1];
   1097         }
   1098 
   1099         return null;
   1100     }
   1101 
   1102     protected int findClosestHeightIndex(int x, int z) {
   1103 
   1104         if (x < 0 || x >= width - 1) {
   1105             return -1;
   1106         }
   1107         if (z < 0 || z >= width - 1) {
   1108             return -1;
   1109         }
   1110 
   1111         return z * width + x;
   1112     }
   1113 
   1114     @Override
   1115     public void write(JmeExporter ex) throws IOException {
   1116         super.write(ex);
   1117     }
   1118 
   1119     @Override
   1120     public void read(JmeImporter im) throws IOException {
   1121         super.read(im);
   1122     }
   1123 }
   1124