physicsViewer.ts 11 KB

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