cameraGizmo.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import { Vector3 } from "../Maths/math.vector.js";
  2. import { Color3, Color4 } from "../Maths/math.color.js";
  3. import { Mesh } from "../Meshes/mesh.js";
  4. import { Gizmo } from "./gizmo.js";
  5. import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer.js";
  6. import { StandardMaterial } from "../Materials/standardMaterial.js";
  7. import { CreateBox } from "../Meshes/Builders/boxBuilder.js";
  8. import { CreateCylinder } from "../Meshes/Builders/cylinderBuilder.js";
  9. import { Matrix } from "../Maths/math.js";
  10. import { CreateLines } from "../Meshes/Builders/linesBuilder.js";
  11. import { PointerEventTypes } from "../Events/pointerEvents.js";
  12. import { Observable } from "../Misc/observable.js";
  13. /**
  14. * Gizmo that enables viewing a camera
  15. */
  16. export class CameraGizmo extends Gizmo {
  17. /**
  18. * Creates a CameraGizmo
  19. * @param gizmoLayer The utility layer the gizmo will be added to
  20. * @param gizmoColor Camera mesh color. Default is Gray
  21. * @param frustumLinesColor Frustum lines color. Default is White
  22. */
  23. constructor(gizmoLayer = UtilityLayerRenderer.DefaultUtilityLayer, gizmoColor, frustumLinesColor) {
  24. super(gizmoLayer);
  25. this._pointerObserver = null;
  26. /**
  27. * Event that fires each time the gizmo is clicked
  28. */
  29. this.onClickedObservable = new Observable();
  30. this._camera = null;
  31. this._invProjection = new Matrix();
  32. this._material = new StandardMaterial("cameraGizmoMaterial", this.gizmoLayer.utilityLayerScene);
  33. this._frustumLinesColor = frustumLinesColor;
  34. this._material.diffuseColor = gizmoColor ?? new Color3(0.5, 0.5, 0.5);
  35. this._material.specularColor = new Color3(0.1, 0.1, 0.1);
  36. this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add((pointerInfo) => {
  37. if (!this._camera) {
  38. return;
  39. }
  40. this._isHovered = !!(pointerInfo.pickInfo && this._rootMesh.getChildMeshes().indexOf(pointerInfo.pickInfo.pickedMesh) != -1);
  41. if (this._isHovered && pointerInfo.event.button === 0) {
  42. this.onClickedObservable.notifyObservers(this._camera);
  43. }
  44. }, PointerEventTypes.POINTERDOWN);
  45. }
  46. /** Gets or sets a boolean indicating if frustum lines must be rendered (true by default)) */
  47. get displayFrustum() {
  48. return this._cameraLinesMesh.isEnabled();
  49. }
  50. set displayFrustum(value) {
  51. this._cameraLinesMesh.setEnabled(value);
  52. }
  53. /**
  54. * The camera that the gizmo is attached to
  55. */
  56. set camera(camera) {
  57. this._camera = camera;
  58. this.attachedNode = camera;
  59. if (camera) {
  60. // Create the mesh for the given camera
  61. if (!this._customMeshSet) {
  62. if (this._cameraMesh) {
  63. this._cameraMesh.dispose();
  64. }
  65. this._cameraMesh = CameraGizmo._CreateCameraMesh(this.gizmoLayer.utilityLayerScene);
  66. this._cameraMesh.getChildMeshes(false).forEach((m) => {
  67. m.material = this._material;
  68. });
  69. this._cameraMesh.parent = this._rootMesh;
  70. }
  71. if (this._cameraLinesMesh) {
  72. this._cameraLinesMesh.dispose();
  73. }
  74. const linesColor = this._frustumLinesColor?.toColor4(1) ?? new Color4(1, 1, 1, 1);
  75. this._cameraLinesMesh = CameraGizmo._CreateCameraFrustum(this.gizmoLayer.utilityLayerScene, linesColor);
  76. this._cameraLinesMesh.parent = this._rootMesh;
  77. if (this.gizmoLayer.utilityLayerScene.activeCamera && this.gizmoLayer.utilityLayerScene.activeCamera.maxZ < camera.maxZ * 1.5) {
  78. this.gizmoLayer.utilityLayerScene.activeCamera.maxZ = camera.maxZ * 1.5;
  79. }
  80. if (!this.attachedNode.reservedDataStore) {
  81. this.attachedNode.reservedDataStore = {};
  82. }
  83. this.attachedNode.reservedDataStore.cameraGizmo = this;
  84. // Add lighting to the camera gizmo
  85. const gizmoLight = this.gizmoLayer._getSharedGizmoLight();
  86. gizmoLight.includedOnlyMeshes = gizmoLight.includedOnlyMeshes.concat(this._cameraMesh.getChildMeshes(false));
  87. this._update();
  88. }
  89. }
  90. get camera() {
  91. return this._camera;
  92. }
  93. /**
  94. * Gets the material used to render the camera gizmo
  95. */
  96. get material() {
  97. return this._material;
  98. }
  99. /**
  100. * @internal
  101. * Updates the gizmo to match the attached mesh's position/rotation
  102. */
  103. _update() {
  104. super._update();
  105. if (!this._camera) {
  106. return;
  107. }
  108. // frustum matrix
  109. this._camera.getProjectionMatrix().invertToRef(this._invProjection);
  110. this._cameraLinesMesh.setPivotMatrix(this._invProjection, false);
  111. this._cameraLinesMesh.scaling.x = 1 / this._rootMesh.scaling.x;
  112. this._cameraLinesMesh.scaling.y = 1 / this._rootMesh.scaling.y;
  113. this._cameraLinesMesh.scaling.z = 1 / this._rootMesh.scaling.z;
  114. // take care of coordinate system in camera scene to properly display the mesh with the good Y axis orientation in this scene
  115. this._cameraMesh.parent = null;
  116. this._cameraMesh.rotation.y = Math.PI * 0.5 * (this._camera.getScene().useRightHandedSystem ? 1 : -1);
  117. this._cameraMesh.parent = this._rootMesh;
  118. }
  119. /**
  120. * Disposes and replaces the current camera mesh in the gizmo with the specified mesh
  121. * @param mesh The mesh to replace the default mesh of the camera gizmo
  122. */
  123. setCustomMesh(mesh) {
  124. if (mesh.getScene() != this.gizmoLayer.utilityLayerScene) {
  125. // eslint-disable-next-line no-throw-literal
  126. throw "When setting a custom mesh on a gizmo, the custom meshes scene must be the same as the gizmos (eg. gizmo.gizmoLayer.utilityLayerScene)";
  127. }
  128. if (this._cameraMesh) {
  129. this._cameraMesh.dispose();
  130. }
  131. this._cameraMesh = mesh;
  132. this._cameraMesh.parent = this._rootMesh;
  133. this._customMeshSet = true;
  134. }
  135. /**
  136. * Disposes of the camera gizmo
  137. */
  138. dispose() {
  139. this.onClickedObservable.clear();
  140. this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
  141. if (this._cameraMesh) {
  142. this._cameraMesh.dispose();
  143. }
  144. if (this._cameraLinesMesh) {
  145. this._cameraLinesMesh.dispose();
  146. }
  147. this._material.dispose();
  148. super.dispose();
  149. }
  150. static _CreateCameraMesh(scene) {
  151. const root = new Mesh("rootCameraGizmo", scene);
  152. const mesh = new Mesh(root.name, scene);
  153. mesh.parent = root;
  154. const box = CreateBox(root.name, { width: 1.0, height: 0.8, depth: 0.5 }, scene);
  155. box.parent = mesh;
  156. const cyl1 = CreateCylinder(root.name, { height: 0.5, diameterTop: 0.8, diameterBottom: 0.8 }, scene);
  157. cyl1.parent = mesh;
  158. cyl1.position.y = 0.3;
  159. cyl1.position.x = -0.6;
  160. cyl1.rotation.x = Math.PI * 0.5;
  161. const cyl2 = CreateCylinder(root.name, { height: 0.5, diameterTop: 0.6, diameterBottom: 0.6 }, scene);
  162. cyl2.parent = mesh;
  163. cyl2.position.y = 0.5;
  164. cyl2.position.x = 0.4;
  165. cyl2.rotation.x = Math.PI * 0.5;
  166. const cyl3 = CreateCylinder(root.name, { height: 0.5, diameterTop: 0.5, diameterBottom: 0.5 }, scene);
  167. cyl3.parent = mesh;
  168. cyl3.position.y = 0.0;
  169. cyl3.position.x = 0.6;
  170. cyl3.rotation.z = Math.PI * 0.5;
  171. root.scaling.scaleInPlace(CameraGizmo._Scale);
  172. mesh.position.x = -0.9;
  173. return root;
  174. }
  175. static _CreateCameraFrustum(scene, linesColor) {
  176. const root = new Mesh("rootCameraGizmo", scene);
  177. const mesh = new Mesh(root.name, scene);
  178. mesh.parent = root;
  179. for (let y = 0; y < 4; y += 2) {
  180. for (let x = 0; x < 4; x += 2) {
  181. let line = CreateLines("lines", { points: [new Vector3(-1 + x, -1 + y, -1), new Vector3(-1 + x, -1 + y, 1)], colors: [linesColor, linesColor] }, scene);
  182. line.parent = mesh;
  183. line.alwaysSelectAsActiveMesh = true;
  184. line.isPickable = false;
  185. line = CreateLines("lines", { points: [new Vector3(-1, -1 + x, -1 + y), new Vector3(1, -1 + x, -1 + y)], colors: [linesColor, linesColor] }, scene);
  186. line.parent = mesh;
  187. line.alwaysSelectAsActiveMesh = true;
  188. line.isPickable = false;
  189. line = CreateLines("lines", { points: [new Vector3(-1 + x, -1, -1 + y), new Vector3(-1 + x, 1, -1 + y)], colors: [linesColor, linesColor] }, scene);
  190. line.parent = mesh;
  191. line.alwaysSelectAsActiveMesh = true;
  192. line.isPickable = false;
  193. }
  194. }
  195. return root;
  196. }
  197. }
  198. // Static helper methods
  199. CameraGizmo._Scale = 0.05;
  200. //# sourceMappingURL=cameraGizmo.js.map