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 boolean[] dirtyVertices; 161 private float[] cachedNormals; 162 private float[] cachedTangents; 163 private float[] cachedBitangents; 164 private float[] vertexBuffer; 165 private int[] indexBuffer; 166 private short[] indexBufferShort; 167 private int indexBufferSize; 168 private int numberOfVertices; 169 170 private HashMap<Integer, MeshGeomComp2VB> point2vbMap; 171 private HashMap<Integer, MeshGeomComp2VB> normal2vbMap; 172 private HashMap<Integer, MeshGeomComp2VB> texCoord2vbMap; 173 174 private boolean buildSkipMeshNormalGeometry() { 175 176 HashMap<Long, Integer> face2vbMap = new HashMap(); 177 178 if (point2vbMap == null) { 179 point2vbMap = new HashMap(); 180 } else { 181 point2vbMap.clear(); 182 } 183 if (texCoord2vbMap == null) { 184 texCoord2vbMap = new HashMap(); 185 } else { 186 texCoord2vbMap.clear(); 187 } 188 189 Integer mf2vb; 190 BaseMesh.MeshGeomComp2VB mp2vb; 191 BaseMesh.MeshGeomComp2VB mt2vb; 192 vertexBuffer = new float[nVerts * VERTEX_SIZE_VB]; 193 indexBuffer = new int[nFaces * 3]; 194 int ibCount = 0; 195 int vbCount = 0; 196 197 for (int faceCount = 0; faceCount < nFaces; faceCount++) { 198 int faceIndex = faceCount * 6; 199 for (int i = 0; i < 3; i++) { 200 int vertexIndex = i * 2; 201 int pointIndex = faceIndex + vertexIndex; 202 int texCoordIndex = pointIndex + 1; 203 long key = (long) ((long) (faces[pointIndex]) << 32 | faces[texCoordIndex]); 204 mf2vb = (Integer) face2vbMap.get(key); 205 if (mf2vb == null) { 206 mf2vb = vbCount / VERTEX_SIZE_VB; 207 208 face2vbMap.put(key, mf2vb); 209 if (vertexBuffer.length <= vbCount) { 210 float[] temp = new float[vbCount + 10 * VERTEX_SIZE_VB]; // Let's increment by 10 211 System.arraycopy(vertexBuffer, 0, temp, 0, vertexBuffer.length); 212 vertexBuffer = temp; 213 } 214 int pointOffset = faces[pointIndex] * POINT_SIZE; 215 int texCoordOffset = faces[texCoordIndex] * TEXCOORD_SIZE; 216 vertexBuffer[vbCount] = pos[pointOffset]; 217 vertexBuffer[vbCount + 1] = pos[pointOffset + 1]; 218 vertexBuffer[vbCount + 2] = pos[pointOffset + 2]; 219 vertexBuffer[vbCount + 3] = uv[texCoordOffset]; 220 vertexBuffer[vbCount + 4] = uv[texCoordOffset + 1]; 221 vertexBuffer[vbCount + 5] = 0; 222 vertexBuffer[vbCount + 6] = 0; 223 vertexBuffer[vbCount + 7] = 0; 224 vertexBuffer[vbCount + 8] = 0; 225 vbCount += VERTEX_SIZE_VB; 226 227 mp2vb = point2vbMap.get(pointOffset); 228 if (mp2vb == null) { 229 // create 230 mp2vb = new MeshGeomComp2VB(pointOffset, mf2vb); 231 point2vbMap.put(pointOffset, mp2vb); 232 } else { 233 // addLoc 234 mp2vb.addLoc(mf2vb); 235 } 236 237 mt2vb = texCoord2vbMap.get(texCoordOffset); 238 if (mt2vb == null) { 239 // create 240 mt2vb = new MeshGeomComp2VB(texCoordOffset, mf2vb); 241 texCoord2vbMap.put(texCoordOffset, mt2vb); 242 } else { 243 // addLoc 244 mt2vb.addLoc(mf2vb); 245 } 246 } 247 248 // Construct IndexBuffer 249 indexBuffer[ibCount++] = mf2vb; 250 } 251 } 252 253 numberOfVertices = vbCount / VERTEX_SIZE_VB; 254 255 if (numberOfVertices > 0x10000) { // > 64K 256 return buildNativeGeometry(vertexBuffer, 257 numberOfVertices * VERTEX_SIZE_VB, indexBuffer, nFaces * 3); 258 } else { 259 260 if (indexBufferShort == null || indexBufferShort.length < nFaces * 3) { 261 indexBufferShort = new short[nFaces * 3]; 262 } 263 int ii = 0; 264 for (int i = 0; i < nFaces; i++) { 265 indexBufferShort[ii] = (short) indexBuffer[ii++]; 266 indexBufferShort[ii] = (short) indexBuffer[ii++]; 267 indexBufferShort[ii] = (short) indexBuffer[ii++]; 268 } 269 indexBuffer = null; // free 270 return buildNativeGeometry(vertexBuffer, 271 numberOfVertices * VERTEX_SIZE_VB, indexBufferShort, nFaces * 3); 272 } 273 } 274 275 private void convertNormalsToQuats(MeshTempState instance, int numberOfVertices, 276 float[] normals, float[] tangents, float[] bitangents, 277 float[] vertexBuffer, boolean[] dirtys) { 278 279 Vec3f normal = instance.vec3f1; 280 Vec3f tangent = instance.vec3f2; 281 Vec3f bitangent = instance.vec3f3; 282 for (int i = 0, vbIndex = 0; i < numberOfVertices; i++, vbIndex += VERTEX_SIZE_VB) { 283 // Note: If dirtys isn't null, dirtys.length == numberOfVertices is true 284 if (dirtys == null || dirtys[i]) { 285 int index = i * NORMAL_SIZE; 286 287 normal.x = normals[index]; 288 normal.y = normals[index + 1]; 289 normal.z = normals[index + 2]; 290 normal.normalize(); 291 292 // tangent and bitangent have been normalized. 293 tangent.x = tangents[index]; 294 tangent.y = tangents[index + 1]; 295 tangent.z = tangents[index + 2]; 296 bitangent.x = bitangents[index]; 297 bitangent.y = bitangents[index + 1]; 298 bitangent.z = bitangents[index + 2]; 299 300 instance.triNormals[0].set(normal); 301 instance.triNormals[1].set(tangent); 302 instance.triNormals[2].set(bitangent); 303 MeshUtil.fixTSpace(instance.triNormals); 304 buildVSQuat(instance.triNormals, instance.quat); 305 306 vertexBuffer[vbIndex + 5] = instance.quat.x; 307 vertexBuffer[vbIndex + 6] = instance.quat.y; 308 vertexBuffer[vbIndex + 7] = instance.quat.z; 309 vertexBuffer[vbIndex + 8] = instance.quat.w; 310 } 311 } 312 } 313 314 // Build PointNormalTexCoordGeometry 315 private boolean doBuildPNTGeometry(float[] points, float[] normals, 316 float[] texCoords, int[] faces) { 317 318 if (point2vbMap == null) { 319 point2vbMap = new HashMap(); 320 } else { 321 point2vbMap.clear(); 322 } 323 if (normal2vbMap == null) { 324 normal2vbMap = new HashMap(); 325 } else { 326 normal2vbMap.clear(); 327 } 328 if (texCoord2vbMap == null) { 329 texCoord2vbMap = new HashMap(); 330 } else { 331 texCoord2vbMap.clear(); 332 } 333 334 int vertexIndexSize = VertexFormat.POINT_NORMAL_TEXCOORD.getVertexIndexSize(); 335 int faceIndexSize = vertexIndexSize * 3; 336 int pointIndexOffset = VertexFormat.POINT_NORMAL_TEXCOORD.getPointIndexOffset(); 337 int normalIndexOffset = VertexFormat.POINT_NORMAL_TEXCOORD.getNormalIndexOffset(); 338 int texCoordIndexOffset = VertexFormat.POINT_NORMAL_TEXCOORD.getTexCoordIndexOffset(); 339 340 int numPoints = points.length / POINT_SIZE; 341 int numNormals = normals.length / NORMAL_SIZE; 342 int numTexCoords = texCoords.length / TEXCOORD_SIZE; 343 int numFaces = faces.length / faceIndexSize; 344 assert numPoints > 0 && numNormals > 0 && numTexCoords > 0 && numFaces > 0; 345 346 Integer mf2vb; 347 BaseMesh.MeshGeomComp2VB mp2vb; 348 BaseMesh.MeshGeomComp2VB mn2vb; 349 BaseMesh.MeshGeomComp2VB mt2vb; 350 // Allocate an initial size, may grow as we process the faces array. 351 cachedNormals = new float[numPoints * NORMAL_SIZE]; 352 cachedTangents = new float[numPoints * NORMAL_SIZE]; 353 cachedBitangents = new float[numPoints * NORMAL_SIZE]; 354 vertexBuffer = new float[numPoints * VERTEX_SIZE_VB]; 355 indexBuffer = new int[numFaces * 3]; 356 int ibCount = 0; 357 int vbCount = 0; 358 359 MeshTempState instance = MeshTempState.getInstance(); 360 for (int i = 0; i < 3; i++) { 361 if (instance.triPoints[i] == null) { 362 instance.triPoints[i] = new Vec3f(); 363 } 364 if (instance.triTexCoords[i] == null) { 365 instance.triTexCoords[i] = new Vec2f(); 366 } 367 } 368 369 for (int faceCount = 0; faceCount < numFaces; faceCount++) { 370 int faceIndex = faceCount * faceIndexSize; 371 for (int i = 0; i < 3; i++) { 372 int vertexIndex = faceIndex + (i * vertexIndexSize); 373 int pointIndex = vertexIndex + pointIndexOffset; 374 int normalIndex = vertexIndex + normalIndexOffset; 375 int texCoordIndex = vertexIndex + texCoordIndexOffset; 376 377 mf2vb = vbCount / VERTEX_SIZE_VB; 378 379 if (vertexBuffer.length <= vbCount) { 380 final int incrementedSize = vbCount + 20; // Let's increment by 20 381 float[] temp = new float[incrementedSize * VERTEX_SIZE_VB]; 382 System.arraycopy(vertexBuffer, 0, temp, 0, vertexBuffer.length); 383 vertexBuffer = temp; 384 // Enlarge cachedNormals, cachedTangents and cachedBitangents too 385 temp = new float[incrementedSize * 3]; 386 System.arraycopy(cachedNormals, 0, temp, 0, cachedNormals.length); 387 cachedNormals = temp; 388 temp = new float[incrementedSize * 3]; 389 System.arraycopy(cachedTangents, 0, temp, 0, cachedTangents.length); 390 cachedTangents = temp; 391 temp = new float[incrementedSize * 3]; 392 System.arraycopy(cachedBitangents, 0, temp, 0, cachedBitangents.length); 393 cachedBitangents = temp; 394 } 395 int pointOffset = faces[pointIndex] * POINT_SIZE; 396 int normalOffset = faces[normalIndex] * NORMAL_SIZE; 397 int texCoordOffset = faces[texCoordIndex] * TEXCOORD_SIZE; 398 399 // Save the vertex of triangle 400 instance.triPointIndex[i] = pointOffset; 401 instance.triTexCoordIndex[i] = texCoordOffset; 402 instance.triVerts[i] = vbCount / VERTEX_SIZE_VB; 403 404 vertexBuffer[vbCount] = points[pointOffset]; 405 vertexBuffer[vbCount + 1] = points[pointOffset + 1]; 406 vertexBuffer[vbCount + 2] = points[pointOffset + 2]; 407 vertexBuffer[vbCount + 3] = texCoords[texCoordOffset]; 408 vertexBuffer[vbCount + 4] = texCoords[texCoordOffset + 1]; 409 // Store the normal in the cachedNormals array 410 int index = instance.triVerts[i] * NORMAL_SIZE; 411 cachedNormals[index] = normals[normalOffset]; 412 cachedNormals[index + 1] = normals[normalOffset + 1]; 413 cachedNormals[index + 2] = normals[normalOffset + 2]; 414 415 vbCount += VERTEX_SIZE_VB; 416 417 mp2vb = point2vbMap.get(pointOffset); 418 if (mp2vb == null) { 419 // create 420 mp2vb = new MeshGeomComp2VB(pointOffset, mf2vb); 421 point2vbMap.put(pointOffset, mp2vb); 422 } else { 423 // addLoc 424 mp2vb.addLoc(mf2vb); 425 } 426 427 mn2vb = normal2vbMap.get(normalOffset); 428 if (mn2vb == null) { 429 // create 430 mn2vb = new MeshGeomComp2VB(normalOffset, mf2vb); 431 normal2vbMap.put(normalOffset, mn2vb); 432 } else { 433 // addLoc 434 mn2vb.addLoc(mf2vb); 435 } 436 437 mt2vb = texCoord2vbMap.get(texCoordOffset); 438 if (mt2vb == null) { 439 // create 440 mt2vb = new MeshGeomComp2VB(texCoordOffset, mf2vb); 441 texCoord2vbMap.put(texCoordOffset, mt2vb); 442 } else { 443 // addLoc 444 mt2vb.addLoc(mf2vb); 445 } 446 447 // Construct IndexBuffer 448 indexBuffer[ibCount++] = mf2vb; 449 } 450 451 // This is the best time to compute the tangent and bitangent for each 452 // of the vertex. Go thro. the 3 vertices of a triangle 453 for (int i = 0; i < 3; i++) { 454 instance.triPoints[i].x = points[instance.triPointIndex[i]]; 455 instance.triPoints[i].y = points[instance.triPointIndex[i] + 1]; 456 instance.triPoints[i].z = points[instance.triPointIndex[i] + 2]; 457 instance.triTexCoords[i].x = texCoords[instance.triTexCoordIndex[i]]; 458 instance.triTexCoords[i].y = texCoords[instance.triTexCoordIndex[i] + 1]; 459 } 460 461 MeshUtil.computeTBNNormalized(instance.triPoints[0], instance.triPoints[1], 462 instance.triPoints[2], instance.triTexCoords[0], 463 instance.triTexCoords[1], instance.triTexCoords[2], 464 instance.triNormals); 465 466 for (int i = 0; i < 3; i++) { 467 int index = instance.triVerts[i] * NORMAL_SIZE; 468 cachedTangents[index] = instance.triNormals[1].x; 469 cachedTangents[index + 1] = instance.triNormals[1].y; 470 cachedTangents[index + 2] = instance.triNormals[1].z; 471 cachedBitangents[index] = instance.triNormals[2].x; 472 cachedBitangents[index + 1] = instance.triNormals[2].y; 473 cachedBitangents[index + 2] = instance.triNormals[2].z; 474 } 475 476 } 477 478 numberOfVertices = vbCount / VERTEX_SIZE_VB; 479 480 convertNormalsToQuats(instance, numberOfVertices, 481 cachedNormals, cachedTangents, cachedBitangents, vertexBuffer, null); 482 483 indexBufferSize = numFaces * 3; 484 485 if (numberOfVertices > 0x10000) { // > 64K 486 return buildNativeGeometry(vertexBuffer, 487 numberOfVertices * VERTEX_SIZE_VB, indexBuffer, indexBufferSize); 488 } else { 489 490 if (indexBufferShort == null || indexBufferShort.length < indexBufferSize) { 491 indexBufferShort = new short[indexBufferSize]; 492 } 493 int ii = 0; 494 for (int i = 0; i < numFaces; i++) { 495 indexBufferShort[ii] = (short) indexBuffer[ii++]; 496 indexBufferShort[ii] = (short) indexBuffer[ii++]; 497 indexBufferShort[ii] = (short) indexBuffer[ii++]; 498 } 499 indexBuffer = null; // free 500 return buildNativeGeometry(vertexBuffer, 501 numberOfVertices * VERTEX_SIZE_VB, indexBufferShort, indexBufferSize); 502 } 503 } 504 505 // Update PointNormalTexCoordGeometry 506 private boolean updatePNTGeometry(float[] points, int[] pointsFromAndLengthIndices, 507 float[] normals, int[] normalsFromAndLengthIndices, 508 float[] texCoords, int[] texCoordsFromAndLengthIndices) { 509 510 if (dirtyVertices == null) { 511 // Create a dirty array of size equal to number of vertices in vertexBuffer. 512 dirtyVertices = new boolean[numberOfVertices]; 513 } 514 // Clear dirty array before use. 515 Arrays.fill(dirtyVertices, false); 516 517 // Find out the list of modified points 518 int startPoint = pointsFromAndLengthIndices[0] / POINT_SIZE; 519 int numPoints = (pointsFromAndLengthIndices[1] / POINT_SIZE); 520 if ((pointsFromAndLengthIndices[1] % POINT_SIZE) > 0) { 521 numPoints++; 522 } 523 if (numPoints > 0) { 524 for (int i = 0; i < numPoints; i++) { 525 int pointOffset = (startPoint + i) * POINT_SIZE; 526 MeshGeomComp2VB mp2vb = (MeshGeomComp2VB) point2vbMap.get(pointOffset); 527 assert mp2vb != null; 528 // mp2vb shouldn't be null. We can't have a point referred by 529 // the faces array that isn't in the vertexBuffer. 530 if (mp2vb != null) { 531 int[] locs = mp2vb.getLocs(); 532 int validLocs = mp2vb.getValidLocs(); 533 if (locs != null) { 534 for (int j = 0; j < validLocs; j++) { 535 int vbIndex = locs[j] * VERTEX_SIZE_VB; 536 vertexBuffer[vbIndex] = points[pointOffset]; 537 vertexBuffer[vbIndex + 1] = points[pointOffset + 1]; 538 vertexBuffer[vbIndex + 2] = points[pointOffset + 2]; 539 dirtyVertices[locs[j]] = true; 540 } 541 } else { 542 int loc = mp2vb.getLoc(); 543 int vbIndex = loc * VERTEX_SIZE_VB; 544 vertexBuffer[vbIndex] = points[pointOffset]; 545 vertexBuffer[vbIndex + 1] = points[pointOffset + 1]; 546 vertexBuffer[vbIndex + 2] = points[pointOffset + 2]; 547 dirtyVertices[loc] = true; 548 } 549 } 550 } 551 } 552 553 // Find out the list of modified tex coords. 554 int startTexCoord = texCoordsFromAndLengthIndices[0] / TEXCOORD_SIZE; 555 int numTexCoords = (texCoordsFromAndLengthIndices[1] / TEXCOORD_SIZE); 556 if ((texCoordsFromAndLengthIndices[1] % TEXCOORD_SIZE) > 0) { 557 numTexCoords++; 558 } 559 if (numTexCoords > 0) { 560 for (int i = 0; i < numTexCoords; i++) { 561 int texCoordOffset = (startTexCoord + i) * TEXCOORD_SIZE; 562 MeshGeomComp2VB mt2vb = (MeshGeomComp2VB) texCoord2vbMap.get(texCoordOffset); 563 assert mt2vb != null; 564 // mt2vb shouldn't be null. We can't have a texCoord referred by 565 // the faces array that isn't in the vertexBuffer. 566 if (mt2vb != null) { 567 int[] locs = mt2vb.getLocs(); 568 int validLocs = mt2vb.getValidLocs(); 569 if (locs != null) { 570 for (int j = 0; j < validLocs; j++) { 571 int vbIndex = (locs[j] * VERTEX_SIZE_VB) + POINT_SIZE_VB; 572 vertexBuffer[vbIndex] = texCoords[texCoordOffset]; 573 vertexBuffer[vbIndex + 1] = texCoords[texCoordOffset + 1]; 574 dirtyVertices[locs[j]] = true; 575 } 576 } else { 577 int loc = mt2vb.getLoc(); 578 int vbIndex = (loc * VERTEX_SIZE_VB) + POINT_SIZE_VB; 579 vertexBuffer[vbIndex] = texCoords[texCoordOffset]; 580 vertexBuffer[vbIndex + 1] = texCoords[texCoordOffset + 1]; 581 dirtyVertices[loc] = true; 582 } 583 } 584 } 585 } 586 587 // Find out the list of modified normals 588 int startNormal = normalsFromAndLengthIndices[0] / NORMAL_SIZE; 589 int numNormals = (normalsFromAndLengthIndices[1] / NORMAL_SIZE); 590 if ((normalsFromAndLengthIndices[1] % NORMAL_SIZE) > 0) { 591 numNormals++; 592 } 593 if (numNormals > 0) { 594 MeshTempState instance = MeshTempState.getInstance(); 595 for (int i = 0; i < numNormals; i++) { 596 int normalOffset = (startNormal + i) * NORMAL_SIZE; 597 MeshGeomComp2VB mn2vb = (MeshGeomComp2VB) normal2vbMap.get(normalOffset); 598 assert mn2vb != null; 599 // mn2vb shouldn't be null. We can't have a normal referred by 600 // the faces array that isn't in the vertexBuffer. 601 if (mn2vb != null) { 602 int[] locs = mn2vb.getLocs(); 603 int validLocs = mn2vb.getValidLocs(); 604 if (locs != null) { 605 for (int j = 0; j < validLocs; j++) { 606 int index = locs[j] * NORMAL_SIZE; 607 cachedNormals[index] = normals[normalOffset]; 608 cachedNormals[index + 1] = normals[normalOffset + 1]; 609 cachedNormals[index + 2] = normals[normalOffset + 2]; 610 dirtyVertices[locs[j]] = true; 611 } 612 } else { 613 int loc = mn2vb.getLoc(); 614 int index = loc * NORMAL_SIZE; 615 cachedNormals[index] = normals[normalOffset]; 616 cachedNormals[index + 1] = normals[normalOffset + 1]; 617 cachedNormals[index + 2] = normals[normalOffset + 2]; 618 dirtyVertices[loc] = true; 619 } 620 } 621 } 622 } 623 624 // Prepare process all dirty vertices 625 MeshTempState instance = MeshTempState.getInstance(); 626 for (int i = 0; i < 3; i++) { 627 if (instance.triPoints[i] == null) { 628 instance.triPoints[i] = new Vec3f(); 629 } 630 if (instance.triTexCoords[i] == null) { 631 instance.triTexCoords[i] = new Vec2f(); 632 } 633 } 634 // Every 3 vertices form a triangle 635 for (int j = 0; j < numberOfVertices; j += 3) { 636 // Only process the triangle that has one of more dirty vertices 637 if (dirtyVertices[j] || dirtyVertices[j+1] || dirtyVertices[j+2]) { 638 int vbIndex = j * VERTEX_SIZE_VB; 639 // Go thro. the 3 vertices of a triangle 640 for (int i = 0; i < 3; i++) { 641 instance.triPoints[i].x = vertexBuffer[vbIndex]; 642 instance.triPoints[i].y = vertexBuffer[vbIndex + 1]; 643 instance.triPoints[i].z = vertexBuffer[vbIndex + 2]; 644 instance.triTexCoords[i].x = vertexBuffer[vbIndex + POINT_SIZE_VB]; 645 instance.triTexCoords[i].y = vertexBuffer[vbIndex + POINT_SIZE_VB + 1]; 646 vbIndex += VERTEX_SIZE_VB; 647 } 648 649 MeshUtil.computeTBNNormalized(instance.triPoints[0], instance.triPoints[1], 650 instance.triPoints[2], instance.triTexCoords[0], 651 instance.triTexCoords[1], instance.triTexCoords[2], 652 instance.triNormals); 653 654 int index = j * NORMAL_SIZE; 655 for (int i = 0; i < 3; i++) { 656 cachedTangents[index] = instance.triNormals[1].x; 657 cachedTangents[index + 1] = instance.triNormals[1].y; 658 cachedTangents[index + 2] = instance.triNormals[1].z; 659 cachedBitangents[index] = instance.triNormals[2].x; 660 cachedBitangents[index + 1] = instance.triNormals[2].y; 661 cachedBitangents[index + 2] = instance.triNormals[2].z; 662 index += NORMAL_SIZE; 663 } 664 665 } 666 } 667 668 convertNormalsToQuats(instance, numberOfVertices, 669 cachedNormals, cachedTangents, cachedBitangents, vertexBuffer, dirtyVertices); 670 671 if (indexBuffer != null) { 672 return buildNativeGeometry(vertexBuffer, 673 numberOfVertices * VERTEX_SIZE_VB, indexBuffer, indexBufferSize); 674 } else { 675 return buildNativeGeometry(vertexBuffer, 676 numberOfVertices * VERTEX_SIZE_VB, indexBufferShort, indexBufferSize); 677 } 678 679 } 680 681 @Override 682 public boolean buildGeometry(boolean userDefinedNormals, 683 float[] points, int[] pointsFromAndLengthIndices, 684 float[] normals, int[] normalsFromAndLengthIndices, 685 float[] texCoords, int[] texCoordsFromAndLengthIndices, 686 int[] faces, int[] facesFromAndLengthIndices, 687 int[] faceSmoothingGroups, int[] faceSmoothingGroupsFromAndLengthIndices) { 688 if (userDefinedNormals) { 689 return buildPNTGeometry(points, pointsFromAndLengthIndices, 690 normals, normalsFromAndLengthIndices, 691 texCoords, texCoordsFromAndLengthIndices, 692 faces, facesFromAndLengthIndices); 693 } else { 694 return buildPTGeometry(points, pointsFromAndLengthIndices, 695 texCoords, texCoordsFromAndLengthIndices, 696 faces, facesFromAndLengthIndices, 697 faceSmoothingGroups, faceSmoothingGroupsFromAndLengthIndices); 698 } 699 } 700 701 // Build PointNormalTexCoordGeometry 702 private boolean buildPNTGeometry( 703 float[] points, int[] pointsFromAndLengthIndices, 704 float[] normals, int[] normalsFromAndLengthIndices, 705 float[] texCoords, int[] texCoordsFromAndLengthIndices, 706 int[] faces, int[] facesFromAndLengthIndices) { 707 708 boolean updatePoints = pointsFromAndLengthIndices[1] > 0; 709 boolean updateNormals = normalsFromAndLengthIndices[1] > 0; 710 boolean updateTexCoords = texCoordsFromAndLengthIndices[1] > 0; 711 boolean updateFaces = facesFromAndLengthIndices[1] > 0; 712 713 // First time creation 714 boolean buildGeom = !(updatePoints || updateNormals || updateTexCoords || updateFaces); 715 716 // We will need to rebuild geom buffers if there is a change to faces 717 if (updateFaces) { 718 buildGeom = true; 719 } 720 721 if ((!buildGeom) && (vertexBuffer != null) 722 && ((indexBuffer != null) || (indexBufferShort != null))) { 723 return updatePNTGeometry(points, pointsFromAndLengthIndices, 724 normals, normalsFromAndLengthIndices, 725 texCoords, texCoordsFromAndLengthIndices); 726 } 727 return doBuildPNTGeometry(points, normals, texCoords, faces); 728 729 } 730 731 // Build PointTexCoordGeometry 732 private boolean buildPTGeometry(float[] pos, int[] posFromAndLengthIndices, 733 float[] uv, int[] uvFromAndLengthIndices, 734 int[] faces, int[] facesFromAndLengthIndices, 735 int[] smoothing, int[] smoothingFromAndLengthIndices) { 736 nVerts = pos.length / 3; 737 nTVerts = uv.length / 2; 738 nFaces = faces.length / (VertexFormat.POINT_TEXCOORD.getVertexIndexSize() * 3); 739 assert nVerts > 0 && nFaces > 0 && nTVerts > 0; 740 this.pos = pos; 741 this.uv = uv; 742 this.faces = faces; 743 this.smoothing = smoothing.length == nFaces ? smoothing : null; 744 745 if (PrismSettings.skipMeshNormalComputation) { 746 boolean updatePoints = posFromAndLengthIndices[1] > 0; 747 boolean updateTexCoords = uvFromAndLengthIndices[1] > 0; 748 boolean updateFaces = facesFromAndLengthIndices[1] > 0; 749 boolean updateSmoothing = smoothingFromAndLengthIndices[1] > 0; 750 751 // First time creation 752 boolean buildGeom = !(updatePoints || updateTexCoords || updateFaces || updateSmoothing); 753 754 // We will need to rebuild if there is a change to faces or smoothing 755 if (updateFaces || updateSmoothing) { 756 buildGeom = true; 757 } 758 759 if ((!buildGeom) && (vertexBuffer != null) 760 && ((indexBuffer != null) || (indexBufferShort != null))) { 761 return updateSkipMeshNormalGeometry(posFromAndLengthIndices, uvFromAndLengthIndices); 762 } 763 764 return buildSkipMeshNormalGeometry(); 765 } 766 767 MeshTempState instance = MeshTempState.getInstance(); 768 // big pool for all possible vertices 769 if (instance.pool == null || instance.pool.length < nFaces * 3) { 770 instance.pool = new MeshVertex[nFaces * 3]; 771 } 772 773 if (instance.indexBuffer == null || instance.indexBuffer.length < nFaces * 3) { 774 instance.indexBuffer = new int[nFaces * 3]; 775 } 776 777 if (instance.pVertex == null || instance.pVertex.length < nVerts) { 778 instance.pVertex = new MeshVertex[nVerts]; 779 } else { 780 Arrays.fill(instance.pVertex, 0, instance.pVertex.length, null); 781 } 782 783 // check if all hard edges or all smooth 784 checkSmoothingGroup(); 785 786 // compute [N, T, B] for each face 787 computeTBNormal(instance.pool, instance.pVertex, instance.indexBuffer); 788 789 // process sm and weld points 790 int nNewVerts = MeshVertex.processVertices(instance.pVertex, nVerts, 791 allHardEdges, allSameSmoothing); 792 793 if (instance.vertexBuffer == null 794 || instance.vertexBuffer.length < nNewVerts * VERTEX_SIZE_VB) { 795 instance.vertexBuffer = new float[nNewVerts * VERTEX_SIZE_VB]; 796 } 797 buildVertexBuffer(instance.pVertex, instance.vertexBuffer); 798 799 if (nNewVerts > 0x10000) { 800 buildIndexBuffer(instance.pool, instance.indexBuffer, null); 801 return buildNativeGeometry(instance.vertexBuffer, 802 nNewVerts * VERTEX_SIZE_VB, instance.indexBuffer, nFaces * 3); 803 } else { 804 if (instance.indexBufferShort == null || instance.indexBufferShort.length < nFaces * 3) { 805 instance.indexBufferShort = new short[nFaces * 3]; 806 } 807 buildIndexBuffer(instance.pool, instance.indexBuffer, instance.indexBufferShort); 808 return buildNativeGeometry(instance.vertexBuffer, 809 nNewVerts * VERTEX_SIZE_VB, instance.indexBufferShort, nFaces * 3); 810 } 811 } 812 813 private void computeTBNormal(MeshVertex[] pool, MeshVertex[] pVertex, int[] indexBuffer) { 814 MeshTempState instance = MeshTempState.getInstance(); 815 816 // tmp variables 817 int[] smFace = instance.smFace; 818 int[] triVerts = instance.triVerts; 819 Vec3f[] triPoints = instance.triPoints; 820 Vec2f[] triTexCoords = instance.triTexCoords; 821 Vec3f[] triNormals = instance.triNormals; 822 final String logname = BaseMesh.class.getName(); 823 824 for (int f = 0, nDeadFaces = 0, poolIndex = 0; f < nFaces; f++) { 825 int index = f * 3; 826 827 smFace = getFace(f, smFace); // copy from mesh to tmp smFace 828 829 // Get tex. point. index 830 triVerts[0] = smFace[BaseMesh.FaceMembers.POINT0.ordinal()]; 831 triVerts[1] = smFace[BaseMesh.FaceMembers.POINT1.ordinal()]; 832 triVerts[2] = smFace[BaseMesh.FaceMembers.POINT2.ordinal()]; 833 834 if (MeshUtil.isDeadFace(triVerts) 835 && PlatformLogger.getLogger(logname).isLoggable(PlatformLogger.Level.FINE)) { 836 // Log degenerated triangle 837 nDeadFaces++; 838 PlatformLogger.getLogger(logname).fine("Dead face [" 839 + triVerts[0] + ", " + triVerts[1] + ", " + triVerts[2] 840 + "] @ face group " + f + "; nEmptyFaces = " + nDeadFaces); 841 } 842 843 for (int i = 0; i < 3; i++) { 844 triPoints[i] = getVertex(triVerts[i], triPoints[i]); 845 } 846 847 // Get tex. coord. index 848 triVerts[0] = smFace[BaseMesh.FaceMembers.TEXCOORD0.ordinal()]; 849 triVerts[1] = smFace[BaseMesh.FaceMembers.TEXCOORD1.ordinal()]; 850 triVerts[2] = smFace[BaseMesh.FaceMembers.TEXCOORD2.ordinal()]; 851 852 for (int i = 0; i < 3; i++) { 853 triTexCoords[i] = getTVertex(triVerts[i], triTexCoords[i]); 854 } 855 856 MeshUtil.computeTBNNormalized(triPoints[0], triPoints[1], triPoints[2], 857 triTexCoords[0], triTexCoords[1], triTexCoords[2], 858 triNormals); 859 860 for (int j = 0; j < 3; ++j) { 861 pool[poolIndex] = (pool[poolIndex] == null) ? new MeshVertex() : pool[poolIndex]; 862 863 for (int i = 0; i < 3; ++i) { 864 pool[poolIndex].norm[i].set(triNormals[i]); 865 } 866 pool[poolIndex].smGroup = smFace[BaseMesh.FaceMembers.SMOOTHING_GROUP.ordinal()]; 867 pool[poolIndex].fIdx = f; 868 pool[poolIndex].tVert = triVerts[j]; 869 pool[poolIndex].index = MeshVertex.IDX_UNDEFINED; 870 int ii = j == 0 ? BaseMesh.FaceMembers.POINT0.ordinal() 871 : j == 1 ? BaseMesh.FaceMembers.POINT1.ordinal() 872 : BaseMesh.FaceMembers.POINT2.ordinal(); 873 int pIdx = smFace[ii]; 874 pool[poolIndex].pVert = pIdx; 875 indexBuffer[index + j] = pIdx; 876 pool[poolIndex].next = pVertex[pIdx]; 877 pVertex[pIdx] = pool[poolIndex]; 878 poolIndex++; 879 } 880 } 881 } 882 883 private void buildVSQuat(Vec3f[] tm, Quat4f quat) { 884 Vec3f v = MeshTempState.getInstance().vec3f1; 885 v.cross(tm[1], tm[2]); 886 float d = tm[0].dot(v); 887 if (d < 0) { 888 tm[2].mul(-1); 889 } 890 891 MeshUtil.buildQuat(tm, quat); 892 893 // This will interfer with degenerated triangle unit test. 894 // assert (quat.w >= 0); 895 896 if (d < 0) { 897 if (quat.w == 0) { 898 quat.w = MeshUtil.MAGIC_SMALL; 899 } 900 quat.scale(-1); 901 } 902 } 903 904 private void buildVertexBuffer(MeshVertex[] pVerts, float[] vertexBuffer) { 905 Quat4f quat = MeshTempState.getInstance().quat; 906 int idLast = 0; 907 908 for (int i = 0, index = 0; i < nVerts; ++i) { 909 MeshVertex v = pVerts[i]; 910 for (; v != null; v = v.next) { 911 if (v.index == idLast) { 912 int ind = v.pVert * 3; 913 vertexBuffer[index++] = pos[ind]; 914 vertexBuffer[index++] = pos[ind + 1]; 915 vertexBuffer[index++] = pos[ind + 2]; 916 ind = v.tVert * 2; 917 vertexBuffer[index++] = uv[ind]; 918 vertexBuffer[index++] = uv[ind + 1]; 919 buildVSQuat(v.norm, quat); 920 vertexBuffer[index++] = quat.x; 921 vertexBuffer[index++] = quat.y; 922 vertexBuffer[index++] = quat.z; 923 vertexBuffer[index++] = quat.w; 924 idLast++; 925 } 926 } 927 } 928 } 929 930 private void buildIndexBuffer(MeshVertex[] pool, int[] indexBuffer, short[] indexBufferShort) { 931 for (int i = 0; i < nFaces; ++i) { 932 int index = i * 3; 933 if (indexBuffer[index] != MeshVertex.IDX_UNDEFINED) { 934 for (int j = 0; j < 3; ++j) { 935 assert (pool[index].fIdx == i); 936 if (indexBufferShort != null) { 937 indexBufferShort[index + j] = (short) pool[index + j].index; 938 } else { 939 indexBuffer[index + j] = pool[index + j].index; 940 } 941 pool[index + j].next = null; // release reference 942 } 943 } else { 944 for (int j = 0; j < 3; ++j) { 945 if (indexBufferShort != null) { 946 indexBufferShort[index + j] = 0; 947 } else { 948 indexBuffer[index + j] = 0; 949 } 950 } 951 } 952 } 953 } 954 955 public int getNumVerts() { 956 return nVerts; 957 } 958 959 public int getNumTVerts() { 960 return nTVerts; 961 } 962 963 public int getNumFaces() { 964 return nFaces; 965 } 966 967 public Vec3f getVertex(int pIdx, Vec3f vertex) { 968 if (vertex == null) { 969 vertex = new Vec3f(); 970 } 971 int index = pIdx * 3; 972 vertex.set(pos[index], pos[index + 1], pos[index + 2]); 973 return vertex; 974 } 975 976 public Vec2f getTVertex(int tIdx, Vec2f texCoord) { 977 if (texCoord == null) { 978 texCoord = new Vec2f(); 979 } 980 int index = tIdx * 2; 981 texCoord.set(uv[index], uv[index + 1]); 982 return texCoord; 983 } 984 985 private void checkSmoothingGroup() { 986 if (smoothing == null || smoothing.length == 0) { // all smooth 987 allSameSmoothing = true; 988 allHardEdges = false; 989 return; 990 } 991 992 for (int i = 0; i + 1 < smoothing.length; i++) { 993 if (smoothing[i] != smoothing[i + 1]) { 994 // various SmGroup 995 allSameSmoothing = false; 996 allHardEdges = false; 997 return; 998 } 999 } 1000 1001 if (smoothing[0] == 0) { // all hard edges 1002 allSameSmoothing = false; 1003 allHardEdges = true; 1004 } else { // all belongs to one group == all smooth 1005 allSameSmoothing = true; 1006 allHardEdges = false; 1007 } 1008 } 1009 1010 public int[] getFace(int fIdx, int[] face) { 1011 int index = fIdx * 6; 1012 if ((face == null) || (face.length < FACE_MEMBERS_SIZE)) { 1013 face = new int[FACE_MEMBERS_SIZE]; 1014 } 1015 // Note: Order matter, [0, 5] == FaceMembers' points and texcoords 1016 for (int i = 0; i < 6; i++) { 1017 face[i] = faces[index + i]; 1018 } 1019 // Note: Order matter, 6 == FaceMembers.SMOOTHING_GROUP.ordinal() 1020 // There is a total of 32 smoothing groups. 1021 // Assign to 1st smoothing group if smoothing is null. 1022 face[6] = smoothing != null ? smoothing[fIdx] : 1; 1023 return face; 1024 } 1025 1026 class MeshGeomComp2VB { 1027 1028 private final int key; // point or texCoord index 1029 private final int loc; // the first index into vertex buffer 1030 private int[] locs; 1031 private int validLocs; 1032 1033 MeshGeomComp2VB(int key, int loc) { 1034 assert loc >= 0; 1035 this.key = key; 1036 this.loc = loc; 1037 locs = null; 1038 validLocs = 0; 1039 } 1040 1041 void addLoc(int loc) { 1042 if (locs == null) { 1043 locs = new int[3]; // edge of mesh case 1044 locs[0] = this.loc; 1045 locs[1] = loc; 1046 this.validLocs = 2; 1047 } else if (locs.length > validLocs) { 1048 locs[validLocs] = loc; 1049 validLocs++; 1050 } else { 1051 int[] temp = new int[validLocs * 2]; 1052 System.arraycopy(locs, 0, temp, 0, locs.length); 1053 locs = temp; 1054 locs[validLocs] = loc; 1055 validLocs++; 1056 } 1057 } 1058 1059 int getKey() { 1060 return key; 1061 } 1062 1063 int getLoc() { 1064 return loc; 1065 } 1066 1067 int[] getLocs() { 1068 return locs; 1069 } 1070 1071 int getValidLocs() { 1072 return validLocs; 1073 } 1074 } 1075 1076 }