Преглед изворни кода

Merge pull request #3186 from bobalazek/master

Radial impulse/force
Raanan Weber пре 7 година
родитељ
комит
8aaf7f191a

+ 2 - 1
Tools/Gulp/config.json

@@ -926,6 +926,7 @@
                 "../../src/Physics/babylon.physicsJoint.js",
                 "../../src/Physics/babylon.physicsImpostor.js",
                 "../../src/Physics/babylon.physicsEngine.js",
+                "../../src/Physics/babylon.physicsHelper.js",
                 "../../src/Physics/Plugins/babylon.cannonJSPlugin.js",
                 "../../src/Physics/Plugins/babylon.oimoJSPlugin.js"
             ],
@@ -1661,4 +1662,4 @@
             "distOutputDirectory": "/inspector/"
         }
     }
-}
+}

Разлика између датотеке није приказан због своје велике величине
+ 3600 - 3489
dist/preview release/babylon.d.ts


+ 13 - 0
package-lock.json

@@ -0,0 +1,13 @@
+{
+    "name": "babylonjs",
+    "version": "3.1.0-beta5",
+    "lockfileVersion": 1,
+    "requires": true,
+    "dependencies": {
+        "cannon": {
+            "version": "0.6.2",
+            "resolved": "https://registry.npmjs.org/cannon/-/cannon-0.6.2.tgz",
+            "integrity": "sha1-HnvHLdWEGYLzwQTCvFeL+k+xxXI="
+        }
+    }
+}

+ 1 - 1
package.json

@@ -67,4 +67,4 @@
     "readmeFilename": "README.md",
     "_id": "babylonjs@3.1.0-alpha2",
     "_from": "babylonjs@"
-}
+}

+ 7 - 1
src/Physics/babylon.physicsEngine.ts

@@ -61,7 +61,7 @@
 
         private _impostors: Array<PhysicsImpostor> = [];
         private _joints: Array<PhysicsImpostorJoint> = [];
-
+        
         /**
          * Adding a new impostor for the impostor tracking.
          * This will be done by the impostor itself.
@@ -146,6 +146,10 @@
         public getPhysicsPlugin(): IPhysicsEnginePlugin {
             return this._physicsPlugin;
         }
+        
+        public getImpostors(): Array<PhysicsImpostor> {
+            return this._impostors;
+        }
 
         public getImpostorForPhysicsObject(object: IPhysicsEnabledObject): Nullable<PhysicsImpostor> {
             for (var i = 0; i < this._impostors.length; ++i) {
@@ -166,6 +170,7 @@
 
             return null;
         }
+
     }
 
     export interface IPhysicsEnginePlugin {
@@ -205,4 +210,5 @@
         syncMeshWithImpostor(mesh:AbstractMesh, impostor:PhysicsImpostor): void;
         dispose(): void;
     }
+    
 }

+ 373 - 0
src/Physics/babylon.physicsHelper.ts

@@ -0,0 +1,373 @@
+module BABYLON {
+    
+    /**
+     * The strenght of the force in correspondence to the distance of the affected object
+     */
+    export enum PhysicsRadialImpulseFallof {
+        Constant, // impulse is constant in strength across it's whole radius
+        Linear // impulse gets weaker if it's further from the origin
+    }
+    
+    export class PhysicsHelper {
+        
+        private _scene: Scene;
+        private _physicsEngine: Nullable<PhysicsEngine>;
+
+        constructor(scene: Scene) {
+            this._scene = scene;
+            this._physicsEngine = this._scene.getPhysicsEngine();
+            
+            if (!this._physicsEngine) {
+                Tools.Warn('Physics engine not enabled. Please enable the physics before you can use the methods.');
+            }
+        }
+        
+        /**
+         * @param {Vector3} origin the origin of the explosion
+         * @param {number} radius the explosion radius
+         * @param {number} strength the explosion strength
+         * @param {PhysicsRadialImpulseFallof} falloff possible options: Constant & Linear. Defaults to Constant
+         */
+        public applyRadialExplosionImpulse(origin: Vector3, radius: number, strength: number, falloff: PhysicsRadialImpulseFallof = PhysicsRadialImpulseFallof.Constant) {
+            if (!this._physicsEngine) {
+                Tools.Warn('Physics engine not enabled. Please enable the physics before you call this method.');
+                return null;
+            }
+            
+            var impostors = this._physicsEngine.getImpostors();
+            if (impostors.length === 0) {
+                return null;
+            }
+
+            var event = new PhysicsRadialExplosionEvent(this._scene);
+
+            for (var i = 0; i < impostors.length; ++i) {
+                var impostor = impostors[i];
+                var impostorForceAndContactPoint = event.getImpostorForceAndContactPoint(
+                    impostor,
+                    origin,
+                    radius,
+                    strength,
+                    falloff
+                );
+                if (impostorForceAndContactPoint === null) {
+                    continue;
+                }
+
+                impostor.applyImpulse(
+                    impostorForceAndContactPoint.force,
+                    impostorForceAndContactPoint.contactPoint
+                );
+            }
+
+            event.cleanup(false);
+
+            return event;
+        }
+
+        /**
+         * @param {Vector3} origin the origin of the explosion
+         * @param {number} radius the explosion radius
+         * @param {number} strength the explosion strength
+         * @param {PhysicsRadialImpulseFallof} falloff possible options: Constant & Linear. Defaults to Constant
+         */
+        public applyRadialExplosionForce(origin: Vector3, radius: number, strength: number, falloff: PhysicsRadialImpulseFallof = PhysicsRadialImpulseFallof.Constant) {
+            if (!this._physicsEngine) {
+                Tools.Warn('Physics engine not enabled. Please enable the physics before you call the PhysicsHelper.');
+                return null;
+            }
+            
+            var impostors = this._physicsEngine.getImpostors();
+            if (impostors.length === 0) {
+                return null;
+            }
+
+            var event = new PhysicsRadialExplosionEvent(this._scene);
+
+            for (var i = 0; i < impostors.length; ++i) {
+                var impostor = impostors[i];
+                var impostorForceAndContactPoint = event.getImpostorForceAndContactPoint(
+                    impostor,
+                    origin,
+                    radius,
+                    strength,
+                    falloff
+                );
+                if (impostorForceAndContactPoint === null) {
+                    continue;
+                }
+
+                impostor.applyForce(
+                    impostorForceAndContactPoint.force,
+                    impostorForceAndContactPoint.contactPoint
+                );
+            }
+
+            event.cleanup(false);
+
+            return event;
+        }
+
+        /**
+         * @param {Vector3} origin the origin of the explosion
+         * @param {number} radius the explosion radius
+         * @param {number} strength the explosion strength
+         * @param {PhysicsRadialImpulseFallof} falloff possible options: Constant & Linear. Defaults to Constant
+         */
+        public gravitationalField(origin: Vector3, radius: number, strength: number, falloff: PhysicsRadialImpulseFallof = PhysicsRadialImpulseFallof.Constant) {
+            if (!this._physicsEngine) {
+                Tools.Warn('Physics engine not enabled. Please enable the physics before you call the PhysicsHelper.');
+                return null;
+            }
+
+            var impostors = this._physicsEngine.getImpostors();
+            if (impostors.length === 0) {
+                return null;
+            }
+
+            var event = new PhysicsGravitationalFieldEvent(
+                this,
+                this._scene,
+                origin,
+                radius,
+                strength,
+                falloff
+            );
+
+            event.cleanup(false);
+
+            return event;
+        }
+    }
+
+    /***** Radial explosion *****/
+
+    export class PhysicsRadialExplosionEvent {
+        
+        private _scene: Scene;
+        private _radialSphere: Mesh; // create a sphere, so we can get the intersecting meshes inside
+        private _rays: Array<Ray> = [];
+        private _dataFetched: boolean = false; // check if the data has been fetched. If not, do cleanup
+
+        constructor(scene: Scene) {
+            this._scene = scene;
+        }
+
+        /**
+         * Returns the data related to the radial explosion event (radialSphere & rays).
+         * @returns {PhysicsRadialExplosionEventData}
+         */
+        public getData(): PhysicsRadialExplosionEventData {
+            this._dataFetched = true;
+
+            return {
+                radialSphere: this._radialSphere,
+                rays: this._rays,
+            };
+        }
+
+        /**
+         * Returns the force and contact point of the impostor or false, if the impostor is not affected by the force/impulse.
+         * @param impostor 
+         * @param {Vector3} origin the origin of the explosion
+         * @param {number} radius the explosion radius
+         * @param {number} strength the explosion strength
+         * @param {PhysicsRadialImpulseFallof} falloff possible options: Constant & Linear
+         * @returns {Nullable<PhysicsForceAndContactPoint>}
+         */
+        public getImpostorForceAndContactPoint(impostor: PhysicsImpostor, origin: Vector3, radius: number, strength: number, falloff: PhysicsRadialImpulseFallof): Nullable<PhysicsForceAndContactPoint> {
+            if (impostor.mass === 0) {
+                return null;
+            }
+
+            if (!this._intersectsWithRadialSphere(impostor, origin, radius)) {
+                return null;
+            }
+
+            var impostorObject = (<Mesh>impostor.object);
+            var impostorObjectCenter = impostor.getObjectCenter();
+            var direction = impostorObjectCenter.subtract(origin);
+
+            var ray = new Ray(origin, direction, radius);
+            this._rays.push(ray);
+            var hit = ray.intersectsMesh(impostorObject);
+
+            var contactPoint = hit.pickedPoint;
+            if (!contactPoint) {
+                return null;
+            }
+
+            var distanceFromOrigin = BABYLON.Vector3.Distance(origin, contactPoint);
+            if (distanceFromOrigin > radius) {
+                return null;
+            }
+
+            var multiplier = falloff === PhysicsRadialImpulseFallof.Constant
+                ? strength
+                : strength * (1 - (distanceFromOrigin / radius));
+
+            var force = direction.multiplyByFloats(multiplier, multiplier, multiplier);
+
+            return { force: force, contactPoint: contactPoint };
+        }
+
+        /**
+         * Disposes the radialSphere.
+         * @param {bolean} force
+         */
+        public cleanup(force: boolean = true) {
+            if (force) {
+                this._radialSphere.dispose();
+            } else {
+                setTimeout(() => {
+                    if (!this._dataFetched) {
+                        this._radialSphere.dispose();
+                    }
+                }, 0);
+            }
+        }
+
+        /*** Helpers ***/
+
+        private _prepareRadialSphere() {
+            if (!this._radialSphere) {
+                this._radialSphere = BABYLON.Mesh.CreateSphere(
+                    "radialSphere",
+                    32,
+                    1,
+                    this._scene
+                );
+                this._radialSphere.isVisible = false;
+            }
+        }
+
+        private _intersectsWithRadialSphere(impostor: PhysicsImpostor, origin: Vector3, radius: number): boolean {
+            var impostorObject = <Mesh>impostor.object;
+
+            this._prepareRadialSphere();
+
+            this._radialSphere.position = origin;
+            this._radialSphere.scaling = new Vector3(radius * 2, radius * 2, radius * 2);
+            this._radialSphere._updateBoundingInfo();
+            this._radialSphere.computeWorldMatrix(true);
+
+            return this._radialSphere.intersectsMesh(
+                impostorObject,
+                true
+            );
+        }
+
+    }
+
+    export interface PhysicsRadialExplosionEventData {
+        radialSphere: Mesh;
+        rays: Array<Ray>;
+    }
+
+    export interface PhysicsForceAndContactPoint {
+        force: Vector3;
+        contactPoint: Vector3;
+    }
+
+
+    /***** Gravitational Field *****/
+
+    export class PhysicsGravitationalFieldEvent {
+
+        private _physicsHelper: PhysicsHelper;
+        private _scene: Scene;
+        private _origin: Vector3;
+        private _radius: number;
+        private _strength: number;
+        private _falloff: PhysicsRadialImpulseFallof;
+        private _tickCallback: any;
+        private _radialSphere: 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
+        ) {
+            this._physicsHelper = physicsHelper;
+            this._scene = scene;
+            this._origin = origin;
+            this._radius = radius;
+            this._strength = strength;
+            this._falloff = falloff;
+            this._tickCallback = this._tick.bind(this);
+        }
+
+        /**
+         * Returns the data related to the gravitational field event (radialSphere).
+         * @returns {PhysicsGravitationalFieldEventData}
+         */
+        public getData(): PhysicsGravitationalFieldEventData {
+            this._dataFetched = true;
+
+            return {
+                radialSphere: this._radialSphere,
+            };
+        }
+
+        /**
+         * Enables the gravitational field.
+         */
+        public enable() {
+            this._tickCallback.call(this);
+            this._scene.registerBeforeRender(this._tickCallback);
+        }
+
+        /**
+         * Disables the gravitational field.
+         */
+        public disable() {
+            this._scene.unregisterBeforeRender(this._tickCallback);
+        }
+
+        /**
+         * Disposes the radialSphere.
+         * @param {bolean} force
+         */
+        public cleanup(force: boolean = true) {
+            if (force) {
+                this._radialSphere.dispose();
+            } else {
+                setTimeout(() => {
+                    if (!this._dataFetched) {
+                        this._radialSphere.dispose();
+                    }
+                }, 0);
+            }
+        }
+
+        private _tick() {
+            // Since the params won't change, we fetch the event only once
+            if (this._radialSphere) {
+                this._physicsHelper.applyRadialExplosionForce(
+                    this._origin,
+                    this._radius,
+                    this._strength * -1,
+                    this._falloff
+                );
+            } else {
+                var radialExplosionEvent = <PhysicsRadialExplosionEvent>this._physicsHelper.applyRadialExplosionForce(
+                    this._origin,
+                    this._radius,
+                    this._strength * -1,
+                    this._falloff
+                );
+                this._radialSphere = <Mesh>radialExplosionEvent.getData().radialSphere.clone('radialSphereClone');
+            }
+        }
+
+    }
+
+    export interface PhysicsGravitationalFieldEventData {
+        radialSphere: Mesh;
+    }
+
+}