瀏覽代碼

Merge pull request #1064 from nockawa/master

Ray.intersectSegment method added
David Catuhe 9 年之前
父節點
當前提交
e4c87c2578
共有 5 個文件被更改,包括 205 次插入22 次删除
  1. 85 0
      src/Culling/babylon.ray.ts
  2. 34 3
      src/Mesh/babylon.geometry.ts
  3. 29 5
      src/Mesh/babylon.linesMesh.ts
  4. 37 12
      src/Mesh/babylon.subMesh.ts
  5. 20 2
      src/Tools/babylon.tools.ts

+ 85 - 0
src/Culling/babylon.ray.ts

@@ -192,6 +192,91 @@
                 return distance;
             }
         }
+
+        private static smallnum = 0.00000001;
+        private static rayl = 10e8;
+
+        /**
+         * Intersection test between the ray and a given segment whithin a given tolerance (threshold)
+         * @param sega the first point of the segment to test the intersection against
+         * @param segb the second point of the segment to test the intersection against
+         * @param threshold the tolerance margin, if the ray doesn't intersect the segment but is close to the given threshold, the intersection is successful
+         * @return the distance from the ray origin to the intersection point if there's intersection, or -1 if there's no intersection
+         */
+        intersectionSegment(sega: Vector3, segb: Vector3, threshold: number) : number
+        {
+            var rsegb = this.origin.add(this.direction.multiplyByFloats(Ray.rayl, Ray.rayl, Ray.rayl));
+
+            var u = segb.subtract(sega);
+            var v = rsegb.subtract(this.origin);
+            var w = sega.subtract(this.origin);
+            var a = Vector3.Dot(u, u);                  // always >= 0
+            var b = Vector3.Dot(u, v);
+            var c = Vector3.Dot(v, v);                  // always >= 0
+            var d = Vector3.Dot(u, w);
+            var e = Vector3.Dot(v, w);
+            var D = a * c - b * b;                      // always >= 0
+            var sc: number, sN: number, sD = D;         // sc = sN / sD, default sD = D >= 0
+            var tc: number, tN: number, tD = D;         // tc = tN / tD, default tD = D >= 0
+
+            // compute the line parameters of the two closest points
+            if (D < Ray.smallnum) {                     // the lines are almost parallel
+                sN = 0.0;                               // force using point P0 on segment S1
+                sD = 1.0;                               // to prevent possible division by 0.0 later
+                tN = e;
+                tD = c;
+            }
+            else {                                      // get the closest points on the infinite lines
+                sN = (b * e - c * d);
+                tN = (a * e - b * d);
+                if (sN < 0.0) {                         // sc < 0 => the s=0 edge is visible
+                    sN = 0.0;
+                    tN = e;
+                    tD = c;
+                } else if (sN > sD) {                   // sc > 1 => the s=1 edge is visible
+                    sN = sD;
+                    tN = e + b;
+                    tD = c;
+                }
+            }
+
+            if (tN < 0.0) {                             // tc < 0 => the t=0 edge is visible
+                tN = 0.0;
+                // recompute sc for this edge
+                if (-d < 0.0) {
+                    sN = 0.0;
+                } else if (-d > a)
+                    sN = sD;
+                else {
+                    sN = -d;
+                    sD = a;
+                }
+            } else if (tN > tD) {                       // tc > 1 => the t=1 edge is visible
+                tN = tD;
+                // recompute sc for this edge
+                if ((-d + b) < 0.0) {
+                    sN = 0;
+                } else if ((-d + b) > a) {
+                    sN = sD;
+                } else {
+                    sN = (-d + b);
+                    sD = a;
+                }
+            }
+            // finally do the division to get sc and tc
+            sc = (Math.abs(sN) < Ray.smallnum ? 0.0 : sN / sD);
+            tc = (Math.abs(tN) < Ray.smallnum ? 0.0 : tN / tD);
+
+            // get the difference of the two closest points
+            var dP = w.add(u.multiplyByFloats(sc, sc, sc)).subtract(v.multiplyByFloats(tc, tc, tc));  // = S1(sc) - S2(tc)
+
+            var isIntersected = (tc > 0) && (tc <= this.length) && (dP.lengthSquared() < (threshold * threshold));   // return intersection result
+
+            if (isIntersected) {
+                return tc;
+            }
+            return -1;
+        }
         
         // Statics
         public static CreateNew(x: number, y: number, viewportWidth: number, viewportHeight: number, world: Matrix, view: Matrix, projection: Matrix): Ray {

+ 34 - 3
src/Mesh/babylon.geometry.ts

@@ -15,12 +15,30 @@
         private _vertexBuffers;
         private _isDisposed = false;
         private _extend: { minimum: Vector3, maximum: Vector3 };
+        private _boundingBias: Vector2;
         public _delayInfo; //ANY
         private _indexBuffer;
         public _boundingInfo: BoundingInfo;
         public _delayLoadingFunction: (any: any, geometry: Geometry) => void;
         public _softwareSkinningRenderId: number;
 
+        /**
+         *  The Bias Vector to apply on the bounding elements (box/sphere), the max extend is computed as v += v * bias.x + bias.y, the min is computed as v -= v * bias.x + bias.y 
+         * @returns The Bias Vector 
+         */
+        public get boundingBias(): Vector2 {
+            return this._boundingBias;
+        }
+
+        public set boundingBias(value: Vector2) {
+            if (this._boundingBias && this._boundingBias.equals(value)) {
+                return;
+            }
+
+            this._boundingBias = value.clone();
+            this.updateExtend();
+        }
+
         constructor(id: string, scene: Scene, vertexData?: VertexData, updatable?: boolean, mesh?: Mesh) {
             this.id = id;
             this._engine = scene.getEngine();
@@ -41,6 +59,11 @@
 
             // applyToMesh
             if (mesh) {
+                if (mesh instanceof LinesMesh) {
+                    this.boundingBias = new Vector2(0, mesh.intersectionThreshold);
+                    this.updateExtend();
+                }
+
                 this.applyToMesh(mesh);
                 mesh.computeWorldMatrix(true);
             }
@@ -79,7 +102,7 @@
 
                 this._totalVertices = data.length / stride;
 
-                this._extend = Tools.ExtractMinAndMax(data, 0, this._totalVertices);
+                this.updateExtend(data);
 
                 var meshes = this._meshes;
                 var numOfMeshes = meshes.length;
@@ -121,7 +144,7 @@
                 this._totalVertices = data.length / stride;
 
                 if (updateExtends) {
-                    this._extend = Tools.ExtractMinAndMax(data, 0, this._totalVertices);
+                    this.updateExtend(data);
                 }
 
                 var meshes = this._meshes;
@@ -316,6 +339,14 @@
             }
         }
 
+        private updateExtend(data=null) {
+            if (!data) {
+                data = this._vertexBuffers[VertexBuffer.PositionKind].getData();
+            }
+
+            this._extend = Tools.ExtractMinAndMax(data, 0, this._totalVertices, this.boundingBias);
+        }
+
         private _applyToMesh(mesh: Mesh): void {
             var numOfMeshes = this._meshes.length;
 
@@ -330,7 +361,7 @@
                     mesh._resetPointsArrayCache();
 
                     if (!this._extend) {
-                        this._extend = Tools.ExtractMinAndMax(this._vertexBuffers[kind].getData(), 0, this._totalVertices);
+                        this.updateExtend(this._vertexBuffers[kind].getData());
                     }
                     mesh._boundingInfo = new BoundingInfo(this._extend.minimum, this._extend.maximum);
 

+ 29 - 5
src/Mesh/babylon.linesMesh.ts

@@ -3,11 +3,39 @@
         public color = new Color3(1, 1, 1);
         public alpha = 1;
 
+        /**
+         * The intersection Threshold is the margin applied when intersection a segment of the LinesMesh with a Ray.
+         * This margin is expressed in world space coordinates, so its value may vary.
+         * Default value is 0.1
+         * @returns the intersection Threshold value.
+         */
+        public get intersectionThreshold(): number {
+            return this._intersectionThreshold;
+        }
+
+        /**
+         * The intersection Threshold is the margin applied when intersection a segment of the LinesMesh with a Ray.
+         * This margin is expressed in world space coordinates, so its value may vary.
+         * @param value the new threshold to apply
+         */
+        public set intersectionThreshold(value: number) {
+            if (this._intersectionThreshold === value) {
+                return;
+            }
+
+            this._intersectionThreshold = value;
+            if (this.geometry) {
+                this.geometry.boundingBias = new Vector2(0, value);
+            }
+        }
+
+        private _intersectionThreshold: number;
         private _colorShader: ShaderMaterial;
 
         constructor(name: string, scene: Scene, parent: Node = null, source?: Mesh, doNotCloneChildren?: boolean) {
             super(name, scene, parent, source, doNotCloneChildren);
 
+            this._intersectionThreshold = 0.1;
             this._colorShader = new ShaderMaterial("colorShader", scene, "color",
                 {
                     attributes: ["position"],
@@ -21,7 +49,7 @@
         }
 
         public get isPickable(): boolean {
-            return false;
+            return true;
         }
 
         public get checkCollisions(): boolean {
@@ -51,10 +79,6 @@
             engine.draw(false, subMesh.indexStart, subMesh.indexCount);
         }
 
-        public intersects(ray: Ray, fastCheck?: boolean) {
-            return null;
-        }
-
         public dispose(doNotRecurse?: boolean): void {
             this._colorShader.dispose();
 

+ 37 - 12
src/Mesh/babylon.subMesh.ts

@@ -87,7 +87,7 @@
                 //the rendering mesh's bounding info can be used, it is the standard submesh for all indices.
                 extend = { minimum: this._renderingMesh.getBoundingInfo().minimum.clone(), maximum: this._renderingMesh.getBoundingInfo().maximum.clone() };
             } else {
-                extend = Tools.ExtractMinAndMaxIndexed(data, indices, this.indexStart, this.indexCount);
+                extend = Tools.ExtractMinAndMaxIndexed(data, indices, this.indexStart, this.indexCount, this._renderingMesh.geometry.boundingBias);
             }
             this._boundingInfo = new BoundingInfo(extend.minimum, extend.maximum);
         }
@@ -134,22 +134,22 @@
         public intersects(ray: Ray, positions: Vector3[], indices: number[] | Int32Array, fastCheck?: boolean): IntersectionInfo {
             var intersectInfo: IntersectionInfo = null;
 
-            // Triangles test
-            for (var index = this.indexStart; index < this.indexStart + this.indexCount; index += 3) {
-                var p0 = positions[indices[index]];
-                var p1 = positions[indices[index + 1]];
-                var p2 = positions[indices[index + 2]];
+            // LineMesh first as it's also a Mesh...
+            if (this._mesh instanceof LinesMesh) {
+                var lineMesh = <LinesMesh>this._mesh;
 
-                var currentIntersectInfo = ray.intersectsTriangle(p0, p1, p2);
+                // Line test
+                for (var index = this.indexStart; index < this.indexStart + this.indexCount; index += 2) {
+                    var p0 = positions[indices[index]];
+                    var p1 = positions[indices[index + 1]];
 
-                if (currentIntersectInfo) {
-                    if (currentIntersectInfo.distance < 0) {
+                    var length = ray.intersectionSegment(p0, p1, lineMesh.intersectionThreshold);
+                    if (length<0) {
                         continue;
                     }
 
-                    if (fastCheck || !intersectInfo || currentIntersectInfo.distance < intersectInfo.distance) {
-                        intersectInfo = currentIntersectInfo;
-                        intersectInfo.faceId = index / 3;
+                    if (fastCheck || !intersectInfo || length < intersectInfo.distance) {
+                        intersectInfo = new IntersectionInfo(null, null, length);
 
                         if (fastCheck) {
                             break;
@@ -157,6 +157,31 @@
                     }
                 }
             }
+            else if (this._mesh instanceof Mesh) {
+                // Triangles test
+                for (var index = this.indexStart; index < this.indexStart + this.indexCount; index += 3) {
+                    var p0 = positions[indices[index]];
+                    var p1 = positions[indices[index + 1]];
+                    var p2 = positions[indices[index + 2]];
+
+                    var currentIntersectInfo = ray.intersectsTriangle(p0, p1, p2);
+
+                    if (currentIntersectInfo) {
+                        if (currentIntersectInfo.distance < 0) {
+                            continue;
+                        }
+
+                        if (fastCheck || !intersectInfo || currentIntersectInfo.distance < intersectInfo.distance) {
+                            intersectInfo = currentIntersectInfo;
+                            intersectInfo.faceId = index / 3;
+
+                            if (fastCheck) {
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
 
             return intersectInfo;
         }

+ 20 - 2
src/Tools/babylon.tools.ts

@@ -137,7 +137,7 @@
             return "data:image/png;base64," + output;
         }
 
-        public static ExtractMinAndMaxIndexed(positions: number[] | Float32Array, indices: number[] | Int32Array, indexStart: number, indexCount: number): { minimum: Vector3; maximum: Vector3 } {
+        public static ExtractMinAndMaxIndexed(positions: number[] | Float32Array, indices: number[] | Int32Array, indexStart: number, indexCount: number, bias: Vector2 = null): { minimum: Vector3; maximum: Vector3 } {
             var minimum = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
             var maximum = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
 
@@ -148,13 +148,22 @@
                 maximum = Vector3.Maximize(current, maximum);
             }
 
+            if (bias) {
+                minimum.x -= minimum.x * bias.x + bias.y;
+                minimum.y -= minimum.y * bias.x + bias.y;
+                minimum.z -= minimum.z * bias.x + bias.y;
+                maximum.x += maximum.x * bias.x + bias.y;
+                maximum.y += maximum.y * bias.x + bias.y;
+                maximum.z += maximum.z * bias.x + bias.y;
+            }
+
             return {
                 minimum: minimum,
                 maximum: maximum
             };
         }
 
-        public static ExtractMinAndMax(positions: number[] | Float32Array, start: number, count: number): { minimum: Vector3; maximum: Vector3 } {
+        public static ExtractMinAndMax(positions: number[] | Float32Array, start: number, count: number, bias: Vector2 = null): { minimum: Vector3; maximum: Vector3 } {
             var minimum = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
             var maximum = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
 
@@ -165,6 +174,15 @@
                 maximum = Vector3.Maximize(current, maximum);
             }
 
+            if (bias) {
+                minimum.x -= minimum.x * bias.x + bias.y;
+                minimum.y -= minimum.y * bias.x + bias.y;
+                minimum.z -= minimum.z * bias.x + bias.y;
+                maximum.x += maximum.x * bias.x + bias.y;
+                maximum.y += maximum.y * bias.x + bias.y;
+                maximum.z += maximum.z * bias.x + bias.y;
+            }
+
             return {
                 minimum: minimum,
                 maximum: maximum