Extruder.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSim Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. //#define SPAM
  28. using OpenSim.Region.Physics.Manager;
  29. namespace OpenSim.Region.Physics.Meshing
  30. {
  31. internal class Extruder
  32. {
  33. public float startParameter;
  34. public float stopParameter;
  35. public PhysicsVector size;
  36. public float taperTopFactorX = 1f;
  37. public float taperTopFactorY = 1f;
  38. public float taperBotFactorX = 1f;
  39. public float taperBotFactorY = 1f;
  40. public float pushX = 0f;
  41. public float pushY = 0f;
  42. // twist amount in radians. NOT DEGREES.
  43. public float twistTop = 0;
  44. public float twistBot = 0;
  45. public float twistMid = 0;
  46. public float pathScaleX = 1.0f;
  47. public float pathScaleY = 0.5f;
  48. public float skew = 0.0f;
  49. public float radius = 0.0f;
  50. public float revolutions = 1.0f;
  51. public float pathCutBegin = 0.0f;
  52. public float pathCutEnd = 1.0f;
  53. public ushort pathBegin = 0;
  54. public ushort pathEnd = 0;
  55. public float pathTaperX = 0.0f;
  56. public float pathTaperY = 0.0f;
  57. /// <summary>
  58. /// (deprecated) creates a 3 layer extruded mesh of a profile hull
  59. /// </summary>
  60. /// <param name="m"></param>
  61. /// <returns></returns>
  62. public Mesh Extrude(Mesh m)
  63. {
  64. startParameter = float.MinValue;
  65. stopParameter = float.MaxValue;
  66. // Currently only works for iSteps=1;
  67. Mesh result = new Mesh();
  68. Mesh workingPlus = m.Clone();
  69. Mesh workingMiddle = m.Clone();
  70. Mesh workingMinus = m.Clone();
  71. Quaternion tt = new Quaternion();
  72. Vertex v2 = new Vertex(0, 0, 0);
  73. foreach (Vertex v in workingPlus.vertices)
  74. {
  75. if (v == null)
  76. continue;
  77. // This is the top
  78. // Set the Z + .5 to match the rest of the scale of the mesh
  79. // Scale it by Size, and Taper the scaling
  80. v.Z = +.5f;
  81. v.X *= (size.X * taperTopFactorX);
  82. v.Y *= (size.Y * taperTopFactorY);
  83. v.Z *= size.Z;
  84. //Push the top of the object over by the Top Shear amount
  85. v.X += pushX * size.X;
  86. v.Y += pushY * size.Y;
  87. if (twistTop != 0)
  88. {
  89. // twist and shout
  90. tt = new Quaternion(new Vertex(0, 0, 1), twistTop);
  91. v2 = v * tt;
  92. v.X = v2.X;
  93. v.Y = v2.Y;
  94. v.Z = v2.Z;
  95. }
  96. }
  97. foreach (Vertex v in workingMiddle.vertices)
  98. {
  99. if (v == null)
  100. continue;
  101. // This is the top
  102. // Set the Z + .5 to match the rest of the scale of the mesh
  103. // Scale it by Size, and Taper the scaling
  104. v.Z *= size.Z;
  105. v.X *= (size.X * ((taperTopFactorX + taperBotFactorX) /2));
  106. v.Y *= (size.Y * ((taperTopFactorY + taperBotFactorY) / 2));
  107. v.X += (pushX / 2) * size.X;
  108. v.Y += (pushY / 2) * size.Y;
  109. //Push the top of the object over by the Top Shear amount
  110. if (twistMid != 0)
  111. {
  112. // twist and shout
  113. tt = new Quaternion(new Vertex(0, 0, 1), twistMid);
  114. v2 = v * tt;
  115. v.X = v2.X;
  116. v.Y = v2.Y;
  117. v.Z = v2.Z;
  118. }
  119. }
  120. foreach (Vertex v in workingMinus.vertices)
  121. {
  122. if (v == null)
  123. continue;
  124. // This is the bottom
  125. v.Z = -.5f;
  126. v.X *= (size.X * taperBotFactorX);
  127. v.Y *= (size.Y * taperBotFactorY);
  128. v.Z *= size.Z;
  129. if (twistBot != 0)
  130. {
  131. // twist and shout
  132. tt = new Quaternion(new Vertex(0, 0, 1), twistBot);
  133. v2 = v * tt;
  134. v.X = v2.X;
  135. v.Y = v2.Y;
  136. v.Z = v2.Z;
  137. }
  138. }
  139. foreach (Triangle t in workingMinus.triangles)
  140. {
  141. t.invertNormal();
  142. }
  143. result.Append(workingMinus);
  144. result.Append(workingMiddle);
  145. int iLastNull = 0;
  146. for (int i = 0; i < workingMiddle.vertices.Count; i++)
  147. {
  148. int iNext = i + 1;
  149. if (workingMiddle.vertices[i] == null) // Can't make a simplex here
  150. {
  151. iLastNull = i + 1;
  152. continue;
  153. }
  154. if (i == workingMiddle.vertices.Count - 1) // End of list
  155. {
  156. iNext = iLastNull;
  157. }
  158. if (workingMiddle.vertices[iNext] == null) // Null means wrap to begin of last segment
  159. {
  160. iNext = iLastNull;
  161. }
  162. Triangle tSide;
  163. tSide = new Triangle(workingMiddle.vertices[i], workingMinus.vertices[i], workingMiddle.vertices[iNext]);
  164. result.Add(tSide);
  165. tSide =
  166. new Triangle(workingMiddle.vertices[iNext], workingMinus.vertices[i], workingMinus.vertices[iNext]);
  167. result.Add(tSide);
  168. }
  169. //foreach (Triangle t in workingPlus.triangles)
  170. //{
  171. //t.invertNormal();
  172. // }
  173. result.Append(workingPlus);
  174. iLastNull = 0;
  175. for (int i = 0; i < workingPlus.vertices.Count; i++)
  176. {
  177. int iNext = i + 1;
  178. if (workingPlus.vertices[i] == null) // Can't make a simplex here
  179. {
  180. iLastNull = i + 1;
  181. continue;
  182. }
  183. if (i == workingPlus.vertices.Count - 1) // End of list
  184. {
  185. iNext = iLastNull;
  186. }
  187. if (workingPlus.vertices[iNext] == null) // Null means wrap to begin of last segment
  188. {
  189. iNext = iLastNull;
  190. }
  191. Triangle tSide;
  192. tSide = new Triangle(workingPlus.vertices[i], workingMiddle.vertices[i], workingPlus.vertices[iNext]);
  193. result.Add(tSide);
  194. tSide =
  195. new Triangle(workingPlus.vertices[iNext], workingMiddle.vertices[i], workingMiddle.vertices[iNext]);
  196. result.Add(tSide);
  197. }
  198. if (twistMid != 0)
  199. {
  200. foreach (Vertex v in result.vertices)
  201. {
  202. // twist and shout
  203. if (v != null)
  204. {
  205. tt = new Quaternion(new Vertex(0, 0, -1), twistMid*2);
  206. v2 = v * tt;
  207. v.X = v2.X;
  208. v.Y = v2.Y;
  209. v.Z = v2.Z;
  210. }
  211. }
  212. }
  213. return result;
  214. }
  215. /// <summary>
  216. /// Creates an extrusion of a profile along a linear path. Used to create prim types box, cylinder, and prism.
  217. /// </summary>
  218. /// <param name="m"></param>
  219. /// <returns>A mesh of the extruded shape</returns>
  220. public Mesh ExtrudeLinearPath(Mesh m)
  221. {
  222. Mesh result = new Mesh();
  223. // Quaternion tt = new Quaternion();
  224. // Vertex v2 = new Vertex(0, 0, 0);
  225. Mesh newLayer;
  226. Mesh lastLayer = null;
  227. int step = 0;
  228. int steps = 1;
  229. float twistTotal = twistTop - twistBot;
  230. // if the profile has a lot of twist, add more layers otherwise the layers may overlap
  231. // and the resulting mesh may be quite inaccurate. This method is arbitrary and may not
  232. // accurately match the viewer
  233. float twistTotalAbs = System.Math.Abs(twistTotal);
  234. if (twistTotalAbs > 0.01)
  235. steps += (int)(twistTotalAbs * 3.66f); // dahlia's magic number ;)
  236. #if SPAM
  237. System.Console.WriteLine("ExtrudeLinearPath: twistTotalAbs: " + twistTotalAbs.ToString() + " steps: " + steps.ToString());
  238. #endif
  239. double percentOfPathMultiplier = 1.0 / steps;
  240. float start = -0.5f;
  241. float stepSize = 1.0f / (float)steps;
  242. float xProfileScale = 1.0f;
  243. float yProfileScale = 1.0f;
  244. float xOffset = 0.0f;
  245. float yOffset = 0.0f;
  246. float zOffset = start;
  247. float xOffsetStepIncrement = pushX / steps;
  248. float yOffsetStepIncrement = pushY / steps;
  249. #if SPAM
  250. System.Console.WriteLine("Extruder: twistTop: " + twistTop.ToString() + " twistbot: " + twistBot.ToString() + " twisttotal: " + twistTotal.ToString());
  251. System.Console.WriteLine("Extruder: taperBotFactorX: " + taperBotFactorX.ToString() + " taperBotFactorY: " + taperBotFactorY.ToString()
  252. + " taperTopFactorX: " + taperTopFactorX.ToString() + " taperTopFactorY: " + taperTopFactorY.ToString());
  253. System.Console.WriteLine("Extruder: PathScaleX: " + pathScaleX.ToString() + " pathScaleY: " + pathScaleY.ToString());
  254. #endif
  255. //float percentOfPath = 0.0f;
  256. float percentOfPath = (float)pathBegin * 2.0e-5f;
  257. zOffset += percentOfPath;
  258. bool done = false;
  259. do // loop through the length of the path and add the layers
  260. {
  261. newLayer = m.Clone();
  262. if (taperBotFactorX < 1.0f)
  263. xProfileScale = 1.0f - (1.0f - percentOfPath) * (1.0f - taperBotFactorX);
  264. else if (taperTopFactorX < 1.0f)
  265. xProfileScale = 1.0f - percentOfPath * (1.0f - taperTopFactorX);
  266. else xProfileScale = 1.0f;
  267. if (taperBotFactorY < 1.0f)
  268. yProfileScale = 1.0f - (1.0f - percentOfPath) * (1.0f - taperBotFactorY);
  269. else if (taperTopFactorY < 1.0f)
  270. yProfileScale = 1.0f - percentOfPath * (1.0f - taperTopFactorY);
  271. else yProfileScale = 1.0f;
  272. #if SPAM
  273. //System.Console.WriteLine("xProfileScale: " + xProfileScale.ToString() + " yProfileScale: " + yProfileScale.ToString());
  274. #endif
  275. Vertex vTemp = new Vertex(0.0f, 0.0f, 0.0f);
  276. // apply the taper to the profile before any rotations
  277. if (xProfileScale != 1.0f || yProfileScale != 1.0f)
  278. {
  279. foreach (Vertex v in newLayer.vertices)
  280. {
  281. if (v != null)
  282. {
  283. v.X *= xProfileScale;
  284. v.Y *= yProfileScale;
  285. }
  286. }
  287. }
  288. float twist = twistBot + (twistTotal * (float)percentOfPath);
  289. #if SPAM
  290. System.Console.WriteLine("Extruder: percentOfPath: " + percentOfPath.ToString() + " zOffset: " + zOffset.ToString()
  291. + " xProfileScale: " + xProfileScale.ToString() + " yProfileScale: " + yProfileScale.ToString());
  292. #endif
  293. // apply twist rotation to the profile layer and position the layer in the prim
  294. Quaternion profileRot = new Quaternion(new Vertex(0.0f, 0.0f, -1.0f), twist);
  295. foreach (Vertex v in newLayer.vertices)
  296. {
  297. if (v != null)
  298. {
  299. vTemp = v * profileRot;
  300. v.X = vTemp.X + xOffset;
  301. v.Y = vTemp.Y + yOffset;
  302. v.Z = vTemp.Z + zOffset;
  303. }
  304. }
  305. if (step == 0) // the first layer, invert normals
  306. {
  307. foreach (Triangle t in newLayer.triangles)
  308. {
  309. t.invertNormal();
  310. }
  311. }
  312. result.Append(newLayer);
  313. int iLastNull = 0;
  314. if (lastLayer != null)
  315. {
  316. int i, count = newLayer.vertices.Count;
  317. for (i = 0; i < count; i++)
  318. {
  319. int iNext = (i + 1);
  320. if (lastLayer.vertices[i] == null) // cant make a simplex here
  321. {
  322. iLastNull = i + 1;
  323. }
  324. else
  325. {
  326. if (i == count - 1) // End of list
  327. iNext = iLastNull;
  328. if (lastLayer.vertices[iNext] == null) // Null means wrap to begin of last segment
  329. iNext = iLastNull;
  330. result.Add(new Triangle(newLayer.vertices[i], lastLayer.vertices[i], newLayer.vertices[iNext]));
  331. result.Add(new Triangle(newLayer.vertices[iNext], lastLayer.vertices[i], lastLayer.vertices[iNext]));
  332. }
  333. }
  334. }
  335. lastLayer = newLayer;
  336. // calc the step for the next interation of the loop
  337. if (step < steps)
  338. {
  339. step++;
  340. percentOfPath += (float)percentOfPathMultiplier;
  341. xOffset += xOffsetStepIncrement;
  342. yOffset += yOffsetStepIncrement;
  343. zOffset += stepSize;
  344. if (percentOfPath > 1.0f - (float)pathEnd * 2.0e-5f)
  345. done = true;
  346. }
  347. else done = true;
  348. } while (!done); // loop until all the layers in the path are completed
  349. // scale the mesh to the desired size
  350. float xScale = size.X;
  351. float yScale = size.Y;
  352. float zScale = size.Z;
  353. foreach (Vertex v in result.vertices)
  354. {
  355. if (v != null)
  356. {
  357. v.X *= xScale;
  358. v.Y *= yScale;
  359. v.Z *= zScale;
  360. }
  361. }
  362. return result;
  363. }
  364. /// <summary>
  365. /// Extrudes a shape around a circular path. Used to create prim types torus, ring, and tube.
  366. /// </summary>
  367. /// <param name="m"></param>
  368. /// <returns>a mesh of the extruded shape</returns>
  369. public Mesh ExtrudeCircularPath(Mesh m)
  370. {
  371. Mesh result = new Mesh();
  372. // Quaternion tt = new Quaternion();
  373. // Vertex v2 = new Vertex(0, 0, 0);
  374. Mesh newLayer;
  375. Mesh lastLayer = null;
  376. int step;
  377. int steps = 24;
  378. float twistTotal = twistTop - twistBot;
  379. // if the profile has a lot of twist, add more layers otherwise the layers may overlap
  380. // and the resulting mesh may be quite inaccurate. This method is arbitrary and doesn't
  381. // accurately match the viewer
  382. if (System.Math.Abs(twistTotal) > (float)System.Math.PI * 1.5f) steps *= 2;
  383. if (System.Math.Abs(twistTotal) > (float)System.Math.PI * 3.0f) steps *= 2;
  384. // double percentOfPathMultiplier = 1.0 / steps;
  385. // double angleStepMultiplier = System.Math.PI * 2.0 / steps;
  386. float yPathScale = pathScaleY * 0.5f;
  387. float pathLength = pathCutEnd - pathCutBegin;
  388. float totalSkew = skew * 2.0f * pathLength;
  389. float skewStart = (-skew) + pathCutBegin * 2.0f * skew;
  390. // It's not quite clear what pushY (Y top shear) does, but subtracting it from the start and end
  391. // angles appears to approximate it's effects on path cut. Likewise, adding it to the angle used
  392. // to calculate the sine for generating the path radius appears to approximate it's effects there
  393. // too, but there are some subtle differences in the radius which are noticeable as the prim size
  394. // increases and it may affect megaprims quite a bit. The effect of the Y top shear parameter on
  395. // the meshes generated with this technique appear nearly identical in shape to the same prims when
  396. // displayed by the viewer.
  397. float startAngle = (float)(System.Math.PI * 2.0 * pathCutBegin * revolutions) - pushY * 0.9f;
  398. float endAngle = (float)(System.Math.PI * 2.0 * pathCutEnd * revolutions) - pushY * 0.9f;
  399. float stepSize = (float)0.2617993878; // 2*PI / 24 segments per revolution
  400. step = (int)(startAngle / stepSize);
  401. float angle = startAngle;
  402. float xProfileScale = 1.0f;
  403. float yProfileScale = 1.0f;
  404. #if SPAM
  405. System.Console.WriteLine("Extruder: twistTop: " + twistTop.ToString() + " twistbot: " + twistBot.ToString() + " twisttotal: " + twistTotal.ToString());
  406. System.Console.WriteLine("Extruder: startAngle: " + startAngle.ToString() + " endAngle: " + endAngle.ToString() + " step: " + step.ToString());
  407. System.Console.WriteLine("Extruder: taperBotFactorX: " + taperBotFactorX.ToString() + " taperBotFactorY: " + taperBotFactorY.ToString()
  408. + " taperTopFactorX: " + taperTopFactorX.ToString() + " taperTopFactorY: " + taperTopFactorY.ToString());
  409. System.Console.WriteLine("Extruder: PathScaleX: " + pathScaleX.ToString() + " pathScaleY: " + pathScaleY.ToString());
  410. #endif
  411. bool done = false;
  412. do // loop through the length of the path and add the layers
  413. {
  414. newLayer = m.Clone();
  415. float percentOfPath = (angle - startAngle) / (endAngle - startAngle); // endAngle should always be larger than startAngle
  416. if (pathTaperX > 0.001f) // can't really compare to 0.0f as the value passed is never exactly zero
  417. xProfileScale = 1.0f - percentOfPath * pathTaperX;
  418. else if (pathTaperX < -0.001f)
  419. xProfileScale = 1.0f + (1.0f - percentOfPath) * pathTaperX;
  420. else xProfileScale = 1.0f;
  421. if (pathTaperY > 0.001f)
  422. yProfileScale = 1.0f - percentOfPath * pathTaperY;
  423. else if (pathTaperY < -0.001f)
  424. yProfileScale = 1.0f + (1.0f - percentOfPath) * pathTaperY;
  425. else yProfileScale = 1.0f;
  426. #if SPAM
  427. //System.Console.WriteLine("xProfileScale: " + xProfileScale.ToString() + " yProfileScale: " + yProfileScale.ToString());
  428. #endif
  429. Vertex vTemp = new Vertex(0.0f, 0.0f, 0.0f);
  430. // apply the taper to the profile before any rotations
  431. if (xProfileScale != 1.0f || yProfileScale != 1.0f)
  432. {
  433. foreach (Vertex v in newLayer.vertices)
  434. {
  435. if (v != null)
  436. {
  437. v.X *= xProfileScale;
  438. v.Y *= yProfileScale;
  439. }
  440. }
  441. }
  442. float radiusScale;
  443. if (radius > 0.001f)
  444. radiusScale = 1.0f - radius * percentOfPath;
  445. else if (radius < 0.001f)
  446. radiusScale = 1.0f + radius * (1.0f - percentOfPath);
  447. else
  448. radiusScale = 1.0f;
  449. #if SPAM
  450. System.Console.WriteLine("Extruder: angle: " + angle.ToString() + " percentOfPath: " + percentOfPath.ToString()
  451. + " radius: " + radius.ToString() + " radiusScale: " + radiusScale.ToString()
  452. + " xProfileScale: " + xProfileScale.ToString() + " yProfileScale: " + yProfileScale.ToString());
  453. #endif
  454. float twist = twistBot + (twistTotal * (float)percentOfPath);
  455. float xOffset;
  456. float yOffset;
  457. float zOffset;
  458. xOffset = 0.5f * (skewStart + totalSkew * (float)percentOfPath);
  459. xOffset += (float) System.Math.Sin(angle) * pushX * 0.45f;
  460. yOffset = (float)(System.Math.Cos(angle) * (0.5f - yPathScale)) * radiusScale;
  461. zOffset = (float)(System.Math.Sin(angle + pushY * 0.9f) * (0.5f - yPathScale)) * radiusScale;
  462. // next apply twist rotation to the profile layer
  463. if (twistTotal != 0.0f || twistBot != 0.0f)
  464. {
  465. Quaternion profileRot = new Quaternion(new Vertex(0.0f, 0.0f, -1.0f), twist);
  466. foreach (Vertex v in newLayer.vertices)
  467. {
  468. if (v != null)
  469. {
  470. vTemp = v * profileRot;
  471. v.X = vTemp.X;
  472. v.Y = vTemp.Y;
  473. v.Z = vTemp.Z;
  474. }
  475. }
  476. }
  477. // now orient the rotation of the profile layer relative to it's position on the path
  478. // adding pushY to the angle used to generate the quat appears to approximate the viewer
  479. Quaternion layerRot = new Quaternion(new Vertex(-1.0f, 0.0f, 0.0f), (float)angle + pushY * 0.9f);
  480. foreach (Vertex v in newLayer.vertices)
  481. {
  482. if (v != null)
  483. {
  484. vTemp = v * layerRot;
  485. v.X = vTemp.X + xOffset;
  486. v.Y = vTemp.Y + yOffset;
  487. v.Z = vTemp.Z + zOffset;
  488. }
  489. }
  490. if (angle == startAngle) // the first layer, invert normals
  491. {
  492. foreach (Triangle t in newLayer.triangles)
  493. {
  494. t.invertNormal();
  495. }
  496. }
  497. result.Append(newLayer);
  498. int iLastNull = 0;
  499. if (lastLayer != null)
  500. {
  501. int i, count = newLayer.vertices.Count;
  502. for (i = 0; i < count; i++)
  503. {
  504. int iNext = (i + 1);
  505. if (lastLayer.vertices[i] == null) // cant make a simplex here
  506. {
  507. iLastNull = i + 1;
  508. }
  509. else
  510. {
  511. if (i == count - 1) // End of list
  512. iNext = iLastNull;
  513. if (lastLayer.vertices[iNext] == null) // Null means wrap to begin of last segment
  514. iNext = iLastNull;
  515. result.Add(new Triangle(newLayer.vertices[i], lastLayer.vertices[i], newLayer.vertices[iNext]));
  516. result.Add(new Triangle(newLayer.vertices[iNext], lastLayer.vertices[i], lastLayer.vertices[iNext]));
  517. }
  518. }
  519. }
  520. lastLayer = newLayer;
  521. // calc the angle for the next interation of the loop
  522. if (angle >= endAngle)
  523. {
  524. done = true;
  525. }
  526. else
  527. {
  528. angle = stepSize * ++step;
  529. if (angle > endAngle)
  530. angle = endAngle;
  531. }
  532. } while (!done); // loop until all the layers in the path are completed
  533. // scale the mesh to the desired size
  534. float xScale = size.X;
  535. float yScale = size.Y;
  536. float zScale = size.Z;
  537. foreach (Vertex v in result.vertices)
  538. {
  539. if (v != null)
  540. {
  541. v.X *= xScale;
  542. v.Y *= yScale;
  543. v.Z *= zScale;
  544. }
  545. }
  546. return result;
  547. }
  548. }
  549. }