aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim
diff options
context:
space:
mode:
authorDahlia Trimble2008-11-30 03:52:18 +0000
committerDahlia Trimble2008-11-30 03:52:18 +0000
commit07ee2c3504edb0c328020aed6f5d3f182a3b67c5 (patch)
treeeb373c5e8fef7ce2a840ecfa87c04cc7a10fa425 /OpenSim
parent* try a prebuild fix to the primmesher thing (diff)
downloadopensim-SC-07ee2c3504edb0c328020aed6f5d3f182a3b67c5.zip
opensim-SC-07ee2c3504edb0c328020aed6f5d3f182a3b67c5.tar.gz
opensim-SC-07ee2c3504edb0c328020aed6f5d3f182a3b67c5.tar.bz2
opensim-SC-07ee2c3504edb0c328020aed6f5d3f182a3b67c5.tar.xz
Revert r7548 and r7549 until someone with prebuild-fu can help structure the dependencies
Diffstat (limited to 'OpenSim')
-rw-r--r--OpenSim/Region/Physics/Meshing/PrimMesher.cs2168
-rw-r--r--OpenSim/Region/Physics/Meshing/SculptMesh.cs343
2 files changed, 2511 insertions, 0 deletions
diff --git a/OpenSim/Region/Physics/Meshing/PrimMesher.cs b/OpenSim/Region/Physics/Meshing/PrimMesher.cs
new file mode 100644
index 0000000..1fed64d
--- /dev/null
+++ b/OpenSim/Region/Physics/Meshing/PrimMesher.cs
@@ -0,0 +1,2168 @@
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 OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Text;
31using System.IO;
32
33namespace PrimMesher
34{
35 public struct Quat
36 {
37 /// <summary>X value</summary>
38 public float X;
39 /// <summary>Y value</summary>
40 public float Y;
41 /// <summary>Z value</summary>
42 public float Z;
43 /// <summary>W value</summary>
44 public float W;
45
46 public Quat(float x, float y, float z, float w)
47 {
48 X = x;
49 Y = y;
50 Z = z;
51 W = w;
52 }
53
54 public Quat(Coord axis, float angle)
55 {
56 axis = axis.Normalize();
57
58 angle *= 0.5f;
59 float c = (float)Math.Cos(angle);
60 float s = (float)Math.Sin(angle);
61
62 X = axis.X * s;
63 Y = axis.Y * s;
64 Z = axis.Z * s;
65 W = c;
66
67 Normalize();
68 }
69
70 public float Length()
71 {
72 return (float)Math.Sqrt(X * X + Y * Y + Z * Z + W * W);
73 }
74
75 public Quat Normalize()
76 {
77 const float MAG_THRESHOLD = 0.0000001f;
78 float mag = Length();
79
80 // Catch very small rounding errors when normalizing
81 if (mag > MAG_THRESHOLD)
82 {
83 float oomag = 1f / mag;
84 X *= oomag;
85 Y *= oomag;
86 Z *= oomag;
87 W *= oomag;
88 }
89 else
90 {
91 X = 0f;
92 Y = 0f;
93 Z = 0f;
94 W = 1f;
95 }
96
97 return this;
98 }
99
100 public override string ToString()
101 {
102 return "< X: " + this.X.ToString() + ", Y: " + this.Y.ToString() + ", Z: " + this.Z.ToString() + ", W: " + this.W.ToString() + ">";
103 }
104 }
105
106 public struct Coord
107 {
108 public float X;
109 public float Y;
110 public float Z;
111
112 public Coord(float x, float y, float z)
113 {
114 this.X = x;
115 this.Y = y;
116 this.Z = z;
117 }
118
119 public float Length()
120 {
121 return (float)Math.Sqrt(this.X * this.X + this.Y * this.Y + this.Z * this.Z);
122 }
123
124 public Coord Invert()
125 {
126 this.X = -this.X;
127 this.Y = -this.Y;
128 this.Z = -this.Z;
129
130 return this;
131 }
132
133 public Coord Normalize()
134 {
135 const float MAG_THRESHOLD = 0.0000001f;
136 float mag = Length();
137
138 // Catch very small rounding errors when normalizing
139 if (mag > MAG_THRESHOLD)
140 {
141 float oomag = 1.0f / mag;
142 this.X *= oomag;
143 this.Y *= oomag;
144 this.Z *= oomag;
145 }
146 else
147 {
148 this.X = 0.0f;
149 this.Y = 0.0f;
150 this.Z = 0.0f;
151 }
152
153 return this;
154 }
155
156 public override string ToString()
157 {
158 return this.X.ToString() + " " + this.Y.ToString() + " " + this.Z.ToString();
159 }
160
161 public static Coord Cross(Coord c1, Coord c2)
162 {
163 return new Coord(
164 c1.Y * c2.Z - c2.Y * c1.Z,
165 c1.Z * c2.X - c2.Z * c1.X,
166 c1.X * c2.Y - c2.X * c1.Y
167 );
168 }
169
170 public static Coord operator +(Coord v, Coord a)
171 {
172 return new Coord(v.X + a.X, v.Y + a.Y, v.Z + a.Z);
173 }
174
175 public static Coord operator *(Coord v, Coord m)
176 {
177 return new Coord(v.X * m.X, v.Y * m.Y, v.Z * m.Z);
178 }
179
180 public static Coord operator *(Coord v, Quat q)
181 {
182 // From http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/transforms/
183
184 Coord c2 = new Coord(0.0f, 0.0f, 0.0f);
185
186 c2.X = q.W * q.W * v.X +
187 2f * q.Y * q.W * v.Z -
188 2f * q.Z * q.W * v.Y +
189 q.X * q.X * v.X +
190 2f * q.Y * q.X * v.Y +
191 2f * q.Z * q.X * v.Z -
192 q.Z * q.Z * v.X -
193 q.Y * q.Y * v.X;
194
195 c2.Y =
196 2f * q.X * q.Y * v.X +
197 q.Y * q.Y * v.Y +
198 2f * q.Z * q.Y * v.Z +
199 2f * q.W * q.Z * v.X -
200 q.Z * q.Z * v.Y +
201 q.W * q.W * v.Y -
202 2f * q.X * q.W * v.Z -
203 q.X * q.X * v.Y;
204
205 c2.Z =
206 2f * q.X * q.Z * v.X +
207 2f * q.Y * q.Z * v.Y +
208 q.Z * q.Z * v.Z -
209 2f * q.W * q.Y * v.X -
210 q.Y * q.Y * v.Z +
211 2f * q.W * q.X * v.Y -
212 q.X * q.X * v.Z +
213 q.W * q.W * v.Z;
214
215 return c2;
216 }
217 }
218
219 public struct UVCoord
220 {
221 public float U;
222 public float V;
223
224
225 public UVCoord(float u, float v)
226 {
227 this.U = u;
228 this.V = v;
229 }
230 }
231
232 public struct Face
233 {
234 public int primFace;
235
236 // vertices
237 public int v1;
238 public int v2;
239 public int v3;
240
241 //normals
242 public int n1;
243 public int n2;
244 public int n3;
245
246 // uvs
247 public int uv1;
248 public int uv2;
249 public int uv3;
250
251
252 public Face(int v1, int v2, int v3)
253 {
254 primFace = 0;
255
256 this.v1 = v1;
257 this.v2 = v2;
258 this.v3 = v3;
259
260 this.n1 = 0;
261 this.n2 = 0;
262 this.n3 = 0;
263
264 this.uv1 = 0;
265 this.uv2 = 0;
266 this.uv3 = 0;
267
268 }
269
270 public Face(int v1, int v2, int v3, int n1, int n2, int n3)
271 {
272 primFace = 0;
273
274 this.v1 = v1;
275 this.v2 = v2;
276 this.v3 = v3;
277
278 this.n1 = n1;
279 this.n2 = n2;
280 this.n3 = n3;
281
282 this.uv1 = 0;
283 this.uv2 = 0;
284 this.uv3 = 0;
285 }
286
287 public Coord SurfaceNormal(List<Coord> coordList)
288 {
289 Coord c1 = coordList[this.v1];
290 Coord c2 = coordList[this.v2];
291 Coord c3 = coordList[this.v3];
292
293 Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z);
294 Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z);
295
296 return Coord.Cross(edge1, edge2).Normalize();
297 }
298 }
299
300 public struct ViewerFace
301 {
302 public int primFaceNumber;
303
304 public Coord v1;
305 public Coord v2;
306 public Coord v3;
307
308 public Coord n1;
309 public Coord n2;
310 public Coord n3;
311
312 public UVCoord uv1;
313 public UVCoord uv2;
314 public UVCoord uv3;
315
316 public ViewerFace(int primFaceNumber)
317 {
318 this.primFaceNumber = primFaceNumber;
319
320 this.v1 = new Coord();
321 this.v2 = new Coord();
322 this.v3 = new Coord();
323
324 this.n1 = new Coord();
325 this.n2 = new Coord();
326 this.n3 = new Coord();
327
328 this.uv1 = new UVCoord();
329 this.uv2 = new UVCoord();
330 this.uv3 = new UVCoord();
331 }
332
333 public void Scale(float x, float y, float z)
334 {
335 this.v1.X *= x;
336 this.v1.Y *= y;
337 this.v1.Z *= z;
338
339 this.v2.X *= x;
340 this.v2.Y *= y;
341 this.v2.Z *= z;
342
343 this.v3.X *= x;
344 this.v3.Y *= y;
345 this.v3.Z *= z;
346 }
347
348 public void AddRot(Quat q)
349 {
350 this.v1 *= q;
351 this.v2 *= q;
352 this.v3 *= q;
353
354 this.n1 *= q;
355 this.n2 *= q;
356 this.n3 *= q;
357 }
358
359 public void CalcSurfaceNormal()
360 {
361
362 Coord edge1 = new Coord(this.v2.X - this.v1.X, this.v2.Y - this.v1.Y, this.v2.Z - this.v1.Z);
363 Coord edge2 = new Coord(this.v3.X - this.v1.X, this.v3.Y - this.v1.Y, this.v3.Z - this.v1.Z);
364
365 this.n1 = this.n2 = this.n3 = Coord.Cross(edge1, edge2).Normalize();
366 }
367 }
368
369 internal struct Angle
370 {
371 internal float angle;
372 internal float X;
373 internal float Y;
374
375 internal Angle(float angle, float x, float y)
376 {
377 this.angle = angle;
378 this.X = x;
379 this.Y = y;
380 }
381 }
382
383 internal class AngleList
384 {
385 private float iX, iY; // intersection point
386
387 private Angle[] angles3 =
388 {
389 new Angle(0.0f, 1.0f, 0.0f),
390 new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f),
391 new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f),
392 new Angle(1.0f, 1.0f, 0.0f)
393 };
394
395 private Coord[] normals3 =
396 {
397 new Coord(0.25f, 0.4330127019f, 0.0f).Normalize(),
398 new Coord(-0.5f, 0.0f, 0.0f).Normalize(),
399 new Coord(0.25f, -0.4330127019f, 0.0f).Normalize(),
400 new Coord(0.25f, 0.4330127019f, 0.0f).Normalize()
401 };
402
403 private Angle[] angles4 =
404 {
405 new Angle(0.0f, 1.0f, 0.0f),
406 new Angle(0.25f, 0.0f, 1.0f),
407 new Angle(0.5f, -1.0f, 0.0f),
408 new Angle(0.75f, 0.0f, -1.0f),
409 new Angle(1.0f, 1.0f, 0.0f)
410 };
411
412 private Coord[] normals4 =
413 {
414 new Coord(0.5f, 0.5f, 0.0f).Normalize(),
415 new Coord(-0.5f, 0.5f, 0.0f).Normalize(),
416 new Coord(-0.5f, -0.5f, 0.0f).Normalize(),
417 new Coord(0.5f, -0.5f, 0.0f).Normalize(),
418 new Coord(0.5f, 0.5f, 0.0f).Normalize()
419 };
420
421 private Angle[] angles24 =
422 {
423 new Angle(0.0f, 1.0f, 0.0f),
424 new Angle(0.041666666666666664f, 0.96592582628906831f, 0.25881904510252074f),
425 new Angle(0.083333333333333329f, 0.86602540378443871f, 0.5f),
426 new Angle(0.125f, 0.70710678118654757f, 0.70710678118654746f),
427 new Angle(0.16666666666666667f, 0.5f, 0.8660254037844386f),
428 new Angle(0.20833333333333331f, 0.25881904510252096f, 0.9659258262890682f),
429 new Angle(0.25f, 0.0f, 1.0f),
430 new Angle(0.29166666666666663f, -0.25881904510252063f, 0.96592582628906831f),
431 new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f),
432 new Angle(0.375f, -0.70710678118654746f, 0.70710678118654757f),
433 new Angle(0.41666666666666663f, -0.86602540378443849f, 0.5f),
434 new Angle(0.45833333333333331f, -0.9659258262890682f, 0.25881904510252102f),
435 new Angle(0.5f, -1.0f, 0.0f),
436 new Angle(0.54166666666666663f, -0.96592582628906842f, -0.25881904510252035f),
437 new Angle(0.58333333333333326f, -0.86602540378443882f, -0.5f),
438 new Angle(0.62499999999999989f, -0.70710678118654791f, -0.70710678118654713f),
439 new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f),
440 new Angle(0.70833333333333326f, -0.25881904510252152f, -0.96592582628906809f),
441 new Angle(0.75f, 0.0f, -1.0f),
442 new Angle(0.79166666666666663f, 0.2588190451025203f, -0.96592582628906842f),
443 new Angle(0.83333333333333326f, 0.5f, -0.86602540378443904f),
444 new Angle(0.875f, 0.70710678118654735f, -0.70710678118654768f),
445 new Angle(0.91666666666666663f, 0.86602540378443837f, -0.5f),
446 new Angle(0.95833333333333326f, 0.96592582628906809f, -0.25881904510252157f),
447 new Angle(1.0f, 1.0f, 0.0f)
448 };
449
450 private Angle interpolatePoints(float newPoint, Angle p1, Angle p2)
451 {
452 float m = (newPoint - p1.angle) / (p2.angle - p1.angle);
453 return new Angle(newPoint, p1.X + m * (p2.X - p1.X), p1.Y + m * (p2.Y - p1.Y));
454 }
455
456 private void intersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
457 { // ref: http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
458 double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
459 double uaNumerator = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3);
460
461 if (denom != 0.0)
462 {
463 double ua = uaNumerator / denom;
464 iX = (float)(x1 + ua * (x2 - x1));
465 iY = (float)(y1 + ua * (y2 - y1));
466 }
467 }
468
469 internal List<Angle> angles;
470 internal List<Coord> normals;
471
472 internal void makeAngles(int sides, float startAngle, float stopAngle)
473 {
474 angles = new List<Angle>();
475 normals = new List<Coord>();
476
477 double twoPi = System.Math.PI * 2.0;
478 float twoPiInv = 1.0f / (float)twoPi;
479
480 if (sides < 1)
481 throw new Exception("number of sides not greater than zero");
482 if (stopAngle <= startAngle)
483 throw new Exception("stopAngle not greater than startAngle");
484
485 if ((sides == 3 || sides == 4 || sides == 24))
486 {
487 startAngle *= twoPiInv;
488 stopAngle *= twoPiInv;
489
490 Angle[] sourceAngles;
491 if (sides == 3)
492 sourceAngles = angles3;
493 else if (sides == 4)
494 sourceAngles = angles4;
495 else sourceAngles = angles24;
496
497 int startAngleIndex = (int)(startAngle * sides);
498 int endAngleIndex = sourceAngles.Length - 1;
499 if (stopAngle < 1.0f)
500 endAngleIndex = (int)(stopAngle * sides) + 1;
501 if (endAngleIndex == startAngleIndex)
502 endAngleIndex++;
503
504 for (int angleIndex = startAngleIndex; angleIndex < endAngleIndex + 1; angleIndex++)
505 {
506 angles.Add(sourceAngles[angleIndex]);
507 if (sides == 3)
508 normals.Add(normals3[angleIndex]);
509 else if (sides == 4)
510 normals.Add(normals4[angleIndex]);
511 }
512
513 if (startAngle > 0.0f)
514 angles[0] = interpolatePoints(startAngle, angles[0], angles[1]);
515
516 if (stopAngle < 1.0f)
517 {
518 int lastAngleIndex = angles.Count - 1;
519 angles[lastAngleIndex] = interpolatePoints(stopAngle, angles[lastAngleIndex - 1], angles[lastAngleIndex]);
520 }
521 }
522 else
523 {
524 double stepSize = twoPi / sides;
525
526 int startStep = (int)(startAngle / stepSize);
527 double angle = stepSize * startStep;
528 int step = startStep;
529 double stopAngleTest = stopAngle;
530 if (stopAngle < twoPi)
531 {
532 stopAngleTest = stepSize * ((int)(stopAngle / stepSize) + 1);
533 if (stopAngleTest < stopAngle)
534 stopAngleTest += stepSize;
535 if (stopAngleTest > twoPi)
536 stopAngleTest = twoPi;
537 }
538
539 while (angle <= stopAngleTest)
540 {
541 Angle newAngle;
542 newAngle.angle = (float)angle;
543 newAngle.X = (float)System.Math.Cos(angle);
544 newAngle.Y = (float)System.Math.Sin(angle);
545 angles.Add(newAngle);
546 step += 1;
547 angle = stepSize * step;
548 }
549
550 if (startAngle > angles[0].angle)
551 {
552 Angle newAngle;
553 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));
554 newAngle.angle = startAngle;
555 newAngle.X = iX;
556 newAngle.Y = iY;
557 angles[0] = newAngle;
558 }
559
560 int index = angles.Count - 1;
561 if (stopAngle < angles[index].angle)
562 {
563 Angle newAngle;
564 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));
565 newAngle.angle = stopAngle;
566 newAngle.X = iX;
567 newAngle.Y = iY;
568 angles[index] = newAngle;
569 }
570 }
571 }
572 }
573
574 /// <summary>
575 /// generates a profile for extrusion
576 /// </summary>
577 internal class Profile
578 {
579 private const float twoPi = 2.0f * (float)Math.PI;
580
581 internal List<Coord> coords;
582 internal List<Face> faces;
583 internal List<Coord> vertexNormals;
584 internal List<float> us;
585 internal List<UVCoord> faceUVs;
586 internal List<int> faceNumbers;
587
588 internal Coord faceNormal = new Coord(0.0f, 0.0f, 1.0f);
589 internal Coord cutNormal1 = new Coord();
590 internal Coord cutNormal2 = new Coord();
591
592 internal int numOuterVerts = 0;
593 internal int numHollowVerts = 0;
594
595 internal bool calcVertexNormals = false;
596 internal int bottomFaceNumber = 0;
597 internal int numPrimFaces = 0;
598
599 internal Profile()
600 {
601 this.coords = new List<Coord>();
602 this.faces = new List<Face>();
603 this.vertexNormals = new List<Coord>();
604 this.us = new List<float>();
605 this.faceUVs = new List<UVCoord>();
606 this.faceNumbers = new List<int>();
607 }
608
609 internal Profile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides, bool createFaces, bool calcVertexNormals)
610 {
611 this.calcVertexNormals = calcVertexNormals;
612 this.coords = new List<Coord>();
613 this.faces = new List<Face>();
614 this.vertexNormals = new List<Coord>();
615 this.us = new List<float>();
616 this.faceUVs = new List<UVCoord>();
617 this.faceNumbers = new List<int>();
618
619 Coord center = new Coord(0.0f, 0.0f, 0.0f);
620
621 List<Coord> hollowCoords = new List<Coord>();
622 List<Coord> hollowNormals = new List<Coord>();
623 List<float> hollowUs = new List<float>();
624
625 bool hasHollow = (hollow > 0.0f);
626
627 bool hasProfileCut = (profileStart > 0.0f || profileEnd < 1.0f);
628
629 AngleList angles = new AngleList();
630 AngleList hollowAngles = new AngleList();
631
632 float xScale = 0.5f;
633 float yScale = 0.5f;
634 if (sides == 4) // corners of a square are sqrt(2) from center
635 {
636 xScale = 0.707f;
637 yScale = 0.707f;
638 }
639
640 float startAngle = profileStart * twoPi;
641 float stopAngle = profileEnd * twoPi;
642
643 try { angles.makeAngles(sides, startAngle, stopAngle); }
644 catch (Exception ex)
645 {
646 Console.WriteLine("makeAngles failed: Exception: " + ex.ToString());
647 Console.WriteLine("sides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString());
648 return;
649 }
650
651 this.numOuterVerts = angles.angles.Count;
652
653 // flag to create as few triangles as possible for 3 or 4 side profile
654 bool simpleFace = (sides < 5 && !(hasHollow || hasProfileCut));
655
656 if (hasHollow)
657 {
658 if (sides == hollowSides)
659 hollowAngles = angles;
660 else
661 {
662 try { hollowAngles.makeAngles(hollowSides, startAngle, stopAngle); }
663 catch (Exception ex)
664 {
665 Console.WriteLine("makeAngles failed: Exception: " + ex.ToString());
666 Console.WriteLine("sides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString());
667 return;
668 }
669 }
670 this.numHollowVerts = hollowAngles.angles.Count;
671 }
672 else if (!simpleFace)
673 {
674 this.coords.Add(center);
675 if (this.calcVertexNormals)
676 this.vertexNormals.Add(new Coord(0.0f, 0.0f, 1.0f));
677 this.us.Add(0.0f);
678 }
679
680 float z = 0.0f;
681
682 Angle angle;
683 Coord newVert = new Coord();
684 if (hasHollow && hollowSides != sides)
685 {
686 int numHollowAngles = hollowAngles.angles.Count;
687 for (int i = 0; i < numHollowAngles; i++)
688 {
689 angle = hollowAngles.angles[i];
690 newVert.X = hollow * xScale * angle.X;
691 newVert.Y = hollow * yScale * angle.Y;
692 newVert.Z = z;
693
694 hollowCoords.Add(newVert);
695 if (this.calcVertexNormals)
696 {
697 if (hollowSides < 5)
698 hollowNormals.Add(hollowAngles.normals[i].Invert());
699 else
700 hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f));
701
702 hollowUs.Add(angle.angle * hollow);
703 }
704 }
705 }
706
707 int index = 0;
708 int numAngles = angles.angles.Count;
709
710 for (int i = 0; i < numAngles; i++)
711 {
712 int iNext = i == numAngles ? i + 1 : 0;
713 angle = angles.angles[i];
714 newVert.X = angle.X * xScale;
715 newVert.Y = angle.Y * yScale;
716 newVert.Z = z;
717 this.coords.Add(newVert);
718 if (this.calcVertexNormals)
719 {
720
721 if (sides < 5)
722 {
723 this.vertexNormals.Add(angles.normals[i]);
724 float u = angle.angle;
725 this.us.Add(u);
726 }
727 else
728 {
729 this.vertexNormals.Add(new Coord(angle.X, angle.Y, 0.0f));
730 this.us.Add(angle.angle);
731 }
732 }
733
734 if (hollow > 0.0f)
735 {
736 if (hollowSides == sides)
737 {
738 newVert.X *= hollow;
739 newVert.Y *= hollow;
740 newVert.Z = z;
741 hollowCoords.Add(newVert);
742 if (this.calcVertexNormals)
743 {
744 if (sides < 5)
745 {
746 hollowNormals.Add(angles.normals[i].Invert());
747 }
748
749 else
750 hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f));
751
752 hollowUs.Add(angle.angle * hollow);
753 }
754 }
755 }
756 else if (!simpleFace && createFaces && angle.angle > 0.0001f)
757 {
758 Face newFace = new Face();
759 newFace.v1 = 0;
760 newFace.v2 = index;
761 newFace.v3 = index + 1;
762
763 this.faces.Add(newFace);
764 }
765 index += 1;
766 }
767
768 if (hasHollow)
769 {
770 hollowCoords.Reverse();
771 if (this.calcVertexNormals)
772 {
773 hollowNormals.Reverse();
774 hollowUs.Reverse();
775 }
776
777 if (createFaces)
778 {
779 int numOuterVerts = this.coords.Count;
780 int numHollowVerts = hollowCoords.Count;
781 int numTotalVerts = numOuterVerts + numHollowVerts;
782
783 if (numOuterVerts == numHollowVerts)
784 {
785 Face newFace = new Face();
786
787 for (int coordIndex = 0; coordIndex < numOuterVerts - 1; coordIndex++)
788 {
789 newFace.v1 = coordIndex;
790 newFace.v2 = coordIndex + 1;
791 newFace.v3 = numTotalVerts - coordIndex - 1;
792 this.faces.Add(newFace);
793
794 newFace.v1 = coordIndex + 1;
795 newFace.v2 = numTotalVerts - coordIndex - 2;
796 newFace.v3 = numTotalVerts - coordIndex - 1;
797 this.faces.Add(newFace);
798 }
799 }
800 else
801 {
802 if (numOuterVerts < numHollowVerts)
803 {
804 Face newFace = new Face();
805 int j = 0; // j is the index for outer vertices
806 int maxJ = numOuterVerts - 1;
807 for (int i = 0; i < numHollowVerts; i++) // i is the index for inner vertices
808 {
809 if (j < maxJ)
810 if (angles.angles[j + 1].angle - hollowAngles.angles[i].angle <= hollowAngles.angles[i].angle - angles.angles[j].angle)
811 {
812 newFace.v1 = numTotalVerts - i - 1;
813 newFace.v2 = j;
814 newFace.v3 = j + 1;
815
816 this.faces.Add(newFace);
817 j += 1;
818 }
819
820 newFace.v1 = j;
821 newFace.v2 = numTotalVerts - i - 2;
822 newFace.v3 = numTotalVerts - i - 1;
823
824 this.faces.Add(newFace);
825 }
826 }
827 else // numHollowVerts < numOuterVerts
828 {
829 Face newFace = new Face();
830 int j = 0; // j is the index for inner vertices
831 int maxJ = numHollowVerts - 1;
832 for (int i = 0; i < numOuterVerts; i++)
833 {
834 if (j < maxJ)
835 if (hollowAngles.angles[j + 1].angle - angles.angles[i].angle <= angles.angles[i].angle - hollowAngles.angles[j].angle)
836 {
837 newFace.v1 = i;
838 newFace.v2 = numTotalVerts - j - 2;
839 newFace.v3 = numTotalVerts - j - 1;
840
841 this.faces.Add(newFace);
842 j += 1;
843 }
844
845 newFace.v1 = numTotalVerts - j - 1;
846 newFace.v2 = i;
847 newFace.v3 = i + 1;
848
849 this.faces.Add(newFace);
850 }
851 }
852 }
853 }
854
855 this.coords.AddRange(hollowCoords);
856 if (this.calcVertexNormals)
857 {
858 this.vertexNormals.AddRange(hollowNormals);
859 this.us.AddRange(hollowUs);
860
861 }
862 }
863
864 if (simpleFace && createFaces)
865 {
866 if (sides == 3)
867 this.faces.Add(new Face(0, 1, 2));
868 else if (sides == 4)
869 {
870 this.faces.Add(new Face(0, 1, 2));
871 this.faces.Add(new Face(0, 2, 3));
872 }
873 }
874
875 if (calcVertexNormals && hasProfileCut)
876 {
877 if (hasHollow)
878 {
879 int lastOuterVertIndex = this.numOuterVerts - 1;
880
881 this.cutNormal1.X = this.coords[0].Y - this.coords[this.coords.Count - 1].Y;
882 this.cutNormal1.Y = -(this.coords[0].X - this.coords[this.coords.Count - 1].X);
883
884 this.cutNormal2.X = this.coords[lastOuterVertIndex + 1].Y - this.coords[lastOuterVertIndex].Y;
885 this.cutNormal2.Y = -(this.coords[lastOuterVertIndex + 1].X - this.coords[lastOuterVertIndex].X);
886 }
887
888 else
889 {
890 this.cutNormal1.X = this.vertexNormals[1].Y;
891 this.cutNormal1.Y = -this.vertexNormals[1].X;
892
893 this.cutNormal2.X = -this.vertexNormals[this.vertexNormals.Count - 2].Y;
894 this.cutNormal2.Y = this.vertexNormals[this.vertexNormals.Count - 2].X;
895
896 }
897 this.cutNormal1.Normalize();
898 this.cutNormal2.Normalize();
899 }
900
901 this.MakeFaceUVs();
902
903 hollowCoords = null;
904 hollowNormals = null;
905 hollowUs = null;
906
907 if (calcVertexNormals)
908 { // calculate prim face numbers
909 // I know it's ugly but so is the whole concept of prim face numbers
910 int faceNum = 1;
911 int startVert = hasProfileCut && !hasHollow ? 1 : 0;
912 if (startVert > 0)
913 this.faceNumbers.Add(0);
914 for (int i = 0; i < numOuterVerts; i++)
915 this.faceNumbers.Add(sides < 5 ? faceNum++ : faceNum);
916 if (sides > 4)
917 faceNum++;
918 if (hasProfileCut)
919 this.faceNumbers.Add(0);
920 for (int i = 0; i < numHollowVerts; i++)
921 this.faceNumbers.Add(faceNum++);
922 this.bottomFaceNumber = faceNum++;
923 if (hasHollow && hasProfileCut)
924 this.faceNumbers.Add(faceNum++);
925 for (int i = 0; i < this.faceNumbers.Count; i++)
926 if (this.faceNumbers[i] == 0)
927 this.faceNumbers[i] = faceNum++;
928
929 this.numPrimFaces = faceNum;
930 }
931
932 }
933
934 internal void MakeFaceUVs()
935 {
936 this.faceUVs = new List<UVCoord>();
937 foreach (Coord c in this.coords)
938 this.faceUVs.Add(new UVCoord(1.0f - (0.5f + c.X), 1.0f - (0.5f - c.Y)));
939 }
940
941 internal Profile Clone()
942 {
943 return this.Clone(true);
944 }
945
946 internal Profile Clone(bool needFaces)
947 {
948 Profile clone = new Profile();
949
950 clone.coords.AddRange(this.coords);
951 clone.faceUVs.AddRange(this.faceUVs);
952
953 if (needFaces)
954 clone.faces.AddRange(this.faces);
955 if ((clone.calcVertexNormals = this.calcVertexNormals) == true)
956 {
957 clone.vertexNormals.AddRange(this.vertexNormals);
958 clone.faceNormal = this.faceNormal;
959 clone.cutNormal1 = this.cutNormal1;
960 clone.cutNormal2 = this.cutNormal2;
961 clone.us.AddRange(this.us);
962 clone.faceNumbers.AddRange(this.faceNumbers);
963 }
964 clone.numOuterVerts = this.numOuterVerts;
965 clone.numHollowVerts = this.numHollowVerts;
966
967 return clone;
968 }
969
970 internal void AddPos(Coord v)
971 {
972 this.AddPos(v.X, v.Y, v.Z);
973 }
974
975 internal void AddPos(float x, float y, float z)
976 {
977 int i;
978 int numVerts = this.coords.Count;
979 Coord vert;
980
981 for (i = 0; i < numVerts; i++)
982 {
983 vert = this.coords[i];
984 vert.X += x;
985 vert.Y += y;
986 vert.Z += z;
987 this.coords[i] = vert;
988 }
989 }
990
991 internal void AddRot(Quat q)
992 {
993 int i;
994 int numVerts = this.coords.Count;
995
996 for (i = 0; i < numVerts; i++)
997 this.coords[i] *= q;
998
999 if (this.calcVertexNormals)
1000 {
1001 int numNormals = this.vertexNormals.Count;
1002 for (i = 0; i < numNormals; i++)
1003 this.vertexNormals[i] *= q;
1004
1005 this.faceNormal *= q;
1006 this.cutNormal1 *= q;
1007 this.cutNormal2 *= q;
1008
1009 }
1010 }
1011
1012 internal void Scale(float x, float y)
1013 {
1014 int i;
1015 int numVerts = this.coords.Count;
1016 Coord vert;
1017
1018 for (i = 0; i < numVerts; i++)
1019 {
1020 vert = this.coords[i];
1021 vert.X *= x;
1022 vert.Y *= y;
1023 this.coords[i] = vert;
1024 }
1025 }
1026
1027 /// <summary>
1028 /// Changes order of the vertex indices and negates the center vertex normal. Does not alter vertex normals of radial vertices
1029 /// </summary>
1030 internal void FlipNormals()
1031 {
1032 int i;
1033 int numFaces = this.faces.Count;
1034 Face tmpFace;
1035 int tmp;
1036
1037 for (i = 0; i < numFaces; i++)
1038 {
1039 tmpFace = this.faces[i];
1040 tmp = tmpFace.v3;
1041 tmpFace.v3 = tmpFace.v1;
1042 tmpFace.v1 = tmp;
1043 this.faces[i] = tmpFace;
1044 }
1045
1046 if (this.calcVertexNormals)
1047 {
1048 int normalCount = this.vertexNormals.Count;
1049 if (normalCount > 0)
1050 {
1051 Coord n = this.vertexNormals[normalCount - 1];
1052 n.Z = -n.Z;
1053 this.vertexNormals[normalCount - 1] = n;
1054 }
1055 }
1056
1057 this.faceNormal.X = -this.faceNormal.X;
1058 this.faceNormal.Y = -this.faceNormal.Y;
1059 this.faceNormal.Z = -this.faceNormal.Z;
1060
1061 int numfaceUVs = this.faceUVs.Count;
1062 for (i = 0; i < numfaceUVs; i++)
1063 {
1064 UVCoord uv = this.faceUVs[i];
1065 uv.V = 1.0f - uv.V;
1066 this.faceUVs[i] = uv;
1067 }
1068 }
1069
1070 internal void AddValue2FaceVertexIndices(int num)
1071 {
1072 int numFaces = this.faces.Count;
1073 Face tmpFace;
1074 for (int i = 0; i < numFaces; i++)
1075 {
1076 tmpFace = this.faces[i];
1077 tmpFace.v1 += num;
1078 tmpFace.v2 += num;
1079 tmpFace.v3 += num;
1080
1081 this.faces[i] = tmpFace;
1082 }
1083 }
1084
1085 internal void AddValue2FaceNormalIndices(int num)
1086 {
1087 if (this.calcVertexNormals)
1088 {
1089 int numFaces = this.faces.Count;
1090 Face tmpFace;
1091 for (int i = 0; i < numFaces; i++)
1092 {
1093 tmpFace = this.faces[i];
1094 tmpFace.n1 += num;
1095 tmpFace.n2 += num;
1096 tmpFace.n3 += num;
1097
1098 this.faces[i] = tmpFace;
1099 }
1100 }
1101 }
1102
1103 internal void DumpRaw(String path, String name, String title)
1104 {
1105 if (path == null)
1106 return;
1107 String fileName = name + "_" + title + ".raw";
1108 String completePath = Path.Combine(path, fileName);
1109 StreamWriter sw = new StreamWriter(completePath);
1110
1111 for (int i = 0; i < this.faces.Count; i++)
1112 {
1113 string s = this.coords[this.faces[i].v1].ToString();
1114 s += " " + this.coords[this.faces[i].v2].ToString();
1115 s += " " + this.coords[this.faces[i].v3].ToString();
1116
1117 sw.WriteLine(s);
1118 }
1119
1120 sw.Close();
1121 }
1122 }
1123
1124 public class PrimMesh
1125 {
1126 private const float twoPi = 2.0f * (float)Math.PI;
1127
1128 public List<Coord> coords;
1129 public List<Coord> normals;
1130 public List<Face> faces;
1131
1132 public List<ViewerFace> viewerFaces;
1133
1134 private int sides = 4;
1135 private int hollowSides = 4;
1136 private float profileStart = 0.0f;
1137 private float profileEnd = 1.0f;
1138 private float hollow = 0.0f;
1139 public int twistBegin = 0;
1140 public int twistEnd = 0;
1141 public float topShearX = 0.0f;
1142 public float topShearY = 0.0f;
1143 public float pathCutBegin = 0.0f;
1144 public float pathCutEnd = 1.0f;
1145 public float dimpleBegin = 0.0f;
1146 public float dimpleEnd = 1.0f;
1147 public float skew = 0.0f;
1148 public float holeSizeX = 1.0f; // called pathScaleX in pbs
1149 public float holeSizeY = 0.25f;
1150 public float taperX = 0.0f;
1151 public float taperY = 0.0f;
1152 public float radius = 0.0f;
1153 public float revolutions = 1.0f;
1154 public int stepsPerRevolution = 24;
1155
1156 private bool hasProfileCut = false;
1157 private bool hasHollow = false;
1158 public bool calcVertexNormals = false;
1159 private bool normalsProcessed = false;
1160 public bool viewerMode = false;
1161
1162 public int numPrimFaces = 0;
1163
1164 /// <summary>
1165 /// Human readable string representation of the parameters used to create a mesh.
1166 /// </summary>
1167 /// <returns></returns>
1168 public string ParamsToDisplayString()
1169 {
1170 string s = "";
1171 s += "sides..................: " + this.sides.ToString();
1172 s += "\nhollowSides..........: " + this.hollowSides.ToString();
1173 s += "\nprofileStart.........: " + this.profileStart.ToString();
1174 s += "\nprofileEnd...........: " + this.profileEnd.ToString();
1175 s += "\nhollow...............: " + this.hollow.ToString();
1176 s += "\ntwistBegin...........: " + this.twistBegin.ToString();
1177 s += "\ntwistEnd.............: " + this.twistEnd.ToString();
1178 s += "\ntopShearX............: " + this.topShearX.ToString();
1179 s += "\ntopShearY............: " + this.topShearY.ToString();
1180 s += "\npathCutBegin.........: " + this.pathCutBegin.ToString();
1181 s += "\npathCutEnd...........: " + this.pathCutEnd.ToString();
1182 s += "\ndimpleBegin..........: " + this.dimpleBegin.ToString();
1183 s += "\ndimpleEnd............: " + this.dimpleEnd.ToString();
1184 s += "\nskew.................: " + this.skew.ToString();
1185 s += "\nholeSizeX............: " + this.holeSizeX.ToString();
1186 s += "\nholeSizeY............: " + this.holeSizeY.ToString();
1187 s += "\ntaperX...............: " + this.taperX.ToString();
1188 s += "\ntaperY...............: " + this.taperY.ToString();
1189 s += "\nradius...............: " + this.radius.ToString();
1190 s += "\nrevolutions..........: " + this.revolutions.ToString();
1191 s += "\nstepsPerRevolution...: " + this.stepsPerRevolution.ToString();
1192
1193 return s;
1194 }
1195
1196 /// <summary>
1197 /// Constructs a PrimMesh object and creates the profile for extrusion.
1198 /// </summary>
1199 /// <param name="sides"></param>
1200 /// <param name="profileStart"></param>
1201 /// <param name="profileEnd"></param>
1202 /// <param name="hollow"></param>
1203 /// <param name="hollowSides"></param>
1204 public PrimMesh(int sides, float profileStart, float profileEnd, float hollow, int hollowSides)
1205 {
1206 this.coords = new List<Coord>();
1207 this.faces = new List<Face>();
1208
1209 this.sides = sides;
1210 this.profileStart = profileStart;
1211 this.profileEnd = profileEnd;
1212 this.hollow = hollow;
1213 this.hollowSides = hollowSides;
1214
1215 if (sides < 3)
1216 this.sides = 3;
1217 if (hollowSides < 3)
1218 this.hollowSides = 3;
1219 if (profileStart < 0.0f)
1220 this.profileStart = 0.0f;
1221 if (profileEnd > 1.0f)
1222 this.profileEnd = 1.0f;
1223 if (profileEnd < 0.02f)
1224 this.profileEnd = 0.02f;
1225 if (profileStart >= profileEnd)
1226 this.profileStart = profileEnd - 0.02f;
1227 if (hollow > 1.0f)
1228 this.hollow = 1.0f;
1229 if (hollow < 0.0f)
1230 this.hollow = 0.0f;
1231
1232 this.hasProfileCut = (this.profileStart > 0.0f || this.profileEnd < 1.0f);
1233 this.hasHollow = (this.hollow > 0.001f);
1234 }
1235
1236 /// <summary>
1237 /// Extrudes a profile along a straight line path. Used for prim types box, cylinder, and prism.
1238 /// </summary>
1239 public void ExtrudeLinear()
1240 {
1241 this.coords = new List<Coord>();
1242 this.faces = new List<Face>();
1243
1244 if (this.viewerMode)
1245 {
1246 this.viewerFaces = new List<ViewerFace>();
1247 this.calcVertexNormals = true;
1248 }
1249
1250 if (this.calcVertexNormals)
1251 this.normals = new List<Coord>();
1252
1253 int step = 0;
1254 int steps = 1;
1255
1256 float length = this.pathCutEnd - this.pathCutBegin;
1257 normalsProcessed = false;
1258
1259 if (this.viewerMode && this.sides == 3)
1260 {
1261 // prisms don't taper well so add some vertical resolution
1262 // other prims may benefit from this but just do prisms for now
1263 if (Math.Abs(this.taperX) > 0.01 || Math.Abs(this.taperY) > 0.01)
1264 steps = (int)(steps * 4.5 * length);
1265 }
1266
1267
1268 float twistBegin = this.twistBegin / 360.0f * twoPi;
1269 float twistEnd = this.twistEnd / 360.0f * twoPi;
1270 float twistTotal = twistEnd - twistBegin;
1271 float twistTotalAbs = Math.Abs(twistTotal);
1272 if (twistTotalAbs > 0.01f)
1273 steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number
1274
1275 float start = -0.5f;
1276 float stepSize = length / (float)steps;
1277 float percentOfPathMultiplier = stepSize;
1278 float xProfileScale = 1.0f;
1279 float yProfileScale = 1.0f;
1280 float xOffset = 0.0f;
1281 float yOffset = 0.0f;
1282 float zOffset = start;
1283 float xOffsetStepIncrement = this.topShearX / steps;
1284 float yOffsetStepIncrement = this.topShearY / steps;
1285
1286 float percentOfPath = this.pathCutBegin;
1287 zOffset += percentOfPath;
1288
1289 float hollow = this.hollow;
1290
1291 // sanity checks
1292 float initialProfileRot = 0.0f;
1293 if (this.sides == 3)
1294 {
1295 if (this.hollowSides == 4)
1296 {
1297 if (hollow > 0.7f)
1298 hollow = 0.7f;
1299 hollow *= 0.707f;
1300 }
1301 else hollow *= 0.5f;
1302 }
1303 else if (this.sides == 4)
1304 {
1305 initialProfileRot = 1.25f * (float)Math.PI;
1306 if (this.hollowSides != 4)
1307 hollow *= 0.707f;
1308 }
1309 else if (this.sides == 24 && this.hollowSides == 4)
1310 hollow *= 1.414f;
1311
1312 Profile profile = new Profile(this.sides, this.profileStart, this.profileEnd, hollow, this.hollowSides, true, calcVertexNormals);
1313 this.numPrimFaces = profile.numPrimFaces;
1314
1315 int cut1Vert = -1;
1316 int cut2Vert = -1;
1317 if (hasProfileCut)
1318 {
1319 cut1Vert = hasHollow ? profile.coords.Count - 1 : 0;
1320 cut2Vert = hasHollow ? profile.numOuterVerts - 1 : profile.numOuterVerts;
1321 }
1322
1323 if (initialProfileRot != 0.0f)
1324 {
1325 profile.AddRot(new Quat(new Coord(0.0f, 0.0f, 1.0f), initialProfileRot));
1326 if (viewerMode)
1327 profile.MakeFaceUVs();
1328 }
1329
1330
1331 Coord lastCutNormal1 = new Coord();
1332 Coord lastCutNormal2 = new Coord();
1333 float lastV = 1.0f;
1334
1335 bool done = false;
1336 while (!done)
1337 {
1338 Profile newLayer = profile.Clone();
1339
1340 if (this.taperX == 0.0f)
1341 xProfileScale = 1.0f;
1342 else if (this.taperX > 0.0f)
1343 xProfileScale = 1.0f - percentOfPath * this.taperX;
1344 else xProfileScale = 1.0f + (1.0f - percentOfPath) * this.taperX;
1345
1346 if (this.taperY == 0.0f)
1347 yProfileScale = 1.0f;
1348 else if (this.taperY > 0.0f)
1349 yProfileScale = 1.0f - percentOfPath * this.taperY;
1350 else yProfileScale = 1.0f + (1.0f - percentOfPath) * this.taperY;
1351
1352 if (xProfileScale != 1.0f || yProfileScale != 1.0f)
1353 newLayer.Scale(xProfileScale, yProfileScale);
1354
1355 float twist = twistBegin + twistTotal * percentOfPath;
1356 if (twist != 0.0f)
1357 newLayer.AddRot(new Quat(new Coord(0.0f, 0.0f, 1.0f), twist));
1358
1359 newLayer.AddPos(xOffset, yOffset, zOffset);
1360
1361 if (step == 0)
1362 {
1363 newLayer.FlipNormals();
1364
1365 // add the top faces to the viewerFaces list here
1366 if (this.viewerMode)
1367 {
1368 Coord faceNormal = newLayer.faceNormal;
1369 ViewerFace newViewerFace = new ViewerFace(0);
1370 foreach (Face face in newLayer.faces)
1371 {
1372 newViewerFace.v1 = newLayer.coords[face.v1];
1373 newViewerFace.v2 = newLayer.coords[face.v2];
1374 newViewerFace.v3 = newLayer.coords[face.v3];
1375
1376 newViewerFace.n1 = faceNormal;
1377 newViewerFace.n2 = faceNormal;
1378 newViewerFace.n3 = faceNormal;
1379
1380 newViewerFace.uv1 = newLayer.faceUVs[face.v1];
1381 newViewerFace.uv2 = newLayer.faceUVs[face.v2];
1382 newViewerFace.uv3 = newLayer.faceUVs[face.v3];
1383
1384 this.viewerFaces.Add(newViewerFace);
1385 }
1386 }
1387 }
1388
1389 // append this layer
1390
1391 int coordsLen = this.coords.Count;
1392 newLayer.AddValue2FaceVertexIndices(coordsLen);
1393
1394 this.coords.AddRange(newLayer.coords);
1395
1396 if (this.calcVertexNormals)
1397 {
1398 newLayer.AddValue2FaceNormalIndices(this.normals.Count);
1399 this.normals.AddRange(newLayer.vertexNormals);
1400 }
1401
1402 if (percentOfPath < this.pathCutBegin + 0.01f || percentOfPath > this.pathCutEnd - 0.01f)
1403 this.faces.AddRange(newLayer.faces);
1404
1405 // fill faces between layers
1406
1407 int numVerts = newLayer.coords.Count;
1408 Face newFace = new Face();
1409
1410 if (step > 0)
1411 {
1412 int startVert = coordsLen + 1;
1413 int endVert = this.coords.Count;
1414
1415 if (sides < 5 || this.hasProfileCut || hollow > 0.0f)
1416 startVert--;
1417
1418 for (int i = startVert; i < endVert; i++)
1419 {
1420 int iNext = i + 1;
1421 if (i == endVert - 1)
1422 iNext = startVert;
1423
1424 int whichVert = i - startVert;
1425
1426 newFace.v1 = i;
1427 newFace.v2 = i - numVerts;
1428 newFace.v3 = iNext - numVerts;
1429 this.faces.Add(newFace);
1430
1431 newFace.v2 = iNext - numVerts;
1432 newFace.v3 = iNext;
1433 this.faces.Add(newFace);
1434
1435 if (this.viewerMode)
1436 {
1437 // add the side faces to the list of viewerFaces here
1438 int primFaceNum = 1;
1439 if (whichVert >= sides)
1440 primFaceNum = 2;
1441 ViewerFace newViewerFace1 = new ViewerFace(primFaceNum);
1442 ViewerFace newViewerFace2 = new ViewerFace(primFaceNum);
1443
1444 float u1 = newLayer.us[whichVert];
1445 float u2 = 1.0f;
1446 if (whichVert < newLayer.us.Count - 1)
1447 u2 = newLayer.us[whichVert + 1];
1448
1449 if (whichVert == cut1Vert || whichVert == cut2Vert)
1450 {
1451 u1 = 0.0f;
1452 u2 = 1.0f;
1453 }
1454 else if (sides < 5)
1455 { // boxes and prisms have one texture face per side of the prim, so the U values have to be scaled
1456 // to reflect the entire texture width
1457 u1 *= sides;
1458 u2 *= sides;
1459 u2 -= (int)u1;
1460 u1 -= (int)u1;
1461 if (u2 < 0.1f)
1462 u2 = 1.0f;
1463
1464 //newViewerFace2.primFaceNumber = newViewerFace1.primFaceNumber = whichVert + 1;
1465 }
1466
1467 newViewerFace1.uv1.U = u1;
1468 newViewerFace1.uv2.U = u1;
1469 newViewerFace1.uv3.U = u2;
1470
1471 newViewerFace1.uv1.V = 1.0f - percentOfPath;
1472 newViewerFace1.uv2.V = lastV;
1473 newViewerFace1.uv3.V = lastV;
1474
1475 newViewerFace2.uv1.U = u1;
1476 newViewerFace2.uv2.U = u2;
1477 newViewerFace2.uv3.U = u2;
1478
1479 newViewerFace2.uv1.V = 1.0f - percentOfPath;
1480 newViewerFace2.uv2.V = lastV;
1481 newViewerFace2.uv3.V = 1.0f - percentOfPath;
1482
1483 newViewerFace1.v1 = this.coords[i];
1484 newViewerFace1.v2 = this.coords[i - numVerts];
1485 newViewerFace1.v3 = this.coords[iNext - numVerts];
1486
1487 newViewerFace2.v1 = this.coords[i];
1488 newViewerFace2.v2 = this.coords[iNext - numVerts];
1489 newViewerFace2.v3 = this.coords[iNext];
1490
1491 // profile cut faces
1492 if (whichVert == cut1Vert)
1493 {
1494 newViewerFace1.n1 = newLayer.cutNormal1;
1495 newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal1;
1496
1497 newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal1;
1498 newViewerFace2.n2 = lastCutNormal1;
1499 }
1500 else if (whichVert == cut2Vert)
1501 {
1502 newViewerFace1.n1 = newLayer.cutNormal2;
1503 newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal2;
1504
1505 newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal2;
1506 newViewerFace2.n2 = lastCutNormal2;
1507 }
1508
1509 else // outer and hollow faces
1510 {
1511 if ((sides < 5 && whichVert < newLayer.numOuterVerts) || (hollowSides < 5 && whichVert >= newLayer.numOuterVerts))
1512 {
1513 newViewerFace1.CalcSurfaceNormal();
1514 newViewerFace2.CalcSurfaceNormal();
1515 }
1516 else
1517 {
1518 newViewerFace1.n1 = this.normals[i];
1519 newViewerFace1.n2 = this.normals[i - numVerts];
1520 newViewerFace1.n3 = this.normals[iNext - numVerts];
1521
1522 newViewerFace2.n1 = this.normals[i];
1523 newViewerFace2.n2 = this.normals[iNext - numVerts];
1524 newViewerFace2.n3 = this.normals[iNext];
1525 }
1526 }
1527
1528 newViewerFace2.primFaceNumber = newViewerFace1.primFaceNumber = newLayer.faceNumbers[whichVert];
1529
1530 this.viewerFaces.Add(newViewerFace1);
1531 this.viewerFaces.Add(newViewerFace2);
1532
1533 }
1534 }
1535 }
1536
1537 lastCutNormal1 = newLayer.cutNormal1;
1538 lastCutNormal2 = newLayer.cutNormal2;
1539 lastV = 1.0f - percentOfPath;
1540
1541 // calc the step for the next iteration of the loop
1542
1543 if (step < steps)
1544 {
1545 step += 1;
1546 percentOfPath += percentOfPathMultiplier;
1547 xOffset += xOffsetStepIncrement;
1548 yOffset += yOffsetStepIncrement;
1549 zOffset += stepSize;
1550 if (percentOfPath > this.pathCutEnd)
1551 done = true;
1552 }
1553 else done = true;
1554
1555 if (done && viewerMode)
1556 {
1557 // add the top faces to the viewerFaces list here
1558 Coord faceNormal = newLayer.faceNormal;
1559 ViewerFace newViewerFace = new ViewerFace();
1560 newViewerFace.primFaceNumber = newLayer.bottomFaceNumber;
1561 foreach (Face face in newLayer.faces)
1562 {
1563 newViewerFace.v1 = newLayer.coords[face.v1 - coordsLen];
1564 newViewerFace.v2 = newLayer.coords[face.v2 - coordsLen];
1565 newViewerFace.v3 = newLayer.coords[face.v3 - coordsLen];
1566
1567 newViewerFace.n1 = faceNormal;
1568 newViewerFace.n2 = faceNormal;
1569 newViewerFace.n3 = faceNormal;
1570
1571 newViewerFace.uv1 = newLayer.faceUVs[face.v1 - coordsLen];
1572 newViewerFace.uv2 = newLayer.faceUVs[face.v2 - coordsLen];
1573 newViewerFace.uv3 = newLayer.faceUVs[face.v3 - coordsLen];
1574
1575 this.viewerFaces.Add(newViewerFace);
1576 }
1577 }
1578 }
1579 }
1580
1581 /// <summary>
1582 /// Extrude a profile into a circular path prim mesh. Used for prim types torus, tube, and ring.
1583 /// </summary>
1584 public void ExtrudeCircular()
1585 {
1586 this.coords = new List<Coord>();
1587 this.faces = new List<Face>();
1588
1589 if (this.viewerMode)
1590 {
1591 this.viewerFaces = new List<ViewerFace>();
1592 this.calcVertexNormals = true;
1593 }
1594
1595 if (this.calcVertexNormals)
1596 this.normals = new List<Coord>();
1597
1598 int step = 0;
1599 int steps = 24;
1600
1601 normalsProcessed = false;
1602
1603 float twistBegin = this.twistBegin / 360.0f * twoPi;
1604 float twistEnd = this.twistEnd / 360.0f * twoPi;
1605 float twistTotal = twistEnd - twistBegin;
1606
1607 // if the profile has a lot of twist, add more layers otherwise the layers may overlap
1608 // and the resulting mesh may be quite inaccurate. This method is arbitrary and doesn't
1609 // accurately match the viewer
1610 float twistTotalAbs = Math.Abs(twistTotal);
1611 if (twistTotalAbs > 0.01f)
1612 {
1613 if (twistTotalAbs > Math.PI * 1.5f)
1614 steps *= 2;
1615 if (twistTotalAbs > Math.PI * 3.0f)
1616 steps *= 2;
1617 }
1618
1619 float yPathScale = this.holeSizeY * 0.5f;
1620 float pathLength = this.pathCutEnd - this.pathCutBegin;
1621 float totalSkew = this.skew * 2.0f * pathLength;
1622 float skewStart = this.pathCutBegin * 2.0f * this.skew - this.skew;
1623 float xOffsetTopShearXFactor = this.topShearX * (0.25f + 0.5f * (0.5f - this.holeSizeY));
1624 float yShearCompensation = 1.0f + Math.Abs(this.topShearY) * 0.25f;
1625
1626 // It's not quite clear what pushY (Y top shear) does, but subtracting it from the start and end
1627 // angles appears to approximate it's effects on path cut. Likewise, adding it to the angle used
1628 // to calculate the sine for generating the path radius appears to approximate it's effects there
1629 // too, but there are some subtle differences in the radius which are noticeable as the prim size
1630 // increases and it may affect megaprims quite a bit. The effect of the Y top shear parameter on
1631 // the meshes generated with this technique appear nearly identical in shape to the same prims when
1632 // displayed by the viewer.
1633
1634 float startAngle = (twoPi * this.pathCutBegin * this.revolutions) - this.topShearY * 0.9f;
1635 float endAngle = (twoPi * this.pathCutEnd * this.revolutions) - this.topShearY * 0.9f;
1636 float stepSize = twoPi / this.stepsPerRevolution;
1637
1638 step = (int)(startAngle / stepSize);
1639 int firstStep = step;
1640 float angle = startAngle;
1641 float hollow = this.hollow;
1642
1643 // sanity checks
1644 float initialProfileRot = 0.0f;
1645 if (this.sides == 3)
1646 {
1647 initialProfileRot = (float)Math.PI;
1648 if (this.hollowSides == 4)
1649 {
1650 if (hollow > 0.7f)
1651 hollow = 0.7f;
1652 hollow *= 0.707f;
1653 }
1654 else hollow *= 0.5f;
1655 }
1656 else if (this.sides == 4)
1657 {
1658 initialProfileRot = 0.25f * (float)Math.PI;
1659 if (this.hollowSides != 4)
1660 hollow *= 0.707f;
1661 }
1662 else if (this.sides > 4)
1663 {
1664 initialProfileRot = (float)Math.PI;
1665 if (this.hollowSides == 4)
1666 {
1667 if (hollow > 0.7f)
1668 hollow = 0.7f;
1669 hollow /= 0.7f;
1670 }
1671 }
1672
1673 bool needEndFaces = false;
1674 if (this.pathCutBegin != 0.0f || this.pathCutEnd != 1.0f)
1675 needEndFaces = true;
1676 else if (this.taperX != 0.0f || this.taperY != 0.0f)
1677 needEndFaces = true;
1678 else if (this.skew != 0.0f)
1679 needEndFaces = true;
1680 else if (twistTotal != 0.0f)
1681 needEndFaces = true;
1682 else if (this.radius != 0.0f)
1683 needEndFaces = true;
1684
1685 Profile profile = new Profile(this.sides, this.profileStart, this.profileEnd, hollow, this.hollowSides, needEndFaces, calcVertexNormals);
1686 this.numPrimFaces = profile.numPrimFaces;
1687
1688 int cut1Vert = -1;
1689 int cut2Vert = -1;
1690 if (hasProfileCut)
1691 {
1692 cut1Vert = hasHollow ? profile.coords.Count - 1 : 0;
1693 cut2Vert = hasHollow ? profile.numOuterVerts - 1 : profile.numOuterVerts;
1694 }
1695
1696 if (initialProfileRot != 0.0f)
1697 {
1698 profile.AddRot(new Quat(new Coord(0.0f, 0.0f, 1.0f), initialProfileRot));
1699 if (viewerMode)
1700 profile.MakeFaceUVs();
1701 }
1702
1703 Coord lastCutNormal1 = new Coord();
1704 Coord lastCutNormal2 = new Coord();
1705 float lastV = 1.0f;
1706
1707 bool done = false;
1708 while (!done) // loop through the length of the path and add the layers
1709 {
1710 bool isEndLayer = false;
1711 if (angle <= startAngle + .01f || angle >= endAngle - .01f)
1712 isEndLayer = true;
1713
1714 //Profile newLayer = profile.Clone(isEndLayer && needEndFaces);
1715 Profile newLayer = profile.Clone();
1716
1717 float xProfileScale = (1.0f - Math.Abs(this.skew)) * this.holeSizeX;
1718 float yProfileScale = this.holeSizeY;
1719
1720 float percentOfPath = angle / (twoPi * this.revolutions);
1721 float percentOfAngles = (angle - startAngle) / (endAngle - startAngle);
1722
1723 if (this.taperX > 0.01f)
1724 xProfileScale *= 1.0f - percentOfPath * this.taperX;
1725 else if (this.taperX < -0.01f)
1726 xProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperX;
1727
1728 if (this.taperY > 0.01f)
1729 yProfileScale *= 1.0f - percentOfPath * this.taperY;
1730 else if (this.taperY < -0.01f)
1731 yProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperY;
1732
1733 if (xProfileScale != 1.0f || yProfileScale != 1.0f)
1734 newLayer.Scale(xProfileScale, yProfileScale);
1735
1736 float radiusScale = 1.0f;
1737 if (this.radius > 0.001f)
1738 radiusScale = 1.0f - this.radius * percentOfPath;
1739 else if (this.radius < 0.001f)
1740 radiusScale = 1.0f + this.radius * (1.0f - percentOfPath);
1741
1742 float twist = twistBegin + twistTotal * percentOfPath;
1743
1744 float xOffset = 0.5f * (skewStart + totalSkew * percentOfAngles);
1745 xOffset += (float)Math.Sin(angle) * xOffsetTopShearXFactor;
1746
1747 float yOffset = yShearCompensation * (float)Math.Cos(angle) * (0.5f - yPathScale) * radiusScale;
1748
1749 float zOffset = (float)Math.Sin(angle + this.topShearY) * (0.5f - yPathScale) * radiusScale;
1750
1751 // next apply twist rotation to the profile layer
1752 if (twistTotal != 0.0f || twistBegin != 0.0f)
1753 newLayer.AddRot(new Quat(new Coord(0.0f, 0.0f, 1.0f), twist));
1754
1755 // now orient the rotation of the profile layer relative to it's position on the path
1756 // adding taperY to the angle used to generate the quat appears to approximate the viewer
1757 newLayer.AddRot(new Quat(new Coord(1.0f, 0.0f, 0.0f), angle + this.topShearY));
1758 newLayer.AddPos(xOffset, yOffset, zOffset);
1759
1760 if (isEndLayer && angle <= startAngle + .01f)
1761 {
1762 newLayer.FlipNormals();
1763
1764 // add the top faces to the viewerFaces list here
1765 if (this.viewerMode && needEndFaces)
1766 {
1767 Coord faceNormal = newLayer.faceNormal;
1768 ViewerFace newViewerFace = new ViewerFace();
1769 newViewerFace.primFaceNumber = 0;
1770 foreach (Face face in newLayer.faces)
1771 {
1772 newViewerFace.v1 = newLayer.coords[face.v1];
1773 newViewerFace.v2 = newLayer.coords[face.v2];
1774 newViewerFace.v3 = newLayer.coords[face.v3];
1775
1776 newViewerFace.n1 = faceNormal;
1777 newViewerFace.n2 = faceNormal;
1778 newViewerFace.n3 = faceNormal;
1779
1780 newViewerFace.uv1 = newLayer.faceUVs[face.v1];
1781 newViewerFace.uv2 = newLayer.faceUVs[face.v2];
1782 newViewerFace.uv3 = newLayer.faceUVs[face.v3];
1783
1784 this.viewerFaces.Add(newViewerFace);
1785 }
1786 }
1787 }
1788
1789 // append the layer and fill in the sides
1790
1791 int coordsLen = this.coords.Count;
1792 newLayer.AddValue2FaceVertexIndices(coordsLen);
1793
1794 this.coords.AddRange(newLayer.coords);
1795
1796 if (this.calcVertexNormals)
1797 {
1798 newLayer.AddValue2FaceNormalIndices(this.normals.Count);
1799 this.normals.AddRange(newLayer.vertexNormals);
1800 }
1801
1802 if (isEndLayer)
1803 this.faces.AddRange(newLayer.faces);
1804
1805 // fill faces between layers
1806
1807 int numVerts = newLayer.coords.Count;
1808 Face newFace = new Face();
1809 if (step > firstStep)
1810 {
1811 int startVert = coordsLen + 1;
1812 int endVert = this.coords.Count;
1813
1814 if (sides < 5 || this.hasProfileCut || hollow > 0.0f)
1815 startVert--;
1816
1817 for (int i = startVert; i < endVert; i++)
1818 {
1819 int iNext = i + 1;
1820 if (i == endVert - 1)
1821 iNext = startVert;
1822
1823 int whichVert = i - startVert;
1824
1825 newFace.v1 = i;
1826 newFace.v2 = i - numVerts;
1827 newFace.v3 = iNext - numVerts;
1828 this.faces.Add(newFace);
1829
1830 newFace.v2 = iNext - numVerts;
1831 newFace.v3 = iNext;
1832 this.faces.Add(newFace);
1833
1834 if (this.viewerMode)
1835 {
1836 // add the side faces to the list of viewerFaces here
1837 ViewerFace newViewerFace1 = new ViewerFace();
1838 ViewerFace newViewerFace2 = new ViewerFace();
1839 float u1 = newLayer.us[whichVert];
1840 float u2 = 1.0f;
1841 if (whichVert < newLayer.us.Count - 1)
1842 u2 = newLayer.us[whichVert + 1];
1843
1844 if (whichVert == cut1Vert || whichVert == cut2Vert)
1845 {
1846 u1 = 0.0f;
1847 u2 = 1.0f;
1848 }
1849 else if (sides < 5)
1850 { // boxes and prisms have one texture face per side of the prim, so the U values have to be scaled
1851 // to reflect the entire texture width
1852 u1 *= sides;
1853 u2 *= sides;
1854 u2 -= (int)u1;
1855 u1 -= (int)u1;
1856 if (u2 < 0.1f)
1857 u2 = 1.0f;
1858
1859 //newViewerFace2.primFaceNumber = newViewerFace1.primFaceNumber = whichVert + 1;
1860 }
1861
1862 newViewerFace1.uv1.U = u1;
1863 newViewerFace1.uv2.U = u1;
1864 newViewerFace1.uv3.U = u2;
1865
1866 newViewerFace1.uv1.V = 1.0f - percentOfPath;
1867 newViewerFace1.uv2.V = lastV;
1868 newViewerFace1.uv3.V = lastV;
1869
1870 newViewerFace2.uv1.U = u1;
1871 newViewerFace2.uv2.U = u2;
1872 newViewerFace2.uv3.U = u2;
1873
1874 newViewerFace2.uv1.V = 1.0f - percentOfPath;
1875 newViewerFace2.uv2.V = lastV;
1876 newViewerFace2.uv3.V = 1.0f - percentOfPath;
1877
1878 newViewerFace1.v1 = this.coords[i];
1879 newViewerFace1.v2 = this.coords[i - numVerts];
1880 newViewerFace1.v3 = this.coords[iNext - numVerts];
1881
1882 newViewerFace2.v1 = this.coords[i];
1883 newViewerFace2.v2 = this.coords[iNext - numVerts];
1884 newViewerFace2.v3 = this.coords[iNext];
1885
1886 // profile cut faces
1887 if (whichVert == cut1Vert)
1888 {
1889 newViewerFace1.n1 = newLayer.cutNormal1;
1890 newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal1;
1891
1892 newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal1;
1893 newViewerFace2.n2 = lastCutNormal1;
1894 }
1895 else if (whichVert == cut2Vert)
1896 {
1897 newViewerFace1.n1 = newLayer.cutNormal2;
1898 newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal2;
1899
1900 newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal2;
1901 newViewerFace2.n2 = lastCutNormal2;
1902 }
1903 else // periphery faces
1904 {
1905 if (sides < 5 && whichVert < newLayer.numOuterVerts)
1906 {
1907 newViewerFace1.n1 = this.normals[i];
1908 newViewerFace1.n2 = this.normals[i - numVerts];
1909 newViewerFace1.n3 = this.normals[i - numVerts];
1910
1911 newViewerFace2.n1 = this.normals[i];
1912 newViewerFace2.n2 = this.normals[i - numVerts];
1913 newViewerFace2.n3 = this.normals[i];
1914 }
1915 else if (hollowSides < 5 && whichVert >= newLayer.numOuterVerts)
1916 {
1917 newViewerFace1.n1 = this.normals[iNext];
1918 newViewerFace1.n2 = this.normals[iNext - numVerts];
1919 newViewerFace1.n3 = this.normals[iNext - numVerts];
1920
1921 newViewerFace2.n1 = this.normals[iNext];
1922 newViewerFace2.n2 = this.normals[iNext - numVerts];
1923 newViewerFace2.n3 = this.normals[iNext];
1924 }
1925 else
1926 {
1927 newViewerFace1.n1 = this.normals[i];
1928 newViewerFace1.n2 = this.normals[i - numVerts];
1929 newViewerFace1.n3 = this.normals[iNext - numVerts];
1930
1931 newViewerFace2.n1 = this.normals[i];
1932 newViewerFace2.n2 = this.normals[iNext - numVerts];
1933 newViewerFace2.n3 = this.normals[iNext];
1934 }
1935 }
1936
1937 newViewerFace1.primFaceNumber = newViewerFace2.primFaceNumber = newLayer.faceNumbers[whichVert];
1938 this.viewerFaces.Add(newViewerFace1);
1939 this.viewerFaces.Add(newViewerFace2);
1940
1941 }
1942 }
1943 }
1944
1945 lastCutNormal1 = newLayer.cutNormal1;
1946 lastCutNormal2 = newLayer.cutNormal2;
1947 lastV = 1.0f - percentOfPath;
1948
1949 // calculate terms for next iteration
1950 // calculate the angle for the next iteration of the loop
1951
1952 if (angle >= endAngle)
1953 done = true;
1954 else
1955 {
1956 step += 1;
1957 angle = stepSize * step;
1958 if (angle > endAngle)
1959 angle = endAngle;
1960 }
1961
1962 if (done && viewerMode && needEndFaces)
1963 {
1964 // add the bottom faces to the viewerFaces list here
1965 Coord faceNormal = newLayer.faceNormal;
1966 ViewerFace newViewerFace = new ViewerFace();
1967 newViewerFace.primFaceNumber = newLayer.bottomFaceNumber;
1968 foreach (Face face in newLayer.faces)
1969 {
1970 newViewerFace.v1 = newLayer.coords[face.v1 - coordsLen];
1971 newViewerFace.v2 = newLayer.coords[face.v2 - coordsLen];
1972 newViewerFace.v3 = newLayer.coords[face.v3 - coordsLen];
1973
1974 newViewerFace.n1 = faceNormal;
1975 newViewerFace.n2 = faceNormal;
1976 newViewerFace.n3 = faceNormal;
1977
1978 newViewerFace.uv1 = newLayer.faceUVs[face.v1 - coordsLen];
1979 newViewerFace.uv2 = newLayer.faceUVs[face.v2 - coordsLen];
1980 newViewerFace.uv3 = newLayer.faceUVs[face.v3 - coordsLen];
1981
1982 this.viewerFaces.Add(newViewerFace);
1983 }
1984 }
1985 }
1986 }
1987
1988 private Coord SurfaceNormal(Coord c1, Coord c2, Coord c3)
1989 {
1990 Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z);
1991 Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z);
1992
1993 Coord normal = Coord.Cross(edge1, edge2);
1994
1995 normal.Normalize();
1996
1997 return normal;
1998 }
1999
2000 private Coord SurfaceNormal(Face face)
2001 {
2002 return SurfaceNormal(this.coords[face.v1], this.coords[face.v2], this.coords[face.v3]);
2003 }
2004
2005 /// <summary>
2006 /// Calculate the surface normal for a face in the list of faces
2007 /// </summary>
2008 /// <param name="faceIndex"></param>
2009 /// <returns></returns>
2010 public Coord SurfaceNormal(int faceIndex)
2011 {
2012 int numFaces = this.faces.Count;
2013 if (faceIndex < 0 || faceIndex >= numFaces)
2014 throw new Exception("faceIndex out of range");
2015
2016 return SurfaceNormal(this.faces[faceIndex]);
2017 }
2018
2019 /// <summary>
2020 /// Calculate surface normals for all of the faces in the list of faces in this mesh
2021 /// </summary>
2022 public void CalcNormals()
2023 {
2024 if (normalsProcessed)
2025 return;
2026
2027 normalsProcessed = true;
2028
2029 int numFaces = faces.Count;
2030
2031 if (!this.calcVertexNormals)
2032 this.normals = new List<Coord>();
2033
2034 for (int i = 0; i < numFaces; i++)
2035 {
2036 Face face = faces[i];
2037
2038 this.normals.Add(SurfaceNormal(i).Normalize());
2039
2040 int normIndex = normals.Count - 1;
2041 face.n1 = normIndex;
2042 face.n2 = normIndex;
2043 face.n3 = normIndex;
2044
2045 this.faces[i] = face;
2046 }
2047 }
2048
2049 /// <summary>
2050 /// Adds a value to each XYZ vertex coordinate in the mesh
2051 /// </summary>
2052 /// <param name="x"></param>
2053 /// <param name="y"></param>
2054 /// <param name="z"></param>
2055 public void AddPos(float x, float y, float z)
2056 {
2057 int i;
2058 int numVerts = this.coords.Count;
2059 Coord vert;
2060
2061 for (i = 0; i < numVerts; i++)
2062 {
2063 vert = this.coords[i];
2064 vert.X += x;
2065 vert.Y += y;
2066 vert.Z += z;
2067 this.coords[i] = vert;
2068 }
2069 }
2070
2071 /// <summary>
2072 /// Rotates the mesh
2073 /// </summary>
2074 /// <param name="q"></param>
2075 public void AddRot(Quat q)
2076 {
2077 int i;
2078 int numVerts = this.coords.Count;
2079
2080 for (i = 0; i < numVerts; i++)
2081 this.coords[i] *= q;
2082
2083 if (this.normals != null)
2084 {
2085 int numNormals = this.normals.Count;
2086 for (i = 0; i < numNormals; i++)
2087 this.normals[i] *= q;
2088 }
2089
2090 if (this.viewerFaces != null)
2091 {
2092 int numViewerFaces = this.viewerFaces.Count;
2093
2094 for (i = 0; i < numViewerFaces; i++)
2095 {
2096 ViewerFace v = this.viewerFaces[i];
2097 v.v1 *= q;
2098 v.v2 *= q;
2099 v.v3 *= q;
2100
2101 v.n1 *= q;
2102 v.n2 *= q;
2103 v.n3 *= q;
2104 this.viewerFaces[i] = v;
2105 }
2106 }
2107
2108 }
2109
2110 /// <summary>
2111 /// Scales the mesh
2112 /// </summary>
2113 /// <param name="x"></param>
2114 /// <param name="y"></param>
2115 /// <param name="z"></param>
2116 public void Scale(float x, float y, float z)
2117 {
2118 int i;
2119 int numVerts = this.coords.Count;
2120 //Coord vert;
2121
2122 Coord m = new Coord(x, y, z);
2123 for (i = 0; i < numVerts; i++)
2124 this.coords[i] *= m;
2125
2126 if (this.viewerFaces != null)
2127 {
2128 int numViewerFaces = this.viewerFaces.Count;
2129 for (i = 0; i < numViewerFaces; i++)
2130 {
2131 ViewerFace v = this.viewerFaces[i];
2132 v.v1 *= m;
2133 v.v2 *= m;
2134 v.v3 *= m;
2135 this.viewerFaces[i] = v;
2136 }
2137
2138 }
2139
2140 }
2141
2142 /// <summary>
2143 /// Dumps the mesh to a Blender compatible "Raw" format file
2144 /// </summary>
2145 /// <param name="path"></param>
2146 /// <param name="name"></param>
2147 /// <param name="title"></param>
2148 public void DumpRaw(String path, String name, String title)
2149 {
2150 if (path == null)
2151 return;
2152 String fileName = name + "_" + title + ".raw";
2153 String completePath = Path.Combine(path, fileName);
2154 StreamWriter sw = new StreamWriter(completePath);
2155
2156 for (int i = 0; i < this.faces.Count; i++)
2157 {
2158 string s = this.coords[this.faces[i].v1].ToString();
2159 s += " " + this.coords[this.faces[i].v2].ToString();
2160 s += " " + this.coords[this.faces[i].v3].ToString();
2161
2162 sw.WriteLine(s);
2163 }
2164
2165 sw.Close();
2166 }
2167 }
2168}
diff --git a/OpenSim/Region/Physics/Meshing/SculptMesh.cs b/OpenSim/Region/Physics/Meshing/SculptMesh.cs
new file mode 100644
index 0000000..312f89a
--- /dev/null
+++ b/OpenSim/Region/Physics/Meshing/SculptMesh.cs
@@ -0,0 +1,343 @@
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 OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Text;
31using System.IO;
32using System.Drawing;
33using System.Drawing.Imaging;
34
35namespace PrimMesher
36{
37
38 public class SculptMesh
39 {
40 public List<Coord> coords;
41 public List<Face> faces;
42
43 public List<ViewerFace> viewerFaces;
44 public List<Coord> normals;
45 public List<UVCoord> uvs;
46
47 public enum SculptType { sphere = 1, torus = 2, plane = 3, cylinder = 4 };
48 private const float pixScale = 0.00390625f; // 1.0 / 256
49
50 private Bitmap ScaleImage(Bitmap srcImage, float scale)
51 {
52 int sourceWidth = srcImage.Width;
53 int sourceHeight = srcImage.Height;
54 int sourceX = 0;
55 int sourceY = 0;
56
57 int destX = 0;
58 int destY = 0;
59 int destWidth = (int)(sourceWidth * scale);
60 int destHeight = (int)(sourceHeight * scale);
61
62 Bitmap scaledImage = new Bitmap(destWidth, destHeight,
63 PixelFormat.Format24bppRgb);
64 scaledImage.SetResolution(srcImage.HorizontalResolution,
65 srcImage.VerticalResolution);
66
67 Graphics grPhoto = Graphics.FromImage(scaledImage);
68 grPhoto.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear;
69
70 grPhoto.DrawImage(srcImage,
71 new Rectangle(destX, destY, destWidth, destHeight),
72 new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight),
73 GraphicsUnit.Pixel);
74
75 grPhoto.Dispose();
76 return scaledImage;
77 }
78
79 public SculptMesh SculptMeshFromFile(string fileName, SculptType sculptType, int lod, bool viewerMode)
80 {
81 Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName);
82 SculptMesh sculptMesh = new SculptMesh(bitmap, sculptType, lod, viewerMode);
83 bitmap.Dispose();
84 return sculptMesh;
85 }
86
87 public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode)
88 {
89 coords = new List<Coord>();
90 faces = new List<Face>();
91 normals = new List<Coord>();
92 uvs = new List<UVCoord>();
93
94 float sourceScaleFactor = (float)lod / (float)Math.Max(sculptBitmap.Width, sculptBitmap.Height);
95 bool scaleSourceImage = sourceScaleFactor < 1.0f ? true : false;
96
97 Bitmap bitmap;
98 if (scaleSourceImage)
99 bitmap = ScaleImage(sculptBitmap, sourceScaleFactor);
100 else
101 bitmap = sculptBitmap;
102
103 viewerFaces = new List<ViewerFace>();
104
105 int width = bitmap.Width;
106 int height = bitmap.Height;
107
108 float widthUnit = 1.0f / width;
109 float heightUnit = 1.0f / (height - 1);
110
111 int p1, p2, p3, p4;
112 Color color;
113 float x, y, z;
114
115 int imageX, imageY;
116
117 if (sculptType == SculptType.sphere)
118 { // average the top and bottom row pixel values so the resulting vertices appear to converge
119 int lastRow = height - 1;
120 int r1 = 0, g1 = 0, b1 = 0;
121 int r2 = 0, g2 = 0, b2 = 0;
122 for (imageX = 0; imageX < width; imageX++)
123 {
124 Color c1 = bitmap.GetPixel(imageX, 0);
125 Color c2 = bitmap.GetPixel(imageX, lastRow);
126
127 r1 += c1.R;
128 g1 += c1.G;
129 b1 += c1.B;
130
131 r2 += c2.R;
132 g2 += c2.G;
133 b2 += c2.B;
134 }
135
136 Color newC1 = Color.FromArgb(r1 / width, g1 / width, b1 / width);
137 Color newC2 = Color.FromArgb(r2 / width, g2 / width, b2 / width);
138
139 for (imageX = 0; imageX < width; imageX++)
140 {
141 bitmap.SetPixel(imageX, 0, newC1);
142 bitmap.SetPixel(imageX, lastRow, newC2);
143 }
144 }
145
146
147 int pixelsAcross = sculptType == SculptType.plane ? width : width + 1;
148 int pixelsDown = sculptType == SculptType.sphere || sculptType == SculptType.cylinder ? height + 1 : height;
149
150 for (imageY = 0; imageY < pixelsDown; imageY++)
151 {
152 int rowOffset = imageY * width;
153
154 for (imageX = 0; imageX < pixelsAcross; imageX++)
155 {
156 /*
157 * p1-----p2
158 * | \ f2 |
159 * | \ |
160 * | f1 \|
161 * p3-----p4
162 */
163
164 if (imageX < width)
165 {
166 p4 = rowOffset + imageX;
167 p3 = p4 - 1;
168 }
169 else
170 {
171 p4 = rowOffset; // wrap around to beginning
172 p3 = rowOffset + imageX - 1;
173 }
174
175 p2 = p4 - width;
176 p1 = p3 - width;
177
178 color = bitmap.GetPixel(imageX == width ? 0 : imageX, imageY == height ? height - 1 : imageY);
179
180 x = (color.R - 128) * pixScale;
181 y = (color.G - 128) * pixScale;
182 z = (color.B - 128) * pixScale;
183
184 Coord c = new Coord(x, y, z);
185 this.coords.Add(c);
186 if (viewerMode)
187 {
188 this.normals.Add(new Coord());
189 this.uvs.Add(new UVCoord(widthUnit * imageX, heightUnit * imageY));
190 }
191
192 if (imageY > 0 && imageX > 0)
193 {
194 Face f1, f2;
195
196 if (viewerMode)
197 {
198 f1 = new Face(p1, p3, p4, p1, p3, p4);
199 f1.uv1 = p1;
200 f1.uv2 = p3;
201 f1.uv3 = p4;
202
203 f2 = new Face(p1, p4, p2, p1, p4, p2);
204 f2.uv1 = p1;
205 f2.uv2 = p4;
206 f2.uv3 = p2;
207 }
208 else
209 {
210 f1 = new Face(p1, p3, p4);
211 f2 = new Face(p1, p4, p2);
212 }
213
214 this.faces.Add(f1);
215 this.faces.Add(f2);
216 }
217 }
218 }
219
220 if (scaleSourceImage)
221 bitmap.Dispose();
222
223 if (viewerMode)
224 { // compute vertex normals by summing all the surface normals of all the triangles sharing
225 // each vertex and then normalizing
226 int numFaces = this.faces.Count;
227 for (int i = 0; i < numFaces; i++)
228 {
229 Face face = this.faces[i];
230 Coord surfaceNormal = face.SurfaceNormal(this.coords);
231 this.normals[face.v1] += surfaceNormal;
232 this.normals[face.v2] += surfaceNormal;
233 this.normals[face.v3] += surfaceNormal;
234 }
235
236 int numCoords = this.coords.Count;
237 for (int i = 0; i < numCoords; i++)
238 this.coords[i].Normalize();
239
240 if (sculptType != SculptType.plane)
241 { // blend the vertex normals at the cylinder seam
242 pixelsAcross = width + 1;
243 for (imageY = 0; imageY < height; imageY++)
244 {
245 int rowOffset = imageY * pixelsAcross;
246
247 this.normals[rowOffset] = this.normals[rowOffset + width - 1] = (this.normals[rowOffset] + this.normals[rowOffset + width - 1]).Normalize();
248 }
249 }
250
251 foreach (Face face in this.faces)
252 {
253 ViewerFace vf = new ViewerFace(0);
254 vf.v1 = this.coords[face.v1];
255 vf.v2 = this.coords[face.v2];
256 vf.v3 = this.coords[face.v3];
257
258 vf.n1 = this.normals[face.n1];
259 vf.n2 = this.normals[face.n2];
260 vf.n3 = this.normals[face.n3];
261
262 vf.uv1 = this.uvs[face.uv1];
263 vf.uv2 = this.uvs[face.uv2];
264 vf.uv3 = this.uvs[face.uv3];
265
266 this.viewerFaces.Add(vf);
267 }
268 }
269 }
270
271 public void AddRot(Quat q)
272 {
273 int i;
274 int numVerts = this.coords.Count;
275
276 for (i = 0; i < numVerts; i++)
277 this.coords[i] *= q;
278
279 if (this.viewerFaces != null)
280 {
281 int numViewerFaces = this.viewerFaces.Count;
282
283 for (i = 0; i < numViewerFaces; i++)
284 {
285 ViewerFace v = this.viewerFaces[i];
286 v.v1 *= q;
287 v.v2 *= q;
288 v.v3 *= q;
289
290 v.n1 *= q;
291 v.n2 *= q;
292 v.n3 *= q;
293
294 this.viewerFaces[i] = v;
295 }
296 }
297 }
298
299 public void Scale(float x, float y, float z)
300 {
301 int i;
302 int numVerts = this.coords.Count;
303 //Coord vert;
304
305 Coord m = new Coord(x, y, z);
306 for (i = 0; i < numVerts; i++)
307 this.coords[i] *= m;
308
309 if (this.viewerFaces != null)
310 {
311 int numViewerFaces = this.viewerFaces.Count;
312 for (i = 0; i < numViewerFaces; i++)
313 {
314 ViewerFace v = this.viewerFaces[i];
315 v.v1 *= m;
316 v.v2 *= m;
317 v.v3 *= m;
318 this.viewerFaces[i] = v;
319 }
320 }
321 }
322
323 public void DumpRaw(String path, String name, String title)
324 {
325 if (path == null)
326 return;
327 String fileName = name + "_" + title + ".raw";
328 String completePath = Path.Combine(path, fileName);
329 StreamWriter sw = new StreamWriter(completePath);
330
331 for (int i = 0; i < this.faces.Count; i++)
332 {
333 string s = this.coords[this.faces[i].v1].ToString();
334 s += " " + this.coords[this.faces[i].v2].ToString();
335 s += " " + this.coords[this.faces[i].v3].ToString();
336
337 sw.WriteLine(s);
338 }
339
340 sw.Close();
341 }
342 }
343}