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 }