mtlFileLoader.ts 8.6 KB

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