aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/PhysicsModules/ubOdeMeshing/PrimMesher.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/PhysicsModules/ubOdeMeshing/PrimMesher.cs')
-rw-r--r--OpenSim/Region/PhysicsModules/ubOdeMeshing/PrimMesher.cs1707
1 files changed, 1707 insertions, 0 deletions
diff --git a/OpenSim/Region/PhysicsModules/ubOdeMeshing/PrimMesher.cs b/OpenSim/Region/PhysicsModules/ubOdeMeshing/PrimMesher.cs
new file mode 100644
index 0000000..e93175f
--- /dev/null
+++ b/OpenSim/Region/PhysicsModules/ubOdeMeshing/PrimMesher.cs
@@ -0,0 +1,1707 @@
1/*
2 * Copyright (c) Contributors
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 OpenSimulator 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
28using System;
29using System.Collections.Generic;
30using System.Text;
31using System.IO;
32
33namespace PrimMesher
34{
35 public struct Quat
36 {
37 /// <summary>X value</summary>
38 public float X;
39 /// <summary>Y value</summary>
40 public float Y;
41 /// <summary>Z value</summary>
42 public float Z;
43 /// <summary>W value</summary>
44 public float W;
45
46 public Quat(float x, float y, float z, float w)
47 {
48 X = x;
49 Y = y;
50 Z = z;
51 W = w;
52 }
53
54 public Quat(Coord axis, float angle)
55 {
56 axis = axis.Normalize();
57
58 angle *= 0.5f;
59 float c = (float)Math.Cos(angle);
60 float s = (float)Math.Sin(angle);
61
62 X = axis.X * s;
63 Y = axis.Y * s;
64 Z = axis.Z * s;
65 W = c;
66
67 Normalize();
68 }
69
70 public float Length()
71 {
72 return (float)Math.Sqrt(X * X + Y * Y + Z * Z + W * W);
73 }
74
75 public Quat Normalize()
76 {
77 const float MAG_THRESHOLD = 0.0000001f;
78 float mag = Length();
79
80 // Catch very small rounding errors when normalizing
81 if (mag > MAG_THRESHOLD)
82 {
83 float oomag = 1f / mag;
84 X *= oomag;
85 Y *= oomag;
86 Z *= oomag;
87 W *= oomag;
88 }
89 else
90 {
91 X = 0f;
92 Y = 0f;
93 Z = 0f;
94 W = 1f;
95 }
96
97 return this;
98 }
99
100 public static Quat operator *(Quat q1, Quat q2)
101 {
102 float x = q1.W * q2.X + q1.X * q2.W + q1.Y * q2.Z - q1.Z * q2.Y;
103 float y = q1.W * q2.Y - q1.X * q2.Z + q1.Y * q2.W + q1.Z * q2.X;
104 float z = q1.W * q2.Z + q1.X * q2.Y - q1.Y * q2.X + q1.Z * q2.W;
105 float w = q1.W * q2.W - q1.X * q2.X - q1.Y * q2.Y - q1.Z * q2.Z;
106 return new Quat(x, y, z, w);
107 }
108
109 public override string ToString()
110 {
111 return "< X: " + this.X.ToString() + ", Y: " + this.Y.ToString() + ", Z: " + this.Z.ToString() + ", W: " + this.W.ToString() + ">";
112 }
113 }
114
115 public struct Coord
116 {
117 public float X;
118 public float Y;
119 public float Z;
120
121 public Coord(float x, float y, float z)
122 {
123 this.X = x;
124 this.Y = y;
125 this.Z = z;
126 }
127
128 public float Length()
129 {
130 return (float)Math.Sqrt(this.X * this.X + this.Y * this.Y + this.Z * this.Z);
131 }
132
133 public Coord Invert()
134 {
135 this.X = -this.X;
136 this.Y = -this.Y;
137 this.Z = -this.Z;
138
139 return this;
140 }
141
142 public Coord Normalize()
143 {
144 const float MAG_THRESHOLD = 0.0000001f;
145 float mag = Length();
146
147 // Catch very small rounding errors when normalizing
148 if (mag > MAG_THRESHOLD)
149 {
150 float oomag = 1.0f / mag;
151 this.X *= oomag;
152 this.Y *= oomag;
153 this.Z *= oomag;
154 }
155 else
156 {
157 this.X = 0.0f;
158 this.Y = 0.0f;
159 this.Z = 0.0f;
160 }
161
162 return this;
163 }
164
165 public override string ToString()
166 {
167 return this.X.ToString() + " " + this.Y.ToString() + " " + this.Z.ToString();
168 }
169
170 public static Coord Cross(Coord c1, Coord c2)
171 {
172 return new Coord(
173 c1.Y * c2.Z - c2.Y * c1.Z,
174 c1.Z * c2.X - c2.Z * c1.X,
175 c1.X * c2.Y - c2.X * c1.Y
176 );
177 }
178
179 public static Coord operator +(Coord v, Coord a)
180 {
181 return new Coord(v.X + a.X, v.Y + a.Y, v.Z + a.Z);
182 }
183
184 public static Coord operator *(Coord v, Coord m)
185 {
186 return new Coord(v.X * m.X, v.Y * m.Y, v.Z * m.Z);
187 }
188
189 public static Coord operator *(Coord v, Quat q)
190 {
191 // From http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/transforms/
192
193 Coord c2 = new Coord(0.0f, 0.0f, 0.0f);
194
195 c2.X = q.W * q.W * v.X +
196 2f * q.Y * q.W * v.Z -
197 2f * q.Z * q.W * v.Y +
198 q.X * q.X * v.X +
199 2f * q.Y * q.X * v.Y +
200 2f * q.Z * q.X * v.Z -
201 q.Z * q.Z * v.X -
202 q.Y * q.Y * v.X;
203
204 c2.Y =
205 2f * q.X * q.Y * v.X +
206 q.Y * q.Y * v.Y +
207 2f * q.Z * q.Y * v.Z +
208 2f * q.W * q.Z * v.X -
209 q.Z * q.Z * v.Y +
210 q.W * q.W * v.Y -
211 2f * q.X * q.W * v.Z -
212 q.X * q.X * v.Y;
213
214 c2.Z =
215 2f * q.X * q.Z * v.X +
216 2f * q.Y * q.Z * v.Y +
217 q.Z * q.Z * v.Z -
218 2f * q.W * q.Y * v.X -
219 q.Y * q.Y * v.Z +
220 2f * q.W * q.X * v.Y -
221 q.X * q.X * v.Z +
222 q.W * q.W * v.Z;
223
224 return c2;
225 }
226 }
227
228 public struct Face
229 {
230 public int primFace;
231
232 // vertices
233 public int v1;
234 public int v2;
235 public int v3;
236
237 public Face(int v1, int v2, int v3)
238 {
239 primFace = 0;
240
241 this.v1 = v1;
242 this.v2 = v2;
243 this.v3 = v3;
244 }
245
246 public Coord SurfaceNormal(List<Coord> coordList)
247 {
248 Coord c1 = coordList[this.v1];
249 Coord c2 = coordList[this.v2];
250 Coord c3 = coordList[this.v3];
251
252 Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z);
253 Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z);
254
255 return Coord.Cross(edge1, edge2).Normalize();
256 }
257 }
258
259 internal struct Angle
260 {
261 internal float angle;
262 internal float X;
263 internal float Y;
264
265 internal Angle(float angle, float x, float y)
266 {
267 this.angle = angle;
268 this.X = x;
269 this.Y = y;
270 }
271 }
272
273 internal class AngleList
274 {
275 private float iX, iY; // intersection point
276
277 private static Angle[] angles3 =
278 {
279 new Angle(0.0f, 1.0f, 0.0f),
280 new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f),
281 new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f),
282 new Angle(1.0f, 1.0f, 0.0f)
283 };
284
285 private static Angle[] angles4 =
286 {
287 new Angle(0.0f, 1.0f, 0.0f),
288 new Angle(0.25f, 0.0f, 1.0f),
289 new Angle(0.5f, -1.0f, 0.0f),
290 new Angle(0.75f, 0.0f, -1.0f),
291 new Angle(1.0f, 1.0f, 0.0f)
292 };
293
294 private static Angle[] angles6 =
295 {
296 new Angle(0.0f, 1.0f, 0.0f),
297 new Angle(0.16666666666666667f, 0.5f, 0.8660254037844386f),
298 new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f),
299 new Angle(0.5f, -1.0f, 0.0f),
300 new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f),
301 new Angle(0.83333333333333326f, 0.5f, -0.86602540378443904f),
302 new Angle(1.0f, 1.0f, 0.0f)
303 };
304
305 private static Angle[] angles12 =
306 {
307 new Angle(0.0f, 1.0f, 0.0f),
308 new Angle(0.083333333333333329f, 0.86602540378443871f, 0.5f),
309 new Angle(0.16666666666666667f, 0.5f, 0.8660254037844386f),
310 new Angle(0.25f, 0.0f, 1.0f),
311 new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f),
312 new Angle(0.41666666666666663f, -0.86602540378443849f, 0.5f),
313 new Angle(0.5f, -1.0f, 0.0f),
314 new Angle(0.58333333333333326f, -0.86602540378443882f, -0.5f),
315 new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f),
316 new Angle(0.75f, 0.0f, -1.0f),
317 new Angle(0.83333333333333326f, 0.5f, -0.86602540378443904f),
318 new Angle(0.91666666666666663f, 0.86602540378443837f, -0.5f),
319 new Angle(1.0f, 1.0f, 0.0f)
320 };
321
322 private static Angle[] angles24 =
323 {
324 new Angle(0.0f, 1.0f, 0.0f),
325 new Angle(0.041666666666666664f, 0.96592582628906831f, 0.25881904510252074f),
326 new Angle(0.083333333333333329f, 0.86602540378443871f, 0.5f),
327 new Angle(0.125f, 0.70710678118654757f, 0.70710678118654746f),
328 new Angle(0.16666666666666667f, 0.5f, 0.8660254037844386f),
329 new Angle(0.20833333333333331f, 0.25881904510252096f, 0.9659258262890682f),
330 new Angle(0.25f, 0.0f, 1.0f),
331 new Angle(0.29166666666666663f, -0.25881904510252063f, 0.96592582628906831f),
332 new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f),
333 new Angle(0.375f, -0.70710678118654746f, 0.70710678118654757f),
334 new Angle(0.41666666666666663f, -0.86602540378443849f, 0.5f),
335 new Angle(0.45833333333333331f, -0.9659258262890682f, 0.25881904510252102f),
336 new Angle(0.5f, -1.0f, 0.0f),
337 new Angle(0.54166666666666663f, -0.96592582628906842f, -0.25881904510252035f),
338 new Angle(0.58333333333333326f, -0.86602540378443882f, -0.5f),
339 new Angle(0.62499999999999989f, -0.70710678118654791f, -0.70710678118654713f),
340 new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f),
341 new Angle(0.70833333333333326f, -0.25881904510252152f, -0.96592582628906809f),
342 new Angle(0.75f, 0.0f, -1.0f),
343 new Angle(0.79166666666666663f, 0.2588190451025203f, -0.96592582628906842f),
344 new Angle(0.83333333333333326f, 0.5f, -0.86602540378443904f),
345 new Angle(0.875f, 0.70710678118654735f, -0.70710678118654768f),
346 new Angle(0.91666666666666663f, 0.86602540378443837f, -0.5f),
347 new Angle(0.95833333333333326f, 0.96592582628906809f, -0.25881904510252157f),
348 new Angle(1.0f, 1.0f, 0.0f)
349 };
350
351 private Angle interpolatePoints(float newPoint, Angle p1, Angle p2)
352 {
353 float m = (newPoint - p1.angle) / (p2.angle - p1.angle);
354 return new Angle(newPoint, p1.X + m * (p2.X - p1.X), p1.Y + m * (p2.Y - p1.Y));
355 }
356
357 private void intersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
358 { // ref: http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
359 double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
360 double uaNumerator = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3);
361
362 if (denom != 0.0)
363 {
364 double ua = uaNumerator / denom;
365 iX = (float)(x1 + ua * (x2 - x1));
366 iY = (float)(y1 + ua * (y2 - y1));
367 }
368 }
369
370 internal List<Angle> angles;
371
372 internal void makeAngles(int sides, float startAngle, float stopAngle, bool hasCut)
373 {
374 angles = new List<Angle>();
375
376 const double twoPi = System.Math.PI * 2.0;
377 const float twoPiInv = (float)(1.0d / twoPi);
378
379 if (sides < 1)
380 throw new Exception("number of sides not greater than zero");
381 if (stopAngle <= startAngle)
382 throw new Exception("stopAngle not greater than startAngle");
383
384 if ((sides == 3 || sides == 4 || sides == 6 || sides == 12 || sides == 24))
385 {
386 startAngle *= twoPiInv;
387 stopAngle *= twoPiInv;
388
389 Angle[] sourceAngles;
390 switch (sides)
391 {
392 case 3:
393 sourceAngles = angles3;
394 break;
395 case 4:
396 sourceAngles = angles4;
397 break;
398 case 6:
399 sourceAngles = angles6;
400 break;
401 case 12:
402 sourceAngles = angles12;
403 break;
404 default:
405 sourceAngles = angles24;
406 break;
407 }
408
409 int startAngleIndex = (int)(startAngle * sides);
410 int endAngleIndex = sourceAngles.Length - 1;
411
412 if (hasCut)
413 {
414 if (stopAngle < 1.0f)
415 endAngleIndex = (int)(stopAngle * sides) + 1;
416 if (endAngleIndex == startAngleIndex)
417 endAngleIndex++;
418
419 for (int angleIndex = startAngleIndex; angleIndex < endAngleIndex + 1; angleIndex++)
420 {
421 angles.Add(sourceAngles[angleIndex]);
422 }
423
424 if (startAngle > 0.0f)
425 angles[0] = interpolatePoints(startAngle, angles[0], angles[1]);
426
427 if (stopAngle < 1.0f)
428 {
429 int lastAngleIndex = angles.Count - 1;
430 angles[lastAngleIndex] = interpolatePoints(stopAngle, angles[lastAngleIndex - 1], angles[lastAngleIndex]);
431 }
432 }
433 else
434 {
435 for (int angleIndex = startAngleIndex; angleIndex < endAngleIndex; angleIndex++)
436 angles.Add(sourceAngles[angleIndex]);
437 }
438 }
439 else
440 {
441 double stepSize = twoPi / sides;
442
443 int startStep = (int)(startAngle / stepSize);
444 double angle = stepSize * startStep;
445 int step = startStep;
446 double stopAngleTest = stopAngle;
447 if (stopAngle < twoPi)
448 {
449 stopAngleTest = stepSize * ((int)(stopAngle / stepSize) + 1);
450 if (stopAngleTest < stopAngle)
451 stopAngleTest += stepSize;
452 if (stopAngleTest > twoPi)
453 stopAngleTest = twoPi;
454 }
455
456 while (angle <= stopAngleTest)
457 {
458 Angle newAngle;
459 newAngle.angle = (float)angle;
460 newAngle.X = (float)System.Math.Cos(angle);
461 newAngle.Y = (float)System.Math.Sin(angle);
462 angles.Add(newAngle);
463 step += 1;
464 angle = stepSize * step;
465 }
466
467 if (startAngle > angles[0].angle)
468 {
469 Angle newAngle;
470 intersection(angles[0].X, angles[0].Y, angles[1].X, angles[1].Y, 0.0f, 0.0f, (float)Math.Cos(startAngle), (float)Math.Sin(startAngle));
471 newAngle.angle = startAngle;
472 newAngle.X = iX;
473 newAngle.Y = iY;
474 angles[0] = newAngle;
475 }
476
477 int index = angles.Count - 1;
478 if (stopAngle < angles[index].angle)
479 {
480 Angle newAngle;
481 intersection(angles[index - 1].X, angles[index - 1].Y, angles[index].X, angles[index].Y, 0.0f, 0.0f, (float)Math.Cos(stopAngle), (float)Math.Sin(stopAngle));
482 newAngle.angle = stopAngle;
483 newAngle.X = iX;
484 newAngle.Y = iY;
485 angles[index] = newAngle;
486 }
487 }
488 }
489 }
490
491 /// <summary>
492 /// generates a profile for extrusion
493 /// </summary>
494 public class Profile
495 {
496 private const float twoPi = 2.0f * (float)Math.PI;
497
498 public string errorMessage = null;
499
500 public List<Coord> coords;
501 public List<Face> faces;
502
503 // use these for making individual meshes for each prim face
504 public List<int> outerCoordIndices = null;
505 public List<int> hollowCoordIndices = null;
506
507 public int numOuterVerts = 0;
508 public int numHollowVerts = 0;
509
510 public int outerFaceNumber = -1;
511 public int hollowFaceNumber = -1;
512
513 public int bottomFaceNumber = 0;
514 public int numPrimFaces = 0;
515
516 public Profile()
517 {
518 coords = new List<Coord>();
519 faces = new List<Face>();
520 }
521
522 public Profile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides, bool hasProfileCut, bool createFaces)
523 {
524 const float halfSqr2 = 0.7071067811866f;
525
526 coords = new List<Coord>();
527 faces = new List<Face>();
528
529 List<Coord> hollowCoords = new List<Coord>();
530
531 bool hasHollow = (hollow > 0.0f);
532
533 AngleList angles = new AngleList();
534 AngleList hollowAngles = new AngleList();
535
536 float xScale = 0.5f;
537 float yScale = 0.5f;
538 if (sides == 4) // corners of a square are sqrt(2) from center
539 {
540 xScale = halfSqr2;
541 yScale = halfSqr2;
542 }
543
544 float startAngle = profileStart * twoPi;
545 float stopAngle = profileEnd * twoPi;
546
547 try { angles.makeAngles(sides, startAngle, stopAngle,hasProfileCut); }
548 catch (Exception ex)
549 {
550
551 errorMessage = "makeAngles failed: Exception: " + ex.ToString()
552 + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString();
553
554 return;
555 }
556
557 numOuterVerts = angles.angles.Count;
558
559 Angle angle;
560 Coord newVert = new Coord();
561
562 // flag to create as few triangles as possible for 3 or 4 side profile
563 bool simpleFace = (sides < 5 && !hasHollow && !hasProfileCut);
564
565 if (hasHollow)
566 {
567 if (sides == hollowSides)
568 hollowAngles = angles;
569 else
570 {
571 try { hollowAngles.makeAngles(hollowSides, startAngle, stopAngle, hasProfileCut); }
572 catch (Exception ex)
573 {
574 errorMessage = "makeAngles failed: Exception: " + ex.ToString()
575 + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString();
576
577 return;
578 }
579
580 int numHollowAngles = hollowAngles.angles.Count;
581 for (int i = 0; i < numHollowAngles; i++)
582 {
583 angle = hollowAngles.angles[i];
584 newVert.X = hollow * xScale * angle.X;
585 newVert.Y = hollow * yScale * angle.Y;
586 newVert.Z = 0.0f;
587
588 hollowCoords.Add(newVert);
589 }
590 }
591 numHollowVerts = hollowAngles.angles.Count;
592 }
593 else if (!simpleFace)
594 {
595 Coord center = new Coord(0.0f, 0.0f, 0.0f);
596 this.coords.Add(center);
597 }
598
599 int numAngles = angles.angles.Count;
600 bool hollowsame = (hasHollow && hollowSides == sides);
601
602 for (int i = 0; i < numAngles; i++)
603 {
604 angle = angles.angles[i];
605 newVert.X = angle.X * xScale;
606 newVert.Y = angle.Y * yScale;
607 newVert.Z = 0.0f;
608 coords.Add(newVert);
609 if (hollowsame)
610 {
611 newVert.X *= hollow;
612 newVert.Y *= hollow;
613 hollowCoords.Add(newVert);
614 }
615 }
616
617 if (hasHollow)
618 {
619 hollowCoords.Reverse();
620 coords.AddRange(hollowCoords);
621
622 if (createFaces)
623 {
624 int numTotalVerts = numOuterVerts + numHollowVerts;
625
626 if (numOuterVerts == numHollowVerts)
627 {
628 Face newFace = new Face();
629
630 for (int coordIndex = 0; coordIndex < numOuterVerts - 1; coordIndex++)
631 {
632 newFace.v1 = coordIndex;
633 newFace.v2 = coordIndex + 1;
634 newFace.v3 = numTotalVerts - coordIndex - 1;
635 faces.Add(newFace);
636
637 newFace.v1 = coordIndex + 1;
638 newFace.v2 = numTotalVerts - coordIndex - 2;
639 newFace.v3 = numTotalVerts - coordIndex - 1;
640 faces.Add(newFace);
641 }
642 if (!hasProfileCut)
643 {
644 newFace.v1 = numOuterVerts - 1;
645 newFace.v2 = 0;
646 newFace.v3 = numOuterVerts;
647 faces.Add(newFace);
648
649 newFace.v1 = 0;
650 newFace.v2 = numTotalVerts - 1;
651 newFace.v3 = numOuterVerts;
652 faces.Add(newFace);
653 }
654 }
655 else if (numOuterVerts < numHollowVerts)
656 {
657 Face newFace = new Face();
658 int j = 0; // j is the index for outer vertices
659 int i;
660 int maxJ = numOuterVerts - 1;
661 float curHollowAngle = 0;
662 for (i = 0; i < numHollowVerts; i++) // i is the index for inner vertices
663 {
664 curHollowAngle = hollowAngles.angles[i].angle;
665 if (j < maxJ)
666 {
667 if (angles.angles[j + 1].angle - curHollowAngle < curHollowAngle - angles.angles[j].angle + 0.000001f)
668 {
669 newFace.v1 = numTotalVerts - i - 1;
670 newFace.v2 = j;
671 newFace.v3 = j + 1;
672 faces.Add(newFace);
673 j++;
674 }
675 }
676 else
677 {
678 if (1.0f - curHollowAngle < curHollowAngle - angles.angles[j].angle + 0.000001f)
679 break;
680 }
681
682 newFace.v1 = j;
683 newFace.v2 = numTotalVerts - i - 2;
684 newFace.v3 = numTotalVerts - i - 1;
685
686 faces.Add(newFace);
687 }
688
689 if (!hasProfileCut)
690 {
691 if (i == numHollowVerts)
692 {
693 newFace.v1 = numTotalVerts - numHollowVerts;
694 newFace.v2 = maxJ;
695 newFace.v3 = 0;
696
697 faces.Add(newFace);
698 }
699 else
700 {
701 if (1.0f - curHollowAngle < curHollowAngle - angles.angles[maxJ].angle + 0.000001f)
702 {
703 newFace.v1 = numTotalVerts - i - 1;
704 newFace.v2 = maxJ;
705 newFace.v3 = 0;
706
707 faces.Add(newFace);
708 }
709
710 for (; i < numHollowVerts - 1; i++)
711 {
712 newFace.v1 = 0;
713 newFace.v2 = numTotalVerts - i - 2;
714 newFace.v3 = numTotalVerts - i - 1;
715
716 faces.Add(newFace);
717 }
718 }
719
720 newFace.v1 = 0;
721 newFace.v2 = numTotalVerts - 1;
722 newFace.v3 = numTotalVerts - numHollowVerts;
723 faces.Add(newFace);
724 }
725 }
726 else // numHollowVerts < numOuterVerts
727 {
728 Face newFace = new Face();
729 int j = 0; // j is the index for inner vertices
730 int maxJ = numHollowVerts - 1;
731 for (int i = 0; i < numOuterVerts; i++)
732 {
733 if (j < maxJ)
734 if (hollowAngles.angles[j + 1].angle - angles.angles[i].angle < angles.angles[i].angle - hollowAngles.angles[j].angle + 0.000001f)
735 {
736 newFace.v1 = i;
737 newFace.v2 = numTotalVerts - j - 2;
738 newFace.v3 = numTotalVerts - j - 1;
739
740 faces.Add(newFace);
741 j += 1;
742 }
743
744 newFace.v1 = numTotalVerts - j - 1;
745 newFace.v2 = i;
746 newFace.v3 = i + 1;
747
748 faces.Add(newFace);
749 }
750
751 if (!hasProfileCut)
752 {
753 int i = numOuterVerts - 1;
754
755 if (hollowAngles.angles[0].angle - angles.angles[i].angle < angles.angles[i].angle - hollowAngles.angles[maxJ].angle + 0.000001f)
756 {
757 newFace.v1 = 0;
758 newFace.v2 = numTotalVerts - 1;
759 newFace.v3 = numTotalVerts - maxJ - 1;
760
761 faces.Add(newFace);
762 }
763
764 newFace.v1 = numTotalVerts - maxJ - 1;
765 newFace.v2 = i;
766 newFace.v3 = 0;
767
768 faces.Add(newFace);
769 }
770 }
771 }
772
773 }
774
775 else if (createFaces)
776 {
777 if (simpleFace)
778 {
779 if (sides == 3)
780 faces.Add(new Face(0, 1, 2));
781 else if (sides == 4)
782 {
783 faces.Add(new Face(0, 1, 2));
784 faces.Add(new Face(0, 2, 3));
785 }
786 }
787 else
788 {
789 for (int i = 1; i < numAngles ; i++)
790 {
791 Face newFace = new Face();
792 newFace.v1 = 0;
793 newFace.v2 = i;
794 newFace.v3 = i + 1;
795 faces.Add(newFace);
796 }
797 if (!hasProfileCut)
798 {
799 Face newFace = new Face();
800 newFace.v1 = 0;
801 newFace.v2 = numAngles;
802 newFace.v3 = 1;
803 faces.Add(newFace);
804 }
805 }
806 }
807
808
809 hollowCoords = null;
810 }
811
812
813 public Profile Copy()
814 {
815 return Copy(true);
816 }
817
818 public Profile Copy(bool needFaces)
819 {
820 Profile copy = new Profile();
821
822 copy.coords.AddRange(coords);
823
824 if (needFaces)
825 copy.faces.AddRange(faces);
826
827 copy.numOuterVerts = numOuterVerts;
828 copy.numHollowVerts = numHollowVerts;
829
830 return copy;
831 }
832
833 public void AddPos(Coord v)
834 {
835 this.AddPos(v.X, v.Y, v.Z);
836 }
837
838 public void AddPos(float x, float y, float z)
839 {
840 int i;
841 int numVerts = coords.Count;
842 Coord vert;
843
844 for (i = 0; i < numVerts; i++)
845 {
846 vert = coords[i];
847 vert.X += x;
848 vert.Y += y;
849 vert.Z += z;
850 this.coords[i] = vert;
851 }
852 }
853
854 public void AddRot(Quat q)
855 {
856 int i;
857 int numVerts = coords.Count;
858
859 for (i = 0; i < numVerts; i++)
860 coords[i] *= q;
861 }
862
863 public void Scale(float x, float y)
864 {
865 int i;
866 int numVerts = coords.Count;
867 Coord vert;
868
869 for (i = 0; i < numVerts; i++)
870 {
871 vert = coords[i];
872 vert.X *= x;
873 vert.X = (float)Math.Round(vert.X,5);
874 vert.Y *= y;
875 vert.Y = (float)Math.Round(vert.Y,5);
876 coords[i] = vert;
877 }
878
879 if(x == 0f || y == 0f)
880 faces = new List<Face>();
881 }
882
883 /// <summary>
884 /// Changes order of the vertex indices and negates the center vertex normal. Does not alter vertex normals of radial vertices
885 /// </summary>
886 public void FlipNormals()
887 {
888 int numFaces = faces.Count;
889 if(numFaces == 0)
890 return;
891
892 int i;
893 Face tmpFace;
894 int tmp;
895
896 for (i = 0; i < numFaces; i++)
897 {
898 tmpFace = faces[i];
899 tmp = tmpFace.v3;
900 tmpFace.v3 = tmpFace.v1;
901 tmpFace.v1 = tmp;
902 faces[i] = tmpFace;
903 }
904 }
905
906 public void AddValue2FaceVertexIndices(int num)
907 {
908 int numFaces = faces.Count;
909 if(numFaces == 0)
910 return;
911
912 Face tmpFace;
913
914 for (int i = 0; i < numFaces; i++)
915 {
916 tmpFace = faces[i];
917 tmpFace.v1 += num;
918 tmpFace.v2 += num;
919 tmpFace.v3 += num;
920
921 faces[i] = tmpFace;
922 }
923 }
924
925 public void DumpRaw(String path, String name, String title)
926 {
927 if (path == null)
928 return;
929 String fileName = name + "_" + title + ".raw";
930 String completePath = System.IO.Path.Combine(path, fileName);
931 StreamWriter sw = new StreamWriter(completePath);
932
933 for (int i = 0; i < faces.Count; i++)
934 {
935 string s = coords[faces[i].v1].ToString();
936 s += " " + coords[faces[i].v2].ToString();
937 s += " " + coords[faces[i].v3].ToString();
938
939 sw.WriteLine(s);
940 }
941
942 sw.Close();
943 }
944 }
945
946 public struct PathNode
947 {
948 public Coord position;
949 public Quat rotation;
950 public float xScale;
951 public float yScale;
952 public float percentOfPath;
953 }
954
955 public enum PathType { Linear = 0, Circular = 1, Flexible = 2 }
956
957 public class Path
958 {
959 public List<PathNode> pathNodes = new List<PathNode>();
960
961 public float twistBegin = 0.0f;
962 public float twistEnd = 0.0f;
963 public float topShearX = 0.0f;
964 public float topShearY = 0.0f;
965 public float pathCutBegin = 0.0f;
966 public float pathCutEnd = 1.0f;
967 public float dimpleBegin = 0.0f;
968 public float dimpleEnd = 1.0f;
969 public float skew = 0.0f;
970 public float holeSizeX = 1.0f; // called pathScaleX in pbs
971 public float holeSizeY = 0.25f;
972 public float taperX = 0.0f;
973 public float taperY = 0.0f;
974 public float radius = 0.0f;
975 public float revolutions = 1.0f;
976 public int stepsPerRevolution = 24;
977
978 private const float twoPi = 2.0f * (float)Math.PI;
979
980 public void Create(PathType pathType, int steps)
981 {
982 if (taperX > .9999f)
983 taperX = 1.0f;
984 else if (taperX < -.9999f)
985 taperX = -1.0f;
986 if (taperY > .9999f)
987 taperY = 1.0f;
988 else if (taperY < -.9999f)
989 taperY = -1.0f;
990
991 if (pathType == PathType.Linear || pathType == PathType.Flexible)
992 {
993 int step = 0;
994
995 float length = pathCutEnd - pathCutBegin;
996 float twistTotal = twistEnd - twistBegin;
997 float twistTotalAbs = Math.Abs(twistTotal);
998 if (twistTotalAbs > 0.01f)
999 steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number
1000
1001 float start = -0.5f;
1002 float stepSize = length / (float)steps;
1003 float percentOfPathMultiplier = stepSize * 0.999999f;
1004 float xOffset = topShearX * pathCutBegin;
1005 float yOffset = topShearY * pathCutBegin;
1006 float zOffset = start;
1007 float xOffsetStepIncrement = topShearX * length / steps;
1008 float yOffsetStepIncrement = topShearY * length / steps;
1009
1010 float percentOfPath = pathCutBegin;
1011 zOffset += percentOfPath;
1012
1013 // sanity checks
1014
1015 bool done = false;
1016
1017 while (!done)
1018 {
1019 PathNode newNode = new PathNode();
1020
1021 newNode.xScale = 1.0f;
1022 if (taperX > 0.0f)
1023 newNode.xScale -= percentOfPath * taperX;
1024 else if(taperX < 0.0f)
1025 newNode.xScale += (1.0f - percentOfPath) * taperX;
1026
1027 newNode.yScale = 1.0f;
1028 if (taperY > 0.0f)
1029 newNode.yScale -= percentOfPath * taperY;
1030 else if(taperY < 0.0f)
1031 newNode.yScale += (1.0f - percentOfPath) * taperY;
1032
1033 float twist = twistBegin + twistTotal * percentOfPath;
1034
1035 newNode.rotation = new Quat(new Coord(0.0f, 0.0f, 1.0f), twist);
1036 newNode.position = new Coord(xOffset, yOffset, zOffset);
1037 newNode.percentOfPath = percentOfPath;
1038
1039 pathNodes.Add(newNode);
1040
1041 if (step < steps)
1042 {
1043 step += 1;
1044 percentOfPath += percentOfPathMultiplier;
1045 xOffset += xOffsetStepIncrement;
1046 yOffset += yOffsetStepIncrement;
1047 zOffset += stepSize;
1048 if (percentOfPath > pathCutEnd)
1049 done = true;
1050 }
1051 else done = true;
1052 }
1053 } // end of linear path code
1054
1055 else // pathType == Circular
1056 {
1057 float twistTotal = twistEnd - twistBegin;
1058
1059 // if the profile has a lot of twist, add more layers otherwise the layers may overlap
1060 // and the resulting mesh may be quite inaccurate. This method is arbitrary and doesn't
1061 // accurately match the viewer
1062 float twistTotalAbs = Math.Abs(twistTotal);
1063 if (twistTotalAbs > 0.01f)
1064 {
1065 if (twistTotalAbs > Math.PI * 1.5f)
1066 steps *= 2;
1067 if (twistTotalAbs > Math.PI * 3.0f)
1068 steps *= 2;
1069 }
1070
1071 float yPathScale = holeSizeY * 0.5f;
1072 float pathLength = pathCutEnd - pathCutBegin;
1073 float totalSkew = skew * 2.0f * pathLength;
1074 float skewStart = pathCutBegin * 2.0f * skew - skew;
1075 float xOffsetTopShearXFactor = topShearX * (0.25f + 0.5f * (0.5f - holeSizeY));
1076 float yShearCompensation = 1.0f + Math.Abs(topShearY) * 0.25f;
1077
1078 // It's not quite clear what pushY (Y top shear) does, but subtracting it from the start and end
1079 // angles appears to approximate it's effects on path cut. Likewise, adding it to the angle used
1080 // to calculate the sine for generating the path radius appears to approximate it's effects there
1081 // too, but there are some subtle differences in the radius which are noticeable as the prim size
1082 // increases and it may affect megaprims quite a bit. The effect of the Y top shear parameter on
1083 // the meshes generated with this technique appear nearly identical in shape to the same prims when
1084 // displayed by the viewer.
1085
1086 float startAngle = (twoPi * pathCutBegin * revolutions) - topShearY * 0.9f;
1087 float endAngle = (twoPi * pathCutEnd * revolutions) - topShearY * 0.9f;
1088 float stepSize = twoPi / stepsPerRevolution;
1089
1090 int step = (int)(startAngle / stepSize);
1091 float angle = startAngle;
1092
1093 bool done = false;
1094 while (!done) // loop through the length of the path and add the layers
1095 {
1096 PathNode newNode = new PathNode();
1097
1098 float xProfileScale = (1.0f - Math.Abs(skew)) * holeSizeX;
1099 float yProfileScale = holeSizeY;
1100
1101 float percentOfPath = angle / (twoPi * revolutions);
1102 float percentOfAngles = (angle - startAngle) / (endAngle - startAngle);
1103
1104 if (taperX > 0.01f)
1105 xProfileScale *= 1.0f - percentOfPath * taperX;
1106 else if (taperX < -0.01f)
1107 xProfileScale *= 1.0f + (1.0f - percentOfPath) * taperX;
1108
1109 if (taperY > 0.01f)
1110 yProfileScale *= 1.0f - percentOfPath * taperY;
1111 else if (taperY < -0.01f)
1112 yProfileScale *= 1.0f + (1.0f - percentOfPath) * taperY;
1113
1114 newNode.xScale = xProfileScale;
1115 newNode.yScale = yProfileScale;
1116
1117 float radiusScale = 1.0f;
1118 if (radius > 0.001f)
1119 radiusScale = 1.0f - radius * percentOfPath;
1120 else if (radius < 0.001f)
1121 radiusScale = 1.0f + radius * (1.0f - percentOfPath);
1122
1123 float twist = twistBegin + twistTotal * percentOfPath;
1124
1125 float xOffset = 0.5f * (skewStart + totalSkew * percentOfAngles);
1126 xOffset += (float)Math.Sin(angle) * xOffsetTopShearXFactor;
1127
1128 float yOffset = yShearCompensation * (float)Math.Cos(angle) * (0.5f - yPathScale) * radiusScale;
1129
1130 float zOffset = (float)Math.Sin(angle + topShearY) * (0.5f - yPathScale) * radiusScale;
1131
1132 newNode.position = new Coord(xOffset, yOffset, zOffset);
1133
1134 // now orient the rotation of the profile layer relative to it's position on the path
1135 // adding taperY to the angle used to generate the quat appears to approximate the viewer
1136
1137 newNode.rotation = new Quat(new Coord(1.0f, 0.0f, 0.0f), angle + topShearY);
1138
1139 // next apply twist rotation to the profile layer
1140 if (twistTotal != 0.0f || twistBegin != 0.0f)
1141 newNode.rotation *= new Quat(new Coord(0.0f, 0.0f, 1.0f), twist);
1142
1143 newNode.percentOfPath = percentOfPath;
1144
1145 pathNodes.Add(newNode);
1146
1147 // calculate terms for next iteration
1148 // calculate the angle for the next iteration of the loop
1149
1150 if (angle >= endAngle - 0.01)
1151 done = true;
1152 else
1153 {
1154 step += 1;
1155 angle = stepSize * step;
1156 if (angle > endAngle)
1157 angle = endAngle;
1158 }
1159 }
1160 }
1161 }
1162 }
1163
1164 public class PrimMesh
1165 {
1166 public string errorMessage = "";
1167 private const float twoPi = 2.0f * (float)Math.PI;
1168
1169 public List<Coord> coords;
1170// public List<Coord> normals;
1171 public List<Face> faces;
1172
1173 private int sides = 4;
1174 private int hollowSides = 4;
1175 private float profileStart = 0.0f;
1176 private float profileEnd = 1.0f;
1177 private float hollow = 0.0f;
1178 public int twistBegin = 0;
1179 public int twistEnd = 0;
1180 public float topShearX = 0.0f;
1181 public float topShearY = 0.0f;
1182 public float pathCutBegin = 0.0f;
1183 public float pathCutEnd = 1.0f;
1184 public float dimpleBegin = 0.0f;
1185 public float dimpleEnd = 1.0f;
1186 public float skew = 0.0f;
1187 public float holeSizeX = 1.0f; // called pathScaleX in pbs
1188 public float holeSizeY = 0.25f;
1189 public float taperX = 0.0f;
1190 public float taperY = 0.0f;
1191 public float radius = 0.0f;
1192 public float revolutions = 1.0f;
1193 public int stepsPerRevolution = 24;
1194
1195 private bool hasProfileCut = false;
1196 private bool hasHollow = false;
1197
1198 public int numPrimFaces = 0;
1199
1200 /// <summary>
1201 /// Human readable string representation of the parameters used to create a mesh.
1202 /// </summary>
1203 /// <returns></returns>
1204 public string ParamsToDisplayString()
1205 {
1206 string s = "";
1207 s += "sides..................: " + this.sides.ToString();
1208 s += "\nhollowSides..........: " + this.hollowSides.ToString();
1209 s += "\nprofileStart.........: " + this.profileStart.ToString();
1210 s += "\nprofileEnd...........: " + this.profileEnd.ToString();
1211 s += "\nhollow...............: " + this.hollow.ToString();
1212 s += "\ntwistBegin...........: " + this.twistBegin.ToString();
1213 s += "\ntwistEnd.............: " + this.twistEnd.ToString();
1214 s += "\ntopShearX............: " + this.topShearX.ToString();
1215 s += "\ntopShearY............: " + this.topShearY.ToString();
1216 s += "\npathCutBegin.........: " + this.pathCutBegin.ToString();
1217 s += "\npathCutEnd...........: " + this.pathCutEnd.ToString();
1218 s += "\ndimpleBegin..........: " + this.dimpleBegin.ToString();
1219 s += "\ndimpleEnd............: " + this.dimpleEnd.ToString();
1220 s += "\nskew.................: " + this.skew.ToString();
1221 s += "\nholeSizeX............: " + this.holeSizeX.ToString();
1222 s += "\nholeSizeY............: " + this.holeSizeY.ToString();
1223 s += "\ntaperX...............: " + this.taperX.ToString();
1224 s += "\ntaperY...............: " + this.taperY.ToString();
1225 s += "\nradius...............: " + this.radius.ToString();
1226 s += "\nrevolutions..........: " + this.revolutions.ToString();
1227 s += "\nstepsPerRevolution...: " + this.stepsPerRevolution.ToString();
1228 s += "\nhasProfileCut........: " + this.hasProfileCut.ToString();
1229 s += "\nhasHollow............: " + this.hasHollow.ToString();
1230
1231 return s;
1232 }
1233
1234 public bool HasProfileCut
1235 {
1236 get { return hasProfileCut; }
1237 set { hasProfileCut = value; }
1238 }
1239
1240 public bool HasHollow
1241 {
1242 get { return hasHollow; }
1243 }
1244
1245
1246 /// <summary>
1247 /// Constructs a PrimMesh object and creates the profile for extrusion.
1248 /// </summary>
1249 /// <param name="sides"></param>
1250 /// <param name="profileStart"></param>
1251 /// <param name="profileEnd"></param>
1252 /// <param name="hollow"></param>
1253 /// <param name="hollowSides"></param>
1254 /// <param name="sphereMode"></param>
1255 public PrimMesh(int _sides, float _profileStart, float _profileEnd, float _hollow, int _hollowSides)
1256 {
1257 coords = new List<Coord>();
1258 faces = new List<Face>();
1259
1260 sides = _sides;
1261 profileStart = _profileStart;
1262 profileEnd = _profileEnd;
1263 hollow = _hollow;
1264 hollowSides = _hollowSides;
1265
1266 if (sides < 3)
1267 sides = 3;
1268 if (hollowSides < 3)
1269 hollowSides = 3;
1270 if (profileStart < 0.0f)
1271 profileStart = 0.0f;
1272 if (profileEnd > 1.0f)
1273 profileEnd = 1.0f;
1274 if (profileEnd < 0.02f)
1275 profileEnd = 0.02f;
1276 if (profileStart >= profileEnd)
1277 profileStart = profileEnd - 0.02f;
1278 if (hollow > 0.99f)
1279 hollow = 0.99f;
1280 if (hollow < 0.0f)
1281 hollow = 0.0f;
1282 }
1283
1284 /// <summary>
1285 /// Extrudes a profile along a path.
1286 /// </summary>
1287 public void Extrude(PathType pathType)
1288 {
1289 bool needEndFaces = false;
1290
1291 coords = new List<Coord>();
1292 faces = new List<Face>();
1293
1294 int steps = 1;
1295
1296 float length = pathCutEnd - pathCutBegin;
1297
1298 hasProfileCut = this.profileEnd - this.profileStart < 0.9999f;
1299
1300 hasHollow = (this.hollow > 0.001f);
1301
1302 float twistBegin = this.twistBegin / 360.0f * twoPi;
1303 float twistEnd = this.twistEnd / 360.0f * twoPi;
1304 float twistTotal = twistEnd - twistBegin;
1305 float twistTotalAbs = Math.Abs(twistTotal);
1306 if (twistTotalAbs > 0.01f)
1307 steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number
1308
1309 float hollow = this.hollow;
1310 float initialProfileRot = 0.0f;
1311
1312 if (pathType == PathType.Circular)
1313 {
1314 needEndFaces = false;
1315 if (pathCutBegin != 0.0f || pathCutEnd != 1.0f)
1316 needEndFaces = true;
1317 else if (taperX != 0.0f || taperY != 0.0f)
1318 needEndFaces = true;
1319 else if (skew != 0.0f)
1320 needEndFaces = true;
1321 else if (twistTotal != 0.0f)
1322 needEndFaces = true;
1323 else if (radius != 0.0f)
1324 needEndFaces = true;
1325 }
1326 else needEndFaces = true;
1327
1328 if (pathType == PathType.Circular)
1329 {
1330 if (sides == 3)
1331 {
1332 initialProfileRot = (float)Math.PI;
1333 if (hollowSides == 4)
1334 {
1335 if (hollow > 0.7f)
1336 hollow = 0.7f;
1337 hollow *= 0.707f;
1338 }
1339 else hollow *= 0.5f;
1340 }
1341 else if (sides == 4)
1342 {
1343 initialProfileRot = 0.25f * (float)Math.PI;
1344 if (hollowSides != 4)
1345 hollow *= 0.707f;
1346 }
1347 else if (sides > 4)
1348 {
1349 initialProfileRot = (float)Math.PI;
1350 if (hollowSides == 4)
1351 {
1352 if (hollow > 0.7f)
1353 hollow = 0.7f;
1354 hollow /= 0.7f;
1355 }
1356 }
1357 }
1358 else
1359 {
1360 if (sides == 3)
1361 {
1362 if (hollowSides == 4)
1363 {
1364 if (hollow > 0.7f)
1365 hollow = 0.7f;
1366 hollow *= 0.707f;
1367 }
1368 else hollow *= 0.5f;
1369 }
1370 else if (sides == 4)
1371 {
1372 initialProfileRot = 1.25f * (float)Math.PI;
1373 if (hollowSides != 4)
1374 hollow *= 0.707f;
1375 }
1376 else if (sides == 24 && hollowSides == 4)
1377 hollow *= 1.414f;
1378 }
1379
1380 Profile profile = new Profile(sides, profileStart, profileEnd, hollow, hollowSides,
1381 HasProfileCut,true);
1382 errorMessage = profile.errorMessage;
1383
1384 numPrimFaces = profile.numPrimFaces;
1385
1386 if (initialProfileRot != 0.0f)
1387 {
1388 profile.AddRot(new Quat(new Coord(0.0f, 0.0f, 1.0f), initialProfileRot));
1389 }
1390
1391 Path path = new Path();
1392 path.twistBegin = twistBegin;
1393 path.twistEnd = twistEnd;
1394 path.topShearX = topShearX;
1395 path.topShearY = topShearY;
1396 path.pathCutBegin = pathCutBegin;
1397 path.pathCutEnd = pathCutEnd;
1398 path.dimpleBegin = dimpleBegin;
1399 path.dimpleEnd = dimpleEnd;
1400 path.skew = skew;
1401 path.holeSizeX = holeSizeX;
1402 path.holeSizeY = holeSizeY;
1403 path.taperX = taperX;
1404 path.taperY = taperY;
1405 path.radius = radius;
1406 path.revolutions = revolutions;
1407 path.stepsPerRevolution = stepsPerRevolution;
1408
1409 path.Create(pathType, steps);
1410
1411 int lastNode = path.pathNodes.Count - 1;
1412
1413 for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++)
1414 {
1415 PathNode node = path.pathNodes[nodeIndex];
1416 Profile newLayer = profile.Copy();
1417
1418 newLayer.Scale(node.xScale, node.yScale);
1419 newLayer.AddRot(node.rotation);
1420 newLayer.AddPos(node.position);
1421
1422 // append this layer
1423 int coordsStart = coords.Count;
1424 coords.AddRange(newLayer.coords);
1425
1426 if (needEndFaces && nodeIndex == 0 && newLayer.faces.Count > 0)
1427 {
1428 newLayer.AddValue2FaceVertexIndices(coordsStart);
1429 newLayer.FlipNormals();
1430 faces.AddRange(newLayer.faces);
1431 }
1432
1433 // fill faces between layers
1434
1435 List<Face> linkfaces = new List<Face>();
1436 int numVerts = newLayer.coords.Count;
1437 Face newFace1 = new Face();
1438 Face newFace2 = new Face();
1439
1440 if (nodeIndex > 0)
1441 {
1442 int startVert = coordsStart;
1443 int endVert = coords.Count;
1444 if (!hasProfileCut)
1445 {
1446 if(numVerts > 5 && !hasHollow)
1447 startVert++;
1448 int i = startVert;
1449 for (int l = 0; l < profile.numOuterVerts - 1; l++)
1450 {
1451 newFace1.v1 = i;
1452 newFace1.v2 = i - numVerts;
1453 newFace1.v3 = i + 1;
1454 linkfaces.Add(newFace1);
1455
1456 newFace2.v1 = i + 1;
1457 newFace2.v2 = i - numVerts;
1458 newFace2.v3 = i + 1 - numVerts;
1459 linkfaces.Add(newFace2);
1460 i++;
1461 }
1462
1463 newFace1.v1 = i;
1464 newFace1.v2 = i - numVerts;
1465 newFace1.v3 = startVert;
1466 linkfaces.Add(newFace1);
1467
1468 newFace2.v1 = startVert;
1469 newFace2.v2 = i - numVerts;
1470 newFace2.v3 = startVert - numVerts;
1471 linkfaces.Add(newFace2);
1472
1473 if (hasHollow)
1474 {
1475 startVert = ++i;
1476 for (int l = 0; l < profile.numHollowVerts - 1; l++)
1477 {
1478 newFace1.v1 = i;
1479 newFace1.v2 = i - numVerts;
1480 newFace1.v3 = i + 1;
1481 linkfaces.Add(newFace1);
1482
1483 newFace2.v1 = i + 1;
1484 newFace2.v2 = i - numVerts;
1485 newFace2.v3 = i + 1 - numVerts;
1486 linkfaces.Add(newFace2);
1487 i++;
1488 }
1489
1490 newFace1.v1 = i;
1491 newFace1.v2 = i - numVerts;
1492 newFace1.v3 = startVert;
1493 linkfaces.Add(newFace1);
1494
1495 newFace2.v1 = startVert;
1496 newFace2.v2 = i - numVerts;
1497 newFace2.v3 = startVert - numVerts;
1498 linkfaces.Add(newFace2);
1499 }
1500 }
1501 else
1502 {
1503 for (int i = startVert; i < endVert; i++)
1504 {
1505 int iNext = i + 1;
1506 if (i == endVert - 1)
1507 iNext = startVert;
1508
1509 newFace1.v1 = i;
1510 newFace1.v2 = i - numVerts;
1511 newFace1.v3 = iNext;
1512 linkfaces.Add(newFace1);
1513
1514 newFace2.v1 = iNext;
1515 newFace2.v2 = i - numVerts;
1516 newFace2.v3 = iNext - numVerts;
1517 linkfaces.Add(newFace2);
1518 }
1519 }
1520 }
1521
1522 if(linkfaces.Count > 0)
1523 faces.AddRange(linkfaces);
1524
1525 if (needEndFaces && nodeIndex == lastNode && newLayer.faces.Count > 0)
1526 {
1527 newLayer.AddValue2FaceVertexIndices(coordsStart);
1528 faces.AddRange(newLayer.faces);
1529 }
1530
1531 } // for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++)
1532 // more cleanup will be done at Meshmerizer.cs
1533 }
1534
1535
1536 /// <summary>
1537 /// DEPRICATED - use Extrude(PathType.Linear) instead
1538 /// Extrudes a profile along a straight line path. Used for prim types box, cylinder, and prism.
1539 /// </summary>
1540 ///
1541 public void ExtrudeLinear()
1542 {
1543 Extrude(PathType.Linear);
1544 }
1545
1546
1547 /// <summary>
1548 /// DEPRICATED - use Extrude(PathType.Circular) instead
1549 /// Extrude a profile into a circular path prim mesh. Used for prim types torus, tube, and ring.
1550 /// </summary>
1551 ///
1552 public void ExtrudeCircular()
1553 {
1554 Extrude(PathType.Circular);
1555 }
1556
1557
1558 private Coord SurfaceNormal(Coord c1, Coord c2, Coord c3)
1559 {
1560 Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z);
1561 Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z);
1562
1563 Coord normal = Coord.Cross(edge1, edge2);
1564
1565 normal.Normalize();
1566
1567 return normal;
1568 }
1569
1570 private Coord SurfaceNormal(Face face)
1571 {
1572 return SurfaceNormal(this.coords[face.v1], this.coords[face.v2], this.coords[face.v3]);
1573 }
1574
1575 /// <summary>
1576 /// Calculate the surface normal for a face in the list of faces
1577 /// </summary>
1578 /// <param name="faceIndex"></param>
1579 /// <returns></returns>
1580 public Coord SurfaceNormal(int faceIndex)
1581 {
1582 int numFaces = this.faces.Count;
1583 if (faceIndex < 0 || faceIndex >= numFaces)
1584 throw new Exception("faceIndex out of range");
1585
1586 return SurfaceNormal(this.faces[faceIndex]);
1587 }
1588
1589 /// <summary>
1590 /// Duplicates a PrimMesh object. All object properties are copied by value, including lists.
1591 /// </summary>
1592 /// <returns></returns>
1593 public PrimMesh Copy()
1594 {
1595 PrimMesh copy = new PrimMesh(this.sides, this.profileStart, this.profileEnd, this.hollow, this.hollowSides);
1596 copy.twistBegin = this.twistBegin;
1597 copy.twistEnd = this.twistEnd;
1598 copy.topShearX = this.topShearX;
1599 copy.topShearY = this.topShearY;
1600 copy.pathCutBegin = this.pathCutBegin;
1601 copy.pathCutEnd = this.pathCutEnd;
1602 copy.dimpleBegin = this.dimpleBegin;
1603 copy.dimpleEnd = this.dimpleEnd;
1604 copy.skew = this.skew;
1605 copy.holeSizeX = this.holeSizeX;
1606 copy.holeSizeY = this.holeSizeY;
1607 copy.taperX = this.taperX;
1608 copy.taperY = this.taperY;
1609 copy.radius = this.radius;
1610 copy.revolutions = this.revolutions;
1611 copy.stepsPerRevolution = this.stepsPerRevolution;
1612
1613 copy.numPrimFaces = this.numPrimFaces;
1614 copy.errorMessage = this.errorMessage;
1615
1616 copy.coords = new List<Coord>(this.coords);
1617 copy.faces = new List<Face>(this.faces);
1618
1619 return copy;
1620 }
1621
1622 /// <summary>
1623 /// Adds a value to each XYZ vertex coordinate in the mesh
1624 /// </summary>
1625 /// <param name="x"></param>
1626 /// <param name="y"></param>
1627 /// <param name="z"></param>
1628 public void AddPos(float x, float y, float z)
1629 {
1630 int i;
1631 int numVerts = this.coords.Count;
1632 Coord vert;
1633
1634 for (i = 0; i < numVerts; i++)
1635 {
1636 vert = this.coords[i];
1637 vert.X += x;
1638 vert.Y += y;
1639 vert.Z += z;
1640 this.coords[i] = vert;
1641 }
1642 }
1643
1644 /// <summary>
1645 /// Rotates the mesh
1646 /// </summary>
1647 /// <param name="q"></param>
1648 public void AddRot(Quat q)
1649 {
1650 int i;
1651 int numVerts = this.coords.Count;
1652
1653 for (i = 0; i < numVerts; i++)
1654 this.coords[i] *= q;
1655 }
1656
1657#if VERTEX_INDEXER
1658 public VertexIndexer GetVertexIndexer()
1659 {
1660 return null;
1661 }
1662#endif
1663
1664 /// <summary>
1665 /// Scales the mesh
1666 /// </summary>
1667 /// <param name="x"></param>
1668 /// <param name="y"></param>
1669 /// <param name="z"></param>
1670 public void Scale(float x, float y, float z)
1671 {
1672 int i;
1673 int numVerts = this.coords.Count;
1674 //Coord vert;
1675
1676 Coord m = new Coord(x, y, z);
1677 for (i = 0; i < numVerts; i++)
1678 this.coords[i] *= m;
1679 }
1680
1681 /// <summary>
1682 /// Dumps the mesh to a Blender compatible "Raw" format file
1683 /// </summary>
1684 /// <param name="path"></param>
1685 /// <param name="name"></param>
1686 /// <param name="title"></param>
1687 public void DumpRaw(String path, String name, String title)
1688 {
1689 if (path == null)
1690 return;
1691 String fileName = name + "_" + title + ".raw";
1692 String completePath = System.IO.Path.Combine(path, fileName);
1693 StreamWriter sw = new StreamWriter(completePath);
1694
1695 for (int i = 0; i < this.faces.Count; i++)
1696 {
1697 string s = this.coords[this.faces[i].v1].ToString();
1698 s += " " + this.coords[this.faces[i].v2].ToString();
1699 s += " " + this.coords[this.faces[i].v3].ToString();
1700
1701 sw.WriteLine(s);
1702 }
1703
1704 sw.Close();
1705 }
1706 }
1707}