physicsHelper.ts 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934
  1. import { Nullable } from "../types";
  2. import { Logger } from "../Misc/logger";
  3. import { Vector3 } from "../Maths/math";
  4. import { AbstractMesh } from "../Meshes/abstractMesh";
  5. import { Mesh } from "../Meshes/mesh";
  6. import { SphereBuilder } from "../Meshes/Builders/sphereBuilder";
  7. import { CylinderBuilder } from "../Meshes/Builders/cylinderBuilder";
  8. import { Ray } from "../Culling/ray";
  9. import { Scene } from "../scene";
  10. import { IPhysicsEngine } from "./IPhysicsEngine";
  11. import { PhysicsEngine } from "./physicsEngine";
  12. import { PhysicsImpostor } from "./physicsImpostor";
  13. /**
  14. * A helper for physics simulations
  15. * @see https://doc.babylonjs.com/how_to/using_the_physics_engine#further-functionality-of-the-impostor-class
  16. */
  17. export class PhysicsHelper {
  18. private _scene: Scene;
  19. private _physicsEngine: Nullable<IPhysicsEngine>;
  20. /**
  21. * Initializes the Physics helper
  22. * @param scene Babylon.js scene
  23. */
  24. constructor(scene: Scene) {
  25. this._scene = scene;
  26. this._physicsEngine = this._scene.getPhysicsEngine();
  27. if (!this._physicsEngine) {
  28. Logger.Warn('Physics engine not enabled. Please enable the physics before you can use the methods.');
  29. return;
  30. }
  31. }
  32. /**
  33. * Applies a radial explosion impulse
  34. * @param origin the origin of the explosion
  35. * @param radiusOrEventOptions the radius or the options of radial explosion
  36. * @param strength the explosion strength
  37. * @param falloff possible options: Constant & Linear. Defaults to Constant
  38. * @returns A physics radial explosion event, or null
  39. */
  40. public applyRadialExplosionImpulse(origin: Vector3, radiusOrEventOptions: number | PhysicsRadialExplosionEventOptions, strength?: number, falloff?: PhysicsRadialImpulseFalloff): Nullable<PhysicsRadialExplosionEvent> {
  41. if (!this._physicsEngine) {
  42. Logger.Warn('Physics engine not enabled. Please enable the physics before you call this method.');
  43. return null;
  44. }
  45. var impostors = this._physicsEngine.getImpostors();
  46. if (impostors.length === 0) {
  47. return null;
  48. }
  49. if (typeof radiusOrEventOptions === 'number') {
  50. radiusOrEventOptions = new PhysicsRadialExplosionEventOptions();
  51. radiusOrEventOptions.radius = <number><any>radiusOrEventOptions;
  52. radiusOrEventOptions.strength = strength || radiusOrEventOptions.strength;
  53. radiusOrEventOptions.falloff = falloff || radiusOrEventOptions.falloff;
  54. }
  55. var event = new PhysicsRadialExplosionEvent(this._scene, radiusOrEventOptions);
  56. var affectedImpostorsWithData = Array<PhysicsAffectedImpostorWithData>();
  57. impostors.forEach((impostor) => {
  58. var impostorHitData = event.getImpostorHitData(impostor, origin);
  59. if (!impostorHitData) {
  60. return;
  61. }
  62. impostor.applyImpulse(impostorHitData.force, impostorHitData.contactPoint);
  63. affectedImpostorsWithData.push({
  64. impostor: impostor,
  65. hitData: impostorHitData,
  66. });
  67. });
  68. event.triggerAffectedImpostorsCallback(affectedImpostorsWithData);
  69. event.dispose(false);
  70. return event;
  71. }
  72. /**
  73. * Applies a radial explosion force
  74. * @param origin the origin of the explosion
  75. * @param radiusOrEventOptions the radius or the options of radial explosion
  76. * @param strength the explosion strength
  77. * @param falloff possible options: Constant & Linear. Defaults to Constant
  78. * @returns A physics radial explosion event, or null
  79. */
  80. public applyRadialExplosionForce(origin: Vector3, radiusOrEventOptions: number | PhysicsRadialExplosionEventOptions, strength?: number, falloff?: PhysicsRadialImpulseFalloff): Nullable<PhysicsRadialExplosionEvent> {
  81. if (!this._physicsEngine) {
  82. Logger.Warn('Physics engine not enabled. Please enable the physics before you call the PhysicsHelper.');
  83. return null;
  84. }
  85. var impostors = this._physicsEngine.getImpostors();
  86. if (impostors.length === 0) {
  87. return null;
  88. }
  89. if (typeof radiusOrEventOptions === 'number') {
  90. radiusOrEventOptions = new PhysicsRadialExplosionEventOptions();
  91. radiusOrEventOptions.radius = <number><any>radiusOrEventOptions;
  92. radiusOrEventOptions.strength = strength || radiusOrEventOptions.strength;
  93. radiusOrEventOptions.falloff = falloff || radiusOrEventOptions.falloff;
  94. }
  95. var event = new PhysicsRadialExplosionEvent(this._scene, radiusOrEventOptions);
  96. var affectedImpostorsWithData = Array<PhysicsAffectedImpostorWithData>();
  97. impostors.forEach((impostor) => {
  98. var impostorHitData = event.getImpostorHitData(impostor, origin);
  99. if (!impostorHitData) {
  100. return;
  101. }
  102. impostor.applyForce(impostorHitData.force, impostorHitData.contactPoint);
  103. affectedImpostorsWithData.push({
  104. impostor: impostor,
  105. hitData: impostorHitData,
  106. });
  107. });
  108. event.triggerAffectedImpostorsCallback(affectedImpostorsWithData);
  109. event.dispose(false);
  110. return event;
  111. }
  112. /**
  113. * Creates a gravitational field
  114. * @param origin the origin of the explosion
  115. * @param radiusOrEventOptions the radius or the options of radial explosion
  116. * @param strength the explosion strength
  117. * @param falloff possible options: Constant & Linear. Defaults to Constant
  118. * @returns A physics gravitational field event, or null
  119. */
  120. public gravitationalField(origin: Vector3, radiusOrEventOptions: number | PhysicsRadialExplosionEventOptions, strength?: number, falloff?: PhysicsRadialImpulseFalloff): Nullable<PhysicsGravitationalFieldEvent> {
  121. if (!this._physicsEngine) {
  122. Logger.Warn('Physics engine not enabled. Please enable the physics before you call the PhysicsHelper.');
  123. return null;
  124. }
  125. var impostors = this._physicsEngine.getImpostors();
  126. if (impostors.length === 0) {
  127. return null;
  128. }
  129. if (typeof radiusOrEventOptions === 'number') {
  130. radiusOrEventOptions = new PhysicsRadialExplosionEventOptions();
  131. radiusOrEventOptions.radius = <number><any>radiusOrEventOptions;
  132. radiusOrEventOptions.strength = strength || radiusOrEventOptions.strength;
  133. radiusOrEventOptions.falloff = falloff || radiusOrEventOptions.falloff;
  134. }
  135. var event = new PhysicsGravitationalFieldEvent(this, this._scene, origin, radiusOrEventOptions);
  136. event.dispose(false);
  137. return event;
  138. }
  139. /**
  140. * Creates a physics updraft event
  141. * @param origin the origin of the updraft
  142. * @param radiusOrEventOptions the radius or the options of the updraft
  143. * @param strength the strength of the updraft
  144. * @param height the height of the updraft
  145. * @param updraftMode possible options: Center & Perpendicular. Defaults to Center
  146. * @returns A physics updraft event, or null
  147. */
  148. public updraft(origin: Vector3, radiusOrEventOptions: number | PhysicsUpdraftEventOptions, strength?: number, height?: number, updraftMode?: PhysicsUpdraftMode): Nullable<PhysicsUpdraftEvent> {
  149. if (!this._physicsEngine) {
  150. Logger.Warn('Physics engine not enabled. Please enable the physics before you call the PhysicsHelper.');
  151. return null;
  152. }
  153. if (this._physicsEngine.getImpostors().length === 0) {
  154. return null;
  155. }
  156. if (typeof radiusOrEventOptions === 'number') {
  157. radiusOrEventOptions = new PhysicsUpdraftEventOptions();
  158. radiusOrEventOptions.radius = <number><any>radiusOrEventOptions;
  159. radiusOrEventOptions.strength = strength || radiusOrEventOptions.strength;
  160. radiusOrEventOptions.height = height || radiusOrEventOptions.height;
  161. radiusOrEventOptions.updraftMode = updraftMode || radiusOrEventOptions.updraftMode;
  162. }
  163. var event = new PhysicsUpdraftEvent(this._scene, origin, radiusOrEventOptions);
  164. event.dispose(false);
  165. return event;
  166. }
  167. /**
  168. * Creates a physics vortex event
  169. * @param origin the of the vortex
  170. * @param radiusOrEventOptions the radius or the options of the vortex
  171. * @param strength the strength of the vortex
  172. * @param height the height of the vortex
  173. * @returns a Physics vortex event, or null
  174. * A physics vortex event or null
  175. */
  176. public vortex(origin: Vector3, radiusOrEventOptions: number | PhysicsVortexEventOptions, strength?: number, height?: number): Nullable<PhysicsVortexEvent> {
  177. if (!this._physicsEngine) {
  178. Logger.Warn('Physics engine not enabled. Please enable the physics before you call the PhysicsHelper.');
  179. return null;
  180. }
  181. if (this._physicsEngine.getImpostors().length === 0) {
  182. return null;
  183. }
  184. if (typeof radiusOrEventOptions === 'number') {
  185. radiusOrEventOptions = new PhysicsVortexEventOptions();
  186. radiusOrEventOptions.radius = <number><any>radiusOrEventOptions;
  187. radiusOrEventOptions.strength = strength || radiusOrEventOptions.strength;
  188. radiusOrEventOptions.height = height || radiusOrEventOptions.height;
  189. }
  190. var event = new PhysicsVortexEvent(this._scene, origin, radiusOrEventOptions);
  191. event.dispose(false);
  192. return event;
  193. }
  194. }
  195. /**
  196. * Represents a physics radial explosion event
  197. */
  198. class PhysicsRadialExplosionEvent {
  199. private _sphere: Mesh; // create a sphere, so we can get the intersecting meshes inside
  200. private _dataFetched: boolean = false; // check if the data has been fetched. If not, do cleanup
  201. /**
  202. * Initializes a radial explosioin event
  203. * @param _scene BabylonJS scene
  204. * @param _options The options for the vortex event
  205. */
  206. constructor(private _scene: Scene, private _options: PhysicsRadialExplosionEventOptions) {
  207. this._options = { ...(new PhysicsRadialExplosionEventOptions()), ...this._options };
  208. }
  209. /**
  210. * Returns the data related to the radial explosion event (sphere).
  211. * @returns The radial explosion event data
  212. */
  213. public getData(): PhysicsRadialExplosionEventData {
  214. this._dataFetched = true;
  215. return {
  216. sphere: this._sphere,
  217. };
  218. }
  219. /**
  220. * Returns the force and contact point of the impostor or false, if the impostor is not affected by the force/impulse.
  221. * @param impostor A physics imposter
  222. * @param origin the origin of the explosion
  223. * @returns {Nullable<PhysicsHitData>} A physics force and contact point, or null
  224. */
  225. public getImpostorHitData(impostor: PhysicsImpostor, origin: Vector3): Nullable<PhysicsHitData> {
  226. if (impostor.mass === 0) {
  227. return null;
  228. }
  229. if (!this._intersectsWithSphere(impostor, origin, this._options.radius)) {
  230. return null;
  231. }
  232. if (impostor.object.getClassName() !== 'Mesh' && impostor.object.getClassName() !== 'InstancedMesh') {
  233. return null;
  234. }
  235. var impostorObjectCenter = impostor.getObjectCenter();
  236. var direction = impostorObjectCenter.subtract(origin);
  237. var ray = new Ray(origin, direction, this._options.radius);
  238. var hit = ray.intersectsMesh(<AbstractMesh>impostor.object);
  239. var contactPoint = hit.pickedPoint;
  240. if (!contactPoint) {
  241. return null;
  242. }
  243. var distanceFromOrigin = Vector3.Distance(origin, contactPoint);
  244. if (distanceFromOrigin > this._options.radius) {
  245. return null;
  246. }
  247. var multiplier = this._options.falloff === PhysicsRadialImpulseFalloff.Constant
  248. ? this._options.strength
  249. : this._options.strength * (1 - (distanceFromOrigin / this._options.radius));
  250. var force = direction.multiplyByFloats(multiplier, multiplier, multiplier);
  251. return { force: force, contactPoint: contactPoint, distanceFromOrigin: distanceFromOrigin };
  252. }
  253. /**
  254. * Triggers affecterd impostors callbacks
  255. * @param affectedImpostorsWithData defines the list of affected impostors (including associated data)
  256. */
  257. public triggerAffectedImpostorsCallback(affectedImpostorsWithData: Array<PhysicsAffectedImpostorWithData>) {
  258. if (this._options.affectedImpostorsCallback) {
  259. this._options.affectedImpostorsCallback(affectedImpostorsWithData);
  260. }
  261. }
  262. /**
  263. * Disposes the sphere.
  264. * @param force Specifies if the sphere should be disposed by force
  265. */
  266. public dispose(force: boolean = true) {
  267. if (force) {
  268. this._sphere.dispose();
  269. } else {
  270. setTimeout(() => {
  271. if (!this._dataFetched) {
  272. this._sphere.dispose();
  273. }
  274. }, 0);
  275. }
  276. }
  277. /*** Helpers ***/
  278. private _prepareSphere(): void {
  279. if (!this._sphere) {
  280. this._sphere = SphereBuilder.CreateSphere("radialExplosionEventSphere", this._options.sphere, this._scene);
  281. this._sphere.isVisible = false;
  282. }
  283. }
  284. private _intersectsWithSphere(impostor: PhysicsImpostor, origin: Vector3, radius: number): boolean {
  285. var impostorObject = <AbstractMesh>impostor.object;
  286. this._prepareSphere();
  287. this._sphere.position = origin;
  288. this._sphere.scaling = new Vector3(radius * 2, radius * 2, radius * 2);
  289. this._sphere._updateBoundingInfo();
  290. this._sphere.computeWorldMatrix(true);
  291. return this._sphere.intersectsMesh(impostorObject, true);
  292. }
  293. }
  294. /**
  295. * Represents a gravitational field event
  296. */
  297. class PhysicsGravitationalFieldEvent {
  298. private _tickCallback: any;
  299. private _sphere: Mesh;
  300. private _dataFetched: boolean = false; // check if the has been fetched the data. If not, do cleanup
  301. /**
  302. * Initializes the physics gravitational field event
  303. * @param _physicsHelper A physics helper
  304. * @param _scene BabylonJS scene
  305. * @param _origin The origin position of the gravitational field event
  306. * @param _options The options for the vortex event
  307. */
  308. constructor(private _physicsHelper: PhysicsHelper, private _scene: Scene, private _origin: Vector3, private _options: PhysicsRadialExplosionEventOptions) {
  309. this._options = { ...(new PhysicsRadialExplosionEventOptions()), ...this._options };
  310. this._tickCallback = this._tick.bind(this);
  311. this._options.strength = this._options.strength * -1;
  312. }
  313. /**
  314. * Returns the data related to the gravitational field event (sphere).
  315. * @returns A gravitational field event
  316. */
  317. public getData(): PhysicsGravitationalFieldEventData {
  318. this._dataFetched = true;
  319. return {
  320. sphere: this._sphere,
  321. };
  322. }
  323. /**
  324. * Enables the gravitational field.
  325. */
  326. public enable() {
  327. this._tickCallback.call(this);
  328. this._scene.registerBeforeRender(this._tickCallback);
  329. }
  330. /**
  331. * Disables the gravitational field.
  332. */
  333. public disable() {
  334. this._scene.unregisterBeforeRender(this._tickCallback);
  335. }
  336. /**
  337. * Disposes the sphere.
  338. * @param force The force to dispose from the gravitational field event
  339. */
  340. public dispose(force: boolean = true) {
  341. if (force) {
  342. this._sphere.dispose();
  343. } else {
  344. setTimeout(() => {
  345. if (!this._dataFetched) {
  346. this._sphere.dispose();
  347. }
  348. }, 0);
  349. }
  350. }
  351. private _tick() {
  352. // Since the params won't change, we fetch the event only once
  353. if (this._sphere) {
  354. this._physicsHelper.applyRadialExplosionForce(this._origin, this._options);
  355. } else {
  356. var radialExplosionEvent = this._physicsHelper.applyRadialExplosionForce(this._origin, this._options);
  357. if (radialExplosionEvent) {
  358. this._sphere = <Mesh>radialExplosionEvent.getData().sphere.clone('radialExplosionEventSphereClone');
  359. }
  360. }
  361. }
  362. }
  363. /**
  364. * Represents a physics updraft event
  365. */
  366. class PhysicsUpdraftEvent {
  367. private _physicsEngine: PhysicsEngine;
  368. private _originTop: Vector3 = Vector3.Zero(); // the most upper part of the cylinder
  369. private _originDirection: Vector3 = Vector3.Zero(); // used if the updraftMode is perpendicular
  370. private _tickCallback: any;
  371. private _cylinder: Mesh;
  372. private _cylinderPosition: Vector3 = Vector3.Zero(); // to keep the cylinders position, because normally the origin is in the center and not on the bottom
  373. private _dataFetched: boolean = false; // check if the has been fetched the data. If not, do cleanup
  374. /**
  375. * Initializes the physics updraft event
  376. * @param _scene BabylonJS scene
  377. * @param _origin The origin position of the updraft
  378. * @param _options The options for the updraft event
  379. */
  380. constructor(private _scene: Scene, private _origin: Vector3, private _options: PhysicsUpdraftEventOptions) {
  381. this._physicsEngine = <PhysicsEngine>this._scene.getPhysicsEngine();
  382. this._options = { ...(new PhysicsUpdraftEventOptions()), ...this._options };
  383. this._origin.addToRef(new Vector3(0, this._options.height / 2, 0), this._cylinderPosition);
  384. this._origin.addToRef(new Vector3(0, this._options.height, 0), this._originTop);
  385. if (this._options.updraftMode === PhysicsUpdraftMode.Perpendicular) {
  386. this._originDirection = this._origin.subtract(this._originTop).normalize();
  387. }
  388. this._tickCallback = this._tick.bind(this);
  389. this._prepareCylinder();
  390. }
  391. /**
  392. * Returns the data related to the updraft event (cylinder).
  393. * @returns A physics updraft event
  394. */
  395. public getData(): PhysicsUpdraftEventData {
  396. this._dataFetched = true;
  397. return {
  398. cylinder: this._cylinder,
  399. };
  400. }
  401. /**
  402. * Enables the updraft.
  403. */
  404. public enable() {
  405. this._tickCallback.call(this);
  406. this._scene.registerBeforeRender(this._tickCallback);
  407. }
  408. /**
  409. * Disables the updraft.
  410. */
  411. public disable() {
  412. this._scene.unregisterBeforeRender(this._tickCallback);
  413. }
  414. /**
  415. * Disposes the cylinder.
  416. * @param force Specifies if the updraft should be disposed by force
  417. */
  418. public dispose(force: boolean = true) {
  419. if (!this._cylinder) {
  420. return;
  421. }
  422. if (force) {
  423. this._cylinder.dispose();
  424. } else {
  425. setTimeout(() => {
  426. if (!this._dataFetched) {
  427. this._cylinder.dispose();
  428. }
  429. }, 0);
  430. }
  431. }
  432. private getImpostorHitData(impostor: PhysicsImpostor): Nullable<PhysicsHitData> {
  433. if (impostor.mass === 0) {
  434. return null;
  435. }
  436. if (!this._intersectsWithCylinder(impostor)) {
  437. return null;
  438. }
  439. var impostorObjectCenter = impostor.getObjectCenter();
  440. if (this._options.updraftMode === PhysicsUpdraftMode.Perpendicular) {
  441. var direction = this._originDirection;
  442. } else {
  443. var direction = impostorObjectCenter.subtract(this._originTop);
  444. }
  445. var distanceFromOrigin = Vector3.Distance(this._origin, impostorObjectCenter);
  446. var multiplier = this._options.strength * -1;
  447. var force = direction.multiplyByFloats(multiplier, multiplier, multiplier);
  448. return { force: force, contactPoint: impostorObjectCenter, distanceFromOrigin: distanceFromOrigin };
  449. }
  450. private _tick() {
  451. this._physicsEngine.getImpostors().forEach((impostor) => {
  452. var impostorHitData = this.getImpostorHitData(impostor);
  453. if (!impostorHitData) {
  454. return;
  455. }
  456. impostor.applyForce(impostorHitData.force, impostorHitData.contactPoint);
  457. });
  458. }
  459. /*** Helpers ***/
  460. private _prepareCylinder(): void {
  461. if (!this._cylinder) {
  462. this._cylinder = CylinderBuilder.CreateCylinder("updraftEventCylinder", {
  463. height: this._options.height,
  464. diameter: this._options.radius * 2,
  465. }, this._scene);
  466. this._cylinder.isVisible = false;
  467. }
  468. }
  469. private _intersectsWithCylinder(impostor: PhysicsImpostor): boolean {
  470. var impostorObject = <AbstractMesh>impostor.object;
  471. this._cylinder.position = this._cylinderPosition;
  472. return this._cylinder.intersectsMesh(impostorObject, true);
  473. }
  474. }
  475. /**
  476. * Represents a physics vortex event
  477. */
  478. class PhysicsVortexEvent {
  479. private _physicsEngine: PhysicsEngine;
  480. private _originTop: Vector3 = Vector3.Zero(); // the most upper part of the cylinder
  481. private _tickCallback: any;
  482. private _cylinder: Mesh;
  483. private _cylinderPosition: Vector3 = Vector3.Zero(); // to keep the cylinders position, because normally the origin is in the center and not on the bottom
  484. private _dataFetched: boolean = false; // check if the has been fetched the data. If not, do cleanup
  485. /**
  486. * Initializes the physics vortex event
  487. * @param _scene The BabylonJS scene
  488. * @param _origin The origin position of the vortex
  489. * @param _options The options for the vortex event
  490. */
  491. constructor(private _scene: Scene, private _origin: Vector3, private _options: PhysicsVortexEventOptions) {
  492. this._physicsEngine = <PhysicsEngine>this._scene.getPhysicsEngine();
  493. this._options = { ...(new PhysicsVortexEventOptions()), ...this._options };
  494. this._origin.addToRef(new Vector3(0, this._options.height / 2, 0), this._cylinderPosition);
  495. this._origin.addToRef(new Vector3(0, this._options.height, 0), this._originTop);
  496. this._tickCallback = this._tick.bind(this);
  497. this._prepareCylinder();
  498. }
  499. /**
  500. * Returns the data related to the vortex event (cylinder).
  501. * @returns The physics vortex event data
  502. */
  503. public getData(): PhysicsVortexEventData {
  504. this._dataFetched = true;
  505. return {
  506. cylinder: this._cylinder,
  507. };
  508. }
  509. /**
  510. * Enables the vortex.
  511. */
  512. public enable() {
  513. this._tickCallback.call(this);
  514. this._scene.registerBeforeRender(this._tickCallback);
  515. }
  516. /**
  517. * Disables the cortex.
  518. */
  519. public disable() {
  520. this._scene.unregisterBeforeRender(this._tickCallback);
  521. }
  522. /**
  523. * Disposes the sphere.
  524. * @param force
  525. */
  526. public dispose(force: boolean = true) {
  527. if (force) {
  528. this._cylinder.dispose();
  529. } else {
  530. setTimeout(() => {
  531. if (!this._dataFetched) {
  532. this._cylinder.dispose();
  533. }
  534. }, 0);
  535. }
  536. }
  537. private getImpostorHitData(impostor: PhysicsImpostor): Nullable<PhysicsHitData> {
  538. if (impostor.mass === 0) {
  539. return null;
  540. }
  541. if (!this._intersectsWithCylinder(impostor)) {
  542. return null;
  543. }
  544. if (impostor.object.getClassName() !== 'Mesh' && impostor.object.getClassName() !== 'InstancedMesh') {
  545. return null;
  546. }
  547. var impostorObjectCenter = impostor.getObjectCenter();
  548. 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)
  549. var originToImpostorDirection = impostorObjectCenter.subtract(originOnPlane);
  550. var ray = new Ray(originOnPlane, originToImpostorDirection, this._options.radius);
  551. var hit = ray.intersectsMesh(<AbstractMesh>impostor.object);
  552. var contactPoint = hit.pickedPoint;
  553. if (!contactPoint) {
  554. return null;
  555. }
  556. var absoluteDistanceFromOrigin = hit.distance / this._options.radius;
  557. var directionToOrigin = contactPoint.normalize();
  558. if (absoluteDistanceFromOrigin > this._options.centripetalForceThreshold) {
  559. directionToOrigin = directionToOrigin.negate();
  560. }
  561. if (absoluteDistanceFromOrigin > this._options.centripetalForceThreshold) {
  562. var forceX = directionToOrigin.x * this._options.centripetalForceMultiplier;
  563. var forceY = directionToOrigin.y * this._options.updraftForceMultiplier;
  564. var forceZ = directionToOrigin.z * this._options.centripetalForceMultiplier;
  565. } else {
  566. var perpendicularDirection = Vector3.Cross(originOnPlane, impostorObjectCenter).normalize();
  567. var forceX = (perpendicularDirection.x + directionToOrigin.x) * this._options.centrifugalForceMultiplier;
  568. var forceY = this._originTop.y * this._options.updraftForceMultiplier;
  569. var forceZ = (perpendicularDirection.z + directionToOrigin.z) * this._options.centrifugalForceMultiplier;
  570. }
  571. var force = new Vector3(forceX, forceY, forceZ);
  572. force = force.multiplyByFloats(this._options.strength, this._options.strength, this._options.strength);
  573. return { force: force, contactPoint: impostorObjectCenter, distanceFromOrigin: absoluteDistanceFromOrigin };
  574. }
  575. private _tick() {
  576. this._physicsEngine.getImpostors().forEach((impostor) => {
  577. var impostorHitData = this.getImpostorHitData(impostor);
  578. if (!impostorHitData) {
  579. return;
  580. }
  581. impostor.applyForce(impostorHitData.force, impostorHitData.contactPoint);
  582. });
  583. }
  584. /*** Helpers ***/
  585. private _prepareCylinder(): void {
  586. if (!this._cylinder) {
  587. this._cylinder = CylinderBuilder.CreateCylinder("vortexEventCylinder", {
  588. height: this._options.height,
  589. diameter: this._options.radius * 2,
  590. }, this._scene);
  591. this._cylinder.isVisible = false;
  592. }
  593. }
  594. private _intersectsWithCylinder(impostor: PhysicsImpostor): boolean {
  595. var impostorObject = <AbstractMesh>impostor.object;
  596. this._cylinder.position = this._cylinderPosition;
  597. return this._cylinder.intersectsMesh(impostorObject, true);
  598. }
  599. }
  600. /**
  601. * Options fot the radial explosion event
  602. * @see https://doc.babylonjs.com/how_to/using_the_physics_engine#further-functionality-of-the-impostor-class
  603. */
  604. export class PhysicsRadialExplosionEventOptions {
  605. /**
  606. * The radius of the sphere for the radial explosion.
  607. */
  608. radius: number = 5;
  609. /**
  610. * The strenth of the explosion.
  611. */
  612. strength: number = 10;
  613. /**
  614. * The strenght of the force in correspondence to the distance of the affected object
  615. */
  616. falloff: PhysicsRadialImpulseFalloff = PhysicsRadialImpulseFalloff.Constant;
  617. /**
  618. * Sphere options for the radial explosion.
  619. */
  620. sphere: { segments: number, diameter: number } = { segments: 32, diameter: 1 };
  621. /**
  622. * Sphere options for the radial explosion.
  623. */
  624. affectedImpostorsCallback: (affectedImpostorsWithData: Array<PhysicsAffectedImpostorWithData>) => void;
  625. }
  626. /**
  627. * Options fot the updraft event
  628. * @see https://doc.babylonjs.com/how_to/using_the_physics_engine#further-functionality-of-the-impostor-class
  629. */
  630. export class PhysicsUpdraftEventOptions {
  631. /**
  632. * The radius of the cylinder for the vortex
  633. */
  634. radius: number = 5;
  635. /**
  636. * The strenth of the updraft.
  637. */
  638. strength: number = 10;
  639. /**
  640. * The height of the cylinder for the updraft.
  641. */
  642. height: number = 10;
  643. /**
  644. * The mode for the the updraft.
  645. */
  646. updraftMode: PhysicsUpdraftMode = PhysicsUpdraftMode.Center;
  647. }
  648. /**
  649. * Options fot the vortex event
  650. * @see https://doc.babylonjs.com/how_to/using_the_physics_engine#further-functionality-of-the-impostor-class
  651. */
  652. export class PhysicsVortexEventOptions {
  653. /**
  654. * The radius of the cylinder for the vortex
  655. */
  656. radius: number = 5;
  657. /**
  658. * The strenth of the vortex.
  659. */
  660. strength: number = 10;
  661. /**
  662. * The height of the cylinder for the vortex.
  663. */
  664. height: number = 10;
  665. /**
  666. * At which distance, relative to the radius the centripetal forces should kick in? Range: 0-1
  667. */
  668. centripetalForceThreshold: number = 0.7;
  669. /**
  670. * This multiplier determines with how much force the objects will be pushed sideways/around the vortex, when below the treshold.
  671. */
  672. centripetalForceMultiplier: number = 5;
  673. /**
  674. * This multiplier determines with how much force the objects will be pushed sideways/around the vortex, when above the treshold.
  675. */
  676. centrifugalForceMultiplier: number = 0.5;
  677. /**
  678. * This multiplier determines with how much force the objects will be pushed upwards, when in the vortex.
  679. */
  680. updraftForceMultiplier: number = 0.02;
  681. }
  682. /**
  683. * The strenght of the force in correspondence to the distance of the affected object
  684. * @see https://doc.babylonjs.com/how_to/using_the_physics_engine#further-functionality-of-the-impostor-class
  685. */
  686. export enum PhysicsRadialImpulseFalloff {
  687. /** Defines that impulse is constant in strength across it's whole radius */
  688. Constant,
  689. /** Defines that impulse gets weaker if it's further from the origin */
  690. Linear
  691. }
  692. /**
  693. * The strength of the force in correspondence to the distance of the affected object
  694. * @see https://doc.babylonjs.com/how_to/using_the_physics_engine#further-functionality-of-the-impostor-class
  695. */
  696. export enum PhysicsUpdraftMode {
  697. /** Defines that the upstream forces will pull towards the top center of the cylinder */
  698. Center,
  699. /** Defines that once a impostor is inside the cylinder, it will shoot out perpendicular from the ground of the cylinder */
  700. Perpendicular
  701. }
  702. /**
  703. * Interface for a physics hit data
  704. * @see https://doc.babylonjs.com/how_to/using_the_physics_engine#further-functionality-of-the-impostor-class
  705. */
  706. export interface PhysicsHitData {
  707. /**
  708. * The force applied at the contact point
  709. */
  710. force: Vector3;
  711. /**
  712. * The contact point
  713. */
  714. contactPoint: Vector3;
  715. /**
  716. * The distance from the origin to the contact point
  717. */
  718. distanceFromOrigin: number;
  719. }
  720. /**
  721. * Interface for radial explosion event data
  722. * @see https://doc.babylonjs.com/how_to/using_the_physics_engine#further-functionality-of-the-impostor-class
  723. */
  724. export interface PhysicsRadialExplosionEventData {
  725. /**
  726. * A sphere used for the radial explosion event
  727. */
  728. sphere: Mesh;
  729. }
  730. /**
  731. * Interface for gravitational field event data
  732. * @see https://doc.babylonjs.com/how_to/using_the_physics_engine#further-functionality-of-the-impostor-class
  733. */
  734. export interface PhysicsGravitationalFieldEventData {
  735. /**
  736. * A sphere mesh used for the gravitational field event
  737. */
  738. sphere: Mesh;
  739. }
  740. /**
  741. * Interface for updraft event data
  742. * @see https://doc.babylonjs.com/how_to/using_the_physics_engine#further-functionality-of-the-impostor-class
  743. */
  744. export interface PhysicsUpdraftEventData {
  745. /**
  746. * A cylinder used for the updraft event
  747. */
  748. cylinder: Mesh;
  749. }
  750. /**
  751. * Interface for vortex event data
  752. * @see https://doc.babylonjs.com/how_to/using_the_physics_engine#further-functionality-of-the-impostor-class
  753. */
  754. export interface PhysicsVortexEventData {
  755. /**
  756. * A cylinder used for the vortex event
  757. */
  758. cylinder: Mesh;
  759. }
  760. /**
  761. * Interface for an affected physics impostor
  762. * @see https://doc.babylonjs.com/how_to/using_the_physics_engine#further-functionality-of-the-impostor-class
  763. */
  764. export interface PhysicsAffectedImpostorWithData {
  765. /**
  766. * The impostor affected by the effect
  767. */
  768. impostor: PhysicsImpostor;
  769. /**
  770. * The data about the hit/horce from the explosion
  771. */
  772. hitData: PhysicsHitData;
  773. }