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