浏览代码

Merge pull request #9557 from snagy/master

Add a smoothing threshold to smooth the sides of an extruded polygon.
David Catuhe 4 年之前
父节点
当前提交
eb55c2b62c
共有 3 个文件被更改,包括 67 次插入33 次删除
  1. 1 0
      dist/preview release/what's new.md
  2. 3 2
      src/Meshes/Builders/polygonBuilder.ts
  3. 63 31
      src/Meshes/polygonMesh.ts

+ 1 - 0
dist/preview release/what's new.md

@@ -8,6 +8,7 @@
 
 - Added static CenterToRef for vectors 2/3/4  ([aWeirdo](https://github.com/aWeirdo))
 - Added ability to view images (ktx2, png, jpg) to the sandbox. ([bghgary](https://github.com/bghgary))
+- Added optional smoothed normals for extruded procedural polygons. ([snagy](https://github.com/snagy))
 - Added support for infinite perspective cameras ([Deltakosh](https://github.com/deltakosh))
 - Added ability to enable/disable ArcRotateCamera zoom on multiTouch event ([NicolasBuecher](https://github.com/NicolasBuecher))
 

+ 3 - 2
src/Meshes/Builders/polygonBuilder.ts

@@ -154,11 +154,12 @@ export class PolygonBuilder {
      * @param earcutInjection can be used to inject your own earcut reference
      * @returns the polygon mesh
      */
-    public static CreatePolygon(name: string, options: { shape: Vector3[], holes?: Vector3[][], depth?: number, faceUV?: Vector4[], faceColors?: Color4[], updatable?: boolean, sideOrientation?: number, frontUVs?: Vector4, backUVs?: Vector4, wrap?: boolean}, scene: Nullable<Scene> = null, earcutInjection = earcut): Mesh {
+    public static CreatePolygon(name: string, options: { shape: Vector3[], holes?: Vector3[][], depth?: number, smoothingThreshold?: number, faceUV?: Vector4[], faceColors?: Color4[], updatable?: boolean, sideOrientation?: number, frontUVs?: Vector4, backUVs?: Vector4, wrap?: boolean}, scene: Nullable<Scene> = null, earcutInjection = earcut): Mesh {
         options.sideOrientation = Mesh._GetDefaultSideOrientation(options.sideOrientation);
         var shape = options.shape;
         var holes = options.holes || [];
         var depth = options.depth || 0;
+        var smoothingThreshold = options.smoothingThreshold || 2;
         var contours: Array<Vector2> = [];
         var hole: Array<Vector2> = [];
 
@@ -178,7 +179,7 @@ export class PolygonBuilder {
             }
             polygonTriangulation.addHole(hole);
         }
-        var polygon = polygonTriangulation.build(options.updatable, depth);
+        var polygon = polygonTriangulation.build(options.updatable, depth, smoothingThreshold);
         polygon._originalBuilderSideOrientation = options.sideOrientation;
         var vertexData = VertexData.CreatePolygon(polygon, options.sideOrientation, options.faceUV, options.faceColors, options.frontUVs, options.backUVs, options.wrap);
         vertexData.applyToMesh(polygon, options.updatable);

+ 63 - 31
src/Meshes/polygonMesh.ts

@@ -7,6 +7,7 @@ import { VertexData } from "../Meshes/mesh.vertexData";
 import { Engine } from "../Engines/engine";
 import { Nullable } from "../types";
 import { Path2 } from '../Maths/math.path';
+import { Epsilon } from '../Maths/math.constants';
 
 declare var earcut: any;
 /**
@@ -201,7 +202,7 @@ export class PolygonMeshBuilder {
     }
 
     /**
-     * Adds a whole within the polygon
+     * Adds a hole within the polygon
      * @param hole Array of points defining the hole
      * @returns this
      */
@@ -221,12 +222,13 @@ export class PolygonMeshBuilder {
      * Creates the polygon
      * @param updatable If the mesh should be updatable
      * @param depth The depth of the mesh created
+     * @param smoothingThreshold Dot product threshold for smoothed normals
      * @returns the created mesh
      */
-    build(updatable: boolean = false, depth: number = 0): Mesh {
+    build(updatable: boolean = false, depth: number = 0, smoothingThreshold: number = 2): Mesh {
         var result = new Mesh(this._name, this._scene);
 
-        var vertexData = this.buildVertexData(depth);
+        var vertexData = this.buildVertexData(depth, smoothingThreshold);
 
         result.setVerticesData(VertexBuffer.PositionKind, <number[]>vertexData.positions, updatable);
         result.setVerticesData(VertexBuffer.NormalKind, <number[]>vertexData.normals, updatable);
@@ -239,9 +241,10 @@ export class PolygonMeshBuilder {
     /**
      * Creates the polygon
      * @param depth The depth of the mesh created
+     * @param smoothingThreshold Dot product threshold for smoothed normals
      * @returns the created VertexData
      */
-    buildVertexData(depth: number = 0): VertexData {
+    buildVertexData(depth: number = 0, smoothingThreshold: number = 2): VertexData {
         var result = new VertexData();
 
         var normals = new Array<number>();
@@ -284,10 +287,10 @@ export class PolygonMeshBuilder {
             }
 
             //Add the sides
-            this.addSide(positions, normals, uvs, indices, bounds, this._outlinepoints, depth, false);
+            this.addSide(positions, normals, uvs, indices, bounds, this._outlinepoints, depth, false, smoothingThreshold);
 
             this._holes.forEach((hole) => {
-                this.addSide(positions, normals, uvs, indices, bounds, hole, depth, true);
+                this.addSide(positions, normals, uvs, indices, bounds, hole, depth, true, smoothingThreshold);
             });
         }
 
@@ -310,43 +313,76 @@ export class PolygonMeshBuilder {
      * @param depth depth of the polygon
      * @param flip flip of the polygon
      */
-    private addSide(positions: any[], normals: any[], uvs: any[], indices: any[], bounds: any, points: PolygonPoints, depth: number, flip: boolean) {
+    private addSide(positions: any[], normals: any[], uvs: any[], indices: any[], bounds: any, points: PolygonPoints, depth: number, flip: boolean, smoothingThreshold: number) {
         var StartIndex: number = positions.length / 3;
         var ulength: number = 0;
         for (var i: number = 0; i < points.elements.length; i++) {
-            var p: IndexedVector2 = points.elements[i];
-            var p1: IndexedVector2;
-            if ((i + 1) > points.elements.length - 1) {
-                p1 = points.elements[0];
-            }
-            else {
-                p1 = points.elements[i + 1];
-            }
+            const p: IndexedVector2 = points.elements[i];
+            const p1: IndexedVector2 = points.elements[(i + 1) % points.elements.length];
 
             positions.push(p.x, 0, p.y);
             positions.push(p.x, -depth, p.y);
             positions.push(p1.x, 0, p1.y);
             positions.push(p1.x, -depth, p1.y);
 
-            var v1 = new Vector3(p.x, 0, p.y);
-            var v2 = new Vector3(p1.x, 0, p1.y);
-            var v3 = v2.subtract(v1);
-            var v4 = new Vector3(0, 1, 0);
-            var vn = Vector3.Cross(v3, v4);
-            vn = vn.normalize();
+            const p0: IndexedVector2 = points.elements[(i + points.elements.length - 1) % points.elements.length];
+            const p2: IndexedVector2 = points.elements[(i + 2) % points.elements.length];
+
+            let vc = new Vector3(-(p1.y - p.y), 0, p1.x - p.x);
+            let vp = new Vector3(-(p.y - p0.y), 0,  p.x - p0.x);
+            let vn = new Vector3(-(p2.y - p1.y), 0, p2.x - p1.x);
+
+            if (!flip) {
+                vc = vc.scale(-1);
+                vp = vp.scale(-1);
+                vn = vn.scale(-1);
+            }
+
+            let vc_norm = vc.normalizeToNew();
+            let vp_norm = vp.normalizeToNew();
+            let vn_norm = vn.normalizeToNew();
+
+            const dotp = Vector3.Dot(vp_norm, vc_norm);
+            if (dotp > smoothingThreshold) {
+                if (dotp < Epsilon - 1) {
+                    vp_norm = (new Vector3(p.x, 0, p.y)).subtract(new Vector3(p1.x, 0, p1.y)).normalize();
+                }
+                else {
+                    // cheap average weighed by side length
+                    vp_norm = vp.add(vc).normalize();
+                }
+            }
+            else {
+                vp_norm = vc_norm;
+            }
+
+            const dotn = Vector3.Dot(vn, vc);
+            if (dotn > smoothingThreshold) {
+                if (dotn < Epsilon - 1) {
+                    // back to back
+                    vn_norm = (new Vector3(p1.x, 0, p1.y)).subtract(new Vector3(p.x, 0, p.y)).normalize();
+                }
+                else {
+                    // cheap average weighed by side length
+                    vn_norm = vn.add(vc).normalize();
+                }
+            }
+            else {
+                vn_norm = vc_norm;
+            }
 
             uvs.push(ulength / bounds.width, 0);
             uvs.push(ulength / bounds.width, 1);
-            ulength += v3.length();
+            ulength += vc.length();
             uvs.push((ulength / bounds.width), 0);
             uvs.push((ulength / bounds.width), 1);
 
-            if (!flip) {
-                normals.push(-vn.x, - vn.y, -vn.z);
-                normals.push(-vn.x, -vn.y, -vn.z);
-                normals.push(-vn.x, -vn.y, -vn.z);
-                normals.push(-vn.x, -vn.y, -vn.z);
+            normals.push(vp_norm.x, vp_norm.y, vp_norm.z);
+            normals.push(vp_norm.x, vp_norm.y, vp_norm.z);
+            normals.push(vn_norm.x, vn_norm.y, vn_norm.z);
+            normals.push(vn_norm.x, vn_norm.y, vn_norm.z);
 
+            if (!flip) {
                 indices.push(StartIndex);
                 indices.push(StartIndex + 1);
                 indices.push(StartIndex + 2);
@@ -356,10 +392,6 @@ export class PolygonMeshBuilder {
                 indices.push(StartIndex + 2);
             }
             else {
-                normals.push(vn.x, vn.y, vn.z);
-                normals.push(vn.x, vn.y, vn.z);
-                normals.push(vn.x, vn.y, vn.z);
-                normals.push(vn.x, vn.y, vn.z);
 
                 indices.push(StartIndex);
                 indices.push(StartIndex + 2);