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