import { Nullable } from "../types"; import { Scene } from "../scene"; import { AbstractMesh } from "../Meshes/abstractMesh"; import { Mesh } from "../Meshes/mesh"; import { BoxBuilder } from "../Meshes/Builders/boxBuilder"; import { SphereBuilder } from "../Meshes/Builders/sphereBuilder"; import { Quaternion, Color3, Vector3 } from "../Maths/math"; import { Material } from "../Materials/material"; import { EngineStore } from "../Engines/engineStore"; import { StandardMaterial } from "../Materials/standardMaterial"; import { IPhysicsEnginePlugin } from "../Physics/IPhysicsEngine"; import { PhysicsImpostor } from "../Physics/physicsImpostor"; import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer"; /** * Used to show the physics impostor around the specific mesh */ export class PhysicsViewer { /** @hidden */ protected _impostors: Array> = []; /** @hidden */ protected _meshes: Array> = []; /** @hidden */ protected _scene: Nullable; /** @hidden */ protected _numMeshes = 0; /** @hidden */ protected _physicsEnginePlugin: Nullable; private _renderFunction: () => void; private _utilityLayer: Nullable; private _debugBoxMesh: Mesh; private _debugSphereMesh: Mesh; private _debugMaterial: StandardMaterial; private _debugMeshMeshes = new Array(); /** * Creates a new PhysicsViewer * @param scene defines the hosting scene */ constructor(scene: Scene) { this._scene = scene || EngineStore.LastCreatedScene; let physicEngine = this._scene.getPhysicsEngine(); if (physicEngine) { this._physicsEnginePlugin = physicEngine.getPhysicsPlugin(); } this._utilityLayer = new UtilityLayerRenderer(this._scene, false); this._utilityLayer.pickUtilitySceneFirst = false; this._utilityLayer.utilityLayerScene.autoClearDepthAndStencil = true; } /** @hidden */ protected _updateDebugMeshes(): void { var plugin = this._physicsEnginePlugin; for (var i = 0; i < this._numMeshes; i++) { let impostor = this._impostors[i]; if (!impostor) { continue; } if (impostor.isDisposed) { this.hideImpostor(this._impostors[i--]); } else { if (impostor.type === PhysicsImpostor.MeshImpostor) { continue; } let mesh = this._meshes[i]; if (mesh && plugin) { plugin.syncMeshWithImpostor(mesh, impostor); } } } } /** * Renders a specified physic impostor * @param impostor defines the impostor to render * @param targetMesh defines the mesh represented by the impostor * @returns the new debug mesh used to render the impostor */ public showImpostor(impostor: PhysicsImpostor, targetMesh?: Mesh): Nullable { if (!this._scene) { return null; } for (var i = 0; i < this._numMeshes; i++) { if (this._impostors[i] == impostor) { return null; } } var debugMesh = this._getDebugMesh(impostor, targetMesh); if (debugMesh) { this._impostors[this._numMeshes] = impostor; this._meshes[this._numMeshes] = debugMesh; if (this._numMeshes === 0) { this._renderFunction = this._updateDebugMeshes.bind(this); this._scene.registerBeforeRender(this._renderFunction); } this._numMeshes++; } return debugMesh; } /** * Hides a specified physic impostor * @param impostor defines the impostor to hide */ public hideImpostor(impostor: Nullable) { if (!impostor || !this._scene || !this._utilityLayer) { return; } var removed = false; const utilityLayerScene = this._utilityLayer.utilityLayerScene; for (var i = 0; i < this._numMeshes; i++) { if (this._impostors[i] == impostor) { let mesh = this._meshes[i]; if (!mesh) { continue; } utilityLayerScene.removeMesh(mesh); mesh.dispose(); let index = this._debugMeshMeshes.indexOf(mesh as Mesh); if (index > -1) { this._debugMeshMeshes.splice(index, 1); } this._numMeshes--; if (this._numMeshes > 0) { this._meshes[i] = this._meshes[this._numMeshes]; this._impostors[i] = this._impostors[this._numMeshes]; this._meshes[this._numMeshes] = null; this._impostors[this._numMeshes] = null; } else { this._meshes[0] = null; this._impostors[0] = null; } removed = true; break; } } if (removed && this._numMeshes === 0) { this._scene.unregisterBeforeRender(this._renderFunction); } } private _getDebugMaterial(scene: Scene): Material { if (!this._debugMaterial) { this._debugMaterial = new StandardMaterial('', scene); this._debugMaterial.wireframe = true; this._debugMaterial.emissiveColor = Color3.White(); this._debugMaterial.disableLighting = true; } return this._debugMaterial; } private _getDebugBoxMesh(scene: Scene): AbstractMesh { if (!this._debugBoxMesh) { this._debugBoxMesh = BoxBuilder.CreateBox('physicsBodyBoxViewMesh', { size: 1 }, scene); this._debugBoxMesh.rotationQuaternion = Quaternion.Identity(); this._debugBoxMesh.material = this._getDebugMaterial(scene); this._debugBoxMesh.setEnabled(false); } return this._debugBoxMesh.createInstance('physicsBodyBoxViewInstance'); } private _getDebugSphereMesh(scene: Scene): AbstractMesh { if (!this._debugSphereMesh) { this._debugSphereMesh = SphereBuilder.CreateSphere('physicsBodySphereViewMesh', { diameter: 1 }, scene); this._debugSphereMesh.rotationQuaternion = Quaternion.Identity(); this._debugSphereMesh.material = this._getDebugMaterial(scene); this._debugSphereMesh.setEnabled(false); } return this._debugSphereMesh.createInstance('physicsBodyBoxViewInstance'); } private _getDebugMeshMesh(mesh: Mesh, scene: Scene): AbstractMesh { var wireframeOver = new Mesh(mesh.name, scene, null, mesh); wireframeOver.position = Vector3.Zero(); wireframeOver.setParent(mesh); wireframeOver.material = this._getDebugMaterial(scene); this._debugMeshMeshes.push(wireframeOver); return wireframeOver; } private _getDebugMesh(impostor: PhysicsImpostor, targetMesh?: Mesh): Nullable { if (!this._utilityLayer) { return null; } var mesh: Nullable = null; const utilityLayerScene = this._utilityLayer.utilityLayerScene; switch (impostor.type) { case PhysicsImpostor.BoxImpostor: mesh = this._getDebugBoxMesh(utilityLayerScene); impostor.getBoxSizeToRef(mesh.scaling); break; case PhysicsImpostor.SphereImpostor: mesh = this._getDebugSphereMesh(utilityLayerScene); var radius = impostor.getRadius(); mesh.scaling.x = radius * 2; mesh.scaling.y = radius * 2; mesh.scaling.z = radius * 2; break; case PhysicsImpostor.MeshImpostor: if (targetMesh) { mesh = this._getDebugMeshMesh(targetMesh, utilityLayerScene); } break; } return mesh; } /** Releases all resources */ public dispose() { let count = this._numMeshes; for (var index = 0; index < count; index++) { this.hideImpostor(this._impostors[0]); } if (this._debugBoxMesh) { this._debugBoxMesh.dispose(); } if (this._debugSphereMesh) { this._debugSphereMesh.dispose(); } if (this._debugMaterial) { this._debugMaterial.dispose(); } this._impostors.length = 0; this._scene = null; this._physicsEnginePlugin = null; if (this._utilityLayer) { this._utilityLayer.dispose(); this._utilityLayer = null; } } }