tubeBuilder.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import { Vector3, TmpVectors, Matrix } from "../../Maths/math.vector.js";
  2. import { Mesh } from "../mesh.js";
  3. import { CreateRibbon } from "./ribbonBuilder.js";
  4. import { Path3D } from "../../Maths/math.path.js";
  5. /**
  6. * Creates a tube mesh.
  7. * The tube is a parametric shape. It has no predefined shape. Its final shape will depend on the input parameters
  8. * * The parameter `path` is a required array of successive Vector3. It is the curve used as the axis of the tube
  9. * * The parameter `radius` (positive float, default 1) sets the tube radius size
  10. * * The parameter `tessellation` (positive float, default 64) is the number of sides on the tubular surface
  11. * * The parameter `radiusFunction` (javascript function, default null) is a vanilla javascript function. If it is not null, it overrides the parameter `radius`
  12. * * This function is called on each point of the tube path and is passed the index `i` of the i-th point and the distance of this point from the first point of the path. It must return a radius value (positive float)
  13. * * The parameter `arc` (positive float, maximum 1, default 1) is the ratio to apply to the tube circumference : 2 x PI x arc
  14. * * The parameter `cap` sets the way the extruded shape is capped. Possible values : BABYLON.Mesh.NO_CAP (default), BABYLON.Mesh.CAP_START, BABYLON.Mesh.CAP_END, BABYLON.Mesh.CAP_ALL
  15. * * The optional parameter `instance` is an instance of an existing Tube object to be updated with the passed `pathArray` parameter. The `path`Array HAS to have the SAME number of points as the previous one: https://doc.babylonjs.com/features/featuresDeepDive/mesh/dynamicMeshMorph#tube
  16. * * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
  17. * * 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). Detail here : https://doc.babylonjs.com/features/featuresDeepDive/mesh/creation/set#side-orientation
  18. * * The optional parameter `invertUV` (boolean, default false) swaps in the geometry the U and V coordinates to apply a texture
  19. * * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created. The NUMBER of points CAN'T CHANGE, only their positions.
  20. * @param name defines the name of the mesh
  21. * @param options defines the options used to create the mesh
  22. * @param options.path
  23. * @param options.radius
  24. * @param options.tessellation
  25. * @param options.radiusFunction
  26. * @param options.cap
  27. * @param options.arc
  28. * @param options.updatable
  29. * @param options.sideOrientation
  30. * @param options.frontUVs
  31. * @param options.backUVs
  32. * @param options.instance
  33. * @param options.invertUV
  34. * @param scene defines the hosting scene
  35. * @returns the tube mesh
  36. * @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/creation/param
  37. * @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/creation/set#tube
  38. */
  39. export function CreateTube(name, options, scene = null) {
  40. const path = options.path;
  41. let instance = options.instance;
  42. let radius = 1.0;
  43. if (options.radius !== undefined) {
  44. radius = options.radius;
  45. }
  46. else if (instance) {
  47. radius = instance._creationDataStorage.radius;
  48. }
  49. const tessellation = options.tessellation || 64 | 0;
  50. const radiusFunction = options.radiusFunction || null;
  51. let cap = options.cap || Mesh.NO_CAP;
  52. const invertUV = options.invertUV || false;
  53. const updatable = options.updatable;
  54. const sideOrientation = Mesh._GetDefaultSideOrientation(options.sideOrientation);
  55. options.arc = options.arc && (options.arc <= 0.0 || options.arc > 1.0) ? 1.0 : options.arc || 1.0;
  56. // tube geometry
  57. const tubePathArray = (path, path3D, circlePaths, radius, tessellation, radiusFunction, cap, arc) => {
  58. const tangents = path3D.getTangents();
  59. const normals = path3D.getNormals();
  60. const distances = path3D.getDistances();
  61. const pi2 = Math.PI * 2;
  62. const step = (pi2 / tessellation) * arc;
  63. const returnRadius = () => radius;
  64. const radiusFunctionFinal = radiusFunction || returnRadius;
  65. let circlePath;
  66. let rad;
  67. let normal;
  68. let rotated;
  69. const rotationMatrix = TmpVectors.Matrix[0];
  70. let index = cap === Mesh.NO_CAP || cap === Mesh.CAP_END ? 0 : 2;
  71. for (let i = 0; i < path.length; i++) {
  72. rad = radiusFunctionFinal(i, distances[i]); // current radius
  73. circlePath = Array(); // current circle array
  74. normal = normals[i]; // current normal
  75. for (let t = 0; t < tessellation; t++) {
  76. Matrix.RotationAxisToRef(tangents[i], step * t, rotationMatrix);
  77. rotated = circlePath[t] ? circlePath[t] : Vector3.Zero();
  78. Vector3.TransformCoordinatesToRef(normal, rotationMatrix, rotated);
  79. rotated.scaleInPlace(rad).addInPlace(path[i]);
  80. circlePath[t] = rotated;
  81. }
  82. circlePaths[index] = circlePath;
  83. index++;
  84. }
  85. // cap
  86. const capPath = (nbPoints, pathIndex) => {
  87. const pointCap = Array();
  88. for (let i = 0; i < nbPoints; i++) {
  89. pointCap.push(path[pathIndex]);
  90. }
  91. return pointCap;
  92. };
  93. switch (cap) {
  94. case Mesh.NO_CAP:
  95. break;
  96. case Mesh.CAP_START:
  97. circlePaths[0] = capPath(tessellation, 0);
  98. circlePaths[1] = circlePaths[2].slice(0);
  99. break;
  100. case Mesh.CAP_END:
  101. circlePaths[index] = circlePaths[index - 1].slice(0);
  102. circlePaths[index + 1] = capPath(tessellation, path.length - 1);
  103. break;
  104. case Mesh.CAP_ALL:
  105. circlePaths[0] = capPath(tessellation, 0);
  106. circlePaths[1] = circlePaths[2].slice(0);
  107. circlePaths[index] = circlePaths[index - 1].slice(0);
  108. circlePaths[index + 1] = capPath(tessellation, path.length - 1);
  109. break;
  110. default:
  111. break;
  112. }
  113. return circlePaths;
  114. };
  115. let path3D;
  116. let pathArray;
  117. if (instance) {
  118. // tube update
  119. const storage = instance._creationDataStorage;
  120. const arc = options.arc || storage.arc;
  121. path3D = storage.path3D.update(path);
  122. pathArray = tubePathArray(path, path3D, storage.pathArray, radius, storage.tessellation, radiusFunction, storage.cap, arc);
  123. instance = CreateRibbon("", { pathArray: pathArray, instance: instance });
  124. // Update mode, no need to recreate the storage.
  125. storage.path3D = path3D;
  126. storage.pathArray = pathArray;
  127. storage.arc = arc;
  128. storage.radius = radius;
  129. return instance;
  130. }
  131. // tube creation
  132. path3D = new Path3D(path);
  133. const newPathArray = new Array();
  134. cap = cap < 0 || cap > 3 ? 0 : cap;
  135. pathArray = tubePathArray(path, path3D, newPathArray, radius, tessellation, radiusFunction, cap, options.arc);
  136. const tube = CreateRibbon(name, {
  137. pathArray: pathArray,
  138. closePath: true,
  139. closeArray: false,
  140. updatable: updatable,
  141. sideOrientation: sideOrientation,
  142. invertUV: invertUV,
  143. frontUVs: options.frontUVs,
  144. backUVs: options.backUVs,
  145. }, scene);
  146. tube._creationDataStorage.pathArray = pathArray;
  147. tube._creationDataStorage.path3D = path3D;
  148. tube._creationDataStorage.tessellation = tessellation;
  149. tube._creationDataStorage.cap = cap;
  150. tube._creationDataStorage.arc = options.arc;
  151. tube._creationDataStorage.radius = radius;
  152. return tube;
  153. }
  154. /**
  155. * Class containing static functions to help procedurally build meshes
  156. * @deprecated use CreateTube directly
  157. */
  158. export const TubeBuilder = {
  159. // eslint-disable-next-line @typescript-eslint/naming-convention
  160. CreateTube,
  161. };
  162. Mesh.CreateTube = (name, path, radius, tessellation, radiusFunction, cap, scene, updatable, sideOrientation, instance) => {
  163. const options = {
  164. path: path,
  165. radius: radius,
  166. tessellation: tessellation,
  167. radiusFunction: radiusFunction,
  168. arc: 1,
  169. cap: cap,
  170. updatable: updatable,
  171. sideOrientation: sideOrientation,
  172. instance: instance,
  173. };
  174. return CreateTube(name, options, scene);
  175. };
  176. //# sourceMappingURL=tubeBuilder.js.map