Pārlūkot izejas kodu

Merge pull request #3217 from bobalazek/master

PhysicsHelper - Updraft effect
Raanan Weber 7 gadi atpakaļ
vecāks
revīzija
91303ea654
2 mainītis faili ar 218 papildinājumiem un 50 dzēšanām
  1. 1 1
      dist/preview release/babylon.d.ts
  2. 217 49
      src/Physics/babylon.physicsHelper.ts

+ 1 - 1
dist/preview release/babylon.d.ts

@@ -20816,4 +20816,4 @@ declare module BABYLON {
         static Parse(source: any, scene: Scene, rootUrl: string): StandardRenderingPipeline;
         static LuminanceSteps: number;
     }
-}
+}

+ 217 - 49
src/Physics/babylon.physicsHelper.ts

@@ -8,6 +8,14 @@ module BABYLON {
         Linear // impulse gets weaker if it's further from the origin
     }
 
+    /**
+     * The strenght of the force in correspondence to the distance of the affected object
+     */
+    export enum PhysicsUpdraftMode {
+        Center, // the upstream forces will pull towards the top center of the cylinder
+        Perpendicular // once a impostor is inside the cylinder, it will shoot out perpendicular from the ground of the cylinder
+    }
+
     export class PhysicsHelper {
 
         private _scene: Scene;
@@ -42,7 +50,6 @@ module BABYLON {
             var event = new PhysicsRadialExplosionEvent(this._scene);
 
             impostors.forEach(impostor => {
-
                 var impostorForceAndContactPoint = event.getImpostorForceAndContactPoint(impostor, origin, radius, strength, falloff);
                 if (!impostorForceAndContactPoint) {
                     return;
@@ -77,13 +84,12 @@ module BABYLON {
 
             impostors.forEach(impostor => {
                 var impostorForceAndContactPoint = event.getImpostorForceAndContactPoint(impostor, origin, radius, strength, falloff);
-
                 if (!impostorForceAndContactPoint) {
                     return;
                 }
 
                 impostor.applyForce(impostorForceAndContactPoint.force, impostorForceAndContactPoint.contactPoint);
-            })
+            });
 
             event.dispose(false);
 
@@ -113,6 +119,29 @@ module BABYLON {
 
             return event;
         }
+
+        /**
+         * @param {Vector3} origin the origin of the updraft
+         * @param {number} radius the radius of the updraft
+         * @param {number} strength the strength of the updraft
+         * @param {number} height the height of the updraft
+         */
+        public updraft(origin: Vector3, radius: number, strength: number, height: number, updraftMode: PhysicsUpdraftMode): Nullable<PhysicsUpdraftEvent> {
+            if (!this._physicsEngine) {
+                Tools.Warn('Physics engine not enabled. Please enable the physics before you call the PhysicsHelper.');
+                return null;
+            }
+
+            if (this._physicsEngine.getImpostors().length === 0) {
+                return null;
+            }
+
+            var event = new PhysicsUpdraftEvent(this._physicsEngine, this._scene, origin, radius, strength, height, updraftMode);
+
+            event.dispose(false);
+
+            return event;
+        }
     }
 
     /***** Radial explosion *****/
@@ -120,7 +149,8 @@ module BABYLON {
     export class PhysicsRadialExplosionEvent {
 
         private _scene: Scene;
-        private _radialSphere: Mesh; // create a sphere, so we can get the intersecting meshes inside
+        private _sphere: Mesh; // create a sphere, so we can get the intersecting meshes inside
+        private _sphereOptions: { segments: number, diameter: number } = { segments: 32, diameter: 1 }; // TODO: make configurable
         private _rays: Array<Ray> = [];
         private _dataFetched: boolean = false; // check if the data has been fetched. If not, do cleanup
 
@@ -129,14 +159,14 @@ module BABYLON {
         }
 
         /**
-         * Returns the data related to the radial explosion event (radialSphere & rays).
+         * Returns the data related to the radial explosion event (sphere & rays).
          * @returns {PhysicsRadialExplosionEventData}
          */
         public getData(): PhysicsRadialExplosionEventData {
             this._dataFetched = true;
 
             return {
-                radialSphere: this._radialSphere,
+                sphere: this._sphere,
                 rays: this._rays,
             };
         }
@@ -155,7 +185,7 @@ module BABYLON {
                 return null;
             }
 
-            if (!this._intersectsWithRadialSphere(impostor, origin, radius)) {
+            if (!this._intersectsWithSphere(impostor, origin, radius)) {
                 return null;
             }
 
@@ -187,16 +217,16 @@ module BABYLON {
         }
 
         /**
-         * Disposes the radialSphere.
+         * Disposes the sphere.
          * @param {bolean} force
          */
         public dispose(force: boolean = true) {
             if (force) {
-                this._radialSphere.dispose();
+                this._sphere.dispose();
             } else {
                 setTimeout(() => {
                     if (!this._dataFetched) {
-                        this._radialSphere.dispose();
+                        this._sphere.dispose();
                     }
                 }, 0);
             }
@@ -204,39 +234,28 @@ module BABYLON {
 
         /*** Helpers ***/
 
-        private _prepareRadialSphere(): void {
-            if (!this._radialSphere) {
-                this._radialSphere = MeshBuilder.CreateSphere("radialSphere", { segments: 32, diameter: 1 }, this._scene);
-                this._radialSphere.isVisible = false;
+        private _prepareSphere(): void {
+            if (!this._sphere) {
+                this._sphere = MeshBuilder.CreateSphere("radialExplosionEventSphere", this._sphereOptions, this._scene);
+                this._sphere.isVisible = false;
             }
         }
 
-        private _intersectsWithRadialSphere(impostor: PhysicsImpostor, origin: Vector3, radius: number): boolean {
+        private _intersectsWithSphere(impostor: PhysicsImpostor, origin: Vector3, radius: number): boolean {
             var impostorObject = <Mesh>impostor.object;
 
-            this._prepareRadialSphere();
+            this._prepareSphere();
 
-            this._radialSphere.position = origin;
-            this._radialSphere.scaling = new Vector3(radius * 2, radius * 2, radius * 2);
-            this._radialSphere._updateBoundingInfo();
-            this._radialSphere.computeWorldMatrix(true);
+            this._sphere.position = origin;
+            this._sphere.scaling = new Vector3(radius * 2, radius * 2, radius * 2);
+            this._sphere._updateBoundingInfo();
+            this._sphere.computeWorldMatrix(true);
 
-            return this._radialSphere.intersectsMesh(impostorObject, true);
+            return this._sphere.intersectsMesh(impostorObject, true);
         }
 
     }
 
-    export interface PhysicsRadialExplosionEventData {
-        radialSphere: Mesh;
-        rays: Array<Ray>;
-    }
-
-    export interface PhysicsForceAndContactPoint {
-        force: Vector3;
-        contactPoint: Vector3;
-    }
-
-
     /***** Gravitational Field *****/
 
     export class PhysicsGravitationalFieldEvent {
@@ -248,17 +267,10 @@ module BABYLON {
         private _strength: number;
         private _falloff: PhysicsRadialImpulseFallof;
         private _tickCallback: any;
-        private _radialSphere: Mesh;
+        private _sphere: Mesh;
         private _dataFetched: boolean = false; // check if the has been fetched the data. If not, do cleanup
 
-        constructor(
-            physicsHelper: PhysicsHelper,
-            scene: Scene,
-            origin: Vector3,
-            radius: number,
-            strength: number,
-            falloff: PhysicsRadialImpulseFallof = PhysicsRadialImpulseFallof.Constant
-        ) {
+        constructor(physicsHelper: PhysicsHelper, scene: Scene, origin: Vector3, radius: number, strength: number, falloff: PhysicsRadialImpulseFallof = PhysicsRadialImpulseFallof.Constant) {
             this._physicsHelper = physicsHelper;
             this._scene = scene;
             this._origin = origin;
@@ -269,14 +281,14 @@ module BABYLON {
         }
 
         /**
-         * Returns the data related to the gravitational field event (radialSphere).
+         * Returns the data related to the gravitational field event (sphere).
          * @returns {PhysicsGravitationalFieldEventData}
          */
         public getData(): PhysicsGravitationalFieldEventData {
             this._dataFetched = true;
 
             return {
-                radialSphere: this._radialSphere,
+                sphere: this._sphere,
             };
         }
 
@@ -296,16 +308,16 @@ module BABYLON {
         }
 
         /**
-         * Disposes the radialSphere.
+         * Disposes the sphere.
          * @param {bolean} force
          */
         public dispose(force: boolean = true) {
             if (force) {
-                this._radialSphere.dispose();
+                this._sphere.dispose();
             } else {
                 setTimeout(() => {
                     if (!this._dataFetched) {
-                        this._radialSphere.dispose();
+                        this._sphere.dispose();
                     }
                 }, 0);
             }
@@ -313,12 +325,12 @@ module BABYLON {
 
         private _tick() {
             // Since the params won't change, we fetch the event only once
-            if (this._radialSphere) {
+            if (this._sphere) {
                 this._physicsHelper.applyRadialExplosionForce(this._origin, this._radius, this._strength * -1, this._falloff);
             } else {
                 var radialExplosionEvent = this._physicsHelper.applyRadialExplosionForce(this._origin, this._radius, this._strength * -1, this._falloff);
                 if (radialExplosionEvent) {
-                    this._radialSphere = <Mesh>radialExplosionEvent.getData().radialSphere.clone('radialSphereClone');
+                    this._sphere = <Mesh>radialExplosionEvent.getData().sphere.clone('radialExplosionEventSphereClone');
                 }
             }
         }
@@ -326,7 +338,163 @@ module BABYLON {
     }
 
     export interface PhysicsGravitationalFieldEventData {
-        radialSphere: Mesh;
+        sphere: Mesh;
+    }
+
+    /***** Updraft *****/
+
+    export class PhysicsUpdraftEvent {
+
+        private _physicsEngine: PhysicsEngine;
+        private _scene: Scene;
+        private _origin: Vector3;
+        private _originTop: Vector3 = Vector3.Zero(); // the most upper part of the cylinder
+        private _originDirection: Vector3 = Vector3.Zero(); // used if the updraftMode is perpendicular
+        private _radius: number;
+        private _strength: number;
+        private _height: number;
+        private _updraftMode: PhysicsUpdraftMode;
+        private _tickCallback: any;
+        private _cylinder: Mesh;
+        private _cylinderPosition: Vector3 = Vector3.Zero(); // to keep the cylinders position, because normally the origin is in the center and not on the bottom
+        private _dataFetched: boolean = false; // check if the has been fetched the data. If not, do cleanup
+ 
+        constructor(physicsEngine: PhysicsEngine, scene: Scene, origin: Vector3, radius: number, strength: number, height: number, updraftMode: PhysicsUpdraftMode) {
+            this._physicsEngine = physicsEngine;
+            this._scene = scene;
+            this._origin = origin;
+            this._radius = radius;
+            this._strength = strength;
+            this._height = height;
+            this._updraftMode = updraftMode;
+
+            // TODO: for this._cylinderPosition & this._originTop, take rotation into account
+            this._origin.addToRef(new Vector3(0, this._height / 2, 0), this._cylinderPosition);
+            this._origin.addToRef(new Vector3(0, this._height, 0), this._originTop);
+
+            if (this._updraftMode === PhysicsUpdraftMode.Perpendicular) {
+                this._originDirection = this._origin.subtract(this._originTop).normalize();
+            }
+ 
+            this._tickCallback = this._tick.bind(this);
+        }
+
+        /**
+         * Returns the data related to the updraft event (cylinder).
+         * @returns {PhysicsGravitationalFieldEventData}
+         */
+        public getData(): PhysicsUpdraftEventData {
+            this._dataFetched = true;
+
+            return {
+                cylinder: this._cylinder,
+            };
+        }
+
+        /**
+         * Enables the updraft.
+         */
+        public enable() {
+            this._tickCallback.call(this);
+            this._scene.registerBeforeRender(this._tickCallback);
+        }
+
+        /**
+         * Disables the cortex.
+         */
+        public disable() {
+            this._scene.unregisterBeforeRender(this._tickCallback);
+        }
+
+        /**
+         * Disposes the sphere.
+         * @param {bolean} force
+         */
+        public dispose(force: boolean = true) {
+            if (force) {
+                this._cylinder.dispose();
+            } else {
+                setTimeout(() => {
+                    if (!this._dataFetched) {
+                        this._cylinder.dispose();
+                    }
+                }, 0);
+            }
+        }
+
+        private getImpostorForceAndContactPoint(impostor: PhysicsImpostor): Nullable<PhysicsForceAndContactPoint> {
+            if (impostor.mass === 0) {
+                return null;
+            }
+
+            if (!this._intersectsWithCylinder(impostor)) {
+                return null;
+            }
+
+            var impostorObjectCenter = impostor.getObjectCenter();
+
+            if (this._updraftMode === PhysicsUpdraftMode.Perpendicular) {
+                var direction = this._originDirection;
+            } else {
+                var direction = impostorObjectCenter.subtract(this._originTop);
+            }
+
+            var multiplier = this._strength * -1;
+
+            var force = direction.multiplyByFloats(multiplier, multiplier, multiplier);
+
+            return { force: force, contactPoint: impostorObjectCenter };
+        }
+
+        private _tick() {
+            this._physicsEngine.getImpostors().forEach(impostor => {
+                var impostorForceAndContactPoint = this.getImpostorForceAndContactPoint(impostor);
+                if (!impostorForceAndContactPoint) {
+                    return;
+                }
+
+                impostor.applyForce(impostorForceAndContactPoint.force, impostorForceAndContactPoint.contactPoint);
+            });
+        }
+
+        /*** Helpers ***/
+
+        private _prepareCylinder(): void {
+            if (!this._cylinder) {
+                this._cylinder = MeshBuilder.CreateCylinder("updraftEventCylinder", {
+                    height: this._height,
+                    diameter: this._radius * 2,
+                }, this._scene);
+                this._cylinder.isVisible = false;
+            }
+        }
+
+        private _intersectsWithCylinder(impostor: PhysicsImpostor): boolean {
+            var impostorObject = <Mesh>impostor.object;
+
+            this._prepareCylinder();
+
+            this._cylinder.position = this._cylinderPosition;
+
+            return this._cylinder.intersectsMesh(impostorObject, true);
+        }
+
+    }
+
+    /***** Data interfaces *****/
+
+    export interface PhysicsRadialExplosionEventData {
+        sphere: Mesh;
+        rays: Array<Ray>;
+    }
+
+    export interface PhysicsForceAndContactPoint {
+        force: Vector3;
+        contactPoint: Vector3;
+    }
+
+    export interface PhysicsUpdraftEventData {
+        cylinder: Mesh;
     }
 
 }