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.util; 33 34 import com.jme3.math.ColorRGBA; 35 import com.jme3.math.FastMath; 36 import com.jme3.math.Vector2f; 37 import com.jme3.math.Vector3f; 38 import com.jme3.scene.*; 39 import com.jme3.scene.VertexBuffer.Format; 40 import com.jme3.scene.VertexBuffer.Type; 41 import com.jme3.scene.VertexBuffer.Usage; 42 import com.jme3.scene.mesh.IndexBuffer; 43 import static com.jme3.util.BufferUtils.*; 44 import java.nio.FloatBuffer; 45 import java.nio.IntBuffer; 46 import java.util.ArrayList; 47 import java.util.logging.Level; 48 import java.util.logging.Logger; 49 50 /** 51 * 52 * @author Lex (Aleksey Nikiforov) 53 */ 54 public class TangentBinormalGenerator { 55 56 private static final float ZERO_TOLERANCE = 0.0000001f; 57 private static final Logger log = Logger.getLogger( 58 TangentBinormalGenerator.class.getName()); 59 private static float toleranceAngle; 60 private static float toleranceDot; 61 62 static { 63 setToleranceAngle(45); 64 } 65 66 67 private static class VertexInfo { 68 public final Vector3f position; 69 public final Vector3f normal; 70 public final ArrayList<Integer> indices = new ArrayList<Integer>(); 71 72 public VertexInfo(Vector3f position, Vector3f normal) { 73 this.position = position; 74 this.normal = normal; 75 } 76 } 77 78 /** Collects all the triangle data for one vertex. 79 */ 80 private static class VertexData { 81 public final ArrayList<TriangleData> triangles = new ArrayList<TriangleData>(); 82 83 public VertexData() { } 84 } 85 86 /** Keeps track of tangent, binormal, and normal for one triangle. 87 */ 88 public static class TriangleData { 89 public final Vector3f tangent; 90 public final Vector3f binormal; 91 public final Vector3f normal; 92 93 public TriangleData(Vector3f tangent, Vector3f binormal, Vector3f normal) { 94 this.tangent = tangent; 95 this.binormal = binormal; 96 this.normal = normal; 97 } 98 } 99 100 private static VertexData[] initVertexData(int size) { 101 VertexData[] vertices = new VertexData[size]; 102 for (int i = 0; i < size; i++) { 103 vertices[i] = new VertexData(); 104 } 105 return vertices; 106 } 107 108 public static void generate(Mesh mesh) { 109 generate(mesh, true); 110 } 111 112 public static void generate(Spatial scene) { 113 if (scene instanceof Node) { 114 Node node = (Node) scene; 115 for (Spatial child : node.getChildren()) { 116 generate(child); 117 } 118 } else { 119 Geometry geom = (Geometry) scene; 120 Mesh mesh = geom.getMesh(); 121 122 // Check to ensure mesh has texcoords and normals before generating 123 if (mesh.getBuffer(Type.TexCoord) != null 124 && mesh.getBuffer(Type.Normal) != null){ 125 generate(geom.getMesh()); 126 } 127 } 128 } 129 130 public static void generate(Mesh mesh, boolean approxTangents) { 131 int[] index = new int[3]; 132 Vector3f[] v = new Vector3f[3]; 133 Vector2f[] t = new Vector2f[3]; 134 for (int i = 0; i < 3; i++) { 135 v[i] = new Vector3f(); 136 t[i] = new Vector2f(); 137 } 138 139 if (mesh.getBuffer(Type.Normal) == null) { 140 throw new IllegalArgumentException("The given mesh has no normal data!"); 141 } 142 143 VertexData[] vertices; 144 switch (mesh.getMode()) { 145 case Triangles: 146 vertices = processTriangles(mesh, index, v, t); 147 break; 148 case TriangleStrip: 149 vertices = processTriangleStrip(mesh, index, v, t); 150 break; 151 case TriangleFan: 152 vertices = processTriangleFan(mesh, index, v, t); 153 break; 154 default: 155 throw new UnsupportedOperationException( 156 mesh.getMode() + " is not supported."); 157 } 158 159 processTriangleData(mesh, vertices, approxTangents); 160 161 //if the mesh has a bind pose, we need to generate the bind pose for the tangent buffer 162 if (mesh.getBuffer(Type.BindPosePosition) != null) { 163 164 VertexBuffer tangents = mesh.getBuffer(Type.Tangent); 165 if (tangents != null) { 166 VertexBuffer bindTangents = new VertexBuffer(Type.BindPoseTangent); 167 bindTangents.setupData(Usage.CpuOnly, 168 4, 169 Format.Float, 170 BufferUtils.clone(tangents.getData())); 171 172 if (mesh.getBuffer(Type.BindPoseTangent) != null) { 173 mesh.clearBuffer(Type.BindPoseTangent); 174 } 175 mesh.setBuffer(bindTangents); 176 tangents.setUsage(Usage.Stream); 177 } 178 } 179 } 180 181 private static VertexData[] processTriangles(Mesh mesh, 182 int[] index, Vector3f[] v, Vector2f[] t) { 183 IndexBuffer indexBuffer = mesh.getIndexBuffer(); 184 FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData(); 185 if (mesh.getBuffer(Type.TexCoord) == null) { 186 throw new IllegalArgumentException("Can only generate tangents for " 187 + "meshes with texture coordinates"); 188 } 189 190 FloatBuffer textureBuffer = (FloatBuffer) mesh.getBuffer(Type.TexCoord).getData(); 191 192 VertexData[] vertices = initVertexData(vertexBuffer.capacity() / 3); 193 194 for (int i = 0; i < indexBuffer.size() / 3; i++) { 195 for (int j = 0; j < 3; j++) { 196 index[j] = indexBuffer.get(i * 3 + j); 197 populateFromBuffer(v[j], vertexBuffer, index[j]); 198 populateFromBuffer(t[j], textureBuffer, index[j]); 199 } 200 201 TriangleData triData = processTriangle(index, v, t); 202 if (triData != null) { 203 vertices[index[0]].triangles.add(triData); 204 vertices[index[1]].triangles.add(triData); 205 vertices[index[2]].triangles.add(triData); 206 } 207 } 208 209 return vertices; 210 } 211 212 private static VertexData[] processTriangleStrip(Mesh mesh, 213 int[] index, Vector3f[] v, Vector2f[] t) { 214 IndexBuffer indexBuffer = mesh.getIndexBuffer(); 215 FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData(); 216 FloatBuffer textureBuffer = (FloatBuffer) mesh.getBuffer(Type.TexCoord).getData(); 217 218 VertexData[] vertices = initVertexData(vertexBuffer.capacity() / 3); 219 220 index[0] = indexBuffer.get(0); 221 index[1] = indexBuffer.get(1); 222 223 populateFromBuffer(v[0], vertexBuffer, index[0]); 224 populateFromBuffer(v[1], vertexBuffer, index[1]); 225 226 populateFromBuffer(t[0], textureBuffer, index[0]); 227 populateFromBuffer(t[1], textureBuffer, index[1]); 228 229 for (int i = 2; i < indexBuffer.size(); i++) { 230 index[2] = indexBuffer.get(i); 231 BufferUtils.populateFromBuffer(v[2], vertexBuffer, index[2]); 232 BufferUtils.populateFromBuffer(t[2], textureBuffer, index[2]); 233 234 boolean isDegenerate = isDegenerateTriangle(v[0], v[1], v[2]); 235 TriangleData triData = processTriangle(index, v, t); 236 237 if (triData != null && !isDegenerate) { 238 vertices[index[0]].triangles.add(triData); 239 vertices[index[1]].triangles.add(triData); 240 vertices[index[2]].triangles.add(triData); 241 } 242 243 Vector3f vTemp = v[0]; 244 v[0] = v[1]; 245 v[1] = v[2]; 246 v[2] = vTemp; 247 248 Vector2f tTemp = t[0]; 249 t[0] = t[1]; 250 t[1] = t[2]; 251 t[2] = tTemp; 252 253 index[0] = index[1]; 254 index[1] = index[2]; 255 } 256 257 return vertices; 258 } 259 260 private static VertexData[] processTriangleFan(Mesh mesh, 261 int[] index, Vector3f[] v, Vector2f[] t) { 262 IndexBuffer indexBuffer = mesh.getIndexBuffer(); 263 FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData(); 264 FloatBuffer textureBuffer = (FloatBuffer) mesh.getBuffer(Type.TexCoord).getData(); 265 266 VertexData[] vertices = initVertexData(vertexBuffer.capacity() / 3); 267 268 index[0] = indexBuffer.get(0); 269 index[1] = indexBuffer.get(1); 270 271 populateFromBuffer(v[0], vertexBuffer, index[0]); 272 populateFromBuffer(v[1], vertexBuffer, index[1]); 273 274 populateFromBuffer(t[0], textureBuffer, index[0]); 275 populateFromBuffer(t[1], textureBuffer, index[1]); 276 277 for (int i = 2; i < vertexBuffer.capacity() / 3; i++) { 278 index[2] = indexBuffer.get(i); 279 populateFromBuffer(v[2], vertexBuffer, index[2]); 280 populateFromBuffer(t[2], textureBuffer, index[2]); 281 282 TriangleData triData = processTriangle(index, v, t); 283 if (triData != null) { 284 vertices[index[0]].triangles.add(triData); 285 vertices[index[1]].triangles.add(triData); 286 vertices[index[2]].triangles.add(triData); 287 } 288 289 Vector3f vTemp = v[1]; 290 v[1] = v[2]; 291 v[2] = vTemp; 292 293 Vector2f tTemp = t[1]; 294 t[1] = t[2]; 295 t[2] = tTemp; 296 297 index[1] = index[2]; 298 } 299 300 return vertices; 301 } 302 303 // check if the area is greater than zero 304 private static boolean isDegenerateTriangle(Vector3f a, Vector3f b, Vector3f c) { 305 return (a.subtract(b).cross(c.subtract(b))).lengthSquared() == 0; 306 } 307 308 public static TriangleData processTriangle(int[] index, 309 Vector3f[] v, Vector2f[] t) { 310 Vector3f edge1 = new Vector3f(); 311 Vector3f edge2 = new Vector3f(); 312 Vector2f edge1uv = new Vector2f(); 313 Vector2f edge2uv = new Vector2f(); 314 315 Vector3f tangent = new Vector3f(); 316 Vector3f binormal = new Vector3f(); 317 Vector3f normal = new Vector3f(); 318 319 t[1].subtract(t[0], edge1uv); 320 t[2].subtract(t[0], edge2uv); 321 float det = edge1uv.x * edge2uv.y - edge1uv.y * edge2uv.x; 322 323 boolean normalize = false; 324 if (Math.abs(det) < ZERO_TOLERANCE) { 325 log.log(Level.WARNING, "Colinear uv coordinates for triangle " 326 + "[{0}, {1}, {2}]; tex0 = [{3}, {4}], " 327 + "tex1 = [{5}, {6}], tex2 = [{7}, {8}]", 328 new Object[]{index[0], index[1], index[2], 329 t[0].x, t[0].y, t[1].x, t[1].y, t[2].x, t[2].y}); 330 det = 1; 331 normalize = true; 332 } 333 334 v[1].subtract(v[0], edge1); 335 v[2].subtract(v[0], edge2); 336 337 tangent.set(edge1); 338 tangent.normalizeLocal(); 339 binormal.set(edge2); 340 binormal.normalizeLocal(); 341 342 if (Math.abs(Math.abs(tangent.dot(binormal)) - 1) 343 < ZERO_TOLERANCE) { 344 log.log(Level.WARNING, "Vertices are on the same line " 345 + "for triangle [{0}, {1}, {2}].", 346 new Object[]{index[0], index[1], index[2]}); 347 } 348 349 float factor = 1 / det; 350 tangent.x = (edge2uv.y * edge1.x - edge1uv.y * edge2.x) * factor; 351 tangent.y = (edge2uv.y * edge1.y - edge1uv.y * edge2.y) * factor; 352 tangent.z = (edge2uv.y * edge1.z - edge1uv.y * edge2.z) * factor; 353 if (normalize) { 354 tangent.normalizeLocal(); 355 } 356 357 binormal.x = (edge1uv.x * edge2.x - edge2uv.x * edge1.x) * factor; 358 binormal.y = (edge1uv.x * edge2.y - edge2uv.x * edge1.y) * factor; 359 binormal.z = (edge1uv.x * edge2.z - edge2uv.x * edge1.z) * factor; 360 if (normalize) { 361 binormal.normalizeLocal(); 362 } 363 364 tangent.cross(binormal, normal); 365 normal.normalizeLocal(); 366 367 return new TriangleData( 368 tangent, 369 binormal, 370 normal); 371 } 372 373 public static void setToleranceAngle(float angle) { 374 if (angle < 0 || angle > 179) { 375 throw new IllegalArgumentException( 376 "The angle must be between 0 and 179 degrees."); 377 } 378 toleranceDot = FastMath.cos(angle * FastMath.DEG_TO_RAD); 379 toleranceAngle = angle; 380 } 381 382 383 private static boolean approxEqual(Vector3f u, Vector3f v) { 384 float tolerance = 1E-4f; 385 return (FastMath.abs(u.x - v.x) < tolerance) && 386 (FastMath.abs(u.y - v.y) < tolerance) && 387 (FastMath.abs(u.z - v.z) < tolerance); 388 } 389 390 private static ArrayList<VertexInfo> linkVertices(Mesh mesh) { 391 ArrayList<VertexInfo> vertexMap = new ArrayList<VertexInfo>(); 392 393 FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData(); 394 FloatBuffer normalBuffer = (FloatBuffer) mesh.getBuffer(Type.Normal).getData(); 395 396 Vector3f position = new Vector3f(); 397 Vector3f normal = new Vector3f(); 398 399 final int size = vertexBuffer.capacity() / 3; 400 for (int i = 0; i < size; i++) { 401 402 populateFromBuffer(position, vertexBuffer, i); 403 populateFromBuffer(normal, normalBuffer, i); 404 405 boolean found = false; 406 407 for (int j = 0; j < vertexMap.size(); j++) { 408 VertexInfo vertexInfo = vertexMap.get(j); 409 if (approxEqual(vertexInfo.position, position) && 410 approxEqual(vertexInfo.normal, normal)) 411 { 412 vertexInfo.indices.add(i); 413 found = true; 414 break; 415 } 416 } 417 418 if (!found) { 419 VertexInfo vertexInfo = new VertexInfo(position.clone(), normal.clone()); 420 vertexInfo.indices.add(i); 421 vertexMap.add(vertexInfo); 422 } 423 } 424 425 return vertexMap; 426 } 427 428 private static void processTriangleData(Mesh mesh, VertexData[] vertices, 429 boolean approxTangent) 430 { 431 ArrayList<VertexInfo> vertexMap = linkVertices(mesh); 432 433 FloatBuffer normalBuffer = (FloatBuffer) mesh.getBuffer(Type.Normal).getData(); 434 435 FloatBuffer tangents = BufferUtils.createFloatBuffer(vertices.length * 4); 436 // FloatBuffer binormals = BufferUtils.createFloatBuffer(vertices.length * 3); 437 438 Vector3f tangent = new Vector3f(); 439 Vector3f binormal = new Vector3f(); 440 Vector3f normal = new Vector3f(); 441 Vector3f givenNormal = new Vector3f(); 442 443 Vector3f tangentUnit = new Vector3f(); 444 Vector3f binormalUnit = new Vector3f(); 445 446 for (int k = 0; k < vertexMap.size(); k++) { 447 float wCoord = -1; 448 449 VertexInfo vertexInfo = vertexMap.get(k); 450 451 givenNormal.set(vertexInfo.normal); 452 givenNormal.normalizeLocal(); 453 454 TriangleData firstTriangle = vertices[vertexInfo.indices.get(0)].triangles.get(0); 455 456 // check tangent and binormal consistency 457 tangent.set(firstTriangle.tangent); 458 tangent.normalizeLocal(); 459 binormal.set(firstTriangle.binormal); 460 binormal.normalizeLocal(); 461 462 for (int i : vertexInfo.indices) { 463 ArrayList<TriangleData> triangles = vertices[i].triangles; 464 465 for (int j = 0; j < triangles.size(); j++) { 466 TriangleData triangleData = triangles.get(j); 467 468 tangentUnit.set(triangleData.tangent); 469 tangentUnit.normalizeLocal(); 470 if (tangent.dot(tangentUnit) < toleranceDot) { 471 log.log(Level.WARNING, 472 "Angle between tangents exceeds tolerance " 473 + "for vertex {0}.", i); 474 break; 475 } 476 477 if (!approxTangent) { 478 binormalUnit.set(triangleData.binormal); 479 binormalUnit.normalizeLocal(); 480 if (binormal.dot(binormalUnit) < toleranceDot) { 481 log.log(Level.WARNING, 482 "Angle between binormals exceeds tolerance " 483 + "for vertex {0}.", i); 484 break; 485 } 486 } 487 } 488 } 489 490 491 // find average tangent 492 tangent.set(0, 0, 0); 493 binormal.set(0, 0, 0); 494 495 int triangleCount = 0; 496 for (int i : vertexInfo.indices) { 497 ArrayList<TriangleData> triangles = vertices[i].triangles; 498 triangleCount += triangles.size(); 499 500 boolean flippedNormal = false; 501 for (int j = 0; j < triangles.size(); j++) { 502 TriangleData triangleData = triangles.get(j); 503 tangent.addLocal(triangleData.tangent); 504 binormal.addLocal(triangleData.binormal); 505 506 if (givenNormal.dot(triangleData.normal) < 0) { 507 flippedNormal = true; 508 } 509 } 510 if (flippedNormal /*&& approxTangent*/) { 511 // Generated normal is flipped for this vertex, 512 // so binormal = normal.cross(tangent) will be flipped in the shader 513 // log.log(Level.WARNING, 514 // "Binormal is flipped for vertex {0}.", i); 515 516 wCoord = 1; 517 } 518 } 519 520 521 int blameVertex = vertexInfo.indices.get(0); 522 523 if (tangent.length() < ZERO_TOLERANCE) { 524 log.log(Level.WARNING, 525 "Shared tangent is zero for vertex {0}.", blameVertex); 526 // attempt to fix from binormal 527 if (binormal.length() >= ZERO_TOLERANCE) { 528 binormal.cross(givenNormal, tangent); 529 tangent.normalizeLocal(); 530 } // if all fails use the tangent from the first triangle 531 else { 532 tangent.set(firstTriangle.tangent); 533 } 534 } else { 535 tangent.divideLocal(triangleCount); 536 } 537 538 tangentUnit.set(tangent); 539 tangentUnit.normalizeLocal(); 540 if (Math.abs(Math.abs(tangentUnit.dot(givenNormal)) - 1) 541 < ZERO_TOLERANCE) { 542 log.log(Level.WARNING, 543 "Normal and tangent are parallel for vertex {0}.", blameVertex); 544 } 545 546 547 if (!approxTangent) { 548 if (binormal.length() < ZERO_TOLERANCE) { 549 log.log(Level.WARNING, 550 "Shared binormal is zero for vertex {0}.", blameVertex); 551 // attempt to fix from tangent 552 if (tangent.length() >= ZERO_TOLERANCE) { 553 givenNormal.cross(tangent, binormal); 554 binormal.normalizeLocal(); 555 } // if all fails use the binormal from the first triangle 556 else { 557 binormal.set(firstTriangle.binormal); 558 } 559 } else { 560 binormal.divideLocal(triangleCount); 561 } 562 563 binormalUnit.set(binormal); 564 binormalUnit.normalizeLocal(); 565 if (Math.abs(Math.abs(binormalUnit.dot(givenNormal)) - 1) 566 < ZERO_TOLERANCE) { 567 log.log(Level.WARNING, 568 "Normal and binormal are parallel for vertex {0}.", blameVertex); 569 } 570 571 if (Math.abs(Math.abs(binormalUnit.dot(tangentUnit)) - 1) 572 < ZERO_TOLERANCE) { 573 log.log(Level.WARNING, 574 "Tangent and binormal are parallel for vertex {0}.", blameVertex); 575 } 576 } 577 578 for (int i : vertexInfo.indices) { 579 if (approxTangent) { 580 // This calculation ensures that normal and tagent have a 90 degree angle. 581 // Removing this will lead to visual artifacts. 582 givenNormal.cross(tangent, binormal); 583 binormal.cross(givenNormal, tangent); 584 585 tangent.normalizeLocal(); 586 587 tangents.put((i * 4), tangent.x); 588 tangents.put((i * 4) + 1, tangent.y); 589 tangents.put((i * 4) + 2, tangent.z); 590 tangents.put((i * 4) + 3, wCoord); 591 } else { 592 tangents.put((i * 4), tangent.x); 593 tangents.put((i * 4) + 1, tangent.y); 594 tangents.put((i * 4) + 2, tangent.z); 595 tangents.put((i * 4) + 3, wCoord); 596 597 //setInBuffer(binormal, binormals, i); 598 } 599 } 600 } 601 602 mesh.setBuffer(Type.Tangent, 4, tangents); 603 // if (!approxTangent) mesh.setBuffer(Type.Binormal, 3, binormals); 604 } 605 606 public static Mesh genTbnLines(Mesh mesh, float scale) { 607 if (mesh.getBuffer(Type.Tangent) == null) { 608 return genNormalLines(mesh, scale); 609 } else { 610 return genTangentLines(mesh, scale); 611 } 612 } 613 614 public static Mesh genNormalLines(Mesh mesh, float scale) { 615 FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData(); 616 FloatBuffer normalBuffer = (FloatBuffer) mesh.getBuffer(Type.Normal).getData(); 617 618 ColorRGBA originColor = ColorRGBA.White; 619 ColorRGBA normalColor = ColorRGBA.Blue; 620 621 Mesh lineMesh = new Mesh(); 622 lineMesh.setMode(Mesh.Mode.Lines); 623 624 Vector3f origin = new Vector3f(); 625 Vector3f point = new Vector3f(); 626 627 FloatBuffer lineVertex = BufferUtils.createFloatBuffer(vertexBuffer.capacity() * 2); 628 FloatBuffer lineColor = BufferUtils.createFloatBuffer(vertexBuffer.capacity() / 3 * 4 * 2); 629 630 for (int i = 0; i < vertexBuffer.capacity() / 3; i++) { 631 populateFromBuffer(origin, vertexBuffer, i); 632 populateFromBuffer(point, normalBuffer, i); 633 634 int index = i * 2; 635 636 setInBuffer(origin, lineVertex, index); 637 setInBuffer(originColor, lineColor, index); 638 639 point.multLocal(scale); 640 point.addLocal(origin); 641 setInBuffer(point, lineVertex, index + 1); 642 setInBuffer(normalColor, lineColor, index + 1); 643 } 644 645 lineMesh.setBuffer(Type.Position, 3, lineVertex); 646 lineMesh.setBuffer(Type.Color, 4, lineColor); 647 648 lineMesh.setStatic(); 649 //lineMesh.setInterleaved(); 650 return lineMesh; 651 } 652 653 private static Mesh genTangentLines(Mesh mesh, float scale) { 654 FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData(); 655 FloatBuffer normalBuffer = (FloatBuffer) mesh.getBuffer(Type.Normal).getData(); 656 FloatBuffer tangentBuffer = (FloatBuffer) mesh.getBuffer(Type.Tangent).getData(); 657 658 FloatBuffer binormalBuffer = null; 659 if (mesh.getBuffer(Type.Binormal) != null) { 660 binormalBuffer = (FloatBuffer) mesh.getBuffer(Type.Binormal).getData(); 661 } 662 663 ColorRGBA originColor = ColorRGBA.White; 664 ColorRGBA tangentColor = ColorRGBA.Red; 665 ColorRGBA binormalColor = ColorRGBA.Green; 666 ColorRGBA normalColor = ColorRGBA.Blue; 667 668 Mesh lineMesh = new Mesh(); 669 lineMesh.setMode(Mesh.Mode.Lines); 670 671 Vector3f origin = new Vector3f(); 672 Vector3f point = new Vector3f(); 673 Vector3f tangent = new Vector3f(); 674 Vector3f normal = new Vector3f(); 675 676 IntBuffer lineIndex = BufferUtils.createIntBuffer(vertexBuffer.capacity() / 3 * 6); 677 FloatBuffer lineVertex = BufferUtils.createFloatBuffer(vertexBuffer.capacity() * 4); 678 FloatBuffer lineColor = BufferUtils.createFloatBuffer(vertexBuffer.capacity() / 3 * 4 * 4); 679 680 boolean hasParity = mesh.getBuffer(Type.Tangent).getNumComponents() == 4; 681 float tangentW = 1; 682 683 for (int i = 0; i < vertexBuffer.capacity() / 3; i++) { 684 populateFromBuffer(origin, vertexBuffer, i); 685 populateFromBuffer(normal, normalBuffer, i); 686 687 if (hasParity) { 688 tangent.x = tangentBuffer.get(i * 4); 689 tangent.y = tangentBuffer.get(i * 4 + 1); 690 tangent.z = tangentBuffer.get(i * 4 + 2); 691 tangentW = tangentBuffer.get(i * 4 + 3); 692 } else { 693 populateFromBuffer(tangent, tangentBuffer, i); 694 } 695 696 int index = i * 4; 697 698 int id = i * 6; 699 lineIndex.put(id, index); 700 lineIndex.put(id + 1, index + 1); 701 lineIndex.put(id + 2, index); 702 lineIndex.put(id + 3, index + 2); 703 lineIndex.put(id + 4, index); 704 lineIndex.put(id + 5, index + 3); 705 706 setInBuffer(origin, lineVertex, index); 707 setInBuffer(originColor, lineColor, index); 708 709 point.set(tangent); 710 point.multLocal(scale); 711 point.addLocal(origin); 712 setInBuffer(point, lineVertex, index + 1); 713 setInBuffer(tangentColor, lineColor, index + 1); 714 715 // wvBinormal = cross(wvNormal, wvTangent) * -inTangent.w 716 717 if (binormalBuffer == null) { 718 normal.cross(tangent, point); 719 point.multLocal(-tangentW); 720 point.normalizeLocal(); 721 } else { 722 populateFromBuffer(point, binormalBuffer, i); 723 } 724 725 point.multLocal(scale); 726 point.addLocal(origin); 727 setInBuffer(point, lineVertex, index + 2); 728 setInBuffer(binormalColor, lineColor, index + 2); 729 730 point.set(normal); 731 point.multLocal(scale); 732 point.addLocal(origin); 733 setInBuffer(point, lineVertex, index + 3); 734 setInBuffer(normalColor, lineColor, index + 3); 735 } 736 737 lineMesh.setBuffer(Type.Index, 1, lineIndex); 738 lineMesh.setBuffer(Type.Position, 3, lineVertex); 739 lineMesh.setBuffer(Type.Color, 4, lineColor); 740 741 lineMesh.setStatic(); 742 //lineMesh.setInterleaved(); 743 return lineMesh; 744 } 745 } 746