babylon.light.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. module BABYLON {
  2. export interface IShadowLight extends Light {
  3. id: string;
  4. position: Vector3;
  5. transformedPosition: Vector3;
  6. name: string;
  7. shadowMinZ: number;
  8. shadowMaxZ: number;
  9. computeTransformedPosition(): boolean;
  10. getScene(): Scene;
  11. customProjectionMatrixBuilder: (viewMatrix: Matrix, renderList: Array<AbstractMesh>, result: Matrix) => void;
  12. setShadowProjectionMatrix(matrix: Matrix, viewMatrix: Matrix, renderList: Array<AbstractMesh>): void;
  13. getDepthScale(): number;
  14. needRefreshPerFrame(): boolean;
  15. needCube(): boolean;
  16. getShadowDirection(faceIndex?: number): Vector3;
  17. _shadowGenerator: IShadowGenerator;
  18. }
  19. export class Light extends Node {
  20. //lightmapMode Consts
  21. private static _LIGHTMAP_DEFAULT = 0;
  22. private static _LIGHTMAP_SPECULAR = 1;
  23. private static _LIGHTMAP_SHADOWSONLY = 2;
  24. /**
  25. * If every light affecting the material is in this lightmapMode,
  26. * material.lightmapTexture adds or multiplies
  27. * (depends on material.useLightmapAsShadowmap)
  28. * after every other light calculations.
  29. */
  30. public static get LIGHTMAP_DEFAULT(): number {
  31. return Light._LIGHTMAP_DEFAULT;
  32. }
  33. /**
  34. * material.lightmapTexture as only diffuse lighting from this light
  35. * adds pnly specular lighting from this light
  36. * adds dynamic shadows
  37. */
  38. public static get LIGHTMAP_SPECULAR(): number {
  39. return Light._LIGHTMAP_SPECULAR;
  40. }
  41. /**
  42. * material.lightmapTexture as only lighting
  43. * no light calculation from this light
  44. * only adds dynamic shadows from this light
  45. */
  46. public static get LIGHTMAP_SHADOWSONLY(): number {
  47. return Light._LIGHTMAP_SHADOWSONLY;
  48. }
  49. @serializeAsColor3()
  50. public diffuse = new Color3(1.0, 1.0, 1.0);
  51. @serializeAsColor3()
  52. public specular = new Color3(1.0, 1.0, 1.0);
  53. @serialize()
  54. public intensity = 1.0;
  55. @serialize()
  56. public range = Number.MAX_VALUE;
  57. private _includedOnlyMeshes: AbstractMesh[];
  58. public get includedOnlyMeshes(): AbstractMesh[] {
  59. return this._includedOnlyMeshes;
  60. }
  61. public set includedOnlyMeshes(value: AbstractMesh[]) {
  62. this._includedOnlyMeshes = value;
  63. this._hookArrayForIncludedOnly(value);
  64. }
  65. private _excludedMeshes: AbstractMesh[];
  66. public get excludedMeshes(): AbstractMesh[] {
  67. return this._excludedMeshes;
  68. }
  69. public set excludedMeshes(value: AbstractMesh[]) {
  70. this._excludedMeshes = value;
  71. this._hookArrayForExcluded(value);
  72. }
  73. @serialize("excludeWithLayerMask")
  74. private _excludeWithLayerMask = 0;
  75. public get excludeWithLayerMask(): number {
  76. return this._excludeWithLayerMask;
  77. }
  78. public set excludeWithLayerMask(value: number) {
  79. this._excludeWithLayerMask = value;
  80. this._resyncMeshes();
  81. }
  82. @serialize("includeOnlyWithLayerMask")
  83. private _includeOnlyWithLayerMask = 0;
  84. public get includeOnlyWithLayerMask(): number {
  85. return this._includeOnlyWithLayerMask;
  86. }
  87. public set includeOnlyWithLayerMask(value: number) {
  88. this._includeOnlyWithLayerMask = value;
  89. this._resyncMeshes();
  90. }
  91. @serialize("lightmapMode")
  92. private _lightmapMode = 0;
  93. public get lightmapMode(): number {
  94. return this._lightmapMode;
  95. }
  96. public set lightmapMode(value: number) {
  97. if (this._lightmapMode === value) {
  98. return;
  99. }
  100. this._lightmapMode = value;
  101. this._markMeshesAsLightDirty();
  102. }
  103. // PBR Properties.
  104. @serialize()
  105. public radius = 0.00001;
  106. public _shadowGenerator: IShadowGenerator;
  107. private _parentedWorldMatrix: Matrix;
  108. public _excludedMeshesIds = new Array<string>();
  109. public _includedOnlyMeshesIds = new Array<string>();
  110. // Light uniform buffer
  111. public _uniformBuffer: UniformBuffer;
  112. /**
  113. * Creates a Light object in the scene.
  114. * Documentation : http://doc.babylonjs.com/tutorials/lights
  115. */
  116. constructor(name: string, scene: Scene) {
  117. super(name, scene);
  118. this.getScene().addLight(this);
  119. this._uniformBuffer = new UniformBuffer(this.getScene().getEngine());
  120. this._buildUniformLayout();
  121. this.includedOnlyMeshes = new Array<AbstractMesh>();
  122. this.excludedMeshes = new Array<AbstractMesh>();
  123. this._resyncMeshes();
  124. }
  125. protected _buildUniformLayout(): void {
  126. // Overridden
  127. }
  128. /**
  129. * Returns the string "Light".
  130. */
  131. public getClassName(): string {
  132. return "Light";
  133. }
  134. /**
  135. * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
  136. */
  137. public toString(fullDetails? : boolean) : string {
  138. var ret = "Name: " + this.name;
  139. ret += ", type: " + (["Point", "Directional", "Spot", "Hemispheric"])[this.getTypeID()];
  140. if (this.animations){
  141. for (var i = 0; i < this.animations.length; i++){
  142. ret += ", animation[0]: " + this.animations[i].toString(fullDetails);
  143. }
  144. }
  145. if (fullDetails){
  146. }
  147. return ret;
  148. }
  149. /**
  150. * Set the enabled state of this node.
  151. * @param {boolean} value - the new enabled state
  152. * @see isEnabled
  153. */
  154. public setEnabled(value: boolean): void {
  155. super.setEnabled(value);
  156. this._resyncMeshes();
  157. }
  158. /**
  159. * Returns the Light associated shadow generator.
  160. */
  161. public getShadowGenerator(): IShadowGenerator {
  162. return this._shadowGenerator;
  163. }
  164. /**
  165. * Returns a Vector3, the absolute light position in the World.
  166. */
  167. public getAbsolutePosition(): Vector3 {
  168. return Vector3.Zero();
  169. }
  170. public transferToEffect(effect: Effect, lightIndex: string): void {
  171. }
  172. public _getWorldMatrix(): Matrix {
  173. return Matrix.Identity();
  174. }
  175. /**
  176. * Boolean : True if the light will affect the passed mesh.
  177. */
  178. public canAffectMesh(mesh: AbstractMesh): boolean {
  179. if (!mesh) {
  180. return true;
  181. }
  182. if (this.includedOnlyMeshes.length > 0 && this.includedOnlyMeshes.indexOf(mesh) === -1) {
  183. return false;
  184. }
  185. if (this.excludedMeshes.length > 0 && this.excludedMeshes.indexOf(mesh) !== -1) {
  186. return false;
  187. }
  188. if (this.includeOnlyWithLayerMask !== 0 && (this.includeOnlyWithLayerMask & mesh.layerMask) === 0) {
  189. return false;
  190. }
  191. if (this.excludeWithLayerMask !== 0 && this.excludeWithLayerMask & mesh.layerMask) {
  192. return false;
  193. }
  194. return true;
  195. }
  196. /**
  197. * Returns the light World matrix.
  198. */
  199. public getWorldMatrix(): Matrix {
  200. this._currentRenderId = this.getScene().getRenderId();
  201. var worldMatrix = this._getWorldMatrix();
  202. if (this.parent && this.parent.getWorldMatrix) {
  203. if (!this._parentedWorldMatrix) {
  204. this._parentedWorldMatrix = Matrix.Identity();
  205. }
  206. worldMatrix.multiplyToRef(this.parent.getWorldMatrix(), this._parentedWorldMatrix);
  207. this._markSyncedWithParent();
  208. return this._parentedWorldMatrix;
  209. }
  210. return worldMatrix;
  211. }
  212. /**
  213. * Disposes the light.
  214. */
  215. public dispose(): void {
  216. if (this._shadowGenerator) {
  217. this._shadowGenerator.dispose();
  218. this._shadowGenerator = null;
  219. }
  220. // Animations
  221. this.getScene().stopAnimation(this);
  222. // Remove from meshes
  223. for (var mesh of this.getScene().meshes) {
  224. mesh._removeLightSource(this);
  225. }
  226. this._uniformBuffer.dispose();
  227. // Remove from scene
  228. this.getScene().removeLight(this);
  229. super.dispose();
  230. }
  231. /**
  232. * Returns the light type ID (integer).
  233. */
  234. public getTypeID(): number {
  235. return 0;
  236. }
  237. /**
  238. * Returns a new Light object, named "name", from the current one.
  239. */
  240. public clone(name: string): Light {
  241. return SerializationHelper.Clone(Light.GetConstructorFromName(this.getTypeID(), name, this.getScene()), this);
  242. }
  243. /**
  244. * Serializes the current light into a Serialization object.
  245. * Returns the serialized object.
  246. */
  247. public serialize(): any {
  248. var serializationObject = SerializationHelper.Serialize(this);
  249. // Type
  250. serializationObject.type = this.getTypeID();
  251. // Parent
  252. if (this.parent) {
  253. serializationObject.parentId = this.parent.id;
  254. }
  255. // Inclusion / exclusions
  256. if (this.excludedMeshes.length > 0) {
  257. serializationObject.excludedMeshesIds = [];
  258. this.excludedMeshes.forEach((mesh: AbstractMesh) => {
  259. serializationObject.excludedMeshesIds.push(mesh.id);
  260. });
  261. }
  262. if (this.includedOnlyMeshes.length > 0) {
  263. serializationObject.includedOnlyMeshesIds = [];
  264. this.includedOnlyMeshes.forEach((mesh: AbstractMesh) => {
  265. serializationObject.includedOnlyMeshesIds.push(mesh.id);
  266. });
  267. }
  268. // Animations
  269. Animation.AppendSerializedAnimations(this, serializationObject);
  270. serializationObject.ranges = this.serializeAnimationRanges();
  271. return serializationObject;
  272. }
  273. /**
  274. * Creates a new typed light from the passed type (integer) : point light = 0, directional light = 1, spot light = 2, hemispheric light = 3.
  275. * This new light is named "name" and added to the passed scene.
  276. */
  277. static GetConstructorFromName(type: number, name: string, scene: Scene): () => Light {
  278. switch (type) {
  279. case 0:
  280. return () => new PointLight(name, Vector3.Zero(), scene);
  281. case 1:
  282. return () => new DirectionalLight(name, Vector3.Zero(), scene);
  283. case 2:
  284. return () => new SpotLight(name, Vector3.Zero(), Vector3.Zero(), 0, 0, scene);
  285. case 3:
  286. return () => new HemisphericLight(name, Vector3.Zero(), scene);
  287. }
  288. }
  289. /**
  290. * Parses the passed "parsedLight" and returns a new instanced Light from this parsing.
  291. */
  292. public static Parse(parsedLight: any, scene: Scene): Light {
  293. var light = SerializationHelper.Parse(Light.GetConstructorFromName(parsedLight.type, parsedLight.name, scene), parsedLight, scene);
  294. // Inclusion / exclusions
  295. if (parsedLight.excludedMeshesIds) {
  296. light._excludedMeshesIds = parsedLight.excludedMeshesIds;
  297. }
  298. if (parsedLight.includedOnlyMeshesIds) {
  299. light._includedOnlyMeshesIds = parsedLight.includedOnlyMeshesIds;
  300. }
  301. // Parent
  302. if (parsedLight.parentId) {
  303. light._waitingParentId = parsedLight.parentId;
  304. }
  305. // Animations
  306. if (parsedLight.animations) {
  307. for (var animationIndex = 0; animationIndex < parsedLight.animations.length; animationIndex++) {
  308. var parsedAnimation = parsedLight.animations[animationIndex];
  309. light.animations.push(Animation.Parse(parsedAnimation));
  310. }
  311. Node.ParseAnimationRanges(light, parsedLight, scene);
  312. }
  313. if (parsedLight.autoAnimate) {
  314. scene.beginAnimation(light, parsedLight.autoAnimateFrom, parsedLight.autoAnimateTo, parsedLight.autoAnimateLoop, parsedLight.autoAnimateSpeed || 1.0);
  315. }
  316. return light;
  317. }
  318. private _hookArrayForExcluded(array: AbstractMesh[]): void {
  319. var oldPush = array.push;
  320. array.push = (...items: AbstractMesh[]) => {
  321. var result = oldPush.apply(array, items);
  322. for (var item of items) {
  323. item._resyncLighSource(this);
  324. }
  325. return result;
  326. }
  327. var oldSplice = array.splice;
  328. array.splice = (index: number, deleteCount?: number) => {
  329. var deleted = oldSplice.apply(array, [index, deleteCount]);
  330. for (var item of deleted) {
  331. item._resyncLighSource(this);
  332. }
  333. return deleted;
  334. }
  335. }
  336. private _hookArrayForIncludedOnly(array: AbstractMesh[]): void {
  337. var oldPush = array.push;
  338. array.push = (...items: AbstractMesh[]) => {
  339. var result = oldPush.apply(array, items);
  340. this._resyncMeshes();
  341. return result;
  342. }
  343. var oldSplice = array.splice;
  344. array.splice = (index: number, deleteCount?: number) => {
  345. var deleted = oldSplice.apply(array, [index, deleteCount]);
  346. this._resyncMeshes();
  347. return deleted;
  348. }
  349. }
  350. private _resyncMeshes() {
  351. for (var mesh of this.getScene().meshes) {
  352. mesh._resyncLighSource(this);
  353. }
  354. }
  355. public _markMeshesAsLightDirty() {
  356. for (var mesh of this.getScene().meshes) {
  357. if (mesh._lightSources.indexOf(this) !== -1) {
  358. mesh._markSubMeshesAsLightDirty();
  359. }
  360. }
  361. }
  362. }
  363. }