blob: 3348b482beaa83f4bb9725cf0e31dee17571aec4 [file] [log] [blame]
Scott Barta59b2e682012-03-01 12:35:35 -08001/*
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 */
32package com.jme3.terrain.geomipmap;
33
34import com.jme3.export.JmeExporter;
35import com.jme3.export.JmeImporter;
36import com.jme3.math.FastMath;
Scott Bartaa6b44652012-03-09 13:52:20 -080037import com.jme3.math.Plane;
Scott Barta59b2e682012-03-01 12:35:35 -080038import com.jme3.math.Triangle;
39import com.jme3.math.Vector2f;
40import com.jme3.math.Vector3f;
41import com.jme3.scene.Mesh;
42import com.jme3.scene.Mesh.Mode;
43import com.jme3.scene.VertexBuffer.Type;
44import com.jme3.terrain.GeoMap;
45import com.jme3.util.BufferUtils;
46import com.jme3.util.TempVars;
47import java.io.IOException;
48import java.nio.BufferOverflowException;
49import java.nio.BufferUnderflowException;
50import java.nio.FloatBuffer;
51import 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 */
66public 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);
Scott Bartaa6b44652012-03-09 13:52:20 -080091 writeTangentArray(nb, tanb, bb, texb, scale);
Scott Barta59b2e682012-03-01 12:35:35 -080092 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
Scott Bartaa6b44652012-03-09 13:52:20 -0800631 public FloatBuffer[] writeTangentArray(FloatBuffer normalBuffer, FloatBuffer tangentStore, FloatBuffer binormalStore, FloatBuffer textureBuffer, Vector3f scale) {
Scott Barta59b2e682012-03-01 12:35:35 -0800632 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
Scott Bartaa6b44652012-03-09 13:52:20 -0800654 Vector3f normal = new Vector3f();
Scott Barta59b2e682012-03-01 12:35:35 -0800655 Vector3f tangent = new Vector3f();
656 Vector3f binormal = new Vector3f();
Scott Bartaa6b44652012-03-09 13:52:20 -0800657 /*Vector3f v1 = new Vector3f();
Scott Barta59b2e682012-03-01 12:35:35 -0800658 Vector3f v2 = new Vector3f();
659 Vector3f v3 = new Vector3f();
660 Vector2f t1 = new Vector2f();
661 Vector2f t2 = new Vector2f();
Scott Bartaa6b44652012-03-09 13:52:20 -0800662 Vector2f t3 = new Vector2f();*/
Scott Barta59b2e682012-03-01 12:35:35 -0800663
664 for (int r = 0; r < getHeight(); r++) {
665 for (int c = 0; c < getWidth(); c++) {
Scott Bartaa6b44652012-03-09 13:52:20 -0800666
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++) {
Scott Barta59b2e682012-03-01 12:35:35 -0800679
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 }
Scott Bartaa6b44652012-03-09 13:52:20 -0800718 */
Scott Barta59b2e682012-03-01 12:35:35 -0800719 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}