KHR_lights_punctual.ts 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import { SpotLight } from "babylonjs/Lights/spotLight";
  2. import { Vector3, Color3, Quaternion } from "babylonjs/Maths/math";
  3. import { Light } from "babylonjs/Lights/light";
  4. import { Node } from "babylonjs/node";
  5. import { ShadowLight } from "babylonjs/Lights/shadowLight";
  6. import { IChildRootProperty } from "babylonjs-gltf2interface";
  7. import { INode } from "babylonjs-gltf2interface";
  8. import { IGLTFExporterExtensionV2 } from "../glTFExporterExtension";
  9. import { _Exporter } from "../glTFExporter";
  10. import { Logger } from "babylonjs/Misc/logger";
  11. import { _GLTFUtilities } from "../glTFUtilities";
  12. const NAME = "KHR_lights_punctual";
  13. enum LightType {
  14. DIRECTIONAL = "directional",
  15. POINT = "point",
  16. SPOT = "spot"
  17. }
  18. interface ILightReference {
  19. light: number;
  20. }
  21. interface ILight extends IChildRootProperty {
  22. type: LightType;
  23. color?: number[];
  24. intensity?: number;
  25. range?: number;
  26. spot?: {
  27. innerConeAngle?: number;
  28. outerConeAngle?: number;
  29. };
  30. }
  31. interface ILights {
  32. lights: ILight[];
  33. }
  34. /**
  35. * [Specification](https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_lights_punctual/README.md)
  36. */
  37. export class KHR_lights_punctual implements IGLTFExporterExtensionV2 {
  38. /** The name of this extension. */
  39. public readonly name = NAME;
  40. /** Defines whether this extension is enabled. */
  41. public enabled = true;
  42. /** Defines whether this extension is required */
  43. public required = false;
  44. /** Reference to the glTF exporter */
  45. private _exporter: _Exporter;
  46. private _lights: ILights;
  47. /** @hidden */
  48. constructor(exporter: _Exporter) {
  49. this._exporter = exporter;
  50. }
  51. /** @hidden */
  52. public dispose() {
  53. delete this._exporter;
  54. delete this._lights;
  55. }
  56. /** @hidden */
  57. public onExporting(): void {
  58. if (this._lights) {
  59. if (this._exporter._glTF.extensionsUsed == null) {
  60. this._exporter._glTF.extensionsUsed = [];
  61. }
  62. if (this._exporter._glTF.extensionsUsed.indexOf(NAME) === -1) {
  63. this._exporter._glTF.extensionsUsed.push(NAME);
  64. }
  65. if (this.required) {
  66. if (this._exporter._glTF.extensionsRequired == null) {
  67. this._exporter._glTF.extensionsRequired = [];
  68. }
  69. if (this._exporter._glTF.extensionsRequired.indexOf(NAME) === -1) {
  70. this._exporter._glTF.extensionsRequired.push(NAME);
  71. }
  72. }
  73. if (this._exporter._glTF.extensions == null) {
  74. this._exporter._glTF.extensions = {};
  75. }
  76. this._exporter._glTF.extensions[NAME] = this._lights;
  77. }
  78. }
  79. /**
  80. * Define this method to modify the default behavior when exporting a node
  81. * @param context The context when exporting the node
  82. * @param node glTF node
  83. * @param babylonNode BabylonJS node
  84. * @returns nullable INode promise
  85. */
  86. public postExportNodeAsync(context: string, node: INode, babylonNode: Node): Promise<INode> {
  87. return new Promise((resolve, reject) => {
  88. if (babylonNode instanceof ShadowLight) {
  89. const babylonLight: ShadowLight = babylonNode;
  90. let light: ILight;
  91. const lightType = (
  92. babylonLight.getTypeID() == Light.LIGHTTYPEID_POINTLIGHT ? LightType.POINT : (
  93. babylonLight.getTypeID() == Light.LIGHTTYPEID_DIRECTIONALLIGHT ? LightType.DIRECTIONAL : (
  94. babylonLight.getTypeID() == Light.LIGHTTYPEID_SPOTLIGHT ? LightType.SPOT : null
  95. )));
  96. if (lightType == null) {
  97. Logger.Warn(`${context}: Light ${babylonLight.name} is not supported in ${NAME}`);
  98. }
  99. else {
  100. const lightPosition = babylonLight.position.clone();
  101. if (!lightPosition.equals(Vector3.Zero())) {
  102. if (this._exporter._convertToRightHandedSystem) {
  103. _GLTFUtilities._GetRightHandedPositionVector3FromRef(lightPosition);
  104. }
  105. node.translation = lightPosition.asArray();
  106. }
  107. if (lightType !== LightType.POINT) {
  108. const localAxis = babylonLight.direction;
  109. const yaw = -Math.atan2(localAxis.z, localAxis.x) + Math.PI / 2;
  110. const len = Math.sqrt(localAxis.x * localAxis.x + localAxis.z * localAxis.z);
  111. const pitch = -Math.atan2(localAxis.y, len);
  112. const lightRotationQuaternion = Quaternion.RotationYawPitchRoll(yaw, pitch, 0);
  113. if (this._exporter._convertToRightHandedSystem) {
  114. _GLTFUtilities._GetRightHandedQuaternionFromRef(lightRotationQuaternion);
  115. }
  116. if (!lightRotationQuaternion.equals(Quaternion.Identity())) {
  117. node.rotation = lightRotationQuaternion.asArray();
  118. }
  119. }
  120. if (babylonLight.falloffType !== Light.FALLOFF_GLTF) {
  121. Logger.Warn(`${context}: Light falloff for ${babylonLight.name} does not match the ${NAME} specification!`);
  122. }
  123. light = {
  124. type: lightType
  125. };
  126. if (!babylonLight.diffuse.equals(Color3.White())) {
  127. light.color = babylonLight.diffuse.asArray();
  128. }
  129. if (babylonLight.intensity !== 1.0) {
  130. light.intensity = babylonLight.intensity;
  131. }
  132. if (babylonLight.range !== Number.MAX_VALUE) {
  133. light.range = babylonLight.range;
  134. }
  135. if (lightType === LightType.SPOT) {
  136. const babylonSpotLight = babylonLight as SpotLight;
  137. if (babylonSpotLight.angle !== Math.PI / 2.0) {
  138. if (light.spot == null) {
  139. light.spot = {};
  140. }
  141. light.spot.outerConeAngle = babylonSpotLight.angle / 2.0;
  142. }
  143. if (babylonSpotLight.innerAngle !== 0) {
  144. if (light.spot == null) {
  145. light.spot = {};
  146. }
  147. light.spot.innerConeAngle = babylonSpotLight.innerAngle / 2.0;
  148. }
  149. }
  150. if (this._lights == null) {
  151. this._lights = {
  152. lights: []
  153. };
  154. }
  155. this._lights.lights.push(light);
  156. if (node.extensions == null) {
  157. node.extensions = {};
  158. }
  159. const lightReference: ILightReference = {
  160. light: this._lights.lights.length - 1
  161. };
  162. node.extensions[NAME] = lightReference;
  163. }
  164. }
  165. resolve(node);
  166. });
  167. }
  168. }
  169. _Exporter.RegisterExtension(NAME, (exporter) => new KHR_lights_punctual(exporter));