001/** 002 * Copyright (c) 2008 Ardor Labs, Inc. 003 * 004 * This file is part of Ardor3D. 005 * 006 * Ardor3D is free software: you can redistribute it and/or modify it 007 * under the terms of its license which may be found in the accompanying 008 * LICENSE file or at <http://www.ardor3d.com/LICENSE>. 009 */ 010 011package com.ardor3d.extension.effect.water; 012 013import java.nio.FloatBuffer; 014import java.util.Stack; 015import java.util.concurrent.ExecutionException; 016import java.util.concurrent.ExecutorService; 017import java.util.concurrent.Executors; 018import java.util.concurrent.Future; 019import java.util.concurrent.ThreadFactory; 020import java.util.concurrent.atomic.AtomicInteger; 021import java.util.logging.Level; 022import java.util.logging.Logger; 023 024import com.ardor3d.math.ColorRGBA; 025import com.ardor3d.math.MathUtils; 026import com.ardor3d.math.Matrix4; 027import com.ardor3d.math.Vector2; 028import com.ardor3d.math.Vector3; 029import com.ardor3d.math.Vector4; 030import com.ardor3d.math.type.ReadOnlyMatrix4; 031import com.ardor3d.math.type.ReadOnlyVector2; 032import com.ardor3d.math.type.ReadOnlyVector3; 033import com.ardor3d.renderer.Camera; 034import com.ardor3d.renderer.Renderer; 035import com.ardor3d.scenegraph.IndexBufferData; 036import com.ardor3d.scenegraph.Mesh; 037import com.ardor3d.util.ExtendedCamera; 038import com.ardor3d.util.Timer; 039import com.ardor3d.util.geom.BufferUtils; 040import com.ardor3d.util.geom.Debugger; 041 042/** 043 * <code>ProjectedGrid</code> Projected grid mesh 044 */ 045public class ProjectedGrid extends Mesh { 046 /** The Constant logger. */ 047 private static final Logger logger = Logger.getLogger(ProjectedGrid.class.getName()); 048 049 private final int sizeX; 050 private final int sizeY; 051 052 private FloatBuffer vertBuf; 053 private final FloatBuffer normBuf; 054 private final FloatBuffer texs; 055 056 private final ExtendedCamera mainCamera = new ExtendedCamera(); 057 private final Camera projectorCamera = new Camera(); 058 059 private final Vector4 origin = new Vector4(); 060 private final Vector4 direction = new Vector4(); 061 private final Vector2 source = new Vector2(); 062 private final Matrix4 rangeMatrix = new Matrix4(); 063 064 private final Vector4 intersectBottomLeft = new Vector4(); 065 private final Vector4 intersectTopLeft = new Vector4(); 066 private final Vector4 intersectTopRight = new Vector4(); 067 private final Vector4 intersectBottomRight = new Vector4(); 068 069 private final Vector3 planeIntersection = new Vector3(); 070 071 public boolean freezeProjector = false; 072 private final Timer timer; 073 private final Camera camera; 074 075 private final HeightGenerator heightGenerator; 076 private final float textureScale; 077 078 private double projectorMinHeight = 100.0; 079 private final Vector3[] intersections = new Vector3[24]; 080 081 private final float[] vertBufArray; 082 private final float[] normBufArray; 083 private final float[] texBufArray; 084 085 private int nrUpdateThreads = 1; 086 private final ExecutorService executorService = Executors.newCachedThreadPool(new DeamonThreadFactory()); 087 private final Stack<Future<?>> futureStack = new Stack<>(); 088 089 private final int connections[] = { 0, 1, 2, 3, 0, 4, 1, 5, 2, 6, 3, 7, 4, 5, 6, 7, }; 090 091 // Debug drawing 092 private boolean drawDebug = false; 093 094 public ProjectedGrid(final String name, final Camera camera, final int sizeX, final int sizeY, 095 final float textureScale, final HeightGenerator heightGenerator, final Timer timer) { 096 super(name); 097 this.sizeX = sizeX; 098 this.sizeY = sizeY; 099 this.textureScale = textureScale; 100 this.heightGenerator = heightGenerator; 101 this.camera = camera; 102 this.timer = timer; 103 104 buildVertices(sizeX * sizeY); 105 texs = BufferUtils.createVector2Buffer(_meshData.getVertexCount()); 106 _meshData.setTextureBuffer(texs, 0); 107 normBuf = BufferUtils.createVector3Buffer(_meshData.getVertexCount()); 108 _meshData.setNormalBuffer(normBuf); 109 110 vertBufArray = new float[_meshData.getVertexCount() * 3]; 111 normBufArray = new float[_meshData.getVertexCount() * 3]; 112 texBufArray = new float[_meshData.getVertexCount() * 2]; 113 114 for (int i = 0; i < 24; i++) { 115 intersections[i] = new Vector3(); 116 } 117 } 118 119 public void setNrUpdateThreads(final int nrUpdateThreads) { 120 this.nrUpdateThreads = nrUpdateThreads; 121 if (this.nrUpdateThreads < 1) { 122 this.nrUpdateThreads = 1; 123 } 124 } 125 126 public int getNrUpdateThreads() { 127 return nrUpdateThreads; 128 } 129 130 public void setFreezeUpdate(final boolean freeze) { 131 freezeProjector = freeze; 132 } 133 134 public boolean isFreezeUpdate() { 135 return freezeProjector; 136 } 137 138 @Override 139 public void render(final Renderer renderer) { 140 final boolean doDraw = update(); 141 if (doDraw) { 142 super.render(renderer); 143 } 144 145 if (drawDebug) { 146 Debugger.drawCameraFrustum(renderer, mainCamera, new ColorRGBA(1, 0, 0, 1), (short) 0xFFFF, true); 147 Debugger.drawCameraFrustum(renderer, projectorCamera, new ColorRGBA(0, 1, 1, 1), (short) 0xFFFF, true); 148 } 149 } 150 151 public boolean update() { 152 final double upperBound = heightGenerator.getMaximumHeight(); 153 154 if (!freezeProjector) { 155 mainCamera.set(camera); 156 157 final Vector3 tmp = new Vector3(); 158 getWorldTransform().applyInverse(mainCamera.getLocation(), tmp); 159 mainCamera.setLocation(tmp); 160 getWorldTransform().applyInverseVector(mainCamera.getLeft(), tmp); 161 mainCamera.setLeft(tmp); 162 getWorldTransform().applyInverseVector(mainCamera.getUp(), tmp); 163 mainCamera.setUp(tmp); 164 getWorldTransform().applyInverseVector(mainCamera.getDirection(), tmp); 165 mainCamera.setDirection(tmp); 166 } 167 168 final ReadOnlyVector3 mainCameraLocation = mainCamera.getLocation(); 169 if (mainCameraLocation.getY() > 0.0 && mainCameraLocation.getY() < upperBound + mainCamera.getFrustumNear()) { 170 mainCamera.setLocation(mainCameraLocation.getX(), upperBound + mainCamera.getFrustumNear(), 171 mainCameraLocation.getZ()); 172 } else if (mainCameraLocation.getY() < 0.0 173 && mainCameraLocation.getY() > -upperBound - mainCamera.getFrustumNear()) { 174 mainCamera.setLocation(mainCameraLocation.getX(), -upperBound - mainCamera.getFrustumNear(), 175 mainCameraLocation.getZ()); 176 } 177 mainCamera.calculateFrustum(); 178 final Vector3[] corners = mainCamera.getCorners(); 179 180 int nrPoints = 0; 181 182 // check intersections of frustum connections with upper and lower bound 183 final Vector3 tmpStorage = Vector3.fetchTempInstance(); 184 for (int i = 0; i < 8; i++) { 185 final int source = connections[i * 2]; 186 final int destination = connections[i * 2 + 1]; 187 188 if (corners[source].getY() > upperBound && corners[destination].getY() < upperBound 189 || corners[source].getY() < upperBound && corners[destination].getY() > upperBound) { 190 getWorldIntersection(upperBound, corners[source], corners[destination], intersections[nrPoints++], 191 tmpStorage); 192 } 193 if (corners[source].getY() > -upperBound && corners[destination].getY() < -upperBound 194 || corners[source].getY() < -upperBound && corners[destination].getY() > -upperBound) { 195 getWorldIntersection(-upperBound, corners[source], corners[destination], intersections[nrPoints++], 196 tmpStorage); 197 } 198 } 199 // check if any of the frustums corner vertices lie between the upper and lower bound planes 200 for (int i = 0; i < 8; i++) { 201 if (corners[i].getY() < upperBound && corners[i].getY() > -upperBound) { 202 intersections[nrPoints++].set(corners[i]); 203 } 204 } 205 206 if (nrPoints == 0) { 207 // No intersection, grid not visible 208 return false; 209 } 210 211 // set projector 212 projectorCamera.set(mainCamera); 213 214 // force the projector to point at the plane 215 if (projectorCamera.getLocation().getY() > 0.0 && projectorCamera.getDirection().getY() > 0.0 216 || projectorCamera.getLocation().getY() < 0.0 && projectorCamera.getDirection().getY() < 0.0) { 217 projectorCamera.setDirection(new Vector3(projectorCamera.getDirection().getX(), 218 -projectorCamera.getDirection().getY(), projectorCamera.getDirection().getZ())); 219 projectorCamera 220 .setUp(projectorCamera.getDirection().cross(projectorCamera.getLeft(), null).normalizeLocal()); 221 } 222 223 // find the plane intersection point 224 source.set(0.5, 0.5); 225 getWorldIntersection(0.0, source, projectorCamera.getModelViewProjectionInverseMatrix(), planeIntersection); 226 227 // force the projector to be a certain distance above the plane 228 final ReadOnlyVector3 cameraLocation = projectorCamera.getLocation(); 229 if (cameraLocation.getY() > 0.0 && cameraLocation.getY() < projectorMinHeight * 2) { 230 final double delta = (projectorMinHeight * 2 - cameraLocation.getY()) / (projectorMinHeight * 2); 231 232 projectorCamera.setLocation(cameraLocation.getX(), projectorMinHeight * 2 - projectorMinHeight * delta, 233 cameraLocation.getZ()); 234 } else if (cameraLocation.getY() < 0.0 && cameraLocation.getY() > -projectorMinHeight * 2) { 235 final double delta = (-projectorMinHeight * 2 - cameraLocation.getY()) / (-projectorMinHeight * 2); 236 237 projectorCamera.setLocation(cameraLocation.getX(), -projectorMinHeight * 2 + projectorMinHeight * delta, 238 cameraLocation.getZ()); 239 } 240 241 // restrict the intersection point to be a certain distance from the camera in plane coords 242 planeIntersection.subtractLocal(projectorCamera.getLocation()); 243 planeIntersection.setY(0.0); 244 final double length = planeIntersection.length(); 245 if (length > Math.abs(projectorCamera.getLocation().getY())) { 246 planeIntersection.normalizeLocal(); 247 planeIntersection.multiplyLocal(Math.abs(projectorCamera.getLocation().getY())); 248 } else if (length < MathUtils.EPSILON) { 249 planeIntersection.addLocal(projectorCamera.getUp()); 250 planeIntersection.setY(0.0); 251 planeIntersection.normalizeLocal(); 252 planeIntersection.multiplyLocal(0.1); // TODO: magic number 253 } 254 planeIntersection.addLocal(projectorCamera.getLocation()); 255 planeIntersection.setY(0.0); 256 257 // point projector at the new intersection point 258 projectorCamera.lookAt(planeIntersection, Vector3.UNIT_Y); 259 260 // transform points to projector space 261 final ReadOnlyMatrix4 modelViewProjectionMatrix = projectorCamera.getModelViewProjectionMatrix(); 262 final Vector4 spaceTransformation = new Vector4(); 263 for (int i = 0; i < nrPoints; i++) { 264 spaceTransformation.set(intersections[i].getX(), 0.0, intersections[i].getZ(), 1.0); 265 modelViewProjectionMatrix.applyPre(spaceTransformation, spaceTransformation); 266 intersections[i].set(spaceTransformation.getX(), spaceTransformation.getY(), 0); 267 intersections[i].divideLocal(spaceTransformation.getW()); 268 } 269 270 // find min/max in projector space 271 double minX = Double.MAX_VALUE; 272 double maxX = -Double.MAX_VALUE; 273 double minY = Double.MAX_VALUE; 274 double maxY = -Double.MAX_VALUE; 275 for (int i = 0; i < nrPoints; i++) { 276 if (intersections[i].getX() < minX) { 277 minX = intersections[i].getX(); 278 } 279 if (intersections[i].getX() > maxX) { 280 maxX = intersections[i].getX(); 281 } 282 if (intersections[i].getY() < minY) { 283 minY = intersections[i].getY(); 284 } 285 if (intersections[i].getY() > maxY) { 286 maxY = intersections[i].getY(); 287 } 288 } 289 290 // create range matrix 291 rangeMatrix.setIdentity(); 292 rangeMatrix.setM00(maxX - minX); 293 rangeMatrix.setM11(maxY - minY); 294 rangeMatrix.setM30(minX); 295 rangeMatrix.setM31(minY); 296 297 final ReadOnlyMatrix4 modelViewProjectionInverseMatrix = projectorCamera.getModelViewProjectionInverseMatrix(); 298 rangeMatrix.multiplyLocal(modelViewProjectionInverseMatrix); 299 300 // convert screen coords to homogenous world coords with new range matrix 301 source.set(0.5, 0.5); 302 getWorldIntersectionHomogenous(0.0, source, rangeMatrix, intersectBottomLeft); 303 source.set(0.5, 1); 304 getWorldIntersectionHomogenous(0.0, source, rangeMatrix, intersectTopLeft); 305 source.set(1, 1); 306 getWorldIntersectionHomogenous(0.0, source, rangeMatrix, intersectTopRight); 307 source.set(1, 0.5); 308 getWorldIntersectionHomogenous(0.0, source, rangeMatrix, intersectBottomRight); 309 310 // update data 311 if (nrUpdateThreads <= 1) { 312 updateGrid(0, sizeY); 313 } else { 314 for (int i = 0; i < nrUpdateThreads; i++) { 315 final int from = sizeY * i / (nrUpdateThreads); 316 final int to = sizeY * (i + 1) / (nrUpdateThreads); 317 final Future<?> future = executorService.submit(new Runnable() { 318 @Override 319 public void run() { 320 updateGrid(from, to); 321 } 322 }); 323 futureStack.push(future); 324 } 325 try { 326 while (!futureStack.isEmpty()) { 327 futureStack.pop().get(); 328 } 329 } catch (final InterruptedException ex) { 330 logger.log(Level.SEVERE, "InterruptedException in thread execution", ex); 331 } catch (final ExecutionException ex) { 332 logger.log(Level.SEVERE, "ExecutionException in thread execution", ex); 333 } 334 } 335 336 vertBuf.rewind(); 337 vertBuf.put(vertBufArray); 338 339 texs.rewind(); 340 texs.put(texBufArray); 341 342 normBuf.rewind(); 343 normBuf.put(normBufArray); 344 345 return true; 346 } 347 348 private boolean getWorldIntersection(final double planeHeight, final Vector3 source, final Vector3 destination, 349 final Vector3 store, final Vector3 tmpStorage) { 350 final Vector3 origin = store.set(source); 351 final Vector3 direction = tmpStorage.set(destination).subtractLocal(origin); 352 353 final double t = (planeHeight - origin.getY()) / (direction.getY()); 354 355 direction.multiplyLocal(t); 356 origin.addLocal(direction); 357 358 return t >= 0.0 && t <= 1.0; 359 } 360 361 private void updateGrid(final int from, final int to) { 362 final double time = timer.getTimeInSeconds(); 363 final double du = 1.0f / (double) (sizeX - 1); 364 final double dv = 1.0f / (double) (sizeY - 1); 365 366 final Vector4 pointTop = Vector4.fetchTempInstance(); 367 final Vector4 pointFinal = Vector4.fetchTempInstance(); 368 final Vector4 pointBottom = Vector4.fetchTempInstance(); 369 370 int smallerFrom = from; 371 if (smallerFrom > 0) { 372 smallerFrom--; 373 } 374 int biggerTo = to; 375 if (biggerTo < sizeY) { 376 biggerTo++; 377 } 378 double u = 0, v = smallerFrom * dv; 379 int index = smallerFrom * sizeX * 3; 380 for (int y = smallerFrom; y < biggerTo; y++) { 381 for (int x = 0; x < sizeX; x++) { 382 pointTop.lerpLocal(intersectTopLeft, intersectTopRight, u); 383 pointBottom.lerpLocal(intersectBottomLeft, intersectBottomRight, u); 384 pointFinal.lerpLocal(pointTop, pointBottom, v); 385 386 pointFinal.setX(pointFinal.getX() / pointFinal.getW()); 387 pointFinal.setZ(pointFinal.getZ() / pointFinal.getW()); 388 pointFinal.setY(heightGenerator.getHeight(pointFinal.getX(), pointFinal.getZ(), time)); 389 390 vertBufArray[index++] = pointFinal.getXf(); 391 vertBufArray[index++] = pointFinal.getYf(); 392 vertBufArray[index++] = pointFinal.getZf(); 393 394 u += du; 395 } 396 v += dv; 397 u = 0; 398 } 399 400 Vector4.releaseTempInstance(pointTop); 401 Vector4.releaseTempInstance(pointFinal); 402 Vector4.releaseTempInstance(pointBottom); 403 404 final Vector3 oppositePoint = Vector3.fetchTempInstance(); 405 final Vector3 adjacentPoint = Vector3.fetchTempInstance(); 406 final Vector3 rootPoint = Vector3.fetchTempInstance(); 407 408 int adj = 0, opp = 0; 409 int normalIndex = from * sizeX; 410 for (int row = from; row < to; row++) { 411 for (int col = 0; col < sizeX; col++) { 412 if (row == sizeY - 1) { 413 if (col == sizeX - 1) { // last row, last col 414 // up cross left 415 adj = normalIndex - sizeX; 416 opp = normalIndex - 1; 417 } else { // last row, except for last col 418 // right cross up 419 adj = normalIndex + 1; 420 opp = normalIndex - sizeX; 421 } 422 } else { 423 if (col == sizeX - 1) { // last column except for last row 424 // left cross down 425 adj = normalIndex - 1; 426 opp = normalIndex + sizeX; 427 } else { // most cases 428 // down cross right 429 adj = normalIndex + sizeX; 430 opp = normalIndex + 1; 431 } 432 } 433 434 final float x = vertBufArray[normalIndex * 3]; 435 final float y = vertBufArray[normalIndex * 3 + 1]; 436 final float z = vertBufArray[normalIndex * 3 + 2]; 437 438 texBufArray[normalIndex * 2] = x * textureScale; 439 texBufArray[normalIndex * 2 + 1] = z * textureScale; 440 441 rootPoint.set(x, y, z); 442 adjacentPoint.set(vertBufArray[adj * 3], vertBufArray[adj * 3 + 1], vertBufArray[adj * 3 + 2]); 443 adjacentPoint.subtractLocal(rootPoint); 444 oppositePoint.set(vertBufArray[opp * 3], vertBufArray[opp * 3 + 1], vertBufArray[opp * 3 + 2]); 445 oppositePoint.subtractLocal(rootPoint); 446 447 adjacentPoint.crossLocal(oppositePoint).normalizeLocal(); 448 449 normBufArray[normalIndex * 3] = adjacentPoint.getXf(); 450 normBufArray[normalIndex * 3 + 1] = adjacentPoint.getYf(); 451 normBufArray[normalIndex * 3 + 2] = adjacentPoint.getZf(); 452 453 normalIndex++; 454 } 455 } 456 457 Vector3.releaseTempInstance(oppositePoint); 458 Vector3.releaseTempInstance(adjacentPoint); 459 Vector3.releaseTempInstance(rootPoint); 460 } 461 462 private void getWorldIntersectionHomogenous(final double planeHeight, final ReadOnlyVector2 screenPosition, 463 final ReadOnlyMatrix4 modelViewProjectionInverseMatrix, final Vector4 store) { 464 calculateIntersection(planeHeight, screenPosition, modelViewProjectionInverseMatrix); 465 store.set(origin); 466 } 467 468 private void getWorldIntersection(final double planeHeight, final ReadOnlyVector2 screenPosition, 469 final ReadOnlyMatrix4 modelViewProjectionInverseMatrix, final Vector3 store) { 470 calculateIntersection(planeHeight, screenPosition, modelViewProjectionInverseMatrix); 471 store.set(origin.getX(), origin.getY(), origin.getZ()).divideLocal(origin.getW()); 472 } 473 474 private void calculateIntersection(final double planeHeight, final ReadOnlyVector2 screenPosition, 475 final ReadOnlyMatrix4 modelViewProjectionInverseMatrix) { 476 origin.set(screenPosition.getX() * 2 - 1, screenPosition.getY() * 2 - 1, -1, 1); 477 direction.set(screenPosition.getX() * 2 - 1, screenPosition.getY() * 2 - 1, 1, 1); 478 479 modelViewProjectionInverseMatrix.applyPre(origin, origin); 480 modelViewProjectionInverseMatrix.applyPre(direction, direction); 481 482 direction.subtractLocal(origin); 483 484 // final double t = (planeHeight * origin.getW() - origin.getY()) 485 // / (direction.getY() - planeHeight * direction.getW()); 486 487 if (Math.abs(direction.getY()) > MathUtils.EPSILON) { 488 final double t = (planeHeight - origin.getY()) / direction.getY(); 489 direction.multiplyLocal(t); 490 } else { 491 direction.normalizeLocal(); 492 direction.multiplyLocal(mainCamera.getFrustumFar()); 493 } 494 495 origin.addLocal(direction); 496 } 497 498 /** 499 * <code>getSurfaceNormal</code> returns the normal of an arbitrary point on the terrain. The normal is linearly 500 * interpreted from the normals of the 4 nearest defined points. If the point provided is not within the bounds of 501 * the height map, null is returned. 502 * 503 * @param position 504 * the vector representing the location to find a normal at. 505 * @param store 506 * the Vector3 object to store the result in. If null, a new one is created. 507 * @return the normal vector at the provided location. 508 */ 509 public Vector3 getSurfaceNormal(final Vector2 position, final Vector3 store) { 510 return getSurfaceNormal(position.getX(), position.getY(), store); 511 } 512 513 /** 514 * <code>getSurfaceNormal</code> returns the normal of an arbitrary point on the terrain. The normal is linearly 515 * interpreted from the normals of the 4 nearest defined points. If the point provided is not within the bounds of 516 * the height map, null is returned. 517 * 518 * @param position 519 * the vector representing the location to find a normal at. Only the x and z values are used. 520 * @param store 521 * the Vector3 object to store the result in. If null, a new one is created. 522 * @return the normal vector at the provided location. 523 */ 524 public Vector3 getSurfaceNormal(final Vector3 position, final Vector3 store) { 525 return getSurfaceNormal(position.getX(), position.getZ(), store); 526 } 527 528 /** 529 * <code>getSurfaceNormal</code> returns the normal of an arbitrary point on the terrain. The normal is linearly 530 * interpreted from the normals of the 4 nearest defined points. If the point provided is not within the bounds of 531 * the height map, null is returned. 532 * 533 * @param x 534 * the x coordinate to check. 535 * @param z 536 * the z coordinate to check. 537 * @param store 538 * the Vector3 object to store the result in. If null, a new one is created. 539 * @return the normal unit vector at the provided location. 540 */ 541 public Vector3 getSurfaceNormal(final double x, final double z, Vector3 store) { 542 final double col = MathUtils.floor(x); 543 final double row = MathUtils.floor(z); 544 545 if (col < 0 || row < 0 || col >= sizeX - 1 || row >= sizeY - 1) { 546 return null; 547 } 548 final double intOnX = x - col, intOnZ = z - row; 549 550 if (store == null) { 551 store = new Vector3(); 552 } 553 554 final Vector3 topLeft = store, topRight = new Vector3(), bottomLeft = new Vector3(), 555 bottomRight = new Vector3(); 556 557 final int focalSpot = (int) (col + row * sizeX); 558 559 // find the heightmap point closest to this position (but will always 560 // be to the left ( < x) and above (< z) of the spot. 561 BufferUtils.populateFromBuffer(topLeft, normBuf, focalSpot); 562 563 // now find the next point to the right of topLeft's position... 564 BufferUtils.populateFromBuffer(topRight, normBuf, focalSpot + 1); 565 566 // now find the next point below topLeft's position... 567 BufferUtils.populateFromBuffer(bottomLeft, normBuf, focalSpot + sizeX); 568 569 // now find the next point below and to the right of topLeft's 570 // position... 571 BufferUtils.populateFromBuffer(bottomRight, normBuf, focalSpot + sizeX + 1); 572 573 // Use linear interpolation to find the height. 574 topLeft.lerpLocal(topRight, intOnX); 575 bottomLeft.lerpLocal(bottomRight, intOnX); 576 topLeft.lerpLocal(bottomLeft, intOnZ); 577 return topLeft.normalizeLocal(); 578 } 579 580 /** 581 * <code>buildVertices</code> sets up the vertex and index arrays of the TriMesh. 582 * 583 * @param vertexCount 584 * the vertex count 585 */ 586 private void buildVertices(final int vertexCount) { 587 vertBuf = BufferUtils.createVector3Buffer(vertBuf, vertexCount); 588 _meshData.setVertexBuffer(vertBuf); 589 590 final Vector3 point = new Vector3(); 591 for (int x = 0; x < sizeX; x++) { 592 for (int y = 0; y < sizeY; y++) { 593 point.set(x, 0, y); 594 BufferUtils.setInBuffer(point, vertBuf, (x + (y * sizeX))); 595 } 596 } 597 598 // set up the indices 599 final int triangleQuantity = ((sizeX - 1) * (sizeY - 1)) * 2; 600 final IndexBufferData<?> indices = BufferUtils.createIndexBufferData(triangleQuantity * 3, vertexCount - 1); 601 _meshData.setIndices(indices); 602 603 // go through entire array up to the second to last column. 604 for (int i = 0; i < (sizeX * (sizeY - 1)); i++) { 605 // we want to skip the top row. 606 if (i % ((sizeX * (i / sizeX + 1)) - 1) == 0 && i != 0) { 607 // logger.info("skip row: "+i+" cause: "+((sizeY * (i / sizeX + 1)) - 1)); 608 continue; 609 } else { 610 // logger.info("i: "+i); 611 } 612 // set the top left corner. 613 indices.put(i); 614 // set the bottom right corner. 615 indices.put((1 + sizeX) + i); 616 // set the top right corner. 617 indices.put(1 + i); 618 // set the top left corner 619 indices.put(i); 620 // set the bottom left corner 621 indices.put(sizeX + i); 622 // set the bottom right corner 623 indices.put((1 + sizeX) + i); 624 } 625 } 626 627 static class DeamonThreadFactory implements ThreadFactory { 628 static final AtomicInteger poolNumber = new AtomicInteger(1); 629 final ThreadGroup group; 630 final AtomicInteger threadNumber = new AtomicInteger(1); 631 final String namePrefix; 632 633 DeamonThreadFactory() { 634 group = Thread.currentThread().getThreadGroup(); 635 namePrefix = "ProjectedGrid Pool-" + poolNumber.getAndIncrement() + "-thread-"; 636 } 637 638 @Override 639 public Thread newThread(final Runnable r) { 640 final Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); 641 if (!t.isDaemon()) { 642 t.setDaemon(true); 643 } 644 if (t.getPriority() != Thread.NORM_PRIORITY) { 645 t.setPriority(Thread.NORM_PRIORITY); 646 } 647 return t; 648 } 649 } 650 651 public double getProjectorMinHeight() { 652 return projectorMinHeight; 653 } 654 655 public void setProjectorMinHeight(final double projectorMinHeight) { 656 this.projectorMinHeight = projectorMinHeight; 657 } 658 659 public boolean isDrawDebug() { 660 return drawDebug; 661 } 662 663 public void setDrawDebug(final boolean drawDebug) { 664 this.drawDebug = drawDebug; 665 } 666}