|
@@ -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;
|
|
|
+ }
|
|
|
+
|
|
|
}
|