瀏覽代碼

Fix #8638 (#8639)

* Fix #8638

* better..

* Better

* Lint

* Feedback
David Catuhe 5 年之前
父節點
當前提交
57cfc50feb
共有 5 個文件被更改,包括 115 次插入36 次删除
  1. 3 0
      src/Collisions/pickingInfo.ts
  2. 76 33
      src/Culling/ray.ts
  3. 3 2
      src/Meshes/abstractMesh.ts
  4. 1 0
      src/Meshes/mesh.ts
  5. 32 1
      src/Meshes/thinInstanceMesh.ts

+ 3 - 0
src/Collisions/pickingInfo.ts

@@ -42,6 +42,9 @@ export class PickingInfo {
     public subMeshId = 0;
     /** If a sprite was picked, this will be the sprite the pick collided with */
     public pickedSprite: Nullable<Sprite> = null;
+
+    /** If we are pikcing a mesh with thin instance, this will give you the picked thin instance */
+    public thinInstanceIndex = -1;
     /**
      * If a mesh was used to do the picking (eg. 6dof controller) this will be populated.
      */

+ 76 - 33
src/Culling/ray.ts

@@ -9,11 +9,14 @@ import { BoundingSphere } from "./boundingSphere";
 import { Scene } from "../scene";
 import { Camera } from "../Cameras/camera";
 import { Plane } from "../Maths/math.plane";
+
+declare type Mesh = import("../Meshes/mesh").Mesh;
+
 /**
  * Class representing a ray with position and direction
  */
 export class Ray {
-    private static readonly TmpVector3 = ArrayTools.BuildArray(6, Vector3.Zero);
+    private static readonly _TmpVector3 = ArrayTools.BuildArray(6, Vector3.Zero);
     private _tmpRay: Ray;
 
     /**
@@ -41,8 +44,8 @@ export class Ray {
      * @returns if the box was hit
      */
     public intersectsBoxMinMax(minimum: DeepImmutable<Vector3>, maximum: DeepImmutable<Vector3>, intersectionTreshold: number = 0): boolean {
-        const newMinimum = Ray.TmpVector3[0].copyFromFloats(minimum.x - intersectionTreshold, minimum.y - intersectionTreshold, minimum.z - intersectionTreshold);
-        const newMaximum = Ray.TmpVector3[1].copyFromFloats(maximum.x + intersectionTreshold, maximum.y + intersectionTreshold, maximum.z + intersectionTreshold);
+        const newMinimum = Ray._TmpVector3[0].copyFromFloats(minimum.x - intersectionTreshold, minimum.y - intersectionTreshold, minimum.z - intersectionTreshold);
+        const newMaximum = Ray._TmpVector3[1].copyFromFloats(maximum.x + intersectionTreshold, maximum.y + intersectionTreshold, maximum.z + intersectionTreshold);
         var d = 0.0;
         var maxValue = Number.MAX_VALUE;
         var inv: number;
@@ -178,11 +181,11 @@ export class Ray {
      * @returns intersection information if hit
      */
     public intersectsTriangle(vertex0: DeepImmutable<Vector3>, vertex1: DeepImmutable<Vector3>, vertex2: DeepImmutable<Vector3>): Nullable<IntersectionInfo> {
-        const edge1 = Ray.TmpVector3[0];
-        const edge2 = Ray.TmpVector3[1];
-        const pvec = Ray.TmpVector3[2];
-        const tvec = Ray.TmpVector3[3];
-        const qvec = Ray.TmpVector3[4];
+        const edge1 = Ray._TmpVector3[0];
+        const edge2 = Ray._TmpVector3[1];
+        const pvec = Ray._TmpVector3[2];
+        const tvec = Ray._TmpVector3[3];
+        const qvec = Ray._TmpVector3[4];
 
         vertex1.subtractToRef(vertex0, edge1);
         vertex2.subtractToRef(vertex0, edge2);
@@ -586,6 +589,9 @@ declare module "../scene" {
 
         /** @hidden */
         _internalMultiPick(rayFunction: (world: Matrix) => Ray, predicate?: (mesh: AbstractMesh) => boolean, trianglePredicate?: TrianglePickingPredicate): Nullable<PickingInfo[]>;
+
+        /** @hidden */
+        _internalPickForMesh(pickingInfo: Nullable<PickingInfo>, rayFunction: (world: Matrix) => Ray, mesh: AbstractMesh, world: Matrix, fastCheck?: boolean, onlyBoundingInfo?: boolean, trianglePredicate?: TrianglePickingPredicate): Nullable<PickingInfo>;
     }
 }
 
@@ -653,15 +659,30 @@ Scene.prototype.createPickingRayInCameraSpaceToRef = function (x: number, y: num
     return this;
 };
 
+Scene.prototype._internalPickForMesh = function (pickingInfo: Nullable<PickingInfo>, rayFunction: (world: Matrix) => Ray, mesh: AbstractMesh, world: Matrix, fastCheck?: boolean, onlyBoundingInfo?: boolean, trianglePredicate?: TrianglePickingPredicate) {
+    let ray = rayFunction(world);
+
+    let result = mesh.intersects(ray, fastCheck, trianglePredicate, onlyBoundingInfo, world);
+    if (!result || !result.hit) {
+        return null;
+    }
+
+    if (!fastCheck && pickingInfo != null && result.distance >= pickingInfo.distance) {
+        return null;
+    }
+
+    return result;
+};
+
 Scene.prototype._internalPick = function (rayFunction: (world: Matrix) => Ray, predicate?: (mesh: AbstractMesh) => boolean, fastCheck?: boolean, onlyBoundingInfo?: boolean, trianglePredicate?: TrianglePickingPredicate): Nullable<PickingInfo> {
     if (!PickingInfo) {
         return null;
     }
 
-    var pickingInfo = null;
+    let pickingInfo = null;
 
-    for (var meshIndex = 0; meshIndex < this.meshes.length; meshIndex++) {
-        var mesh = this.meshes[meshIndex];
+    for (let meshIndex = 0; meshIndex < this.meshes.length; meshIndex++) {
+        let mesh = this.meshes[meshIndex];
 
         if (predicate) {
             if (!predicate(mesh)) {
@@ -671,22 +692,33 @@ Scene.prototype._internalPick = function (rayFunction: (world: Matrix) => Ray, p
             continue;
         }
 
-        var world = mesh.skeleton && mesh.skeleton.overrideMesh ? mesh.skeleton.overrideMesh.getWorldMatrix() : mesh.getWorldMatrix();
-        var ray = rayFunction(world);
+        if (mesh.hasThinInstances && (mesh as Mesh).thinInstanceEnablePicking) {
+            let thinMatrices = (mesh as Mesh).thinInstanceGetWorldMatrices();
+            for (let index = 0; index < thinMatrices.length; index++) {
+                let world = thinMatrices[index];
+                let result = this._internalPickForMesh(pickingInfo, rayFunction, mesh, world, fastCheck, onlyBoundingInfo, trianglePredicate);
 
-        var result = mesh.intersects(ray, fastCheck, trianglePredicate, onlyBoundingInfo);
-        if (!result || !result.hit) {
-            continue;
-        }
+                if (result) {
+                    pickingInfo = result;
+                    pickingInfo.thinInstanceIndex = index;
 
-        if (!fastCheck && pickingInfo != null && result.distance >= pickingInfo.distance) {
-            continue;
-        }
+                    if (fastCheck) {
+                        return pickingInfo;
+                    }
+                }
+            }
+        } else {
+            let world = mesh.skeleton && mesh.skeleton.overrideMesh ? mesh.skeleton.overrideMesh.getWorldMatrix() : mesh.getWorldMatrix();
+
+            let result = this._internalPickForMesh(pickingInfo, rayFunction, mesh, world, fastCheck, onlyBoundingInfo, trianglePredicate);
 
-        pickingInfo = result;
+            if (result) {
+                pickingInfo = result;
 
-        if (fastCheck) {
-            break;
+                if (fastCheck) {
+                    return pickingInfo;
+                }
+            }
         }
     }
 
@@ -697,10 +729,10 @@ Scene.prototype._internalMultiPick = function (rayFunction: (world: Matrix) => R
     if (!PickingInfo) {
         return null;
     }
-    var pickingInfos = new Array<PickingInfo>();
+    let pickingInfos = new Array<PickingInfo>();
 
-    for (var meshIndex = 0; meshIndex < this.meshes.length; meshIndex++) {
-        var mesh = this.meshes[meshIndex];
+    for (let meshIndex = 0; meshIndex < this.meshes.length; meshIndex++) {
+        let mesh = this.meshes[meshIndex];
 
         if (predicate) {
             if (!predicate(mesh)) {
@@ -710,15 +742,26 @@ Scene.prototype._internalMultiPick = function (rayFunction: (world: Matrix) => R
             continue;
         }
 
-        var world = mesh.getWorldMatrix();
-        var ray = rayFunction(world);
+        if (mesh.hasThinInstances && (mesh as Mesh).thinInstanceEnablePicking) {
+            let thinMatrices = (mesh as Mesh).thinInstanceGetWorldMatrices();
+            for (let index = 0; index < thinMatrices.length; index++) {
+                let world = thinMatrices[index];
+                let result = this._internalPickForMesh(null, rayFunction, mesh, world, false, false, trianglePredicate);
 
-        var result = mesh.intersects(ray, false, trianglePredicate);
-        if (!result || !result.hit) {
-            continue;
-        }
+                if (result) {
+                    result.thinInstanceIndex = index;
+                    pickingInfos.push(result);
+                }
+            }
+        } else {
+            let world = mesh.skeleton && mesh.skeleton.overrideMesh ? mesh.skeleton.overrideMesh.getWorldMatrix() : mesh.getWorldMatrix();
+
+            let result = this._internalPickForMesh(null, rayFunction, mesh, world, false, false, trianglePredicate);
 
-        pickingInfos.push(result);
+            if (result) {
+                pickingInfos.push(result);
+            }
+        }
     }
 
     return pickingInfos;

+ 3 - 2
src/Meshes/abstractMesh.ts

@@ -1485,10 +1485,11 @@ export class AbstractMesh extends TransformNode implements IDisposable, ICullabl
      * @param fastCheck defines if fast mode (but less precise) must be used (false by default)
      * @param trianglePredicate defines an optional predicate used to select faces when a mesh intersection is detected
      * @param onlyBoundingInfo defines a boolean indicating if picking should only happen using bounding info (false by default)
+     * @param worldToUse defines the world matrix to use to get the world coordinate of the intersection point
      * @returns the picking info
      * @see https://doc.babylonjs.com/babylon101/intersect_collisions_-_mesh
      */
-    public intersects(ray: Ray, fastCheck?: boolean, trianglePredicate?: TrianglePickingPredicate, onlyBoundingInfo = false): PickingInfo {
+    public intersects(ray: Ray, fastCheck?: boolean, trianglePredicate?: TrianglePickingPredicate, onlyBoundingInfo = false, worldToUse?: Matrix): PickingInfo {
         var pickingInfo = new PickingInfo();
         const intersectionThreshold = this.getClassName() === "InstancedLinesMesh" || this.getClassName() === "LinesMesh" ? (this as any).intersectionThreshold : 0;
         const boundingInfo = this._boundingInfo;
@@ -1538,7 +1539,7 @@ export class AbstractMesh extends TransformNode implements IDisposable, ICullabl
 
         if (intersectInfo) {
             // Get picked point
-            const world = this.skeleton && this.skeleton.overrideMesh ? this.skeleton.overrideMesh.getWorldMatrix() : this.getWorldMatrix();
+            const world = worldToUse ?? (this.skeleton && this.skeleton.overrideMesh ? this.skeleton.overrideMesh.getWorldMatrix() : this.getWorldMatrix());
             const worldOrigin = TmpVectors.Vector3[0];
             const direction = TmpVectors.Vector3[1];
             Vector3.TransformCoordinatesToRef(ray.origin, world, worldOrigin);

+ 1 - 0
src/Meshes/mesh.ts

@@ -97,6 +97,7 @@ class _ThinInstanceDataStorage {
     public matrixBufferSize = 32 * 16; // let's start with a maximum of 32 thin instances
     public matrixData: Nullable<Float32Array>;
     public boundingVectors: Array<Vector3> = [];
+    public worldMatrices: Nullable<Matrix[]> = null;
 }
 
 /**

+ 32 - 1
src/Meshes/thinInstanceMesh.ts

@@ -6,6 +6,10 @@ import { Matrix, Vector3, TmpVectors } from '../Maths/math.vector';
 declare module "./mesh" {
     export interface Mesh {
         /**
+         * Gets or sets a boolean defining if we want picking to pick thin instances as well
+         */
+        thinInstanceEnablePicking: boolean;
+        /**
          * Creates a new thin instance
          * @param matrix the matrix or array of matrices (position, rotation, scale) of the thin instance(s) to create
          * @param refresh true to refresh the underlying gpu buffer (default: true). If you do multiple calls to this method in a row, set refresh to true only for the last call to save performance
@@ -59,6 +63,12 @@ declare module "./mesh" {
         thinInstanceSetBuffer(kind: string, buffer: Nullable<Float32Array>,  stride: number, staticBuffer: boolean): void;
 
         /**
+         * Gets the list of world matrices
+         * @return an array containing all the world matrices from the thin instances
+         */
+        thinInstanceGetWorldMatrices(): Matrix[];
+
+        /**
          * Synchronize the gpu buffers with a thin instance buffer. Call this method if you update later on the buffers passed to thinInstanceSetBuffer
          * @param kind name of the attribute to update. Use "matrix" to update the buffer of matrices
          */
@@ -83,7 +93,6 @@ declare module "./mesh" {
             vertexBuffers: {[key: string]: Nullable<VertexBuffer>},
             strides: {[key: string]: number}
         };
-
     }
 }
 
@@ -129,6 +138,10 @@ Mesh.prototype.thinInstanceSetMatrixAt = function(index: number, matrix: DeepImm
 
     matrix.copyToArray(matrixData, index * 16);
 
+    if (this._thinInstanceDataStorage.worldMatrices) {
+        this._thinInstanceDataStorage.worldMatrices[index] = matrix as Matrix;
+    }
+
     if (refresh) {
         this.thinInstanceBufferUpdated("matrix");
 
@@ -179,6 +192,7 @@ Mesh.prototype.thinInstanceSetBuffer = function(kind: string, buffer: Nullable<F
         this._thinInstanceDataStorage.matrixBuffer = null;
         this._thinInstanceDataStorage.matrixBufferSize = buffer ? buffer.length : 32 * stride;
         this._thinInstanceDataStorage.matrixData = buffer;
+        this._thinInstanceDataStorage.worldMatrices = null;
 
         if (buffer !== null) {
             this._thinInstanceDataStorage.instancesCount = buffer.length / stride;
@@ -234,6 +248,23 @@ Mesh.prototype.thinInstanceBufferUpdated = function(kind: string): void {
     }
 };
 
+Mesh.prototype.thinInstanceGetWorldMatrices = function(): Matrix[] {
+    if (!this._thinInstanceDataStorage.matrixData || !this._thinInstanceDataStorage.matrixBuffer) {
+        return [];
+    }
+    const matrixData = this._thinInstanceDataStorage.matrixData;
+
+    if (!this._thinInstanceDataStorage.worldMatrices) {
+        this._thinInstanceDataStorage.worldMatrices = new Array<Matrix>();
+
+        for (let i = 0; i < this._thinInstanceDataStorage.instancesCount; ++i) {
+            this._thinInstanceDataStorage.worldMatrices[i] = Matrix.FromArray(matrixData, i * 16);
+        }
+    }
+
+    return this._thinInstanceDataStorage.worldMatrices;
+};
+
 Mesh.prototype.thinInstanceRefreshBoundingInfo = function(forceRefreshParentInfo: boolean = false) {
     if (!this._thinInstanceDataStorage.matrixData || !this._thinInstanceDataStorage.matrixBuffer) {
         return;