polygonBuilder.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. import { Vector2, Vector4 } from "../../Maths/math.vector.js";
  2. import { Color4 } from "../../Maths/math.color.js";
  3. import { Mesh } from "../mesh.js";
  4. import { VertexData } from "../mesh.vertexData.js";
  5. import { PolygonMeshBuilder } from "../polygonMesh.js";
  6. import { VertexBuffer } from "../../Buffers/buffer.js";
  7. import { EngineStore } from "../../Engines/engineStore.js";
  8. import { CompatibilityOptions } from "../../Compat/compatibilityOptions.js";
  9. /**
  10. * Creates the VertexData for an irregular Polygon in the XoZ plane using a mesh built by polygonTriangulation.build()
  11. * All parameters are provided by CreatePolygon as needed
  12. * @param polygon a mesh built from polygonTriangulation.build()
  13. * @param sideOrientation takes the values Mesh.FRONTSIDE (default), Mesh.BACKSIDE or Mesh.DOUBLESIDE
  14. * @param fUV an array of Vector4 elements used to set different images to the top, rings and bottom respectively
  15. * @param fColors an array of Color3 elements used to set different colors to the top, rings and bottom respectively
  16. * @param frontUVs only usable when you create a double-sided mesh, used to choose what parts of the texture image to crop and apply on the front side, optional, default vector4 (0, 0, 1, 1)
  17. * @param backUVs only usable when you create a double-sided mesh, used to choose what parts of the texture image to crop and apply on the back side, optional, default vector4 (0, 0, 1, 1)
  18. * @param wrp a boolean, default false, when true and fUVs used texture is wrapped around all sides, when false texture is applied side
  19. * @returns the VertexData of the Polygon
  20. */
  21. export function CreatePolygonVertexData(polygon, sideOrientation, fUV, fColors, frontUVs, backUVs, wrp) {
  22. const faceUV = fUV || new Array(3);
  23. const faceColors = fColors;
  24. const colors = [];
  25. const wrap = wrp || false;
  26. // default face colors and UV if undefined
  27. for (let f = 0; f < 3; f++) {
  28. if (faceUV[f] === undefined) {
  29. faceUV[f] = new Vector4(0, 0, 1, 1);
  30. }
  31. if (faceColors && faceColors[f] === undefined) {
  32. faceColors[f] = new Color4(1, 1, 1, 1);
  33. }
  34. }
  35. const positions = polygon.getVerticesData(VertexBuffer.PositionKind);
  36. const normals = polygon.getVerticesData(VertexBuffer.NormalKind);
  37. const uvs = polygon.getVerticesData(VertexBuffer.UVKind);
  38. const indices = polygon.getIndices();
  39. const startIndex = positions.length / 9;
  40. let disp = 0;
  41. let distX = 0;
  42. let distZ = 0;
  43. let dist = 0;
  44. let totalLen = 0;
  45. const cumulate = [0];
  46. if (wrap) {
  47. for (let idx = startIndex; idx < positions.length / 3; idx += 4) {
  48. distX = positions[3 * (idx + 2)] - positions[3 * idx];
  49. distZ = positions[3 * (idx + 2) + 2] - positions[3 * idx + 2];
  50. dist = Math.sqrt(distX * distX + distZ * distZ);
  51. totalLen += dist;
  52. cumulate.push(totalLen);
  53. }
  54. }
  55. // set face colours and textures
  56. let idx = 0;
  57. let face = 0;
  58. for (let index = 0; index < normals.length; index += 3) {
  59. //Edge Face no. 1
  60. if (Math.abs(normals[index + 1]) < 0.001) {
  61. face = 1;
  62. }
  63. //Top Face no. 0
  64. if (Math.abs(normals[index + 1] - 1) < 0.001) {
  65. face = 0;
  66. }
  67. //Bottom Face no. 2
  68. if (Math.abs(normals[index + 1] + 1) < 0.001) {
  69. face = 2;
  70. }
  71. idx = index / 3;
  72. if (face === 1) {
  73. disp = idx - startIndex;
  74. if (disp % 4 < 1.5) {
  75. if (wrap) {
  76. uvs[2 * idx] = faceUV[face].x + ((faceUV[face].z - faceUV[face].x) * cumulate[Math.floor(disp / 4)]) / totalLen;
  77. }
  78. else {
  79. uvs[2 * idx] = faceUV[face].x;
  80. }
  81. }
  82. else {
  83. if (wrap) {
  84. uvs[2 * idx] = faceUV[face].x + ((faceUV[face].z - faceUV[face].x) * cumulate[Math.floor(disp / 4) + 1]) / totalLen;
  85. }
  86. else {
  87. uvs[2 * idx] = faceUV[face].z;
  88. }
  89. }
  90. if (disp % 2 === 0) {
  91. uvs[2 * idx + 1] = CompatibilityOptions.UseOpenGLOrientationForUV ? 1.0 - faceUV[face].w : faceUV[face].w;
  92. }
  93. else {
  94. uvs[2 * idx + 1] = CompatibilityOptions.UseOpenGLOrientationForUV ? 1.0 - faceUV[face].y : faceUV[face].y;
  95. }
  96. }
  97. else {
  98. uvs[2 * idx] = (1 - uvs[2 * idx]) * faceUV[face].x + uvs[2 * idx] * faceUV[face].z;
  99. uvs[2 * idx + 1] = (1 - uvs[2 * idx + 1]) * faceUV[face].y + uvs[2 * idx + 1] * faceUV[face].w;
  100. if (CompatibilityOptions.UseOpenGLOrientationForUV) {
  101. uvs[2 * idx + 1] = 1.0 - uvs[2 * idx + 1];
  102. }
  103. }
  104. if (faceColors) {
  105. colors.push(faceColors[face].r, faceColors[face].g, faceColors[face].b, faceColors[face].a);
  106. }
  107. }
  108. // sides
  109. VertexData._ComputeSides(sideOrientation, positions, indices, normals, uvs, frontUVs, backUVs);
  110. // Result
  111. const vertexData = new VertexData();
  112. vertexData.indices = indices;
  113. vertexData.positions = positions;
  114. vertexData.normals = normals;
  115. vertexData.uvs = uvs;
  116. if (faceColors) {
  117. const totalColors = sideOrientation === VertexData.DOUBLESIDE ? colors.concat(colors) : colors;
  118. vertexData.colors = totalColors;
  119. }
  120. return vertexData;
  121. }
  122. /**
  123. * Creates a polygon mesh
  124. * The polygon's shape will depend on the input parameters and is constructed parallel to a ground mesh
  125. * * The parameter `shape` is a required array of successive Vector3 representing the corners of the polygon in th XoZ plane, that is y = 0 for all vectors
  126. * * You can set the mesh side orientation with the values : Mesh.FRONTSIDE (default), Mesh.BACKSIDE or Mesh.DOUBLESIDE
  127. * * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created
  128. * * If you create a double-sided mesh, you can choose what parts of the texture image to crop and stick respectively on the front and the back sides with the parameters `frontUVs` and `backUVs` (Vector4)
  129. * * Remember you can only change the shape positions, not their number when updating a polygon
  130. * @param name defines the name of the mesh
  131. * @param options defines the options used to create the mesh
  132. * @param scene defines the hosting scene
  133. * @param earcutInjection can be used to inject your own earcut reference
  134. * @returns the polygon mesh
  135. */
  136. export function CreatePolygon(name, options, scene = null, earcutInjection = earcut) {
  137. options.sideOrientation = Mesh._GetDefaultSideOrientation(options.sideOrientation);
  138. const shape = options.shape;
  139. const holes = options.holes || [];
  140. const depth = options.depth || 0;
  141. const smoothingThreshold = options.smoothingThreshold || 2;
  142. const contours = [];
  143. let hole = [];
  144. for (let i = 0; i < shape.length; i++) {
  145. contours[i] = new Vector2(shape[i].x, shape[i].z);
  146. }
  147. const epsilon = 0.00000001;
  148. if (contours[0].equalsWithEpsilon(contours[contours.length - 1], epsilon)) {
  149. contours.pop();
  150. }
  151. const polygonTriangulation = new PolygonMeshBuilder(name, contours, scene || EngineStore.LastCreatedScene, earcutInjection);
  152. for (let hNb = 0; hNb < holes.length; hNb++) {
  153. hole = [];
  154. for (let hPoint = 0; hPoint < holes[hNb].length; hPoint++) {
  155. hole.push(new Vector2(holes[hNb][hPoint].x, holes[hNb][hPoint].z));
  156. }
  157. polygonTriangulation.addHole(hole);
  158. }
  159. //updatability is set during applyToMesh; setting to true in triangulation build produces errors
  160. const polygon = polygonTriangulation.build(false, depth, smoothingThreshold);
  161. polygon._originalBuilderSideOrientation = options.sideOrientation;
  162. const vertexData = CreatePolygonVertexData(polygon, options.sideOrientation, options.faceUV, options.faceColors, options.frontUVs, options.backUVs, options.wrap);
  163. vertexData.applyToMesh(polygon, options.updatable);
  164. return polygon;
  165. }
  166. /**
  167. * Creates an extruded polygon mesh, with depth in the Y direction.
  168. * * You can set different colors and different images to the top, bottom and extruded side by using the parameters `faceColors` (an array of 3 Color3 elements) and `faceUV` (an array of 3 Vector4 elements)
  169. * @see https://doc.babylonjs.com/features/featuresDeepDive/materials/using/texturePerBoxFace
  170. * @param name defines the name of the mesh
  171. * @param options defines the options used to create the mesh
  172. * @param scene defines the hosting scene
  173. * @param earcutInjection can be used to inject your own earcut reference
  174. * @returns the polygon mesh
  175. */
  176. export function ExtrudePolygon(name, options, scene = null, earcutInjection = earcut) {
  177. return CreatePolygon(name, options, scene, earcutInjection);
  178. }
  179. /**
  180. * Class containing static functions to help procedurally build meshes
  181. * @deprecated use the functions directly from the module
  182. */
  183. export const PolygonBuilder = {
  184. ExtrudePolygon,
  185. CreatePolygon,
  186. };
  187. VertexData.CreatePolygon = CreatePolygonVertexData;
  188. Mesh.CreatePolygon = (name, shape, scene, holes, updatable, sideOrientation, earcutInjection = earcut) => {
  189. const options = {
  190. shape: shape,
  191. holes: holes,
  192. updatable: updatable,
  193. sideOrientation: sideOrientation,
  194. };
  195. return CreatePolygon(name, options, scene, earcutInjection);
  196. };
  197. Mesh.ExtrudePolygon = (name, shape, depth, scene, holes, updatable, sideOrientation, earcutInjection = earcut) => {
  198. const options = {
  199. shape: shape,
  200. holes: holes,
  201. depth: depth,
  202. updatable: updatable,
  203. sideOrientation: sideOrientation,
  204. };
  205. return ExtrudePolygon(name, options, scene, earcutInjection);
  206. };
  207. //# sourceMappingURL=polygonBuilder.js.map