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