• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
LODGeomap()68     public LODGeomap() {
69     }
70 
71     @Deprecated
LODGeomap(int size, FloatBuffer heightMap)72     public LODGeomap(int size, FloatBuffer heightMap) {
73         super(heightMap, size, size, 1);
74     }
75 
LODGeomap(int size, float[] heightMap)76     public LODGeomap(int size, float[] heightMap) {
77         super(heightMap, size, size, 1);
78     }
79 
createMesh(Vector3f scale, Vector2f tcScale, Vector2f tcOffset, float offsetAmount, int totalSize, boolean center)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 
createMesh(Vector3f scale, Vector2f tcScale, Vector2f tcOffset, float offsetAmount, int totalSize, boolean center, int lod, boolean rightLod, boolean topLod, boolean leftLod, boolean bottomLod)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 
writeTexCoordArray(FloatBuffer store, Vector2f offset, Vector2f scale, float offsetAmount, int totalSize)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 
getUV(int x, int y, Vector2f store, Vector2f offset, float offsetAmount, int totalSize)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      */
writeIndexArrayLodDiff(IntBuffer store, int lod, boolean rightLod, boolean topLod, boolean leftLod, boolean bottomLod)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 
writeIndexArrayLodVariable(IntBuffer store, int lod, int rightLod, int topLod, int leftLod, int bottomLod)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      */
calculateNumIndexesLodDiff(int lod)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 
writeTangentArray(FloatBuffer normalBuffer, FloatBuffer tangentStore, FloatBuffer binormalStore, FloatBuffer textureBuffer, Vector3f scale)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      */
calculateTangent(Vector3f[] v, Vector2f[] t, Vector3f tangent, Vector3f binormal)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
writeNormalArray(FloatBuffer store, Vector3f scale)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 
getNormal(Vector3f firstPoint, Vector3f rootPoint, Vector3f secondPoint, Vector3f scale, Vector3f store)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 
VerboseIntBuffer(IntBuffer d)915         public VerboseIntBuffer(IntBuffer d) {
916             delegate = d;
917         }
918 
put(int value)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 
getCount()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      */
getTriangleAtPoint(float x, float z, Vector3f scale, Vector3f translation)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      */
getGridTrianglesAtPoint(float x, float z, Vector3f scale, Vector3f translation)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      */
getGridTrianglesAtPoint(float x, float z)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      */
getTriangleAtPoint(float x, float z)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 
findClosestHeightIndex(int x, int z)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
write(JmeExporter ex)1115     public void write(JmeExporter ex) throws IOException {
1116         super.write(ex);
1117     }
1118 
1119     @Override
read(JmeImporter im)1120     public void read(JmeImporter im) throws IOException {
1121         super.read(im);
1122     }
1123 }
1124