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