aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/UbitMeshing/PrimMesher.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/Physics/UbitMeshing/PrimMesher.cs2324
1 files changed, 2324 insertions, 0 deletions
diff --git a/OpenSim/Region/Physics/UbitMeshing/PrimMesher.cs b/OpenSim/Region/Physics/UbitMeshing/PrimMesher.cs
new file mode 100644
index 0000000..4049ee1
--- /dev/null
+++ b/OpenSim/Region/Physics/UbitMeshing/PrimMesher.cs
@@ -0,0 +1,2324 @@
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 UVCoord
229 {
230 public float U;
231 public float V;
232
233
234 public UVCoord(float u, float v)
235 {
236 this.U = u;
237 this.V = v;
238 }
239
240 public UVCoord Flip()
241 {
242 this.U = 1.0f - this.U;
243 this.V = 1.0f - this.V;
244 return this;
245 }
246 }
247
248 public struct Face
249 {
250 public int primFace;
251
252 // vertices
253 public int v1;
254 public int v2;
255 public int v3;
256
257 //normals
258 public int n1;
259 public int n2;
260 public int n3;
261
262 // uvs
263 public int uv1;
264 public int uv2;
265 public int uv3;
266
267 public Face(int v1, int v2, int v3)
268 {
269 primFace = 0;
270
271 this.v1 = v1;
272 this.v2 = v2;
273 this.v3 = v3;
274
275 this.n1 = 0;
276 this.n2 = 0;
277 this.n3 = 0;
278
279 this.uv1 = 0;
280 this.uv2 = 0;
281 this.uv3 = 0;
282
283 }
284
285 public Face(int v1, int v2, int v3, int n1, int n2, int n3)
286 {
287 primFace = 0;
288
289 this.v1 = v1;
290 this.v2 = v2;
291 this.v3 = v3;
292
293 this.n1 = n1;
294 this.n2 = n2;
295 this.n3 = n3;
296
297 this.uv1 = 0;
298 this.uv2 = 0;
299 this.uv3 = 0;
300 }
301
302 public Coord SurfaceNormal(List<Coord> coordList)
303 {
304 Coord c1 = coordList[this.v1];
305 Coord c2 = coordList[this.v2];
306 Coord c3 = coordList[this.v3];
307
308 Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z);
309 Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z);
310
311 return Coord.Cross(edge1, edge2).Normalize();
312 }
313 }
314
315 public struct ViewerFace
316 {
317 public int primFaceNumber;
318
319 public Coord v1;
320 public Coord v2;
321 public Coord v3;
322
323 public int coordIndex1;
324 public int coordIndex2;
325 public int coordIndex3;
326
327 public Coord n1;
328 public Coord n2;
329 public Coord n3;
330
331 public UVCoord uv1;
332 public UVCoord uv2;
333 public UVCoord uv3;
334
335 public ViewerFace(int primFaceNumber)
336 {
337 this.primFaceNumber = primFaceNumber;
338
339 this.v1 = new Coord();
340 this.v2 = new Coord();
341 this.v3 = new Coord();
342
343 this.coordIndex1 = this.coordIndex2 = this.coordIndex3 = -1; // -1 means not assigned yet
344
345 this.n1 = new Coord();
346 this.n2 = new Coord();
347 this.n3 = new Coord();
348
349 this.uv1 = new UVCoord();
350 this.uv2 = new UVCoord();
351 this.uv3 = new UVCoord();
352 }
353
354 public void Scale(float x, float y, float z)
355 {
356 this.v1.X *= x;
357 this.v1.Y *= y;
358 this.v1.Z *= z;
359
360 this.v2.X *= x;
361 this.v2.Y *= y;
362 this.v2.Z *= z;
363
364 this.v3.X *= x;
365 this.v3.Y *= y;
366 this.v3.Z *= z;
367 }
368
369 public void AddPos(float x, float y, float z)
370 {
371 this.v1.X += x;
372 this.v2.X += x;
373 this.v3.X += x;
374
375 this.v1.Y += y;
376 this.v2.Y += y;
377 this.v3.Y += y;
378
379 this.v1.Z += z;
380 this.v2.Z += z;
381 this.v3.Z += z;
382 }
383
384 public void AddRot(Quat q)
385 {
386 this.v1 *= q;
387 this.v2 *= q;
388 this.v3 *= q;
389
390 this.n1 *= q;
391 this.n2 *= q;
392 this.n3 *= q;
393 }
394
395 public void CalcSurfaceNormal()
396 {
397
398 Coord edge1 = new Coord(this.v2.X - this.v1.X, this.v2.Y - this.v1.Y, this.v2.Z - this.v1.Z);
399 Coord edge2 = new Coord(this.v3.X - this.v1.X, this.v3.Y - this.v1.Y, this.v3.Z - this.v1.Z);
400
401 this.n1 = this.n2 = this.n3 = Coord.Cross(edge1, edge2).Normalize();
402 }
403 }
404
405 internal struct Angle
406 {
407 internal float angle;
408 internal float X;
409 internal float Y;
410
411 internal Angle(float angle, float x, float y)
412 {
413 this.angle = angle;
414 this.X = x;
415 this.Y = y;
416 }
417 }
418
419 internal class AngleList
420 {
421 private float iX, iY; // intersection point
422
423 private static Angle[] angles3 =
424 {
425 new Angle(0.0f, 1.0f, 0.0f),
426 new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f),
427 new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f),
428 new Angle(1.0f, 1.0f, 0.0f)
429 };
430
431 private static Coord[] normals3 =
432 {
433 new Coord(0.25f, 0.4330127019f, 0.0f).Normalize(),
434 new Coord(-0.5f, 0.0f, 0.0f).Normalize(),
435 new Coord(0.25f, -0.4330127019f, 0.0f).Normalize(),
436 new Coord(0.25f, 0.4330127019f, 0.0f).Normalize()
437 };
438
439 private static Angle[] angles4 =
440 {
441 new Angle(0.0f, 1.0f, 0.0f),
442 new Angle(0.25f, 0.0f, 1.0f),
443 new Angle(0.5f, -1.0f, 0.0f),
444 new Angle(0.75f, 0.0f, -1.0f),
445 new Angle(1.0f, 1.0f, 0.0f)
446 };
447
448 private static Coord[] normals4 =
449 {
450 new Coord(0.5f, 0.5f, 0.0f).Normalize(),
451 new Coord(-0.5f, 0.5f, 0.0f).Normalize(),
452 new Coord(-0.5f, -0.5f, 0.0f).Normalize(),
453 new Coord(0.5f, -0.5f, 0.0f).Normalize(),
454 new Coord(0.5f, 0.5f, 0.0f).Normalize()
455 };
456
457 private static Angle[] angles24 =
458 {
459 new Angle(0.0f, 1.0f, 0.0f),
460 new Angle(0.041666666666666664f, 0.96592582628906831f, 0.25881904510252074f),
461 new Angle(0.083333333333333329f, 0.86602540378443871f, 0.5f),
462 new Angle(0.125f, 0.70710678118654757f, 0.70710678118654746f),
463 new Angle(0.16666666666666667f, 0.5f, 0.8660254037844386f),
464 new Angle(0.20833333333333331f, 0.25881904510252096f, 0.9659258262890682f),
465 new Angle(0.25f, 0.0f, 1.0f),
466 new Angle(0.29166666666666663f, -0.25881904510252063f, 0.96592582628906831f),
467 new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f),
468 new Angle(0.375f, -0.70710678118654746f, 0.70710678118654757f),
469 new Angle(0.41666666666666663f, -0.86602540378443849f, 0.5f),
470 new Angle(0.45833333333333331f, -0.9659258262890682f, 0.25881904510252102f),
471 new Angle(0.5f, -1.0f, 0.0f),
472 new Angle(0.54166666666666663f, -0.96592582628906842f, -0.25881904510252035f),
473 new Angle(0.58333333333333326f, -0.86602540378443882f, -0.5f),
474 new Angle(0.62499999999999989f, -0.70710678118654791f, -0.70710678118654713f),
475 new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f),
476 new Angle(0.70833333333333326f, -0.25881904510252152f, -0.96592582628906809f),
477 new Angle(0.75f, 0.0f, -1.0f),
478 new Angle(0.79166666666666663f, 0.2588190451025203f, -0.96592582628906842f),
479 new Angle(0.83333333333333326f, 0.5f, -0.86602540378443904f),
480 new Angle(0.875f, 0.70710678118654735f, -0.70710678118654768f),
481 new Angle(0.91666666666666663f, 0.86602540378443837f, -0.5f),
482 new Angle(0.95833333333333326f, 0.96592582628906809f, -0.25881904510252157f),
483 new Angle(1.0f, 1.0f, 0.0f)
484 };
485
486 private Angle interpolatePoints(float newPoint, Angle p1, Angle p2)
487 {
488 float m = (newPoint - p1.angle) / (p2.angle - p1.angle);
489 return new Angle(newPoint, p1.X + m * (p2.X - p1.X), p1.Y + m * (p2.Y - p1.Y));
490 }
491
492 private void intersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
493 { // ref: http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
494 double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
495 double uaNumerator = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3);
496
497 if (denom != 0.0)
498 {
499 double ua = uaNumerator / denom;
500 iX = (float)(x1 + ua * (x2 - x1));
501 iY = (float)(y1 + ua * (y2 - y1));
502 }
503 }
504
505 internal List<Angle> angles;
506 internal List<Coord> normals;
507
508 internal void makeAngles(int sides, float startAngle, float stopAngle)
509 {
510 angles = new List<Angle>();
511 normals = new List<Coord>();
512
513 double twoPi = System.Math.PI * 2.0;
514 float twoPiInv = 1.0f / (float)twoPi;
515
516 if (sides < 1)
517 throw new Exception("number of sides not greater than zero");
518 if (stopAngle <= startAngle)
519 throw new Exception("stopAngle not greater than startAngle");
520
521 if ((sides == 3 || sides == 4 || sides == 24))
522 {
523 startAngle *= twoPiInv;
524 stopAngle *= twoPiInv;
525
526 Angle[] sourceAngles;
527 if (sides == 3)
528 sourceAngles = angles3;
529 else if (sides == 4)
530 sourceAngles = angles4;
531 else sourceAngles = angles24;
532
533 int startAngleIndex = (int)(startAngle * sides);
534 int endAngleIndex = sourceAngles.Length - 1;
535 if (stopAngle < 1.0f)
536 endAngleIndex = (int)(stopAngle * sides) + 1;
537 if (endAngleIndex == startAngleIndex)
538 endAngleIndex++;
539
540 for (int angleIndex = startAngleIndex; angleIndex < endAngleIndex + 1; angleIndex++)
541 {
542 angles.Add(sourceAngles[angleIndex]);
543 if (sides == 3)
544 normals.Add(normals3[angleIndex]);
545 else if (sides == 4)
546 normals.Add(normals4[angleIndex]);
547 }
548
549 if (startAngle > 0.0f)
550 angles[0] = interpolatePoints(startAngle, angles[0], angles[1]);
551
552 if (stopAngle < 1.0f)
553 {
554 int lastAngleIndex = angles.Count - 1;
555 angles[lastAngleIndex] = interpolatePoints(stopAngle, angles[lastAngleIndex - 1], angles[lastAngleIndex]);
556 }
557 }
558 else
559 {
560 double stepSize = twoPi / sides;
561
562 int startStep = (int)(startAngle / stepSize);
563 double angle = stepSize * startStep;
564 int step = startStep;
565 double stopAngleTest = stopAngle;
566 if (stopAngle < twoPi)
567 {
568 stopAngleTest = stepSize * ((int)(stopAngle / stepSize) + 1);
569 if (stopAngleTest < stopAngle)
570 stopAngleTest += stepSize;
571 if (stopAngleTest > twoPi)
572 stopAngleTest = twoPi;
573 }
574
575 while (angle <= stopAngleTest)
576 {
577 Angle newAngle;
578 newAngle.angle = (float)angle;
579 newAngle.X = (float)System.Math.Cos(angle);
580 newAngle.Y = (float)System.Math.Sin(angle);
581 angles.Add(newAngle);
582 step += 1;
583 angle = stepSize * step;
584 }
585
586 if (startAngle > angles[0].angle)
587 {
588 Angle newAngle;
589 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));
590 newAngle.angle = startAngle;
591 newAngle.X = iX;
592 newAngle.Y = iY;
593 angles[0] = newAngle;
594 }
595
596 int index = angles.Count - 1;
597 if (stopAngle < angles[index].angle)
598 {
599 Angle newAngle;
600 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));
601 newAngle.angle = stopAngle;
602 newAngle.X = iX;
603 newAngle.Y = iY;
604 angles[index] = newAngle;
605 }
606 }
607 }
608 }
609
610 /// <summary>
611 /// generates a profile for extrusion
612 /// </summary>
613 public class Profile
614 {
615 private const float twoPi = 2.0f * (float)Math.PI;
616
617 public string errorMessage = null;
618
619 public List<Coord> coords;
620 public List<Face> faces;
621 public List<Coord> vertexNormals;
622 public List<float> us;
623 public List<UVCoord> faceUVs;
624 public List<int> faceNumbers;
625
626 // use these for making individual meshes for each prim face
627 public List<int> outerCoordIndices = null;
628 public List<int> hollowCoordIndices = null;
629 public List<int> cut1CoordIndices = null;
630 public List<int> cut2CoordIndices = null;
631
632 public Coord faceNormal = new Coord(0.0f, 0.0f, 1.0f);
633 public Coord cutNormal1 = new Coord();
634 public Coord cutNormal2 = new Coord();
635
636 public int numOuterVerts = 0;
637 public int numHollowVerts = 0;
638
639 public int outerFaceNumber = -1;
640 public int hollowFaceNumber = -1;
641
642 public bool calcVertexNormals = false;
643 public int bottomFaceNumber = 0;
644 public int numPrimFaces = 0;
645
646 public Profile()
647 {
648 this.coords = new List<Coord>();
649 this.faces = new List<Face>();
650 this.vertexNormals = new List<Coord>();
651 this.us = new List<float>();
652 this.faceUVs = new List<UVCoord>();
653 this.faceNumbers = new List<int>();
654 }
655
656 public Profile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides, bool createFaces, bool calcVertexNormals)
657 {
658 this.calcVertexNormals = calcVertexNormals;
659 this.coords = new List<Coord>();
660 this.faces = new List<Face>();
661 this.vertexNormals = new List<Coord>();
662 this.us = new List<float>();
663 this.faceUVs = new List<UVCoord>();
664 this.faceNumbers = new List<int>();
665
666 Coord center = new Coord(0.0f, 0.0f, 0.0f);
667
668 List<Coord> hollowCoords = new List<Coord>();
669 List<Coord> hollowNormals = new List<Coord>();
670 List<float> hollowUs = new List<float>();
671
672 if (calcVertexNormals)
673 {
674 this.outerCoordIndices = new List<int>();
675 this.hollowCoordIndices = new List<int>();
676 this.cut1CoordIndices = new List<int>();
677 this.cut2CoordIndices = new List<int>();
678 }
679
680 bool hasHollow = (hollow > 0.0f);
681
682 bool hasProfileCut = (profileStart > 0.0f || profileEnd < 1.0f);
683
684 AngleList angles = new AngleList();
685 AngleList hollowAngles = new AngleList();
686
687 float xScale = 0.5f;
688 float yScale = 0.5f;
689 if (sides == 4) // corners of a square are sqrt(2) from center
690 {
691 xScale = 0.707107f;
692 yScale = 0.707107f;
693 }
694
695 float startAngle = profileStart * twoPi;
696 float stopAngle = profileEnd * twoPi;
697
698 try { angles.makeAngles(sides, startAngle, stopAngle); }
699 catch (Exception ex)
700 {
701
702 errorMessage = "makeAngles failed: Exception: " + ex.ToString()
703 + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString();
704
705 return;
706 }
707
708 this.numOuterVerts = angles.angles.Count;
709
710 // flag to create as few triangles as possible for 3 or 4 side profile
711 bool simpleFace = (sides < 5 && !hasHollow && !hasProfileCut);
712
713 if (hasHollow)
714 {
715 if (sides == hollowSides)
716 hollowAngles = angles;
717 else
718 {
719 try { hollowAngles.makeAngles(hollowSides, startAngle, stopAngle); }
720 catch (Exception ex)
721 {
722 errorMessage = "makeAngles failed: Exception: " + ex.ToString()
723 + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString();
724
725 return;
726 }
727 }
728 this.numHollowVerts = hollowAngles.angles.Count;
729 }
730 else if (!simpleFace)
731 {
732 this.coords.Add(center);
733 if (this.calcVertexNormals)
734 this.vertexNormals.Add(new Coord(0.0f, 0.0f, 1.0f));
735 this.us.Add(0.0f);
736 }
737
738 float z = 0.0f;
739
740 Angle angle;
741 Coord newVert = new Coord();
742 if (hasHollow && hollowSides != sides)
743 {
744 int numHollowAngles = hollowAngles.angles.Count;
745 for (int i = 0; i < numHollowAngles; i++)
746 {
747 angle = hollowAngles.angles[i];
748 newVert.X = hollow * xScale * angle.X;
749 newVert.Y = hollow * yScale * angle.Y;
750 newVert.Z = z;
751
752 hollowCoords.Add(newVert);
753 if (this.calcVertexNormals)
754 {
755 if (hollowSides < 5)
756 hollowNormals.Add(hollowAngles.normals[i].Invert());
757 else
758 hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f));
759
760 if (hollowSides == 4)
761 hollowUs.Add(angle.angle * hollow * 0.707107f);
762 else
763 hollowUs.Add(angle.angle * hollow);
764 }
765 }
766 }
767
768 int index = 0;
769 int numAngles = angles.angles.Count;
770
771 for (int i = 0; i < numAngles; i++)
772 {
773 angle = angles.angles[i];
774 newVert.X = angle.X * xScale;
775 newVert.Y = angle.Y * yScale;
776 newVert.Z = z;
777 this.coords.Add(newVert);
778 if (this.calcVertexNormals)
779 {
780 this.outerCoordIndices.Add(this.coords.Count - 1);
781
782 if (sides < 5)
783 {
784 this.vertexNormals.Add(angles.normals[i]);
785 float u = angle.angle;
786 this.us.Add(u);
787 }
788 else
789 {
790 this.vertexNormals.Add(new Coord(angle.X, angle.Y, 0.0f));
791 this.us.Add(angle.angle);
792 }
793 }
794
795 if (hasHollow)
796 {
797 if (hollowSides == sides)
798 {
799 newVert.X *= hollow;
800 newVert.Y *= hollow;
801 newVert.Z = z;
802 hollowCoords.Add(newVert);
803 if (this.calcVertexNormals)
804 {
805 if (sides < 5)
806 {
807 hollowNormals.Add(angles.normals[i].Invert());
808 }
809
810 else
811 hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f));
812
813 hollowUs.Add(angle.angle * hollow);
814 }
815 }
816 }
817 else if (!simpleFace && createFaces && angle.angle > 0.0001f)
818 {
819 Face newFace = new Face();
820 newFace.v1 = 0;
821 newFace.v2 = index;
822 newFace.v3 = index + 1;
823
824 this.faces.Add(newFace);
825 }
826 index += 1;
827 }
828
829 if (hasHollow)
830 {
831 hollowCoords.Reverse();
832 if (this.calcVertexNormals)
833 {
834 hollowNormals.Reverse();
835 hollowUs.Reverse();
836 }
837
838 if (createFaces)
839 {
840 int numTotalVerts = this.numOuterVerts + this.numHollowVerts;
841
842 if (this.numOuterVerts == this.numHollowVerts)
843 {
844 Face newFace = new Face();
845
846 for (int coordIndex = 0; coordIndex < this.numOuterVerts - 1; coordIndex++)
847 {
848 newFace.v1 = coordIndex;
849 newFace.v2 = coordIndex + 1;
850 newFace.v3 = numTotalVerts - coordIndex - 1;
851 this.faces.Add(newFace);
852
853 newFace.v1 = coordIndex + 1;
854 newFace.v2 = numTotalVerts - coordIndex - 2;
855 newFace.v3 = numTotalVerts - coordIndex - 1;
856 this.faces.Add(newFace);
857 }
858 }
859 else
860 {
861 if (this.numOuterVerts < this.numHollowVerts)
862 {
863 Face newFace = new Face();
864 int j = 0; // j is the index for outer vertices
865 int maxJ = this.numOuterVerts - 1;
866 for (int i = 0; i < this.numHollowVerts; i++) // i is the index for inner vertices
867 {
868 if (j < maxJ)
869 if (angles.angles[j + 1].angle - hollowAngles.angles[i].angle < hollowAngles.angles[i].angle - angles.angles[j].angle + 0.000001f)
870 {
871 newFace.v1 = numTotalVerts - i - 1;
872 newFace.v2 = j;
873 newFace.v3 = j + 1;
874
875 this.faces.Add(newFace);
876 j += 1;
877 }
878
879 newFace.v1 = j;
880 newFace.v2 = numTotalVerts - i - 2;
881 newFace.v3 = numTotalVerts - i - 1;
882
883 this.faces.Add(newFace);
884 }
885 }
886 else // numHollowVerts < numOuterVerts
887 {
888 Face newFace = new Face();
889 int j = 0; // j is the index for inner vertices
890 int maxJ = this.numHollowVerts - 1;
891 for (int i = 0; i < this.numOuterVerts; i++)
892 {
893 if (j < maxJ)
894 if (hollowAngles.angles[j + 1].angle - angles.angles[i].angle < angles.angles[i].angle - hollowAngles.angles[j].angle + 0.000001f)
895 {
896 newFace.v1 = i;
897 newFace.v2 = numTotalVerts - j - 2;
898 newFace.v3 = numTotalVerts - j - 1;
899
900 this.faces.Add(newFace);
901 j += 1;
902 }
903
904 newFace.v1 = numTotalVerts - j - 1;
905 newFace.v2 = i;
906 newFace.v3 = i + 1;
907
908 this.faces.Add(newFace);
909 }
910 }
911 }
912 }
913
914 if (calcVertexNormals)
915 {
916 foreach (Coord hc in hollowCoords)
917 {
918 this.coords.Add(hc);
919 hollowCoordIndices.Add(this.coords.Count - 1);
920 }
921 }
922 else
923 this.coords.AddRange(hollowCoords);
924
925 if (this.calcVertexNormals)
926 {
927 this.vertexNormals.AddRange(hollowNormals);
928 this.us.AddRange(hollowUs);
929
930 }
931 }
932
933 if (simpleFace && createFaces)
934 {
935 if (sides == 3)
936 this.faces.Add(new Face(0, 1, 2));
937 else if (sides == 4)
938 {
939 this.faces.Add(new Face(0, 1, 2));
940 this.faces.Add(new Face(0, 2, 3));
941 }
942 }
943
944 if (calcVertexNormals && hasProfileCut)
945 {
946 int lastOuterVertIndex = this.numOuterVerts - 1;
947
948 if (hasHollow)
949 {
950 this.cut1CoordIndices.Add(0);
951 this.cut1CoordIndices.Add(this.coords.Count - 1);
952
953 this.cut2CoordIndices.Add(lastOuterVertIndex + 1);
954 this.cut2CoordIndices.Add(lastOuterVertIndex);
955
956 this.cutNormal1.X = this.coords[0].Y - this.coords[this.coords.Count - 1].Y;
957 this.cutNormal1.Y = -(this.coords[0].X - this.coords[this.coords.Count - 1].X);
958
959 this.cutNormal2.X = this.coords[lastOuterVertIndex + 1].Y - this.coords[lastOuterVertIndex].Y;
960 this.cutNormal2.Y = -(this.coords[lastOuterVertIndex + 1].X - this.coords[lastOuterVertIndex].X);
961 }
962
963 else
964 {
965 this.cut1CoordIndices.Add(0);
966 this.cut1CoordIndices.Add(1);
967
968 this.cut2CoordIndices.Add(lastOuterVertIndex);
969 this.cut2CoordIndices.Add(0);
970
971 this.cutNormal1.X = this.vertexNormals[1].Y;
972 this.cutNormal1.Y = -this.vertexNormals[1].X;
973
974 this.cutNormal2.X = -this.vertexNormals[this.vertexNormals.Count - 2].Y;
975 this.cutNormal2.Y = this.vertexNormals[this.vertexNormals.Count - 2].X;
976
977 }
978 this.cutNormal1.Normalize();
979 this.cutNormal2.Normalize();
980 }
981
982 this.MakeFaceUVs();
983
984 hollowCoords = null;
985 hollowNormals = null;
986 hollowUs = null;
987
988 if (calcVertexNormals)
989 { // calculate prim face numbers
990
991 // face number order is top, outer, hollow, bottom, start cut, end cut
992 // I know it's ugly but so is the whole concept of prim face numbers
993
994 int faceNum = 1; // start with outer faces
995 this.outerFaceNumber = faceNum;
996
997 int startVert = hasProfileCut && !hasHollow ? 1 : 0;
998 if (startVert > 0)
999 this.faceNumbers.Add(-1);
1000 for (int i = 0; i < this.numOuterVerts - 1; i++)
1001 this.faceNumbers.Add(sides < 5 && i <= sides ? faceNum++ : faceNum);
1002
1003 this.faceNumbers.Add(hasProfileCut ? -1 : faceNum++);
1004
1005 if (sides > 4 && (hasHollow || hasProfileCut))
1006 faceNum++;
1007
1008 if (sides < 5 && (hasHollow || hasProfileCut) && this.numOuterVerts < sides)
1009 faceNum++;
1010
1011 if (hasHollow)
1012 {
1013 for (int i = 0; i < this.numHollowVerts; i++)
1014 this.faceNumbers.Add(faceNum);
1015
1016 this.hollowFaceNumber = faceNum++;
1017 }
1018
1019 this.bottomFaceNumber = faceNum++;
1020
1021 if (hasHollow && hasProfileCut)
1022 this.faceNumbers.Add(faceNum++);
1023
1024 for (int i = 0; i < this.faceNumbers.Count; i++)
1025 if (this.faceNumbers[i] == -1)
1026 this.faceNumbers[i] = faceNum++;
1027
1028 this.numPrimFaces = faceNum;
1029 }
1030
1031 }
1032
1033 public void MakeFaceUVs()
1034 {
1035 this.faceUVs = new List<UVCoord>();
1036 foreach (Coord c in this.coords)
1037 this.faceUVs.Add(new UVCoord(1.0f - (0.5f + c.X), 1.0f - (0.5f - c.Y)));
1038 }
1039
1040 public Profile Copy()
1041 {
1042 return this.Copy(true);
1043 }
1044
1045 public Profile Copy(bool needFaces)
1046 {
1047 Profile copy = new Profile();
1048
1049 copy.coords.AddRange(this.coords);
1050 copy.faceUVs.AddRange(this.faceUVs);
1051
1052 if (needFaces)
1053 copy.faces.AddRange(this.faces);
1054 if ((copy.calcVertexNormals = this.calcVertexNormals) == true)
1055 {
1056 copy.vertexNormals.AddRange(this.vertexNormals);
1057 copy.faceNormal = this.faceNormal;
1058 copy.cutNormal1 = this.cutNormal1;
1059 copy.cutNormal2 = this.cutNormal2;
1060 copy.us.AddRange(this.us);
1061 copy.faceNumbers.AddRange(this.faceNumbers);
1062
1063 copy.cut1CoordIndices = new List<int>(this.cut1CoordIndices);
1064 copy.cut2CoordIndices = new List<int>(this.cut2CoordIndices);
1065 copy.hollowCoordIndices = new List<int>(this.hollowCoordIndices);
1066 copy.outerCoordIndices = new List<int>(this.outerCoordIndices);
1067 }
1068 copy.numOuterVerts = this.numOuterVerts;
1069 copy.numHollowVerts = this.numHollowVerts;
1070
1071 return copy;
1072 }
1073
1074 public void AddPos(Coord v)
1075 {
1076 this.AddPos(v.X, v.Y, v.Z);
1077 }
1078
1079 public void AddPos(float x, float y, float z)
1080 {
1081 int i;
1082 int numVerts = this.coords.Count;
1083 Coord vert;
1084
1085 for (i = 0; i < numVerts; i++)
1086 {
1087 vert = this.coords[i];
1088 vert.X += x;
1089 vert.Y += y;
1090 vert.Z += z;
1091 this.coords[i] = vert;
1092 }
1093 }
1094
1095 public void AddRot(Quat q)
1096 {
1097 int i;
1098 int numVerts = this.coords.Count;
1099
1100 for (i = 0; i < numVerts; i++)
1101 this.coords[i] *= q;
1102
1103 if (this.calcVertexNormals)
1104 {
1105 int numNormals = this.vertexNormals.Count;
1106 for (i = 0; i < numNormals; i++)
1107 this.vertexNormals[i] *= q;
1108
1109 this.faceNormal *= q;
1110 this.cutNormal1 *= q;
1111 this.cutNormal2 *= q;
1112
1113 }
1114 }
1115
1116 public void Scale(float x, float y)
1117 {
1118 int i;
1119 int numVerts = this.coords.Count;
1120 Coord vert;
1121
1122 for (i = 0; i < numVerts; i++)
1123 {
1124 vert = this.coords[i];
1125 vert.X *= x;
1126 vert.Y *= y;
1127 this.coords[i] = vert;
1128 }
1129 }
1130
1131 /// <summary>
1132 /// Changes order of the vertex indices and negates the center vertex normal. Does not alter vertex normals of radial vertices
1133 /// </summary>
1134 public void FlipNormals()
1135 {
1136 int i;
1137 int numFaces = this.faces.Count;
1138 Face tmpFace;
1139 int tmp;
1140
1141 for (i = 0; i < numFaces; i++)
1142 {
1143 tmpFace = this.faces[i];
1144 tmp = tmpFace.v3;
1145 tmpFace.v3 = tmpFace.v1;
1146 tmpFace.v1 = tmp;
1147 this.faces[i] = tmpFace;
1148 }
1149
1150 if (this.calcVertexNormals)
1151 {
1152 int normalCount = this.vertexNormals.Count;
1153 if (normalCount > 0)
1154 {
1155 Coord n = this.vertexNormals[normalCount - 1];
1156 n.Z = -n.Z;
1157 this.vertexNormals[normalCount - 1] = n;
1158 }
1159 }
1160
1161 this.faceNormal.X = -this.faceNormal.X;
1162 this.faceNormal.Y = -this.faceNormal.Y;
1163 this.faceNormal.Z = -this.faceNormal.Z;
1164
1165 int numfaceUVs = this.faceUVs.Count;
1166 for (i = 0; i < numfaceUVs; i++)
1167 {
1168 UVCoord uv = this.faceUVs[i];
1169 uv.V = 1.0f - uv.V;
1170 this.faceUVs[i] = uv;
1171 }
1172 }
1173
1174 public void AddValue2FaceVertexIndices(int num)
1175 {
1176 int numFaces = this.faces.Count;
1177 Face tmpFace;
1178 for (int i = 0; i < numFaces; i++)
1179 {
1180 tmpFace = this.faces[i];
1181 tmpFace.v1 += num;
1182 tmpFace.v2 += num;
1183 tmpFace.v3 += num;
1184
1185 this.faces[i] = tmpFace;
1186 }
1187 }
1188
1189 public void AddValue2FaceNormalIndices(int num)
1190 {
1191 if (this.calcVertexNormals)
1192 {
1193 int numFaces = this.faces.Count;
1194 Face tmpFace;
1195 for (int i = 0; i < numFaces; i++)
1196 {
1197 tmpFace = this.faces[i];
1198 tmpFace.n1 += num;
1199 tmpFace.n2 += num;
1200 tmpFace.n3 += num;
1201
1202 this.faces[i] = tmpFace;
1203 }
1204 }
1205 }
1206
1207 public void DumpRaw(String path, String name, String title)
1208 {
1209 if (path == null)
1210 return;
1211 String fileName = name + "_" + title + ".raw";
1212 String completePath = System.IO.Path.Combine(path, fileName);
1213 StreamWriter sw = new StreamWriter(completePath);
1214
1215 for (int i = 0; i < this.faces.Count; i++)
1216 {
1217 string s = this.coords[this.faces[i].v1].ToString();
1218 s += " " + this.coords[this.faces[i].v2].ToString();
1219 s += " " + this.coords[this.faces[i].v3].ToString();
1220
1221 sw.WriteLine(s);
1222 }
1223
1224 sw.Close();
1225 }
1226 }
1227
1228 public struct PathNode
1229 {
1230 public Coord position;
1231 public Quat rotation;
1232 public float xScale;
1233 public float yScale;
1234 public float percentOfPath;
1235 }
1236
1237 public enum PathType { Linear = 0, Circular = 1, Flexible = 2 }
1238
1239 public class Path
1240 {
1241 public List<PathNode> pathNodes = new List<PathNode>();
1242
1243 public float twistBegin = 0.0f;
1244 public float twistEnd = 0.0f;
1245 public float topShearX = 0.0f;
1246 public float topShearY = 0.0f;
1247 public float pathCutBegin = 0.0f;
1248 public float pathCutEnd = 1.0f;
1249 public float dimpleBegin = 0.0f;
1250 public float dimpleEnd = 1.0f;
1251 public float skew = 0.0f;
1252 public float holeSizeX = 1.0f; // called pathScaleX in pbs
1253 public float holeSizeY = 0.25f;
1254 public float taperX = 0.0f;
1255 public float taperY = 0.0f;
1256 public float radius = 0.0f;
1257 public float revolutions = 1.0f;
1258 public int stepsPerRevolution = 24;
1259
1260 private const float twoPi = 2.0f * (float)Math.PI;
1261
1262 public void Create(PathType pathType, int steps)
1263 {
1264 if (this.taperX > 0.999f)
1265 this.taperX = 0.999f;
1266 if (this.taperX < -0.999f)
1267 this.taperX = -0.999f;
1268 if (this.taperY > 0.999f)
1269 this.taperY = 0.999f;
1270 if (this.taperY < -0.999f)
1271 this.taperY = -0.999f;
1272
1273 if (pathType == PathType.Linear || pathType == PathType.Flexible)
1274 {
1275 int step = 0;
1276
1277 float length = this.pathCutEnd - this.pathCutBegin;
1278 float twistTotal = twistEnd - twistBegin;
1279 float twistTotalAbs = Math.Abs(twistTotal);
1280 if (twistTotalAbs > 0.01f)
1281 steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number
1282
1283 float start = -0.5f;
1284 float stepSize = length / (float)steps;
1285 float percentOfPathMultiplier = stepSize * 0.999999f;
1286 float xOffset = this.topShearX * this.pathCutBegin;
1287 float yOffset = this.topShearY * this.pathCutBegin;
1288 float zOffset = start;
1289 float xOffsetStepIncrement = this.topShearX * length / steps;
1290 float yOffsetStepIncrement = this.topShearY * length / steps;
1291
1292 float percentOfPath = this.pathCutBegin;
1293 zOffset += percentOfPath;
1294
1295 // sanity checks
1296
1297 bool done = false;
1298
1299 while (!done)
1300 {
1301 PathNode newNode = new PathNode();
1302
1303 newNode.xScale = 1.0f;
1304 if (this.taperX == 0.0f)
1305 newNode.xScale = 1.0f;
1306 else if (this.taperX > 0.0f)
1307 newNode.xScale = 1.0f - percentOfPath * this.taperX;
1308 else newNode.xScale = 1.0f + (1.0f - percentOfPath) * this.taperX;
1309
1310 newNode.yScale = 1.0f;
1311 if (this.taperY == 0.0f)
1312 newNode.yScale = 1.0f;
1313 else if (this.taperY > 0.0f)
1314 newNode.yScale = 1.0f - percentOfPath * this.taperY;
1315 else newNode.yScale = 1.0f + (1.0f - percentOfPath) * this.taperY;
1316
1317 float twist = twistBegin + twistTotal * percentOfPath;
1318
1319 newNode.rotation = new Quat(new Coord(0.0f, 0.0f, 1.0f), twist);
1320 newNode.position = new Coord(xOffset, yOffset, zOffset);
1321 newNode.percentOfPath = percentOfPath;
1322
1323 pathNodes.Add(newNode);
1324
1325 if (step < steps)
1326 {
1327 step += 1;
1328 percentOfPath += percentOfPathMultiplier;
1329 xOffset += xOffsetStepIncrement;
1330 yOffset += yOffsetStepIncrement;
1331 zOffset += stepSize;
1332 if (percentOfPath > this.pathCutEnd)
1333 done = true;
1334 }
1335 else done = true;
1336 }
1337 } // end of linear path code
1338
1339 else // pathType == Circular
1340 {
1341 float twistTotal = twistEnd - twistBegin;
1342
1343 // if the profile has a lot of twist, add more layers otherwise the layers may overlap
1344 // and the resulting mesh may be quite inaccurate. This method is arbitrary and doesn't
1345 // accurately match the viewer
1346 float twistTotalAbs = Math.Abs(twistTotal);
1347 if (twistTotalAbs > 0.01f)
1348 {
1349 if (twistTotalAbs > Math.PI * 1.5f)
1350 steps *= 2;
1351 if (twistTotalAbs > Math.PI * 3.0f)
1352 steps *= 2;
1353 }
1354
1355 float yPathScale = this.holeSizeY * 0.5f;
1356 float pathLength = this.pathCutEnd - this.pathCutBegin;
1357 float totalSkew = this.skew * 2.0f * pathLength;
1358 float skewStart = this.pathCutBegin * 2.0f * this.skew - this.skew;
1359 float xOffsetTopShearXFactor = this.topShearX * (0.25f + 0.5f * (0.5f - this.holeSizeY));
1360 float yShearCompensation = 1.0f + Math.Abs(this.topShearY) * 0.25f;
1361
1362 // It's not quite clear what pushY (Y top shear) does, but subtracting it from the start and end
1363 // angles appears to approximate it's effects on path cut. Likewise, adding it to the angle used
1364 // to calculate the sine for generating the path radius appears to approximate it's effects there
1365 // too, but there are some subtle differences in the radius which are noticeable as the prim size
1366 // increases and it may affect megaprims quite a bit. The effect of the Y top shear parameter on
1367 // the meshes generated with this technique appear nearly identical in shape to the same prims when
1368 // displayed by the viewer.
1369
1370 float startAngle = (twoPi * this.pathCutBegin * this.revolutions) - this.topShearY * 0.9f;
1371 float endAngle = (twoPi * this.pathCutEnd * this.revolutions) - this.topShearY * 0.9f;
1372 float stepSize = twoPi / this.stepsPerRevolution;
1373
1374 int step = (int)(startAngle / stepSize);
1375 float angle = startAngle;
1376
1377 bool done = false;
1378 while (!done) // loop through the length of the path and add the layers
1379 {
1380 PathNode newNode = new PathNode();
1381
1382 float xProfileScale = (1.0f - Math.Abs(this.skew)) * this.holeSizeX;
1383 float yProfileScale = this.holeSizeY;
1384
1385 float percentOfPath = angle / (twoPi * this.revolutions);
1386 float percentOfAngles = (angle - startAngle) / (endAngle - startAngle);
1387
1388 if (this.taperX > 0.01f)
1389 xProfileScale *= 1.0f - percentOfPath * this.taperX;
1390 else if (this.taperX < -0.01f)
1391 xProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperX;
1392
1393 if (this.taperY > 0.01f)
1394 yProfileScale *= 1.0f - percentOfPath * this.taperY;
1395 else if (this.taperY < -0.01f)
1396 yProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperY;
1397
1398 newNode.xScale = xProfileScale;
1399 newNode.yScale = yProfileScale;
1400
1401 float radiusScale = 1.0f;
1402 if (this.radius > 0.001f)
1403 radiusScale = 1.0f - this.radius * percentOfPath;
1404 else if (this.radius < 0.001f)
1405 radiusScale = 1.0f + this.radius * (1.0f - percentOfPath);
1406
1407 float twist = twistBegin + twistTotal * percentOfPath;
1408
1409 float xOffset = 0.5f * (skewStart + totalSkew * percentOfAngles);
1410 xOffset += (float)Math.Sin(angle) * xOffsetTopShearXFactor;
1411
1412 float yOffset = yShearCompensation * (float)Math.Cos(angle) * (0.5f - yPathScale) * radiusScale;
1413
1414 float zOffset = (float)Math.Sin(angle + this.topShearY) * (0.5f - yPathScale) * radiusScale;
1415
1416 newNode.position = new Coord(xOffset, yOffset, zOffset);
1417
1418 // now orient the rotation of the profile layer relative to it's position on the path
1419 // adding taperY to the angle used to generate the quat appears to approximate the viewer
1420
1421 newNode.rotation = new Quat(new Coord(1.0f, 0.0f, 0.0f), angle + this.topShearY);
1422
1423 // next apply twist rotation to the profile layer
1424 if (twistTotal != 0.0f || twistBegin != 0.0f)
1425 newNode.rotation *= new Quat(new Coord(0.0f, 0.0f, 1.0f), twist);
1426
1427 newNode.percentOfPath = percentOfPath;
1428
1429 pathNodes.Add(newNode);
1430
1431 // calculate terms for next iteration
1432 // calculate the angle for the next iteration of the loop
1433
1434 if (angle >= endAngle - 0.01)
1435 done = true;
1436 else
1437 {
1438 step += 1;
1439 angle = stepSize * step;
1440 if (angle > endAngle)
1441 angle = endAngle;
1442 }
1443 }
1444 }
1445 }
1446 }
1447
1448 public class PrimMesh
1449 {
1450 public string errorMessage = "";
1451 private const float twoPi = 2.0f * (float)Math.PI;
1452
1453 public List<Coord> coords;
1454 public List<Coord> normals;
1455 public List<Face> faces;
1456
1457 public List<ViewerFace> viewerFaces;
1458
1459 private int sides = 4;
1460 private int hollowSides = 4;
1461 private float profileStart = 0.0f;
1462 private float profileEnd = 1.0f;
1463 private float hollow = 0.0f;
1464 public int twistBegin = 0;
1465 public int twistEnd = 0;
1466 public float topShearX = 0.0f;
1467 public float topShearY = 0.0f;
1468 public float pathCutBegin = 0.0f;
1469 public float pathCutEnd = 1.0f;
1470 public float dimpleBegin = 0.0f;
1471 public float dimpleEnd = 1.0f;
1472 public float skew = 0.0f;
1473 public float holeSizeX = 1.0f; // called pathScaleX in pbs
1474 public float holeSizeY = 0.25f;
1475 public float taperX = 0.0f;
1476 public float taperY = 0.0f;
1477 public float radius = 0.0f;
1478 public float revolutions = 1.0f;
1479 public int stepsPerRevolution = 24;
1480
1481 private int profileOuterFaceNumber = -1;
1482 private int profileHollowFaceNumber = -1;
1483
1484 private bool hasProfileCut = false;
1485 private bool hasHollow = false;
1486 public bool calcVertexNormals = false;
1487 private bool normalsProcessed = false;
1488 public bool viewerMode = false;
1489 public bool sphereMode = false;
1490
1491 public int numPrimFaces = 0;
1492
1493 /// <summary>
1494 /// Human readable string representation of the parameters used to create a mesh.
1495 /// </summary>
1496 /// <returns></returns>
1497 public string ParamsToDisplayString()
1498 {
1499 string s = "";
1500 s += "sides..................: " + this.sides.ToString();
1501 s += "\nhollowSides..........: " + this.hollowSides.ToString();
1502 s += "\nprofileStart.........: " + this.profileStart.ToString();
1503 s += "\nprofileEnd...........: " + this.profileEnd.ToString();
1504 s += "\nhollow...............: " + this.hollow.ToString();
1505 s += "\ntwistBegin...........: " + this.twistBegin.ToString();
1506 s += "\ntwistEnd.............: " + this.twistEnd.ToString();
1507 s += "\ntopShearX............: " + this.topShearX.ToString();
1508 s += "\ntopShearY............: " + this.topShearY.ToString();
1509 s += "\npathCutBegin.........: " + this.pathCutBegin.ToString();
1510 s += "\npathCutEnd...........: " + this.pathCutEnd.ToString();
1511 s += "\ndimpleBegin..........: " + this.dimpleBegin.ToString();
1512 s += "\ndimpleEnd............: " + this.dimpleEnd.ToString();
1513 s += "\nskew.................: " + this.skew.ToString();
1514 s += "\nholeSizeX............: " + this.holeSizeX.ToString();
1515 s += "\nholeSizeY............: " + this.holeSizeY.ToString();
1516 s += "\ntaperX...............: " + this.taperX.ToString();
1517 s += "\ntaperY...............: " + this.taperY.ToString();
1518 s += "\nradius...............: " + this.radius.ToString();
1519 s += "\nrevolutions..........: " + this.revolutions.ToString();
1520 s += "\nstepsPerRevolution...: " + this.stepsPerRevolution.ToString();
1521 s += "\nsphereMode...........: " + this.sphereMode.ToString();
1522 s += "\nhasProfileCut........: " + this.hasProfileCut.ToString();
1523 s += "\nhasHollow............: " + this.hasHollow.ToString();
1524 s += "\nviewerMode...........: " + this.viewerMode.ToString();
1525
1526 return s;
1527 }
1528
1529 public int ProfileOuterFaceNumber
1530 {
1531 get { return profileOuterFaceNumber; }
1532 }
1533
1534 public int ProfileHollowFaceNumber
1535 {
1536 get { return profileHollowFaceNumber; }
1537 }
1538
1539 public bool HasProfileCut
1540 {
1541 get { return hasProfileCut; }
1542 }
1543
1544 public bool HasHollow
1545 {
1546 get { return hasHollow; }
1547 }
1548
1549
1550 /// <summary>
1551 /// Constructs a PrimMesh object and creates the profile for extrusion.
1552 /// </summary>
1553 /// <param name="sides"></param>
1554 /// <param name="profileStart"></param>
1555 /// <param name="profileEnd"></param>
1556 /// <param name="hollow"></param>
1557 /// <param name="hollowSides"></param>
1558 public PrimMesh(int sides, float profileStart, float profileEnd, float hollow, int hollowSides)
1559 {
1560 this.coords = new List<Coord>();
1561 this.faces = new List<Face>();
1562
1563 this.sides = sides;
1564 this.profileStart = profileStart;
1565 this.profileEnd = profileEnd;
1566 this.hollow = hollow;
1567 this.hollowSides = hollowSides;
1568
1569 if (sides < 3)
1570 this.sides = 3;
1571 if (hollowSides < 3)
1572 this.hollowSides = 3;
1573 if (profileStart < 0.0f)
1574 this.profileStart = 0.0f;
1575 if (profileEnd > 1.0f)
1576 this.profileEnd = 1.0f;
1577 if (profileEnd < 0.02f)
1578 this.profileEnd = 0.02f;
1579 if (profileStart >= profileEnd)
1580 this.profileStart = profileEnd - 0.02f;
1581 if (hollow > 0.99f)
1582 this.hollow = 0.99f;
1583 if (hollow < 0.0f)
1584 this.hollow = 0.0f;
1585 }
1586
1587 /// <summary>
1588 /// Extrudes a profile along a path.
1589 /// </summary>
1590 public void Extrude(PathType pathType)
1591 {
1592 bool needEndFaces = false;
1593
1594 this.coords = new List<Coord>();
1595 this.faces = new List<Face>();
1596
1597 if (this.viewerMode)
1598 {
1599 this.viewerFaces = new List<ViewerFace>();
1600 this.calcVertexNormals = true;
1601 }
1602
1603 if (this.calcVertexNormals)
1604 this.normals = new List<Coord>();
1605
1606 int steps = 1;
1607
1608 float length = this.pathCutEnd - this.pathCutBegin;
1609 normalsProcessed = false;
1610
1611 if (this.viewerMode && this.sides == 3)
1612 {
1613 // prisms don't taper well so add some vertical resolution
1614 // other prims may benefit from this but just do prisms for now
1615 if (Math.Abs(this.taperX) > 0.01 || Math.Abs(this.taperY) > 0.01)
1616 steps = (int)(steps * 4.5 * length);
1617 }
1618
1619 if (this.sphereMode)
1620 this.hasProfileCut = this.profileEnd - this.profileStart < 0.4999f;
1621 else
1622 this.hasProfileCut = this.profileEnd - this.profileStart < 0.9999f;
1623 this.hasHollow = (this.hollow > 0.001f);
1624
1625 float twistBegin = this.twistBegin / 360.0f * twoPi;
1626 float twistEnd = this.twistEnd / 360.0f * twoPi;
1627 float twistTotal = twistEnd - twistBegin;
1628 float twistTotalAbs = Math.Abs(twistTotal);
1629 if (twistTotalAbs > 0.01f)
1630 steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number
1631
1632 float hollow = this.hollow;
1633
1634 if (pathType == PathType.Circular)
1635 {
1636 needEndFaces = false;
1637 if (this.pathCutBegin != 0.0f || this.pathCutEnd != 1.0f)
1638 needEndFaces = true;
1639 else if (this.taperX != 0.0f || this.taperY != 0.0f)
1640 needEndFaces = true;
1641 else if (this.skew != 0.0f)
1642 needEndFaces = true;
1643 else if (twistTotal != 0.0f)
1644 needEndFaces = true;
1645 else if (this.radius != 0.0f)
1646 needEndFaces = true;
1647 }
1648 else needEndFaces = true;
1649
1650 // sanity checks
1651 float initialProfileRot = 0.0f;
1652 if (pathType == PathType.Circular)
1653 {
1654 if (this.sides == 3)
1655 {
1656 initialProfileRot = (float)Math.PI;
1657 if (this.hollowSides == 4)
1658 {
1659 if (hollow > 0.7f)
1660 hollow = 0.7f;
1661 hollow *= 0.707f;
1662 }
1663 else hollow *= 0.5f;
1664 }
1665 else if (this.sides == 4)
1666 {
1667 initialProfileRot = 0.25f * (float)Math.PI;
1668 if (this.hollowSides != 4)
1669 hollow *= 0.707f;
1670 }
1671 else if (this.sides > 4)
1672 {
1673 initialProfileRot = (float)Math.PI;
1674 if (this.hollowSides == 4)
1675 {
1676 if (hollow > 0.7f)
1677 hollow = 0.7f;
1678 hollow /= 0.7f;
1679 }
1680 }
1681 }
1682 else
1683 {
1684 if (this.sides == 3)
1685 {
1686 if (this.hollowSides == 4)
1687 {
1688 if (hollow > 0.7f)
1689 hollow = 0.7f;
1690 hollow *= 0.707f;
1691 }
1692 else hollow *= 0.5f;
1693 }
1694 else if (this.sides == 4)
1695 {
1696 initialProfileRot = 1.25f * (float)Math.PI;
1697 if (this.hollowSides != 4)
1698 hollow *= 0.707f;
1699 }
1700 else if (this.sides == 24 && this.hollowSides == 4)
1701 hollow *= 1.414f;
1702 }
1703
1704 Profile profile = new Profile(this.sides, this.profileStart, this.profileEnd, hollow, this.hollowSides, true, calcVertexNormals);
1705 this.errorMessage = profile.errorMessage;
1706
1707 this.numPrimFaces = profile.numPrimFaces;
1708
1709 int cut1FaceNumber = profile.bottomFaceNumber + 1;
1710 int cut2FaceNumber = cut1FaceNumber + 1;
1711 if (!needEndFaces)
1712 {
1713 cut1FaceNumber -= 2;
1714 cut2FaceNumber -= 2;
1715 }
1716
1717 profileOuterFaceNumber = profile.outerFaceNumber;
1718 if (!needEndFaces)
1719 profileOuterFaceNumber--;
1720
1721 if (hasHollow)
1722 {
1723 profileHollowFaceNumber = profile.hollowFaceNumber;
1724 if (!needEndFaces)
1725 profileHollowFaceNumber--;
1726 }
1727
1728 int cut1Vert = -1;
1729 int cut2Vert = -1;
1730 if (hasProfileCut)
1731 {
1732 cut1Vert = hasHollow ? profile.coords.Count - 1 : 0;
1733 cut2Vert = hasHollow ? profile.numOuterVerts - 1 : profile.numOuterVerts;
1734 }
1735
1736 if (initialProfileRot != 0.0f)
1737 {
1738 profile.AddRot(new Quat(new Coord(0.0f, 0.0f, 1.0f), initialProfileRot));
1739 if (viewerMode)
1740 profile.MakeFaceUVs();
1741 }
1742
1743 Coord lastCutNormal1 = new Coord();
1744 Coord lastCutNormal2 = new Coord();
1745 float thisV = 0.0f;
1746 float lastV = 0.0f;
1747
1748 Path path = new Path();
1749 path.twistBegin = twistBegin;
1750 path.twistEnd = twistEnd;
1751 path.topShearX = topShearX;
1752 path.topShearY = topShearY;
1753 path.pathCutBegin = pathCutBegin;
1754 path.pathCutEnd = pathCutEnd;
1755 path.dimpleBegin = dimpleBegin;
1756 path.dimpleEnd = dimpleEnd;
1757 path.skew = skew;
1758 path.holeSizeX = holeSizeX;
1759 path.holeSizeY = holeSizeY;
1760 path.taperX = taperX;
1761 path.taperY = taperY;
1762 path.radius = radius;
1763 path.revolutions = revolutions;
1764 path.stepsPerRevolution = stepsPerRevolution;
1765
1766 path.Create(pathType, steps);
1767
1768 for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++)
1769 {
1770 PathNode node = path.pathNodes[nodeIndex];
1771 Profile newLayer = profile.Copy();
1772 newLayer.Scale(node.xScale, node.yScale);
1773
1774 newLayer.AddRot(node.rotation);
1775 newLayer.AddPos(node.position);
1776
1777 if (needEndFaces && nodeIndex == 0)
1778 {
1779 newLayer.FlipNormals();
1780
1781 // add the bottom faces to the viewerFaces list
1782 if (this.viewerMode)
1783 {
1784 Coord faceNormal = newLayer.faceNormal;
1785 ViewerFace newViewerFace = new ViewerFace(profile.bottomFaceNumber);
1786 int numFaces = newLayer.faces.Count;
1787 List<Face> faces = newLayer.faces;
1788
1789 for (int i = 0; i < numFaces; i++)
1790 {
1791 Face face = faces[i];
1792 newViewerFace.v1 = newLayer.coords[face.v1];
1793 newViewerFace.v2 = newLayer.coords[face.v2];
1794 newViewerFace.v3 = newLayer.coords[face.v3];
1795
1796 newViewerFace.coordIndex1 = face.v1;
1797 newViewerFace.coordIndex2 = face.v2;
1798 newViewerFace.coordIndex3 = face.v3;
1799
1800 newViewerFace.n1 = faceNormal;
1801 newViewerFace.n2 = faceNormal;
1802 newViewerFace.n3 = faceNormal;
1803
1804 newViewerFace.uv1 = newLayer.faceUVs[face.v1];
1805 newViewerFace.uv2 = newLayer.faceUVs[face.v2];
1806 newViewerFace.uv3 = newLayer.faceUVs[face.v3];
1807
1808 if (pathType == PathType.Linear)
1809 {
1810 newViewerFace.uv1.Flip();
1811 newViewerFace.uv2.Flip();
1812 newViewerFace.uv3.Flip();
1813 }
1814
1815 this.viewerFaces.Add(newViewerFace);
1816 }
1817 }
1818 } // if (nodeIndex == 0)
1819
1820 // append this layer
1821
1822 int coordsLen = this.coords.Count;
1823 newLayer.AddValue2FaceVertexIndices(coordsLen);
1824
1825 this.coords.AddRange(newLayer.coords);
1826
1827 if (this.calcVertexNormals)
1828 {
1829 newLayer.AddValue2FaceNormalIndices(this.normals.Count);
1830 this.normals.AddRange(newLayer.vertexNormals);
1831 }
1832
1833 if (node.percentOfPath < this.pathCutBegin + 0.01f || node.percentOfPath > this.pathCutEnd - 0.01f)
1834 this.faces.AddRange(newLayer.faces);
1835
1836 // fill faces between layers
1837
1838 int numVerts = newLayer.coords.Count;
1839 Face newFace1 = new Face();
1840 Face newFace2 = new Face();
1841
1842 thisV = 1.0f - node.percentOfPath;
1843
1844 if (nodeIndex > 0)
1845 {
1846 int startVert = coordsLen + 1;
1847 int endVert = this.coords.Count;
1848
1849 if (sides < 5 || this.hasProfileCut || this.hasHollow)
1850 startVert--;
1851
1852 for (int i = startVert; i < endVert; i++)
1853 {
1854 int iNext = i + 1;
1855 if (i == endVert - 1)
1856 iNext = startVert;
1857
1858 int whichVert = i - startVert;
1859
1860 newFace1.v1 = i;
1861 newFace1.v2 = i - numVerts;
1862 newFace1.v3 = iNext;
1863
1864 newFace1.n1 = newFace1.v1;
1865 newFace1.n2 = newFace1.v2;
1866 newFace1.n3 = newFace1.v3;
1867 this.faces.Add(newFace1);
1868
1869 newFace2.v1 = iNext;
1870 newFace2.v2 = i - numVerts;
1871 newFace2.v3 = iNext - numVerts;
1872
1873 newFace2.n1 = newFace2.v1;
1874 newFace2.n2 = newFace2.v2;
1875 newFace2.n3 = newFace2.v3;
1876 this.faces.Add(newFace2);
1877
1878 if (this.viewerMode)
1879 {
1880 // add the side faces to the list of viewerFaces here
1881
1882 int primFaceNum = profile.faceNumbers[whichVert];
1883 if (!needEndFaces)
1884 primFaceNum -= 1;
1885
1886 ViewerFace newViewerFace1 = new ViewerFace(primFaceNum);
1887 ViewerFace newViewerFace2 = new ViewerFace(primFaceNum);
1888
1889 int uIndex = whichVert;
1890 if (!hasHollow && sides > 4 && uIndex < newLayer.us.Count - 1)
1891 {
1892 uIndex++;
1893 }
1894
1895 float u1 = newLayer.us[uIndex];
1896 float u2 = 1.0f;
1897 if (uIndex < (int)newLayer.us.Count - 1)
1898 u2 = newLayer.us[uIndex + 1];
1899
1900 if (whichVert == cut1Vert || whichVert == cut2Vert)
1901 {
1902 u1 = 0.0f;
1903 u2 = 1.0f;
1904 }
1905 else if (sides < 5)
1906 {
1907 if (whichVert < profile.numOuterVerts)
1908 { // boxes and prisms have one texture face per side of the prim, so the U values have to be scaled
1909 // to reflect the entire texture width
1910 u1 *= sides;
1911 u2 *= sides;
1912 u2 -= (int)u1;
1913 u1 -= (int)u1;
1914 if (u2 < 0.1f)
1915 u2 = 1.0f;
1916 }
1917 }
1918
1919 if (this.sphereMode)
1920 {
1921 if (whichVert != cut1Vert && whichVert != cut2Vert)
1922 {
1923 u1 = u1 * 2.0f - 1.0f;
1924 u2 = u2 * 2.0f - 1.0f;
1925
1926 if (whichVert >= newLayer.numOuterVerts)
1927 {
1928 u1 -= hollow;
1929 u2 -= hollow;
1930 }
1931
1932 }
1933 }
1934
1935 newViewerFace1.uv1.U = u1;
1936 newViewerFace1.uv2.U = u1;
1937 newViewerFace1.uv3.U = u2;
1938
1939 newViewerFace1.uv1.V = thisV;
1940 newViewerFace1.uv2.V = lastV;
1941 newViewerFace1.uv3.V = thisV;
1942
1943 newViewerFace2.uv1.U = u2;
1944 newViewerFace2.uv2.U = u1;
1945 newViewerFace2.uv3.U = u2;
1946
1947 newViewerFace2.uv1.V = thisV;
1948 newViewerFace2.uv2.V = lastV;
1949 newViewerFace2.uv3.V = lastV;
1950
1951 newViewerFace1.v1 = this.coords[newFace1.v1];
1952 newViewerFace1.v2 = this.coords[newFace1.v2];
1953 newViewerFace1.v3 = this.coords[newFace1.v3];
1954
1955 newViewerFace2.v1 = this.coords[newFace2.v1];
1956 newViewerFace2.v2 = this.coords[newFace2.v2];
1957 newViewerFace2.v3 = this.coords[newFace2.v3];
1958
1959 newViewerFace1.coordIndex1 = newFace1.v1;
1960 newViewerFace1.coordIndex2 = newFace1.v2;
1961 newViewerFace1.coordIndex3 = newFace1.v3;
1962
1963 newViewerFace2.coordIndex1 = newFace2.v1;
1964 newViewerFace2.coordIndex2 = newFace2.v2;
1965 newViewerFace2.coordIndex3 = newFace2.v3;
1966
1967 // profile cut faces
1968 if (whichVert == cut1Vert)
1969 {
1970 newViewerFace1.primFaceNumber = cut1FaceNumber;
1971 newViewerFace2.primFaceNumber = cut1FaceNumber;
1972 newViewerFace1.n1 = newLayer.cutNormal1;
1973 newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal1;
1974
1975 newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal1;
1976 newViewerFace2.n2 = lastCutNormal1;
1977 }
1978 else if (whichVert == cut2Vert)
1979 {
1980 newViewerFace1.primFaceNumber = cut2FaceNumber;
1981 newViewerFace2.primFaceNumber = cut2FaceNumber;
1982 newViewerFace1.n1 = newLayer.cutNormal2;
1983 newViewerFace1.n2 = lastCutNormal2;
1984 newViewerFace1.n3 = lastCutNormal2;
1985
1986 newViewerFace2.n1 = newLayer.cutNormal2;
1987 newViewerFace2.n3 = newLayer.cutNormal2;
1988 newViewerFace2.n2 = lastCutNormal2;
1989 }
1990
1991 else // outer and hollow faces
1992 {
1993 if ((sides < 5 && whichVert < newLayer.numOuterVerts) || (hollowSides < 5 && whichVert >= newLayer.numOuterVerts))
1994 { // looks terrible when path is twisted... need vertex normals here
1995 newViewerFace1.CalcSurfaceNormal();
1996 newViewerFace2.CalcSurfaceNormal();
1997 }
1998 else
1999 {
2000 newViewerFace1.n1 = this.normals[newFace1.n1];
2001 newViewerFace1.n2 = this.normals[newFace1.n2];
2002 newViewerFace1.n3 = this.normals[newFace1.n3];
2003
2004 newViewerFace2.n1 = this.normals[newFace2.n1];
2005 newViewerFace2.n2 = this.normals[newFace2.n2];
2006 newViewerFace2.n3 = this.normals[newFace2.n3];
2007 }
2008 }
2009
2010 this.viewerFaces.Add(newViewerFace1);
2011 this.viewerFaces.Add(newViewerFace2);
2012
2013 }
2014 }
2015 }
2016
2017 lastCutNormal1 = newLayer.cutNormal1;
2018 lastCutNormal2 = newLayer.cutNormal2;
2019 lastV = thisV;
2020
2021 if (needEndFaces && nodeIndex == path.pathNodes.Count - 1 && viewerMode)
2022 {
2023 // add the top faces to the viewerFaces list here
2024 Coord faceNormal = newLayer.faceNormal;
2025 ViewerFace newViewerFace = new ViewerFace(0);
2026 int numFaces = newLayer.faces.Count;
2027 List<Face> faces = newLayer.faces;
2028
2029 for (int i = 0; i < numFaces; i++)
2030 {
2031 Face face = faces[i];
2032 newViewerFace.v1 = newLayer.coords[face.v1 - coordsLen];
2033 newViewerFace.v2 = newLayer.coords[face.v2 - coordsLen];
2034 newViewerFace.v3 = newLayer.coords[face.v3 - coordsLen];
2035
2036 newViewerFace.coordIndex1 = face.v1 - coordsLen;
2037 newViewerFace.coordIndex2 = face.v2 - coordsLen;
2038 newViewerFace.coordIndex3 = face.v3 - coordsLen;
2039
2040 newViewerFace.n1 = faceNormal;
2041 newViewerFace.n2 = faceNormal;
2042 newViewerFace.n3 = faceNormal;
2043
2044 newViewerFace.uv1 = newLayer.faceUVs[face.v1 - coordsLen];
2045 newViewerFace.uv2 = newLayer.faceUVs[face.v2 - coordsLen];
2046 newViewerFace.uv3 = newLayer.faceUVs[face.v3 - coordsLen];
2047
2048 if (pathType == PathType.Linear)
2049 {
2050 newViewerFace.uv1.Flip();
2051 newViewerFace.uv2.Flip();
2052 newViewerFace.uv3.Flip();
2053 }
2054
2055 this.viewerFaces.Add(newViewerFace);
2056 }
2057 }
2058
2059
2060 } // for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++)
2061
2062 }
2063
2064
2065 /// <summary>
2066 /// DEPRICATED - use Extrude(PathType.Linear) instead
2067 /// Extrudes a profile along a straight line path. Used for prim types box, cylinder, and prism.
2068 /// </summary>
2069 ///
2070 public void ExtrudeLinear()
2071 {
2072 this.Extrude(PathType.Linear);
2073 }
2074
2075
2076 /// <summary>
2077 /// DEPRICATED - use Extrude(PathType.Circular) instead
2078 /// Extrude a profile into a circular path prim mesh. Used for prim types torus, tube, and ring.
2079 /// </summary>
2080 ///
2081 public void ExtrudeCircular()
2082 {
2083 this.Extrude(PathType.Circular);
2084 }
2085
2086
2087 private Coord SurfaceNormal(Coord c1, Coord c2, Coord c3)
2088 {
2089 Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z);
2090 Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z);
2091
2092 Coord normal = Coord.Cross(edge1, edge2);
2093
2094 normal.Normalize();
2095
2096 return normal;
2097 }
2098
2099 private Coord SurfaceNormal(Face face)
2100 {
2101 return SurfaceNormal(this.coords[face.v1], this.coords[face.v2], this.coords[face.v3]);
2102 }
2103
2104 /// <summary>
2105 /// Calculate the surface normal for a face in the list of faces
2106 /// </summary>
2107 /// <param name="faceIndex"></param>
2108 /// <returns></returns>
2109 public Coord SurfaceNormal(int faceIndex)
2110 {
2111 int numFaces = this.faces.Count;
2112 if (faceIndex < 0 || faceIndex >= numFaces)
2113 throw new Exception("faceIndex out of range");
2114
2115 return SurfaceNormal(this.faces[faceIndex]);
2116 }
2117
2118 /// <summary>
2119 /// Duplicates a PrimMesh object. All object properties are copied by value, including lists.
2120 /// </summary>
2121 /// <returns></returns>
2122 public PrimMesh Copy()
2123 {
2124 PrimMesh copy = new PrimMesh(this.sides, this.profileStart, this.profileEnd, this.hollow, this.hollowSides);
2125 copy.twistBegin = this.twistBegin;
2126 copy.twistEnd = this.twistEnd;
2127 copy.topShearX = this.topShearX;
2128 copy.topShearY = this.topShearY;
2129 copy.pathCutBegin = this.pathCutBegin;
2130 copy.pathCutEnd = this.pathCutEnd;
2131 copy.dimpleBegin = this.dimpleBegin;
2132 copy.dimpleEnd = this.dimpleEnd;
2133 copy.skew = this.skew;
2134 copy.holeSizeX = this.holeSizeX;
2135 copy.holeSizeY = this.holeSizeY;
2136 copy.taperX = this.taperX;
2137 copy.taperY = this.taperY;
2138 copy.radius = this.radius;
2139 copy.revolutions = this.revolutions;
2140 copy.stepsPerRevolution = this.stepsPerRevolution;
2141 copy.calcVertexNormals = this.calcVertexNormals;
2142 copy.normalsProcessed = this.normalsProcessed;
2143 copy.viewerMode = this.viewerMode;
2144 copy.numPrimFaces = this.numPrimFaces;
2145 copy.errorMessage = this.errorMessage;
2146
2147 copy.coords = new List<Coord>(this.coords);
2148 copy.faces = new List<Face>(this.faces);
2149 copy.viewerFaces = new List<ViewerFace>(this.viewerFaces);
2150 copy.normals = new List<Coord>(this.normals);
2151
2152 return copy;
2153 }
2154
2155 /// <summary>
2156 /// Calculate surface normals for all of the faces in the list of faces in this mesh
2157 /// </summary>
2158 public void CalcNormals()
2159 {
2160 if (normalsProcessed)
2161 return;
2162
2163 normalsProcessed = true;
2164
2165 int numFaces = faces.Count;
2166
2167 if (!this.calcVertexNormals)
2168 this.normals = new List<Coord>();
2169
2170 for (int i = 0; i < numFaces; i++)
2171 {
2172 Face face = faces[i];
2173
2174 this.normals.Add(SurfaceNormal(i).Normalize());
2175
2176 int normIndex = normals.Count - 1;
2177 face.n1 = normIndex;
2178 face.n2 = normIndex;
2179 face.n3 = normIndex;
2180
2181 this.faces[i] = face;
2182 }
2183 }
2184
2185 /// <summary>
2186 /// Adds a value to each XYZ vertex coordinate in the mesh
2187 /// </summary>
2188 /// <param name="x"></param>
2189 /// <param name="y"></param>
2190 /// <param name="z"></param>
2191 public void AddPos(float x, float y, float z)
2192 {
2193 int i;
2194 int numVerts = this.coords.Count;
2195 Coord vert;
2196
2197 for (i = 0; i < numVerts; i++)
2198 {
2199 vert = this.coords[i];
2200 vert.X += x;
2201 vert.Y += y;
2202 vert.Z += z;
2203 this.coords[i] = vert;
2204 }
2205
2206 if (this.viewerFaces != null)
2207 {
2208 int numViewerFaces = this.viewerFaces.Count;
2209
2210 for (i = 0; i < numViewerFaces; i++)
2211 {
2212 ViewerFace v = this.viewerFaces[i];
2213 v.AddPos(x, y, z);
2214 this.viewerFaces[i] = v;
2215 }
2216 }
2217 }
2218
2219 /// <summary>
2220 /// Rotates the mesh
2221 /// </summary>
2222 /// <param name="q"></param>
2223 public void AddRot(Quat q)
2224 {
2225 int i;
2226 int numVerts = this.coords.Count;
2227
2228 for (i = 0; i < numVerts; i++)
2229 this.coords[i] *= q;
2230
2231 if (this.normals != null)
2232 {
2233 int numNormals = this.normals.Count;
2234 for (i = 0; i < numNormals; i++)
2235 this.normals[i] *= q;
2236 }
2237
2238 if (this.viewerFaces != null)
2239 {
2240 int numViewerFaces = this.viewerFaces.Count;
2241
2242 for (i = 0; i < numViewerFaces; i++)
2243 {
2244 ViewerFace v = this.viewerFaces[i];
2245 v.v1 *= q;
2246 v.v2 *= q;
2247 v.v3 *= q;
2248
2249 v.n1 *= q;
2250 v.n2 *= q;
2251 v.n3 *= q;
2252 this.viewerFaces[i] = v;
2253 }
2254 }
2255 }
2256
2257#if VERTEX_INDEXER
2258 public VertexIndexer GetVertexIndexer()
2259 {
2260 if (this.viewerMode && this.viewerFaces.Count > 0)
2261 return new VertexIndexer(this);
2262 return null;
2263 }
2264#endif
2265
2266 /// <summary>
2267 /// Scales the mesh
2268 /// </summary>
2269 /// <param name="x"></param>
2270 /// <param name="y"></param>
2271 /// <param name="z"></param>
2272 public void Scale(float x, float y, float z)
2273 {
2274 int i;
2275 int numVerts = this.coords.Count;
2276 //Coord vert;
2277
2278 Coord m = new Coord(x, y, z);
2279 for (i = 0; i < numVerts; i++)
2280 this.coords[i] *= m;
2281
2282 if (this.viewerFaces != null)
2283 {
2284 int numViewerFaces = this.viewerFaces.Count;
2285 for (i = 0; i < numViewerFaces; i++)
2286 {
2287 ViewerFace v = this.viewerFaces[i];
2288 v.v1 *= m;
2289 v.v2 *= m;
2290 v.v3 *= m;
2291 this.viewerFaces[i] = v;
2292 }
2293
2294 }
2295
2296 }
2297
2298 /// <summary>
2299 /// Dumps the mesh to a Blender compatible "Raw" format file
2300 /// </summary>
2301 /// <param name="path"></param>
2302 /// <param name="name"></param>
2303 /// <param name="title"></param>
2304 public void DumpRaw(String path, String name, String title)
2305 {
2306 if (path == null)
2307 return;
2308 String fileName = name + "_" + title + ".raw";
2309 String completePath = System.IO.Path.Combine(path, fileName);
2310 StreamWriter sw = new StreamWriter(completePath);
2311
2312 for (int i = 0; i < this.faces.Count; i++)
2313 {
2314 string s = this.coords[this.faces[i].v1].ToString();
2315 s += " " + this.coords[this.faces[i].v2].ToString();
2316 s += " " + this.coords[this.faces[i].v3].ToString();
2317
2318 sw.WriteLine(s);
2319 }
2320
2321 sw.Close();
2322 }
2323 }
2324}