physicsViewer.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. import { Nullable } from "../types";
  2. import { Scene } from "../scene";
  3. import { AbstractMesh } from "../Meshes/abstractMesh";
  4. import { Mesh } from "../Meshes/mesh";
  5. import { BoxBuilder } from "../Meshes/Builders/boxBuilder";
  6. import { SphereBuilder } from "../Meshes/Builders/sphereBuilder";
  7. import { Quaternion, Color3, Vector3 } from "../Maths/math";
  8. import { Material } from "../Materials/material";
  9. import { EngineStore } from "../Engines/engineStore";
  10. import { StandardMaterial } from "../Materials/standardMaterial";
  11. import { IPhysicsEnginePlugin } from "../Physics/IPhysicsEngine";
  12. import { PhysicsImpostor } from "../Physics/physicsImpostor";
  13. import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer";
  14. import { CylinderBuilder } from '../Meshes/Builders/cylinderBuilder';
  15. /**
  16. * Used to show the physics impostor around the specific mesh
  17. */
  18. export class PhysicsViewer {
  19. /** @hidden */
  20. protected _impostors: Array<Nullable<PhysicsImpostor>> = [];
  21. /** @hidden */
  22. protected _meshes: Array<Nullable<AbstractMesh>> = [];
  23. /** @hidden */
  24. protected _scene: Nullable<Scene>;
  25. /** @hidden */
  26. protected _numMeshes = 0;
  27. /** @hidden */
  28. protected _physicsEnginePlugin: Nullable<IPhysicsEnginePlugin>;
  29. private _renderFunction: () => void;
  30. private _utilityLayer: Nullable<UtilityLayerRenderer>;
  31. private _debugBoxMesh: Mesh;
  32. private _debugSphereMesh: Mesh;
  33. private _debugCylinderMesh: Mesh;
  34. private _debugMaterial: StandardMaterial;
  35. private _debugMeshMeshes = new Array<Mesh>();
  36. /**
  37. * Creates a new PhysicsViewer
  38. * @param scene defines the hosting scene
  39. */
  40. constructor(scene: Scene) {
  41. this._scene = scene || EngineStore.LastCreatedScene;
  42. let physicEngine = this._scene.getPhysicsEngine();
  43. if (physicEngine) {
  44. this._physicsEnginePlugin = physicEngine.getPhysicsPlugin();
  45. }
  46. this._utilityLayer = new UtilityLayerRenderer(this._scene, false);
  47. this._utilityLayer.pickUtilitySceneFirst = false;
  48. this._utilityLayer.utilityLayerScene.autoClearDepthAndStencil = true;
  49. }
  50. /** @hidden */
  51. protected _updateDebugMeshes(): void {
  52. var plugin = this._physicsEnginePlugin;
  53. for (var i = 0; i < this._numMeshes; i++) {
  54. let impostor = this._impostors[i];
  55. if (!impostor) {
  56. continue;
  57. }
  58. if (impostor.isDisposed) {
  59. this.hideImpostor(this._impostors[i--]);
  60. } else {
  61. if (impostor.type === PhysicsImpostor.MeshImpostor) {
  62. continue;
  63. }
  64. let mesh = this._meshes[i];
  65. if (mesh && plugin) {
  66. plugin.syncMeshWithImpostor(mesh, impostor);
  67. }
  68. }
  69. }
  70. }
  71. /**
  72. * Renders a specified physic impostor
  73. * @param impostor defines the impostor to render
  74. * @param targetMesh defines the mesh represented by the impostor
  75. * @returns the new debug mesh used to render the impostor
  76. */
  77. public showImpostor(impostor: PhysicsImpostor, targetMesh?: Mesh): Nullable<AbstractMesh> {
  78. if (!this._scene) {
  79. return null;
  80. }
  81. for (var i = 0; i < this._numMeshes; i++) {
  82. if (this._impostors[i] == impostor) {
  83. return null;
  84. }
  85. }
  86. var debugMesh = this._getDebugMesh(impostor, targetMesh);
  87. if (debugMesh) {
  88. this._impostors[this._numMeshes] = impostor;
  89. this._meshes[this._numMeshes] = debugMesh;
  90. if (this._numMeshes === 0) {
  91. this._renderFunction = this._updateDebugMeshes.bind(this);
  92. this._scene.registerBeforeRender(this._renderFunction);
  93. }
  94. this._numMeshes++;
  95. }
  96. return debugMesh;
  97. }
  98. /**
  99. * Hides a specified physic impostor
  100. * @param impostor defines the impostor to hide
  101. */
  102. public hideImpostor(impostor: Nullable<PhysicsImpostor>) {
  103. if (!impostor || !this._scene || !this._utilityLayer) {
  104. return;
  105. }
  106. var removed = false;
  107. const utilityLayerScene = this._utilityLayer.utilityLayerScene;
  108. for (var i = 0; i < this._numMeshes; i++) {
  109. if (this._impostors[i] == impostor) {
  110. let mesh = this._meshes[i];
  111. if (!mesh) {
  112. continue;
  113. }
  114. utilityLayerScene.removeMesh(mesh);
  115. mesh.dispose();
  116. let index = this._debugMeshMeshes.indexOf(mesh as Mesh);
  117. if (index > -1) {
  118. this._debugMeshMeshes.splice(index, 1);
  119. }
  120. this._numMeshes--;
  121. if (this._numMeshes > 0) {
  122. this._meshes[i] = this._meshes[this._numMeshes];
  123. this._impostors[i] = this._impostors[this._numMeshes];
  124. this._meshes[this._numMeshes] = null;
  125. this._impostors[this._numMeshes] = null;
  126. } else {
  127. this._meshes[0] = null;
  128. this._impostors[0] = null;
  129. }
  130. removed = true;
  131. break;
  132. }
  133. }
  134. if (removed && this._numMeshes === 0) {
  135. this._scene.unregisterBeforeRender(this._renderFunction);
  136. }
  137. }
  138. private _getDebugMaterial(scene: Scene): Material {
  139. if (!this._debugMaterial) {
  140. this._debugMaterial = new StandardMaterial('', scene);
  141. this._debugMaterial.wireframe = true;
  142. this._debugMaterial.emissiveColor = Color3.White();
  143. this._debugMaterial.disableLighting = true;
  144. }
  145. return this._debugMaterial;
  146. }
  147. private _getDebugBoxMesh(scene: Scene): AbstractMesh {
  148. if (!this._debugBoxMesh) {
  149. this._debugBoxMesh = BoxBuilder.CreateBox('physicsBodyBoxViewMesh', { size: 1 }, scene);
  150. this._debugBoxMesh.rotationQuaternion = Quaternion.Identity();
  151. this._debugBoxMesh.material = this._getDebugMaterial(scene);
  152. this._debugBoxMesh.setEnabled(false);
  153. }
  154. return this._debugBoxMesh.createInstance('physicsBodyBoxViewInstance');
  155. }
  156. private _getDebugSphereMesh(scene: Scene): AbstractMesh {
  157. if (!this._debugSphereMesh) {
  158. this._debugSphereMesh = SphereBuilder.CreateSphere('physicsBodySphereViewMesh', { diameter: 1 }, scene);
  159. this._debugSphereMesh.rotationQuaternion = Quaternion.Identity();
  160. this._debugSphereMesh.material = this._getDebugMaterial(scene);
  161. this._debugSphereMesh.setEnabled(false);
  162. }
  163. return this._debugSphereMesh.createInstance('physicsBodyBoxViewInstance');
  164. }
  165. private _getDebugCylinderMesh(scene: Scene): AbstractMesh {
  166. if (!this._debugCylinderMesh) {
  167. this._debugCylinderMesh = CylinderBuilder.CreateCylinder('physicsBodyCylinderViewMesh', { diameterTop: 1, diameterBottom: 1, height: 1 }, scene);
  168. this._debugCylinderMesh.rotationQuaternion = Quaternion.Identity();
  169. this._debugCylinderMesh.material = this._getDebugMaterial(scene);
  170. this._debugCylinderMesh.setEnabled(false);
  171. }
  172. return this._debugCylinderMesh.createInstance('physicsBodyBoxViewInstance');
  173. }
  174. private _getDebugMeshMesh(mesh: Mesh, scene: Scene): AbstractMesh {
  175. var wireframeOver = new Mesh(mesh.name, scene, null, mesh);
  176. wireframeOver.position = Vector3.Zero();
  177. wireframeOver.setParent(mesh);
  178. wireframeOver.material = this._getDebugMaterial(scene);
  179. this._debugMeshMeshes.push(wireframeOver);
  180. return wireframeOver;
  181. }
  182. private _getDebugMesh(impostor: PhysicsImpostor, targetMesh?: Mesh): Nullable<AbstractMesh> {
  183. if (!this._utilityLayer) {
  184. return null;
  185. }
  186. // Only create child impostor debug meshes when evaluating the parent
  187. if (targetMesh && targetMesh.parent && (targetMesh.parent as Mesh).physicsImpostor) {
  188. return null;
  189. }
  190. var mesh: Nullable<AbstractMesh> = null;
  191. const utilityLayerScene = this._utilityLayer.utilityLayerScene;
  192. switch (impostor.type) {
  193. case PhysicsImpostor.BoxImpostor:
  194. mesh = this._getDebugBoxMesh(utilityLayerScene);
  195. impostor.getBoxSizeToRef(mesh.scaling);
  196. break;
  197. case PhysicsImpostor.SphereImpostor:
  198. mesh = this._getDebugSphereMesh(utilityLayerScene);
  199. var radius = impostor.getRadius();
  200. mesh.scaling.x = radius * 2;
  201. mesh.scaling.y = radius * 2;
  202. mesh.scaling.z = radius * 2;
  203. break;
  204. case PhysicsImpostor.MeshImpostor:
  205. if (targetMesh) {
  206. mesh = this._getDebugMeshMesh(targetMesh, utilityLayerScene);
  207. }
  208. break;
  209. case PhysicsImpostor.NoImpostor:
  210. if (targetMesh) {
  211. // Handle compound impostors
  212. var childMeshes = targetMesh.getChildMeshes().filter((c) => {return c.physicsImpostor ? 1 : 0; });
  213. childMeshes.forEach((m) => {
  214. var a = this._getDebugBoxMesh(utilityLayerScene);
  215. a.parent = m;
  216. });
  217. }
  218. break;
  219. case PhysicsImpostor.CylinderImpostor:
  220. mesh = this._getDebugCylinderMesh(utilityLayerScene);
  221. var bi = impostor.object.getBoundingInfo();
  222. mesh.scaling.x = bi.boundingBox.maximum.x - bi.boundingBox.minimum.x;
  223. mesh.scaling.y = bi.boundingBox.maximum.y - bi.boundingBox.minimum.y;
  224. mesh.scaling.z = bi.boundingBox.maximum.z - bi.boundingBox.minimum.z;
  225. break;
  226. }
  227. return mesh;
  228. }
  229. /** Releases all resources */
  230. public dispose() {
  231. let count = this._numMeshes;
  232. for (var index = 0; index < count; index++) {
  233. this.hideImpostor(this._impostors[0]);
  234. }
  235. if (this._debugBoxMesh) {
  236. this._debugBoxMesh.dispose();
  237. }
  238. if (this._debugSphereMesh) {
  239. this._debugSphereMesh.dispose();
  240. }
  241. if (this._debugCylinderMesh) {
  242. this._debugCylinderMesh.dispose();
  243. }
  244. if (this._debugMaterial) {
  245. this._debugMaterial.dispose();
  246. }
  247. this._impostors.length = 0;
  248. this._scene = null;
  249. this._physicsEnginePlugin = null;
  250. if (this._utilityLayer) {
  251. this._utilityLayer.dispose();
  252. this._utilityLayer = null;
  253. }
  254. }
  255. }