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