lightGizmo.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. import { Nullable } from "../types";
  2. import { Vector3, Quaternion } from "../Maths/math.vector";
  3. import { Color3 } from '../Maths/math.color';
  4. import { AbstractMesh } from "../Meshes/abstractMesh";
  5. import { Mesh } from "../Meshes/mesh";
  6. import { Gizmo } from "./gizmo";
  7. import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer";
  8. import { StandardMaterial } from '../Materials/standardMaterial';
  9. import { Light } from '../Lights/light';
  10. import { Scene } from '../scene';
  11. import { HemisphericLight } from '../Lights/hemisphericLight';
  12. import { DirectionalLight } from '../Lights/directionalLight';
  13. import { SphereBuilder } from '../Meshes/Builders/sphereBuilder';
  14. import { HemisphereBuilder } from '../Meshes/Builders/hemisphereBuilder';
  15. import { SpotLight } from '../Lights/spotLight';
  16. import { TransformNode } from '../Meshes/transformNode';
  17. /**
  18. * Gizmo that enables viewing a light
  19. */
  20. export class LightGizmo extends Gizmo {
  21. private _lightMesh: Mesh;
  22. private _material: StandardMaterial;
  23. private _cachedPosition = new Vector3();
  24. private _cachedForward = new Vector3(0, 0, 1);
  25. private _attachedMeshParent: TransformNode;
  26. /**
  27. * Creates a LightGizmo
  28. * @param gizmoLayer The utility layer the gizmo will be added to
  29. */
  30. constructor(gizmoLayer?: UtilityLayerRenderer) {
  31. super(gizmoLayer);
  32. this.attachedMesh = new AbstractMesh("", this.gizmoLayer.utilityLayerScene);
  33. this._attachedMeshParent = new TransformNode("parent", this.gizmoLayer.utilityLayerScene);
  34. this.attachedMesh.parent = this._attachedMeshParent;
  35. this._material = new StandardMaterial("light", this.gizmoLayer.utilityLayerScene);
  36. this._material.diffuseColor = new Color3(0.5, 0.5, 0.5);
  37. this._material.specularColor = new Color3(0.1, 0.1, 0.1);
  38. }
  39. private _light: Nullable<Light> = null;
  40. /**
  41. * The light that the gizmo is attached to
  42. */
  43. public set light(light: Nullable<Light>) {
  44. this._light = light;
  45. if (light) {
  46. // Create the mesh for the given light type
  47. if (this._lightMesh) {
  48. this._lightMesh.dispose();
  49. }
  50. if (light instanceof HemisphericLight) {
  51. this._lightMesh = LightGizmo._CreateHemisphericLightMesh(this.gizmoLayer.utilityLayerScene);
  52. } else if (light instanceof DirectionalLight) {
  53. this._lightMesh = LightGizmo._CreateDirectionalLightMesh(this.gizmoLayer.utilityLayerScene);
  54. } else if (light instanceof SpotLight) {
  55. this._lightMesh = LightGizmo._CreateSpotLightMesh(this.gizmoLayer.utilityLayerScene);
  56. } else {
  57. this._lightMesh = LightGizmo._CreatePointLightMesh(this.gizmoLayer.utilityLayerScene);
  58. }
  59. this._lightMesh.getChildMeshes(false).forEach((m) => {
  60. m.material = this._material;
  61. });
  62. this._lightMesh.parent = this._rootMesh;
  63. // Add lighting to the light gizmo
  64. var gizmoLight = this.gizmoLayer._getSharedGizmoLight();
  65. gizmoLight.includedOnlyMeshes = gizmoLight.includedOnlyMeshes.concat(this._lightMesh.getChildMeshes(false));
  66. this._lightMesh.rotationQuaternion = new Quaternion();
  67. if (!this.attachedMesh!.reservedDataStore) {
  68. this.attachedMesh!.reservedDataStore = {};
  69. }
  70. this.attachedMesh!.reservedDataStore.lightGizmo = this;
  71. if (light.parent) {
  72. this._attachedMeshParent.freezeWorldMatrix(light.parent.getWorldMatrix());
  73. }
  74. // Get update position and direction if the light has it
  75. if ((light as any).position) {
  76. this.attachedMesh!.position.copyFrom((light as any).position);
  77. this.attachedMesh!.computeWorldMatrix(true);
  78. this._cachedPosition.copyFrom(this.attachedMesh!.position);
  79. }
  80. if ((light as any).direction) {
  81. this.attachedMesh!.setDirection((light as any).direction);
  82. this.attachedMesh!.computeWorldMatrix(true);
  83. this._cachedForward.copyFrom(this.attachedMesh!.forward);
  84. }
  85. this._update();
  86. }
  87. }
  88. public get light() {
  89. return this._light;
  90. }
  91. /**
  92. * Gets the material used to render the light gizmo
  93. */
  94. public get material() {
  95. return this._material;
  96. }
  97. /**
  98. * @hidden
  99. * Updates the gizmo to match the attached mesh's position/rotation
  100. */
  101. protected _update() {
  102. super._update();
  103. if (!this._light) {
  104. return;
  105. }
  106. if (this._light.parent) {
  107. this._attachedMeshParent.freezeWorldMatrix(this._light.parent.getWorldMatrix());
  108. }
  109. if ((this._light as any).position) {
  110. // If the gizmo is moved update the light otherwise update the gizmo to match the light
  111. if (!this.attachedMesh!.position.equals(this._cachedPosition)) {
  112. // update light to match gizmo
  113. (this._light as any).position.copyFrom(this.attachedMesh!.position);
  114. this._cachedPosition.copyFrom(this.attachedMesh!.position);
  115. } else {
  116. // update gizmo to match light
  117. this.attachedMesh!.position.copyFrom((this._light as any).position);
  118. this.attachedMesh!.computeWorldMatrix(true);
  119. this._cachedPosition.copyFrom(this.attachedMesh!.position);
  120. }
  121. }
  122. if ((this._light as any).direction) {
  123. // If the gizmo is moved update the light otherwise update the gizmo to match the light
  124. if (Vector3.DistanceSquared(this.attachedMesh!.forward, this._cachedForward) > 0.0001) {
  125. // update light to match gizmo
  126. (this._light as any).direction.copyFrom(this.attachedMesh!.forward);
  127. this._cachedForward.copyFrom(this.attachedMesh!.forward);
  128. } else if (Vector3.DistanceSquared(this.attachedMesh!.forward, (this._light as any).direction) > 0.0001) {
  129. // update gizmo to match light
  130. this.attachedMesh!.setDirection((this._light as any).direction);
  131. this.attachedMesh!.computeWorldMatrix(true);
  132. this._cachedForward.copyFrom(this.attachedMesh!.forward);
  133. }
  134. }
  135. if (!this._light.isEnabled()) {
  136. this._material.diffuseColor.set(0, 0, 0);
  137. } else {
  138. this._material.diffuseColor.set(this._light.diffuse.r / 3, this._light.diffuse.g / 3, this._light.diffuse.b / 3);
  139. }
  140. }
  141. // Static helper methods
  142. private static _Scale = 0.007;
  143. /**
  144. * Creates the lines for a light mesh
  145. */
  146. private static _CreateLightLines = (levels: number, scene: Scene) => {
  147. var distFromSphere = 1.2;
  148. var root = new Mesh("root", scene);
  149. root.rotation.x = Math.PI / 2;
  150. // Create the top line, this will be cloned for all other lines
  151. var linePivot = new Mesh("linePivot", scene);
  152. linePivot.parent = root;
  153. var line = Mesh.CreateCylinder("line", 2, 0.2, 0.3, 6, 1, scene);
  154. line.position.y = line.scaling.y / 2 + distFromSphere;
  155. line.parent = linePivot;
  156. if (levels < 2) {
  157. return linePivot;
  158. }
  159. for (var i = 0; i < 4; i++) {
  160. var l = linePivot.clone("lineParentClone")!;
  161. l.rotation.z = Math.PI / 4;
  162. l.rotation.y = (Math.PI / 2) + (Math.PI / 2 * i);
  163. l.getChildMeshes()[0].scaling.y = 0.5;
  164. l.getChildMeshes()[0].scaling.x = l.getChildMeshes()[0].scaling.z = 0.8;
  165. l.getChildMeshes()[0].position.y = l.getChildMeshes()[0].scaling.y / 2 + distFromSphere;
  166. }
  167. if (levels < 3) {
  168. return root;
  169. }
  170. for (var i = 0; i < 4; i++) {
  171. var l = linePivot.clone("linePivotClone");
  172. l.rotation.z = Math.PI / 2;
  173. l.rotation.y = (Math.PI / 2 * i);
  174. }
  175. if (levels < 4) {
  176. return root;
  177. }
  178. for (var i = 0; i < 4; i++) {
  179. var l = linePivot.clone("linePivotClone");
  180. l.rotation.z = Math.PI + (Math.PI / 4);
  181. l.rotation.y = (Math.PI / 2) + (Math.PI / 2 * i);
  182. l.getChildMeshes()[0].scaling.y = 0.5;
  183. l.getChildMeshes()[0].scaling.x = l.getChildMeshes()[0].scaling.z = 0.8;
  184. l.getChildMeshes()[0].position.y = l.getChildMeshes()[0].scaling.y / 2 + distFromSphere;
  185. }
  186. if (levels < 5) {
  187. return root;
  188. }
  189. var l = linePivot.clone("linePivotClone");
  190. l.rotation.z = Math.PI;
  191. return root;
  192. }
  193. /**
  194. * Disposes of the light gizmo
  195. */
  196. public dispose() {
  197. this._material.dispose();
  198. super.dispose();
  199. this._attachedMeshParent.dispose();
  200. }
  201. private static _CreateHemisphericLightMesh(scene: Scene) {
  202. var root = new Mesh("hemisphereLight", scene);
  203. var hemisphere = HemisphereBuilder.CreateHemisphere(root.name, { segments: 10, diameter: 1 }, scene);
  204. hemisphere.position.z = -0.15;
  205. hemisphere.rotation.x = Math.PI / 2;
  206. hemisphere.parent = root;
  207. var lines = this._CreateLightLines(3, scene);
  208. lines.parent = root;
  209. lines.position.z - 0.15;
  210. root.scaling.scaleInPlace(LightGizmo._Scale);
  211. root.rotation.x = Math.PI / 2;
  212. return root;
  213. }
  214. private static _CreatePointLightMesh(scene: Scene) {
  215. var root = new Mesh("pointLight", scene);
  216. var sphere = SphereBuilder.CreateSphere(root.name, { segments: 10, diameter: 1 }, scene);
  217. sphere.rotation.x = Math.PI / 2;
  218. sphere.parent = root;
  219. var lines = this._CreateLightLines(5, scene);
  220. lines.parent = root;
  221. root.scaling.scaleInPlace(LightGizmo._Scale);
  222. root.rotation.x = Math.PI / 2;
  223. return root;
  224. }
  225. private static _CreateSpotLightMesh(scene: Scene) {
  226. var root = new Mesh("spotLight", scene);
  227. var sphere = SphereBuilder.CreateSphere(root.name, { segments: 10, diameter: 1 }, scene);
  228. sphere.parent = root;
  229. var hemisphere = HemisphereBuilder.CreateHemisphere(root.name, { segments: 10, diameter: 2 }, scene);
  230. hemisphere.parent = root;
  231. hemisphere.rotation.x = -Math.PI / 2;
  232. var lines = this._CreateLightLines(2, scene);
  233. lines.parent = root;
  234. root.scaling.scaleInPlace(LightGizmo._Scale);
  235. root.rotation.x = Math.PI / 2;
  236. return root;
  237. }
  238. private static _CreateDirectionalLightMesh(scene: Scene) {
  239. var root = new Mesh("directionalLight", scene);
  240. var mesh = new Mesh(root.name, scene);
  241. mesh.parent = root;
  242. var sphere = SphereBuilder.CreateSphere(root.name, { diameter: 1.2, segments: 10 }, scene);
  243. sphere.parent = mesh;
  244. var line = Mesh.CreateCylinder(root.name, 6, 0.3, 0.3, 6, 1, scene);
  245. line.parent = mesh;
  246. var left = line.clone(root.name)!;
  247. left.scaling.y = 0.5;
  248. left.position.x += 1.25;
  249. var right = line.clone(root.name)!;
  250. right.scaling.y = 0.5;
  251. right.position.x += -1.25;
  252. var arrowHead = Mesh.CreateCylinder(root.name, 1, 0, 0.6, 6, 1, scene);
  253. arrowHead.position.y += 3;
  254. arrowHead.parent = mesh;
  255. var left = arrowHead.clone(root.name);
  256. left.position.y = 1.5;
  257. left.position.x += 1.25;
  258. var right = arrowHead.clone(root.name);
  259. right.position.y = 1.5;
  260. right.position.x += -1.25;
  261. mesh.scaling.scaleInPlace(LightGizmo._Scale);
  262. mesh.rotation.z = Math.PI / 2;
  263. mesh.rotation.y = Math.PI / 2;
  264. return root;
  265. }
  266. }