1 /* 2 * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.prism.impl; 27 28 import com.sun.javafx.geom.Quat4f; 29 import com.sun.javafx.geom.Vec2f; 30 import com.sun.javafx.geom.Vec3f; 31 import com.sun.prism.Mesh; 32 import java.util.Arrays; 33 import java.util.HashMap; 34 import javafx.scene.shape.VertexFormat; 35 import sun.util.logging.PlatformLogger; 36 37 /** 38 * TODO: 3D - Need documentation 39 */ 40 public abstract class BaseMesh extends BaseGraphicsResource implements Mesh { 41 42 private int nVerts; 43 private int nTVerts; 44 private int nFaces; 45 private float[] pos; 46 private float[] uv; 47 private int[] faces; 48 private int[] smoothing; 49 private boolean allSameSmoothing; 50 private boolean allHardEdges; 51 52 protected static final int POINT_SIZE = 3; 53 protected static final int NORMAL_SIZE = 3; 54 protected static final int TEXCOORD_SIZE = 2; 55 56 protected static final int POINT_SIZE_VB = 3; 57 protected static final int TEXCOORD_SIZE_VB = 2; 58 protected static final int NORMAL_SIZE_VB = 4; 59 //point (3 floats), texcoord (2 floats) and normal (as in 4 floats) 60 protected static final int VERTEX_SIZE_VB = 9; 61 62 // Data members container for a single face 63 // Vec3i pVerts; 64 // Vec3i tVerts; 65 // int smGroup; 66 public static enum FaceMembers { 67 POINT0, TEXCOORD0, POINT1, TEXCOORD1, POINT2, TEXCOORD2, SMOOTHING_GROUP 68 }; 69 public static final int FACE_MEMBERS_SIZE = 7; 70 71 protected BaseMesh(Disposer.Record disposerRecord) { 72 super(disposerRecord); 73 } 74 75 public abstract boolean buildNativeGeometry(float[] vertexBuffer, 76 int vertexBufferLength, int[] indexBufferInt, int indexBufferLength); 77 78 public abstract boolean buildNativeGeometry(float[] vertexBuffer, 79 int vertexBufferLength, short[] indexBufferShort, int indexBufferLength); 80 81 private boolean updateSkipMeshNormalGeometry(int[] posFromAndLengthIndices, int[] uvFromAndLengthIndices) { 82 83 // Find out the list of modified tex coords. 84 int startTexCoord = uvFromAndLengthIndices[0] / TEXCOORD_SIZE; 85 int numTexCoords = (uvFromAndLengthIndices[1] / TEXCOORD_SIZE); 86 if ((uvFromAndLengthIndices[1] % TEXCOORD_SIZE) > 0) { 87 numTexCoords++; 88 } 89 90 if (numTexCoords > 0) { 91 for (int i = 0; i < numTexCoords; i++) { 92 int texCoordOffset = (startTexCoord + i) * TEXCOORD_SIZE; 93 MeshGeomComp2VB mt2vb = (MeshGeomComp2VB) texCoord2vbMap.get(texCoordOffset); 94 assert mt2vb != null; 95 // mt2vb shouldn't be null. We can't have a texCoord referred by 96 // the faces array that isn't in the vertexBuffer. 97 if (mt2vb != null) { 98 int[] locs = mt2vb.getLocs(); 99 int validLocs = mt2vb.getValidLocs(); 100 if (locs != null) { 101 for (int j = 0; j < validLocs; j++) { 102 int vbIndex = (locs[j] * VERTEX_SIZE_VB) + POINT_SIZE_VB; 103 vertexBuffer[vbIndex] = uv[texCoordOffset]; 104 vertexBuffer[vbIndex + 1] = uv[texCoordOffset + 1]; 105 } 106 } else { 107 int loc = mt2vb.getLoc(); 108 int vbIndex = (loc * VERTEX_SIZE_VB) + POINT_SIZE_VB; 109 vertexBuffer[vbIndex] = uv[texCoordOffset]; 110 vertexBuffer[vbIndex + 1] = uv[texCoordOffset + 1]; 111 } 112 } 113 } 114 } 115 116 // Find out the list of modified points 117 int startPoint = posFromAndLengthIndices[0] / POINT_SIZE; 118 int numPoints = (posFromAndLengthIndices[1] / POINT_SIZE); 119 if ((posFromAndLengthIndices[1] % POINT_SIZE) > 0) { 120 numPoints++; 121 } 122 123 if (numPoints > 0) { 124 for (int i = 0; i < numPoints; i++) { 125 int pointOffset = (startPoint + i) * POINT_SIZE; 126 MeshGeomComp2VB mp2vb = (MeshGeomComp2VB) point2vbMap.get(pointOffset); 127 assert mp2vb != null; 128 // mp2vb shouldn't be null. We can't have a point referred by 129 // the faces array that isn't in the vertexBuffer. 130 if (mp2vb != null) { 131 int[] locs = mp2vb.getLocs(); 132 int validLocs = mp2vb.getValidLocs(); 133 if (locs != null) { 134 for (int j = 0; j < validLocs; j++) { 135 int vbIndex = locs[j] * VERTEX_SIZE_VB; 136 vertexBuffer[vbIndex] = pos[pointOffset]; 137 vertexBuffer[vbIndex + 1] = pos[pointOffset + 1]; 138 vertexBuffer[vbIndex + 2] = pos[pointOffset + 2]; 139 } 140 } else { 141 int loc = mp2vb.getLoc(); 142 int vbIndex = loc * VERTEX_SIZE_VB; 143 vertexBuffer[vbIndex] = pos[pointOffset]; 144 vertexBuffer[vbIndex + 1] = pos[pointOffset + 1]; 145 vertexBuffer[vbIndex + 2] = pos[pointOffset + 2]; 146 } 147 } 148 } 149 } 150 151 if (indexBuffer != null) { 152 return buildNativeGeometry(vertexBuffer, 153 numberOfVertices * VERTEX_SIZE_VB, indexBuffer, nFaces * 3); 154 } else { 155 return buildNativeGeometry(vertexBuffer, 156 numberOfVertices * VERTEX_SIZE_VB, indexBufferShort, nFaces * 3); 157 } 158 } 159 160 private float[] tangents; 161 private float[] bitangents; 162 private float[] vertexBuffer; 163 private int[] indexBuffer; 164 private short[] indexBufferShort; 165 private int indexBufferSize; 166 private int numberOfVertices; 167 168 private HashMap<Integer, MeshGeomComp2VB> point2vbMap; 169 private HashMap<Integer, MeshGeomComp2VB> normal2vbMap; 170 private HashMap<Integer, MeshGeomComp2VB> texCoord2vbMap; 171 172 private boolean buildSkipMeshNormalGeometry() { 173 174 HashMap<Long, Integer> face2vbMap = new HashMap(); 175 176 if (point2vbMap == null) { 177 point2vbMap = new HashMap(); 178 } else { 179 point2vbMap.clear(); 180 } 181 if (texCoord2vbMap == null) { 182 texCoord2vbMap = new HashMap(); 183 } else { 184 texCoord2vbMap.clear(); 185 } 186 187 Integer mf2vb; 188 BaseMesh.MeshGeomComp2VB mp2vb; 189 BaseMesh.MeshGeomComp2VB mt2vb; 190 vertexBuffer = new float[nVerts * VERTEX_SIZE_VB]; 191 indexBuffer = new int[nFaces * 3]; 192 int ibCount = 0; 193 int vbCount = 0; 194 195 for (int faceCount = 0; faceCount < nFaces; faceCount++) { 196 int faceIndex = faceCount * 6; 197 for (int i = 0; i < 3; i++) { 198 int vertexIndex = i * 2; 199 int pointIndex = faceIndex + vertexIndex; 200 int texCoordIndex = pointIndex + 1; 201 long key = (long) ((long) (faces[pointIndex]) << 32 | faces[texCoordIndex]); 202 mf2vb = (Integer) face2vbMap.get(key); 203 if (mf2vb == null) { 204 mf2vb = vbCount / VERTEX_SIZE_VB; 205 206 face2vbMap.put(key, mf2vb); 207 if (vertexBuffer.length <= vbCount) { 208 float[] temp = new float[vbCount + 10 * VERTEX_SIZE_VB]; // Let's increment by 10 209 System.arraycopy(vertexBuffer, 0, temp, 0, vertexBuffer.length); 210 vertexBuffer = temp; 211 } 212 int pointOffset = faces[pointIndex] * POINT_SIZE; 213 int texCoordOffset = faces[texCoordIndex] * TEXCOORD_SIZE; 214 vertexBuffer[vbCount] = pos[pointOffset]; 215 vertexBuffer[vbCount + 1] = pos[pointOffset + 1]; 216 vertexBuffer[vbCount + 2] = pos[pointOffset + 2]; 217 vertexBuffer[vbCount + 3] = uv[texCoordOffset]; 218 vertexBuffer[vbCount + 4] = uv[texCoordOffset + 1]; 219 vertexBuffer[vbCount + 5] = 0; 220 vertexBuffer[vbCount + 6] = 0; 221 vertexBuffer[vbCount + 7] = 0; 222 vertexBuffer[vbCount + 8] = 0; 223 vbCount += VERTEX_SIZE_VB; 224 225 mp2vb = point2vbMap.get(pointOffset); 226 if (mp2vb == null) { 227 // create 228 mp2vb = new MeshGeomComp2VB(pointOffset, mf2vb); 229 point2vbMap.put(pointOffset, mp2vb); 230 } else { 231 // addLoc 232 mp2vb.addLoc(mf2vb); 233 } 234 235 mt2vb = texCoord2vbMap.get(texCoordOffset); 236 if (mt2vb == null) { 237 // create 238 mt2vb = new MeshGeomComp2VB(texCoordOffset, mf2vb); 239 texCoord2vbMap.put(texCoordOffset, mt2vb); 240 } else { 241 // addLoc 242 mt2vb.addLoc(mf2vb); 243 } 244 } 245 246 // Construct IndexBuffer 247 indexBuffer[ibCount++] = mf2vb; 248 } 249 } 250 251 numberOfVertices = vbCount / VERTEX_SIZE_VB; 252 253 if (numberOfVertices > 0x10000) { // > 64K 254 return buildNativeGeometry(vertexBuffer, 255 numberOfVertices * VERTEX_SIZE_VB, indexBuffer, nFaces * 3); 256 } else { 257 258 if (indexBufferShort == null || indexBufferShort.length < nFaces * 3) { 259 indexBufferShort = new short[nFaces * 3]; 260 } 261 int ii = 0; 262 for (int i = 0; i < nFaces; i++) { 263 indexBufferShort[ii] = (short) indexBuffer[ii++]; 264 indexBufferShort[ii] = (short) indexBuffer[ii++]; 265 indexBufferShort[ii] = (short) indexBuffer[ii++]; 266 } 267 indexBuffer = null; // free 268 return buildNativeGeometry(vertexBuffer, 269 numberOfVertices * VERTEX_SIZE_VB, indexBufferShort, nFaces * 3); 270 } 271 } 272 273 // Build PointNormalTexCoordGeometry 274 private boolean doBuildPNTGeometry(float[] points, float[] normals, 275 float[] texCoords, int[] faces) { 276 277 if (point2vbMap == null) { 278 point2vbMap = new HashMap(); 279 } else { 280 point2vbMap.clear(); 281 } 282 if (normal2vbMap == null) { 283 normal2vbMap = new HashMap(); 284 } else { 285 normal2vbMap.clear(); 286 } 287 if (texCoord2vbMap == null) { 288 texCoord2vbMap = new HashMap(); 289 } else { 290 texCoord2vbMap.clear(); 291 } 292 293 int vertexIndexSize = VertexFormat.POINT_NORMAL_TEXCOORD.getVertexIndexSize(); 294 int faceIndexSize = vertexIndexSize * 3; 295 int pointIndexOffset = VertexFormat.POINT_NORMAL_TEXCOORD.getPointIndexOffset(); 296 int normalIndexOffset = VertexFormat.POINT_NORMAL_TEXCOORD.getNormalIndexOffset(); 297 int texCoordIndexOffset = VertexFormat.POINT_NORMAL_TEXCOORD.getTexCoordIndexOffset(); 298 299 int numPoints = points.length / POINT_SIZE; 300 int numNormals = normals.length / NORMAL_SIZE; 301 int numTexCoords = texCoords.length / TEXCOORD_SIZE; 302 int numFaces = faces.length / faceIndexSize; 303 assert numPoints > 0 && numNormals > 0 && numTexCoords > 0 && numFaces > 0; 304 305 Integer mf2vb; 306 BaseMesh.MeshGeomComp2VB mp2vb; 307 BaseMesh.MeshGeomComp2VB mn2vb; 308 BaseMesh.MeshGeomComp2VB mt2vb; 309 // Allocate an initial size, may grow as we process the faces array. 310 tangents = new float[numPoints * NORMAL_SIZE]; 311 bitangents = new float[numPoints * NORMAL_SIZE]; 312 vertexBuffer = new float[numPoints * VERTEX_SIZE_VB]; 313 indexBuffer = new int[numFaces * 3]; 314 int ibCount = 0; 315 int vbCount = 0; 316 317 MeshTempState instance = MeshTempState.getInstance(); 318 319 for (int faceCount = 0; faceCount < numFaces; faceCount++) { 320 int faceIndex = faceCount * faceIndexSize; 321 for (int i = 0; i < 3; i++) { 322 int vertexIndex = faceIndex + (i * vertexIndexSize); 323 int pointIndex = vertexIndex + pointIndexOffset; 324 int normalIndex = vertexIndex + normalIndexOffset; 325 int texCoordIndex = vertexIndex + texCoordIndexOffset; 326 327 mf2vb = vbCount / VERTEX_SIZE_VB; 328 329 if (vertexBuffer.length <= vbCount) { 330 final int incrementedSize = vbCount + 20; // Let's increment by 20 331 float[] temp = new float[incrementedSize * VERTEX_SIZE_VB]; 332 System.arraycopy(vertexBuffer, 0, temp, 0, vertexBuffer.length); 333 vertexBuffer = temp; 334 // Enlarge tangents and bitangents too 335 temp = new float[incrementedSize * 3]; 336 System.arraycopy(tangents, 0, temp, 0, tangents.length); 337 tangents = temp; 338 temp = new float[incrementedSize * 3]; 339 System.arraycopy(bitangents, 0, temp, 0, bitangents.length); 340 bitangents = temp; 341 } 342 int pointOffset = faces[pointIndex] * POINT_SIZE; 343 int normalOffset = faces[normalIndex] * NORMAL_SIZE; 344 int texCoordOffset = faces[texCoordIndex] * TEXCOORD_SIZE; 345 346 // Save the vertex of triangle 347 instance.triPointIndex[i] = pointOffset; 348 instance.triTexCoordIndex[i] = texCoordOffset; 349 instance.triVerts[i] = vbCount / VERTEX_SIZE_VB; 350 351 vertexBuffer[vbCount] = points[pointOffset]; 352 vertexBuffer[vbCount + 1] = points[pointOffset + 1]; 353 vertexBuffer[vbCount + 2] = points[pointOffset + 2]; 354 vertexBuffer[vbCount + 3] = texCoords[texCoordOffset]; 355 vertexBuffer[vbCount + 4] = texCoords[texCoordOffset + 1]; 356 // Temporarily store the normal in the quaterion slot 357 vertexBuffer[vbCount + 5] = normals[normalOffset]; 358 vertexBuffer[vbCount + 6] = normals[normalOffset + 1]; 359 vertexBuffer[vbCount + 7] = normals[normalOffset + 2]; 360 vertexBuffer[vbCount + 8] = 0; 361 362 vbCount += VERTEX_SIZE_VB; 363 364 mp2vb = point2vbMap.get(pointOffset); 365 if (mp2vb == null) { 366 // create 367 mp2vb = new MeshGeomComp2VB(pointOffset, mf2vb); 368 point2vbMap.put(pointOffset, mp2vb); 369 } else { 370 // addLoc 371 mp2vb.addLoc(mf2vb); 372 } 373 374 mn2vb = normal2vbMap.get(normalOffset); 375 if (mn2vb == null) { 376 // create 377 mn2vb = new MeshGeomComp2VB(normalOffset, mf2vb); 378 normal2vbMap.put(normalOffset, mn2vb); 379 } else { 380 // addLoc 381 mn2vb.addLoc(mf2vb); 382 } 383 384 mt2vb = texCoord2vbMap.get(texCoordOffset); 385 if (mt2vb == null) { 386 // create 387 mt2vb = new MeshGeomComp2VB(texCoordOffset, mf2vb); 388 texCoord2vbMap.put(texCoordOffset, mt2vb); 389 } else { 390 // addLoc 391 mt2vb.addLoc(mf2vb); 392 } 393 394 // Construct IndexBuffer 395 indexBuffer[ibCount++] = mf2vb; 396 } 397 398 // This is the best time to compute the tangent and bitangent for each 399 // of the vertex. Go thro. the 3 vertices of a triangle 400 for (int i = 0; i < 3; i++) { 401 if (instance.triPoints[i] == null) { 402 instance.triPoints[i] = new Vec3f(); 403 } 404 instance.triPoints[i].x = points[instance.triPointIndex[i]]; 405 instance.triPoints[i].y = points[instance.triPointIndex[i] + 1]; 406 instance.triPoints[i].z = points[instance.triPointIndex[i] + 2]; 407 if (instance.triTexCoords[i] == null) { 408 instance.triTexCoords[i] = new Vec2f(); 409 } 410 instance.triTexCoords[i].x = texCoords[instance.triTexCoordIndex[i]]; 411 instance.triTexCoords[i].y = texCoords[instance.triTexCoordIndex[i] + 1]; 412 } 413 414 MeshUtil.computeTBNNormalized(instance.triPoints[0], instance.triPoints[1], 415 instance.triPoints[2], instance.triTexCoords[0], 416 instance.triTexCoords[1], instance.triTexCoords[2], 417 instance.triNormals); 418 419 for (int i = 0; i < 3; i++) { 420 int index = instance.triVerts[i] * NORMAL_SIZE; 421 tangents[index] = instance.triNormals[1].x; 422 tangents[index + 1] = instance.triNormals[1].y; 423 tangents[index + 2] = instance.triNormals[1].z; 424 bitangents[index] = instance.triNormals[2].x; 425 bitangents[index + 1] = instance.triNormals[2].y; 426 bitangents[index + 2] = instance.triNormals[2].z; 427 } 428 429 } 430 431 numberOfVertices = vbCount / VERTEX_SIZE_VB; 432 433 Vec3f normal = instance.vec3f1; 434 Vec3f tangent = instance.vec3f2; 435 Vec3f bitangent = instance.vec3f3; 436 for (int i = 0, vbIndex = 0; i < numberOfVertices; i++, vbIndex += VERTEX_SIZE_VB) { 437 normal.x = vertexBuffer[vbIndex + 5]; 438 normal.y = vertexBuffer[vbIndex + 6]; 439 normal.z = vertexBuffer[vbIndex + 7]; 440 normal.normalize(); 441 442 int index = i * NORMAL_SIZE; 443 // tangent and bitangent have been normalized. 444 tangent.x = tangents[index]; 445 tangent.y = tangents[index + 1]; 446 tangent.z = tangents[index + 2]; 447 bitangent.x = bitangents[index]; 448 bitangent.y = bitangents[index + 1]; 449 bitangent.z = bitangents[index + 2]; 450 451 instance.triNormals[0].set(normal); 452 instance.triNormals[1].set(tangent); 453 instance.triNormals[2].set(bitangent); 454 MeshUtil.fixTSpace(instance.triNormals); 455 buildVSQuat(instance.triNormals, instance.quat); 456 457 vertexBuffer[vbIndex + 5] = instance.quat.x; 458 vertexBuffer[vbIndex + 6] = instance.quat.y; 459 vertexBuffer[vbIndex + 7] = instance.quat.z; 460 vertexBuffer[vbIndex + 8] = instance.quat.w; 461 } 462 463 indexBufferSize = numFaces * 3; 464 465 if (numberOfVertices > 0x10000) { // > 64K 466 return buildNativeGeometry(vertexBuffer, 467 numberOfVertices * VERTEX_SIZE_VB, indexBuffer, indexBufferSize); 468 } else { 469 470 if (indexBufferShort == null || indexBufferShort.length < indexBufferSize) { 471 indexBufferShort = new short[indexBufferSize]; 472 } 473 int ii = 0; 474 for (int i = 0; i < numFaces; i++) { 475 indexBufferShort[ii] = (short) indexBuffer[ii++]; 476 indexBufferShort[ii] = (short) indexBuffer[ii++]; 477 indexBufferShort[ii] = (short) indexBuffer[ii++]; 478 } 479 indexBuffer = null; // free 480 return buildNativeGeometry(vertexBuffer, 481 numberOfVertices * VERTEX_SIZE_VB, indexBufferShort, indexBufferSize); 482 } 483 } 484 485 // Update PointNormalTexCoordGeometry 486 private boolean updatePNTGeometry(float[] points, int[] pointsFromAndLengthIndices, 487 float[] normals, int[] normalsFromAndLengthIndices, 488 float[] texCoords, int[] texCoordsFromAndLengthIndices) { 489 // Find out the list of modified points 490 int startPoint = pointsFromAndLengthIndices[0] / POINT_SIZE; 491 int numPoints = (pointsFromAndLengthIndices[1] / POINT_SIZE); 492 if ((pointsFromAndLengthIndices[1] % POINT_SIZE) > 0) { 493 numPoints++; 494 } 495 if (numPoints > 0) { 496 for (int i = 0; i < numPoints; i++) { 497 int pointOffset = (startPoint + i) * POINT_SIZE; 498 MeshGeomComp2VB mp2vb = (MeshGeomComp2VB) point2vbMap.get(pointOffset); 499 assert mp2vb != null; 500 // mp2vb shouldn't be null. We can't have a point referred by 501 // the faces array that isn't in the vertexBuffer. 502 if (mp2vb != null) { 503 int[] locs = mp2vb.getLocs(); 504 int validLocs = mp2vb.getValidLocs(); 505 if (locs != null) { 506 for (int j = 0; j < validLocs; j++) { 507 int vbIndex = locs[j] * VERTEX_SIZE_VB; 508 vertexBuffer[vbIndex] = points[pointOffset]; 509 vertexBuffer[vbIndex + 1] = points[pointOffset + 1]; 510 vertexBuffer[vbIndex + 2] = points[pointOffset + 2]; 511 } 512 } else { 513 int loc = mp2vb.getLoc(); 514 int vbIndex = loc * VERTEX_SIZE_VB; 515 vertexBuffer[vbIndex] = points[pointOffset]; 516 vertexBuffer[vbIndex + 1] = points[pointOffset + 1]; 517 vertexBuffer[vbIndex + 2] = points[pointOffset + 2]; 518 } 519 } 520 } 521 } 522 523 // Find out the list of modified tex coords. 524 int startTexCoord = texCoordsFromAndLengthIndices[0] / TEXCOORD_SIZE; 525 int numTexCoords = (texCoordsFromAndLengthIndices[1] / TEXCOORD_SIZE); 526 if ((texCoordsFromAndLengthIndices[1] % TEXCOORD_SIZE) > 0) { 527 numTexCoords++; 528 } 529 if (numTexCoords > 0) { 530 for (int i = 0; i < numTexCoords; i++) { 531 int texCoordOffset = (startTexCoord + i) * TEXCOORD_SIZE; 532 MeshGeomComp2VB mt2vb = (MeshGeomComp2VB) texCoord2vbMap.get(texCoordOffset); 533 assert mt2vb != null; 534 // mt2vb shouldn't be null. We can't have a texCoord referred by 535 // the faces array that isn't in the vertexBuffer. 536 if (mt2vb != null) { 537 int[] locs = mt2vb.getLocs(); 538 int validLocs = mt2vb.getValidLocs(); 539 if (locs != null) { 540 for (int j = 0; j < validLocs; j++) { 541 int vbIndex = (locs[j] * VERTEX_SIZE_VB) + POINT_SIZE_VB; 542 vertexBuffer[vbIndex] = texCoords[texCoordOffset]; 543 vertexBuffer[vbIndex + 1] = texCoords[texCoordOffset + 1]; 544 } 545 } else { 546 int loc = mt2vb.getLoc(); 547 int vbIndex = (loc * VERTEX_SIZE_VB) + POINT_SIZE_VB; 548 vertexBuffer[vbIndex] = texCoords[texCoordOffset]; 549 vertexBuffer[vbIndex + 1] = texCoords[texCoordOffset + 1]; 550 } 551 } 552 } 553 } 554 555 // Find out the list of modified normals 556 int startNormal = normalsFromAndLengthIndices[0] / NORMAL_SIZE; 557 int numNormals = (normalsFromAndLengthIndices[1] / NORMAL_SIZE); 558 if ((normalsFromAndLengthIndices[1] % NORMAL_SIZE) > 0) { 559 numNormals++; 560 } 561 if (numNormals > 0) { 562 MeshTempState instance = MeshTempState.getInstance(); 563 for (int i = 0; i < numNormals; i++) { 564 int normalOffset = (startNormal + i) * NORMAL_SIZE; 565 MeshGeomComp2VB mn2vb = (MeshGeomComp2VB) normal2vbMap.get(normalOffset); 566 assert mn2vb != null; 567 // mn2vb shouldn't be null. We can't have a normal referred by 568 // the faces array that isn't in the vertexBuffer. 569 if (mn2vb != null) { 570 int[] locs = mn2vb.getLocs(); 571 int validLocs = mn2vb.getValidLocs(); 572 if (locs != null) { 573 for (int j = 0; j < validLocs; j++) { 574 int vbIndex = (locs[j] * VERTEX_SIZE_VB) 575 + POINT_SIZE_VB + TEXCOORD_SIZE_VB; 576 577 instance.triNormals[0].x = normals[normalOffset]; 578 instance.triNormals[0].y = normals[normalOffset + 1]; 579 instance.triNormals[0].z = normals[normalOffset + 2]; 580 instance.triNormals[0].normalize(); 581 instance.triNormals[1].x = tangents[normalOffset]; 582 instance.triNormals[1].y = tangents[normalOffset + 1]; 583 instance.triNormals[1].z = tangents[normalOffset + 2]; 584 instance.triNormals[2].x = bitangents[normalOffset]; 585 instance.triNormals[2].y = bitangents[normalOffset + 1]; 586 instance.triNormals[2].z = bitangents[normalOffset + 2]; 587 MeshUtil.fixTSpace(instance.triNormals); 588 buildVSQuat(instance.triNormals, instance.quat); 589 vertexBuffer[vbIndex] = instance.quat.x; 590 vertexBuffer[vbIndex + 1] = instance.quat.y; 591 vertexBuffer[vbIndex + 2] = instance.quat.z; 592 vertexBuffer[vbIndex + 3] = instance.quat.w; 593 } 594 } else { 595 int loc = mn2vb.getLoc(); 596 int vbIndex = (loc * VERTEX_SIZE_VB) 597 + POINT_SIZE_VB + TEXCOORD_SIZE_VB; 598 599 instance.triNormals[0].x = normals[normalOffset]; 600 instance.triNormals[0].y = normals[normalOffset + 1]; 601 instance.triNormals[0].z = normals[normalOffset + 2]; 602 instance.triNormals[0].normalize(); 603 instance.triNormals[1].x = tangents[normalOffset]; 604 instance.triNormals[1].y = tangents[normalOffset + 1]; 605 instance.triNormals[1].z = tangents[normalOffset + 2]; 606 instance.triNormals[2].x = bitangents[normalOffset]; 607 instance.triNormals[2].y = bitangents[normalOffset + 1]; 608 instance.triNormals[2].z = bitangents[normalOffset + 2]; 609 MeshUtil.fixTSpace(instance.triNormals); 610 buildVSQuat(instance.triNormals, instance.quat); 611 vertexBuffer[vbIndex] = instance.quat.x; 612 vertexBuffer[vbIndex + 1] = instance.quat.y; 613 vertexBuffer[vbIndex + 2] = instance.quat.z; 614 vertexBuffer[vbIndex + 3] = instance.quat.w; 615 } 616 } 617 } 618 } 619 620 if (indexBuffer != null) { 621 return buildNativeGeometry(vertexBuffer, 622 numberOfVertices * VERTEX_SIZE_VB, indexBuffer, indexBufferSize); 623 } else { 624 return buildNativeGeometry(vertexBuffer, 625 numberOfVertices * VERTEX_SIZE_VB, indexBufferShort, indexBufferSize); 626 } 627 628 } 629 630 @Override 631 public boolean buildGeometry(boolean userDefinedNormals, 632 float[] points, int[] pointsFromAndLengthIndices, 633 float[] normals, int[] normalsFromAndLengthIndices, 634 float[] texCoords, int[] texCoordsFromAndLengthIndices, 635 int[] faces, int[] facesFromAndLengthIndices, 636 int[] faceSmoothingGroups, int[] faceSmoothingGroupsFromAndLengthIndices) { 637 if (userDefinedNormals) { 638 return buildPNTGeometry(points, pointsFromAndLengthIndices, 639 normals, normalsFromAndLengthIndices, 640 texCoords, texCoordsFromAndLengthIndices, 641 faces, facesFromAndLengthIndices); 642 } else { 643 return buildPTGeometry(points, pointsFromAndLengthIndices, 644 texCoords, texCoordsFromAndLengthIndices, 645 faces, facesFromAndLengthIndices, 646 faceSmoothingGroups, faceSmoothingGroupsFromAndLengthIndices); 647 } 648 } 649 650 // Build PointNormalTexCoordGeometry 651 private boolean buildPNTGeometry( 652 float[] points, int[] pointsFromAndLengthIndices, 653 float[] normals, int[] normalsFromAndLengthIndices, 654 float[] texCoords, int[] texCoordsFromAndLengthIndices, 655 int[] faces, int[] facesFromAndLengthIndices) { 656 657 boolean updatePoints = pointsFromAndLengthIndices[1] > 0; 658 boolean updateNormals = normalsFromAndLengthIndices[1] > 0; 659 boolean updateTexCoords = texCoordsFromAndLengthIndices[1] > 0; 660 boolean updateFaces = facesFromAndLengthIndices[1] > 0; 661 662 // First time creation 663 boolean buildGeom = !(updatePoints || updateNormals || updateTexCoords || updateFaces); 664 665 // We will need to rebuild geom buffers if there is a change to faces 666 if (updateFaces) { 667 buildGeom = true; 668 } 669 670 if ((!buildGeom) && (vertexBuffer != null) 671 && ((indexBuffer != null) || (indexBufferShort != null))) { 672 return updatePNTGeometry(points, pointsFromAndLengthIndices, 673 normals, normalsFromAndLengthIndices, 674 texCoords, texCoordsFromAndLengthIndices); 675 } 676 return doBuildPNTGeometry(points, normals, texCoords, faces); 677 678 } 679 680 // Build PointTexCoordGeometry 681 private boolean buildPTGeometry(float[] pos, int[] posFromAndLengthIndices, 682 float[] uv, int[] uvFromAndLengthIndices, 683 int[] faces, int[] facesFromAndLengthIndices, 684 int[] smoothing, int[] smoothingFromAndLengthIndices) { 685 nVerts = pos.length / 3; 686 nTVerts = uv.length / 2; 687 nFaces = faces.length / (VertexFormat.POINT_TEXCOORD.getVertexIndexSize() * 3); 688 assert nVerts > 0 && nFaces > 0 && nTVerts > 0; 689 this.pos = pos; 690 this.uv = uv; 691 this.faces = faces; 692 this.smoothing = smoothing.length == nFaces ? smoothing : null; 693 694 if (PrismSettings.skipMeshNormalComputation) { 695 boolean updatePoints = posFromAndLengthIndices[1] > 0; 696 boolean updateTexCoords = uvFromAndLengthIndices[1] > 0; 697 boolean updateFaces = facesFromAndLengthIndices[1] > 0; 698 boolean updateSmoothing = smoothingFromAndLengthIndices[1] > 0; 699 700 // First time creation 701 boolean buildGeom = !(updatePoints || updateTexCoords || updateFaces || updateSmoothing); 702 703 // We will need to rebuild if there is a change to faces or smoothing 704 if (updateFaces || updateSmoothing) { 705 buildGeom = true; 706 } 707 708 if ((!buildGeom) && (vertexBuffer != null) 709 && ((indexBuffer != null) || (indexBufferShort != null))) { 710 return updateSkipMeshNormalGeometry(posFromAndLengthIndices, uvFromAndLengthIndices); 711 } 712 713 return buildSkipMeshNormalGeometry(); 714 } 715 716 MeshTempState instance = MeshTempState.getInstance(); 717 // big pool for all possible vertices 718 if (instance.pool == null || instance.pool.length < nFaces * 3) { 719 instance.pool = new MeshVertex[nFaces * 3]; 720 } 721 722 if (instance.indexBuffer == null || instance.indexBuffer.length < nFaces * 3) { 723 instance.indexBuffer = new int[nFaces * 3]; 724 } 725 726 if (instance.pVertex == null || instance.pVertex.length < nVerts) { 727 instance.pVertex = new MeshVertex[nVerts]; 728 } else { 729 Arrays.fill(instance.pVertex, 0, instance.pVertex.length, null); 730 } 731 732 // check if all hard edges or all smooth 733 checkSmoothingGroup(); 734 735 // compute [N, T, B] for each face 736 computeTBNormal(instance.pool, instance.pVertex, instance.indexBuffer); 737 738 // process sm and weld points 739 int nNewVerts = MeshVertex.processVertices(instance.pVertex, nVerts, 740 allHardEdges, allSameSmoothing); 741 742 if (instance.vertexBuffer == null 743 || instance.vertexBuffer.length < nNewVerts * VERTEX_SIZE_VB) { 744 instance.vertexBuffer = new float[nNewVerts * VERTEX_SIZE_VB]; 745 } 746 buildVertexBuffer(instance.pVertex, instance.vertexBuffer); 747 748 if (nNewVerts > 0x10000) { 749 buildIndexBuffer(instance.pool, instance.indexBuffer, null); 750 return buildNativeGeometry(instance.vertexBuffer, 751 nNewVerts * VERTEX_SIZE_VB, instance.indexBuffer, nFaces * 3); 752 } else { 753 if (instance.indexBufferShort == null || instance.indexBufferShort.length < nFaces * 3) { 754 instance.indexBufferShort = new short[nFaces * 3]; 755 } 756 buildIndexBuffer(instance.pool, instance.indexBuffer, instance.indexBufferShort); 757 return buildNativeGeometry(instance.vertexBuffer, 758 nNewVerts * VERTEX_SIZE_VB, instance.indexBufferShort, nFaces * 3); 759 } 760 } 761 762 private void computeTBNormal(MeshVertex[] pool, MeshVertex[] pVertex, int[] indexBuffer) { 763 MeshTempState instance = MeshTempState.getInstance(); 764 765 // tmp variables 766 int[] smFace = instance.smFace; 767 int[] triVerts = instance.triVerts; 768 Vec3f[] triPoints = instance.triPoints; 769 Vec2f[] triTexCoords = instance.triTexCoords; 770 Vec3f[] triNormals = instance.triNormals; 771 final String logname = BaseMesh.class.getName(); 772 773 for (int f = 0, nDeadFaces = 0, poolIndex = 0; f < nFaces; f++) { 774 int index = f * 3; 775 776 smFace = getFace(f, smFace); // copy from mesh to tmp smFace 777 778 // Get tex. point. index 779 triVerts[0] = smFace[BaseMesh.FaceMembers.POINT0.ordinal()]; 780 triVerts[1] = smFace[BaseMesh.FaceMembers.POINT1.ordinal()]; 781 triVerts[2] = smFace[BaseMesh.FaceMembers.POINT2.ordinal()]; 782 783 if (MeshUtil.isDeadFace(triVerts) 784 && PlatformLogger.getLogger(logname).isLoggable(PlatformLogger.Level.FINE)) { 785 // Log degenerated triangle 786 nDeadFaces++; 787 PlatformLogger.getLogger(logname).fine("Dead face [" 788 + triVerts[0] + ", " + triVerts[1] + ", " + triVerts[2] 789 + "] @ face group " + f + "; nEmptyFaces = " + nDeadFaces); 790 } 791 792 for (int i = 0; i < 3; i++) { 793 triPoints[i] = getVertex(triVerts[i], triPoints[i]); 794 } 795 796 // Get tex. coord. index 797 triVerts[0] = smFace[BaseMesh.FaceMembers.TEXCOORD0.ordinal()]; 798 triVerts[1] = smFace[BaseMesh.FaceMembers.TEXCOORD1.ordinal()]; 799 triVerts[2] = smFace[BaseMesh.FaceMembers.TEXCOORD2.ordinal()]; 800 801 for (int i = 0; i < 3; i++) { 802 triTexCoords[i] = getTVertex(triVerts[i], triTexCoords[i]); 803 } 804 805 MeshUtil.computeTBNNormalized(triPoints[0], triPoints[1], triPoints[2], 806 triTexCoords[0], triTexCoords[1], triTexCoords[2], 807 triNormals); 808 809 for (int j = 0; j < 3; ++j) { 810 pool[poolIndex] = (pool[poolIndex] == null) ? new MeshVertex() : pool[poolIndex]; 811 812 for (int i = 0; i < 3; ++i) { 813 pool[poolIndex].norm[i].set(triNormals[i]); 814 } 815 pool[poolIndex].smGroup = smFace[BaseMesh.FaceMembers.SMOOTHING_GROUP.ordinal()]; 816 pool[poolIndex].fIdx = f; 817 pool[poolIndex].tVert = triVerts[j]; 818 pool[poolIndex].index = MeshVertex.IDX_UNDEFINED; 819 int ii = j == 0 ? BaseMesh.FaceMembers.POINT0.ordinal() 820 : j == 1 ? BaseMesh.FaceMembers.POINT1.ordinal() 821 : BaseMesh.FaceMembers.POINT2.ordinal(); 822 int pIdx = smFace[ii]; 823 pool[poolIndex].pVert = pIdx; 824 indexBuffer[index + j] = pIdx; 825 pool[poolIndex].next = pVertex[pIdx]; 826 pVertex[pIdx] = pool[poolIndex]; 827 poolIndex++; 828 } 829 } 830 } 831 832 private void buildVSQuat(Vec3f[] tm, Quat4f quat) { 833 Vec3f v = MeshTempState.getInstance().vec3f1; 834 v.cross(tm[1], tm[2]); 835 float d = tm[0].dot(v); 836 if (d < 0) { 837 tm[2].mul(-1); 838 } 839 840 MeshUtil.buildQuat(tm, quat); 841 842 // This will interfer with degenerated triangle unit test. 843 // assert (quat.w >= 0); 844 845 if (d < 0) { 846 if (quat.w == 0) { 847 quat.w = MeshUtil.MAGIC_SMALL; 848 } 849 quat.scale(-1); 850 } 851 } 852 853 private void buildVertexBuffer(MeshVertex[] pVerts, float[] vertexBuffer) { 854 Quat4f quat = MeshTempState.getInstance().quat; 855 int idLast = 0; 856 857 for (int i = 0, index = 0; i < nVerts; ++i) { 858 MeshVertex v = pVerts[i]; 859 for (; v != null; v = v.next) { 860 if (v.index == idLast) { 861 int ind = v.pVert * 3; 862 vertexBuffer[index++] = pos[ind]; 863 vertexBuffer[index++] = pos[ind + 1]; 864 vertexBuffer[index++] = pos[ind + 2]; 865 ind = v.tVert * 2; 866 vertexBuffer[index++] = uv[ind]; 867 vertexBuffer[index++] = uv[ind + 1]; 868 buildVSQuat(v.norm, quat); 869 vertexBuffer[index++] = quat.x; 870 vertexBuffer[index++] = quat.y; 871 vertexBuffer[index++] = quat.z; 872 vertexBuffer[index++] = quat.w; 873 idLast++; 874 } 875 } 876 } 877 } 878 879 private void buildIndexBuffer(MeshVertex[] pool, int[] indexBuffer, short[] indexBufferShort) { 880 for (int i = 0; i < nFaces; ++i) { 881 int index = i * 3; 882 if (indexBuffer[index] != MeshVertex.IDX_UNDEFINED) { 883 for (int j = 0; j < 3; ++j) { 884 assert (pool[index].fIdx == i); 885 if (indexBufferShort != null) { 886 indexBufferShort[index + j] = (short) pool[index + j].index; 887 } else { 888 indexBuffer[index + j] = pool[index + j].index; 889 } 890 pool[index + j].next = null; // release reference 891 } 892 } else { 893 for (int j = 0; j < 3; ++j) { 894 if (indexBufferShort != null) { 895 indexBufferShort[index + j] = 0; 896 } else { 897 indexBuffer[index + j] = 0; 898 } 899 } 900 } 901 } 902 } 903 904 public int getNumVerts() { 905 return nVerts; 906 } 907 908 public int getNumTVerts() { 909 return nTVerts; 910 } 911 912 public int getNumFaces() { 913 return nFaces; 914 } 915 916 public Vec3f getVertex(int pIdx, Vec3f vertex) { 917 if (vertex == null) { 918 vertex = new Vec3f(); 919 } 920 int index = pIdx * 3; 921 vertex.set(pos[index], pos[index + 1], pos[index + 2]); 922 return vertex; 923 } 924 925 public Vec2f getTVertex(int tIdx, Vec2f texCoord) { 926 if (texCoord == null) { 927 texCoord = new Vec2f(); 928 } 929 int index = tIdx * 2; 930 texCoord.set(uv[index], uv[index + 1]); 931 return texCoord; 932 } 933 934 private void checkSmoothingGroup() { 935 if (smoothing == null || smoothing.length == 0) { // all smooth 936 allSameSmoothing = true; 937 allHardEdges = false; 938 return; 939 } 940 941 for (int i = 0; i + 1 < smoothing.length; i++) { 942 if (smoothing[i] != smoothing[i + 1]) { 943 // various SmGroup 944 allSameSmoothing = false; 945 allHardEdges = false; 946 return; 947 } 948 } 949 950 if (smoothing[0] == 0) { // all hard edges 951 allSameSmoothing = false; 952 allHardEdges = true; 953 } else { // all belongs to one group == all smooth 954 allSameSmoothing = true; 955 allHardEdges = false; 956 } 957 } 958 959 public int[] getFace(int fIdx, int[] face) { 960 int index = fIdx * 6; 961 if ((face == null) || (face.length < FACE_MEMBERS_SIZE)) { 962 face = new int[FACE_MEMBERS_SIZE]; 963 } 964 // Note: Order matter, [0, 5] == FaceMembers' points and texcoords 965 for (int i = 0; i < 6; i++) { 966 face[i] = faces[index + i]; 967 } 968 // Note: Order matter, 6 == FaceMembers.SMOOTHING_GROUP.ordinal() 969 // There is a total of 32 smoothing groups. 970 // Assign to 1st smoothing group if smoothing is null. 971 face[6] = smoothing != null ? smoothing[fIdx] : 1; 972 return face; 973 } 974 975 class MeshGeomComp2VB { 976 977 private final int key; // point or texCoord index 978 private final int loc; // the first index into vertex buffer 979 private int[] locs; 980 private int validLocs; 981 982 MeshGeomComp2VB(int key, int loc) { 983 assert loc >= 0; 984 this.key = key; 985 this.loc = loc; 986 locs = null; 987 validLocs = 0; 988 } 989 990 void addLoc(int loc) { 991 if (locs == null) { 992 locs = new int[3]; // edge of mesh case 993 locs[0] = this.loc; 994 locs[1] = loc; 995 this.validLocs = 2; 996 } else if (locs.length > validLocs) { 997 locs[validLocs] = loc; 998 validLocs++; 999 } else { 1000 int[] temp = new int[validLocs * 2]; 1001 System.arraycopy(locs, 0, temp, 0, locs.length); 1002 locs = temp; 1003 locs[validLocs] = loc; 1004 validLocs++; 1005 } 1006 } 1007 1008 int getKey() { 1009 return key; 1010 } 1011 1012 int getLoc() { 1013 return loc; 1014 } 1015 1016 int[] getLocs() { 1017 return locs; 1018 } 1019 1020 int getValidLocs() { 1021 return validLocs; 1022 } 1023 } 1024 1025 }