1 /* 2 * Copyright (c) 2013, 2017, 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[] dirtyVertices; 82 private float[] cachedNormals; 83 private float[] cachedTangents; 84 private float[] cachedBitangents; 85 private float[] vertexBuffer; 86 private int[] indexBuffer; 87 private short[] indexBufferShort; 88 private int indexBufferSize; 89 private int numberOfVertices; 90 91 private HashMap<Integer, MeshGeomComp2VB> point2vbMap; 92 private HashMap<Integer, MeshGeomComp2VB> normal2vbMap; 93 private HashMap<Integer, MeshGeomComp2VB> texCoord2vbMap; 94 95 private void convertNormalsToQuats(MeshTempState instance, int numberOfVertices, 96 float[] normals, float[] tangents, float[] bitangents, 97 float[] vertexBuffer, boolean[] dirtys) { 98 99 Vec3f normal = instance.vec3f1; 100 Vec3f tangent = instance.vec3f2; 101 Vec3f bitangent = instance.vec3f3; 102 for (int i = 0, vbIndex = 0; i < numberOfVertices; i++, vbIndex += VERTEX_SIZE_VB) { 103 // Note: If dirtys isn't null, dirtys.length == numberOfVertices is true 104 if (dirtys == null || dirtys[i]) { 105 int index = i * NORMAL_SIZE; 106 107 normal.x = normals[index]; 108 normal.y = normals[index + 1]; 109 normal.z = normals[index + 2]; 110 normal.normalize(); 111 112 // tangent and bitangent have been normalized. 113 tangent.x = tangents[index]; 114 tangent.y = tangents[index + 1]; 115 tangent.z = tangents[index + 2]; 116 bitangent.x = bitangents[index]; 117 bitangent.y = bitangents[index + 1]; 118 bitangent.z = bitangents[index + 2]; 119 120 instance.triNormals[0].set(normal); 121 instance.triNormals[1].set(tangent); 122 instance.triNormals[2].set(bitangent); 123 MeshUtil.fixTSpace(instance.triNormals); 124 buildVSQuat(instance.triNormals, instance.quat); 125 126 vertexBuffer[vbIndex + 5] = instance.quat.x; 127 vertexBuffer[vbIndex + 6] = instance.quat.y; 128 vertexBuffer[vbIndex + 7] = instance.quat.z; 129 vertexBuffer[vbIndex + 8] = instance.quat.w; 130 } 131 } 132 } 133 134 // Build PointNormalTexCoordGeometry 135 private boolean doBuildPNTGeometry(float[] points, float[] normals, 136 float[] texCoords, int[] faces) { 137 138 if (point2vbMap == null) { 139 point2vbMap = new HashMap(); 140 } else { 141 point2vbMap.clear(); 142 } 143 if (normal2vbMap == null) { 144 normal2vbMap = new HashMap(); 145 } else { 146 normal2vbMap.clear(); 147 } 148 if (texCoord2vbMap == null) { 149 texCoord2vbMap = new HashMap(); 150 } else { 151 texCoord2vbMap.clear(); 152 } 153 154 int vertexIndexSize = VertexFormat.POINT_NORMAL_TEXCOORD.getVertexIndexSize(); 155 int faceIndexSize = vertexIndexSize * 3; 156 int pointIndexOffset = VertexFormat.POINT_NORMAL_TEXCOORD.getPointIndexOffset(); 157 int normalIndexOffset = VertexFormat.POINT_NORMAL_TEXCOORD.getNormalIndexOffset(); 158 int texCoordIndexOffset = VertexFormat.POINT_NORMAL_TEXCOORD.getTexCoordIndexOffset(); 159 160 int numPoints = points.length / POINT_SIZE; 161 int numNormals = normals.length / NORMAL_SIZE; 162 int numTexCoords = texCoords.length / TEXCOORD_SIZE; 163 int numFaces = faces.length / faceIndexSize; 164 assert numPoints > 0 && numNormals > 0 && numTexCoords > 0 && numFaces > 0; 165 166 Integer mf2vb; 167 BaseMesh.MeshGeomComp2VB mp2vb; 168 BaseMesh.MeshGeomComp2VB mn2vb; 169 BaseMesh.MeshGeomComp2VB mt2vb; 170 // Allocate an initial size, may grow as we process the faces array. 171 cachedNormals = new float[numPoints * NORMAL_SIZE]; 172 cachedTangents = new float[numPoints * NORMAL_SIZE]; 173 cachedBitangents = new float[numPoints * NORMAL_SIZE]; 174 vertexBuffer = new float[numPoints * VERTEX_SIZE_VB]; 175 indexBuffer = new int[numFaces * 3]; 176 int ibCount = 0; 177 int vbCount = 0; 178 179 MeshTempState instance = MeshTempState.getInstance(); 180 for (int i = 0; i < 3; i++) { 181 if (instance.triPoints[i] == null) { 182 instance.triPoints[i] = new Vec3f(); 183 } 184 if (instance.triTexCoords[i] == null) { 185 instance.triTexCoords[i] = new Vec2f(); 186 } 187 } 188 189 for (int faceCount = 0; faceCount < numFaces; faceCount++) { 190 int faceIndex = faceCount * faceIndexSize; 191 for (int i = 0; i < 3; i++) { 192 int vertexIndex = faceIndex + (i * vertexIndexSize); 193 int pointIndex = vertexIndex + pointIndexOffset; 194 int normalIndex = vertexIndex + normalIndexOffset; 195 int texCoordIndex = vertexIndex + texCoordIndexOffset; 196 197 mf2vb = vbCount / VERTEX_SIZE_VB; 198 199 if (vertexBuffer.length <= vbCount) { 200 int numVertices = vbCount / VERTEX_SIZE_VB; 201 // Increment by 1/8th of numVertices or 6 (by 2 triangles) which ever is greater 202 final int newNumVertices = numVertices + Math.max((numVertices >> 3), 6); 203 float[] temp = new float[newNumVertices * VERTEX_SIZE_VB]; 204 System.arraycopy(vertexBuffer, 0, temp, 0, vertexBuffer.length); 205 vertexBuffer = temp; 206 // Enlarge cachedNormals, cachedTangents and cachedBitangents too 207 temp = new float[newNumVertices * 3]; 208 System.arraycopy(cachedNormals, 0, temp, 0, cachedNormals.length); 209 cachedNormals = temp; 210 temp = new float[newNumVertices * 3]; 211 System.arraycopy(cachedTangents, 0, temp, 0, cachedTangents.length); 212 cachedTangents = temp; 213 temp = new float[newNumVertices * 3]; 214 System.arraycopy(cachedBitangents, 0, temp, 0, cachedBitangents.length); 215 cachedBitangents = temp; 216 } 217 int pointOffset = faces[pointIndex] * POINT_SIZE; 218 int normalOffset = faces[normalIndex] * NORMAL_SIZE; 219 int texCoordOffset = faces[texCoordIndex] * TEXCOORD_SIZE; 220 221 // Save the vertex of triangle 222 instance.triPointIndex[i] = pointOffset; 223 instance.triTexCoordIndex[i] = texCoordOffset; 224 instance.triVerts[i] = vbCount / VERTEX_SIZE_VB; 225 226 vertexBuffer[vbCount] = points[pointOffset]; 227 vertexBuffer[vbCount + 1] = points[pointOffset + 1]; 228 vertexBuffer[vbCount + 2] = points[pointOffset + 2]; 229 vertexBuffer[vbCount + 3] = texCoords[texCoordOffset]; 230 vertexBuffer[vbCount + 4] = texCoords[texCoordOffset + 1]; 231 // Store the normal in the cachedNormals array 232 int index = instance.triVerts[i] * NORMAL_SIZE; 233 cachedNormals[index] = normals[normalOffset]; 234 cachedNormals[index + 1] = normals[normalOffset + 1]; 235 cachedNormals[index + 2] = normals[normalOffset + 2]; 236 237 vbCount += VERTEX_SIZE_VB; 238 239 mp2vb = point2vbMap.get(pointOffset); 240 if (mp2vb == null) { 241 // create 242 mp2vb = new MeshGeomComp2VB(pointOffset, mf2vb); 243 point2vbMap.put(pointOffset, mp2vb); 244 } else { 245 // addLoc 246 mp2vb.addLoc(mf2vb); 247 } 248 249 mn2vb = normal2vbMap.get(normalOffset); 250 if (mn2vb == null) { 251 // create 252 mn2vb = new MeshGeomComp2VB(normalOffset, mf2vb); 253 normal2vbMap.put(normalOffset, mn2vb); 254 } else { 255 // addLoc 256 mn2vb.addLoc(mf2vb); 257 } 258 259 mt2vb = texCoord2vbMap.get(texCoordOffset); 260 if (mt2vb == null) { 261 // create 262 mt2vb = new MeshGeomComp2VB(texCoordOffset, mf2vb); 263 texCoord2vbMap.put(texCoordOffset, mt2vb); 264 } else { 265 // addLoc 266 mt2vb.addLoc(mf2vb); 267 } 268 269 // Construct IndexBuffer 270 indexBuffer[ibCount++] = mf2vb; 271 } 272 273 // This is the best time to compute the tangent and bitangent for each 274 // of the vertex. Go thro. the 3 vertices of a triangle 275 for (int i = 0; i < 3; i++) { 276 instance.triPoints[i].x = points[instance.triPointIndex[i]]; 277 instance.triPoints[i].y = points[instance.triPointIndex[i] + 1]; 278 instance.triPoints[i].z = points[instance.triPointIndex[i] + 2]; 279 instance.triTexCoords[i].x = texCoords[instance.triTexCoordIndex[i]]; 280 instance.triTexCoords[i].y = texCoords[instance.triTexCoordIndex[i] + 1]; 281 } 282 283 MeshUtil.computeTBNNormalized(instance.triPoints[0], instance.triPoints[1], 284 instance.triPoints[2], instance.triTexCoords[0], 285 instance.triTexCoords[1], instance.triTexCoords[2], 286 instance.triNormals); 287 288 for (int i = 0; i < 3; i++) { 289 int index = instance.triVerts[i] * NORMAL_SIZE; 290 cachedTangents[index] = instance.triNormals[1].x; 291 cachedTangents[index + 1] = instance.triNormals[1].y; 292 cachedTangents[index + 2] = instance.triNormals[1].z; 293 cachedBitangents[index] = instance.triNormals[2].x; 294 cachedBitangents[index + 1] = instance.triNormals[2].y; 295 cachedBitangents[index + 2] = instance.triNormals[2].z; 296 } 297 298 } 299 300 numberOfVertices = vbCount / VERTEX_SIZE_VB; 301 302 convertNormalsToQuats(instance, numberOfVertices, 303 cachedNormals, cachedTangents, cachedBitangents, vertexBuffer, null); 304 305 indexBufferSize = numFaces * 3; 306 307 if (numberOfVertices > 0x10000) { // > 64K 308 return buildNativeGeometry(vertexBuffer, 309 numberOfVertices * VERTEX_SIZE_VB, indexBuffer, indexBufferSize); 310 } else { 311 312 if (indexBufferShort == null || indexBufferShort.length < indexBufferSize) { 313 indexBufferShort = new short[indexBufferSize]; 314 } 315 int ii = 0; 316 for (int i = 0; i < numFaces; i++) { 317 indexBufferShort[ii] = (short) indexBuffer[ii++]; 318 indexBufferShort[ii] = (short) indexBuffer[ii++]; 319 indexBufferShort[ii] = (short) indexBuffer[ii++]; 320 } 321 indexBuffer = null; // free 322 return buildNativeGeometry(vertexBuffer, 323 numberOfVertices * VERTEX_SIZE_VB, indexBufferShort, indexBufferSize); 324 } 325 } 326 327 // Update PointNormalTexCoordGeometry 328 private boolean updatePNTGeometry(float[] points, int[] pointsFromAndLengthIndices, 329 float[] normals, int[] normalsFromAndLengthIndices, 330 float[] texCoords, int[] texCoordsFromAndLengthIndices) { 331 332 if (dirtyVertices == null) { 333 // Create a dirty array of size equal to number of vertices in vertexBuffer. 334 dirtyVertices = new boolean[numberOfVertices]; 335 } 336 // Clear dirty array before use. 337 Arrays.fill(dirtyVertices, false); 338 339 // Find out the list of modified points 340 int startPoint = pointsFromAndLengthIndices[0] / POINT_SIZE; 341 int numPoints = (pointsFromAndLengthIndices[1] / POINT_SIZE); 342 if ((pointsFromAndLengthIndices[1] % POINT_SIZE) > 0) { 343 numPoints++; 344 } 345 if (numPoints > 0) { 346 for (int i = 0; i < numPoints; i++) { 347 int pointOffset = (startPoint + i) * POINT_SIZE; 348 MeshGeomComp2VB mp2vb = (MeshGeomComp2VB) point2vbMap.get(pointOffset); 349 assert mp2vb != null; 350 // mp2vb shouldn't be null. We can't have a point referred by 351 // the faces array that isn't in the vertexBuffer. 352 if (mp2vb != null) { 353 int[] locs = mp2vb.getLocs(); 354 int validLocs = mp2vb.getValidLocs(); 355 if (locs != null) { 356 for (int j = 0; j < validLocs; j++) { 357 int vbIndex = locs[j] * VERTEX_SIZE_VB; 358 vertexBuffer[vbIndex] = points[pointOffset]; 359 vertexBuffer[vbIndex + 1] = points[pointOffset + 1]; 360 vertexBuffer[vbIndex + 2] = points[pointOffset + 2]; 361 dirtyVertices[locs[j]] = true; 362 } 363 } else { 364 int loc = mp2vb.getLoc(); 365 int vbIndex = loc * VERTEX_SIZE_VB; 366 vertexBuffer[vbIndex] = points[pointOffset]; 367 vertexBuffer[vbIndex + 1] = points[pointOffset + 1]; 368 vertexBuffer[vbIndex + 2] = points[pointOffset + 2]; 369 dirtyVertices[loc] = true; 370 } 371 } 372 } 373 } 374 375 // Find out the list of modified tex coords. 376 int startTexCoord = texCoordsFromAndLengthIndices[0] / TEXCOORD_SIZE; 377 int numTexCoords = (texCoordsFromAndLengthIndices[1] / TEXCOORD_SIZE); 378 if ((texCoordsFromAndLengthIndices[1] % TEXCOORD_SIZE) > 0) { 379 numTexCoords++; 380 } 381 if (numTexCoords > 0) { 382 for (int i = 0; i < numTexCoords; i++) { 383 int texCoordOffset = (startTexCoord + i) * TEXCOORD_SIZE; 384 MeshGeomComp2VB mt2vb = (MeshGeomComp2VB) texCoord2vbMap.get(texCoordOffset); 385 assert mt2vb != null; 386 // mt2vb shouldn't be null. We can't have a texCoord referred by 387 // the faces array that isn't in the vertexBuffer. 388 if (mt2vb != null) { 389 int[] locs = mt2vb.getLocs(); 390 int validLocs = mt2vb.getValidLocs(); 391 if (locs != null) { 392 for (int j = 0; j < validLocs; j++) { 393 int vbIndex = (locs[j] * VERTEX_SIZE_VB) + POINT_SIZE_VB; 394 vertexBuffer[vbIndex] = texCoords[texCoordOffset]; 395 vertexBuffer[vbIndex + 1] = texCoords[texCoordOffset + 1]; 396 dirtyVertices[locs[j]] = true; 397 } 398 } else { 399 int loc = mt2vb.getLoc(); 400 int vbIndex = (loc * VERTEX_SIZE_VB) + POINT_SIZE_VB; 401 vertexBuffer[vbIndex] = texCoords[texCoordOffset]; 402 vertexBuffer[vbIndex + 1] = texCoords[texCoordOffset + 1]; 403 dirtyVertices[loc] = true; 404 } 405 } 406 } 407 } 408 409 // Find out the list of modified normals 410 int startNormal = normalsFromAndLengthIndices[0] / NORMAL_SIZE; 411 int numNormals = (normalsFromAndLengthIndices[1] / NORMAL_SIZE); 412 if ((normalsFromAndLengthIndices[1] % NORMAL_SIZE) > 0) { 413 numNormals++; 414 } 415 if (numNormals > 0) { 416 MeshTempState instance = MeshTempState.getInstance(); 417 for (int i = 0; i < numNormals; i++) { 418 int normalOffset = (startNormal + i) * NORMAL_SIZE; 419 MeshGeomComp2VB mn2vb = (MeshGeomComp2VB) normal2vbMap.get(normalOffset); 420 assert mn2vb != null; 421 // mn2vb shouldn't be null. We can't have a normal referred by 422 // the faces array that isn't in the vertexBuffer. 423 if (mn2vb != null) { 424 int[] locs = mn2vb.getLocs(); 425 int validLocs = mn2vb.getValidLocs(); 426 if (locs != null) { 427 for (int j = 0; j < validLocs; j++) { 428 int index = locs[j] * NORMAL_SIZE; 429 cachedNormals[index] = normals[normalOffset]; 430 cachedNormals[index + 1] = normals[normalOffset + 1]; 431 cachedNormals[index + 2] = normals[normalOffset + 2]; 432 dirtyVertices[locs[j]] = true; 433 } 434 } else { 435 int loc = mn2vb.getLoc(); 436 int index = loc * NORMAL_SIZE; 437 cachedNormals[index] = normals[normalOffset]; 438 cachedNormals[index + 1] = normals[normalOffset + 1]; 439 cachedNormals[index + 2] = normals[normalOffset + 2]; 440 dirtyVertices[loc] = true; 441 } 442 } 443 } 444 } 445 446 // Prepare process all dirty vertices 447 MeshTempState instance = MeshTempState.getInstance(); 448 for (int i = 0; i < 3; i++) { 449 if (instance.triPoints[i] == null) { 450 instance.triPoints[i] = new Vec3f(); 451 } 452 if (instance.triTexCoords[i] == null) { 453 instance.triTexCoords[i] = new Vec2f(); 454 } 455 } 456 // Every 3 vertices form a triangle 457 for (int j = 0; j < numberOfVertices; j += 3) { 458 // Only process the triangle that has one of more dirty vertices 459 if (dirtyVertices[j] || dirtyVertices[j+1] || dirtyVertices[j+2]) { 460 int vbIndex = j * VERTEX_SIZE_VB; 461 // Go thro. the 3 vertices of a triangle 462 for (int i = 0; i < 3; i++) { 463 instance.triPoints[i].x = vertexBuffer[vbIndex]; 464 instance.triPoints[i].y = vertexBuffer[vbIndex + 1]; 465 instance.triPoints[i].z = vertexBuffer[vbIndex + 2]; 466 instance.triTexCoords[i].x = vertexBuffer[vbIndex + POINT_SIZE_VB]; 467 instance.triTexCoords[i].y = vertexBuffer[vbIndex + POINT_SIZE_VB + 1]; 468 vbIndex += VERTEX_SIZE_VB; 469 } 470 471 MeshUtil.computeTBNNormalized(instance.triPoints[0], instance.triPoints[1], 472 instance.triPoints[2], instance.triTexCoords[0], 473 instance.triTexCoords[1], instance.triTexCoords[2], 474 instance.triNormals); 475 476 int index = j * NORMAL_SIZE; 477 for (int i = 0; i < 3; i++) { 478 cachedTangents[index] = instance.triNormals[1].x; 479 cachedTangents[index + 1] = instance.triNormals[1].y; 480 cachedTangents[index + 2] = instance.triNormals[1].z; 481 cachedBitangents[index] = instance.triNormals[2].x; 482 cachedBitangents[index + 1] = instance.triNormals[2].y; 483 cachedBitangents[index + 2] = instance.triNormals[2].z; 484 index += NORMAL_SIZE; 485 } 486 487 } 488 } 489 490 convertNormalsToQuats(instance, numberOfVertices, 491 cachedNormals, cachedTangents, cachedBitangents, vertexBuffer, dirtyVertices); 492 493 if (indexBuffer != null) { 494 return buildNativeGeometry(vertexBuffer, 495 numberOfVertices * VERTEX_SIZE_VB, indexBuffer, indexBufferSize); 496 } else { 497 return buildNativeGeometry(vertexBuffer, 498 numberOfVertices * VERTEX_SIZE_VB, indexBufferShort, indexBufferSize); 499 } 500 501 } 502 503 @Override 504 public boolean buildGeometry(boolean userDefinedNormals, 505 float[] points, int[] pointsFromAndLengthIndices, 506 float[] normals, int[] normalsFromAndLengthIndices, 507 float[] texCoords, int[] texCoordsFromAndLengthIndices, 508 int[] faces, int[] facesFromAndLengthIndices, 509 int[] faceSmoothingGroups, int[] faceSmoothingGroupsFromAndLengthIndices) { 510 if (userDefinedNormals) { 511 return buildPNTGeometry(points, pointsFromAndLengthIndices, 512 normals, normalsFromAndLengthIndices, 513 texCoords, texCoordsFromAndLengthIndices, 514 faces, facesFromAndLengthIndices); 515 } else { 516 return buildPTGeometry(points, texCoords, faces, faceSmoothingGroups); 517 } 518 } 519 520 // Build PointNormalTexCoordGeometry 521 private boolean buildPNTGeometry( 522 float[] points, int[] pointsFromAndLengthIndices, 523 float[] normals, int[] normalsFromAndLengthIndices, 524 float[] texCoords, int[] texCoordsFromAndLengthIndices, 525 int[] faces, int[] facesFromAndLengthIndices) { 526 527 boolean updatePoints = pointsFromAndLengthIndices[1] > 0; 528 boolean updateNormals = normalsFromAndLengthIndices[1] > 0; 529 boolean updateTexCoords = texCoordsFromAndLengthIndices[1] > 0; 530 boolean updateFaces = facesFromAndLengthIndices[1] > 0; 531 532 // First time creation 533 boolean buildGeom = !(updatePoints || updateNormals || updateTexCoords || updateFaces); 534 535 // We will need to rebuild geom buffers if there is a change to faces 536 if (updateFaces) { 537 buildGeom = true; 538 } 539 540 if ((!buildGeom) && (vertexBuffer != null) 541 && ((indexBuffer != null) || (indexBufferShort != null))) { 542 return updatePNTGeometry(points, pointsFromAndLengthIndices, 543 normals, normalsFromAndLengthIndices, 544 texCoords, texCoordsFromAndLengthIndices); 545 } 546 return doBuildPNTGeometry(points, normals, texCoords, faces); 547 548 } 549 550 // Build PointTexCoordGeometry 551 private boolean buildPTGeometry(float[] pos, float[] uv, int[] faces, int[] smoothing) { 552 nVerts = pos.length / 3; 553 nTVerts = uv.length / 2; 554 nFaces = faces.length / (VertexFormat.POINT_TEXCOORD.getVertexIndexSize() * 3); 555 assert nVerts > 0 && nFaces > 0 && nTVerts > 0; 556 this.pos = pos; 557 this.uv = uv; 558 this.faces = faces; 559 this.smoothing = smoothing.length == nFaces ? smoothing : null; 560 561 MeshTempState instance = MeshTempState.getInstance(); 562 // big pool for all possible vertices 563 if (instance.pool == null || instance.pool.length < nFaces * 3) { 564 instance.pool = new MeshVertex[nFaces * 3]; 565 } 566 567 if (instance.indexBuffer == null || instance.indexBuffer.length < nFaces * 3) { 568 instance.indexBuffer = new int[nFaces * 3]; 569 } 570 571 if (instance.pVertex == null || instance.pVertex.length < nVerts) { 572 instance.pVertex = new MeshVertex[nVerts]; 573 } else { 574 Arrays.fill(instance.pVertex, 0, instance.pVertex.length, null); 575 } 576 577 // check if all hard edges or all smooth 578 checkSmoothingGroup(); 579 580 // compute [N, T, B] for each face 581 computeTBNormal(instance.pool, instance.pVertex, instance.indexBuffer); 582 583 // process sm and weld points 584 int nNewVerts = MeshVertex.processVertices(instance.pVertex, nVerts, 585 allHardEdges, allSameSmoothing); 586 587 if (instance.vertexBuffer == null 588 || instance.vertexBuffer.length < nNewVerts * VERTEX_SIZE_VB) { 589 instance.vertexBuffer = new float[nNewVerts * VERTEX_SIZE_VB]; 590 } 591 buildVertexBuffer(instance.pVertex, instance.vertexBuffer); 592 593 if (nNewVerts > 0x10000) { 594 buildIndexBuffer(instance.pool, instance.indexBuffer, null); 595 return buildNativeGeometry(instance.vertexBuffer, 596 nNewVerts * VERTEX_SIZE_VB, instance.indexBuffer, nFaces * 3); 597 } else { 598 if (instance.indexBufferShort == null || instance.indexBufferShort.length < nFaces * 3) { 599 instance.indexBufferShort = new short[nFaces * 3]; 600 } 601 buildIndexBuffer(instance.pool, instance.indexBuffer, instance.indexBufferShort); 602 return buildNativeGeometry(instance.vertexBuffer, 603 nNewVerts * VERTEX_SIZE_VB, instance.indexBufferShort, nFaces * 3); 604 } 605 } 606 607 private void computeTBNormal(MeshVertex[] pool, MeshVertex[] pVertex, int[] indexBuffer) { 608 MeshTempState instance = MeshTempState.getInstance(); 609 610 // tmp variables 611 int[] smFace = instance.smFace; 612 int[] triVerts = instance.triVerts; 613 Vec3f[] triPoints = instance.triPoints; 614 Vec2f[] triTexCoords = instance.triTexCoords; 615 Vec3f[] triNormals = instance.triNormals; 616 final String logname = BaseMesh.class.getName(); 617 618 for (int f = 0, nDeadFaces = 0, poolIndex = 0; f < nFaces; f++) { 619 int index = f * 3; 620 621 smFace = getFace(f, smFace); // copy from mesh to tmp smFace 622 623 // Get tex. point. index 624 triVerts[0] = smFace[BaseMesh.FaceMembers.POINT0.ordinal()]; 625 triVerts[1] = smFace[BaseMesh.FaceMembers.POINT1.ordinal()]; 626 triVerts[2] = smFace[BaseMesh.FaceMembers.POINT2.ordinal()]; 627 628 if (MeshUtil.isDeadFace(triVerts) 629 && PlatformLogger.getLogger(logname).isLoggable(PlatformLogger.Level.FINE)) { 630 // Log degenerated triangle 631 nDeadFaces++; 632 PlatformLogger.getLogger(logname).fine("Dead face [" 633 + triVerts[0] + ", " + triVerts[1] + ", " + triVerts[2] 634 + "] @ face group " + f + "; nEmptyFaces = " + nDeadFaces); 635 } 636 637 for (int i = 0; i < 3; i++) { 638 triPoints[i] = getVertex(triVerts[i], triPoints[i]); 639 } 640 641 // Get tex. coord. index 642 triVerts[0] = smFace[BaseMesh.FaceMembers.TEXCOORD0.ordinal()]; 643 triVerts[1] = smFace[BaseMesh.FaceMembers.TEXCOORD1.ordinal()]; 644 triVerts[2] = smFace[BaseMesh.FaceMembers.TEXCOORD2.ordinal()]; 645 646 for (int i = 0; i < 3; i++) { 647 triTexCoords[i] = getTVertex(triVerts[i], triTexCoords[i]); 648 } 649 650 MeshUtil.computeTBNNormalized(triPoints[0], triPoints[1], triPoints[2], 651 triTexCoords[0], triTexCoords[1], triTexCoords[2], 652 triNormals); 653 654 for (int j = 0; j < 3; ++j) { 655 pool[poolIndex] = (pool[poolIndex] == null) ? new MeshVertex() : pool[poolIndex]; 656 657 for (int i = 0; i < 3; ++i) { 658 pool[poolIndex].norm[i].set(triNormals[i]); 659 } 660 pool[poolIndex].smGroup = smFace[BaseMesh.FaceMembers.SMOOTHING_GROUP.ordinal()]; 661 pool[poolIndex].fIdx = f; 662 pool[poolIndex].tVert = triVerts[j]; 663 pool[poolIndex].index = MeshVertex.IDX_UNDEFINED; 664 int ii = j == 0 ? BaseMesh.FaceMembers.POINT0.ordinal() 665 : j == 1 ? BaseMesh.FaceMembers.POINT1.ordinal() 666 : BaseMesh.FaceMembers.POINT2.ordinal(); 667 int pIdx = smFace[ii]; 668 pool[poolIndex].pVert = pIdx; 669 indexBuffer[index + j] = pIdx; 670 pool[poolIndex].next = pVertex[pIdx]; 671 pVertex[pIdx] = pool[poolIndex]; 672 poolIndex++; 673 } 674 } 675 } 676 677 private void buildVSQuat(Vec3f[] tm, Quat4f quat) { 678 Vec3f v = MeshTempState.getInstance().vec3f1; 679 v.cross(tm[1], tm[2]); 680 float d = tm[0].dot(v); 681 if (d < 0) { 682 tm[2].mul(-1); 683 } 684 685 MeshUtil.buildQuat(tm, quat); 686 687 // This will interfer with degenerated triangle unit test. 688 // assert (quat.w >= 0); 689 690 if (d < 0) { 691 if (quat.w == 0) { 692 quat.w = MeshUtil.MAGIC_SMALL; 693 } 694 quat.scale(-1); 695 } 696 } 697 698 private void buildVertexBuffer(MeshVertex[] pVerts, float[] vertexBuffer) { 699 Quat4f quat = MeshTempState.getInstance().quat; 700 int idLast = 0; 701 702 for (int i = 0, index = 0; i < nVerts; ++i) { 703 MeshVertex v = pVerts[i]; 704 for (; v != null; v = v.next) { 705 if (v.index == idLast) { 706 int ind = v.pVert * 3; 707 vertexBuffer[index++] = pos[ind]; 708 vertexBuffer[index++] = pos[ind + 1]; 709 vertexBuffer[index++] = pos[ind + 2]; 710 ind = v.tVert * 2; 711 vertexBuffer[index++] = uv[ind]; 712 vertexBuffer[index++] = uv[ind + 1]; 713 buildVSQuat(v.norm, quat); 714 vertexBuffer[index++] = quat.x; 715 vertexBuffer[index++] = quat.y; 716 vertexBuffer[index++] = quat.z; 717 vertexBuffer[index++] = quat.w; 718 idLast++; 719 } 720 } 721 } 722 } 723 724 private void buildIndexBuffer(MeshVertex[] pool, int[] indexBuffer, short[] indexBufferShort) { 725 for (int i = 0; i < nFaces; ++i) { 726 int index = i * 3; 727 if (indexBuffer[index] != MeshVertex.IDX_UNDEFINED) { 728 for (int j = 0; j < 3; ++j) { 729 assert (pool[index].fIdx == i); 730 if (indexBufferShort != null) { 731 indexBufferShort[index + j] = (short) pool[index + j].index; 732 } else { 733 indexBuffer[index + j] = pool[index + j].index; 734 } 735 pool[index + j].next = null; // release reference 736 } 737 } else { 738 for (int j = 0; j < 3; ++j) { 739 if (indexBufferShort != null) { 740 indexBufferShort[index + j] = 0; 741 } else { 742 indexBuffer[index + j] = 0; 743 } 744 } 745 } 746 } 747 } 748 749 public int getNumVerts() { 750 return nVerts; 751 } 752 753 public int getNumTVerts() { 754 return nTVerts; 755 } 756 757 public int getNumFaces() { 758 return nFaces; 759 } 760 761 public Vec3f getVertex(int pIdx, Vec3f vertex) { 762 if (vertex == null) { 763 vertex = new Vec3f(); 764 } 765 int index = pIdx * 3; 766 vertex.set(pos[index], pos[index + 1], pos[index + 2]); 767 return vertex; 768 } 769 770 public Vec2f getTVertex(int tIdx, Vec2f texCoord) { 771 if (texCoord == null) { 772 texCoord = new Vec2f(); 773 } 774 int index = tIdx * 2; 775 texCoord.set(uv[index], uv[index + 1]); 776 return texCoord; 777 } 778 779 private void checkSmoothingGroup() { 780 if (smoothing == null || smoothing.length == 0) { // all smooth 781 allSameSmoothing = true; 782 allHardEdges = false; 783 return; 784 } 785 786 for (int i = 0; i + 1 < smoothing.length; i++) { 787 if (smoothing[i] != smoothing[i + 1]) { 788 // various SmGroup 789 allSameSmoothing = false; 790 allHardEdges = false; 791 return; 792 } 793 } 794 795 if (smoothing[0] == 0) { // all hard edges 796 allSameSmoothing = false; 797 allHardEdges = true; 798 } else { // all belongs to one group == all smooth 799 allSameSmoothing = true; 800 allHardEdges = false; 801 } 802 } 803 804 public int[] getFace(int fIdx, int[] face) { 805 int index = fIdx * 6; 806 if ((face == null) || (face.length < FACE_MEMBERS_SIZE)) { 807 face = new int[FACE_MEMBERS_SIZE]; 808 } 809 // Note: Order matter, [0, 5] == FaceMembers' points and texcoords 810 for (int i = 0; i < 6; i++) { 811 face[i] = faces[index + i]; 812 } 813 // Note: Order matter, 6 == FaceMembers.SMOOTHING_GROUP.ordinal() 814 // There is a total of 32 smoothing groups. 815 // Assign to 1st smoothing group if smoothing is null. 816 face[6] = smoothing != null ? smoothing[fIdx] : 1; 817 return face; 818 } 819 820 // Package scope method for testing 821 boolean test_isVertexBufferNull() { 822 return vertexBuffer == null; 823 } 824 825 // Package scope method for testing 826 int test_getVertexBufferLength() { 827 return vertexBuffer.length; 828 } 829 830 // Package scope method for testing 831 int test_getNumberOfVertices() { 832 return numberOfVertices; 833 } 834 835 class MeshGeomComp2VB { 836 837 private final int key; // point or texCoord index 838 private final int loc; // the first index into vertex buffer 839 private int[] locs; 840 private int validLocs; 841 842 MeshGeomComp2VB(int key, int loc) { 843 assert loc >= 0; 844 this.key = key; 845 this.loc = loc; 846 locs = null; 847 validLocs = 0; 848 } 849 850 void addLoc(int loc) { 851 if (locs == null) { 852 locs = new int[3]; // edge of mesh case 853 locs[0] = this.loc; 854 locs[1] = loc; 855 this.validLocs = 2; 856 } else if (locs.length > validLocs) { 857 locs[validLocs] = loc; 858 validLocs++; 859 } else { 860 int[] temp = new int[validLocs * 2]; 861 System.arraycopy(locs, 0, temp, 0, locs.length); 862 locs = temp; 863 locs[validLocs] = loc; 864 validLocs++; 865 } 866 } 867 868 int getKey() { 869 return key; 870 } 871 872 int getLoc() { 873 return loc; 874 } 875 876 int[] getLocs() { 877 return locs; 878 } 879 880 int getValidLocs() { 881 return validLocs; 882 } 883 } 884 885 }