OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
PrimMesher.cs
Go to the documentation of this file.
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 
28 using System;
29 using System.Collections.Generic;
30 using System.Text;
31 using System.IO;
32 
33 namespace PrimMesher
34 {
35  public struct Quat
36  {
38  public float X;
40  public float Y;
42  public float Z;
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 
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 
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 
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 
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 
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 
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 
2070  public void ExtrudeLinear()
2071  {
2072  this.Extrude(PathType.Linear);
2073  }
2074 
2075 
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 
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 
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 
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 
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 
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 
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 
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 }
override string ToString()
Definition: PrimMesher.cs:109
void AddValue2FaceVertexIndices(int num)
Definition: PrimMesher.cs:1174
Quat(float x, float y, float z, float w)
Definition: PrimMesher.cs:46
void CalcNormals()
Calculate surface normals for all of the faces in the list of faces in this mesh
Definition: PrimMesher.cs:2158
void DumpRaw(String path, String name, String title)
Dumps the mesh to a Blender compatible "Raw" format file
Definition: PrimMesher.cs:2304
Quat Normalize()
Definition: PrimMesher.cs:75
float Z
Z value
Definition: PrimMesher.cs:42
float W
W value
Definition: PrimMesher.cs:44
static Coord Cross(Coord c1, Coord c2)
Definition: PrimMesher.cs:170
override string ToString()
Definition: PrimMesher.cs:165
UVCoord(float u, float v)
Definition: PrimMesher.cs:234
void ExtrudeLinear()
DEPRICATED - use Extrude(PathType.Linear) instead Extrudes a profile along a straight line path...
Definition: PrimMesher.cs:2070
Profile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides, bool createFaces, bool calcVertexNormals)
Definition: PrimMesher.cs:656
static Coord operator*(Coord v, Coord m)
Definition: PrimMesher.cs:184
void AddRot(Quat q)
Definition: PrimMesher.cs:384
void AddPos(float x, float y, float z)
Definition: PrimMesher.cs:369
float Length()
Definition: PrimMesher.cs:70
generates a profile for extrusion
Definition: PrimMesher.cs:613
Coord Normalize()
Definition: PrimMesher.cs:142
Profile Copy(bool needFaces)
Definition: PrimMesher.cs:1045
void AddPos(float x, float y, float z)
Adds a value to each XYZ vertex coordinate in the mesh
Definition: PrimMesher.cs:2191
PrimMesh(int sides, float profileStart, float profileEnd, float hollow, int hollowSides)
Constructs a PrimMesh object and creates the profile for extrusion.
Definition: PrimMesher.cs:1558
void FlipNormals()
Changes order of the vertex indices and negates the center vertex normal. Does not alter vertex norma...
Definition: PrimMesher.cs:1134
void Scale(float x, float y, float z)
Definition: PrimMesher.cs:354
OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion rotation
Definition: ICM_Api.cs:32
Coord SurfaceNormal(int faceIndex)
Calculate the surface normal for a face in the list of faces
Definition: PrimMesher.cs:2109
Face(int v1, int v2, int v3)
Definition: PrimMesher.cs:267
void DumpRaw(String path, String name, String title)
Definition: PrimMesher.cs:1207
float Y
Y value
Definition: PrimMesher.cs:40
void AddValue2FaceNormalIndices(int num)
Definition: PrimMesher.cs:1189
void Scale(float x, float y)
Definition: PrimMesher.cs:1116
Face(int v1, int v2, int v3, int n1, int n2, int n3)
Definition: PrimMesher.cs:285
Coord SurfaceNormal(List< Coord > coordList)
Definition: PrimMesher.cs:302
List< PathNode > pathNodes
Definition: PrimMesher.cs:1241
Quat(Coord axis, float angle)
Definition: PrimMesher.cs:54
float X
X value
Definition: PrimMesher.cs:38
void Scale(float x, float y, float z)
Scales the mesh
Definition: PrimMesher.cs:2272
List< float > us
Definition: PrimMesher.cs:622
Coord(float x, float y, float z)
Definition: PrimMesher.cs:121
void ExtrudeCircular()
DEPRICATED - use Extrude(PathType.Circular) instead Extrude a profile into a circular path prim mesh...
Definition: PrimMesher.cs:2081
static Quat operator*(Quat q1, Quat q2)
Definition: PrimMesher.cs:100
void AddPos(Coord v)
Definition: PrimMesher.cs:1074
void AddRot(Quat q)
Definition: PrimMesher.cs:1095
void AddRot(Quat q)
Rotates the mesh
Definition: PrimMesher.cs:2223
PrimMesh Copy()
Duplicates a PrimMesh object. All object properties are copied by value, including lists...
Definition: PrimMesher.cs:2122
void Extrude(PathType pathType)
Extrudes a profile along a path.
Definition: PrimMesher.cs:1590
ViewerFace(int primFaceNumber)
Definition: PrimMesher.cs:335
static Coord operator+(Coord v, Coord a)
Definition: PrimMesher.cs:179
void AddPos(float x, float y, float z)
Definition: PrimMesher.cs:1079