Explorar o código

CreateCylinder() : add subdivisions parameter and fix normals bug

celian %!s(int64=11) %!d(string=hai) anos
pai
achega
c43d225a53
Modificáronse 2 ficheiros con 127 adicións e 110 borrados
  1. 63 53
      Babylon/Mesh/babylon.mesh.vertexData.js
  2. 64 57
      Babylon/Mesh/babylon.mesh.vertexData.ts

+ 63 - 53
Babylon/Mesh/babylon.mesh.vertexData.js

@@ -393,7 +393,7 @@
             return vertexData;
         };
 
-        VertexData.CreateCylinder = function (height, diameterTop, diameterBottom, tessellation) {
+        VertexData.CreateCylinder = function (height, diameterTop, diameterBottom, tessellation, subdivisions) {
             var radiusTop = diameterTop / 2;
             var radiusBottom = diameterBottom / 2;
             var indices = [];
@@ -401,15 +401,17 @@
             var normals = [];
             var uvs = [];
 
-            height = height || 1;
-            diameterTop = diameterTop || 0.5;
-            diameterBottom = diameterBottom || 1;
-            tessellation = tessellation || 16;
+            height          = height || 1;
+            diameterTop     = diameterTop || 0.5;
+            diameterBottom  = diameterBottom || 1;
+            tessellation    = tessellation || 16;
+            subdivisions    = subdivisions || 1;
+            subdivisions    = (subdivisions < 1) ? 1 : subdivisions;
 
             var getCircleVector = function (i) {
                 var angle = (i * 2.0 * Math.PI / tessellation);
-                var dx = Math.sin(angle);
-                var dz = Math.cos(angle);
+                var dx = Math.cos(angle);
+                var dz = Math.sin(angle);
 
                 return new BABYLON.Vector3(dx, 0, dz);
             };
@@ -420,79 +422,87 @@
                 if (radius == 0) {
                     return;
                 }
+                var vbase = positions.length / 3;
 
-                for (var i = 0; i < tessellation - 2; i++) {
-                    var i1 = (i + 1) % tessellation;
-                    var i2 = (i + 2) % tessellation;
-
-                    if (!isTop) {
-                        var tmp = i1;
-                        i1 = i2;
-                        i2 = tmp;
-                    }
-
-                    var vbase = positions.length / 3;
-                    indices.push(vbase);
-                    indices.push(vbase + i1);
-                    indices.push(vbase + i2);
-                }
-
-                // Which end of the cylinder is this?
-                var normal = new BABYLON.Vector3(0, -1, 0);
-                var textureScale = new BABYLON.Vector2(-0.5, -0.5);
+                var offset = new BABYLON.Vector3(0, height / 2, 0);
+                var textureScale = new BABYLON.Vector2(0.5, 0.5);
 
                 if (!isTop) {
-                    normal = normal.scale(-1);
+                    offset.scaleInPlace(-1);
                     textureScale.x = -textureScale.x;
                 }
 
+                // Positions, normals & uvs
                 for (i = 0; i < tessellation; i++) {
                     var circleVector = getCircleVector(i);
-                    var position = circleVector.scale(radius).add(normal.scale(height));
-                    var textureCoordinate = new BABYLON.Vector2(circleVector.x * textureScale.x + 0.5, circleVector.z * textureScale.y + 0.5);
+                    var position = circleVector.scale(radius).add(offset);
+                    var textureCoordinate = new BABYLON.Vector2(
+                        circleVector.x * textureScale.x + 0.5,
+                        circleVector.z * textureScale.y + 0.5
+                    );
 
                     positions.push(position.x, position.y, position.z);
-                    normals.push(normal.x, normal.y, normal.z);
                     uvs.push(textureCoordinate.x, textureCoordinate.y);
                 }
-            };
-
-            height /= 2;
 
-            var topOffset = new BABYLON.Vector3(0, 1, 0).scale(height);
+                // Indices
+                for (var i = 0; i < tessellation - 2; i++) {
+                    if (!isTop) {
+                        indices.push(vbase);
+                        indices.push(vbase + (i + 2) % tessellation);
+                        indices.push(vbase + (i + 1) % tessellation);
+                    } else {
+                        indices.push(vbase);
+                        indices.push(vbase + (i + 1) % tessellation);
+                        indices.push(vbase + (i + 2) % tessellation);
+                    }
+                }
+            };
 
-            var stride = tessellation + 1;
+            var base    = new BABYLON.Vector3(0, -1, 0).scale(height / 2);
+            var offset  = new BABYLON.Vector3(0, 1, 0).scale(height / subdivisions);
+            var stride  = tessellation + 1;
 
+            // Positions, normals & uvs
             for (var i = 0; i <= tessellation; i++) {
-                var normal = getCircleVector(i);
-                var sideOffsetBottom = normal.scale(radiusBottom);
-                var sideOffsetTop = normal.scale(radiusTop);
+                var circleVector = getCircleVector(i);
                 var textureCoordinate = new BABYLON.Vector2(i / tessellation, 0);
+                var position, radius = radiusBottom;
 
-                var position = sideOffsetBottom.add(topOffset);
-                positions.push(position.x, position.y, position.z);
-                normals.push(normal.x, normal.y, normal.z);
-                uvs.push(textureCoordinate.x, textureCoordinate.y);
+                for (var s = 0; s <= subdivisions; s++) {
+                    // Update variables
+                    position = circleVector.scale(radius);
+                    position.addInPlace(base.add(offset.scale(s)));
+                    textureCoordinate.y += 1 / subdivisions;
+                    radius += (radiusTop - radiusBottom)/subdivisions;
 
-                position = sideOffsetTop.subtract(topOffset);
-                textureCoordinate.y += 1;
-                positions.push(position.x, position.y, position.z);
-                normals.push(normal.x, normal.y, normal.z);
-                uvs.push(textureCoordinate.x, textureCoordinate.y);
+                    // Push in arrays
+                    positions.push(position.x, position.y, position.z);
+                    uvs.push(textureCoordinate.x, textureCoordinate.y);
+                }
+            }
 
-                indices.push(i * 2);
-                indices.push((i * 2 + 2) % (stride * 2));
-                indices.push(i * 2 + 1);
+            subdivisions += 1;
+            // Indices
+            for (var s = 0; s < subdivisions - 1; s++) {
+                for (var i = 0; i <= tessellation; i++) {
+                    indices.push( i * subdivisions + s);
+                    indices.push((i * subdivisions + (s + subdivisions)) % (stride * subdivisions));
+                    indices.push( i * subdivisions + (s + 1));
 
-                indices.push(i * 2 + 1);
-                indices.push((i * 2 + 2) % (stride * 2));
-                indices.push((i * 2 + 3) % (stride * 2));
+                    indices.push( i * subdivisions + (s + 1));
+                    indices.push((i * subdivisions + (s + subdivisions)) % (stride * subdivisions));
+                    indices.push((i * subdivisions + (s + subdivisions + 1)) % (stride * subdivisions));
+                }
             }
 
             // Create flat triangle fan caps to seal the top and bottom.
             createCylinderCap(true);
             createCylinderCap(false);
 
+            // Normals
+            BABYLON.VertexData.ComputeNormals(positions, indices, normals);
+
             // Result
             var vertexData = new BABYLON.VertexData();
 

+ 64 - 57
Babylon/Mesh/babylon.mesh.vertexData.ts

@@ -410,7 +410,7 @@
             return vertexData;
         }
 
-        public static CreateCylinder(height: number, diameterTop: number, diameterBottom: number, tessellation: number): VertexData {
+        public static CreateCylinder(height: number, diameterTop: number, diameterBottom: number, tessellation: number, subdivisions: number): VertexData {
             var radiusTop = diameterTop / 2;
             var radiusBottom = diameterBottom / 2;
             var indices = [];
@@ -418,15 +418,17 @@
             var normals = [];
             var uvs = [];
 
-            height = height || 1;
-            diameterTop = diameterTop || 0.5;
-            diameterBottom = diameterBottom || 1;
-            tessellation = tessellation || 16;
+            height          = height || 1;
+            diameterTop     = diameterTop || 0.5;
+            diameterBottom  = diameterBottom || 1;
+            tessellation    = tessellation || 16;
+            subdivisions    = subdivisions || 1;
+            subdivisions    = (subdivisions < 1) ? 1 : subdivisions;
 
             var getCircleVector = i => {
                 var angle = (i * 2.0 * Math.PI / tessellation);
-                var dx = Math.sin(angle);
-                var dz = Math.cos(angle);
+                var dx = Math.cos(angle);
+                var dz = Math.sin(angle);
 
                 return new BABYLON.Vector3(dx, 0, dz);
             };
@@ -437,82 +439,87 @@
                 if (radius == 0) {
                     return;
                 }
+                var vbase = positions.length / 3;
 
-                // Create cap indices.
-                for (var i = 0; i < tessellation - 2; i++) {
-                    var i1 = (i + 1) % tessellation;
-                    var i2 = (i + 2) % tessellation;
-
-                    if (!isTop) {
-                        var tmp = i1;
-                        i1 = i2;
-                        i2 = tmp;
-                    }
-
-                    var vbase = positions.length / 3;
-                    indices.push(vbase);
-                    indices.push(vbase + i1);
-                    indices.push(vbase + i2);
-                }
-
-                // Which end of the cylinder is this?
-                var normal = new BABYLON.Vector3(0, -1, 0);
-                var textureScale = new BABYLON.Vector2(-0.5, -0.5);
+                var offset = new BABYLON.Vector3(0, height / 2, 0);
+                var textureScale = new BABYLON.Vector2(0.5, 0.5);
 
                 if (!isTop) {
-                    normal = normal.scale(-1);
+                    offset.scaleInPlace(-1);
                     textureScale.x = -textureScale.x;
                 }
 
-                // Create cap vertices.
+                // Positions, normals & uvs
                 for (i = 0; i < tessellation; i++) {
                     var circleVector = getCircleVector(i);
-                    var position = circleVector.scale(radius).add(normal.scale(height));
-                    var textureCoordinate = new BABYLON.Vector2(circleVector.x * textureScale.x + 0.5, circleVector.z * textureScale.y + 0.5);
+                    var position = circleVector.scale(radius).add(offset);
+                    var textureCoordinate = new BABYLON.Vector2(
+                        circleVector.x * textureScale.x + 0.5,
+                        circleVector.z * textureScale.y + 0.5
+                    );
 
                     positions.push(position.x, position.y, position.z);
-                    normals.push(normal.x, normal.y, normal.z);
                     uvs.push(textureCoordinate.x, textureCoordinate.y);
                 }
-            };
-
-            height /= 2;
 
-            var topOffset = new BABYLON.Vector3(0, 1, 0).scale(height);
+                // Indices
+                for (var i = 0; i < tessellation - 2; i++) {
+                    if (!isTop) {
+                        indices.push(vbase);
+                        indices.push(vbase + (i + 2) % tessellation);
+                        indices.push(vbase + (i + 1) % tessellation);
+                    } else {
+                        indices.push(vbase);
+                        indices.push(vbase + (i + 1) % tessellation);
+                        indices.push(vbase + (i + 2) % tessellation);
+                    }
+                }
+            };
 
-            var stride = tessellation + 1;
+            var base    = new BABYLON.Vector3(0, -1, 0).scale(height / 2);
+            var offset  = new BABYLON.Vector3(0, 1, 0).scale(height / subdivisions);
+            var stride  = tessellation + 1;
 
-            // Create a ring of triangles around the outside of the cylinder.
+            // Positions, normals & uvs
             for (var i = 0; i <= tessellation; i++) {
-                var normal = getCircleVector(i);
-                var sideOffsetBottom = normal.scale(radiusBottom);
-                var sideOffsetTop = normal.scale(radiusTop);
+                var circleVector = getCircleVector(i);
                 var textureCoordinate = new BABYLON.Vector2(i / tessellation, 0);
+                var position, radius = radiusBottom;
 
-                var position = sideOffsetBottom.add(topOffset);
-                positions.push(position.x, position.y, position.z);
-                normals.push(normal.x, normal.y, normal.z);
-                uvs.push(textureCoordinate.x, textureCoordinate.y);
+                for (var s = 0; s <= subdivisions; s++) {
+                    // Update variables
+                    position = circleVector.scale(radius);
+                    position.addInPlace(base.add(offset.scale(s)));
+                    textureCoordinate.y += 1 / subdivisions;
+                    radius += (radiusTop - radiusBottom)/subdivisions;
 
-                position = sideOffsetTop.subtract(topOffset);
-                textureCoordinate.y += 1;
-                positions.push(position.x, position.y, position.z);
-                normals.push(normal.x, normal.y, normal.z);
-                uvs.push(textureCoordinate.x, textureCoordinate.y);
+                    // Push in arrays
+                    positions.push(position.x, position.y, position.z);
+                    uvs.push(textureCoordinate.x, textureCoordinate.y);
+                }
+            }
 
-                indices.push(i * 2);
-                indices.push((i * 2 + 2) % (stride * 2));
-                indices.push(i * 2 + 1);
+            subdivisions += 1;
+            // Indices
+            for (var s = 0; s < subdivisions - 1; s++) {
+                for (var i = 0; i <= tessellation; i++) {
+                    indices.push( i * subdivisions + s);
+                    indices.push((i * subdivisions + (s + subdivisions)) % (stride * subdivisions));
+                    indices.push( i * subdivisions + (s + 1));
 
-                indices.push(i * 2 + 1);
-                indices.push((i * 2 + 2) % (stride * 2));
-                indices.push((i * 2 + 3) % (stride * 2));
+                    indices.push( i * subdivisions + (s + 1));
+                    indices.push((i * subdivisions + (s + subdivisions)) % (stride * subdivisions));
+                    indices.push((i * subdivisions + (s + subdivisions + 1)) % (stride * subdivisions));
+                }
             }
 
             // Create flat triangle fan caps to seal the top and bottom.
             createCylinderCap(true);
             createCylinderCap(false);
 
+            // Normals
+            BABYLON.VertexData.ComputeNormals(positions, indices, normals);
+
             // Result
             var vertexData = new BABYLON.VertexData();
 
@@ -892,4 +899,4 @@
             }
         }
     }
-} 
+}