objFileLoader.ts 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216
  1. import { Nullable, FloatArray, IndicesArray } from "babylonjs/types";
  2. import { Vector3, Vector2, Color3, Color4 } from "babylonjs/Maths/math";
  3. import { Tools } from "babylonjs/Misc/tools";
  4. import { VertexData } from "babylonjs/Meshes/mesh.vertexData";
  5. import { Geometry } from "babylonjs/Meshes/geometry";
  6. import { AnimationGroup } from "babylonjs/Animations/animationGroup";
  7. import { Skeleton } from "babylonjs/Bones/skeleton";
  8. import { IParticleSystem } from "babylonjs/Particles/IParticleSystem";
  9. import { Texture } from "babylonjs/Materials/Textures/texture";
  10. import { StandardMaterial } from "babylonjs/Materials/standardMaterial";
  11. import { AbstractMesh } from "babylonjs/Meshes/abstractMesh";
  12. import { Mesh } from "babylonjs/Meshes/mesh";
  13. import { SceneLoader, ISceneLoaderPluginAsync, SceneLoaderProgressEvent, ISceneLoaderPluginFactory, ISceneLoaderPlugin } from "babylonjs/Loading/sceneLoader";
  14. import { AssetContainer } from "babylonjs/assetContainer";
  15. import { Scene } from "babylonjs/scene";
  16. import { WebRequest } from 'babylonjs/Misc/webRequest';
  17. /**
  18. * Class reading and parsing the MTL file bundled with the obj file.
  19. */
  20. export class MTLFileLoader {
  21. /**
  22. * All material loaded from the mtl will be set here
  23. */
  24. public materials: StandardMaterial[] = [];
  25. /**
  26. * This function will read the mtl file and create each material described inside
  27. * This function could be improve by adding :
  28. * -some component missing (Ni, Tf...)
  29. * -including the specific options available
  30. *
  31. * @param scene defines the scene the material will be created in
  32. * @param data defines the mtl data to parse
  33. * @param rootUrl defines the rooturl to use in order to load relative dependencies
  34. */
  35. public parseMTL(scene: Scene, data: string | ArrayBuffer, rootUrl: string): void {
  36. if (data instanceof ArrayBuffer) {
  37. return;
  38. }
  39. //Split the lines from the file
  40. var lines = data.split('\n');
  41. //Space char
  42. var delimiter_pattern = /\s+/;
  43. //Array with RGB colors
  44. var color: number[];
  45. //New material
  46. var material: Nullable<StandardMaterial> = null;
  47. //Look at each line
  48. for (var i = 0; i < lines.length; i++) {
  49. var line = lines[i].trim();
  50. // Blank line or comment
  51. if (line.length === 0 || line.charAt(0) === '#') {
  52. continue;
  53. }
  54. //Get the first parameter (keyword)
  55. var pos = line.indexOf(' ');
  56. var key = (pos >= 0) ? line.substring(0, pos) : line;
  57. key = key.toLowerCase();
  58. //Get the data following the key
  59. var value: string = (pos >= 0) ? line.substring(pos + 1).trim() : "";
  60. //This mtl keyword will create the new material
  61. if (key === "newmtl") {
  62. //Check if it is the first material.
  63. // Materials specifications are described after this keyword.
  64. if (material) {
  65. //Add the previous material in the material array.
  66. this.materials.push(material);
  67. }
  68. //Create a new material.
  69. // value is the name of the material read in the mtl file
  70. material = new StandardMaterial(value, scene);
  71. } else if (key === "kd" && material) {
  72. // Diffuse color (color under white light) 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.diffuseColor = Color3.FromArray(color);
  78. } else if (key === "ka" && material) {
  79. // Ambient color (color under shadow) 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 tghe color into the material
  84. material.ambientColor = Color3.FromArray(color);
  85. } else if (key === "ks" && material) {
  86. // Specular color (color when light is reflected from shiny surface) using RGB values
  87. //value = "r g b"
  88. color = <number[]>value.split(delimiter_pattern, 3).map(parseFloat);
  89. //color = [r,g,b]
  90. //Set the color into the material
  91. material.specularColor = Color3.FromArray(color);
  92. } else if (key === "ke" && material) {
  93. // Emissive color using RGB values
  94. color = value.split(delimiter_pattern, 3).map(parseFloat);
  95. material.emissiveColor = Color3.FromArray(color);
  96. } else if (key === "ns" && material) {
  97. //value = "Integer"
  98. material.specularPower = parseFloat(value);
  99. } else if (key === "d" && material) {
  100. //d is dissolve for current material. It mean alpha for BABYLON
  101. material.alpha = parseFloat(value);
  102. //Texture
  103. //This part can be improved by adding the possible options of texture
  104. } else if (key === "map_ka" && material) {
  105. // ambient texture map with a loaded image
  106. //We must first get the folder of the image
  107. material.ambientTexture = MTLFileLoader._getTexture(rootUrl, value, scene);
  108. } else if (key === "map_kd" && material) {
  109. // Diffuse texture map with a loaded image
  110. material.diffuseTexture = MTLFileLoader._getTexture(rootUrl, value, scene);
  111. } else if (key === "map_ks" && material) {
  112. // Specular texture map with a loaded image
  113. //We must first get the folder of the image
  114. material.specularTexture = MTLFileLoader._getTexture(rootUrl, value, scene);
  115. } else if (key === "map_ns") {
  116. //Specular
  117. //Specular highlight component
  118. //We must first get the folder of the image
  119. //
  120. //Not supported by BABYLON
  121. //
  122. // continue;
  123. } else if (key === "map_bump" && material) {
  124. //The bump texture
  125. material.bumpTexture = MTLFileLoader._getTexture(rootUrl, value, scene);
  126. } else if (key === "map_d" && material) {
  127. // The dissolve of the material
  128. material.opacityTexture = MTLFileLoader._getTexture(rootUrl, value, scene);
  129. //Options for illumination
  130. } else if (key === "illum") {
  131. //Illumination
  132. if (value === "0") {
  133. //That mean Kd == Kd
  134. } else if (value === "1") {
  135. //Color on and Ambient on
  136. } else if (value === "2") {
  137. //Highlight on
  138. } else if (value === "3") {
  139. //Reflection on and Ray trace on
  140. } else if (value === "4") {
  141. //Transparency: Glass on, Reflection: Ray trace on
  142. } else if (value === "5") {
  143. //Reflection: Fresnel on and Ray trace on
  144. } else if (value === "6") {
  145. //Transparency: Refraction on, Reflection: Fresnel off and Ray trace on
  146. } else if (value === "7") {
  147. //Transparency: Refraction on, Reflection: Fresnel on and Ray trace on
  148. } else if (value === "8") {
  149. //Reflection on and Ray trace off
  150. } else if (value === "9") {
  151. //Transparency: Glass on, Reflection: Ray trace off
  152. } else if (value === "10") {
  153. //Casts shadows onto invisible surfaces
  154. }
  155. } else {
  156. // console.log("Unhandled expression at line : " + i +'\n' + "with value : " + line);
  157. }
  158. }
  159. //At the end of the file, add the last material
  160. if (material) {
  161. this.materials.push(material);
  162. }
  163. }
  164. /**
  165. * Gets the texture for the material.
  166. *
  167. * If the material is imported from input file,
  168. * We sanitize the url to ensure it takes the textre from aside the material.
  169. *
  170. * @param rootUrl The root url to load from
  171. * @param value The value stored in the mtl
  172. * @return The Texture
  173. */
  174. private static _getTexture(rootUrl: string, value: string, scene: Scene): Nullable<Texture> {
  175. if (!value) {
  176. return null;
  177. }
  178. var url = rootUrl;
  179. // Load from input file.
  180. if (rootUrl === "file:") {
  181. var lastDelimiter = value.lastIndexOf("\\");
  182. if (lastDelimiter === -1) {
  183. lastDelimiter = value.lastIndexOf("/");
  184. }
  185. if (lastDelimiter > -1) {
  186. url += value.substr(lastDelimiter + 1);
  187. }
  188. else {
  189. url += value;
  190. }
  191. }
  192. // Not from input file.
  193. else {
  194. url += value;
  195. }
  196. return new Texture(url, scene, false, OBJFileLoader.INVERT_TEXTURE_Y);
  197. }
  198. }
  199. type MeshObject = {
  200. name: string;
  201. indices?: Array<number>;
  202. positions?: Array<number>;
  203. normals?: Array<number>;
  204. colors?: Array<number>;
  205. uvs?: Array<number>;
  206. materialName: string;
  207. };
  208. /**
  209. * Options for loading OBJ/MTL files
  210. */
  211. type MeshLoadOptions = {
  212. /**
  213. * Defines if UVs are optimized by default during load.
  214. */
  215. OptimizeWithUV: boolean,
  216. /**
  217. * Defines custom scaling of UV coordinates of loaded meshes.
  218. */
  219. UVScaling: Vector2;
  220. /**
  221. * Invert model on y-axis (does a model scaling inversion)
  222. */
  223. InvertY: boolean,
  224. /**
  225. * Invert Y-Axis of referenced textures on load
  226. */
  227. InvertTextureY: boolean;
  228. /**
  229. * Include in meshes the vertex colors available in some OBJ files. This is not part of OBJ standard.
  230. */
  231. ImportVertexColors: boolean,
  232. /**
  233. * Compute the normals for the model, even if normals are present in the file.
  234. */
  235. ComputeNormals: boolean,
  236. /**
  237. * Skip loading the materials even if defined in the OBJ file (materials are ignored).
  238. */
  239. SkipMaterials: boolean,
  240. /**
  241. * When a material fails to load OBJ loader will silently fail and onSuccess() callback will be triggered.
  242. */
  243. MaterialLoadingFailsSilently: boolean
  244. };
  245. /**
  246. * OBJ file type loader.
  247. * This is a babylon scene loader plugin.
  248. */
  249. export class OBJFileLoader implements ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
  250. /**
  251. * Defines if UVs are optimized by default during load.
  252. */
  253. public static OPTIMIZE_WITH_UV = false;
  254. /**
  255. * Invert model on y-axis (does a model scaling inversion)
  256. */
  257. public static INVERT_Y = false;
  258. /**
  259. * Invert Y-Axis of referenced textures on load
  260. */
  261. public static INVERT_TEXTURE_Y = true;
  262. /**
  263. * Include in meshes the vertex colors available in some OBJ files. This is not part of OBJ standard.
  264. */
  265. public static IMPORT_VERTEX_COLORS = false;
  266. /**
  267. * Compute the normals for the model, even if normals are present in the file.
  268. */
  269. public static COMPUTE_NORMALS = false;
  270. /**
  271. * Defines custom scaling of UV coordinates of loaded meshes.
  272. */
  273. public static UV_SCALING = new Vector2(1, 1);
  274. /**
  275. * Skip loading the materials even if defined in the OBJ file (materials are ignored).
  276. */
  277. public static SKIP_MATERIALS = false;
  278. /**
  279. * When a material fails to load OBJ loader will silently fail and onSuccess() callback will be triggered.
  280. *
  281. * Defaults to true for backwards compatibility.
  282. */
  283. public static MATERIAL_LOADING_FAILS_SILENTLY = true;
  284. /**
  285. * Defines the name of the plugin.
  286. */
  287. public name = "obj";
  288. /**
  289. * Defines the extension the plugin is able to load.
  290. */
  291. public extensions = ".obj";
  292. /** @hidden */
  293. public obj = /^o/;
  294. /** @hidden */
  295. public group = /^g/;
  296. /** @hidden */
  297. public mtllib = /^mtllib /;
  298. /** @hidden */
  299. public usemtl = /^usemtl /;
  300. /** @hidden */
  301. public smooth = /^s /;
  302. /** @hidden */
  303. public vertexPattern = /v( +[\d|\.|\+|\-|e|E]+){3,7}/;
  304. // vn float float float
  305. /** @hidden */
  306. public normalPattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/;
  307. // vt float float
  308. /** @hidden */
  309. public uvPattern = /vt( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/;
  310. // f vertex vertex vertex ...
  311. /** @hidden */
  312. public facePattern1 = /f\s+(([\d]{1,}[\s]?){3,})+/;
  313. // f vertex/uvs vertex/uvs vertex/uvs ...
  314. /** @hidden */
  315. public facePattern2 = /f\s+((([\d]{1,}\/[\d]{1,}[\s]?){3,})+)/;
  316. // f vertex/uvs/normal vertex/uvs/normal vertex/uvs/normal ...
  317. /** @hidden */
  318. public facePattern3 = /f\s+((([\d]{1,}\/[\d]{1,}\/[\d]{1,}[\s]?){3,})+)/;
  319. // f vertex//normal vertex//normal vertex//normal ...
  320. /** @hidden */
  321. public facePattern4 = /f\s+((([\d]{1,}\/\/[\d]{1,}[\s]?){3,})+)/;
  322. // f -vertex/-uvs/-normal -vertex/-uvs/-normal -vertex/-uvs/-normal ...
  323. /** @hidden */
  324. public facePattern5 = /f\s+(((-[\d]{1,}\/-[\d]{1,}\/-[\d]{1,}[\s]?){3,})+)/;
  325. private _meshLoadOptions: MeshLoadOptions;
  326. /**
  327. * Creates loader for .OBJ files
  328. *
  329. * @param meshLoadOptions options for loading and parsing OBJ/MTL files.
  330. */
  331. constructor(meshLoadOptions?: MeshLoadOptions) {
  332. this._meshLoadOptions = meshLoadOptions || OBJFileLoader.currentMeshLoadOptions;
  333. }
  334. private static get currentMeshLoadOptions(): MeshLoadOptions {
  335. return {
  336. ComputeNormals: OBJFileLoader.COMPUTE_NORMALS,
  337. ImportVertexColors: OBJFileLoader.IMPORT_VERTEX_COLORS,
  338. InvertY: OBJFileLoader.INVERT_Y,
  339. InvertTextureY: OBJFileLoader.INVERT_TEXTURE_Y,
  340. UVScaling: OBJFileLoader.UV_SCALING,
  341. MaterialLoadingFailsSilently: OBJFileLoader.MATERIAL_LOADING_FAILS_SILENTLY,
  342. OptimizeWithUV: OBJFileLoader.OPTIMIZE_WITH_UV,
  343. SkipMaterials: OBJFileLoader.SKIP_MATERIALS
  344. };
  345. }
  346. /**
  347. * Calls synchronously the MTL file attached to this obj.
  348. * Load function or importMesh function don't enable to load 2 files in the same time asynchronously.
  349. * Without this function materials are not displayed in the first frame (but displayed after).
  350. * In consequence it is impossible to get material information in your HTML file
  351. *
  352. * @param url The URL of the MTL file
  353. * @param rootUrl
  354. * @param onSuccess Callback function to be called when the MTL file is loaded
  355. * @private
  356. */
  357. private _loadMTL(url: string, rootUrl: string, onSuccess: (response: string | ArrayBuffer, responseUrl?: string) => any, onFailure: (pathOfFile: string, exception?: any) => void) {
  358. //The complete path to the mtl file
  359. var pathOfFile = Tools.BaseUrl + rootUrl + url;
  360. // Loads through the babylon tools to allow fileInput search.
  361. Tools.LoadFile(
  362. pathOfFile,
  363. onSuccess,
  364. undefined,
  365. undefined,
  366. false,
  367. (request?: WebRequest | undefined, exception?: any) => {
  368. onFailure(pathOfFile, exception);
  369. }
  370. );
  371. }
  372. /**
  373. * Instantiates a OBJ file loader plugin.
  374. * @returns the created plugin
  375. */
  376. createPlugin(): ISceneLoaderPluginAsync | ISceneLoaderPlugin {
  377. return new OBJFileLoader(OBJFileLoader.currentMeshLoadOptions);
  378. }
  379. /**
  380. * If the data string can be loaded directly.
  381. *
  382. * @param data string containing the file data
  383. * @returns if the data can be loaded directly
  384. */
  385. public canDirectLoad(data: string): boolean {
  386. return false;
  387. }
  388. /**
  389. * Imports one or more meshes from the loaded OBJ data and adds them to the scene
  390. * @param meshesNames a string or array of strings of the mesh names that should be loaded from the file
  391. * @param scene the scene the meshes should be added to
  392. * @param data the OBJ data to load
  393. * @param rootUrl root url to load from
  394. * @param onProgress event that fires when loading progress has occured
  395. * @param fileName Defines the name of the file to load
  396. * @returns a promise containg the loaded meshes, particles, skeletons and animations
  397. */
  398. public importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fileName?: string): Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
  399. //get the meshes from OBJ file
  400. return this._parseSolid(meshesNames, scene, data, rootUrl).then((meshes) => {
  401. return {
  402. meshes,
  403. particleSystems: [],
  404. skeletons: [],
  405. animationGroups: []
  406. };
  407. });
  408. }
  409. /**
  410. * Imports all objects from the loaded OBJ data and adds them to the scene
  411. * @param scene the scene the objects should be added to
  412. * @param data the OBJ data to load
  413. * @param rootUrl root url to load from
  414. * @param onProgress event that fires when loading progress has occured
  415. * @param fileName Defines the name of the file to load
  416. * @returns a promise which completes when objects have been loaded to the scene
  417. */
  418. public loadAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fileName?: string): Promise<void> {
  419. //Get the 3D model
  420. return this.importMeshAsync(null, scene, data, rootUrl, onProgress).then(() => {
  421. // return void
  422. });
  423. }
  424. /**
  425. * Load into an asset container.
  426. * @param scene The scene to load into
  427. * @param data The data to import
  428. * @param rootUrl The root url for scene and resources
  429. * @param onProgress The callback when the load progresses
  430. * @param fileName Defines the name of the file to load
  431. * @returns The loaded asset container
  432. */
  433. public loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fileName?: string): Promise<AssetContainer> {
  434. return this.importMeshAsync(null, scene, data, rootUrl).then((result) => {
  435. var container = new AssetContainer(scene);
  436. result.meshes.forEach((mesh) => container.meshes.push(mesh));
  437. result.meshes.forEach((mesh) => {
  438. var material = mesh.material;
  439. if (material) {
  440. // Materials
  441. if (container.materials.indexOf(material) == -1) {
  442. container.materials.push(material);
  443. // Textures
  444. var textures = material.getActiveTextures();
  445. textures.forEach((t) => {
  446. if (container.textures.indexOf(t) == -1) {
  447. container.textures.push(t);
  448. }
  449. });
  450. }
  451. }
  452. });
  453. container.removeAllFromScene();
  454. return container;
  455. });
  456. }
  457. /**
  458. * Read the OBJ file and create an Array of meshes.
  459. * Each mesh contains all information given by the OBJ and the MTL file.
  460. * i.e. vertices positions and indices, optional normals values, optional UV values, optional material
  461. *
  462. * @param meshesNames
  463. * @param scene Scene The scene where are displayed the data
  464. * @param data String The content of the obj file
  465. * @param rootUrl String The path to the folder
  466. * @returns Array<AbstractMesh>
  467. * @private
  468. */
  469. private _parseSolid(meshesNames: any, scene: Scene, data: string, rootUrl: string): Promise<Array<AbstractMesh>> {
  470. var positions: Array<Vector3> = []; //values for the positions of vertices
  471. var normals: Array<Vector3> = []; //Values for the normals
  472. var uvs: Array<Vector2> = []; //Values for the textures
  473. var colors: Array<Color4> = [];
  474. var meshesFromObj: Array<MeshObject> = []; //[mesh] Contains all the obj meshes
  475. var handledMesh: MeshObject; //The current mesh of meshes array
  476. var indicesForBabylon: Array<number> = []; //The list of indices for VertexData
  477. var wrappedPositionForBabylon: Array<Vector3> = []; //The list of position in vectors
  478. var wrappedUvsForBabylon: Array<Vector2> = []; //Array with all value of uvs to match with the indices
  479. var wrappedColorsForBabylon: Array<Color4> = []; // Array with all color values to match with the indices
  480. var wrappedNormalsForBabylon: Array<Vector3> = []; //Array with all value of normals to match with the indices
  481. var tuplePosNorm: Array<{ normals: Array<number>; idx: Array<number>; uv: Array<number> }> = []; //Create a tuple with indice of Position, Normal, UV [pos, norm, uvs]
  482. var curPositionInIndices = 0;
  483. var hasMeshes: Boolean = false; //Meshes are defined in the file
  484. var unwrappedPositionsForBabylon: Array<number> = []; //Value of positionForBabylon w/o Vector3() [x,y,z]
  485. var unwrappedColorsForBabylon: Array<number> = []; // Value of colorForBabylon w/o Color4() [r,g,b,a]
  486. var unwrappedNormalsForBabylon: Array<number> = []; //Value of normalsForBabylon w/o Vector3() [x,y,z]
  487. var unwrappedUVForBabylon: Array<number> = []; //Value of uvsForBabylon w/o Vector3() [x,y,z]
  488. var triangles: Array<string> = []; //Indices from new triangles coming from polygons
  489. var materialNameFromObj: string = ""; //The name of the current material
  490. var fileToLoad: string = ""; //The name of the mtlFile to load
  491. var materialsFromMTLFile: MTLFileLoader = new MTLFileLoader();
  492. var objMeshName: string = ""; //The name of the current obj mesh
  493. var increment: number = 1; //Id for meshes created by the multimaterial
  494. var isFirstMaterial: boolean = true;
  495. var grayColor = new Color4(0.5, 0.5, 0.5, 1);
  496. /**
  497. * Search for obj in the given array.
  498. * This function is called to check if a couple of data already exists in an array.
  499. *
  500. * If found, returns the index of the founded tuple index. Returns -1 if not found
  501. * @param arr Array<{ normals: Array<number>, idx: Array<number> }>
  502. * @param obj Array<number>
  503. * @returns {boolean}
  504. */
  505. var isInArray = (arr: Array<{ normals: Array<number>; idx: Array<number> }>, obj: Array<number>) => {
  506. if (!arr[obj[0]]) { arr[obj[0]] = { normals: [], idx: [] }; }
  507. var idx = arr[obj[0]].normals.indexOf(obj[1]);
  508. return idx === -1 ? -1 : arr[obj[0]].idx[idx];
  509. };
  510. var isInArrayUV = (arr: Array<{ normals: Array<number>; idx: Array<number>; uv: Array<number> }>, obj: Array<number>) => {
  511. if (!arr[obj[0]]) { arr[obj[0]] = { normals: [], idx: [], uv: [] }; }
  512. var idx = arr[obj[0]].normals.indexOf(obj[1]);
  513. if (idx != 1 && (obj[2] === arr[obj[0]].uv[idx])) {
  514. return arr[obj[0]].idx[idx];
  515. }
  516. return -1;
  517. };
  518. /**
  519. * This function set the data for each triangle.
  520. * Data are position, normals and uvs
  521. * If a tuple of (position, normal) is not set, add the data into the corresponding array
  522. * If the tuple already exist, add only their indice
  523. *
  524. * @param indicePositionFromObj Integer The index in positions array
  525. * @param indiceUvsFromObj Integer The index in uvs array
  526. * @param indiceNormalFromObj Integer The index in normals array
  527. * @param positionVectorFromOBJ Vector3 The value of position at index objIndice
  528. * @param textureVectorFromOBJ Vector3 The value of uvs
  529. * @param normalsVectorFromOBJ Vector3 The value of normals at index objNormale
  530. */
  531. var setData = (indicePositionFromObj: number, indiceUvsFromObj: number, indiceNormalFromObj: number, positionVectorFromOBJ: Vector3, textureVectorFromOBJ: Vector2, normalsVectorFromOBJ: Vector3, positionColorsFromOBJ?: Color4) => {
  532. //Check if this tuple already exists in the list of tuples
  533. var _index: number;
  534. if (this._meshLoadOptions.OptimizeWithUV) {
  535. _index = isInArrayUV(
  536. tuplePosNorm,
  537. [
  538. indicePositionFromObj,
  539. indiceNormalFromObj,
  540. indiceUvsFromObj
  541. ]
  542. );
  543. }
  544. else {
  545. _index = isInArray(
  546. tuplePosNorm,
  547. [
  548. indicePositionFromObj,
  549. indiceNormalFromObj
  550. ]
  551. );
  552. }
  553. //If it not exists
  554. if (_index === -1) {
  555. //Add an new indice.
  556. //The array of indices is only an array with his length equal to the number of triangles - 1.
  557. //We add vertices data in this order
  558. indicesForBabylon.push(wrappedPositionForBabylon.length);
  559. //Push the position of vertice for Babylon
  560. //Each element is a Vector3(x,y,z)
  561. wrappedPositionForBabylon.push(positionVectorFromOBJ);
  562. //Push the uvs for Babylon
  563. //Each element is a Vector3(u,v)
  564. wrappedUvsForBabylon.push(textureVectorFromOBJ);
  565. //Push the normals for Babylon
  566. //Each element is a Vector3(x,y,z)
  567. wrappedNormalsForBabylon.push(normalsVectorFromOBJ);
  568. if (positionColorsFromOBJ !== undefined) {
  569. //Push the colors for Babylon
  570. //Each element is a BABYLON.Color4(r,g,b,a)
  571. wrappedColorsForBabylon.push(positionColorsFromOBJ);
  572. }
  573. //Add the tuple in the comparison list
  574. tuplePosNorm[indicePositionFromObj].normals.push(indiceNormalFromObj);
  575. tuplePosNorm[indicePositionFromObj].idx.push(curPositionInIndices++);
  576. if (this._meshLoadOptions.OptimizeWithUV) { tuplePosNorm[indicePositionFromObj].uv.push(indiceUvsFromObj); }
  577. } else {
  578. //The tuple already exists
  579. //Add the index of the already existing tuple
  580. //At this index we can get the value of position, normal, color and uvs of vertex
  581. indicesForBabylon.push(_index);
  582. }
  583. };
  584. /**
  585. * Transform Vector() and BABYLON.Color() objects into numbers in an array
  586. */
  587. var unwrapData = () => {
  588. //Every array has the same length
  589. for (var l = 0; l < wrappedPositionForBabylon.length; l++) {
  590. //Push the x, y, z values of each element in the unwrapped array
  591. unwrappedPositionsForBabylon.push(wrappedPositionForBabylon[l].x, wrappedPositionForBabylon[l].y, wrappedPositionForBabylon[l].z);
  592. unwrappedNormalsForBabylon.push(wrappedNormalsForBabylon[l].x, wrappedNormalsForBabylon[l].y, wrappedNormalsForBabylon[l].z);
  593. unwrappedUVForBabylon.push(wrappedUvsForBabylon[l].x, wrappedUvsForBabylon[l].y); //z is an optional value not supported by BABYLON
  594. }
  595. if (this._meshLoadOptions.ImportVertexColors === true) {
  596. //Push the r, g, b, a values of each element in the unwrapped array
  597. unwrappedColorsForBabylon.push(wrappedColorsForBabylon[l].r, wrappedColorsForBabylon[l].g, wrappedColorsForBabylon[l].b, wrappedColorsForBabylon[l].a);
  598. }
  599. // Reset arrays for the next new meshes
  600. wrappedPositionForBabylon = [];
  601. wrappedNormalsForBabylon = [];
  602. wrappedUvsForBabylon = [];
  603. wrappedColorsForBabylon = [];
  604. tuplePosNorm = [];
  605. curPositionInIndices = 0;
  606. };
  607. /**
  608. * Create triangles from polygons by recursion
  609. * The best to understand how it works is to draw it in the same time you get the recursion.
  610. * It is important to notice that a triangle is a polygon
  611. * We get 5 patterns of face defined in OBJ File :
  612. * facePattern1 = ["1","2","3","4","5","6"]
  613. * facePattern2 = ["1/1","2/2","3/3","4/4","5/5","6/6"]
  614. * facePattern3 = ["1/1/1","2/2/2","3/3/3","4/4/4","5/5/5","6/6/6"]
  615. * facePattern4 = ["1//1","2//2","3//3","4//4","5//5","6//6"]
  616. * facePattern5 = ["-1/-1/-1","-2/-2/-2","-3/-3/-3","-4/-4/-4","-5/-5/-5","-6/-6/-6"]
  617. * Each pattern is divided by the same method
  618. * @param face Array[String] The indices of elements
  619. * @param v Integer The variable to increment
  620. */
  621. var getTriangles = (face: Array<string>, v: number) => {
  622. //Work for each element of the array
  623. if (v + 1 < face.length) {
  624. //Add on the triangle variable the indexes to obtain triangles
  625. triangles.push(face[0], face[v], face[v + 1]);
  626. //Incrementation for recursion
  627. v += 1;
  628. //Recursion
  629. getTriangles(face, v);
  630. }
  631. //Result obtained after 2 iterations:
  632. //Pattern1 => triangle = ["1","2","3","1","3","4"];
  633. //Pattern2 => triangle = ["1/1","2/2","3/3","1/1","3/3","4/4"];
  634. //Pattern3 => triangle = ["1/1/1","2/2/2","3/3/3","1/1/1","3/3/3","4/4/4"];
  635. //Pattern4 => triangle = ["1//1","2//2","3//3","1//1","3//3","4//4"];
  636. //Pattern5 => triangle = ["-1/-1/-1","-2/-2/-2","-3/-3/-3","-1/-1/-1","-3/-3/-3","-4/-4/-4"];
  637. };
  638. /**
  639. * Create triangles and push the data for each polygon for the pattern 1
  640. * In this pattern we get vertice positions
  641. * @param face
  642. * @param v
  643. */
  644. var setDataForCurrentFaceWithPattern1 = (face: Array<string>, v: number) => {
  645. //Get the indices of triangles for each polygon
  646. getTriangles(face, v);
  647. //For each element in the triangles array.
  648. //This var could contains 1 to an infinity of triangles
  649. for (var k = 0; k < triangles.length; k++) {
  650. // Set position indice
  651. var indicePositionFromObj = parseInt(triangles[k]) - 1;
  652. setData(
  653. indicePositionFromObj,
  654. 0, 0, //In the pattern 1, normals and uvs are not defined
  655. positions[indicePositionFromObj], //Get the vectors data
  656. Vector2.Zero(), Vector3.Up(), //Create default vectors
  657. this._meshLoadOptions.ImportVertexColors === true ? colors[indicePositionFromObj] : undefined
  658. );
  659. }
  660. //Reset variable for the next line
  661. triangles = [];
  662. };
  663. /**
  664. * Create triangles and push the data for each polygon for the pattern 2
  665. * In this pattern we get vertice positions and uvsu
  666. * @param face
  667. * @param v
  668. */
  669. var setDataForCurrentFaceWithPattern2 = (face: Array<string>, v: number) => {
  670. //Get the indices of triangles for each polygon
  671. getTriangles(face, v);
  672. for (var k = 0; k < triangles.length; k++) {
  673. //triangle[k] = "1/1"
  674. //Split the data for getting position and uv
  675. var point = triangles[k].split("/"); // ["1", "1"]
  676. //Set position indice
  677. var indicePositionFromObj = parseInt(point[0]) - 1;
  678. //Set uv indice
  679. var indiceUvsFromObj = parseInt(point[1]) - 1;
  680. setData(
  681. indicePositionFromObj,
  682. indiceUvsFromObj,
  683. 0, //Default value for normals
  684. positions[indicePositionFromObj], //Get the values for each element
  685. uvs[indiceUvsFromObj],
  686. Vector3.Up(), //Default value for normals
  687. this._meshLoadOptions.ImportVertexColors === true ? colors[indicePositionFromObj] : undefined
  688. );
  689. }
  690. //Reset variable for the next line
  691. triangles = [];
  692. };
  693. /**
  694. * Create triangles and push the data for each polygon for the pattern 3
  695. * In this pattern we get vertice positions, uvs and normals
  696. * @param face
  697. * @param v
  698. */
  699. var setDataForCurrentFaceWithPattern3 = (face: Array<string>, v: number) => {
  700. //Get the indices of triangles for each polygon
  701. getTriangles(face, v);
  702. for (var k = 0; k < triangles.length; k++) {
  703. //triangle[k] = "1/1/1"
  704. //Split the data for getting position, uv, and normals
  705. var point = triangles[k].split("/"); // ["1", "1", "1"]
  706. // Set position indice
  707. var indicePositionFromObj = parseInt(point[0]) - 1;
  708. // Set uv indice
  709. var indiceUvsFromObj = parseInt(point[1]) - 1;
  710. // Set normal indice
  711. var indiceNormalFromObj = parseInt(point[2]) - 1;
  712. setData(
  713. indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj,
  714. positions[indicePositionFromObj], uvs[indiceUvsFromObj], normals[indiceNormalFromObj] //Set the vector for each component
  715. );
  716. }
  717. //Reset variable for the next line
  718. triangles = [];
  719. };
  720. /**
  721. * Create triangles and push the data for each polygon for the pattern 4
  722. * In this pattern we get vertice positions and normals
  723. * @param face
  724. * @param v
  725. */
  726. var setDataForCurrentFaceWithPattern4 = (face: Array<string>, v: number) => {
  727. getTriangles(face, v);
  728. for (var k = 0; k < triangles.length; k++) {
  729. //triangle[k] = "1//1"
  730. //Split the data for getting position and normals
  731. var point = triangles[k].split("//"); // ["1", "1"]
  732. // We check indices, and normals
  733. var indicePositionFromObj = parseInt(point[0]) - 1;
  734. var indiceNormalFromObj = parseInt(point[1]) - 1;
  735. setData(
  736. indicePositionFromObj,
  737. 1, //Default value for uv
  738. indiceNormalFromObj,
  739. positions[indicePositionFromObj], //Get each vector of data
  740. Vector2.Zero(),
  741. normals[indiceNormalFromObj],
  742. this._meshLoadOptions.ImportVertexColors === true ? colors[indicePositionFromObj] : undefined
  743. );
  744. }
  745. //Reset variable for the next line
  746. triangles = [];
  747. };
  748. /**
  749. * Create triangles and push the data for each polygon for the pattern 3
  750. * In this pattern we get vertice positions, uvs and normals
  751. * @param face
  752. * @param v
  753. */
  754. var setDataForCurrentFaceWithPattern5 = (face: Array<string>, v: number) => {
  755. //Get the indices of triangles for each polygon
  756. getTriangles(face, v);
  757. for (var k = 0; k < triangles.length; k++) {
  758. //triangle[k] = "-1/-1/-1"
  759. //Split the data for getting position, uv, and normals
  760. var point = triangles[k].split("/"); // ["-1", "-1", "-1"]
  761. // Set position indice
  762. var indicePositionFromObj = positions.length + parseInt(point[0]);
  763. // Set uv indice
  764. var indiceUvsFromObj = uvs.length + parseInt(point[1]);
  765. // Set normal indice
  766. var indiceNormalFromObj = normals.length + parseInt(point[2]);
  767. setData(
  768. indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj,
  769. positions[indicePositionFromObj], uvs[indiceUvsFromObj], normals[indiceNormalFromObj], //Set the vector for each component
  770. this._meshLoadOptions.ImportVertexColors === true ? colors[indicePositionFromObj] : undefined
  771. );
  772. }
  773. //Reset variable for the next line
  774. triangles = [];
  775. };
  776. var addPreviousObjMesh = () => {
  777. //Check if it is not the first mesh. Otherwise we don't have data.
  778. if (meshesFromObj.length > 0) {
  779. //Get the previous mesh for applying the data about the faces
  780. //=> in obj file, faces definition append after the name of the mesh
  781. handledMesh = meshesFromObj[meshesFromObj.length - 1];
  782. //Set the data into Array for the mesh
  783. unwrapData();
  784. // Reverse tab. Otherwise face are displayed in the wrong sens
  785. indicesForBabylon.reverse();
  786. //Set the information for the mesh
  787. //Slice the array to avoid rewriting because of the fact this is the same var which be rewrited
  788. handledMesh.indices = indicesForBabylon.slice();
  789. handledMesh.positions = unwrappedPositionsForBabylon.slice();
  790. handledMesh.normals = unwrappedNormalsForBabylon.slice();
  791. handledMesh.uvs = unwrappedUVForBabylon.slice();
  792. if (this._meshLoadOptions.ImportVertexColors === true) {
  793. handledMesh.colors = unwrappedColorsForBabylon.slice();
  794. }
  795. //Reset the array for the next mesh
  796. indicesForBabylon = [];
  797. unwrappedPositionsForBabylon = [];
  798. unwrappedColorsForBabylon = [];
  799. unwrappedNormalsForBabylon = [];
  800. unwrappedUVForBabylon = [];
  801. }
  802. };
  803. //Main function
  804. //Split the file into lines
  805. var lines = data.split('\n');
  806. //Look at each line
  807. for (var i = 0; i < lines.length; i++) {
  808. var line = lines[i].trim();
  809. var result;
  810. //Comment or newLine
  811. if (line.length === 0 || line.charAt(0) === '#') {
  812. continue;
  813. //Get information about one position possible for the vertices
  814. } else if (this.vertexPattern.test(line)) {
  815. result = line.match(/[^ ]+/g)!; // match will return non-null due to passing regex pattern
  816. // Value of result with line: "v 1.0 2.0 3.0"
  817. // ["v", "1.0", "2.0", "3.0"]
  818. // Create a Vector3 with the position x, y, z
  819. positions.push(new Vector3(
  820. parseFloat(result[1]),
  821. parseFloat(result[2]),
  822. parseFloat(result[3])
  823. ));
  824. if (this._meshLoadOptions.ImportVertexColors === true) {
  825. if (result.length >= 7) {
  826. // TODO: if these numbers are > 1 we can use Color4.FromInts(r,g,b,a)
  827. colors.push(new Color4(
  828. parseFloat(result[4]),
  829. parseFloat(result[5]),
  830. parseFloat(result[6]),
  831. (result.length === 7 || result[7] === undefined) ? 1 : parseFloat(result[7])
  832. ));
  833. } else {
  834. // TODO: maybe push NULL and if all are NULL to skip (and remove grayColor var).
  835. colors.push(grayColor);
  836. }
  837. }
  838. } else if ((result = this.normalPattern.exec(line)) !== null) {
  839. //Create a Vector3 with the normals x, y, z
  840. //Value of result
  841. // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
  842. //Add the Vector in the list of normals
  843. normals.push(new Vector3(
  844. parseFloat(result[1]),
  845. parseFloat(result[2]),
  846. parseFloat(result[3])
  847. ));
  848. } else if ((result = this.uvPattern.exec(line)) !== null) {
  849. //Create a Vector2 with the normals u, v
  850. //Value of result
  851. // ["vt 0.1 0.2 0.3", "0.1", "0.2"]
  852. //Add the Vector in the list of uvs
  853. uvs.push(new Vector2(
  854. parseFloat(result[1]) * OBJFileLoader.UV_SCALING.x,
  855. parseFloat(result[2]) * OBJFileLoader.UV_SCALING.y
  856. ));
  857. //Identify patterns of faces
  858. //Face could be defined in different type of pattern
  859. } else if ((result = this.facePattern3.exec(line)) !== null) {
  860. //Value of result:
  861. //["f 1/1/1 2/2/2 3/3/3", "1/1/1 2/2/2 3/3/3"...]
  862. //Set the data for this face
  863. setDataForCurrentFaceWithPattern3(
  864. result[1].trim().split(" "), // ["1/1/1", "2/2/2", "3/3/3"]
  865. 1
  866. );
  867. } else if ((result = this.facePattern4.exec(line)) !== null) {
  868. //Value of result:
  869. //["f 1//1 2//2 3//3", "1//1 2//2 3//3"...]
  870. //Set the data for this face
  871. setDataForCurrentFaceWithPattern4(
  872. result[1].trim().split(" "), // ["1//1", "2//2", "3//3"]
  873. 1
  874. );
  875. } else if ((result = this.facePattern5.exec(line)) !== null) {
  876. //Value of result:
  877. //["f -1/-1/-1 -2/-2/-2 -3/-3/-3", "-1/-1/-1 -2/-2/-2 -3/-3/-3"...]
  878. //Set the data for this face
  879. setDataForCurrentFaceWithPattern5(
  880. result[1].trim().split(" "), // ["-1/-1/-1", "-2/-2/-2", "-3/-3/-3"]
  881. 1
  882. );
  883. } else if ((result = this.facePattern2.exec(line)) !== null) {
  884. //Value of result:
  885. //["f 1/1 2/2 3/3", "1/1 2/2 3/3"...]
  886. //Set the data for this face
  887. setDataForCurrentFaceWithPattern2(
  888. result[1].trim().split(" "), // ["1/1", "2/2", "3/3"]
  889. 1
  890. );
  891. } else if ((result = this.facePattern1.exec(line)) !== null) {
  892. //Value of result
  893. //["f 1 2 3", "1 2 3"...]
  894. //Set the data for this face
  895. setDataForCurrentFaceWithPattern1(
  896. result[1].trim().split(" "), // ["1", "2", "3"]
  897. 1
  898. );
  899. //Define a mesh or an object
  900. //Each time this keyword is analysed, create a new Object with all data for creating a babylonMesh
  901. } else if (this.group.test(line) || this.obj.test(line)) {
  902. //Create a new mesh corresponding to the name of the group.
  903. //Definition of the mesh
  904. var objMesh: MeshObject = {
  905. name: line.substring(2).trim(), //Set the name of the current obj mesh
  906. indices: undefined,
  907. positions: undefined,
  908. normals: undefined,
  909. uvs: undefined,
  910. colors: undefined,
  911. materialName: ""
  912. };
  913. addPreviousObjMesh();
  914. //Push the last mesh created with only the name
  915. meshesFromObj.push(objMesh);
  916. //Set this variable to indicate that now meshesFromObj has objects defined inside
  917. hasMeshes = true;
  918. isFirstMaterial = true;
  919. increment = 1;
  920. //Keyword for applying a material
  921. } else if (this.usemtl.test(line)) {
  922. //Get the name of the material
  923. materialNameFromObj = line.substring(7).trim();
  924. //If this new material is in the same mesh
  925. if (!isFirstMaterial) {
  926. //Set the data for the previous mesh
  927. addPreviousObjMesh();
  928. //Create a new mesh
  929. var objMesh: MeshObject =
  930. //Set the name of the current obj mesh
  931. {
  932. name: objMeshName + "_mm" + increment.toString(), //Set the name of the current obj mesh
  933. indices: undefined,
  934. positions: undefined,
  935. normals: undefined,
  936. uvs: undefined,
  937. colors: undefined,
  938. materialName: materialNameFromObj
  939. };
  940. increment++;
  941. //If meshes are already defined
  942. meshesFromObj.push(objMesh);
  943. }
  944. //Set the material name if the previous line define a mesh
  945. if (hasMeshes && isFirstMaterial) {
  946. //Set the material name to the previous mesh (1 material per mesh)
  947. meshesFromObj[meshesFromObj.length - 1].materialName = materialNameFromObj;
  948. isFirstMaterial = false;
  949. }
  950. //Keyword for loading the mtl file
  951. } else if (this.mtllib.test(line)) {
  952. //Get the name of mtl file
  953. fileToLoad = line.substring(7).trim();
  954. //Apply smoothing
  955. } else if (this.smooth.test(line)) {
  956. // smooth shading => apply smoothing
  957. //Today I don't know it work with babylon and with obj.
  958. //With the obj file an integer is set
  959. } else {
  960. //If there is another possibility
  961. console.log("Unhandled expression at line : " + line);
  962. }
  963. }
  964. //At the end of the file, add the last mesh into the meshesFromObj array
  965. if (hasMeshes) {
  966. //Set the data for the last mesh
  967. handledMesh = meshesFromObj[meshesFromObj.length - 1];
  968. //Reverse indices for displaying faces in the good sense
  969. indicesForBabylon.reverse();
  970. //Get the good array
  971. unwrapData();
  972. //Set array
  973. handledMesh.indices = indicesForBabylon;
  974. handledMesh.positions = unwrappedPositionsForBabylon;
  975. handledMesh.normals = unwrappedNormalsForBabylon;
  976. handledMesh.uvs = unwrappedUVForBabylon;
  977. if (this._meshLoadOptions.ImportVertexColors === true) {
  978. handledMesh.colors = unwrappedColorsForBabylon;
  979. }
  980. }
  981. //If any o or g keyword found, create a mesh with a random id
  982. if (!hasMeshes) {
  983. // reverse tab of indices
  984. indicesForBabylon.reverse();
  985. //Get positions normals uvs
  986. unwrapData();
  987. //Set data for one mesh
  988. meshesFromObj.push({
  989. name: Geometry.RandomId(),
  990. indices: indicesForBabylon,
  991. positions: unwrappedPositionsForBabylon,
  992. colors: unwrappedColorsForBabylon,
  993. normals: unwrappedNormalsForBabylon,
  994. uvs: unwrappedUVForBabylon,
  995. materialName: materialNameFromObj
  996. });
  997. }
  998. //Create a Mesh list
  999. var babylonMeshesArray: Array<Mesh> = []; //The mesh for babylon
  1000. var materialToUse = new Array<string>();
  1001. //Set data for each mesh
  1002. for (var j = 0; j < meshesFromObj.length; j++) {
  1003. //check meshesNames (stlFileLoader)
  1004. if (meshesNames && meshesFromObj[j].name) {
  1005. if (meshesNames instanceof Array) {
  1006. if (meshesNames.indexOf(meshesFromObj[j].name) === -1) {
  1007. continue;
  1008. }
  1009. }
  1010. else {
  1011. if (meshesFromObj[j].name !== meshesNames) {
  1012. continue;
  1013. }
  1014. }
  1015. }
  1016. //Get the current mesh
  1017. //Set the data with VertexBuffer for each mesh
  1018. handledMesh = meshesFromObj[j];
  1019. //Create a Mesh with the name of the obj mesh
  1020. var babylonMesh = new Mesh(meshesFromObj[j].name, scene);
  1021. //Push the name of the material to an array
  1022. //This is indispensable for the importMesh function
  1023. materialToUse.push(meshesFromObj[j].materialName);
  1024. var vertexData: VertexData = new VertexData(); //The container for the values
  1025. //Set the data for the babylonMesh
  1026. vertexData.uvs = handledMesh.uvs as FloatArray;
  1027. vertexData.indices = handledMesh.indices as IndicesArray;
  1028. vertexData.positions = handledMesh.positions as FloatArray;
  1029. if (this._meshLoadOptions.ComputeNormals === true) {
  1030. let normals: Array<number> = new Array<number>();
  1031. VertexData.ComputeNormals(handledMesh.positions, handledMesh.indices, normals);
  1032. vertexData.normals = normals;
  1033. } else {
  1034. vertexData.normals = handledMesh.normals as FloatArray;
  1035. }
  1036. if (this._meshLoadOptions.ImportVertexColors === true) {
  1037. vertexData.colors = handledMesh.colors as FloatArray;
  1038. }
  1039. //Set the data from the VertexBuffer to the current Mesh
  1040. vertexData.applyToMesh(babylonMesh);
  1041. if (this._meshLoadOptions.InvertY) {
  1042. babylonMesh.scaling.y *= -1;
  1043. }
  1044. //Push the mesh into an array
  1045. babylonMeshesArray.push(babylonMesh);
  1046. }
  1047. let mtlPromises: Array<Promise<any>> = [];
  1048. //load the materials
  1049. //Check if we have a file to load
  1050. if (fileToLoad !== "" && this._meshLoadOptions.SkipMaterials === false) {
  1051. //Load the file synchronously
  1052. mtlPromises.push(new Promise((resolve, reject) => {
  1053. this._loadMTL(fileToLoad, rootUrl, (dataLoaded) => {
  1054. try {
  1055. //Create materials thanks MTLLoader function
  1056. materialsFromMTLFile.parseMTL(scene, dataLoaded, rootUrl);
  1057. //Look at each material loaded in the mtl file
  1058. for (var n = 0; n < materialsFromMTLFile.materials.length; n++) {
  1059. //Three variables to get all meshes with the same material
  1060. var startIndex = 0;
  1061. var _indices = [];
  1062. var _index;
  1063. //The material from MTL file is used in the meshes loaded
  1064. //Push the indice in an array
  1065. //Check if the material is not used for another mesh
  1066. while ((_index = materialToUse.indexOf(materialsFromMTLFile.materials[n].name, startIndex)) > -1) {
  1067. _indices.push(_index);
  1068. startIndex = _index + 1;
  1069. }
  1070. //If the material is not used dispose it
  1071. if (_index === -1 && _indices.length === 0) {
  1072. //If the material is not needed, remove it
  1073. materialsFromMTLFile.materials[n].dispose();
  1074. } else {
  1075. for (var o = 0; o < _indices.length; o++) {
  1076. //Apply the material to the Mesh for each mesh with the material
  1077. babylonMeshesArray[_indices[o]].material = materialsFromMTLFile.materials[n];
  1078. }
  1079. }
  1080. }
  1081. resolve();
  1082. } catch (e) {
  1083. Tools.Warn(`Error processing MTL file: '${fileToLoad}'`);
  1084. if (this._meshLoadOptions.MaterialLoadingFailsSilently) {
  1085. resolve();
  1086. } else {
  1087. reject(e);
  1088. }
  1089. }
  1090. }, (pathOfFile: string, exception?: any) => {
  1091. Tools.Warn(`Error downloading MTL file: '${fileToLoad}'`);
  1092. if (this._meshLoadOptions.MaterialLoadingFailsSilently) {
  1093. resolve();
  1094. } else {
  1095. reject(exception);
  1096. }
  1097. });
  1098. }));
  1099. }
  1100. //Return an array with all Mesh
  1101. return Promise.all(mtlPromises).then(() => {
  1102. return babylonMeshesArray;
  1103. });
  1104. }
  1105. }
  1106. if (SceneLoader) {
  1107. //Add this loader into the register plugin
  1108. SceneLoader.RegisterPlugin(new OBJFileLoader());
  1109. }