babylon.meshBuilder.ts 64 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022
  1. module BABYLON {
  2. export class MeshBuilder {
  3. /**
  4. * Creates a box mesh.
  5. * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#box
  6. * The parameter "size" sets the size (float) of each box side (default 1).
  7. * You can set some different box dimensions by using the parameters "width", "height" and "depth" (all by default have the same value than "size").
  8. * You can set different colors and different images to each box side by using the parameters "faceColors" (an array of 6 Color3 elements) and "faceUV" (an array of 6 Vector4 elements).
  9. * Please read this tutorial : http://doc.babylonjs.com/tutorials/CreateBox_Per_Face_Textures_And_Colors
  10. * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
  11. * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
  12. * The mesh can be set to updatable with the boolean parameter "updatable" (default false) if its internal geometry is supposed to change once created.
  13. */
  14. public static CreateBox(name: string, options: { size?: number, width?: number, height?: number, depth?: number, faceUV?: Vector4[], faceColors?: Color4[], sideOrientation?: number, updatable?: boolean }, scene: Scene): Mesh {
  15. var box = new Mesh(name, scene);
  16. var vertexData = VertexData.CreateBox(options);
  17. vertexData.applyToMesh(box, options.updatable);
  18. return box;
  19. }
  20. /**
  21. * Creates a sphere mesh.
  22. * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#sphere
  23. * The parameter "diameter" sets the diameter size (float) of the sphere (default 1).
  24. * You can set some different sphere dimensions, for instance to build an ellipsoid, by using the parameters "diameterX", "diameterY" and "diameterZ" (all by default have the same value than "diameter").
  25. * The parameter "segments" sets the sphere number of horizontal stripes (positive integer, default 32).
  26. * You can create an unclosed sphere with the parameter "arc" (positive float, default 1), valued between 0 and 1, what is the ratio of the circumference (latitude) : 2 x PI x ratio
  27. * You can create an unclosed sphere on its height with the parameter "slice" (positive float, default1), valued between 0 and 1, what is the height ratio (longitude).
  28. * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
  29. * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
  30. * The mesh can be set to updatable with the boolean parameter "updatable" (default false) if its internal geometry is supposed to change once created.
  31. */
  32. public static CreateSphere(name: string, options: { segments?: number, diameter?: number, diameterX?: number, diameterY?: number, diameterZ?: number, arc?: number, slice?: number, sideOrientation?: number, updatable?: boolean }, scene: any): Mesh {
  33. var sphere = new Mesh(name, scene);
  34. var vertexData = VertexData.CreateSphere(options);
  35. vertexData.applyToMesh(sphere, options.updatable);
  36. return sphere;
  37. }
  38. /**
  39. * Creates a plane polygonal mesh. By default, this is a disc.
  40. * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#disc
  41. * The parameter "radius" sets the radius size (float) of the polygon (default 0.5).
  42. * The parameter "tessellation" sets the number of polygon sides (positive integer, default 64). So a tessellation valued to 3 will build a triangle, to 4 a square, etc.
  43. * You can create an unclosed polygon with the parameter "arc" (positive float, default 1), valued between 0 and 1, what is the ratio of the circumference : 2 x PI x ratio
  44. * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
  45. * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
  46. * The mesh can be set to updatable with the boolean parameter "updatable" (default false) if its internal geometry is supposed to change once created.
  47. */
  48. public static CreateDisc(name: string, options: { radius?: number, tessellation?: number, arc?: number, updatable?: boolean, sideOrientation?: number }, scene: Scene): Mesh {
  49. var disc = new Mesh(name, scene);
  50. var vertexData = VertexData.CreateDisc(options);
  51. vertexData.applyToMesh(disc, options.updatable);
  52. return disc;
  53. }
  54. /**
  55. * Creates a sphere based upon an icosahedron with 20 triangular faces which can be subdivided.
  56. * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#icosphere
  57. * The parameter "radius" sets the radius size (float) of the icosphere (default 1).
  58. * You can set some different icosphere dimensions, for instance to build an ellipsoid, by using the parameters "radiusX", "radiusY" and "radiusZ" (all by default have the same value than "radius").
  59. * The parameter "subdivisions" sets the number of subdivisions (postive integer, default 4). The more subdivisions, the more faces on the icosphere whatever its size.
  60. * The parameter "flat" (boolean, default true) gives each side its own normals. Set it to false to get a smooth continuous light reflection on the surface.
  61. * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
  62. * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
  63. * The mesh can be set to updatable with the boolean parameter "updatable" (default false) if its internal geometry is supposed to change once created.
  64. */
  65. public static CreateIcoSphere(name: string, options: { radius?: number, radiusX?: number, radiusY?: number, radiusZ?: number, flat?: boolean, subdivisions?: number, sideOrientation?: number, updatable?: boolean }, scene: Scene): Mesh {
  66. var sphere = new Mesh(name, scene);
  67. var vertexData = VertexData.CreateIcoSphere(options);
  68. vertexData.applyToMesh(sphere, options.updatable);
  69. return sphere;
  70. };
  71. /**
  72. * Creates a ribbon mesh.
  73. * The ribbon is a parametric shape : http://doc.babylonjs.com/tutorials/Parametric_Shapes. It has no predefined shape. Its final shape will depend on the input parameters.
  74. *
  75. * Please read this full tutorial to understand how to design a ribbon : http://doc.babylonjs.com/tutorials/Ribbon_Tutorial
  76. * The parameter "pathArray" is a required array of paths, what are each an array of successive Vector3. The pathArray parameter depicts the ribbon geometry.
  77. * The parameter "closeArray" (boolean, default false) creates a seam between the first and the last paths of the path array.
  78. * The parameter "closePath" (boolean, default false) creates a seam between the first and the last points of each path of the path array.
  79. * The optional parameter "instance" is an instance of an existing Ribbon object to be updated with the passed "pathArray" parameter : http://doc.babylonjs.com/tutorials/How_to_dynamically_morph_a_mesh#ribbon
  80. * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
  81. * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
  82. * The mesh can be set to updatable with the boolean parameter "updatable" (default false) if its internal geometry is supposed to change once created.
  83. */
  84. public static CreateRibbon(name: string, options: { pathArray: Vector3[][], closeArray?: boolean, closePath?: boolean, offset?: number, updatable?: boolean, sideOrientation?: number, instance?: Mesh }, scene?: Scene): Mesh {
  85. var pathArray = options.pathArray;
  86. var closeArray = options.closeArray;
  87. var closePath = options.closePath;
  88. var offset = options.offset;
  89. var sideOrientation = options.sideOrientation;
  90. var instance = options.instance;
  91. var updatable = options.updatable;
  92. if (instance) { // existing ribbon instance update
  93. // positionFunction : ribbon case
  94. // only pathArray and sideOrientation parameters are taken into account for positions update
  95. var positionFunction = positions => {
  96. var minlg = pathArray[0].length;
  97. var i = 0;
  98. var ns = (instance.sideOrientation === Mesh.DOUBLESIDE) ? 2 : 1;
  99. for (var si = 1; si <= ns; si++) {
  100. for (var p = 0; p < pathArray.length; p++) {
  101. var path = pathArray[p];
  102. var l = path.length;
  103. minlg = (minlg < l) ? minlg : l;
  104. var j = 0;
  105. while (j < minlg) {
  106. positions[i] = path[j].x;
  107. positions[i + 1] = path[j].y;
  108. positions[i + 2] = path[j].z;
  109. j++;
  110. i += 3;
  111. }
  112. if ((<any>instance)._closePath) {
  113. positions[i] = path[0].x;
  114. positions[i + 1] = path[0].y;
  115. positions[i + 2] = path[0].z;
  116. i += 3;
  117. }
  118. }
  119. }
  120. };
  121. var positions = instance.getVerticesData(VertexBuffer.PositionKind);
  122. positionFunction(positions);
  123. instance.updateVerticesData(VertexBuffer.PositionKind, positions, false, false);
  124. if (!(instance.areNormalsFrozen)) {
  125. var indices = instance.getIndices();
  126. var normals = instance.getVerticesData(VertexBuffer.NormalKind);
  127. VertexData.ComputeNormals(positions, indices, normals);
  128. if ((<any>instance)._closePath) {
  129. var indexFirst: number = 0;
  130. var indexLast: number = 0;
  131. for (var p = 0; p < pathArray.length; p++) {
  132. indexFirst = (<any>instance)._idx[p] * 3;
  133. if (p + 1 < pathArray.length) {
  134. indexLast = ((<any>instance)._idx[p + 1] - 1) * 3;
  135. }
  136. else {
  137. indexLast = normals.length - 3;
  138. }
  139. normals[indexFirst] = (normals[indexFirst] + normals[indexLast]) * 0.5;
  140. normals[indexFirst + 1] = (normals[indexFirst + 1] + normals[indexLast + 1]) * 0.5;
  141. normals[indexFirst + 2] = (normals[indexFirst + 2] + normals[indexLast + 2]) * 0.5;
  142. normals[indexLast] = normals[indexFirst];
  143. normals[indexLast + 1] = normals[indexFirst + 1];
  144. normals[indexLast + 2] = normals[indexFirst + 2];
  145. }
  146. }
  147. instance.updateVerticesData(VertexBuffer.NormalKind, normals, false, false);
  148. }
  149. return instance;
  150. }
  151. else { // new ribbon creation
  152. var ribbon = new Mesh(name, scene);
  153. ribbon.sideOrientation = sideOrientation;
  154. var vertexData = VertexData.CreateRibbon(options);
  155. if (closePath) {
  156. (<any>ribbon)._idx = (<any>vertexData)._idx;
  157. }
  158. (<any>ribbon)._closePath = closePath;
  159. (<any>ribbon)._closeArray = closeArray;
  160. vertexData.applyToMesh(ribbon, updatable);
  161. return ribbon;
  162. }
  163. }
  164. /**
  165. * Creates a cylinder or a cone mesh.
  166. * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#cylinder-or-cone
  167. * The parameter "height" sets the height size (float) of the cylinder/cone (float, default 2).
  168. * The parameter "diameter" sets the diameter of the top and bottom cap at once (float, default 1).
  169. * The parameters "diameterTop" and "diameterBottom" overwrite the parameter "diameter" and set respectively the top cap and bottom cap diameter (floats, default 1). The parameter "diameterBottom" can't be zero.
  170. * The parameter "tessellation" sets the number of cylinder sides (positive integer, default 24). Set it to 3 to get a prism for instance.
  171. * The parameter "subdivisions" sets the number of rings along the cylinder height (positive integer, default 1).
  172. * The parameter "hasRings" (boolean, default false) makes the subdivisions independent from each other, so they become different faces.
  173. * The parameter "enclose" (boolean, default false) adds two extra faces per subdivision to a sliced cylinder to close it around its height axis.
  174. * The parameter "arc" (float, default 1) is the ratio (max 1) to apply to the circumference to slice the cylinder.
  175. * You can set different colors and different images to each box side by using the parameters "faceColors" (an array of n Color3 elements) and "faceUV" (an array of n Vector4 elements).
  176. * The value of n is the number of cylinder faces. If the cylinder has only 1 subdivisions, n equals : top face + cylinder surface + bottom face = 3
  177. * Now, if the cylinder has 5 independent subdivisions (hasRings = true), n equals : top face + 5 stripe surfaces + bottom face = 2 + 5 = 7
  178. * Finally, if the cylinder has 5 independent subdivisions and is enclose, n equals : top face + 5 x (stripe surface + 2 closing faces) + bottom face = 2 + 5 * 3 = 17
  179. * Each array (color or UVs) is always ordered the same way : the first element is the bottom cap, the last element is the top cap. The other elements are each a ring surface.
  180. * If enclose is false, a ring surface is one element.
  181. * If enclose true, a ring surface is 3 successive elements in the array : the tubular surface, then the two closing faces.
  182. * Example how to set colors and textures on a sliced cylinder : http://www.html5gamedevs.com/topic/17945-creating-a-closed-slice-of-a-cylinder/#comment-106379
  183. * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
  184. * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
  185. * The mesh can be set to updatable with the boolean parameter "updatable" (default false) if its internal geometry is supposed to change once created.
  186. */
  187. public static CreateCylinder(name: string, options: { height?: number, diameterTop?: number, diameterBottom?: number, diameter?: number, tessellation?: number, subdivisions?: number, arc?: number, faceColors?: Color4[], faceUV?: Vector4[], updatable?: boolean, hasRings?: boolean, enclose?: boolean, sideOrientation?: number }, scene: any): Mesh {
  188. var cylinder = new Mesh(name, scene);
  189. var vertexData = VertexData.CreateCylinder(options);
  190. vertexData.applyToMesh(cylinder, options.updatable);
  191. return cylinder;
  192. }
  193. /**
  194. * Creates a torus mesh.
  195. * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#torus
  196. * The parameter "diameter" sets the diameter size (float) of the torus (default 1).
  197. * The parameter "thickness" sets the diameter size of the tube of the torus (float, default 0.5).
  198. * The parameter "tessellation" sets the number of torus sides (postive integer, default 16).
  199. * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
  200. * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
  201. * The mesh can be set to updatable with the boolean parameter "updatable" (default false) if its internal geometry is supposed to change once created.
  202. */
  203. public static CreateTorus(name: string, options: { diameter?: number, thickness?: number, tessellation?: number, updatable?: boolean, sideOrientation?: number }, scene: any): Mesh {
  204. var torus = new Mesh(name, scene);
  205. var vertexData = VertexData.CreateTorus(options);
  206. vertexData.applyToMesh(torus, options.updatable);
  207. return torus;
  208. }
  209. /**
  210. * Creates a torus knot mesh.
  211. * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#torus-knot
  212. * The parameter "radius" sets the global radius size (float) of the torus knot (default 2).
  213. * The parameter "radialSegments" sets the number of sides on each tube segments (positive integer, default 32).
  214. * The parameter "tubularSegments" sets the number of tubes to decompose the knot into (positive integer, default 32).
  215. * The parameters "p" and "q" are the number of windings on each axis (positive integers, default 2 and 3).
  216. * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
  217. * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
  218. * The mesh can be set to updatable with the boolean parameter "updatable" (default false) if its internal geometry is supposed to change once created.
  219. */
  220. public static CreateTorusKnot(name: string, options: { radius?: number, tube?: number, radialSegments?: number, tubularSegments?: number, p?: number, q?: number, updatable?: boolean, sideOrientation?: number }, scene: any): Mesh {
  221. var torusKnot = new Mesh(name, scene);
  222. var vertexData = VertexData.CreateTorusKnot(options);
  223. vertexData.applyToMesh(torusKnot, options.updatable);
  224. return torusKnot;
  225. }
  226. /**
  227. * Creates a line system mesh.
  228. * A line system is a pool of many lines gathered in a single mesh.
  229. * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#linesystem
  230. * A line system mesh is considered as a parametric shape since it has no predefined original shape. Its shape is determined by the passed array of lines as an input parameter.
  231. * Like every other parametric shape, it is dynamically updatable by passing an existing instance of LineSystem to this static function.
  232. * The parameter "lines" is an array of lines, each line being an array of successive Vector3.
  233. * The optional parameter "instance" is an instance of an existing LineSystem object to be updated with the passed "lines" parameter. The way to update it is the same than for
  234. * updating a simple Line mesh, you just need to update every line in the "lines" array : http://doc.babylonjs.com/tutorials/How_to_dynamically_morph_a_mesh#lines-and-dashedlines
  235. * When updating an instance, remember that only line point positions can change, not the number of points, neither the number of lines.
  236. * The mesh can be set to updatable with the boolean parameter "updatable" (default false) if its internal geometry is supposed to change once created.
  237. */
  238. public static CreateLineSystem(name: string, options: { lines: Vector3[][], updatable: boolean, instance?: LinesMesh }, scene: Scene): LinesMesh {
  239. var instance = options.instance;
  240. var lines = options.lines;
  241. if (instance) { // lines update
  242. var positionFunction = positions => {
  243. var i = 0;
  244. for (var l = 0; l < lines.length; l++) {
  245. var points = lines[l];
  246. for (var p = 0; p < points.length; p++) {
  247. positions[i] = points[p].x;
  248. positions[i + 1] = points[p].y;
  249. positions[i + 2] = points[p].z;
  250. i += 3;
  251. }
  252. }
  253. };
  254. instance.updateMeshPositions(positionFunction, false);
  255. return instance;
  256. }
  257. // line system creation
  258. var lineSystem = new LinesMesh(name, scene);
  259. var vertexData = VertexData.CreateLineSystem(options);
  260. vertexData.applyToMesh(lineSystem, options.updatable);
  261. return lineSystem;
  262. }
  263. /**
  264. * Creates a line mesh.
  265. * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#lines
  266. * A line mesh is considered as a parametric shape since it has no predefined original shape. Its shape is determined by the passed array of points as an input parameter.
  267. * Like every other parametric shape, it is dynamically updatable by passing an existing instance of LineMesh to this static function.
  268. * The parameter "points" is an array successive Vector3.
  269. * The optional parameter "instance" is an instance of an existing LineMesh object to be updated with the passed "points" parameter : http://doc.babylonjs.com/tutorials/How_to_dynamically_morph_a_mesh#lines-and-dashedlines
  270. * When updating an instance, remember that only point positions can change, not the number of points.
  271. * The mesh can be set to updatable with the boolean parameter "updatable" (default false) if its internal geometry is supposed to change once created.
  272. */
  273. public static CreateLines(name: string, options: { points: Vector3[], updatable?: boolean, instance?: LinesMesh }, scene: Scene): LinesMesh {
  274. var lines = MeshBuilder.CreateLineSystem(name, { lines: [options.points], updatable: options.updatable, instance: options.instance }, scene);
  275. return lines;
  276. }
  277. /**
  278. * Creates a dashed line mesh.
  279. * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#dashed-lines
  280. * A dashed line mesh is considered as a parametric shape since it has no predefined original shape. Its shape is determined by the passed array of points as an input parameter.
  281. * Like every other parametric shape, it is dynamically updatable by passing an existing instance of LineMesh to this static function.
  282. * The parameter "points" is an array successive Vector3.
  283. * The parameter "dashNb" is the intended total number of dashes (positive integer, default 200).
  284. * The parameter "dashSize" is the size of the dashes relatively the dash number (positive float, default 3).
  285. * The parameter "gapSize" is the size of the gap between two successive dashes relatively the dash number (positive float, default 1).
  286. * The optional parameter "instance" is an instance of an existing LineMesh object to be updated with the passed "points" parameter : http://doc.babylonjs.com/tutorials/How_to_dynamically_morph_a_mesh#lines-and-dashedlines
  287. * When updating an instance, remember that only point positions can change, not the number of points.
  288. * The mesh can be set to updatable with the boolean parameter "updatable" (default false) if its internal geometry is supposed to change once created.
  289. */
  290. public static CreateDashedLines(name: string, options: { points: Vector3[], dashSize?: number, gapSize?: number, dashNb?: number, updatable?: boolean, instance?: LinesMesh }, scene: Scene): LinesMesh {
  291. var points = options.points;
  292. var instance = options.instance;
  293. var gapSize = options.gapSize;
  294. var dashNb = options.dashNb;
  295. var dashSize = options.dashSize;
  296. if (instance) { // dashed lines update
  297. var positionFunction = (positions: number[]): void => {
  298. var curvect = Vector3.Zero();
  299. var nbSeg = positions.length / 6;
  300. var lg = 0;
  301. var nb = 0;
  302. var shft = 0;
  303. var dashshft = 0;
  304. var curshft = 0;
  305. var p = 0;
  306. var i = 0;
  307. var j = 0;
  308. for (i = 0; i < points.length - 1; i++) {
  309. points[i + 1].subtractToRef(points[i], curvect);
  310. lg += curvect.length();
  311. }
  312. shft = lg / nbSeg;
  313. dashshft = (<any>instance).dashSize * shft / ((<any>instance).dashSize + (<any>instance).gapSize);
  314. for (i = 0; i < points.length - 1; i++) {
  315. points[i + 1].subtractToRef(points[i], curvect);
  316. nb = Math.floor(curvect.length() / shft);
  317. curvect.normalize();
  318. j = 0;
  319. while (j < nb && p < positions.length) {
  320. curshft = shft * j;
  321. positions[p] = points[i].x + curshft * curvect.x;
  322. positions[p + 1] = points[i].y + curshft * curvect.y;
  323. positions[p + 2] = points[i].z + curshft * curvect.z;
  324. positions[p + 3] = points[i].x + (curshft + dashshft) * curvect.x;
  325. positions[p + 4] = points[i].y + (curshft + dashshft) * curvect.y;
  326. positions[p + 5] = points[i].z + (curshft + dashshft) * curvect.z;
  327. p += 6;
  328. j++;
  329. }
  330. }
  331. while (p < positions.length) {
  332. positions[p] = points[i].x;
  333. positions[p + 1] = points[i].y;
  334. positions[p + 2] = points[i].z;
  335. p += 3;
  336. }
  337. };
  338. instance.updateMeshPositions(positionFunction, false);
  339. return instance;
  340. }
  341. // dashed lines creation
  342. var dashedLines = new LinesMesh(name, scene);
  343. var vertexData = VertexData.CreateDashedLines(options);
  344. vertexData.applyToMesh(dashedLines, options.updatable);
  345. (<any>dashedLines).dashSize = dashSize;
  346. (<any>dashedLines).gapSize = gapSize;
  347. return dashedLines;
  348. }
  349. /**
  350. * Creates an extruded shape mesh.
  351. * The extrusion is a parametric shape : http://doc.babylonjs.com/tutorials/Parametric_Shapes. It has no predefined shape. Its final shape will depend on the input parameters.
  352. * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#extruded-shapes
  353. *
  354. * Please read this full tutorial to understand how to design an extruded shape : http://doc.babylonjs.com/tutorials/Parametric_Shapes#extrusion
  355. * The parameter "shape" is a required array of successive Vector3. This array depicts the shape to be extruded in its local space : the shape must be designed in the xOy plane and will be
  356. * extruded along the Z axis.
  357. * The parameter "path" is a required array of successive Vector3. This is the axis curve the shape is extruded along.
  358. * The parameter "rotation" (float, default 0 radians) is the angle value to rotate the shape each step (each path point), from the former step (so rotation added each step) along the curve.
  359. * The parameter "scale" (float, default 1) is the value to scale the shape.
  360. * 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
  361. * The optional parameter "instance" is an instance of an existing ExtrudedShape object to be updated with the passed "shape", "path", "scale" or "rotation" parameters : http://doc.babylonjs.com/tutorials/How_to_dynamically_morph_a_mesh#extruded-shape
  362. * Remember you can only change the shape or path point positions, not their number when updating an extruded shape.
  363. * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
  364. * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
  365. * The mesh can be set to updatable with the boolean parameter "updatable" (default false) if its internal geometry is supposed to change once created.
  366. */
  367. public static ExtrudeShape(name: string, options: { shape: Vector3[], path: Vector3[], scale?: number, rotation?: number, cap?: number, updatable?: boolean, sideOrientation?: number, instance?: Mesh }, scene: Scene): Mesh {
  368. var path = options.path;
  369. var shape = options.shape;
  370. var scale = options.scale || 1;
  371. var rotation = options.rotation || 0;
  372. var cap = (options.cap === 0) ? 0 : options.cap || Mesh.NO_CAP;
  373. var updatable = options.updatable;
  374. var sideOrientation = (options.sideOrientation === 0) ? 0 : options.sideOrientation || Mesh.DEFAULTSIDE;
  375. var instance = options.instance;
  376. return MeshBuilder._ExtrudeShapeGeneric(name, shape, path, scale, rotation, null, null, false, false, cap, false, scene, updatable, sideOrientation, instance);
  377. }
  378. /**
  379. * Creates an custom extruded shape mesh.
  380. * The custom extrusion is a parametric shape : http://doc.babylonjs.com/tutorials/Parametric_Shapes. It has no predefined shape. Its final shape will depend on the input parameters.
  381. * tuto :http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#custom-extruded-shapes
  382. *
  383. * Please read this full tutorial to understand how to design a custom extruded shape : http://doc.babylonjs.com/tutorials/Parametric_Shapes#extrusion
  384. * The parameter "shape" is a required array of successive Vector3. This array depicts the shape to be extruded in its local space : the shape must be designed in the xOy plane and will be
  385. * extruded along the Z axis.
  386. * The parameter "path" is a required array of successive Vector3. This is the axis curve the shape is extruded along.
  387. * The parameter "rotationFunction" (JS function) is a custom Javascript function called on each path point. This function is passed the position i of the point in the path
  388. * and the distance of this point from the begining of the path : rotationFunction = function(i, distance) {}.
  389. * It must returns a float value that will be the rotation in radians applied to the shape on each path point.
  390. * The parameter "scaleFunction" (JS function) is a custom Javascript function called on each path point. This function is passed the position i of the point in the path
  391. * and the distance of this point from the begining of the path : scaleFunction = function(i, distance) {}.
  392. * It must returns a float value that will be the scale value applied to the shape on each path point.
  393. * The parameter "ribbonClosePath" (boolean, default false) forces the extrusion underlying ribbon to close all the paths in its pathArray.
  394. * The parameter "ribbonCloseArray" (boolean, default false) forces the extrusion underlying ribbon to close its pathArray.
  395. * 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
  396. * The optional parameter "instance" is an instance of an existing ExtrudedShape object to be updated with the passed "shape", "path", "scale" or "rotation" parameters : http://doc.babylonjs.com/tutorials/How_to_dynamically_morph_a_mesh#extruded-shape
  397. * Remember you can only change the shape or path point positions, not their number when updating an extruded shape.
  398. * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
  399. * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
  400. * The mesh can be set to updatable with the boolean parameter "updatable" (default false) if its internal geometry is supposed to change once created.
  401. */
  402. public static ExtrudeShapeCustom(name: string, options: { shape: Vector3[], path: Vector3[], scaleFunction?: any, rotationFunction?: any, ribbonCloseArray?: boolean, ribbonClosePath?: boolean, cap?: number, updatable?: boolean, sideOrientation?: number, instance?: Mesh }, scene: Scene): Mesh {
  403. var path = options.path;
  404. var shape = options.shape;
  405. var scaleFunction = options.scaleFunction || (() => { return 1; });
  406. var rotationFunction = options.rotationFunction || (() => { return 0; });
  407. var ribbonCloseArray = options.ribbonCloseArray || false;
  408. var ribbonClosePath = options.ribbonClosePath || false;
  409. var cap = (options.cap === 0) ? 0 : options.cap || Mesh.NO_CAP;
  410. var updatable = options.updatable;
  411. var sideOrientation = (options.sideOrientation === 0) ? 0 : options.sideOrientation || Mesh.DEFAULTSIDE;
  412. var instance = options.instance;
  413. return MeshBuilder._ExtrudeShapeGeneric(name, shape, path, null, null, scaleFunction, rotationFunction, ribbonCloseArray, ribbonClosePath, cap, true, scene, updatable, sideOrientation, instance);
  414. }
  415. /**
  416. * Creates lathe mesh.
  417. * The lathe is a shape with a symetry axis : a 2D model shape is rotated around this axis to design the lathe.
  418. * tuto : http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#lathe
  419. *
  420. * The parameter "shape" is a required array of successive Vector3. This array depicts the shape to be rotated in its local space : the shape must be designed in the xOy plane and will be
  421. * rotated around the Y axis. It's usually a 2D shape, so the Vector3 z coordinates are often set to zero.
  422. * The parameter "radius" (positive float, default 1) is the radius value of the lathe.
  423. * The parameter "tessellation" (positive integer, default 64) is the side number of the lathe.
  424. * The parameter "arc" (positive float, default 1) is the ratio of the lathe. 0.5 builds for instance half a lathe, so an opened shape.
  425. * The parameter "closed" (boolean, default true) opens/closes the lathe circumference. This should be set to false when used with the parameter "arc".
  426. * 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
  427. * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
  428. * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
  429. * The mesh can be set to updatable with the boolean parameter "updatable" (default false) if its internal geometry is supposed to change once created.
  430. */
  431. public static CreateLathe(name: string, options: { shape: Vector3[], radius?: number, tessellation?: number, arc?: number, closed?: boolean, updatable?: boolean, sideOrientation?: number, cap?: number }, scene: Scene): Mesh {
  432. var arc: number = (options.arc <= 0 || options.arc > 1) ? 1.0 : options.arc || 1.0;
  433. var closed: boolean = (options.closed === undefined) ? true : options.closed;
  434. var shape = options.shape;
  435. var radius = options.radius || 1;
  436. var tessellation = options.tessellation || 64;
  437. var updatable = options.updatable;
  438. var sideOrientation = (options.sideOrientation === 0) ? 0 : options.sideOrientation || Mesh.DEFAULTSIDE;
  439. var cap = options.cap || Mesh.NO_CAP;
  440. var pi2 = Math.PI * 2;
  441. var paths = new Array();
  442. var i = 0;
  443. var p = 0;
  444. var step = pi2 / tessellation * arc;
  445. var rotated;
  446. var path = new Array<Vector3>();;
  447. for (i = 0; i <= tessellation; i++) {
  448. var path: Vector3[] = [];
  449. if (cap == Mesh.CAP_START || cap == Mesh.CAP_ALL) {
  450. path.push(new Vector3(0, shape[0].y, 0));
  451. path.push(new Vector3(Math.cos(i * step) * shape[0].x * radius, shape[0].y, Math.sin(i * step) * shape[0].x * radius));
  452. }
  453. for (p = 0; p < shape.length; p++) {
  454. rotated = new Vector3(Math.cos(i * step) * shape[p].x * radius, shape[p].y, Math.sin(i * step) * shape[p].x * radius);
  455. path.push(rotated);
  456. }
  457. if (cap == Mesh.CAP_END || cap == Mesh.CAP_ALL) {
  458. path.push(new Vector3(Math.cos(i * step) * shape[shape.length - 1].x * radius, shape[shape.length - 1].y, Math.sin(i * step) * shape[shape.length - 1].x * radius));
  459. path.push(new Vector3(0, shape[shape.length - 1].y, 0));
  460. }
  461. paths.push(path);
  462. }
  463. // lathe ribbon
  464. var lathe = MeshBuilder.CreateRibbon(name, { pathArray: paths, closeArray: closed, sideOrientation: sideOrientation, updatable: updatable }, scene);
  465. return lathe;
  466. }
  467. public static CreatePlane(name: string, options: { size?: number, width?: number, height?: number, sideOrientation?: number, updatable?: boolean, sourcePlane?: Plane }, scene: Scene): Mesh {
  468. var plane = new Mesh(name, scene);
  469. var vertexData = VertexData.CreatePlane(options);
  470. vertexData.applyToMesh(plane, options.updatable);
  471. if (options.sourcePlane) {
  472. plane.translate(options.sourcePlane.normal, options.sourcePlane.d);
  473. var product = Math.acos(Vector3.Dot(options.sourcePlane.normal, Axis.Z));
  474. var vectorProduct = Vector3.Cross(Axis.Z, options.sourcePlane.normal);
  475. plane.rotate(vectorProduct, product);
  476. }
  477. return plane;
  478. }
  479. public static CreateGround(name: string, options: { width?: number, height?: number, subdivisions?: number, updatable?: boolean }, scene: any): Mesh {
  480. var ground = new GroundMesh(name, scene);
  481. ground._setReady(false);
  482. ground._subdivisions = options.subdivisions || 1;
  483. ground._width = options.width || 1;
  484. ground._height = options.height || 1;
  485. ground._maxX = ground._width / 2;
  486. ground._maxZ = ground._height / 2;
  487. ground._minX = -ground._maxX;
  488. ground._minZ = -ground._maxZ;
  489. var vertexData = VertexData.CreateGround(options);
  490. vertexData.applyToMesh(ground, options.updatable);
  491. ground._setReady(true);
  492. return ground;
  493. }
  494. public static CreateTiledGround(name: string, options: { xmin: number, zmin: number, xmax: number, zmax: number, subdivisions?: { w: number; h: number; }, precision?: { w: number; h: number; }, updatable?: boolean }, scene: Scene): Mesh {
  495. var tiledGround = new Mesh(name, scene);
  496. var vertexData = VertexData.CreateTiledGround(options);
  497. vertexData.applyToMesh(tiledGround, options.updatable);
  498. return tiledGround;
  499. }
  500. public static CreateGroundFromHeightMap(name: string, url: string, options: { width?: number, height?: number, subdivisions?: number, minHeight?: number, maxHeight?: number, updatable?: boolean, onReady?: (mesh: GroundMesh) => void }, scene: Scene): GroundMesh {
  501. var width = options.width || 10;
  502. var height = options.height || 10;
  503. var subdivisions = options.subdivisions || 1;
  504. var minHeight = options.minHeight;
  505. var maxHeight = options.maxHeight || 10;
  506. var updatable = options.updatable;
  507. var onReady = options.onReady;
  508. var ground = new GroundMesh(name, scene);
  509. ground._subdivisions = subdivisions;
  510. ground._width = width;
  511. ground._height = height;
  512. ground._maxX = ground._width / 2;
  513. ground._maxZ = ground._height / 2;
  514. ground._minX = -ground._maxX;
  515. ground._minZ = -ground._maxZ;
  516. ground._setReady(false);
  517. var onload = img => {
  518. // Getting height map data
  519. var canvas = document.createElement("canvas");
  520. var context = canvas.getContext("2d");
  521. var bufferWidth = img.width;
  522. var bufferHeight = img.height;
  523. canvas.width = bufferWidth;
  524. canvas.height = bufferHeight;
  525. context.drawImage(img, 0, 0);
  526. // Create VertexData from map data
  527. // Cast is due to wrong definition in lib.d.ts from ts 1.3 - https://github.com/Microsoft/TypeScript/issues/949
  528. var buffer = <Uint8Array>(<any>context.getImageData(0, 0, bufferWidth, bufferHeight).data);
  529. var vertexData = VertexData.CreateGroundFromHeightMap({
  530. width, height,
  531. subdivisions,
  532. minHeight, maxHeight,
  533. buffer, bufferWidth, bufferHeight
  534. });
  535. vertexData.applyToMesh(ground, updatable);
  536. ground._setReady(true);
  537. //execute ready callback, if set
  538. if (onReady) {
  539. onReady(ground);
  540. }
  541. };
  542. Tools.LoadImage(url, onload, () => { }, scene.database);
  543. return ground;
  544. }
  545. public static CreateTube(name: string, options: { path: Vector3[], radius?: number, tessellation?: number, radiusFunction?: { (i: number, distance: number): number; }, cap?: number, arc?: number, updatable?: boolean, sideOrientation?: number, instance?: Mesh }, scene: Scene): Mesh {
  546. var path = options.path;
  547. var radius = options.radius || 1;
  548. var tessellation = options.tessellation || 64;
  549. var radiusFunction = options.radiusFunction;
  550. var cap = options.cap || Mesh.NO_CAP;
  551. var updatable = options.updatable;
  552. var sideOrientation = options.sideOrientation || Mesh.DEFAULTSIDE;
  553. var instance = options.instance;
  554. options.arc = (options.arc <= 0 || options.arc > 1) ? 1 : options.arc || 1;
  555. // tube geometry
  556. var tubePathArray = (path, path3D, circlePaths, radius, tessellation, radiusFunction, cap, arc) => {
  557. var tangents = path3D.getTangents();
  558. var normals = path3D.getNormals();
  559. var distances = path3D.getDistances();
  560. var pi2 = Math.PI * 2;
  561. var step = pi2 / tessellation * arc;
  562. var returnRadius: { (i: number, distance: number): number; } = () => radius;
  563. var radiusFunctionFinal: { (i: number, distance: number): number; } = radiusFunction || returnRadius;
  564. var circlePath: Vector3[];
  565. var rad: number;
  566. var normal: Vector3;
  567. var rotated: Vector3;
  568. var rotationMatrix: Matrix = Tmp.Matrix[0];
  569. var index = (cap === Mesh._NO_CAP || cap === Mesh.CAP_END) ? 0 : 2;
  570. for (var i = 0; i < path.length; i++) {
  571. rad = radiusFunctionFinal(i, distances[i]); // current radius
  572. circlePath = Array<Vector3>(); // current circle array
  573. normal = normals[i]; // current normal
  574. for (var t = 0; t < tessellation; t++) {
  575. Matrix.RotationAxisToRef(tangents[i], step * t, rotationMatrix);
  576. rotated = circlePath[t] ? circlePath[t] : Vector3.Zero();
  577. Vector3.TransformCoordinatesToRef(normal, rotationMatrix, rotated);
  578. rotated.scaleInPlace(rad).addInPlace(path[i]);
  579. circlePath[t] = rotated;
  580. }
  581. circlePaths[index] = circlePath;
  582. index++;
  583. }
  584. // cap
  585. var capPath = (nbPoints, pathIndex) => {
  586. var pointCap = Array<Vector3>();
  587. for (var i = 0; i < nbPoints; i++) {
  588. pointCap.push(path[pathIndex]);
  589. }
  590. return pointCap;
  591. };
  592. switch (cap) {
  593. case Mesh.NO_CAP:
  594. break;
  595. case Mesh.CAP_START:
  596. circlePaths[0] = capPath(tessellation, 0);
  597. circlePaths[1] = circlePaths[2].slice(0);
  598. break;
  599. case Mesh.CAP_END:
  600. circlePaths[index] = circlePaths[index - 1].slice(0);
  601. circlePaths[index + 1] = capPath(tessellation, path.length - 1);
  602. break;
  603. case Mesh.CAP_ALL:
  604. circlePaths[0] = capPath(tessellation, 0);
  605. circlePaths[1] = circlePaths[2].slice(0);
  606. circlePaths[index] = circlePaths[index - 1].slice(0);
  607. circlePaths[index + 1] = capPath(tessellation, path.length - 1);
  608. break;
  609. default:
  610. break;
  611. }
  612. return circlePaths;
  613. };
  614. var path3D;
  615. var pathArray;
  616. if (instance) { // tube update
  617. var arc = options.arc || (<any>instance).arc;
  618. path3D = ((<any>instance).path3D).update(path);
  619. pathArray = tubePathArray(path, path3D, (<any>instance).pathArray, radius, (<any>instance).tessellation, radiusFunction, (<any>instance).cap, arc);
  620. instance = MeshBuilder.CreateRibbon(null, { pathArray: pathArray, instance: instance });
  621. (<any>instance).path3D = path3D;
  622. (<any>instance).pathArray = pathArray;
  623. (<any>instance).arc = arc;
  624. return instance;
  625. }
  626. // tube creation
  627. path3D = <any>new Path3D(path);
  628. var newPathArray = new Array<Array<Vector3>>();
  629. cap = (cap < 0 || cap > 3) ? 0 : cap;
  630. pathArray = tubePathArray(path, path3D, newPathArray, radius, tessellation, radiusFunction, cap, options.arc);
  631. var tube = MeshBuilder.CreateRibbon(name, { pathArray: pathArray, closePath: true, closeArray: false, updatable: updatable, sideOrientation: sideOrientation }, scene);
  632. (<any>tube).pathArray = pathArray;
  633. (<any>tube).path3D = path3D;
  634. (<any>tube).tessellation = tessellation;
  635. (<any>tube).cap = cap;
  636. (<any>tube).arc = options.arc;
  637. return tube;
  638. }
  639. public static CreatePolyhedron(name: string, options: { type?: number, size?: number, sizeX?: number, sizeY?: number, sizeZ?: number, custom?: any, faceUV?: Vector4[], faceColors?: Color4[], flat?: boolean, updatable?: boolean, sideOrientation?: number }, scene: Scene): Mesh {
  640. var polyhedron = new Mesh(name, scene);
  641. var vertexData = VertexData.CreatePolyhedron(options);
  642. vertexData.applyToMesh(polyhedron, options.updatable);
  643. return polyhedron;
  644. }
  645. public static CreateDecal(name: string, sourceMesh: AbstractMesh, options: { position?: Vector3, normal?: Vector3, size?: Vector3, angle?: number }): Mesh {
  646. var indices = sourceMesh.getIndices();
  647. var positions = sourceMesh.getVerticesData(VertexBuffer.PositionKind);
  648. var normals = sourceMesh.getVerticesData(VertexBuffer.NormalKind);
  649. var position = options.position || Vector3.Zero();
  650. var normal = options.normal || Vector3.Up();
  651. var size = options.size || new Vector3(1, 1, 1);
  652. var angle = options.angle || 0;
  653. // Getting correct rotation
  654. if (!normal) {
  655. var target = new Vector3(0, 0, 1);
  656. var camera = sourceMesh.getScene().activeCamera;
  657. var cameraWorldTarget = Vector3.TransformCoordinates(target, camera.getWorldMatrix());
  658. normal = camera.globalPosition.subtract(cameraWorldTarget);
  659. }
  660. var yaw = -Math.atan2(normal.z, normal.x) - Math.PI / 2;
  661. var len = Math.sqrt(normal.x * normal.x + normal.z * normal.z);
  662. var pitch = Math.atan2(normal.y, len);
  663. // Matrix
  664. var decalWorldMatrix = Matrix.RotationYawPitchRoll(yaw, pitch, angle).multiply(Matrix.Translation(position.x, position.y, position.z));
  665. var inverseDecalWorldMatrix = Matrix.Invert(decalWorldMatrix);
  666. var meshWorldMatrix = sourceMesh.getWorldMatrix();
  667. var transformMatrix = meshWorldMatrix.multiply(inverseDecalWorldMatrix);
  668. var vertexData = new VertexData();
  669. vertexData.indices = [];
  670. vertexData.positions = [];
  671. vertexData.normals = [];
  672. vertexData.uvs = [];
  673. var currentVertexDataIndex = 0;
  674. var extractDecalVector3 = (indexId: number): PositionNormalVertex => {
  675. var vertexId = indices[indexId];
  676. var result = new PositionNormalVertex();
  677. result.position = new Vector3(positions[vertexId * 3], positions[vertexId * 3 + 1], positions[vertexId * 3 + 2]);
  678. // Send vector to decal local world
  679. result.position = Vector3.TransformCoordinates(result.position, transformMatrix);
  680. // Get normal
  681. result.normal = new Vector3(normals[vertexId * 3], normals[vertexId * 3 + 1], normals[vertexId * 3 + 2]);
  682. return result;
  683. }; // Inspired by https://github.com/mrdoob/three.js/blob/eee231960882f6f3b6113405f524956145148146/examples/js/geometries/DecalGeometry.js
  684. var clip = (vertices: PositionNormalVertex[], axis: Vector3): PositionNormalVertex[]=> {
  685. if (vertices.length === 0) {
  686. return vertices;
  687. }
  688. var clipSize = 0.5 * Math.abs(Vector3.Dot(size, axis));
  689. var clipVertices = (v0: PositionNormalVertex, v1: PositionNormalVertex): PositionNormalVertex => {
  690. var clipFactor = Vector3.GetClipFactor(v0.position, v1.position, axis, clipSize);
  691. return new PositionNormalVertex(
  692. Vector3.Lerp(v0.position, v1.position, clipFactor),
  693. Vector3.Lerp(v0.normal, v1.normal, clipFactor)
  694. );
  695. };
  696. var result = new Array<PositionNormalVertex>();
  697. for (var index = 0; index < vertices.length; index += 3) {
  698. var v1Out: boolean;
  699. var v2Out: boolean;
  700. var v3Out: boolean;
  701. var total = 0;
  702. var nV1: PositionNormalVertex, nV2: PositionNormalVertex, nV3: PositionNormalVertex, nV4: PositionNormalVertex;
  703. var d1 = Vector3.Dot(vertices[index].position, axis) - clipSize;
  704. var d2 = Vector3.Dot(vertices[index + 1].position, axis) - clipSize;
  705. var d3 = Vector3.Dot(vertices[index + 2].position, axis) - clipSize;
  706. v1Out = d1 > 0;
  707. v2Out = d2 > 0;
  708. v3Out = d3 > 0;
  709. total = (v1Out ? 1 : 0) + (v2Out ? 1 : 0) + (v3Out ? 1 : 0);
  710. switch (total) {
  711. case 0:
  712. result.push(vertices[index]);
  713. result.push(vertices[index + 1]);
  714. result.push(vertices[index + 2]);
  715. break;
  716. case 1:
  717. if (v1Out) {
  718. nV1 = vertices[index + 1];
  719. nV2 = vertices[index + 2];
  720. nV3 = clipVertices(vertices[index], nV1);
  721. nV4 = clipVertices(vertices[index], nV2);
  722. }
  723. if (v2Out) {
  724. nV1 = vertices[index];
  725. nV2 = vertices[index + 2];
  726. nV3 = clipVertices(vertices[index + 1], nV1);
  727. nV4 = clipVertices(vertices[index + 1], nV2);
  728. result.push(nV3);
  729. result.push(nV2.clone());
  730. result.push(nV1.clone());
  731. result.push(nV2.clone());
  732. result.push(nV3.clone());
  733. result.push(nV4);
  734. break;
  735. }
  736. if (v3Out) {
  737. nV1 = vertices[index];
  738. nV2 = vertices[index + 1];
  739. nV3 = clipVertices(vertices[index + 2], nV1);
  740. nV4 = clipVertices(vertices[index + 2], nV2);
  741. }
  742. result.push(nV1.clone());
  743. result.push(nV2.clone());
  744. result.push(nV3);
  745. result.push(nV4);
  746. result.push(nV3.clone());
  747. result.push(nV2.clone());
  748. break;
  749. case 2:
  750. if (!v1Out) {
  751. nV1 = vertices[index].clone();
  752. nV2 = clipVertices(nV1, vertices[index + 1]);
  753. nV3 = clipVertices(nV1, vertices[index + 2]);
  754. result.push(nV1);
  755. result.push(nV2);
  756. result.push(nV3);
  757. }
  758. if (!v2Out) {
  759. nV1 = vertices[index + 1].clone();
  760. nV2 = clipVertices(nV1, vertices[index + 2]);
  761. nV3 = clipVertices(nV1, vertices[index]);
  762. result.push(nV1);
  763. result.push(nV2);
  764. result.push(nV3);
  765. }
  766. if (!v3Out) {
  767. nV1 = vertices[index + 2].clone();
  768. nV2 = clipVertices(nV1, vertices[index]);
  769. nV3 = clipVertices(nV1, vertices[index + 1]);
  770. result.push(nV1);
  771. result.push(nV2);
  772. result.push(nV3);
  773. }
  774. break;
  775. case 3:
  776. break;
  777. }
  778. }
  779. return result;
  780. };
  781. for (var index = 0; index < indices.length; index += 3) {
  782. var faceVertices = new Array<PositionNormalVertex>();
  783. faceVertices.push(extractDecalVector3(index));
  784. faceVertices.push(extractDecalVector3(index + 1));
  785. faceVertices.push(extractDecalVector3(index + 2));
  786. // Clip
  787. faceVertices = clip(faceVertices, new Vector3(1, 0, 0));
  788. faceVertices = clip(faceVertices, new Vector3(-1, 0, 0));
  789. faceVertices = clip(faceVertices, new Vector3(0, 1, 0));
  790. faceVertices = clip(faceVertices, new Vector3(0, -1, 0));
  791. faceVertices = clip(faceVertices, new Vector3(0, 0, 1));
  792. faceVertices = clip(faceVertices, new Vector3(0, 0, -1));
  793. if (faceVertices.length === 0) {
  794. continue;
  795. }
  796. // Add UVs and get back to world
  797. for (var vIndex = 0; vIndex < faceVertices.length; vIndex++) {
  798. var vertex = faceVertices[vIndex];
  799. //TODO check for Int32Array
  800. (<number[]>vertexData.indices).push(currentVertexDataIndex);
  801. vertex.position.toArray(vertexData.positions, currentVertexDataIndex * 3);
  802. vertex.normal.toArray(vertexData.normals, currentVertexDataIndex * 3);
  803. (<number[]>vertexData.uvs).push(0.5 + vertex.position.x / size.x);
  804. (<number[]>vertexData.uvs).push(0.5 + vertex.position.y / size.y);
  805. currentVertexDataIndex++;
  806. }
  807. }
  808. // Return mesh
  809. var decal = new Mesh(name, sourceMesh.getScene());
  810. vertexData.applyToMesh(decal);
  811. decal.position = position.clone();
  812. decal.rotation = new Vector3(pitch, yaw, angle);
  813. return decal;
  814. }
  815. // Privates
  816. private static _ExtrudeShapeGeneric(name: string, shape: Vector3[], curve: Vector3[], scale: number, rotation: number, scaleFunction: { (i: number, distance: number): number; }, rotateFunction: { (i: number, distance: number): number; }, rbCA: boolean, rbCP: boolean, cap: number, custom: boolean, scene: Scene, updtbl: boolean, side: number, instance: Mesh): Mesh {
  817. // extrusion geometry
  818. var extrusionPathArray = (shape, curve, path3D, shapePaths, scale, rotation, scaleFunction, rotateFunction, cap, custom) => {
  819. var tangents = path3D.getTangents();
  820. var normals = path3D.getNormals();
  821. var binormals = path3D.getBinormals();
  822. var distances = path3D.getDistances();
  823. var angle = 0;
  824. var returnScale: { (i: number, distance: number): number; } = () => { return scale; };
  825. var returnRotation: { (i: number, distance: number): number; } = () => { return rotation; };
  826. var rotate: { (i: number, distance: number): number; } = custom ? rotateFunction : returnRotation;
  827. var scl: { (i: number, distance: number): number; } = custom ? scaleFunction : returnScale;
  828. var index = (cap === Mesh.NO_CAP || cap === Mesh.CAP_END) ? 0 : 2;
  829. var rotationMatrix: Matrix = Tmp.Matrix[0];
  830. for (var i = 0; i < curve.length; i++) {
  831. var shapePath = new Array<Vector3>();
  832. var angleStep = rotate(i, distances[i]);
  833. var scaleRatio = scl(i, distances[i]);
  834. for (var p = 0; p < shape.length; p++) {
  835. Matrix.RotationAxisToRef(tangents[i], angle, rotationMatrix);
  836. var planed = ((tangents[i].scale(shape[p].z)).add(normals[i].scale(shape[p].x)).add(binormals[i].scale(shape[p].y)));
  837. var rotated = shapePath[p] ? shapePath[p] : Vector3.Zero();
  838. Vector3.TransformCoordinatesToRef(planed, rotationMatrix, rotated);
  839. rotated.scaleInPlace(scaleRatio).addInPlace(curve[i]);
  840. shapePath[p] = rotated;
  841. }
  842. shapePaths[index] = shapePath;
  843. angle += angleStep;
  844. index++;
  845. }
  846. // cap
  847. var capPath = shapePath => {
  848. var pointCap = Array<Vector3>();
  849. var barycenter = Vector3.Zero();
  850. var i: number;
  851. for (i = 0; i < shapePath.length; i++) {
  852. barycenter.addInPlace(shapePath[i]);
  853. }
  854. barycenter.scaleInPlace(1 / shapePath.length);
  855. for (i = 0; i < shapePath.length; i++) {
  856. pointCap.push(barycenter);
  857. }
  858. return pointCap;
  859. };
  860. switch (cap) {
  861. case Mesh.NO_CAP:
  862. break;
  863. case Mesh.CAP_START:
  864. shapePaths[0] = capPath(shapePaths[2]);
  865. shapePaths[1] = shapePaths[2].slice(0);
  866. break;
  867. case Mesh.CAP_END:
  868. shapePaths[index] = shapePaths[index - 1];
  869. shapePaths[index + 1] = capPath(shapePaths[index - 1]);
  870. break;
  871. case Mesh.CAP_ALL:
  872. shapePaths[0] = capPath(shapePaths[2]);
  873. shapePaths[1] = shapePaths[2].slice(0);
  874. shapePaths[index] = shapePaths[index - 1];
  875. shapePaths[index + 1] = capPath(shapePaths[index - 1]);
  876. break;
  877. default:
  878. break;
  879. }
  880. return shapePaths;
  881. };
  882. var path3D;
  883. var pathArray;
  884. if (instance) { // instance update
  885. path3D = ((<any>instance).path3D).update(curve);
  886. pathArray = extrusionPathArray(shape, curve, (<any>instance).path3D, (<any>instance).pathArray, scale, rotation, scaleFunction, rotateFunction, (<any>instance).cap, custom);
  887. instance = Mesh.CreateRibbon(null, pathArray, null, null, null, null, null, null, instance);
  888. return instance;
  889. }
  890. // extruded shape creation
  891. path3D = <any>new Path3D(curve);
  892. var newShapePaths = new Array<Array<Vector3>>();
  893. cap = (cap < 0 || cap > 3) ? 0 : cap;
  894. pathArray = extrusionPathArray(shape, curve, path3D, newShapePaths, scale, rotation, scaleFunction, rotateFunction, cap, custom);
  895. var extrudedGeneric = Mesh.CreateRibbon(name, pathArray, rbCA, rbCP, 0, scene, updtbl, side);
  896. (<any>extrudedGeneric).pathArray = pathArray;
  897. (<any>extrudedGeneric).path3D = path3D;
  898. (<any>extrudedGeneric).cap = cap;
  899. return extrudedGeneric;
  900. }
  901. }
  902. }