solidParser.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752
  1. import { VertexBuffer } from "@babylonjs/core/Buffers/buffer.js";
  2. import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial.js";
  3. import { Color3, Color4 } from "@babylonjs/core/Maths/math.color.js";
  4. import { Vector2, Vector3 } from "@babylonjs/core/Maths/math.vector.js";
  5. import { Geometry } from "@babylonjs/core/Meshes/geometry.js";
  6. import { Mesh } from "@babylonjs/core/Meshes/mesh.js";
  7. import { VertexData } from "@babylonjs/core/Meshes/mesh.vertexData.js";
  8. import { Logger } from "@babylonjs/core/Misc/logger.js";
  9. /**
  10. * Class used to load mesh data from OBJ content
  11. */
  12. export class SolidParser {
  13. /**
  14. * Creates a new SolidParser
  15. * @param materialToUse defines the array to fill with the list of materials to use (it will be filled by the parse function)
  16. * @param babylonMeshesArray defines the array to fill with the list of loaded meshes (it will be filled by the parse function)
  17. * @param loadingOptions defines the loading options to use
  18. */
  19. constructor(materialToUse, babylonMeshesArray, loadingOptions) {
  20. this._positions = []; //values for the positions of vertices
  21. this._normals = []; //Values for the normals
  22. this._uvs = []; //Values for the textures
  23. this._colors = [];
  24. this._meshesFromObj = []; //[mesh] Contains all the obj meshes
  25. this._indicesForBabylon = []; //The list of indices for VertexData
  26. this._wrappedPositionForBabylon = []; //The list of position in vectors
  27. this._wrappedUvsForBabylon = []; //Array with all value of uvs to match with the indices
  28. this._wrappedColorsForBabylon = []; // Array with all color values to match with the indices
  29. this._wrappedNormalsForBabylon = []; //Array with all value of normals to match with the indices
  30. this._tuplePosNorm = []; //Create a tuple with indice of Position, Normal, UV [pos, norm, uvs]
  31. this._curPositionInIndices = 0;
  32. this._hasMeshes = false; //Meshes are defined in the file
  33. this._unwrappedPositionsForBabylon = []; //Value of positionForBabylon w/o Vector3() [x,y,z]
  34. this._unwrappedColorsForBabylon = []; // Value of colorForBabylon w/o Color4() [r,g,b,a]
  35. this._unwrappedNormalsForBabylon = []; //Value of normalsForBabylon w/o Vector3() [x,y,z]
  36. this._unwrappedUVForBabylon = []; //Value of uvsForBabylon w/o Vector3() [x,y,z]
  37. this._triangles = []; //Indices from new triangles coming from polygons
  38. this._materialNameFromObj = ""; //The name of the current material
  39. this._objMeshName = ""; //The name of the current obj mesh
  40. this._increment = 1; //Id for meshes created by the multimaterial
  41. this._isFirstMaterial = true;
  42. this._grayColor = new Color4(0.5, 0.5, 0.5, 1);
  43. this._materialToUse = materialToUse;
  44. this._babylonMeshesArray = babylonMeshesArray;
  45. this._loadingOptions = loadingOptions;
  46. }
  47. /**
  48. * Search for obj in the given array.
  49. * This function is called to check if a couple of data already exists in an array.
  50. *
  51. * If found, returns the index of the founded tuple index. Returns -1 if not found
  52. * @param arr Array<{ normals: Array<number>, idx: Array<number> }>
  53. * @param obj Array<number>
  54. * @returns {boolean}
  55. */
  56. _isInArray(arr, obj) {
  57. if (!arr[obj[0]]) {
  58. arr[obj[0]] = { normals: [], idx: [] };
  59. }
  60. const idx = arr[obj[0]].normals.indexOf(obj[1]);
  61. return idx === -1 ? -1 : arr[obj[0]].idx[idx];
  62. }
  63. _isInArrayUV(arr, obj) {
  64. if (!arr[obj[0]]) {
  65. arr[obj[0]] = { normals: [], idx: [], uv: [] };
  66. }
  67. const idx = arr[obj[0]].normals.indexOf(obj[1]);
  68. if (idx != 1 && obj[2] === arr[obj[0]].uv[idx]) {
  69. return arr[obj[0]].idx[idx];
  70. }
  71. return -1;
  72. }
  73. /**
  74. * This function set the data for each triangle.
  75. * Data are position, normals and uvs
  76. * If a tuple of (position, normal) is not set, add the data into the corresponding array
  77. * If the tuple already exist, add only their indice
  78. *
  79. * @param indicePositionFromObj Integer The index in positions array
  80. * @param indiceUvsFromObj Integer The index in uvs array
  81. * @param indiceNormalFromObj Integer The index in normals array
  82. * @param positionVectorFromOBJ Vector3 The value of position at index objIndice
  83. * @param textureVectorFromOBJ Vector3 The value of uvs
  84. * @param normalsVectorFromOBJ Vector3 The value of normals at index objNormale
  85. * @param positionColorsFromOBJ
  86. */
  87. _setData(indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj, positionVectorFromOBJ, textureVectorFromOBJ, normalsVectorFromOBJ, positionColorsFromOBJ) {
  88. //Check if this tuple already exists in the list of tuples
  89. let _index;
  90. if (this._loadingOptions.optimizeWithUV) {
  91. _index = this._isInArrayUV(this._tuplePosNorm, [indicePositionFromObj, indiceNormalFromObj, indiceUvsFromObj]);
  92. }
  93. else {
  94. _index = this._isInArray(this._tuplePosNorm, [indicePositionFromObj, indiceNormalFromObj]);
  95. }
  96. //If it not exists
  97. if (_index === -1) {
  98. //Add an new indice.
  99. //The array of indices is only an array with his length equal to the number of triangles - 1.
  100. //We add vertices data in this order
  101. this._indicesForBabylon.push(this._wrappedPositionForBabylon.length);
  102. //Push the position of vertice for Babylon
  103. //Each element is a Vector3(x,y,z)
  104. this._wrappedPositionForBabylon.push(positionVectorFromOBJ);
  105. //Push the uvs for Babylon
  106. //Each element is a Vector3(u,v)
  107. this._wrappedUvsForBabylon.push(textureVectorFromOBJ);
  108. //Push the normals for Babylon
  109. //Each element is a Vector3(x,y,z)
  110. this._wrappedNormalsForBabylon.push(normalsVectorFromOBJ);
  111. if (positionColorsFromOBJ !== undefined) {
  112. //Push the colors for Babylon
  113. //Each element is a BABYLON.Color4(r,g,b,a)
  114. this._wrappedColorsForBabylon.push(positionColorsFromOBJ);
  115. }
  116. //Add the tuple in the comparison list
  117. this._tuplePosNorm[indicePositionFromObj].normals.push(indiceNormalFromObj);
  118. this._tuplePosNorm[indicePositionFromObj].idx.push(this._curPositionInIndices++);
  119. if (this._loadingOptions.optimizeWithUV) {
  120. this._tuplePosNorm[indicePositionFromObj].uv.push(indiceUvsFromObj);
  121. }
  122. }
  123. else {
  124. //The tuple already exists
  125. //Add the index of the already existing tuple
  126. //At this index we can get the value of position, normal, color and uvs of vertex
  127. this._indicesForBabylon.push(_index);
  128. }
  129. }
  130. /**
  131. * Transform Vector() and BABYLON.Color() objects into numbers in an array
  132. */
  133. _unwrapData() {
  134. //Every array has the same length
  135. for (let l = 0; l < this._wrappedPositionForBabylon.length; l++) {
  136. //Push the x, y, z values of each element in the unwrapped array
  137. this._unwrappedPositionsForBabylon.push(this._wrappedPositionForBabylon[l].x * this._handednessSign, this._wrappedPositionForBabylon[l].y, this._wrappedPositionForBabylon[l].z);
  138. this._unwrappedNormalsForBabylon.push(this._wrappedNormalsForBabylon[l].x * this._handednessSign, this._wrappedNormalsForBabylon[l].y, this._wrappedNormalsForBabylon[l].z);
  139. this._unwrappedUVForBabylon.push(this._wrappedUvsForBabylon[l].x, this._wrappedUvsForBabylon[l].y); //z is an optional value not supported by BABYLON
  140. if (this._loadingOptions.importVertexColors) {
  141. //Push the r, g, b, a values of each element in the unwrapped array
  142. this._unwrappedColorsForBabylon.push(this._wrappedColorsForBabylon[l].r, this._wrappedColorsForBabylon[l].g, this._wrappedColorsForBabylon[l].b, this._wrappedColorsForBabylon[l].a);
  143. }
  144. }
  145. // Reset arrays for the next new meshes
  146. this._wrappedPositionForBabylon.length = 0;
  147. this._wrappedNormalsForBabylon.length = 0;
  148. this._wrappedUvsForBabylon.length = 0;
  149. this._wrappedColorsForBabylon.length = 0;
  150. this._tuplePosNorm.length = 0;
  151. this._curPositionInIndices = 0;
  152. }
  153. /**
  154. * Create triangles from polygons
  155. * It is important to notice that a triangle is a polygon
  156. * We get 5 patterns of face defined in OBJ File :
  157. * facePattern1 = ["1","2","3","4","5","6"]
  158. * facePattern2 = ["1/1","2/2","3/3","4/4","5/5","6/6"]
  159. * facePattern3 = ["1/1/1","2/2/2","3/3/3","4/4/4","5/5/5","6/6/6"]
  160. * facePattern4 = ["1//1","2//2","3//3","4//4","5//5","6//6"]
  161. * facePattern5 = ["-1/-1/-1","-2/-2/-2","-3/-3/-3","-4/-4/-4","-5/-5/-5","-6/-6/-6"]
  162. * Each pattern is divided by the same method
  163. * @param faces Array[String] The indices of elements
  164. * @param v Integer The variable to increment
  165. */
  166. _getTriangles(faces, v) {
  167. //Work for each element of the array
  168. for (let faceIndex = v; faceIndex < faces.length - 1; faceIndex++) {
  169. //Add on the triangle variable the indexes to obtain triangles
  170. this._pushTriangle(faces, faceIndex);
  171. }
  172. //Result obtained after 2 iterations:
  173. //Pattern1 => triangle = ["1","2","3","1","3","4"];
  174. //Pattern2 => triangle = ["1/1","2/2","3/3","1/1","3/3","4/4"];
  175. //Pattern3 => triangle = ["1/1/1","2/2/2","3/3/3","1/1/1","3/3/3","4/4/4"];
  176. //Pattern4 => triangle = ["1//1","2//2","3//3","1//1","3//3","4//4"];
  177. //Pattern5 => triangle = ["-1/-1/-1","-2/-2/-2","-3/-3/-3","-1/-1/-1","-3/-3/-3","-4/-4/-4"];
  178. }
  179. /**
  180. * Create triangles and push the data for each polygon for the pattern 1
  181. * In this pattern we get vertice positions
  182. * @param face
  183. * @param v
  184. */
  185. _setDataForCurrentFaceWithPattern1(face, v) {
  186. //Get the indices of triangles for each polygon
  187. this._getTriangles(face, v);
  188. //For each element in the triangles array.
  189. //This var could contains 1 to an infinity of triangles
  190. for (let k = 0; k < this._triangles.length; k++) {
  191. // Set position indice
  192. const indicePositionFromObj = parseInt(this._triangles[k]) - 1;
  193. this._setData(indicePositionFromObj, 0, 0, // In the pattern 1, normals and uvs are not defined
  194. this._positions[indicePositionFromObj], // Get the vectors data
  195. Vector2.Zero(), Vector3.Up(), // Create default vectors
  196. this._loadingOptions.importVertexColors ? this._colors[indicePositionFromObj] : undefined);
  197. }
  198. //Reset variable for the next line
  199. this._triangles.length = 0;
  200. }
  201. /**
  202. * Create triangles and push the data for each polygon for the pattern 2
  203. * In this pattern we get vertice positions and uvs
  204. * @param face
  205. * @param v
  206. */
  207. _setDataForCurrentFaceWithPattern2(face, v) {
  208. //Get the indices of triangles for each polygon
  209. this._getTriangles(face, v);
  210. for (let k = 0; k < this._triangles.length; k++) {
  211. //triangle[k] = "1/1"
  212. //Split the data for getting position and uv
  213. const point = this._triangles[k].split("/"); // ["1", "1"]
  214. //Set position indice
  215. const indicePositionFromObj = parseInt(point[0]) - 1;
  216. //Set uv indice
  217. const indiceUvsFromObj = parseInt(point[1]) - 1;
  218. this._setData(indicePositionFromObj, indiceUvsFromObj, 0, //Default value for normals
  219. this._positions[indicePositionFromObj], //Get the values for each element
  220. this._uvs[indiceUvsFromObj], Vector3.Up(), //Default value for normals
  221. this._loadingOptions.importVertexColors ? this._colors[indicePositionFromObj] : undefined);
  222. }
  223. //Reset variable for the next line
  224. this._triangles.length = 0;
  225. }
  226. /**
  227. * Create triangles and push the data for each polygon for the pattern 3
  228. * In this pattern we get vertice positions, uvs and normals
  229. * @param face
  230. * @param v
  231. */
  232. _setDataForCurrentFaceWithPattern3(face, v) {
  233. //Get the indices of triangles for each polygon
  234. this._getTriangles(face, v);
  235. for (let k = 0; k < this._triangles.length; k++) {
  236. //triangle[k] = "1/1/1"
  237. //Split the data for getting position, uv, and normals
  238. const point = this._triangles[k].split("/"); // ["1", "1", "1"]
  239. // Set position indice
  240. const indicePositionFromObj = parseInt(point[0]) - 1;
  241. // Set uv indice
  242. const indiceUvsFromObj = parseInt(point[1]) - 1;
  243. // Set normal indice
  244. const indiceNormalFromObj = parseInt(point[2]) - 1;
  245. this._setData(indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj, this._positions[indicePositionFromObj], this._uvs[indiceUvsFromObj], this._normals[indiceNormalFromObj] //Set the vector for each component
  246. );
  247. }
  248. //Reset variable for the next line
  249. this._triangles.length = 0;
  250. }
  251. /**
  252. * Create triangles and push the data for each polygon for the pattern 4
  253. * In this pattern we get vertice positions and normals
  254. * @param face
  255. * @param v
  256. */
  257. _setDataForCurrentFaceWithPattern4(face, v) {
  258. this._getTriangles(face, v);
  259. for (let k = 0; k < this._triangles.length; k++) {
  260. //triangle[k] = "1//1"
  261. //Split the data for getting position and normals
  262. const point = this._triangles[k].split("//"); // ["1", "1"]
  263. // We check indices, and normals
  264. const indicePositionFromObj = parseInt(point[0]) - 1;
  265. const indiceNormalFromObj = parseInt(point[1]) - 1;
  266. this._setData(indicePositionFromObj, 1, //Default value for uv
  267. indiceNormalFromObj, this._positions[indicePositionFromObj], //Get each vector of data
  268. Vector2.Zero(), this._normals[indiceNormalFromObj], this._loadingOptions.importVertexColors ? this._colors[indicePositionFromObj] : undefined);
  269. }
  270. //Reset variable for the next line
  271. this._triangles.length = 0;
  272. }
  273. /*
  274. * Create triangles and push the data for each polygon for the pattern 3
  275. * In this pattern we get vertice positions, uvs and normals
  276. * @param face
  277. * @param v
  278. */
  279. _setDataForCurrentFaceWithPattern5(face, v) {
  280. //Get the indices of triangles for each polygon
  281. this._getTriangles(face, v);
  282. for (let k = 0; k < this._triangles.length; k++) {
  283. //triangle[k] = "-1/-1/-1"
  284. //Split the data for getting position, uv, and normals
  285. const point = this._triangles[k].split("/"); // ["-1", "-1", "-1"]
  286. // Set position indice
  287. const indicePositionFromObj = this._positions.length + parseInt(point[0]);
  288. // Set uv indice
  289. const indiceUvsFromObj = this._uvs.length + parseInt(point[1]);
  290. // Set normal indice
  291. const indiceNormalFromObj = this._normals.length + parseInt(point[2]);
  292. this._setData(indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj, this._positions[indicePositionFromObj], this._uvs[indiceUvsFromObj], this._normals[indiceNormalFromObj], //Set the vector for each component
  293. this._loadingOptions.importVertexColors ? this._colors[indicePositionFromObj] : undefined);
  294. }
  295. //Reset variable for the next line
  296. this._triangles.length = 0;
  297. }
  298. _addPreviousObjMesh() {
  299. //Check if it is not the first mesh. Otherwise we don't have data.
  300. if (this._meshesFromObj.length > 0) {
  301. //Get the previous mesh for applying the data about the faces
  302. //=> in obj file, faces definition append after the name of the mesh
  303. this._handledMesh = this._meshesFromObj[this._meshesFromObj.length - 1];
  304. //Set the data into Array for the mesh
  305. this._unwrapData();
  306. if (this._loadingOptions.useLegacyBehavior) {
  307. // Reverse tab. Otherwise face are displayed in the wrong sens
  308. this._indicesForBabylon.reverse();
  309. }
  310. //Set the information for the mesh
  311. //Slice the array to avoid rewriting because of the fact this is the same var which be rewrited
  312. this._handledMesh.indices = this._indicesForBabylon.slice();
  313. this._handledMesh.positions = this._unwrappedPositionsForBabylon.slice();
  314. this._handledMesh.normals = this._unwrappedNormalsForBabylon.slice();
  315. this._handledMesh.uvs = this._unwrappedUVForBabylon.slice();
  316. if (this._loadingOptions.importVertexColors) {
  317. this._handledMesh.colors = this._unwrappedColorsForBabylon.slice();
  318. }
  319. //Reset the array for the next mesh
  320. this._indicesForBabylon.length = 0;
  321. this._unwrappedPositionsForBabylon.length = 0;
  322. this._unwrappedColorsForBabylon.length = 0;
  323. this._unwrappedNormalsForBabylon.length = 0;
  324. this._unwrappedUVForBabylon.length = 0;
  325. }
  326. }
  327. _optimizeNormals(mesh) {
  328. const positions = mesh.getVerticesData(VertexBuffer.PositionKind);
  329. const normals = mesh.getVerticesData(VertexBuffer.NormalKind);
  330. const mapVertices = {};
  331. if (!positions || !normals) {
  332. return;
  333. }
  334. for (let i = 0; i < positions.length / 3; i++) {
  335. const x = positions[i * 3 + 0];
  336. const y = positions[i * 3 + 1];
  337. const z = positions[i * 3 + 2];
  338. const key = x + "_" + y + "_" + z;
  339. let lst = mapVertices[key];
  340. if (!lst) {
  341. lst = [];
  342. mapVertices[key] = lst;
  343. }
  344. lst.push(i);
  345. }
  346. const normal = new Vector3();
  347. for (const key in mapVertices) {
  348. const lst = mapVertices[key];
  349. if (lst.length < 2) {
  350. continue;
  351. }
  352. const v0Idx = lst[0];
  353. for (let i = 1; i < lst.length; ++i) {
  354. const vIdx = lst[i];
  355. normals[v0Idx * 3 + 0] += normals[vIdx * 3 + 0];
  356. normals[v0Idx * 3 + 1] += normals[vIdx * 3 + 1];
  357. normals[v0Idx * 3 + 2] += normals[vIdx * 3 + 2];
  358. }
  359. normal.copyFromFloats(normals[v0Idx * 3 + 0], normals[v0Idx * 3 + 1], normals[v0Idx * 3 + 2]);
  360. normal.normalize();
  361. for (let i = 0; i < lst.length; ++i) {
  362. const vIdx = lst[i];
  363. normals[vIdx * 3 + 0] = normal.x;
  364. normals[vIdx * 3 + 1] = normal.y;
  365. normals[vIdx * 3 + 2] = normal.z;
  366. }
  367. }
  368. mesh.setVerticesData(VertexBuffer.NormalKind, normals);
  369. }
  370. /**
  371. * Function used to parse an OBJ string
  372. * @param meshesNames defines the list of meshes to load (all if not defined)
  373. * @param data defines the OBJ string
  374. * @param scene defines the hosting scene
  375. * @param assetContainer defines the asset container to load data in
  376. * @param onFileToLoadFound defines a callback that will be called if a MTL file is found
  377. */
  378. parse(meshesNames, data, scene, assetContainer, onFileToLoadFound) {
  379. if (this._loadingOptions.useLegacyBehavior) {
  380. this._pushTriangle = (faces, faceIndex) => this._triangles.push(faces[0], faces[faceIndex], faces[faceIndex + 1]);
  381. this._handednessSign = 1;
  382. }
  383. else if (scene.useRightHandedSystem) {
  384. this._pushTriangle = (faces, faceIndex) => this._triangles.push(faces[0], faces[faceIndex + 1], faces[faceIndex]);
  385. this._handednessSign = 1;
  386. }
  387. else {
  388. this._pushTriangle = (faces, faceIndex) => this._triangles.push(faces[0], faces[faceIndex], faces[faceIndex + 1]);
  389. this._handednessSign = -1;
  390. }
  391. // Split the file into lines
  392. const lines = data.split("\n");
  393. // Look at each line
  394. for (let i = 0; i < lines.length; i++) {
  395. const line = lines[i].trim().replace(/\s\s/g, " ");
  396. let result;
  397. // Comment or newLine
  398. if (line.length === 0 || line.charAt(0) === "#") {
  399. continue;
  400. //Get information about one position possible for the vertices
  401. }
  402. else if (SolidParser.VertexPattern.test(line)) {
  403. result = line.match(/[^ ]+/g); // match will return non-null due to passing regex pattern
  404. // Value of result with line: "v 1.0 2.0 3.0"
  405. // ["v", "1.0", "2.0", "3.0"]
  406. // Create a Vector3 with the position x, y, z
  407. this._positions.push(new Vector3(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3])));
  408. if (this._loadingOptions.importVertexColors) {
  409. if (result.length >= 7) {
  410. const r = parseFloat(result[4]);
  411. const g = parseFloat(result[5]);
  412. const b = parseFloat(result[6]);
  413. this._colors.push(new Color4(r > 1 ? r / 255 : r, g > 1 ? g / 255 : g, b > 1 ? b / 255 : b, result.length === 7 || result[7] === undefined ? 1 : parseFloat(result[7])));
  414. }
  415. else {
  416. // TODO: maybe push NULL and if all are NULL to skip (and remove grayColor var).
  417. this._colors.push(this._grayColor);
  418. }
  419. }
  420. }
  421. else if ((result = SolidParser.NormalPattern.exec(line)) !== null) {
  422. //Create a Vector3 with the normals x, y, z
  423. //Value of result
  424. // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
  425. //Add the Vector in the list of normals
  426. this._normals.push(new Vector3(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3])));
  427. }
  428. else if ((result = SolidParser.UVPattern.exec(line)) !== null) {
  429. //Create a Vector2 with the normals u, v
  430. //Value of result
  431. // ["vt 0.1 0.2 0.3", "0.1", "0.2"]
  432. //Add the Vector in the list of uvs
  433. this._uvs.push(new Vector2(parseFloat(result[1]) * this._loadingOptions.UVScaling.x, parseFloat(result[2]) * this._loadingOptions.UVScaling.y));
  434. //Identify patterns of faces
  435. //Face could be defined in different type of pattern
  436. }
  437. else if ((result = SolidParser.FacePattern3.exec(line)) !== null) {
  438. //Value of result:
  439. //["f 1/1/1 2/2/2 3/3/3", "1/1/1 2/2/2 3/3/3"...]
  440. //Set the data for this face
  441. this._setDataForCurrentFaceWithPattern3(result[1].trim().split(" "), // ["1/1/1", "2/2/2", "3/3/3"]
  442. 1);
  443. }
  444. else if ((result = SolidParser.FacePattern4.exec(line)) !== null) {
  445. //Value of result:
  446. //["f 1//1 2//2 3//3", "1//1 2//2 3//3"...]
  447. //Set the data for this face
  448. this._setDataForCurrentFaceWithPattern4(result[1].trim().split(" "), // ["1//1", "2//2", "3//3"]
  449. 1);
  450. }
  451. else if ((result = SolidParser.FacePattern5.exec(line)) !== null) {
  452. //Value of result:
  453. //["f -1/-1/-1 -2/-2/-2 -3/-3/-3", "-1/-1/-1 -2/-2/-2 -3/-3/-3"...]
  454. //Set the data for this face
  455. this._setDataForCurrentFaceWithPattern5(result[1].trim().split(" "), // ["-1/-1/-1", "-2/-2/-2", "-3/-3/-3"]
  456. 1);
  457. }
  458. else if ((result = SolidParser.FacePattern2.exec(line)) !== null) {
  459. //Value of result:
  460. //["f 1/1 2/2 3/3", "1/1 2/2 3/3"...]
  461. //Set the data for this face
  462. this._setDataForCurrentFaceWithPattern2(result[1].trim().split(" "), // ["1/1", "2/2", "3/3"]
  463. 1);
  464. }
  465. else if ((result = SolidParser.FacePattern1.exec(line)) !== null) {
  466. //Value of result
  467. //["f 1 2 3", "1 2 3"...]
  468. //Set the data for this face
  469. this._setDataForCurrentFaceWithPattern1(result[1].trim().split(" "), // ["1", "2", "3"]
  470. 1);
  471. // Define a mesh or an object
  472. // Each time this keyword is analyzed, create a new Object with all data for creating a babylonMesh
  473. }
  474. else if ((result = SolidParser.LinePattern1.exec(line)) !== null) {
  475. //Value of result
  476. //["l 1 2"]
  477. //Set the data for this face
  478. this._setDataForCurrentFaceWithPattern1(result[1].trim().split(" "), // ["1", "2"]
  479. 0);
  480. // Define a mesh or an object
  481. // Each time this keyword is analyzed, create a new Object with all data for creating a babylonMesh
  482. }
  483. else if ((result = SolidParser.LinePattern2.exec(line)) !== null) {
  484. //Value of result
  485. //["l 1/1 2/2"]
  486. //Set the data for this face
  487. this._setDataForCurrentFaceWithPattern2(result[1].trim().split(" "), // ["1/1", "2/2"]
  488. 0);
  489. // Define a mesh or an object
  490. // Each time this keyword is analyzed, create a new Object with all data for creating a babylonMesh
  491. }
  492. else if ((result = SolidParser.LinePattern3.exec(line)) !== null) {
  493. //Value of result
  494. //["l 1/1/1 2/2/2"]
  495. //Set the data for this face
  496. this._setDataForCurrentFaceWithPattern3(result[1].trim().split(" "), // ["1/1/1", "2/2/2"]
  497. 0);
  498. // Define a mesh or an object
  499. // Each time this keyword is analyzed, create a new Object with all data for creating a babylonMesh
  500. }
  501. else if (SolidParser.GroupDescriptor.test(line) || SolidParser.ObjectDescriptor.test(line)) {
  502. // Create a new mesh corresponding to the name of the group.
  503. // Definition of the mesh
  504. const objMesh = {
  505. name: line.substring(2).trim(),
  506. indices: null,
  507. positions: null,
  508. normals: null,
  509. uvs: null,
  510. colors: null,
  511. materialName: this._materialNameFromObj,
  512. isObject: SolidParser.ObjectDescriptor.test(line),
  513. };
  514. this._addPreviousObjMesh();
  515. //Push the last mesh created with only the name
  516. this._meshesFromObj.push(objMesh);
  517. //Set this variable to indicate that now meshesFromObj has objects defined inside
  518. this._hasMeshes = true;
  519. this._isFirstMaterial = true;
  520. this._increment = 1;
  521. //Keyword for applying a material
  522. }
  523. else if (SolidParser.UseMtlDescriptor.test(line)) {
  524. //Get the name of the material
  525. this._materialNameFromObj = line.substring(7).trim();
  526. //If this new material is in the same mesh
  527. if (!this._isFirstMaterial || !this._hasMeshes) {
  528. //Set the data for the previous mesh
  529. this._addPreviousObjMesh();
  530. //Create a new mesh
  531. const objMesh =
  532. //Set the name of the current obj mesh
  533. {
  534. name: (this._objMeshName || "mesh") + "_mm" + this._increment.toString(),
  535. indices: null,
  536. positions: null,
  537. normals: null,
  538. uvs: null,
  539. colors: null,
  540. materialName: this._materialNameFromObj,
  541. isObject: false,
  542. };
  543. this._increment++;
  544. //If meshes are already defined
  545. this._meshesFromObj.push(objMesh);
  546. this._hasMeshes = true;
  547. }
  548. //Set the material name if the previous line define a mesh
  549. if (this._hasMeshes && this._isFirstMaterial) {
  550. //Set the material name to the previous mesh (1 material per mesh)
  551. this._meshesFromObj[this._meshesFromObj.length - 1].materialName = this._materialNameFromObj;
  552. this._isFirstMaterial = false;
  553. }
  554. // Keyword for loading the mtl file
  555. }
  556. else if (SolidParser.MtlLibGroupDescriptor.test(line)) {
  557. // Get the name of mtl file
  558. onFileToLoadFound(line.substring(7).trim());
  559. // Apply smoothing
  560. }
  561. else if (SolidParser.SmoothDescriptor.test(line)) {
  562. // smooth shading => apply smoothing
  563. // Today I don't know it work with babylon and with obj.
  564. // With the obj file an integer is set
  565. }
  566. else {
  567. //If there is another possibility
  568. Logger.Log("Unhandled expression at line : " + line);
  569. }
  570. }
  571. // At the end of the file, add the last mesh into the meshesFromObj array
  572. if (this._hasMeshes) {
  573. // Set the data for the last mesh
  574. this._handledMesh = this._meshesFromObj[this._meshesFromObj.length - 1];
  575. if (this._loadingOptions.useLegacyBehavior) {
  576. //Reverse indices for displaying faces in the good sense
  577. this._indicesForBabylon.reverse();
  578. }
  579. //Get the good array
  580. this._unwrapData();
  581. //Set array
  582. this._handledMesh.indices = this._indicesForBabylon;
  583. this._handledMesh.positions = this._unwrappedPositionsForBabylon;
  584. this._handledMesh.normals = this._unwrappedNormalsForBabylon;
  585. this._handledMesh.uvs = this._unwrappedUVForBabylon;
  586. if (this._loadingOptions.importVertexColors) {
  587. this._handledMesh.colors = this._unwrappedColorsForBabylon;
  588. }
  589. }
  590. // If any o or g keyword not found, create a mesh with a random id
  591. if (!this._hasMeshes) {
  592. let newMaterial = null;
  593. if (this._indicesForBabylon.length) {
  594. if (this._loadingOptions.useLegacyBehavior) {
  595. // reverse tab of indices
  596. this._indicesForBabylon.reverse();
  597. }
  598. //Get positions normals uvs
  599. this._unwrapData();
  600. }
  601. else {
  602. // There is no indices in the file. We will have to switch to point cloud rendering
  603. for (const pos of this._positions) {
  604. this._unwrappedPositionsForBabylon.push(pos.x, pos.y, pos.z);
  605. }
  606. if (this._normals.length) {
  607. for (const normal of this._normals) {
  608. this._unwrappedNormalsForBabylon.push(normal.x, normal.y, normal.z);
  609. }
  610. }
  611. if (this._uvs.length) {
  612. for (const uv of this._uvs) {
  613. this._unwrappedUVForBabylon.push(uv.x, uv.y);
  614. }
  615. }
  616. if (this._colors.length) {
  617. for (const color of this._colors) {
  618. this._unwrappedColorsForBabylon.push(color.r, color.g, color.b, color.a);
  619. }
  620. }
  621. if (!this._materialNameFromObj) {
  622. // Create a material with point cloud on
  623. newMaterial = new StandardMaterial(Geometry.RandomId(), scene);
  624. newMaterial.pointsCloud = true;
  625. this._materialNameFromObj = newMaterial.name;
  626. if (!this._normals.length) {
  627. newMaterial.disableLighting = true;
  628. newMaterial.emissiveColor = Color3.White();
  629. }
  630. }
  631. }
  632. //Set data for one mesh
  633. this._meshesFromObj.push({
  634. name: Geometry.RandomId(),
  635. indices: this._indicesForBabylon,
  636. positions: this._unwrappedPositionsForBabylon,
  637. colors: this._unwrappedColorsForBabylon,
  638. normals: this._unwrappedNormalsForBabylon,
  639. uvs: this._unwrappedUVForBabylon,
  640. materialName: this._materialNameFromObj,
  641. directMaterial: newMaterial,
  642. isObject: true,
  643. });
  644. }
  645. //Set data for each mesh
  646. for (let j = 0; j < this._meshesFromObj.length; j++) {
  647. //check meshesNames (stlFileLoader)
  648. if (meshesNames && this._meshesFromObj[j].name) {
  649. if (meshesNames instanceof Array) {
  650. if (meshesNames.indexOf(this._meshesFromObj[j].name) === -1) {
  651. continue;
  652. }
  653. }
  654. else {
  655. if (this._meshesFromObj[j].name !== meshesNames) {
  656. continue;
  657. }
  658. }
  659. }
  660. //Get the current mesh
  661. //Set the data with VertexBuffer for each mesh
  662. this._handledMesh = this._meshesFromObj[j];
  663. //Create a Mesh with the name of the obj mesh
  664. scene._blockEntityCollection = !!assetContainer;
  665. const babylonMesh = new Mesh(this._meshesFromObj[j].name, scene);
  666. babylonMesh._parentContainer = assetContainer;
  667. scene._blockEntityCollection = false;
  668. this._handledMesh._babylonMesh = babylonMesh;
  669. // If this is a group mesh, it should have an object mesh as a parent. So look for the first object mesh that appears before it.
  670. if (!this._handledMesh.isObject) {
  671. for (let k = j - 1; k >= 0; --k) {
  672. if (this._meshesFromObj[k].isObject && this._meshesFromObj[k]._babylonMesh) {
  673. babylonMesh.parent = this._meshesFromObj[k]._babylonMesh;
  674. break;
  675. }
  676. }
  677. }
  678. //Push the name of the material to an array
  679. //This is indispensable for the importMesh function
  680. this._materialToUse.push(this._meshesFromObj[j].materialName);
  681. if (this._handledMesh.positions?.length === 0) {
  682. //Push the mesh into an array
  683. this._babylonMeshesArray.push(babylonMesh);
  684. continue;
  685. }
  686. const vertexData = new VertexData(); //The container for the values
  687. //Set the data for the babylonMesh
  688. vertexData.uvs = this._handledMesh.uvs;
  689. vertexData.indices = this._handledMesh.indices;
  690. vertexData.positions = this._handledMesh.positions;
  691. if (this._loadingOptions.computeNormals) {
  692. const normals = new Array();
  693. VertexData.ComputeNormals(this._handledMesh.positions, this._handledMesh.indices, normals);
  694. vertexData.normals = normals;
  695. }
  696. else {
  697. vertexData.normals = this._handledMesh.normals;
  698. }
  699. if (this._loadingOptions.importVertexColors) {
  700. vertexData.colors = this._handledMesh.colors;
  701. }
  702. //Set the data from the VertexBuffer to the current Mesh
  703. vertexData.applyToMesh(babylonMesh);
  704. if (this._loadingOptions.invertY) {
  705. babylonMesh.scaling.y *= -1;
  706. }
  707. if (this._loadingOptions.optimizeNormals) {
  708. this._optimizeNormals(babylonMesh);
  709. }
  710. //Push the mesh into an array
  711. this._babylonMeshesArray.push(babylonMesh);
  712. if (this._handledMesh.directMaterial) {
  713. babylonMesh.material = this._handledMesh.directMaterial;
  714. }
  715. }
  716. }
  717. }
  718. // Descriptor
  719. /** Object descriptor */
  720. SolidParser.ObjectDescriptor = /^o/;
  721. /** Group descriptor */
  722. SolidParser.GroupDescriptor = /^g/;
  723. /** Material lib descriptor */
  724. SolidParser.MtlLibGroupDescriptor = /^mtllib /;
  725. /** Use a material descriptor */
  726. SolidParser.UseMtlDescriptor = /^usemtl /;
  727. /** Smooth descriptor */
  728. SolidParser.SmoothDescriptor = /^s /;
  729. // Patterns
  730. /** Pattern used to detect a vertex */
  731. SolidParser.VertexPattern = /^v(\s+[\d|.|+|\-|e|E]+){3,7}/;
  732. /** Pattern used to detect a normal */
  733. SolidParser.NormalPattern = /^vn(\s+[\d|.|+|\-|e|E]+)( +[\d|.|+|\-|e|E]+)( +[\d|.|+|\-|e|E]+)/;
  734. /** Pattern used to detect a UV set */
  735. SolidParser.UVPattern = /^vt(\s+[\d|.|+|\-|e|E]+)( +[\d|.|+|\-|e|E]+)/;
  736. /** Pattern used to detect a first kind of face (f vertex vertex vertex) */
  737. SolidParser.FacePattern1 = /^f\s+(([\d]{1,}[\s]?){3,})+/;
  738. /** Pattern used to detect a second kind of face (f vertex/uvs vertex/uvs vertex/uvs) */
  739. SolidParser.FacePattern2 = /^f\s+((([\d]{1,}\/[\d]{1,}[\s]?){3,})+)/;
  740. /** Pattern used to detect a third kind of face (f vertex/uvs/normal vertex/uvs/normal vertex/uvs/normal) */
  741. SolidParser.FacePattern3 = /^f\s+((([\d]{1,}\/[\d]{1,}\/[\d]{1,}[\s]?){3,})+)/;
  742. /** Pattern used to detect a fourth kind of face (f vertex//normal vertex//normal vertex//normal)*/
  743. SolidParser.FacePattern4 = /^f\s+((([\d]{1,}\/\/[\d]{1,}[\s]?){3,})+)/;
  744. /** Pattern used to detect a fifth kind of face (f -vertex/-uvs/-normal -vertex/-uvs/-normal -vertex/-uvs/-normal) */
  745. SolidParser.FacePattern5 = /^f\s+(((-[\d]{1,}\/-[\d]{1,}\/-[\d]{1,}[\s]?){3,})+)/;
  746. /** Pattern used to detect a line(l vertex vertex) */
  747. SolidParser.LinePattern1 = /^l\s+(([\d]{1,}[\s]?){2,})+/;
  748. /** Pattern used to detect a second kind of line (l vertex/uvs vertex/uvs) */
  749. SolidParser.LinePattern2 = /^l\s+((([\d]{1,}\/[\d]{1,}[\s]?){2,})+)/;
  750. /** Pattern used to detect a third kind of line (l vertex/uvs/normal vertex/uvs/normal) */
  751. SolidParser.LinePattern3 = /^l\s+((([\d]{1,}\/[\d]{1,}\/[\d]{1,}[\s]?){2,})+)/;
  752. //# sourceMappingURL=solidParser.js.map