tiledBoxBuilder.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. import { Matrix, Vector3, 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 { CreateTiledPlaneVertexData } from "./tiledPlaneBuilder.js";
  6. import { CompatibilityOptions } from "../../Compat/compatibilityOptions.js";
  7. /**
  8. * Creates the VertexData for a tiled box
  9. * @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/creation/set/tiled_box
  10. * @param options an object used to set the following optional parameters for the tiled box, required but can be empty
  11. * * pattern sets the rotation or reflection pattern for the tiles,
  12. * * size of the box
  13. * * width of the box, overwrites size
  14. * * height of the box, overwrites size
  15. * * depth of the box, overwrites size
  16. * * tileSize sets the size of a tile
  17. * * tileWidth sets the tile width and overwrites tileSize
  18. * * tileHeight sets the tile width and overwrites tileSize
  19. * * faceUV an array of 6 Vector4 elements used to set different images to each box side
  20. * * faceColors an array of 6 Color3 elements used to set different colors to each box side
  21. * * alignHorizontal places whole tiles aligned to the center, left or right of a row
  22. * * alignVertical places whole tiles aligned to the center, left or right of a column
  23. * @param options.pattern
  24. * @param options.size
  25. * @param options.width
  26. * @param options.height
  27. * @param options.depth
  28. * @param options.tileSize
  29. * @param options.tileWidth
  30. * @param options.tileHeight
  31. * @param options.faceUV
  32. * @param options.faceColors
  33. * @param options.alignHorizontal
  34. * @param options.alignVertical
  35. * @param options.sideOrientation
  36. * * sideOrientation optional and takes the values : Mesh.FRONTSIDE (default), Mesh.BACKSIDE or Mesh.DOUBLESIDE
  37. * @returns the VertexData of the TiledBox
  38. */
  39. export function CreateTiledBoxVertexData(options) {
  40. const nbFaces = 6;
  41. const faceUV = options.faceUV || new Array(6);
  42. const faceColors = options.faceColors;
  43. const flipTile = options.pattern || Mesh.NO_FLIP;
  44. const width = options.width || options.size || 1;
  45. const height = options.height || options.size || 1;
  46. const depth = options.depth || options.size || 1;
  47. const tileWidth = options.tileWidth || options.tileSize || 1;
  48. const tileHeight = options.tileHeight || options.tileSize || 1;
  49. const alignH = options.alignHorizontal || 0;
  50. const alignV = options.alignVertical || 0;
  51. const sideOrientation = options.sideOrientation === 0 ? 0 : options.sideOrientation || VertexData.DEFAULTSIDE;
  52. // default face colors and UV if undefined
  53. for (let f = 0; f < nbFaces; f++) {
  54. if (faceUV[f] === undefined) {
  55. faceUV[f] = new Vector4(0, 0, 1, 1);
  56. }
  57. if (faceColors && faceColors[f] === undefined) {
  58. faceColors[f] = new Color4(1, 1, 1, 1);
  59. }
  60. }
  61. const halfWidth = width / 2;
  62. const halfHeight = height / 2;
  63. const halfDepth = depth / 2;
  64. const faceVertexData = [];
  65. for (let f = 0; f < 2; f++) {
  66. //front and back
  67. faceVertexData[f] = CreateTiledPlaneVertexData({
  68. pattern: flipTile,
  69. tileWidth: tileWidth,
  70. tileHeight: tileHeight,
  71. width: width,
  72. height: height,
  73. alignVertical: alignV,
  74. alignHorizontal: alignH,
  75. sideOrientation: sideOrientation,
  76. });
  77. }
  78. for (let f = 2; f < 4; f++) {
  79. //sides
  80. faceVertexData[f] = CreateTiledPlaneVertexData({
  81. pattern: flipTile,
  82. tileWidth: tileWidth,
  83. tileHeight: tileHeight,
  84. width: depth,
  85. height: height,
  86. alignVertical: alignV,
  87. alignHorizontal: alignH,
  88. sideOrientation: sideOrientation,
  89. });
  90. }
  91. let baseAlignV = alignV;
  92. if (alignV === Mesh.BOTTOM) {
  93. baseAlignV = Mesh.TOP;
  94. }
  95. else if (alignV === Mesh.TOP) {
  96. baseAlignV = Mesh.BOTTOM;
  97. }
  98. for (let f = 4; f < 6; f++) {
  99. //top and bottom
  100. faceVertexData[f] = CreateTiledPlaneVertexData({
  101. pattern: flipTile,
  102. tileWidth: tileWidth,
  103. tileHeight: tileHeight,
  104. width: width,
  105. height: depth,
  106. alignVertical: baseAlignV,
  107. alignHorizontal: alignH,
  108. sideOrientation: sideOrientation,
  109. });
  110. }
  111. let positions = [];
  112. let normals = [];
  113. let uvs = [];
  114. let indices = [];
  115. const colors = [];
  116. const facePositions = [];
  117. const faceNormals = [];
  118. const newFaceUV = [];
  119. let lu = 0;
  120. let li = 0;
  121. for (let f = 0; f < nbFaces; f++) {
  122. const len = faceVertexData[f].positions.length;
  123. facePositions[f] = [];
  124. faceNormals[f] = [];
  125. for (let p = 0; p < len / 3; p++) {
  126. facePositions[f].push(new Vector3(faceVertexData[f].positions[3 * p], faceVertexData[f].positions[3 * p + 1], faceVertexData[f].positions[3 * p + 2]));
  127. faceNormals[f].push(new Vector3(faceVertexData[f].normals[3 * p], faceVertexData[f].normals[3 * p + 1], faceVertexData[f].normals[3 * p + 2]));
  128. }
  129. // uvs
  130. lu = faceVertexData[f].uvs.length;
  131. newFaceUV[f] = [];
  132. for (let i = 0; i < lu; i += 2) {
  133. newFaceUV[f][i] = faceUV[f].x + (faceUV[f].z - faceUV[f].x) * faceVertexData[f].uvs[i];
  134. newFaceUV[f][i + 1] = faceUV[f].y + (faceUV[f].w - faceUV[f].y) * faceVertexData[f].uvs[i + 1];
  135. if (CompatibilityOptions.UseOpenGLOrientationForUV) {
  136. newFaceUV[f][i + 1] = 1.0 - newFaceUV[f][i + 1];
  137. }
  138. }
  139. uvs = uvs.concat(newFaceUV[f]);
  140. indices = indices.concat(faceVertexData[f].indices.map((x) => x + li));
  141. li += facePositions[f].length;
  142. if (faceColors) {
  143. for (let c = 0; c < 4; c++) {
  144. colors.push(faceColors[f].r, faceColors[f].g, faceColors[f].b, faceColors[f].a);
  145. }
  146. }
  147. }
  148. const vec0 = new Vector3(0, 0, halfDepth);
  149. const mtrx0 = Matrix.RotationY(Math.PI);
  150. positions = facePositions[0]
  151. .map((entry) => Vector3.TransformNormal(entry, mtrx0).add(vec0))
  152. .map((entry) => [entry.x, entry.y, entry.z])
  153. .reduce((accumulator, currentValue) => accumulator.concat(currentValue), []);
  154. normals = faceNormals[0]
  155. .map((entry) => Vector3.TransformNormal(entry, mtrx0))
  156. .map((entry) => [entry.x, entry.y, entry.z])
  157. .reduce((accumulator, currentValue) => accumulator.concat(currentValue), []);
  158. positions = positions.concat(facePositions[1]
  159. .map((entry) => entry.subtract(vec0))
  160. .map((entry) => [entry.x, entry.y, entry.z])
  161. .reduce((accumulator, currentValue) => accumulator.concat(currentValue), []));
  162. normals = normals.concat(faceNormals[1].map((entry) => [entry.x, entry.y, entry.z]).reduce((accumulator, currentValue) => accumulator.concat(currentValue), []));
  163. const vec2 = new Vector3(halfWidth, 0, 0);
  164. const mtrx2 = Matrix.RotationY(-Math.PI / 2);
  165. positions = positions.concat(facePositions[2]
  166. .map((entry) => Vector3.TransformNormal(entry, mtrx2).add(vec2))
  167. .map((entry) => [entry.x, entry.y, entry.z])
  168. .reduce((accumulator, currentValue) => accumulator.concat(currentValue), []));
  169. normals = normals.concat(faceNormals[2]
  170. .map((entry) => Vector3.TransformNormal(entry, mtrx2))
  171. .map((entry) => [entry.x, entry.y, entry.z])
  172. .reduce((accumulator, currentValue) => accumulator.concat(currentValue), []));
  173. const mtrx3 = Matrix.RotationY(Math.PI / 2);
  174. positions = positions.concat(facePositions[3]
  175. .map((entry) => Vector3.TransformNormal(entry, mtrx3).subtract(vec2))
  176. .map((entry) => [entry.x, entry.y, entry.z])
  177. .reduce((accumulator, currentValue) => accumulator.concat(currentValue), []));
  178. normals = normals.concat(faceNormals[3]
  179. .map((entry) => Vector3.TransformNormal(entry, mtrx3))
  180. .map((entry) => [entry.x, entry.y, entry.z])
  181. .reduce((accumulator, currentValue) => accumulator.concat(currentValue), []));
  182. const vec4 = new Vector3(0, halfHeight, 0);
  183. const mtrx4 = Matrix.RotationX(Math.PI / 2);
  184. positions = positions.concat(facePositions[4]
  185. .map((entry) => Vector3.TransformNormal(entry, mtrx4).add(vec4))
  186. .map((entry) => [entry.x, entry.y, entry.z])
  187. .reduce((accumulator, currentValue) => accumulator.concat(currentValue), []));
  188. normals = normals.concat(faceNormals[4]
  189. .map((entry) => Vector3.TransformNormal(entry, mtrx4))
  190. .map((entry) => [entry.x, entry.y, entry.z])
  191. .reduce((accumulator, currentValue) => accumulator.concat(currentValue), []));
  192. const mtrx5 = Matrix.RotationX(-Math.PI / 2);
  193. positions = positions.concat(facePositions[5]
  194. .map((entry) => Vector3.TransformNormal(entry, mtrx5).subtract(vec4))
  195. .map((entry) => [entry.x, entry.y, entry.z])
  196. .reduce((accumulator, currentValue) => accumulator.concat(currentValue), []));
  197. normals = normals.concat(faceNormals[5]
  198. .map((entry) => Vector3.TransformNormal(entry, mtrx5))
  199. .map((entry) => [entry.x, entry.y, entry.z])
  200. .reduce((accumulator, currentValue) => accumulator.concat(currentValue), []));
  201. // sides
  202. VertexData._ComputeSides(sideOrientation, positions, indices, normals, uvs);
  203. // Result
  204. const vertexData = new VertexData();
  205. vertexData.indices = indices;
  206. vertexData.positions = positions;
  207. vertexData.normals = normals;
  208. vertexData.uvs = uvs;
  209. if (faceColors) {
  210. const totalColors = sideOrientation === VertexData.DOUBLESIDE ? colors.concat(colors) : colors;
  211. vertexData.colors = totalColors;
  212. }
  213. return vertexData;
  214. }
  215. /**
  216. * Creates a tiled box mesh
  217. * @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/creation/set/tiled_box
  218. * @param name defines the name of the mesh
  219. * @param options an object used to set the following optional parameters for the tiled box, required but can be empty
  220. * * pattern sets the rotation or reflection pattern for the tiles,
  221. * * size of the box
  222. * * width of the box, overwrites size
  223. * * height of the box, overwrites size
  224. * * depth of the box, overwrites size
  225. * * tileSize sets the size of a tile
  226. * * tileWidth sets the tile width and overwrites tileSize
  227. * * tileHeight sets the tile width and overwrites tileSize
  228. * * faceUV an array of 6 Vector4 elements used to set different images to each box side
  229. * * faceColors an array of 6 Color3 elements used to set different colors to each box side
  230. * * alignHorizontal places whole tiles aligned to the center, left or right of a row
  231. * * alignVertical places whole tiles aligned to the center, left or right of a column
  232. * * sideOrientation optional and takes the values : Mesh.FRONTSIDE (default), Mesh.BACKSIDE or Mesh.DOUBLESIDE
  233. * @param options.pattern
  234. * @param options.width
  235. * @param options.height
  236. * @param options.depth
  237. * @param options.tileSize
  238. * @param options.tileWidth
  239. * @param options.tileHeight
  240. * @param options.alignHorizontal
  241. * @param options.alignVertical
  242. * @param options.faceUV
  243. * @param options.faceColors
  244. * @param options.sideOrientation
  245. * @param options.updatable
  246. * @param scene defines the hosting scene
  247. * @returns the box mesh
  248. */
  249. export function CreateTiledBox(name, options, scene = null) {
  250. const box = new Mesh(name, scene);
  251. options.sideOrientation = Mesh._GetDefaultSideOrientation(options.sideOrientation);
  252. box._originalBuilderSideOrientation = options.sideOrientation;
  253. const vertexData = CreateTiledBoxVertexData(options);
  254. vertexData.applyToMesh(box, options.updatable);
  255. return box;
  256. }
  257. /**
  258. * Class containing static functions to help procedurally build meshes
  259. * @deprecated use CreateTiledBox instead
  260. */
  261. export const TiledBoxBuilder = {
  262. // eslint-disable-next-line @typescript-eslint/naming-convention
  263. CreateTiledBox,
  264. };
  265. VertexData.CreateTiledBox = CreateTiledBoxVertexData;
  266. //# sourceMappingURL=tiledBoxBuilder.js.map