mtlFileLoader.ts 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. import { Nullable } from "babylonjs/types";
  2. import { Color3 } from 'babylonjs/Maths/math.color';
  3. import { Texture } from "babylonjs/Materials/Textures/texture";
  4. import { StandardMaterial } from "babylonjs/Materials/standardMaterial";
  5. import { Scene } from "babylonjs/scene";
  6. /**
  7. * Class reading and parsing the MTL file bundled with the obj file.
  8. */
  9. export class MTLFileLoader {
  10. /**
  11. * Invert Y-Axis of referenced textures on load
  12. */
  13. public static INVERT_TEXTURE_Y = true;
  14. /**
  15. * All material loaded from the mtl will be set here
  16. */
  17. public materials: StandardMaterial[] = [];
  18. /**
  19. * This function will read the mtl file and create each material described inside
  20. * This function could be improve by adding :
  21. * -some component missing (Ni, Tf...)
  22. * -including the specific options available
  23. *
  24. * @param scene defines the scene the material will be created in
  25. * @param data defines the mtl data to parse
  26. * @param rootUrl defines the rooturl to use in order to load relative dependencies
  27. * @param forAssetContainer defines if the material should be registered in the scene
  28. */
  29. public parseMTL(scene: Scene, data: string | ArrayBuffer, rootUrl: string, forAssetContainer: boolean): void {
  30. if (data instanceof ArrayBuffer) {
  31. return;
  32. }
  33. //Split the lines from the file
  34. var lines = data.split('\n');
  35. //Space char
  36. var delimiter_pattern = /\s+/;
  37. //Array with RGB colors
  38. var color: number[];
  39. //New material
  40. var material: Nullable<StandardMaterial> = null;
  41. //Look at each line
  42. for (var i = 0; i < lines.length; i++) {
  43. var line = lines[i].trim();
  44. // Blank line or comment
  45. if (line.length === 0 || line.charAt(0) === '#') {
  46. continue;
  47. }
  48. //Get the first parameter (keyword)
  49. var pos = line.indexOf(' ');
  50. var key = (pos >= 0) ? line.substring(0, pos) : line;
  51. key = key.toLowerCase();
  52. //Get the data following the key
  53. var value: string = (pos >= 0) ? line.substring(pos + 1).trim() : "";
  54. //This mtl keyword will create the new material
  55. if (key === "newmtl") {
  56. //Check if it is the first material.
  57. // Materials specifications are described after this keyword.
  58. if (material) {
  59. //Add the previous material in the material array.
  60. this.materials.push(material);
  61. }
  62. //Create a new material.
  63. // value is the name of the material read in the mtl file
  64. scene._blockEntityCollection = forAssetContainer;
  65. material = new StandardMaterial(value, scene);
  66. scene._blockEntityCollection = false;
  67. } else if (key === "kd" && material) {
  68. // Diffuse color (color under white light) using RGB values
  69. //value = "r g b"
  70. color = <number[]>value.split(delimiter_pattern, 3).map(parseFloat);
  71. //color = [r,g,b]
  72. //Set tghe color into the material
  73. material.diffuseColor = Color3.FromArray(color);
  74. } else if (key === "ka" && material) {
  75. // Ambient color (color under shadow) using RGB values
  76. //value = "r g b"
  77. color = <number[]>value.split(delimiter_pattern, 3).map(parseFloat);
  78. //color = [r,g,b]
  79. //Set tghe color into the material
  80. material.ambientColor = Color3.FromArray(color);
  81. } else if (key === "ks" && material) {
  82. // Specular color (color when light is reflected from shiny surface) using RGB values
  83. //value = "r g b"
  84. color = <number[]>value.split(delimiter_pattern, 3).map(parseFloat);
  85. //color = [r,g,b]
  86. //Set the color into the material
  87. material.specularColor = Color3.FromArray(color);
  88. } else if (key === "ke" && material) {
  89. // Emissive color using RGB values
  90. color = value.split(delimiter_pattern, 3).map(parseFloat);
  91. material.emissiveColor = Color3.FromArray(color);
  92. } else if (key === "ns" && material) {
  93. //value = "Integer"
  94. material.specularPower = parseFloat(value);
  95. } else if (key === "d" && material) {
  96. //d is dissolve for current material. It mean alpha for BABYLON
  97. material.alpha = parseFloat(value);
  98. //Texture
  99. //This part can be improved by adding the possible options of texture
  100. } else if (key === "map_ka" && material) {
  101. // ambient texture map with a loaded image
  102. //We must first get the folder of the image
  103. material.ambientTexture = MTLFileLoader._getTexture(rootUrl, value, scene);
  104. } else if (key === "map_kd" && material) {
  105. // Diffuse texture map with a loaded image
  106. material.diffuseTexture = MTLFileLoader._getTexture(rootUrl, value, scene);
  107. } else if (key === "map_ks" && material) {
  108. // Specular texture map with a loaded image
  109. //We must first get the folder of the image
  110. material.specularTexture = MTLFileLoader._getTexture(rootUrl, value, scene);
  111. } else if (key === "map_ns") {
  112. //Specular
  113. //Specular highlight component
  114. //We must first get the folder of the image
  115. //
  116. //Not supported by BABYLON
  117. //
  118. // continue;
  119. } else if (key === "map_bump" && material) {
  120. //The bump texture
  121. material.bumpTexture = MTLFileLoader._getTexture(rootUrl, value, scene);
  122. } else if (key === "map_d" && material) {
  123. // The dissolve of the material
  124. material.opacityTexture = MTLFileLoader._getTexture(rootUrl, value, scene);
  125. //Options for illumination
  126. } else if (key === "illum") {
  127. //Illumination
  128. if (value === "0") {
  129. //That mean Kd == Kd
  130. } else if (value === "1") {
  131. //Color on and Ambient on
  132. } else if (value === "2") {
  133. //Highlight on
  134. } else if (value === "3") {
  135. //Reflection on and Ray trace on
  136. } else if (value === "4") {
  137. //Transparency: Glass on, Reflection: Ray trace on
  138. } else if (value === "5") {
  139. //Reflection: Fresnel on and Ray trace on
  140. } else if (value === "6") {
  141. //Transparency: Refraction on, Reflection: Fresnel off and Ray trace on
  142. } else if (value === "7") {
  143. //Transparency: Refraction on, Reflection: Fresnel on and Ray trace on
  144. } else if (value === "8") {
  145. //Reflection on and Ray trace off
  146. } else if (value === "9") {
  147. //Transparency: Glass on, Reflection: Ray trace off
  148. } else if (value === "10") {
  149. //Casts shadows onto invisible surfaces
  150. }
  151. } else {
  152. // console.log("Unhandled expression at line : " + i +'\n' + "with value : " + line);
  153. }
  154. }
  155. //At the end of the file, add the last material
  156. if (material) {
  157. this.materials.push(material);
  158. }
  159. }
  160. /**
  161. * Gets the texture for the material.
  162. *
  163. * If the material is imported from input file,
  164. * We sanitize the url to ensure it takes the textre from aside the material.
  165. *
  166. * @param rootUrl The root url to load from
  167. * @param value The value stored in the mtl
  168. * @return The Texture
  169. */
  170. private static _getTexture(rootUrl: string, value: string, scene: Scene): Nullable<Texture> {
  171. if (!value) {
  172. return null;
  173. }
  174. var url = rootUrl;
  175. // Load from input file.
  176. if (rootUrl === "file:") {
  177. var lastDelimiter = value.lastIndexOf("\\");
  178. if (lastDelimiter === -1) {
  179. lastDelimiter = value.lastIndexOf("/");
  180. }
  181. if (lastDelimiter > -1) {
  182. url += value.substr(lastDelimiter + 1);
  183. }
  184. else {
  185. url += value;
  186. }
  187. }
  188. // Not from input file.
  189. else {
  190. url += value;
  191. }
  192. return new Texture(url, scene, false, MTLFileLoader.INVERT_TEXTURE_Y);
  193. }
  194. }