objSerializer.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import { Nullable } from "babylonjs/types";
  2. import { Matrix } from "babylonjs/Maths/math.vector";
  3. import { Tools } from "babylonjs/Misc/tools";
  4. import { StandardMaterial } from "babylonjs/Materials/standardMaterial";
  5. import { Geometry } from "babylonjs/Meshes/geometry";
  6. import { Mesh } from "babylonjs/Meshes/mesh";
  7. /**
  8. * Class for generating OBJ data from a Babylon scene.
  9. */
  10. export class OBJExport {
  11. /**
  12. * Exports the geometry of a Mesh array in .OBJ file format (text)
  13. * @param mesh defines the list of meshes to serialize
  14. * @param materials defines if materials should be exported
  15. * @param matlibname defines the name of the associated mtl file
  16. * @param globalposition defines if the exported positions are globals or local to the exported mesh
  17. * @returns the OBJ content
  18. */
  19. public static OBJ(mesh: Mesh[], materials?: boolean, matlibname?: string, globalposition?: boolean): string {
  20. const output: string[] = [];
  21. let v = 1;
  22. if (materials) {
  23. if (!matlibname) {
  24. matlibname = 'mat';
  25. }
  26. output.push("mtllib " + matlibname + ".mtl");
  27. }
  28. for (let j = 0; j < mesh.length; j++) {
  29. output.push("g object" + j);
  30. output.push("o object_" + j);
  31. //Uses the position of the item in the scene, to the file (this back to normal in the end)
  32. let lastMatrix: Nullable<Matrix> = null;
  33. if (globalposition) {
  34. var newMatrix = Matrix.Translation(mesh[j].position.x, mesh[j].position.y, mesh[j].position.z);
  35. lastMatrix = Matrix.Translation(-(mesh[j].position.x), -(mesh[j].position.y), -(mesh[j].position.z));
  36. mesh[j].bakeTransformIntoVertices(newMatrix);
  37. }
  38. //TODO: submeshes (groups)
  39. //TODO: smoothing groups (s 1, s off);
  40. if (materials) {
  41. let mat = mesh[j].material;
  42. if (mat) {
  43. output.push("usemtl " + mat.id);
  44. }
  45. }
  46. const g: Nullable<Geometry> = mesh[j].geometry;
  47. if (!g) {
  48. Tools.Warn("No geometry is present on the mesh");
  49. continue;
  50. }
  51. const trunkVerts = g.getVerticesData('position');
  52. const trunkNormals = g.getVerticesData('normal');
  53. const trunkUV = g.getVerticesData('uv');
  54. const trunkFaces = g.getIndices();
  55. var curV = 0;
  56. if (!trunkVerts || !trunkFaces) {
  57. Tools.Warn("There are no position vertices or indices on the mesh!");
  58. continue;
  59. }
  60. for (var i = 0; i < trunkVerts.length; i += 3) {
  61. // Babylon.js default is left handed, while OBJ default is right handed
  62. // Need to invert Z vertices unless Babylon is set to use a right handed system
  63. if (mesh[0].getScene().useRightHandedSystem) {
  64. output.push("v " + trunkVerts[i] + " " + trunkVerts[i + 1] + " " + trunkVerts[i + 2]);
  65. } else {
  66. output.push("v " + trunkVerts[i] + " " + trunkVerts[i + 1] + " " + -trunkVerts[i + 2]);
  67. }
  68. curV++;
  69. }
  70. if (trunkNormals != null) {
  71. for (i = 0; i < trunkNormals.length; i += 3) {
  72. output.push("vn " + trunkNormals[i] + " " + trunkNormals[i + 1] + " " + trunkNormals[i + 2]);
  73. }
  74. }
  75. if (trunkUV != null) {
  76. for (i = 0; i < trunkUV.length; i += 2) {
  77. output.push("vt " + trunkUV[i] + " " + trunkUV[i + 1]);
  78. }
  79. }
  80. for (i = 0; i < trunkFaces.length; i += 3) {
  81. const indices = [String(trunkFaces[i + 2] + v), String(trunkFaces[i + 1] + v), String(trunkFaces[i] + v)];
  82. const blanks: string[] = ["", "", ""];
  83. const facePositions = indices;
  84. const faceUVs = trunkUV != null ? indices : blanks;
  85. const faceNormals = trunkNormals != null ? indices : blanks;
  86. output.push(
  87. "f " + facePositions[0] + "/" + faceUVs[0] + "/" + faceNormals[0] +
  88. " " + facePositions[1] + "/" + faceUVs[1] + "/" + faceNormals[1] +
  89. " " + facePositions[2] + "/" + faceUVs[2] + "/" + faceNormals[2]
  90. );
  91. }
  92. //back de previous matrix, to not change the original mesh in the scene
  93. if (globalposition && lastMatrix) {
  94. mesh[j].bakeTransformIntoVertices(lastMatrix);
  95. }
  96. v += curV;
  97. }
  98. const text: string = output.join("\n");
  99. return (text);
  100. }
  101. /**
  102. * Exports the material(s) of a mesh in .MTL file format (text)
  103. * @param mesh defines the mesh to extract the material from
  104. * @returns the mtl content
  105. */
  106. //TODO: Export the materials of mesh array
  107. public static MTL(mesh: Mesh): string {
  108. var output = [];
  109. var m = <StandardMaterial>mesh.material;
  110. output.push("newmtl mat1");
  111. output.push(" Ns " + m.specularPower.toFixed(4));
  112. output.push(" Ni 1.5000");
  113. output.push(" d " + m.alpha.toFixed(4));
  114. output.push(" Tr 0.0000");
  115. output.push(" Tf 1.0000 1.0000 1.0000");
  116. output.push(" illum 2");
  117. output.push(" Ka " + m.ambientColor.r.toFixed(4) + " " + m.ambientColor.g.toFixed(4) + " " + m.ambientColor.b.toFixed(4));
  118. output.push(" Kd " + m.diffuseColor.r.toFixed(4) + " " + m.diffuseColor.g.toFixed(4) + " " + m.diffuseColor.b.toFixed(4));
  119. output.push(" Ks " + m.specularColor.r.toFixed(4) + " " + m.specularColor.g.toFixed(4) + " " + m.specularColor.b.toFixed(4));
  120. output.push(" Ke " + m.emissiveColor.r.toFixed(4) + " " + m.emissiveColor.g.toFixed(4) + " " + m.emissiveColor.b.toFixed(4));
  121. //TODO: uv scale, offset, wrap
  122. //TODO: UV mirrored in Blender? second UV channel? lightMap? reflection textures?
  123. var uvscale = "";
  124. if (m.ambientTexture) {
  125. output.push(" map_Ka " + uvscale + m.ambientTexture.name);
  126. }
  127. if (m.diffuseTexture) {
  128. output.push(" map_Kd " + uvscale + m.diffuseTexture.name);
  129. //TODO: alpha testing, opacity in diffuse texture alpha channel (diffuseTexture.hasAlpha -> map_d)
  130. }
  131. if (m.specularTexture) {
  132. output.push(" map_Ks " + uvscale + m.specularTexture.name);
  133. /* TODO: glossiness = specular highlight component is in alpha channel of specularTexture. (???)
  134. if (m.useGlossinessFromSpecularMapAlpha) {
  135. output.push(" map_Ns "+uvscale + m.specularTexture.name);
  136. }
  137. */
  138. }
  139. /* TODO: emissive texture not in .MAT format (???)
  140. if (m.emissiveTexture) {
  141. output.push(" map_d "+uvscale+m.emissiveTexture.name);
  142. }
  143. */
  144. if (m.bumpTexture) {
  145. output.push(" map_bump -imfchan z " + uvscale + m.bumpTexture.name);
  146. }
  147. if (m.opacityTexture) {
  148. output.push(" map_d " + uvscale + m.opacityTexture.name);
  149. }
  150. var text = output.join("\n");
  151. return (text);
  152. }
  153. }