浏览代码

nouvelle feature : facet data

jbousquie 8 年之前
父节点
当前提交
49728c4c07
共有 2 个文件被更改,包括 399 次插入45 次删除
  1. 245 1
      src/Mesh/babylon.mesh.ts
  2. 154 44
      src/Mesh/babylon.mesh.vertexData.ts

+ 245 - 1
src/Mesh/babylon.mesh.ts

@@ -119,7 +119,45 @@
         private _areNormalsFrozen: boolean = false; // Will be used by ribbons mainly
 
         private _sourcePositions: Float32Array; // Will be used to save original positions when using software skinning
-        private _sourceNormals: Float32Array; // Will be used to save original normals when using software skinning
+        private _sourceNormals: Float32Array;   // Will be used to save original normals when using software skinning
+
+        private _facetPositions: Vector3[];             // facet local positions
+        private _facetNormals: Vector3[];               // facet local normals
+        private _facetPartitioning: number[][];           // partitioning array of facet index arrays
+        private _facetNb: number = 0;                   // facet number
+        private _partitioningSubdivisions: number = 10; // number of subdivisions per axis in the partioning space  
+        private _partitioningBBoxRatio: number = 1.01;  // the partioning array space is by default 1% bigger than the bounding box
+        private _facetDataEnabled: boolean = false;     // is the facet data feature enabled on this mesh ?
+
+        /**
+         * Read-only : the number of facets in the mesh
+         */
+        public get facetNb(): number {
+            return this._facetNb;
+        }
+
+        /**
+         * The number of subdivisions per axis in the partioning space
+         */
+        public get partitioningSubdivisions(): number {
+            return this._partitioningSubdivisions;
+        }
+
+        public set partitioningSubdivisions(nb: number) {
+            this._partitioningSubdivisions = nb;
+        } 
+
+        /**
+         * The ratio to apply to the bouding box size to set to the partioning space.  
+         * Ex : 1.01 (default) the partioning space is 1% bigger than the bounding box.
+         */
+        public get partitioningBBoxRatio(): number {
+            return this._partitioningBBoxRatio;
+        }
+
+        public set partitioningBBoxRatio(ratio: number) {
+            this._partitioningBBoxRatio = ratio;
+        }
 
         // Will be used to save a source mesh reference, If any
         private _source: BABYLON.Mesh = null; 
@@ -1754,6 +1792,212 @@
             });
         }
 
+        // facet data
+        /** 
+         *  Initialize the facet data arrays : facetNormals, facetPositions and facetPartitioning
+         */
+        private _initFacetData(): Mesh {
+            if (!this._facetNormals) {
+                this._facetNormals = new Array<Vector3>();
+            }
+            if (!this._facetPositions) {
+                this._facetPositions = new Array<Vector3>();
+            }
+            if (!this._facetPartitioning) {
+                this._facetPartitioning = new Array<number[]>();
+            }
+            this._facetNb = this.getIndices().length / 3;
+            this._partitioningSubdivisions = (this._partitioningSubdivisions) ? this._partitioningSubdivisions : 10;   // default nb of partitioning subdivisions = 10
+            this._partitioningBBoxRatio = (this._partitioningBBoxRatio) ? this._partitioningBBoxRatio : 1.01;          // default ratio 1.01 = the partitioning is 1% bigger than the bounding box
+            for (var f = 0; f < this._facetNb; f++) {
+                this._facetNormals[f] = BABYLON.Vector3.Zero();
+                this._facetPositions[f] = BABYLON.Vector3.Zero();
+            }
+            this._facetDataEnabled = true;           
+            return this;
+        }
+
+        /**
+         * Updates the mesh facetData arrays and the internal partitioning when the mesh is morphed or updated.
+         * This method can be called within the render loop.
+         */
+        public updateFacetData(): Mesh {
+            if (!this._facetDataEnabled) {
+                this._initFacetData();
+            }
+            var positions = this.getVerticesData(BABYLON.VertexBuffer.PositionKind);
+            var indices = this.getIndices();
+            var normals = this.getVerticesData(BABYLON.VertexBuffer.NormalKind);
+            var options = {
+                facetNormals: this.getFacetLocalNormals(), 
+                facetPositions: this.getFacetLocalPositions(), 
+                facetPartitioning: this._facetPartitioning, 
+                bInfo: this.getBoundingInfo(),
+                ratio: this._partitioningBBoxRatio,
+                partitioningSubdivisions: this._partitioningSubdivisions
+            };
+            BABYLON.VertexData.ComputeNormals(positions, indices, normals, options);
+            this.updateVerticesData(BABYLON.VertexBuffer.NormalKind, normals, false, false);
+            return this;
+        }
+        /**
+         * Returns the facetLocalNormals array.
+         * The normals are expressed in the mesh local space.
+         */
+        public getFacetLocalNormals(): Vector3[] {
+            if (!this._facetNormals) {
+                this.updateFacetData();
+            }
+            return this._facetNormals;
+        }
+        /**
+         * Returns the facetLocalPositions array.
+         * The facet positions are expressed in the mesh local space.
+         */
+        public getFacetLocalPositions(): Vector3[] {
+            if (!this._facetPositions) {
+                this.updateFacetData();
+            }
+            return this._facetPositions;           
+        }
+        /**
+         * Returns the i-th facet position in the world system.
+         * This method allocates a new Vector3 per call.
+         */
+        public getFacetPosition(i: number): Vector3 {
+            var pos = BABYLON.Vector3.Zero();
+            this.getFacetPositionToRef(i, pos);
+            return pos;
+        }
+        /**
+         * Sets the reference Vector3 with the i-th facet position in the world system.
+         * Returns the mesh.
+         */
+        public getFacetPositionToRef(i: number, ref: Vector3): Mesh {
+            var localPos = (this.getFacetLocalPositions())[i];
+            var world = this.getWorldMatrix();
+            BABYLON.Vector3.TransformCoordinatesToRef(localPos, world, ref);
+            return this;
+        }
+        /**
+         * Returns the i-th facet normal in the world system.
+         * This method allocates a new Vector3 per call.
+         */
+        public getFacetNormal(i: number): Vector3 {
+            var norm = BABYLON.Vector3.Zero();
+            this.getFacetNormalToRef(i, norm);
+            return norm;
+        }
+        /**
+         * Sets the reference Vector3 with the i-th facet normal in the world system.
+         * Returns the mesh.
+         */
+        public getFacetNormalToRef(i: number, ref: Vector3) {
+            var localNorm = (this.getFacetLocalNormals())[i];
+            var localPos = (this.getFacetLocalPositions())[i];
+            var world = this.getWorldMatrix();
+            var x = localPos.x + localNorm.x;
+            var y = localPos.y + localNorm.y;
+            var z = localPos.z + localNorm.z;
+            BABYLON.Vector3.TransformCoordinatesFromFloatsToRef(x, y, z, world, ref);
+            var worldPos = BABYLON.Tmp.Vector3[8];
+            this.getFacetPositionToRef(i, worldPos);
+            ref.subtractInPlace(worldPos);
+            return this;
+        }
+        /** 
+         * Returns the facets (in an array) in the same partitioning block than the one the passed coordinates are located (expressed in the mesh local system).
+         */
+        public getFacetsAtLocalCoordinates(x: number, y: number, z: number): number[] {
+            var bInfo = this.getBoundingInfo();
+            var subRatio = this._partitioningSubdivisions * this._partitioningBBoxRatio;
+            var ox = Math.floor((x - bInfo.minimum.x * this._partitioningBBoxRatio) / (bInfo.maximum.x - bInfo.minimum.x) * subRatio);
+            var oy = Math.floor((y - bInfo.minimum.y * this._partitioningBBoxRatio) / (bInfo.maximum.y - bInfo.minimum.y) * subRatio);
+            var oz = Math.floor((z - bInfo.minimum.z * this._partitioningBBoxRatio) / (bInfo.maximum.z - bInfo.minimum.z) * subRatio);
+            if (ox < 0 || ox > this._partitioningSubdivisions || oy < 0 || oy > this._partitioningSubdivisions || oz < 0 || oz > this._partitioningSubdivisions) {
+                return null;
+            }
+            return this._facetPartitioning[ox + this._partitioningSubdivisions * oy + this._partitioningSubdivisions * this._partitioningSubdivisions * oz];
+        }
+        /** 
+         * Returns the closest mesh facet index at (x,y,z) World coordinates, null if not found
+         * If the parameter projected (vector3) is passed, it is set as the (x,y,z) World projection on the facet
+         * If onlyFacing is true, only the facet "facing" (x,y,z) are returned : positive dot normal * (x,y,z)
+         */
+        public getClosestFacetAtCoordinates(x: number, y: number, z: number, projected?: Vector3, onlyFacing?: boolean): number {
+            var world = this.getWorldMatrix();
+            var invMat = BABYLON.Tmp.Matrix[5];
+            world.invertToRef(invMat);
+            var invVect = BABYLON.Tmp.Vector3[8];
+            var closest = null;
+            BABYLON.Vector3.TransformCoordinatesFromFloatsToRef(x, y, z, invMat, invVect);  // transform (x,y,z) to coordinates in the mesh local space
+            closest = this.getClosestFacetAtLocalCoordinates(invVect.x, invVect.y, invVect.z, projected, onlyFacing);
+            if (projected) {
+                // tranform the local computed projected vector to world coordinates
+                BABYLON.Vector3.TransformCoordinatesFromFloatsToRef(projected.x, projected.y, projected.z, world, projected);
+            }
+            return closest;
+        }
+        /** 
+         * Returns the closest mesh facet index at (x,y,z) local coordinates, null if not found
+         * If the parameter projected (vector3) is passed, it is set as the (x,y,z) local projection on the facet
+         * If onlyFacing is true, only the facet "facing" (x,y,z) are returned : positive dot normal * (x,y,z)
+         */
+        public getClosestFacetAtLocalCoordinates(x: number, y: number, z: number, projected?: Vector3, onlyFacing?: boolean) {
+            var closest = null;
+            var tmpx = 0.0;         
+            var tmpy = 0.0;
+            var tmpz = 0.0;
+            var d = 0.0;            // tmp dot facet normal * facet position
+            var t0 = 0.0;
+            var projx = 0.0;
+            var projy = 0.0;
+            var projz = 0.0;
+            // Get all the facets in the same partitioning block than (x, y, z)
+            var facetPositions = this.getFacetLocalPositions();
+            var facetNormals = this.getFacetLocalNormals();
+            var facetsInBlock = this.getFacetsAtLocalCoordinates(x, y, z);
+            if (!facetsInBlock) {
+                return null;
+            }
+            // Get the closest facet to (x, y, z)
+            var shortest = Number.MAX_VALUE;            // init distance vars
+            var tmpDistance = shortest;
+            var fib;                                    // current facet in the block
+            var norm;                                   // current facet normal
+            var p0;                                     // current facet barycenter position
+            // loop on all the facets in the current partitioning block
+            for (var idx = 0; idx < facetsInBlock.length; idx++) {
+                fib = facetsInBlock[idx];           
+                norm = facetNormals[fib];
+                p0 = facetPositions[fib];
+
+                d = (x - p0.x) * norm.x + (y - p0.y) * norm.y + (z - p0.z) * norm.z;
+                if ( !onlyFacing || (onlyFacing && d >= 0) ) {
+                    // compute (x,y,z) projection on the facet = (projx, projy, projz)
+                    d = norm.x * p0.x + norm.y * p0.y + norm.z * p0.z; 
+                    t0 = -(norm.x * x + norm.y * y + norm.z * z - d) / (norm.x * norm.x + norm.y * norm.y + norm.z * norm.z);
+                    projx = x + norm.x * t0;
+                    projy = y + norm.y * t0;
+                    projz = z + norm.z * t0;
+
+                    tmpx = projx - x;
+                    tmpy = projy - y;
+                    tmpz = projz - z;
+                    tmpDistance = tmpx * tmpx + tmpy * tmpy + tmpz * tmpz;             // compute length between (x, y, z) and its projection on the facet
+                    if (tmpDistance < shortest) {                                      // just keep the closest facet to (x, y, z)
+                        shortest = tmpDistance;
+                        closest = fib; 
+                        if (projected) {
+                            projected.x = projx;
+                            projected.y = projy;
+                            projected.z = projz;
+                        }
+                    }
+                }
+            }
+            return closest;
+        }
         // Statics
         /**
          * Returns a new Mesh object what is a deep copy of the passed mesh. 

+ 154 - 44
src/Mesh/babylon.mesh.vertexData.ts

@@ -1961,66 +1961,176 @@
          * @param {any} - positions (number[] or Float32Array)
          * @param {any} - indices   (number[] or Uint16Array)
          * @param {any} - normals   (number[] or Float32Array)
+         * options (optional) :
+         * facetPositions : optional array of facet positions (vector3)
+         * facetNormals : optional array of facet normals (vector3)
+         * facetPartitioning : optional partitioning array. facetPositions is required for facetPartitioning computation
+         * partitioningSubdivisions : optional partitioning number of subdivsions on  each axis (int), required for facetPartitioning computation
+         * ratio : optional partitioning ratio / bounding box, required for facetPartitioning computation
+         * bInfo : optional bounding box info, required for facetPartitioning computation
          */
-        public static ComputeNormals(positions: any, indices: any, normals: any) {
-            var index = 0;
-
-            var p1p2x = 0.0;
-            var p1p2y = 0.0;
-            var p1p2z = 0.0;
-            var p3p2x = 0.0;
-            var p3p2y = 0.0;
-            var p3p2z = 0.0;
-            var faceNormalx = 0.0;
-            var faceNormaly = 0.0;
-            var faceNormalz = 0.0;
-
-            var length = 0.0;
-
-            var i1 = 0;
-            var i2 = 0;
-            var i3 = 0;
-
+        public static ComputeNormals(positions: any, indices: any, normals: any, 
+            options?: { facetNormals?: any, facetPositions?: any, facetPartitioning?: any, ratio?: number, bInfo?: any, partitioningSubdivisions?: number}): void {
+
+            // temporary scalar variables
+            var index = 0;                      // facet index     
+            var p1p2x = 0.0;                    // p1p2 vector x coordinate
+            var p1p2y = 0.0;                    // p1p2 vector y coordinate
+            var p1p2z = 0.0;                    // p1p2 vector z coordinate
+            var p3p2x = 0.0;                    // p3p2 vector x coordinate
+            var p3p2y = 0.0;                    // p3p2 vector y coordinate
+            var p3p2z = 0.0;                    // p3p2 vector z coordinate
+            var faceNormalx = 0.0;              // facet normal x coordinate
+            var faceNormaly = 0.0;              // facet normal y coordinate
+            var faceNormalz = 0.0;              // facet normal z coordinate
+            var length = 0.0;                   // facet normal length before normalization
+            var v1x = 0;                        // vector1 x index in the positions array
+            var v1y = 0;                        // vector1 y index in the positions array
+            var v1z = 0;                        // vector1 z index in the positions array
+            var v2x = 0;                        // vector2 x index in the positions array
+            var v2y = 0;                        // vector2 y index in the positions array
+            var v2z = 0;                        // vector2 z index in the positions array
+            var v3x = 0;                        // vector3 x index in the positions array
+            var v3y = 0;                        // vector3 y index in the positions array
+            var v3z = 0;                        // vector3 z index in the positions array
+            var computeFacetNormals = false;
+            var computeFacetPositions = false;
+            var computeFacetPartitioning = false;
+            if (options) {
+                computeFacetNormals = (options.facetNormals) ? true : false;
+                computeFacetPositions = (options.facetPositions) ? true : false;
+                computeFacetPartitioning = (options.facetPartitioning) ? true : false;
+            }
+
+            // facetPartitioning reinit if needed
+            if (computeFacetPartitioning) {  
+                var ox = 0;                 // X partitioning index for facet position
+                var oy = 0;                 // Y partinioning index for facet position
+                var oz = 0;                 // Z partinioning index for facet position
+                var b1x = 0;                // X partitioning index for facet v1 vertex
+                var b1y = 0;                // Y partitioning index for facet v1 vertex
+                var b1z = 0;                // z partitioning index for facet v1 vertex
+                var b2x = 0;                // X partitioning index for facet v2 vertex
+                var b2y = 0;                // Y partitioning index for facet v2 vertex
+                var b2z = 0;                // Z partitioning index for facet v2 vertex
+                var b3x = 0;                // X partitioning index for facet v3 vertex
+                var b3y = 0;                // Y partitioning index for facet v3 vertex
+                var b3z = 0;                // Z partitioning index for facet v3 vertex
+                var block_idx_o = 0;        // facet barycenter block index
+                var block_idx_v1 = 0;       // v1 vertex block index
+                var block_idx_v2 = 0;       // v2 vertex block index
+                var block_idx_v3 = 0;       // v3 vertex block index  
+                var xSubRatio = 0.0;        // tmp x divider
+                var ySubRatio = 0.0;        // tmp x divider
+                var zSubRatio = 0.0;        // tmp x divider  
+                options.facetPartitioning.length = 0;
+            }
+        
+            // reset the normals
             for (index = 0; index < positions.length; index++) {
                 normals[index] = 0.0;
             }
 
-            // indice triplet = 1 face
+            // Loop : 1 indice triplet = 1 facet
             var nbFaces = indices.length / 3;
             for (index = 0; index < nbFaces; index++) {
-                i1 = indices[index * 3];            // get the indexes of each vertex of the face
-                i2 = indices[index * 3 + 1];
-                i3 = indices[index * 3 + 2];
 
-                p1p2x = positions[i1 * 3] - positions[i2 * 3];          // compute two vectors per face
-                p1p2y = positions[i1 * 3 + 1] - positions[i2 * 3 + 1];
-                p1p2z = positions[i1 * 3 + 2] - positions[i2 * 3 + 2];
-
-                p3p2x = positions[i3 * 3] - positions[i2 * 3];
-                p3p2y = positions[i3 * 3 + 1] - positions[i2 * 3 + 1];
-                p3p2z = positions[i3 * 3 + 2] - positions[i2 * 3 + 2];
-
-                faceNormalx = p1p2y * p3p2z - p1p2z * p3p2y;            // compute the face normal with cross product
+                // get the indexes of the coordinates of each vertex of the facet
+                v1x = indices[index * 3] * 3;
+                v1y = v1x + 1;
+                v1z = v1x + 2;
+                v2x = indices[index * 3 + 1] * 3;
+                v2y = v2x + 1;
+                v2z = v2x + 2;
+                v3x = indices[index * 3 + 2] * 3;
+                v3y = v3x + 1;
+                v3z = v3x + 2;        
+
+                p1p2x = positions[v1x] - positions[v2x];          // compute two vectors per facet : p1p2 and p3p2
+                p1p2y = positions[v1y] - positions[v2y];
+                p1p2z = positions[v1z] - positions[v2z];
+
+                p3p2x = positions[v3x] - positions[v2x];
+                p3p2y = positions[v3y] - positions[v2y];
+                p3p2z = positions[v3z] - positions[v2z];
+
+                // compute the face normal with the cross product
+                faceNormalx = p1p2y * p3p2z - p1p2z * p3p2y;            
                 faceNormaly = p1p2z * p3p2x - p1p2x * p3p2z;
                 faceNormalz = p1p2x * p3p2y - p1p2y * p3p2x;
-
+                // normalize this normal and store it in the array facetData
                 length = Math.sqrt(faceNormalx * faceNormalx + faceNormaly * faceNormaly + faceNormalz * faceNormalz);
                 length = (length === 0) ? 1.0 : length;
-                faceNormalx /= length;                                  // normalize this normal
+                faceNormalx /= length;
                 faceNormaly /= length;
                 faceNormalz /= length;
 
-                normals[i1 * 3] += faceNormalx;                         // accumulate all the normals per face
-                normals[i1 * 3 + 1] += faceNormaly;
-                normals[i1 * 3 + 2] += faceNormalz;
-                normals[i2 * 3] += faceNormalx;
-                normals[i2 * 3 + 1] += faceNormaly;
-                normals[i2 * 3 + 2] += faceNormalz;
-                normals[i3 * 3] += faceNormalx;
-                normals[i3 * 3 + 1] += faceNormaly;
-                normals[i3 * 3 + 2] += faceNormalz;
-            }
+                if (computeFacetNormals) {
+                    options.facetNormals[index].x = faceNormalx;                                  
+                    options.facetNormals[index].y = faceNormaly;
+                    options.facetNormals[index].z = faceNormalz;
+                }
+
+                if (computeFacetPositions) {
+                    // compute and the facet barycenter coordinates in the array facetPositions 
+                    options.facetPositions[index].x = (positions[v1x] + positions[v2x] + positions[v3x]) / 3.0;
+                    options.facetPositions[index].y = (positions[v1y] + positions[v2y] + positions[v3y]) / 3.0;
+                    options.facetPositions[index].z = (positions[v1z] + positions[v2z] + positions[v3z]) / 3.0;
+                }
 
+                if (computeFacetPartitioning) {
+                    // store the facet indexes in arrays in the main facetPartitioning array :
+                    // compute each facet vertex (+ facet barycenter) index in the partiniong array
+                    xSubRatio = options.partitioningSubdivisions * options.ratio / (options.bInfo.maximum.x - options.bInfo.minimum.x);
+                    ySubRatio = options.partitioningSubdivisions * options.ratio / (options.bInfo.maximum.y - options.bInfo.minimum.y);
+                    zSubRatio = options.partitioningSubdivisions * options.ratio / (options.bInfo.maximum.z - options.bInfo.minimum.z);
+                    ox = Math.floor((options.facetPositions[index].x - options.bInfo.minimum.x * options.ratio) * xSubRatio);
+                    oy = Math.floor((options.facetPositions[index].y - options.bInfo.minimum.y * options.ratio) * ySubRatio);
+                    oz = Math.floor((options.facetPositions[index].z - options.bInfo.minimum.z * options.ratio) * zSubRatio);
+                    b1x = Math.floor((positions[v1x] - options.bInfo.minimum.x * options.ratio) * xSubRatio);
+                    b1y = Math.floor((positions[v1y] - options.bInfo.minimum.y * options.ratio) * ySubRatio);
+                    b1z = Math.floor((positions[v1z] - options.bInfo.minimum.z * options.ratio) * zSubRatio);
+                    b2x = Math.floor((positions[v2x] - options.bInfo.minimum.x * options.ratio) * xSubRatio);
+                    b2y = Math.floor((positions[v2y] - options.bInfo.minimum.y * options.ratio) * ySubRatio);
+                    b2z = Math.floor((positions[v2z] - options.bInfo.minimum.z * options.ratio) * zSubRatio);
+                    b3x = Math.floor((positions[v3x] - options.bInfo.minimum.x * options.ratio) * xSubRatio);
+                    b3y = Math.floor((positions[v3y] - options.bInfo.minimum.y * options.ratio) * ySubRatio);
+                    b3z = Math.floor((positions[v3z] - options.bInfo.minimum.z * options.ratio) * zSubRatio);
+                    
+                    block_idx_v1 = b1x + options.partitioningSubdivisions * b1y + options.partitioningSubdivisions * options.partitioningSubdivisions * b1z;
+                    block_idx_v2 = b2x + options.partitioningSubdivisions * b2y + options.partitioningSubdivisions * options.partitioningSubdivisions * b2z;
+                    block_idx_v3 = b3x + options.partitioningSubdivisions * b3y + options.partitioningSubdivisions * options.partitioningSubdivisions * b3z;
+                    block_idx_o = ox + options.partitioningSubdivisions * oy + options.partitioningSubdivisions * options.partitioningSubdivisions * oz;
+
+                    options.facetPartitioning[block_idx_o] = (options.facetPartitioning[block_idx_o]) ? options.facetPartitioning[block_idx_o] :new Array();
+                    options.facetPartitioning[block_idx_v1] = (options.facetPartitioning[block_idx_v1]) ? options.facetPartitioning[block_idx_v1] :new Array();
+                    options.facetPartitioning[block_idx_v2] = (options.facetPartitioning[block_idx_v2]) ? options.facetPartitioning[block_idx_v2] :new Array();
+                    options.facetPartitioning[block_idx_v3] = (options.facetPartitioning[block_idx_v3]) ? options.facetPartitioning[block_idx_v3] :new Array();
+
+                    // push each facet index in each block containing the vertex
+                    options.facetPartitioning[block_idx_v1].push(index);
+                    if (block_idx_v2 != block_idx_v1) {
+                        options.facetPartitioning[block_idx_v2].push(index);
+                    }
+                    if (!(block_idx_v3 == block_idx_v2 || block_idx_v3 == block_idx_v1)) {
+                        options.facetPartitioning[block_idx_v3].push(index);
+                    }
+                    if (!(block_idx_o == block_idx_v1 || block_idx_o == block_idx_v2 || block_idx_o == block_idx_v3)) {
+                        options.facetPartitioning[block_idx_o].push(index); 
+                    }
+                }
+
+                // compute the normals anyway
+                normals[v1x] += faceNormalx;                         // accumulate all the normals per face
+                normals[v1y] += faceNormaly;
+                normals[v1z] += faceNormalz;
+                normals[v2x] += faceNormalx;
+                normals[v2y] += faceNormaly;
+                normals[v2z] += faceNormalz;
+                normals[v3x] += faceNormalx;
+                normals[v3y] += faceNormaly;
+                normals[v3z] += faceNormalz;
+            }
             // last normalization of each normal
             for (index = 0; index < normals.length / 3; index++) {
                 faceNormalx = normals[index * 3];