浏览代码

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js

David Catuhe 7 年之前
父节点
当前提交
250bdba402
共有 2 个文件被更改,包括 218 次插入41 次删除
  1. 217 41
      src/Physics/babylon.physicsHelper.ts
  2. 1 0
      src/Physics/babylon.physicsImpostor.ts

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

@@ -1,21 +1,5 @@
 module BABYLON {
 
-    /**
-     * The strenght of the force in correspondence to the distance of the affected object
-     */
-    export enum PhysicsRadialImpulseFalloff {
-        Constant, // impulse is constant in strength across it's whole radius
-        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;
@@ -125,8 +109,32 @@ module BABYLON {
          * @param {number} radius the radius of the updraft
          * @param {number} strength the strength of the updraft
          * @param {number} height the height of the updraft
+         * @param {PhysicsUpdraftMode} updraftMode possible options: Center & Perpendicular. Defaults to Center
+         */
+        public updraft(origin: Vector3, radius: number, strength: number, height: number, updraftMode: PhysicsUpdraftMode = PhysicsUpdraftMode.Center): 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._scene, origin, radius, strength, height, updraftMode);
+
+            event.dispose(false);
+
+            return event;
+        }
+
+        /**
+         * @param {Vector3} origin the of the vortex
+         * @param {number} radius the radius of the vortex
+         * @param {number} strength the strength of the vortex
+         * @param {number} height   the height of the vortex
          */
-        public updraft(origin: Vector3, radius: number, strength: number, height: number, updraftMode: PhysicsUpdraftMode): Nullable<PhysicsUpdraftEvent> {
+        public vortex(origin: Vector3, radius: number, strength: number, height: number): Nullable<PhysicsVortexEvent> {
             if (!this._physicsEngine) {
                 Tools.Warn('Physics engine not enabled. Please enable the physics before you call the PhysicsHelper.');
                 return null;
@@ -136,7 +144,7 @@ module BABYLON {
                 return null;
             }
 
-            var event = new PhysicsUpdraftEvent(this._physicsEngine, this._scene, origin, radius, strength, height, updraftMode);
+            var event = new PhysicsVortexEvent(this._scene, origin, radius, strength, height);
 
             event.dispose(false);
 
@@ -144,6 +152,7 @@ module BABYLON {
         }
     }
 
+
     /***** Radial explosion *****/
 
     export class PhysicsRadialExplosionEvent {
@@ -188,8 +197,12 @@ module BABYLON {
             if (!this._intersectsWithSphere(impostor, origin, radius)) {
                 return null;
             }
+            
+            if (impostor.object.getClassName() !== 'Mesh') {
+                return null;
+            }
 
-            var impostorObject = (<Mesh>impostor.object);
+            var impostorObject = <Mesh>impostor.object;
             var impostorObjectCenter = impostor.getObjectCenter();
             var direction = impostorObjectCenter.subtract(origin);
 
@@ -256,6 +269,7 @@ module BABYLON {
 
     }
 
+
     /***** Gravitational Field *****/
 
     export class PhysicsGravitationalFieldEvent {
@@ -337,38 +351,22 @@ module BABYLON {
 
     }
 
-    export interface PhysicsGravitationalFieldEventData {
-        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;
+        constructor(private _scene: Scene, private _origin: Vector3, private _radius: number, private _strength: number, private _height: number, private _updraftMode: PhysicsUpdraftMode) {
+            this._physicsEngine = <PhysicsEngine>this._scene.getPhysicsEngine();
 
-            // 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);
 
@@ -381,7 +379,7 @@ module BABYLON {
 
         /**
          * Returns the data related to the updraft event (cylinder).
-         * @returns {PhysicsGravitationalFieldEventData}
+         * @returns {PhysicsUpdraftEventData}
          */
         public getData(): PhysicsUpdraftEventData {
             this._dataFetched = true;
@@ -481,20 +479,198 @@ module BABYLON {
 
     }
 
+
+    /***** Vortex *****/
+
+    export class PhysicsVortexEvent {
+
+        private _physicsEngine: PhysicsEngine;
+        private _originTop: Vector3 = Vector3.Zero(); // the most upper part of the cylinder
+        private _centripetalForceThreshold: number = 0.7; // at which distance, relative to the radius the centripetal forces should kick in
+        private _updraftMultiplier: number = 0.02;
+        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(private _scene: Scene, private _origin: Vector3, private _radius: number, private _strength: number, private _height: number) {
+            this._physicsEngine = <PhysicsEngine>this._scene.getPhysicsEngine();
+
+            this._origin.addToRef(new Vector3(0, this._height / 2, 0), this._cylinderPosition);
+            this._origin.addToRef(new Vector3(0, this._height, 0), this._originTop);
+
+            this._tickCallback = this._tick.bind(this);
+        }
+
+        /**
+         * Returns the data related to the vortex event (cylinder).
+         * @returns {PhysicsVortexEventData}
+         */
+        public getData(): PhysicsVortexEventData {
+            this._dataFetched = true;
+
+            return {
+                cylinder: this._cylinder,
+            };
+        }
+
+        /**
+         * Enables the vortex.
+         */
+        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;
+            }
+            
+            if (impostor.object.getClassName() !== 'Mesh') {
+                return null;
+            }
+
+            var impostorObject = <Mesh>impostor.object;
+            var impostorObjectCenter = impostor.getObjectCenter();
+            var originOnPlane = new Vector3(this._origin.x, impostorObjectCenter.y, this._origin.z); // the distance to the origin as if both objects were on a plane (Y-axis)
+            var originToImpostorDirection = impostorObjectCenter.subtract(originOnPlane);
+
+            var ray = new Ray(originOnPlane, originToImpostorDirection, this._radius);
+            var hit = ray.intersectsMesh(impostorObject);
+            var contactPoint = hit.pickedPoint;
+            if (!contactPoint) {
+                return null;
+            }
+
+            var absoluteDistanceFromOrigin = hit.distance / this._radius;
+            var perpendicularDirection = Vector3.Cross(originOnPlane, impostorObjectCenter).normalize();
+            var directionToOrigin = contactPoint.normalize();
+            if (absoluteDistanceFromOrigin > this._centripetalForceThreshold) {
+                directionToOrigin = directionToOrigin.negate();
+            }
+
+            // TODO: find a more physically based solution
+            if (absoluteDistanceFromOrigin > this._centripetalForceThreshold) {
+                var forceX = directionToOrigin.x * this._strength / 8;
+                var forceY = directionToOrigin.y * this._updraftMultiplier;
+                var forceZ = directionToOrigin.z * this._strength / 8;
+            } else {
+                var forceX = (perpendicularDirection.x + directionToOrigin.x) / 2;
+                var forceY = this._originTop.y * this._updraftMultiplier;
+                var forceZ = (perpendicularDirection.z + directionToOrigin.z) / 2;
+            }
+
+            var force = new Vector3(forceX, forceY, forceZ);
+            force = force.multiplyByFloats(this._strength, this._strength, this._strength);
+
+            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("vortexEventCylinder", {
+                    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);
+        }
+
+    }
+
+
+    /***** Enums *****/
+
+    /**
+    * The strenght of the force in correspondence to the distance of the affected object
+    */
+    export enum PhysicsRadialImpulseFalloff {
+        Constant, // impulse is constant in strength across it's whole radius
+        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
+    }
+
+
     /***** Data interfaces *****/
 
+    export interface PhysicsForceAndContactPoint {
+        force: Vector3;
+        contactPoint: Vector3;
+    }
+
     export interface PhysicsRadialExplosionEventData {
         sphere: Mesh;
         rays: Array<Ray>;
     }
 
-    export interface PhysicsForceAndContactPoint {
-        force: Vector3;
-        contactPoint: Vector3;
+    export interface PhysicsGravitationalFieldEventData {
+        sphere: Mesh;
     }
 
     export interface PhysicsUpdraftEventData {
         cylinder: Mesh;
     }
 
+    export interface PhysicsVortexEventData {
+        cylinder: Mesh;
+    }
+
 }

+ 1 - 0
src/Physics/babylon.physicsImpostor.ts

@@ -27,6 +27,7 @@ module BABYLON {
         rotate(axis: Vector3, amount: number, space?: Space): TransformNode;
         translate(axis: Vector3, distance: number, space?: Space): TransformNode;
         setAbsolutePosition(absolutePosition: Vector3): TransformNode;
+        getClassName(): string;
     }
 
     export class PhysicsImpostor {