aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llmath/llvolume.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llmath/llvolume.cpp
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/llmath/llvolume.cpp')
-rw-r--r--linden/indra/llmath/llvolume.cpp4576
1 files changed, 4576 insertions, 0 deletions
diff --git a/linden/indra/llmath/llvolume.cpp b/linden/indra/llmath/llvolume.cpp
new file mode 100644
index 0000000..14d4cdf
--- /dev/null
+++ b/linden/indra/llmath/llvolume.cpp
@@ -0,0 +1,4576 @@
1/**
2 * @file llvolume.cpp
3 *
4 * Copyright (c) 2002-2007, Linden Research, Inc.
5 *
6 * The source code in this file ("Source Code") is provided by Linden Lab
7 * to you under the terms of the GNU General Public License, version 2.0
8 * ("GPL"), unless you have obtained a separate licensing agreement
9 * ("Other License"), formally executed by you and Linden Lab. Terms of
10 * the GPL can be found in doc/GPL-license.txt in this distribution, or
11 * online at http://secondlife.com/developers/opensource/gplv2
12 *
13 * There are special exceptions to the terms and conditions of the GPL as
14 * it is applied to this Source Code. View the full text of the exception
15 * in the file doc/FLOSS-exception.txt in this software distribution, or
16 * online at http://secondlife.com/developers/opensource/flossexception
17 *
18 * By copying, modifying or distributing this software, you acknowledge
19 * that you have read and understood your obligations described above,
20 * and agree to abide by those obligations.
21 *
22 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
23 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
24 * COMPLETENESS OR PERFORMANCE.
25 */
26
27#include "linden_common.h"
28#include "llmath.h"
29
30#include <set>
31
32#include "llerror.h"
33
34#include "llvolumemgr.h"
35#include "v2math.h"
36#include "v3math.h"
37#include "v4math.h"
38#include "m4math.h"
39#include "m3math.h"
40#include "lldarray.h"
41#include "llvolume.h"
42#include "llstl.h"
43
44#define DEBUG_SILHOUETTE_BINORMALS 0
45#define DEBUG_SILHOUETTE_NORMALS 0 // TomY: Use this to display normals using the silhouette
46#define DEBUG_SILHOUETTE_EDGE_MAP 0 // DaveP: Use this to display edge map using the silhouette
47
48const F32 CUT_MIN = 0.f;
49const F32 CUT_MAX = 1.f;
50const F32 MIN_CUT_DELTA = 0.02f;
51
52const F32 HOLLOW_MIN = 0.f;
53const F32 HOLLOW_MAX = 0.95f;
54const F32 HOLLOW_MAX_SQUARE = 0.7f;
55
56const F32 TWIST_MIN = -1.f;
57const F32 TWIST_MAX = 1.f;
58
59const F32 RATIO_MIN = 0.f;
60const F32 RATIO_MAX = 2.f; // Tom Y: Inverted sense here: 0 = top taper, 2 = bottom taper
61
62const F32 HOLE_X_MIN= 0.05f;
63const F32 HOLE_X_MAX= 1.0f;
64
65const F32 HOLE_Y_MIN= 0.05f;
66const F32 HOLE_Y_MAX= 0.5f;
67
68const F32 SHEAR_MIN = -0.5f;
69const F32 SHEAR_MAX = 0.5f;
70
71const F32 REV_MIN = 1.f;
72const F32 REV_MAX = 4.f;
73
74const F32 TAPER_MIN = -1.f;
75const F32 TAPER_MAX = 1.f;
76
77const F32 SKEW_MIN = -0.95f;
78const F32 SKEW_MAX = 0.95f;
79
80BOOL check_same_clock_dir( const LLVector3& pt1, const LLVector3& pt2, const LLVector3& pt3, const LLVector3& norm)
81{
82 LLVector3 test = (pt2-pt1)%(pt3-pt2);
83
84 //answer
85 if(test * norm < 0)
86 {
87 return FALSE;
88 }
89 else
90 {
91 return TRUE;
92 }
93}
94
95// intersect test between triangle pt1,pt2,pt3 and line from linept to linept+vect
96//returns TRUE if intersecting and moves linept to the point of intersection
97BOOL LLTriangleLineSegmentIntersect( const LLVector3& pt1, const LLVector3& pt2, const LLVector3& pt3, LLVector3& linept, const LLVector3& vect)
98{
99 LLVector3 V1 = pt2-pt1;
100 LLVector3 V2 = pt3-pt2;
101
102 LLVector3 norm = V1 % V2;
103
104 F32 dotprod = norm * vect;
105
106 if(dotprod < 0)
107 {
108 //Find point of intersect to triangle plane.
109 //find t to intersect point
110 F32 t = -(norm * (linept-pt1))/dotprod;
111
112 // if ds is neg line started past triangle so can't hit triangle.
113 if (t > 0)
114 {
115 return FALSE;
116 }
117
118 LLVector3 pt_int = linept + (vect*t);
119
120 if(check_same_clock_dir(pt1, pt2, pt_int, norm))
121 {
122 if(check_same_clock_dir(pt2, pt3, pt_int, norm))
123 {
124 if(check_same_clock_dir(pt3, pt1, pt_int, norm))
125 {
126 // answer in pt_int is insde triangle
127 linept.setVec(pt_int);
128 return TRUE;
129 }
130 }
131 }
132 }
133
134 return FALSE;
135}
136
137
138//-------------------------------------------------------------------
139// statics
140//-------------------------------------------------------------------
141
142
143//----------------------------------------------------
144
145LLProfile::Face* LLProfile::addCap(S16 faceID)
146{
147 Face *face = vector_append(mFaces, 1);
148
149 face->mIndex = 0;
150 face->mCount = mTotal;
151 face->mScaleU= 1.0f;
152 face->mCap = TRUE;
153 face->mFaceID = faceID;
154 return face;
155}
156
157LLProfile::Face* LLProfile::addFace(S32 i, S32 count, F32 scaleU, S16 faceID, BOOL flat)
158{
159 Face *face = vector_append(mFaces, 1);
160
161 face->mIndex = i;
162 face->mCount = count;
163 face->mScaleU= scaleU;
164
165 face->mFlat = flat;
166 face->mCap = FALSE;
167 face->mFaceID = faceID;
168 return face;
169}
170
171// What is the bevel parameter used for? - DJS 04/05/02
172// Bevel parameter is currently unused but presumedly would support
173// filleted and chamfered corners
174void LLProfile::genNGon(S32 sides, F32 offset, F32 bevel, F32 ang_scale, S32 split)
175{
176 // Generate an n-sided "circular" path.
177 // 0 is (1,0), and we go counter-clockwise along a circular path from there.
178 const F32 tableScale[] = { 1, 1, 1, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f };
179 F32 scale = 0.5f;
180 F32 t, t_step, t_first, t_fraction, ang, ang_step;
181 LLVector3 pt1,pt2;
182
183 mMaxX = 0.f;
184 mMinX = 0.f;
185
186 F32 begin = mParams.getBegin();
187 F32 end = mParams.getEnd();
188
189 t_step = 1.0f / sides;
190 ang_step = 2.0f*F_PI*t_step*ang_scale;
191
192 // Scale to have size "match" scale. Compensates to get object to generally fill bounding box.
193
194 S32 total_sides = llround(sides / ang_scale); // Total number of sides all around
195
196 if (total_sides < 8)
197 {
198 scale = tableScale[total_sides];
199 }
200
201 t_first = floor(begin * sides) / (F32)sides;
202
203 // pt1 is the first point on the fractional face.
204 // Starting t and ang values for the first face
205 t = t_first;
206 ang = 2.0f*F_PI*(t*ang_scale + offset);
207 pt1.setVec(cos(ang)*scale,sin(ang)*scale, t);
208
209 // Increment to the next point.
210 // pt2 is the end point on the fractional face
211 t += t_step;
212 ang += ang_step;
213 pt2.setVec(cos(ang)*scale,sin(ang)*scale,t);
214
215 t_fraction = (begin - t_first)*sides;
216
217 // Only use if it's not almost exactly on an edge.
218 if (t_fraction < 0.99f)
219 {
220 LLVector3 new_pt = lerp(pt1, pt2, t_fraction);
221 F32 pt_x = new_pt.mV[VX];
222 if (pt_x < mMinX)
223 {
224 mMinX = pt_x;
225 }
226 else if (pt_x > mMaxX)
227 {
228 mMaxX = pt_x;
229 }
230 mProfile.push_back(new_pt);
231 }
232
233 // There's lots of potential here for floating point error to generate unneeded extra points - DJS 04/05/02
234 while (t < end)
235 {
236 // Iterate through all the integer steps of t.
237 pt1.setVec(cos(ang)*scale,sin(ang)*scale,t);
238
239 F32 pt_x = pt1.mV[VX];
240 if (pt_x < mMinX)
241 {
242 mMinX = pt_x;
243 }
244 else if (pt_x > mMaxX)
245 {
246 mMaxX = pt_x;
247 }
248
249 if (mProfile.size() > 0) {
250 LLVector3 p = mProfile[mProfile.size()-1];
251 for (S32 i = 0; i < split && mProfile.size() > 0; i++) {
252 mProfile.push_back(p+(pt1-p) * 1.0f/(float)(split+1) * (float)(i+1));
253 }
254 }
255 mProfile.push_back(pt1);
256
257 t += t_step;
258 ang += ang_step;
259 }
260
261 t_fraction = (end - (t - t_step))*sides;
262
263 // pt1 is the first point on the fractional face
264 // pt2 is the end point on the fractional face
265 pt2.setVec(cos(ang)*scale,sin(ang)*scale,t);
266
267 // Find the fraction that we need to add to the end point.
268 t_fraction = (end - (t - t_step))*sides;
269 if (t_fraction > 0.01f)
270 {
271 LLVector3 new_pt = lerp(pt1, pt2, t_fraction);
272 F32 pt_x = new_pt.mV[VX];
273 if (pt_x < mMinX)
274 {
275 mMinX = pt_x;
276 }
277 else if (pt_x > mMaxX)
278 {
279 mMaxX = pt_x;
280 }
281
282 if (mProfile.size() > 0) {
283 LLVector3 p = mProfile[mProfile.size()-1];
284 for (S32 i = 0; i < split && mProfile.size() > 0; i++) {
285 mProfile.push_back(p+(new_pt-p) * 1.0f/(float)(split+1) * (float)(i+1));
286 }
287 }
288 mProfile.push_back(new_pt);
289 }
290
291 // If we're sliced, the profile is open.
292 if ((end - begin)*ang_scale < 0.99f)
293 {
294 if ((end - begin)*ang_scale > 0.5f)
295 {
296 mConcave = TRUE;
297 }
298 else
299 {
300 mConcave = FALSE;
301 }
302 mOpen = TRUE;
303 if (!isHollow())
304 {
305 // put center point if not hollow.
306 mProfile.push_back(LLVector3(0,0,0));
307 }
308 }
309 else
310 {
311 // The profile isn't open.
312 mOpen = FALSE;
313 mConcave = FALSE;
314 }
315
316 mTotal = mProfile.size();
317}
318
319void LLProfile::genNormals()
320{
321 S32 count = mProfile.size();
322
323 S32 outer_count;
324 if (mTotalOut)
325 {
326 outer_count = mTotalOut;
327 }
328 else
329 {
330 outer_count = mTotal / 2;
331 }
332
333 mEdgeNormals.resize(count * 2);
334 mEdgeCenters.resize(count * 2);
335 mNormals.resize(count);
336
337 LLVector2 pt0,pt1;
338
339 BOOL hollow;
340 hollow = isHollow();
341
342 S32 i0, i1, i2, i3, i4;
343
344 // Parametrically generate normal
345 for (i2 = 0; i2 < count; i2++)
346 {
347 mNormals[i2].mV[0] = mProfile[i2].mV[0];
348 mNormals[i2].mV[1] = mProfile[i2].mV[1];
349 if (hollow && (i2 >= outer_count))
350 {
351 mNormals[i2] *= -1.f;
352 }
353 if (mNormals[i2].magVec() < 0.001)
354 {
355 // Special case for point at center, get adjacent points.
356 i1 = (i2 - 1) >= 0 ? i2 - 1 : count - 1;
357 i0 = (i1 - 1) >= 0 ? i1 - 1 : count - 1;
358 i3 = (i2 + 1) < count ? i2 + 1 : 0;
359 i4 = (i3 + 1) < count ? i3 + 1 : 0;
360
361 pt0.setVec(mProfile[i1].mV[VX] + mProfile[i1].mV[VX] - mProfile[i0].mV[VX],
362 mProfile[i1].mV[VY] + mProfile[i1].mV[VY] - mProfile[i0].mV[VY]);
363 pt1.setVec(mProfile[i3].mV[VX] + mProfile[i3].mV[VX] - mProfile[i4].mV[VX],
364 mProfile[i3].mV[VY] + mProfile[i3].mV[VY] - mProfile[i4].mV[VY]);
365
366 mNormals[i2] = pt0 + pt1;
367 mNormals[i2] *= 0.5f;
368 }
369 mNormals[i2].normVec();
370 }
371
372 S32 num_normal_sets = isConcave() ? 2 : 1;
373 for (S32 normal_set = 0; normal_set < num_normal_sets; normal_set++)
374 {
375 S32 point_num;
376 for (point_num = 0; point_num < mTotal; point_num++)
377 {
378 LLVector3 point_1 = mProfile[point_num];
379 point_1.mV[VZ] = 0.f;
380
381 LLVector3 point_2;
382
383 if (isConcave() && normal_set == 0 && point_num == (mTotal - 1) / 2)
384 {
385 point_2 = mProfile[mTotal - 1];
386 }
387 else if (isConcave() && normal_set == 1 && point_num == mTotal - 1)
388 {
389 point_2 = mProfile[(mTotal - 1) / 2];
390 }
391 else
392 {
393 LLVector3 delta_pos;
394 S32 neighbor_point = (point_num + 1) % mTotal;
395 while(delta_pos.magVecSquared() < 0.01f * 0.01f)
396 {
397 point_2 = mProfile[neighbor_point];
398 delta_pos = point_2 - point_1;
399 neighbor_point = (neighbor_point + 1) % mTotal;
400 if (neighbor_point == point_num)
401 {
402 break;
403 }
404 }
405 }
406
407 point_2.mV[VZ] = 0.f;
408 LLVector3 face_normal = (point_2 - point_1) % LLVector3::z_axis;
409 face_normal.normVec();
410 mEdgeNormals[normal_set * count + point_num] = face_normal;
411 mEdgeCenters[normal_set * count + point_num] = lerp(point_1, point_2, 0.5f);
412 }
413 }
414}
415
416
417// Hollow is percent of the original bounding box, not of this particular
418// profile's geometry. Thus, a swept triangle needs lower hollow values than
419// a swept square.
420LLProfile::Face* LLProfile::addHole(BOOL flat, F32 sides, F32 offset, F32 box_hollow, F32 ang_scale, S32 split)
421{
422 // Note that addHole will NOT work for non-"circular" profiles, if we ever decide to use them.
423
424 // Total add has number of vertices on outside.
425 mTotalOut = mTotal;
426
427 // Why is the "bevel" parameter -1? DJS 04/05/02
428 genNGon(llfloor(sides),offset,-1, ang_scale, split);
429
430 Face *face = addFace(mTotalOut, mTotal-mTotalOut,0,LL_FACE_INNER_SIDE, flat);
431
432 LLVector3 pt[128];
433
434 for (S32 i=mTotalOut;i<mTotal;i++)
435 {
436 pt[i] = mProfile[i] * box_hollow;
437 }
438
439 S32 j=mTotal-1;
440 for (S32 i=mTotalOut;i<mTotal;i++)
441 {
442 mProfile[i] = pt[j--];
443 }
444
445 for (S32 i=0;i<(S32)mFaces.size();i++)
446 {
447 if (mFaces[i].mCap)
448 {
449 mFaces[i].mCount *= 2;
450 }
451 }
452
453 return face;
454}
455
456BOOL LLProfile::generate(BOOL path_open,F32 detail, S32 split)
457{
458 if (!mDirty)
459 {
460 return FALSE;
461 }
462 mDirty = FALSE;
463
464 if (detail < MIN_LOD)
465 {
466 llinfos << "Generating profile with LOD < MIN_LOD. CLAMPING" << llendl;
467 detail = MIN_LOD;
468 }
469
470 mProfile.clear();
471 mFaces.clear();
472
473 // Generate the face data
474 S32 i;
475 F32 begin = mParams.getBegin();
476 F32 end = mParams.getEnd();
477 F32 hollow = mParams.getHollow();
478
479 // Quick validation to eliminate some server crashes.
480 if (begin > end - 0.01f)
481 {
482 llwarns << "LLProfile::generate() assertion failed (begin >= end)" << llendl;
483 return FALSE;
484 }
485
486 S32 face_num = 0;
487
488 switch (mParams.getCurveType() & LL_PCODE_PROFILE_MASK)
489 {
490 case LL_PCODE_PROFILE_SQUARE:
491 {
492 genNGon(4,-0.375, 0, 1, split);
493 if (path_open)
494 {
495 addCap (LL_FACE_PATH_BEGIN);
496 }
497
498 for (i = llfloor(begin * 4.f); i < llfloor(end * 4.f + .999f); i++)
499 {
500 addFace((face_num++) * (split +1), split+2, 1, LL_FACE_OUTER_SIDE_0 << i, TRUE);
501 }
502
503 for (i = 0; i <(S32) mProfile.size(); i++)
504 {
505 // Scale by 4 to generate proper tex coords.
506 mProfile[i].mV[2] *= 4.f;
507 }
508
509 if (hollow)
510 {
511 switch (mParams.getCurveType() & LL_PCODE_HOLE_MASK)
512 {
513 case LL_PCODE_HOLE_TRIANGLE:
514 // This offset is not correct, but we can't change it now... DK 11/17/04
515 addHole(TRUE, 3, -0.375f, hollow, 1.f, split);
516 break;
517 case LL_PCODE_HOLE_CIRCLE:
518 // TODO: Compute actual detail levels for cubes
519 addHole(FALSE, MIN_DETAIL_FACES * detail, -0.375f, hollow, 1.f);
520 break;
521 case LL_PCODE_HOLE_SAME:
522 case LL_PCODE_HOLE_SQUARE:
523 default:
524 addHole(TRUE, 4, -0.375f, hollow, 1.f, split);
525 break;
526 }
527 }
528
529 if (path_open) {
530 mFaces[0].mCount = mTotal;
531 }
532 }
533 break;
534 case LL_PCODE_PROFILE_ISOTRI:
535 case LL_PCODE_PROFILE_RIGHTTRI:
536 case LL_PCODE_PROFILE_EQUALTRI:
537 {
538 genNGon(3,0, 0, 1, split);
539 for (i = 0; i <(S32) mProfile.size(); i++)
540 {
541 // Scale by 3 to generate proper tex coords.
542 mProfile[i].mV[2] *= 3.f;
543 }
544
545 if (path_open)
546 {
547 addCap(LL_FACE_PATH_BEGIN);
548 }
549
550 for (i = llfloor(begin * 3.f); i < llfloor(end * 3.f + .999f); i++)
551 {
552 addFace((face_num++) * (split +1), split+2, 1, LL_FACE_OUTER_SIDE_0 << i, TRUE);
553 }
554 if (hollow)
555 {
556 // Swept triangles need smaller hollowness values,
557 // because the triangle doesn't fill the bounding box.
558 F32 triangle_hollow = hollow / 2.f;
559
560 switch (mParams.getCurveType() & LL_PCODE_HOLE_MASK)
561 {
562 case LL_PCODE_HOLE_CIRCLE:
563 // TODO: Actually generate level of detail for triangles
564 addHole(FALSE, MIN_DETAIL_FACES * detail, 0, triangle_hollow, 1.f);
565 break;
566 case LL_PCODE_HOLE_SQUARE:
567 addHole(TRUE, 4, 0, triangle_hollow, 1.f, split);
568 break;
569 case LL_PCODE_HOLE_SAME:
570 case LL_PCODE_HOLE_TRIANGLE:
571 default:
572 addHole(TRUE, 3, 0, triangle_hollow, 1.f, split);
573 break;
574 }
575 }
576 }
577 break;
578 case LL_PCODE_PROFILE_CIRCLE:
579 {
580 // If this has a square hollow, we should adjust the
581 // number of faces a bit so that the geometry lines up.
582 U8 hole_type=0;
583 F32 circle_detail = MIN_DETAIL_FACES * detail;
584 if (hollow)
585 {
586 hole_type = mParams.getCurveType() & LL_PCODE_HOLE_MASK;
587 if (hole_type == LL_PCODE_HOLE_SQUARE)
588 {
589 // Snap to the next multiple of four sides,
590 // so that corners line up.
591 circle_detail = llceil(circle_detail / 4.0f) * 4.0f;
592 }
593 }
594
595 //llinfos << "(CIRCLE) detail: " << detail << "; genNGon("
596 // << llfloor(circle_detail) << ")" << llendl;
597 genNGon(llfloor(circle_detail));
598 if (path_open)
599 {
600 addCap (LL_FACE_PATH_BEGIN);
601 }
602
603 if (mOpen && !hollow)
604 {
605 addFace(0,mTotal-1,0,LL_FACE_OUTER_SIDE_0, FALSE);
606 }
607 else
608 {
609 addFace(0,mTotal,0,LL_FACE_OUTER_SIDE_0, FALSE);
610 }
611
612 if (hollow)
613 {
614 switch (hole_type)
615 {
616 case LL_PCODE_HOLE_SQUARE:
617 addHole(TRUE, 4, 0, hollow, 1.f, split);
618 break;
619 case LL_PCODE_HOLE_TRIANGLE:
620 addHole(TRUE, 3, 0, hollow, 1.f, split);
621 break;
622 case LL_PCODE_HOLE_CIRCLE:
623 case LL_PCODE_HOLE_SAME:
624 default:
625 addHole(FALSE, circle_detail, 0, hollow, 1.f);
626 break;
627 }
628 }
629 }
630 break;
631 case LL_PCODE_PROFILE_CIRCLE_HALF:
632 {
633 // If this has a square hollow, we should adjust the
634 // number of faces a bit so that the geometry lines up.
635 U8 hole_type=0;
636 // Number of faces is cut in half because it's only a half-circle.
637 F32 circle_detail = MIN_DETAIL_FACES * detail * 0.5f;
638 if (hollow)
639 {
640 hole_type = mParams.getCurveType() & LL_PCODE_HOLE_MASK;
641 if (hole_type == LL_PCODE_HOLE_SQUARE)
642 {
643 // Snap to the next multiple of four sides (div 2),
644 // so that corners line up.
645 circle_detail = llceil(circle_detail / 2.0f) * 2.0f;
646 }
647 }
648 genNGon(llfloor(circle_detail), 0.5f, 0.f, 0.5f);
649 if (path_open)
650 {
651 addCap(LL_FACE_PATH_BEGIN);
652 }
653 if (mOpen && !mParams.getHollow())
654 {
655 addFace(0,mTotal-1,0,LL_FACE_OUTER_SIDE_0, FALSE);
656 }
657 else
658 {
659 addFace(0,mTotal,0,LL_FACE_OUTER_SIDE_0, FALSE);
660 }
661
662 if (hollow)
663 {
664 switch (hole_type)
665 {
666 case LL_PCODE_HOLE_SQUARE:
667 addHole(TRUE, 2, 0.5f, hollow, 0.5f, split);
668 break;
669 case LL_PCODE_HOLE_TRIANGLE:
670 addHole(TRUE, 3, 0.5f, hollow, 0.5f, split);
671 break;
672 case LL_PCODE_HOLE_CIRCLE:
673 case LL_PCODE_HOLE_SAME:
674 default:
675 addHole(FALSE, circle_detail, 0.5f, hollow, 0.5f);
676 break;
677 }
678 }
679
680 // Special case for openness of sphere
681 if ((mParams.getEnd() - mParams.getBegin()) < 1.f)
682 {
683 mOpen = TRUE;
684 }
685 else if (!hollow)
686 {
687 mOpen = FALSE;
688 mProfile.push_back(mProfile[0]);
689 mTotal++;
690 }
691 }
692 break;
693 default:
694 llerrs << "Unknown profile: getCurveType()=" << mParams.getCurveType() << llendl;
695 break;
696 };
697
698 if (path_open)
699 {
700 addCap(LL_FACE_PATH_END); // bottom
701 }
702
703 if ( mOpen) // interior edge caps
704 {
705 addFace(mTotal-1, 2,0.5,LL_FACE_PROFILE_BEGIN, TRUE);
706
707 if (hollow)
708 {
709 addFace(mTotalOut-1, 2,0.5,LL_FACE_PROFILE_END, TRUE);
710 }
711 else
712 {
713 addFace(mTotal-2, 2,0.5,LL_FACE_PROFILE_END, TRUE);
714 }
715 }
716
717 //genNormals();
718
719 return TRUE;
720}
721
722
723
724BOOL LLProfileParams::importFile(FILE *fp)
725{
726 const S32 BUFSIZE = 16384;
727 char buffer[BUFSIZE];
728 char keyword[256];
729 char valuestr[256];
730 keyword[0] = 0;
731 valuestr[0] = 0;
732 F32 tempF32;
733 U32 tempU32;
734
735 while (!feof(fp))
736 {
737 fgets(buffer, BUFSIZE, fp);
738 sscanf(buffer, " %s %s", keyword, valuestr);
739 if (!keyword)
740 {
741 continue;
742 }
743 if (!strcmp("{", keyword))
744 {
745 continue;
746 }
747 if (!strcmp("}",keyword))
748 {
749 break;
750 }
751 else if (!strcmp("curve", keyword))
752 {
753 sscanf(valuestr,"%d",&tempU32);
754 setCurveType((U8) tempU32);
755 }
756 else if (!strcmp("begin",keyword))
757 {
758 sscanf(valuestr,"%g",&tempF32);
759 setBegin(tempF32);
760 }
761 else if (!strcmp("end",keyword))
762 {
763 sscanf(valuestr,"%g",&tempF32);
764 setEnd(tempF32);
765 }
766 else if (!strcmp("hollow",keyword))
767 {
768 sscanf(valuestr,"%g",&tempF32);
769 setHollow(tempF32);
770 }
771 else
772 {
773 llwarns << "unknown keyword " << keyword << " in profile import" << llendl;
774 }
775 }
776
777 return TRUE;
778}
779
780
781BOOL LLProfileParams::exportFile(FILE *fp) const
782{
783 fprintf(fp,"\t\tprofile 0\n");
784 fprintf(fp,"\t\t{\n");
785 fprintf(fp,"\t\t\tcurve\t%d\n", getCurveType());
786 fprintf(fp,"\t\t\tbegin\t%g\n", getBegin());
787 fprintf(fp,"\t\t\tend\t%g\n", getEnd());
788 fprintf(fp,"\t\t\thollow\t%g\n", getHollow());
789 fprintf(fp, "\t\t}\n");
790 return TRUE;
791}
792
793
794BOOL LLProfileParams::importLegacyStream(std::istream& input_stream)
795{
796 const S32 BUFSIZE = 16384;
797 char buffer[BUFSIZE];
798 char keyword[256];
799 char valuestr[256];
800 keyword[0] = 0;
801 valuestr[0] = 0;
802 F32 tempF32;
803 U32 tempU32;
804
805 while (input_stream.good())
806 {
807 input_stream.getline(buffer, BUFSIZE);
808 sscanf(buffer, " %s %s", keyword, valuestr);
809 if (!keyword)
810 {
811 continue;
812 }
813 if (!strcmp("{", keyword))
814 {
815 continue;
816 }
817 if (!strcmp("}",keyword))
818 {
819 break;
820 }
821 else if (!strcmp("curve", keyword))
822 {
823 sscanf(valuestr,"%d",&tempU32);
824 setCurveType((U8) tempU32);
825 }
826 else if (!strcmp("begin",keyword))
827 {
828 sscanf(valuestr,"%g",&tempF32);
829 setBegin(tempF32);
830 }
831 else if (!strcmp("end",keyword))
832 {
833 sscanf(valuestr,"%g",&tempF32);
834 setEnd(tempF32);
835 }
836 else if (!strcmp("hollow",keyword))
837 {
838 sscanf(valuestr,"%g",&tempF32);
839 setHollow(tempF32);
840 }
841 else
842 {
843 llwarns << "unknown keyword " << keyword << " in profile import" << llendl;
844 }
845 }
846
847 return TRUE;
848}
849
850
851BOOL LLProfileParams::exportLegacyStream(std::ostream& output_stream) const
852{
853 output_stream <<"\t\tprofile 0\n";
854 output_stream <<"\t\t{\n";
855 output_stream <<"\t\t\tcurve\t" << (S32) getCurveType() << "\n";
856 output_stream <<"\t\t\tbegin\t" << getBegin() << "\n";
857 output_stream <<"\t\t\tend\t" << getEnd() << "\n";
858 output_stream <<"\t\t\thollow\t" << getHollow() << "\n";
859 output_stream << "\t\t}\n";
860 return TRUE;
861}
862
863LLSD LLProfileParams::asLLSD() const
864{
865 LLSD sd;
866
867 sd["curve"] = getCurveType();
868 sd["begin"] = getBegin();
869 sd["end"] = getEnd();
870 sd["hollow"] = getHollow();
871 return sd;
872}
873
874bool LLProfileParams::fromLLSD(LLSD& sd)
875{
876 setCurveType(sd["curve"].asInteger());
877 setBegin((F32)sd["begin"].asReal());
878 setEnd((F32)sd["end"].asReal());
879 setHollow((F32)sd["hollow"].asReal());
880 return true;
881}
882
883void LLProfileParams::copyParams(const LLProfileParams &params)
884{
885 setCurveType(params.getCurveType());
886 setBegin(params.getBegin());
887 setEnd(params.getEnd());
888 setHollow(params.getHollow());
889}
890
891
892LLPath::~LLPath()
893{
894}
895
896void LLPath::genNGon(S32 sides, F32 startOff, F32 end_scale, F32 twist_scale)
897{
898 // Generates a circular path, starting at (1, 0, 0), counterclockwise along the xz plane.
899 const F32 tableScale[] = { 1, 1, 1, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f };
900
901 F32 revolutions = mParams.getRevolutions();
902 F32 skew = mParams.getSkew();
903 F32 skew_mag = fabs(skew);
904 F32 hole_x = mParams.getScaleX() * (1.0f - skew_mag);
905 F32 hole_y = mParams.getScaleY();
906
907 // Calculate taper begin/end for x,y (Negative means taper the beginning)
908 F32 taper_x_begin = 1.0f;
909 F32 taper_x_end = 1.0f - mParams.getTaperX();
910 F32 taper_y_begin = 1.0f;
911 F32 taper_y_end = 1.0f - mParams.getTaperY();
912
913 if ( taper_x_end > 1.0f )
914 {
915 // Flip tapering.
916 taper_x_begin = 2.0f - taper_x_end;
917 taper_x_end = 1.0f;
918 }
919 if ( taper_y_end > 1.0f )
920 {
921 // Flip tapering.
922 taper_y_begin = 2.0f - taper_y_end;
923 taper_y_end = 1.0f;
924 }
925
926 // For spheres, the radius is usually zero.
927 F32 radius_start = 0.5f;
928 if (sides < 8)
929 {
930 radius_start = tableScale[sides];
931 }
932
933 // Scale the radius to take the hole size into account.
934 radius_start *= 1.0f - hole_y;
935
936 // Now check the radius offset to calculate the start,end radius. (Negative means
937 // decrease the start radius instead).
938 F32 radius_end = radius_start;
939 F32 radius_offset = mParams.getRadiusOffset();
940 if (radius_offset < 0.f)
941 {
942 radius_start *= 1.f + radius_offset;
943 }
944 else
945 {
946 radius_end *= 1.f - radius_offset;
947 }
948
949 // Is the path NOT a closed loop?
950 mOpen = ( (mParams.getEnd()*end_scale - mParams.getBegin() < 1.0f) ||
951 (skew_mag > 0.001f) ||
952 (fabs(taper_x_end - taper_x_begin) > 0.001f) ||
953 (fabs(taper_y_end - taper_y_begin) > 0.001f) ||
954 (fabs(radius_end - radius_start) > 0.001f) );
955
956 F32 ang, c, s;
957 LLQuaternion twist, qang;
958 PathPt *pt;
959 LLVector3 path_axis (1.f, 0.f, 0.f);
960 //LLVector3 twist_axis(0.f, 0.f, 1.f);
961 F32 twist_begin = mParams.getTwistBegin() * twist_scale;
962 F32 twist_end = mParams.getTwist() * twist_scale;
963
964 // We run through this once before the main loop, to make sure
965 // the path begins at the correct cut.
966 F32 step= 1.0f / sides;
967 F32 t = mParams.getBegin();
968 pt = vector_append(mPath, 1);
969 ang = 2.0f*F_PI*revolutions * t;
970 s = sin(ang)*lerp(radius_start, radius_end, t);
971 c = cos(ang)*lerp(radius_start, radius_end, t);
972
973
974 pt->mPos.setVec(0 + lerp(0,mParams.getShear().mV[0],s)
975 + lerp(-skew ,skew, t) * 0.5f,
976 c + lerp(0,mParams.getShear().mV[1],s),
977 s);
978 pt->mScale.mV[VX] = hole_x * lerp(taper_x_begin, taper_x_end, t);
979 pt->mScale.mV[VY] = hole_y * lerp(taper_y_begin, taper_y_end, t);
980 pt->mTexT = t;
981
982 // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
983 twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
984 // Rotate the point around the circle's center.
985 qang.setQuat (ang,path_axis);
986 pt->mRot = twist * qang;
987
988 t+=step;
989
990 // Snap to a quantized parameter, so that cut does not
991 // affect most sample points.
992 t = ((S32)(t * sides)) / (F32)sides;
993
994 // Run through the non-cut dependent points.
995 while (t < mParams.getEnd())
996 {
997 pt = vector_append(mPath, 1);
998
999 ang = 2.0f*F_PI*revolutions * t;
1000 c = cos(ang)*lerp(radius_start, radius_end, t);
1001 s = sin(ang)*lerp(radius_start, radius_end, t);
1002
1003 pt->mPos.setVec(0 + lerp(0,mParams.getShear().mV[0],s)
1004 + lerp(-skew ,skew, t) * 0.5f,
1005 c + lerp(0,mParams.getShear().mV[1],s),
1006 s);
1007
1008 pt->mScale.mV[VX] = hole_x * lerp(taper_x_begin, taper_x_end, t);
1009 pt->mScale.mV[VY] = hole_y * lerp(taper_y_begin, taper_y_end, t);
1010 pt->mTexT = t;
1011
1012 // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
1013 twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
1014 // Rotate the point around the circle's center.
1015 qang.setQuat (ang,path_axis);
1016 pt->mRot = twist * qang;
1017
1018 t+=step;
1019 }
1020
1021 // Make one final pass for the end cut.
1022 t = mParams.getEnd();
1023 pt = vector_append(mPath, 1);
1024 ang = 2.0f*F_PI*revolutions * t;
1025 c = cos(ang)*lerp(radius_start, radius_end, t);
1026 s = sin(ang)*lerp(radius_start, radius_end, t);
1027
1028 pt->mPos.setVec(0 + lerp(0,mParams.getShear().mV[0],s)
1029 + lerp(-skew ,skew, t) * 0.5f,
1030 c + lerp(0,mParams.getShear().mV[1],s),
1031 s);
1032 pt->mScale.mV[VX] = hole_x * lerp(taper_x_begin, taper_x_end, t);
1033 pt->mScale.mV[VY] = hole_y * lerp(taper_y_begin, taper_y_end, t);
1034 pt->mTexT = t;
1035
1036 // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
1037 twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
1038 // Rotate the point around the circle's center.
1039 qang.setQuat (ang,path_axis);
1040 pt->mRot = twist * qang;
1041
1042 mTotal = mPath.size();
1043}
1044
1045const LLVector2 LLPathParams::getBeginScale() const
1046{
1047 LLVector2 begin_scale(1.f, 1.f);
1048 if (getScaleX() > 1)
1049 {
1050 begin_scale.mV[0] = 2-getScaleX();
1051 }
1052 if (getScaleY() > 1)
1053 {
1054 begin_scale.mV[1] = 2-getScaleY();
1055 }
1056 return begin_scale;
1057}
1058
1059const LLVector2 LLPathParams::getEndScale() const
1060{
1061 LLVector2 end_scale(1.f, 1.f);
1062 if (getScaleX() < 1)
1063 {
1064 end_scale.mV[0] = getScaleX();
1065 }
1066 if (getScaleY() < 1)
1067 {
1068 end_scale.mV[1] = getScaleY();
1069 }
1070 return end_scale;
1071}
1072
1073BOOL LLPath::generate(F32 detail, S32 split)
1074{
1075 if (!mDirty)
1076 {
1077 return FALSE;
1078 }
1079
1080 if (detail < MIN_LOD)
1081 {
1082 llinfos << "Generating path with LOD < MIN! Clamping to 1" << llendl;
1083 detail = MIN_LOD;
1084 }
1085
1086 mDirty = FALSE;
1087 S32 np = 2; // hardcode for line
1088
1089 mPath.clear();
1090 mOpen = TRUE;
1091
1092 // Is this 0xf0 mask really necessary? DK 03/02/05
1093 switch (mParams.getCurveType() & 0xf0)
1094 {
1095 default:
1096 case LL_PCODE_PATH_LINE:
1097 {
1098 // Take the begin/end twist into account for detail.
1099 np = llfloor(fabs(mParams.getTwistBegin() - mParams.getTwist()) * 3.5f * (detail-0.5f)) + 2;
1100 if (np < split+2)
1101 {
1102 np = split+2;
1103 }
1104
1105 mStep = 1.0f / (np-1);
1106
1107 mPath.resize(np);
1108
1109 LLVector2 start_scale = mParams.getBeginScale();
1110 LLVector2 end_scale = mParams.getEndScale();
1111
1112 for (S32 i=0;i<np;i++)
1113 {
1114 F32 t = lerp(mParams.getBegin(),mParams.getEnd(),(F32)i * mStep);
1115 mPath[i].mPos.setVec(lerp(0,mParams.getShear().mV[0],t),
1116 lerp(0,mParams.getShear().mV[1],t),
1117 t - 0.5f);
1118 mPath[i].mRot.setQuat(lerp(F_PI * mParams.getTwistBegin(),F_PI * mParams.getTwist(),t),0,0,1);
1119 mPath[i].mScale.mV[0] = lerp(start_scale.mV[0],end_scale.mV[0],t);
1120 mPath[i].mScale.mV[1] = lerp(start_scale.mV[1],end_scale.mV[1],t);
1121 mPath[i].mTexT = t;
1122 }
1123 }
1124 break;
1125
1126 case LL_PCODE_PATH_CIRCLE:
1127 {
1128 // Increase the detail as the revolutions and twist increase.
1129 F32 twist_mag = fabs(mParams.getTwistBegin() - mParams.getTwist());
1130 genNGon(llfloor(llfloor((MIN_DETAIL_FACES * detail + twist_mag * 3.5f * (detail-0.5f))) * mParams.getRevolutions()));
1131 }
1132 break;
1133
1134 case LL_PCODE_PATH_CIRCLE2:
1135 {
1136 if (mParams.getEnd() - mParams.getBegin() >= 0.99f &&
1137 mParams.getScaleX() >= .99f)
1138 {
1139 mOpen = FALSE;
1140 }
1141
1142 //genNGon(llfloor(MIN_DETAIL_FACES * detail), 4.f, 0.f);
1143 genNGon(llfloor(MIN_DETAIL_FACES * detail));
1144
1145 F32 t = 0.f;
1146 F32 tStep = 1.0f / mPath.size();
1147
1148 F32 toggle = 0.5f;
1149 for (S32 i=0;i<(S32)mPath.size();i++)
1150 {
1151 mPath[i].mPos.mV[0] = toggle;
1152 if (toggle == 0.5f)
1153 toggle = -0.5f;
1154 else
1155 toggle = 0.5f;
1156 t += tStep;
1157 }
1158 }
1159
1160 break;
1161
1162 case LL_PCODE_PATH_TEST:
1163
1164 np = 5;
1165 mStep = 1.0f / (np-1);
1166
1167 mPath.resize(np);
1168
1169 for (S32 i=0;i<np;i++)
1170 {
1171 F32 t = (F32)i * mStep;
1172 mPath[i].mPos.setVec(0,
1173 lerp(0, -sin(F_PI*mParams.getTwist()*t)*0.5f,t),
1174 lerp(-0.5, cos(F_PI*mParams.getTwist()*t)*0.5f,t));
1175 mPath[i].mScale.mV[0] = lerp(1,mParams.getScale().mV[0],t);
1176 mPath[i].mScale.mV[1] = lerp(1,mParams.getScale().mV[1],t);
1177 mPath[i].mTexT = t;
1178 mPath[i].mRot.setQuat(F_PI * mParams.getTwist() * t,1,0,0);
1179 }
1180
1181 break;
1182 };
1183
1184 if (mParams.getTwist() != mParams.getTwistBegin()) mOpen = TRUE;
1185
1186 //if ((int(fabsf(mParams.getTwist() - mParams.getTwistBegin())*100))%100 != 0) {
1187 // mOpen = TRUE;
1188 //}
1189
1190 return TRUE;
1191}
1192
1193BOOL LLDynamicPath::generate(F32 detail, S32 split)
1194{
1195 mOpen = TRUE; // Draw end caps
1196 if (getPathLength() == 0)
1197 {
1198 // Path hasn't been generated yet.
1199 // Some algorithms later assume at least TWO path points.
1200 resizePath(2);
1201 for (U32 i = 0; i < 2; i++)
1202 {
1203 mPath[i].mPos.setVec(0, 0, 0);
1204 mPath[i].mRot.setQuat(0, 0, 0);
1205 mPath[i].mScale.setVec(1, 1);
1206 mPath[i].mTexT = 0;
1207 }
1208 }
1209
1210 return TRUE;
1211}
1212
1213
1214BOOL LLPathParams::importFile(FILE *fp)
1215{
1216 const S32 BUFSIZE = 16384;
1217 char buffer[BUFSIZE];
1218 char keyword[256];
1219 char valuestr[256];
1220 keyword[0] = 0;
1221 valuestr[0] = 0;
1222
1223 F32 tempF32;
1224 F32 x, y;
1225 U32 tempU32;
1226
1227 while (!feof(fp))
1228 {
1229 fgets(buffer, BUFSIZE, fp);
1230 sscanf(buffer, " %s %s", keyword, valuestr);
1231 if (!keyword)
1232 {
1233 continue;
1234 }
1235 if (!strcmp("{", keyword))
1236 {
1237 continue;
1238 }
1239 if (!strcmp("}",keyword))
1240 {
1241 break;
1242 }
1243 else if (!strcmp("curve", keyword))
1244 {
1245 sscanf(valuestr,"%d",&tempU32);
1246 setCurveType((U8) tempU32);
1247 }
1248 else if (!strcmp("begin",keyword))
1249 {
1250 sscanf(valuestr,"%g",&tempF32);
1251 setBegin(tempF32);
1252 }
1253 else if (!strcmp("end",keyword))
1254 {
1255 sscanf(valuestr,"%g",&tempF32);
1256 setEnd(tempF32);
1257 }
1258 else if (!strcmp("scale",keyword))
1259 {
1260 // Legacy for one dimensional scale per path
1261 sscanf(valuestr,"%g",&tempF32);
1262 setScale(tempF32, tempF32);
1263 }
1264 else if (!strcmp("scale_x", keyword))
1265 {
1266 sscanf(valuestr, "%g", &x);
1267 setScaleX(x);
1268 }
1269 else if (!strcmp("scale_y", keyword))
1270 {
1271 sscanf(valuestr, "%g", &y);
1272 setScaleY(y);
1273 }
1274 else if (!strcmp("shear_x", keyword))
1275 {
1276 sscanf(valuestr, "%g", &x);
1277 setShearX(x);
1278 }
1279 else if (!strcmp("shear_y", keyword))
1280 {
1281 sscanf(valuestr, "%g", &y);
1282 setShearY(y);
1283 }
1284 else if (!strcmp("twist",keyword))
1285 {
1286 sscanf(valuestr,"%g",&tempF32);
1287 setTwist(tempF32);
1288 }
1289 else if (!strcmp("twist_begin", keyword))
1290 {
1291 sscanf(valuestr, "%g", &y);
1292 setTwistBegin(y);
1293 }
1294 else if (!strcmp("radius_offset", keyword))
1295 {
1296 sscanf(valuestr, "%g", &y);
1297 setRadiusOffset(y);
1298 }
1299 else if (!strcmp("taper_x", keyword))
1300 {
1301 sscanf(valuestr, "%g", &y);
1302 setTaperX(y);
1303 }
1304 else if (!strcmp("taper_y", keyword))
1305 {
1306 sscanf(valuestr, "%g", &y);
1307 setTaperY(y);
1308 }
1309 else if (!strcmp("revolutions", keyword))
1310 {
1311 sscanf(valuestr, "%g", &y);
1312 setRevolutions(y);
1313 }
1314 else if (!strcmp("skew", keyword))
1315 {
1316 sscanf(valuestr, "%g", &y);
1317 setSkew(y);
1318 }
1319 else
1320 {
1321 llwarns << "unknown keyword " << " in path import" << llendl;
1322 }
1323 }
1324 return TRUE;
1325}
1326
1327
1328BOOL LLPathParams::exportFile(FILE *fp) const
1329{
1330 fprintf(fp, "\t\tpath 0\n");
1331 fprintf(fp, "\t\t{\n");
1332 fprintf(fp, "\t\t\tcurve\t%d\n", getCurveType());
1333 fprintf(fp, "\t\t\tbegin\t%g\n", getBegin());
1334 fprintf(fp, "\t\t\tend\t%g\n", getEnd());
1335 fprintf(fp, "\t\t\tscale_x\t%g\n", getScaleX() );
1336 fprintf(fp, "\t\t\tscale_y\t%g\n", getScaleY() );
1337 fprintf(fp, "\t\t\tshear_x\t%g\n", getShearX() );
1338 fprintf(fp, "\t\t\tshear_y\t%g\n", getShearY() );
1339 fprintf(fp,"\t\t\ttwist\t%g\n", getTwist());
1340
1341 fprintf(fp,"\t\t\ttwist_begin\t%g\n", getTwistBegin());
1342 fprintf(fp,"\t\t\tradius_offset\t%g\n", getRadiusOffset());
1343 fprintf(fp,"\t\t\ttaper_x\t%g\n", getTaperX());
1344 fprintf(fp,"\t\t\ttaper_y\t%g\n", getTaperY());
1345 fprintf(fp,"\t\t\trevolutions\t%g\n", getRevolutions());
1346 fprintf(fp,"\t\t\tskew\t%g\n", getSkew());
1347
1348 fprintf(fp, "\t\t}\n");
1349 return TRUE;
1350}
1351
1352
1353BOOL LLPathParams::importLegacyStream(std::istream& input_stream)
1354{
1355 const S32 BUFSIZE = 16384;
1356 char buffer[BUFSIZE];
1357 char keyword[256];
1358 char valuestr[256];
1359 keyword[0] = 0;
1360 valuestr[0] = 0;
1361
1362 F32 tempF32;
1363 F32 x, y;
1364 U32 tempU32;
1365
1366 while (input_stream.good())
1367 {
1368 input_stream.getline(buffer, BUFSIZE);
1369 sscanf(buffer, " %s %s", keyword, valuestr);
1370 if (!keyword)
1371 {
1372 continue;
1373 }
1374 if (!strcmp("{", keyword))
1375 {
1376 continue;
1377 }
1378 if (!strcmp("}",keyword))
1379 {
1380 break;
1381 }
1382 else if (!strcmp("curve", keyword))
1383 {
1384 sscanf(valuestr,"%d",&tempU32);
1385 setCurveType((U8) tempU32);
1386 }
1387 else if (!strcmp("begin",keyword))
1388 {
1389 sscanf(valuestr,"%g",&tempF32);
1390 setBegin(tempF32);
1391 }
1392 else if (!strcmp("end",keyword))
1393 {
1394 sscanf(valuestr,"%g",&tempF32);
1395 setEnd(tempF32);
1396 }
1397 else if (!strcmp("scale",keyword))
1398 {
1399 // Legacy for one dimensional scale per path
1400 sscanf(valuestr,"%g",&tempF32);
1401 setScale(tempF32, tempF32);
1402 }
1403 else if (!strcmp("scale_x", keyword))
1404 {
1405 sscanf(valuestr, "%g", &x);
1406 setScaleX(x);
1407 }
1408 else if (!strcmp("scale_y", keyword))
1409 {
1410 sscanf(valuestr, "%g", &y);
1411 setScaleY(y);
1412 }
1413 else if (!strcmp("shear_x", keyword))
1414 {
1415 sscanf(valuestr, "%g", &x);
1416 setShearX(x);
1417 }
1418 else if (!strcmp("shear_y", keyword))
1419 {
1420 sscanf(valuestr, "%g", &y);
1421 setShearY(y);
1422 }
1423 else if (!strcmp("twist",keyword))
1424 {
1425 sscanf(valuestr,"%g",&tempF32);
1426 setTwist(tempF32);
1427 }
1428 else if (!strcmp("twist_begin", keyword))
1429 {
1430 sscanf(valuestr, "%g", &y);
1431 setTwistBegin(y);
1432 }
1433 else if (!strcmp("radius_offset", keyword))
1434 {
1435 sscanf(valuestr, "%g", &y);
1436 setRadiusOffset(y);
1437 }
1438 else if (!strcmp("taper_x", keyword))
1439 {
1440 sscanf(valuestr, "%g", &y);
1441 setTaperX(y);
1442 }
1443 else if (!strcmp("taper_y", keyword))
1444 {
1445 sscanf(valuestr, "%g", &y);
1446 setTaperY(y);
1447 }
1448 else if (!strcmp("revolutions", keyword))
1449 {
1450 sscanf(valuestr, "%g", &y);
1451 setRevolutions(y);
1452 }
1453 else if (!strcmp("skew", keyword))
1454 {
1455 sscanf(valuestr, "%g", &y);
1456 setSkew(y);
1457 }
1458 else
1459 {
1460 llwarns << "unknown keyword " << " in path import" << llendl;
1461 }
1462 }
1463 return TRUE;
1464}
1465
1466
1467BOOL LLPathParams::exportLegacyStream(std::ostream& output_stream) const
1468{
1469 output_stream << "\t\tpath 0\n";
1470 output_stream << "\t\t{\n";
1471 output_stream << "\t\t\tcurve\t" << (S32) getCurveType() << "\n";
1472 output_stream << "\t\t\tbegin\t" << getBegin() << "\n";
1473 output_stream << "\t\t\tend\t" << getEnd() << "\n";
1474 output_stream << "\t\t\tscale_x\t" << getScaleX() << "\n";
1475 output_stream << "\t\t\tscale_y\t" << getScaleY() << "\n";
1476 output_stream << "\t\t\tshear_x\t" << getShearX() << "\n";
1477 output_stream << "\t\t\tshear_y\t" << getShearY() << "\n";
1478 output_stream <<"\t\t\ttwist\t" << getTwist() << "\n";
1479
1480 output_stream <<"\t\t\ttwist_begin\t" << getTwistBegin() << "\n";
1481 output_stream <<"\t\t\tradius_offset\t" << getRadiusOffset() << "\n";
1482 output_stream <<"\t\t\ttaper_x\t" << getTaperX() << "\n";
1483 output_stream <<"\t\t\ttaper_y\t" << getTaperY() << "\n";
1484 output_stream <<"\t\t\trevolutions\t" << getRevolutions() << "\n";
1485 output_stream <<"\t\t\tskew\t" << getSkew() << "\n";
1486
1487 output_stream << "\t\t}\n";
1488 return TRUE;
1489}
1490
1491LLSD LLPathParams::asLLSD() const
1492{
1493 LLSD sd = LLSD();
1494 sd["curve"] = getCurveType();
1495 sd["begin"] = getBegin();
1496 sd["end"] = getEnd();
1497 sd["scale_x"] = getScaleX();
1498 sd["scale_y"] = getScaleY();
1499 sd["shear_x"] = getShearX();
1500 sd["shear_y"] = getShearY();
1501 sd["twist"] = getTwist();
1502 sd["twist_begin"] = getTwistBegin();
1503 sd["radius_offset"] = getRadiusOffset();
1504 sd["taper_x"] = getTaperX();
1505 sd["taper_y"] = getTaperY();
1506 sd["revolutions"] = getRevolutions();
1507 sd["skew"] = getSkew();
1508
1509 return sd;
1510}
1511
1512bool LLPathParams::fromLLSD(LLSD& sd)
1513{
1514 setCurveType(sd["curve"].asInteger());
1515 setBegin((F32)sd["begin"].asReal());
1516 setEnd((F32)sd["end"].asReal());
1517 setScaleX((F32)sd["scale_x"].asReal());
1518 setScaleY((F32)sd["scale_y"].asReal());
1519 setShearX((F32)sd["shear_x"].asReal());
1520 setShearY((F32)sd["shear_y"].asReal());
1521 setTwist((F32)sd["twist"].asReal());
1522 setTwistBegin((F32)sd["twist_begin"].asReal());
1523 setRadiusOffset((F32)sd["radius_offset"].asReal());
1524 setTaperX((F32)sd["taper_x"].asReal());
1525 setTaperY((F32)sd["taper_y"].asReal());
1526 setRevolutions((F32)sd["revolutions"].asReal());
1527 setSkew((F32)sd["skew"].asReal());
1528 return true;
1529}
1530
1531void LLPathParams::copyParams(const LLPathParams &params)
1532{
1533 setCurveType(params.getCurveType());
1534 setBegin(params.getBegin());
1535 setEnd(params.getEnd());
1536 setScale(params.getScaleX(), params.getScaleY() );
1537 setShear(params.getShearX(), params.getShearY() );
1538 setTwist(params.getTwist());
1539 setTwistBegin(params.getTwistBegin());
1540 setRadiusOffset(params.getRadiusOffset());
1541 setTaper( params.getTaperX(), params.getTaperY() );
1542 setRevolutions(params.getRevolutions());
1543 setSkew(params.getSkew());
1544}
1545
1546LLProfile::~LLProfile()
1547{
1548
1549}
1550
1551
1552S32 LLVolume::mNumMeshPoints = 0;
1553
1554LLVolume::LLVolume(const LLVolumeParams &params, const F32 detail, const BOOL generate_single_face, const BOOL is_unique) : mParams(params)
1555{
1556 mUnique = is_unique;
1557 mFaceMask = 0x0;
1558 mDetail = detail;
1559 // set defaults
1560 if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE)
1561 {
1562 mPathp = new LLDynamicPath(mParams.getPathParams());
1563 }
1564 else
1565 {
1566 mPathp = new LLPath(mParams.getPathParams());
1567 }
1568 mProfilep = new LLProfile(mParams.getProfileParams());
1569
1570 mNumVolumeFaces = 0;
1571 mVolumeFaces = NULL;
1572 mGenerateSingleFace = generate_single_face;
1573
1574 generate();
1575 createVolumeFaces();
1576}
1577
1578void LLVolume::regen()
1579{
1580 generate();
1581 createVolumeFaces();
1582}
1583
1584LLVolume::~LLVolume()
1585{
1586 mNumMeshPoints -= mMesh.size();
1587 delete mPathp;
1588 delete mProfilep;
1589 delete[] mVolumeFaces;
1590
1591 mPathp = NULL;
1592 mProfilep = NULL;
1593 mVolumeFaces = NULL;
1594}
1595
1596BOOL LLVolume::generate()
1597{
1598 //Added 10.03.05 Dave Parks
1599 // Split is a parameter to LLProfile::generate that tesselates edges on the profile
1600 // to prevent lighting and texture interpolation errors on triangles that are
1601 // stretched due to twisting or scaling on the path.
1602 S32 split = (S32) ((mDetail)*0.66f);
1603
1604 if (mPathp->mParams.getCurveType() == LL_PCODE_PATH_LINE &&
1605 (mPathp->mParams.getScale().mV[0] != 1.0f ||
1606 mPathp->mParams.getScale().mV[1] != 1.0f) &&
1607 (mProfilep->mParams.getCurveType() == LL_PCODE_PROFILE_SQUARE ||
1608 mProfilep->mParams.getCurveType() == LL_PCODE_PROFILE_ISOTRI ||
1609 mProfilep->mParams.getCurveType() == LL_PCODE_PROFILE_EQUALTRI ||
1610 mProfilep->mParams.getCurveType() == LL_PCODE_PROFILE_RIGHTTRI))
1611 {
1612 split = 0;
1613 }
1614
1615 mLODScaleBias.setVec(0.5f, 0.5f, 0.5f);
1616
1617 F32 profile_detail = mDetail;
1618 F32 path_detail = mDetail;
1619
1620 U8 path_type = mPathp->mParams.getCurveType();
1621 U8 profile_type = mProfilep->mParams.getCurveType();
1622
1623 if (path_type == LL_PCODE_PATH_LINE && profile_type == LL_PCODE_PROFILE_CIRCLE)
1624 { //cylinders don't care about Z-Axis
1625 mLODScaleBias.setVec(0.6f, 0.6f, 0.0f);
1626 }
1627 else if (path_type == LL_PCODE_PATH_CIRCLE)
1628 {
1629 mLODScaleBias.setVec(0.6f, 0.6f, 0.6f);
1630 }
1631
1632 BOOL regenPath = mPathp->generate(path_detail, split);
1633 BOOL regenProf = mProfilep->generate(mPathp->isOpen(),profile_detail, split);
1634
1635 if (regenPath || regenProf )
1636 {
1637 mNumMeshPoints -= mMesh.size();
1638 mMesh.resize(mProfilep->mProfile.size() * mPathp->mPath.size());
1639 mNumMeshPoints += mMesh.size();
1640
1641 S32 s = 0, t=0;
1642 S32 sizeS = mPathp->mPath.size();
1643 S32 sizeT = mProfilep->mProfile.size();
1644 S32 line = 0;
1645
1646 //generate vertex positions
1647
1648 // Run along the path.
1649 while (s < sizeS)
1650 {
1651 LLVector2 scale = mPathp->mPath[s].mScale;
1652 LLQuaternion rot = mPathp->mPath[s].mRot;
1653
1654 t = 0;
1655 // Run along the profile.
1656 while (t < sizeT)
1657 {
1658 S32 i = t + line;
1659 Point& pt = mMesh[i];
1660
1661 pt.mPos.mV[0] = mProfilep->mProfile[t].mV[0] * scale.mV[0];
1662 pt.mPos.mV[1] = mProfilep->mProfile[t].mV[1] * scale.mV[1];
1663 pt.mPos.mV[2] = 0.0f;
1664 pt.mPos = pt.mPos * rot;
1665 pt.mPos += mPathp->mPath[s].mPos;
1666 t++;
1667 }
1668 line += sizeT;
1669 s++;
1670 }
1671
1672 for (S32 i = 0; i < (S32)mProfilep->mFaces.size(); i++)
1673 {
1674 mFaceMask |= mProfilep->mFaces[i].mFaceID;
1675 }
1676 return TRUE;
1677 }
1678 return FALSE;
1679}
1680
1681
1682void LLVolume::createVolumeFaces()
1683{
1684 S32 i;
1685
1686 if (mVolumeFaces != NULL)
1687 {
1688 delete[] mVolumeFaces;
1689 mVolumeFaces = NULL;
1690 }
1691
1692 if (mGenerateSingleFace)
1693 {
1694 mNumVolumeFaces = 0;
1695 }
1696 else
1697 {
1698 S32 num_faces = getNumFaces();
1699 mNumVolumeFaces = num_faces;
1700 mVolumeFaces = new LLVolumeFace[num_faces];
1701 // Initialize volume faces with parameter data
1702 for (i = 0; i < num_faces; i++)
1703 {
1704 LLVolumeFace &vf = mVolumeFaces[i];
1705 LLProfile::Face &face = mProfilep->mFaces[i];
1706 vf.mVolumep = this;
1707 vf.mBeginS = face.mIndex;
1708 vf.mNumS = face.mCount;
1709 vf.mBeginT = 0;
1710 vf.mNumT= getPath().mPath.size();
1711 vf.mID = i;
1712
1713 // Set the type mask bits correctly
1714 if (mProfilep->isHollow())
1715 {
1716 vf.mTypeMask |= LLVolumeFace::HOLLOW_MASK;
1717 }
1718 if (mProfilep->isOpen())
1719 {
1720 vf.mTypeMask |= LLVolumeFace::OPEN_MASK;
1721 }
1722 if (face.mCap)
1723 {
1724 vf.mTypeMask |= LLVolumeFace::CAP_MASK;
1725 if (face.mFaceID == LL_FACE_PATH_BEGIN)
1726 {
1727 vf.mTypeMask |= LLVolumeFace::TOP_MASK;
1728 }
1729 else
1730 {
1731 llassert(face.mFaceID == LL_FACE_PATH_END);
1732 vf.mTypeMask |= LLVolumeFace::BOTTOM_MASK;
1733 }
1734 }
1735 else if (face.mFaceID & (LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END))
1736 {
1737 vf.mTypeMask |= LLVolumeFace::FLAT_MASK | LLVolumeFace::END_MASK;
1738 }
1739 else
1740 {
1741 vf.mTypeMask |= LLVolumeFace::SIDE_MASK;
1742 if (face.mFlat)
1743 {
1744 vf.mTypeMask |= LLVolumeFace::FLAT_MASK;
1745 }
1746 if (face.mFaceID & LL_FACE_INNER_SIDE)
1747 {
1748 vf.mTypeMask |= LLVolumeFace::INNER_MASK;
1749 if (face.mFlat && vf.mNumS > 2)
1750 { //flat inner faces have to copy vert normals
1751 vf.mNumS = vf.mNumS*2;
1752 }
1753 }
1754 else
1755 {
1756 vf.mTypeMask |= LLVolumeFace::OUTER_MASK;
1757 }
1758 }
1759 }
1760
1761 for (i = 0; i < mNumVolumeFaces; i++)
1762 {
1763 mVolumeFaces[i].create();
1764 }
1765 }
1766
1767 mBounds[1] = LLVector3(0,0,0);
1768 mBounds[0] = LLVector3(512,512,512);
1769}
1770
1771
1772BOOL LLVolume::isCap(S32 face)
1773{
1774 return mProfilep->mFaces[face].mCap;
1775}
1776
1777BOOL LLVolume::isFlat(S32 face)
1778{
1779 return mProfilep->mFaces[face].mFlat;
1780}
1781
1782
1783bool LLVolumeParams::operator==(const LLVolumeParams &params) const
1784{
1785 return (getPathParams() == params.getPathParams()) &&
1786 (getProfileParams() == params.getProfileParams());
1787}
1788
1789bool LLVolumeParams::operator!=(const LLVolumeParams &params) const
1790{
1791 return (getPathParams() != params.getPathParams()) ||
1792 (getProfileParams() != params.getProfileParams());
1793}
1794
1795bool LLVolumeParams::operator<(const LLVolumeParams &params) const
1796{
1797 if( getPathParams() != params.getPathParams() )
1798 {
1799 return getPathParams() < params.getPathParams();
1800 }
1801 else
1802 {
1803 return getProfileParams() < params.getProfileParams();
1804 }
1805}
1806
1807void LLVolumeParams::copyParams(const LLVolumeParams &params)
1808{
1809 mProfileParams.copyParams(params.mProfileParams);
1810 mPathParams.copyParams(params.mPathParams);
1811}
1812
1813// return true if in range (or nearly so)
1814static bool limit_range(F32& v, F32 min, F32 max)
1815{
1816 F32 min_delta = v - min;
1817 if (min_delta < 0.f)
1818 {
1819 v = min;
1820 if (!is_approx_zero(min_delta))
1821 return false;
1822 }
1823 F32 max_delta = max - v;
1824 if (max_delta < 0.f)
1825 {
1826 v = max;
1827 if (!is_approx_zero(max_delta))
1828 return false;
1829 }
1830 return true;
1831}
1832
1833bool LLVolumeParams::setBeginAndEndS(const F32 b, const F32 e)
1834{
1835 bool valid = true;
1836
1837 // First, clamp to valid ranges.
1838 F32 begin = b;
1839 valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA);
1840
1841 F32 end = e;
1842 valid &= limit_range(end, MIN_CUT_DELTA, 1.f);
1843
1844 valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA);
1845
1846 // Now set them.
1847 mProfileParams.setBegin(begin);
1848 mProfileParams.setEnd(end);
1849
1850 return valid;
1851}
1852
1853bool LLVolumeParams::setBeginAndEndT(const F32 b, const F32 e)
1854{
1855 bool valid = true;
1856
1857 // First, clamp to valid ranges.
1858 F32 begin = b;
1859 valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA);
1860
1861 F32 end = e;
1862 valid &= limit_range(end, MIN_CUT_DELTA, 1.f);
1863
1864 valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA);
1865
1866 // Now set them.
1867 mPathParams.setBegin(begin);
1868 mPathParams.setEnd(end);
1869
1870 return valid;
1871}
1872
1873bool LLVolumeParams::setHollow(const F32 h)
1874{
1875 // Validate the hollow based on path and profile.
1876 U8 profile = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
1877 U8 hole_type = mProfileParams.getCurveType() & LL_PCODE_HOLE_MASK;
1878
1879 F32 max_hollow = HOLLOW_MAX;
1880
1881 // Only square holes have trouble.
1882 if (LL_PCODE_HOLE_SQUARE == hole_type)
1883 {
1884 switch(profile)
1885 {
1886 case LL_PCODE_PROFILE_CIRCLE:
1887 case LL_PCODE_PROFILE_CIRCLE_HALF:
1888 case LL_PCODE_PROFILE_EQUALTRI:
1889 max_hollow = HOLLOW_MAX_SQUARE;
1890 }
1891 }
1892
1893 F32 hollow = h;
1894 bool valid = limit_range(hollow, HOLLOW_MIN, max_hollow);
1895 mProfileParams.setHollow(hollow);
1896
1897 return valid;
1898}
1899
1900bool LLVolumeParams::setTwistBegin(const F32 b)
1901{
1902 F32 twist_begin = b;
1903 bool valid = limit_range(twist_begin, TWIST_MIN, TWIST_MAX);
1904 mPathParams.setTwistBegin(twist_begin);
1905 return valid;
1906}
1907
1908bool LLVolumeParams::setTwistEnd(const F32 e)
1909{
1910 F32 twist_end = e;
1911 bool valid = limit_range(twist_end, TWIST_MIN, TWIST_MAX);
1912 mPathParams.setTwistEnd(twist_end);
1913 return valid;
1914}
1915
1916bool LLVolumeParams::setRatio(const F32 x, const F32 y)
1917{
1918 F32 min_x = RATIO_MIN;
1919 F32 max_x = RATIO_MAX;
1920 F32 min_y = RATIO_MIN;
1921 F32 max_y = RATIO_MAX;
1922 // If this is a circular path (and not a sphere) then 'ratio' is actually hole size.
1923 U8 path_type = mPathParams.getCurveType();
1924 U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
1925 if ( LL_PCODE_PATH_CIRCLE == path_type &&
1926 LL_PCODE_PROFILE_CIRCLE_HALF != profile_type)
1927 {
1928 // Holes are more restricted...
1929 min_x = HOLE_X_MIN;
1930 max_x = HOLE_X_MAX;
1931 min_y = HOLE_Y_MIN;
1932 max_y = HOLE_Y_MAX;
1933 }
1934
1935 F32 ratio_x = x;
1936 bool valid = limit_range(ratio_x, min_x, max_x);
1937 F32 ratio_y = y;
1938 valid &= limit_range(ratio_y, min_y, max_y);
1939
1940 mPathParams.setScale(ratio_x, ratio_y);
1941
1942 return valid;
1943}
1944
1945bool LLVolumeParams::setShear(const F32 x, const F32 y)
1946{
1947 F32 shear_x = x;
1948 bool valid = limit_range(shear_x, SHEAR_MIN, SHEAR_MAX);
1949 F32 shear_y = y;
1950 valid &= limit_range(shear_y, SHEAR_MIN, SHEAR_MAX);
1951 mPathParams.setShear(shear_x, shear_y);
1952 return valid;
1953}
1954
1955bool LLVolumeParams::setTaperX(const F32 v)
1956{
1957 F32 taper = v;
1958 bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX);
1959 mPathParams.setTaperX(taper);
1960 return valid;
1961}
1962
1963bool LLVolumeParams::setTaperY(const F32 v)
1964{
1965 F32 taper = v;
1966 bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX);
1967 mPathParams.setTaperY(taper);
1968 return valid;
1969}
1970
1971bool LLVolumeParams::setRevolutions(const F32 r)
1972{
1973 F32 revolutions = r;
1974 bool valid = limit_range(revolutions, REV_MIN, REV_MAX);
1975 mPathParams.setRevolutions(revolutions);
1976 return valid;
1977}
1978
1979bool LLVolumeParams::setRadiusOffset(const F32 offset)
1980{
1981 bool valid = true;
1982
1983 // If this is a sphere, just set it to 0 and get out.
1984 U8 path_type = mPathParams.getCurveType();
1985 U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
1986 if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type ||
1987 LL_PCODE_PATH_CIRCLE != path_type )
1988 {
1989 mPathParams.setRadiusOffset(0.f);
1990 return true;
1991 }
1992
1993 // Limit radius offset, based on taper and hole size y.
1994 F32 radius_offset = offset;
1995 F32 taper_y = getTaperY();
1996 F32 radius_mag = fabs(radius_offset);
1997 F32 hole_y_mag = fabs(getRatioY());
1998 F32 taper_y_mag = fabs(taper_y);
1999 // Check to see if the taper effects us.
2000 if ( (radius_offset > 0.f && taper_y < 0.f) ||
2001 (radius_offset < 0.f && taper_y > 0.f) )
2002 {
2003 // The taper does not help increase the radius offset range.
2004 taper_y_mag = 0.f;
2005 }
2006 F32 max_radius_mag = 1.f - hole_y_mag * (1.f - taper_y_mag) / (1.f - hole_y_mag);
2007
2008 // Enforce the maximum magnitude.
2009 F32 delta = max_radius_mag - radius_mag;
2010 if (delta < 0.f)
2011 {
2012 // Check radius offset sign.
2013 if (radius_offset < 0.f)
2014 {
2015 radius_offset = -max_radius_mag;
2016 }
2017 else
2018 {
2019 radius_offset = max_radius_mag;
2020 }
2021 valid = is_approx_zero(delta);
2022 }
2023
2024 mPathParams.setRadiusOffset(radius_offset);
2025 return valid;
2026}
2027
2028bool LLVolumeParams::setSkew(const F32 skew_value)
2029{
2030 bool valid = true;
2031
2032 // Check the skew value against the revolutions.
2033 F32 skew = llclamp(skew_value, SKEW_MIN, SKEW_MAX);
2034 F32 skew_mag = fabs(skew);
2035 F32 revolutions = getRevolutions();
2036 F32 scale_x = getRatioX();
2037 F32 min_skew_mag = 1.0f - 1.0f / (revolutions * scale_x + 1.0f);
2038 // Discontinuity; A revolution of 1 allows skews below 0.5.
2039 if ( fabs(revolutions - 1.0f) < 0.001)
2040 min_skew_mag = 0.0f;
2041
2042 // Clip skew.
2043 F32 delta = skew_mag - min_skew_mag;
2044 if (delta < 0.f)
2045 {
2046 // Check skew sign.
2047 if (skew < 0.0f)
2048 {
2049 skew = -min_skew_mag;
2050 }
2051 else
2052 {
2053 skew = min_skew_mag;
2054 }
2055 valid = is_approx_zero(delta);
2056 }
2057
2058 mPathParams.setSkew(skew);
2059 return valid;
2060}
2061
2062bool LLVolumeParams::setType(U8 profile, U8 path)
2063{
2064 bool result = true;
2065 // First, check profile and path for validity.
2066 U8 profile_type = profile & LL_PCODE_PROFILE_MASK;
2067 U8 hole_type = (profile & LL_PCODE_HOLE_MASK) >> 4;
2068 U8 path_type = path >> 4;
2069
2070 if (profile_type > LL_PCODE_PROFILE_MAX)
2071 {
2072 // Bad profile. Make it square.
2073 profile = LL_PCODE_PROFILE_SQUARE;
2074 result = false;
2075 llwarns << "LLVolumeParams::setType changing bad profile type (" << profile_type
2076 << ") to be LL_PCODE_PROFILE_SQUARE" << llendl;
2077 }
2078 else if (hole_type > LL_PCODE_HOLE_MAX)
2079 {
2080 // Bad hole. Make it the same.
2081 profile = profile_type;
2082 result = false;
2083 llwarns << "LLVolumeParams::setType changing bad hole type (" << hole_type
2084 << ") to be LL_PCODE_HOLE_SAME" << llendl;
2085 }
2086
2087 if (path_type < LL_PCODE_PATH_MIN ||
2088 path_type > LL_PCODE_PATH_MAX)
2089 {
2090 // Bad path. Make it linear.
2091 result = false;
2092 llwarns << "LLVolumeParams::setType changing bad path (" << path
2093 << ") to be LL_PCODE_PATH_LINE" << llendl;
2094 path = LL_PCODE_PATH_LINE;
2095 }
2096
2097 mProfileParams.setCurveType(profile);
2098 mPathParams.setCurveType(path);
2099 return result;
2100}
2101
2102// static
2103bool LLVolumeParams::validate(U8 prof_curve, F32 prof_begin, F32 prof_end, F32 hollow,
2104 U8 path_curve, F32 path_begin, F32 path_end,
2105 F32 scx, F32 scy, F32 shx, F32 shy,
2106 F32 twistend, F32 twistbegin, F32 radiusoffset,
2107 F32 tx, F32 ty, F32 revolutions, F32 skew)
2108{
2109 LLVolumeParams test_params;
2110 if (!test_params.setType (prof_curve, path_curve))
2111 {
2112 return false;
2113 }
2114 if (!test_params.setBeginAndEndS (prof_begin, prof_end))
2115 {
2116 return false;
2117 }
2118 if (!test_params.setBeginAndEndT (path_begin, path_end))
2119 {
2120 return false;
2121 }
2122 if (!test_params.setHollow (hollow))
2123 {
2124 return false;
2125 }
2126 if (!test_params.setTwistBegin (twistbegin))
2127 {
2128 return false;
2129 }
2130 if (!test_params.setTwistEnd (twistend))
2131 {
2132 return false;
2133 }
2134 if (!test_params.setRatio (scx, scy))
2135 {
2136 return false;
2137 }
2138 if (!test_params.setShear (shx, shy))
2139 {
2140 return false;
2141 }
2142 if (!test_params.setTaper (tx, ty))
2143 {
2144 return false;
2145 }
2146 if (!test_params.setRevolutions (revolutions))
2147 {
2148 return false;
2149 }
2150 if (!test_params.setRadiusOffset (radiusoffset))
2151 {
2152 return false;
2153 }
2154 if (!test_params.setSkew (skew))
2155 {
2156 return false;
2157 }
2158 return true;
2159}
2160
2161#define MAX_INDEX 10000
2162S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
2163{
2164 S32 index[MAX_INDEX];
2165 S32 count = 0;
2166 S32 *indices = NULL;
2167
2168 // Let's do this totally diffently, as we don't care about faces...
2169 // Counter-clockwise triangles are forward facing...
2170
2171 BOOL open = getProfile().isOpen();
2172 BOOL hollow = getProfile().isHollow();
2173 BOOL path_open = getPath().isOpen();
2174 S32 size_s, size_s_out, size_t;
2175 S32 s, t, i;
2176 size_s = getProfile().getTotal();
2177 size_s_out = getProfile().getTotalOut();
2178 size_t = getPath().mPath.size();
2179
2180 if (open)
2181 {
2182 if (hollow)
2183 {
2184 // Open hollow -- much like the closed solid, except we
2185 // we need to stitch up the gap between s=0 and s=size_s-1
2186
2187 if ( (size_t - 1) * (((size_s -1) * 6) + 6) >= MAX_INDEX)
2188 goto noindices;
2189
2190 for (t = 0; t < size_t - 1; t++)
2191 {
2192 // The outer face, first cut, and inner face
2193 for (s = 0; s < size_s - 1; s++)
2194 {
2195 i = s + t*size_s;
2196 index[count++] = i; // x,y
2197 index[count++] = i + 1; // x+1,y
2198 index[count++] = i + size_s; // x,y+1
2199
2200 index[count++] = i + size_s; // x,y+1
2201 index[count++] = i + 1; // x+1,y
2202 index[count++] = i + size_s + 1; // x+1,y+1
2203 }
2204
2205 // The other cut face
2206 index[count++] = s + t*size_s; // x,y
2207 index[count++] = 0 + t*size_s; // x+1,y
2208 index[count++] = s + (t+1)*size_s; // x,y+1
2209
2210 index[count++] = s + (t+1)*size_s; // x,y+1
2211 index[count++] = 0 + t*size_s; // x+1,y
2212 index[count++] = 0 + (t+1)*size_s; // x+1,y+1
2213 }
2214
2215 // Do the top and bottom caps, if necessary
2216 if (path_open)
2217 {
2218 // Top cap
2219 S32 pt1 = 0;
2220 S32 pt2 = size_s-1;
2221 S32 i = (size_t - 1)*size_s;
2222
2223 while (pt2 - pt1 > 1)
2224 {
2225 // Use the profile points instead of the mesh, since you want
2226 // the un-transformed profile distances.
2227 LLVector3 p1 = getProfile().mProfile[pt1];
2228 LLVector3 p2 = getProfile().mProfile[pt2];
2229 LLVector3 pa = getProfile().mProfile[pt1+1];
2230 LLVector3 pb = getProfile().mProfile[pt2-1];
2231
2232 p1.mV[VZ] = 0.f;
2233 p2.mV[VZ] = 0.f;
2234 pa.mV[VZ] = 0.f;
2235 pb.mV[VZ] = 0.f;
2236
2237 // Use area of triangle to determine backfacing
2238 F32 area_1a2, area_1ba, area_21b, area_2ab;
2239 area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
2240 (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
2241 (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
2242
2243 area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
2244 (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
2245 (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
2246
2247 area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
2248 (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
2249 (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
2250
2251 area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
2252 (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
2253 (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
2254
2255 BOOL use_tri1a2 = TRUE;
2256 BOOL tri_1a2 = TRUE;
2257 BOOL tri_21b = TRUE;
2258
2259 if (area_1a2 < 0)
2260 {
2261 tri_1a2 = FALSE;
2262 }
2263 if (area_2ab < 0)
2264 {
2265 // Can't use, because it contains point b
2266 tri_1a2 = FALSE;
2267 }
2268 if (area_21b < 0)
2269 {
2270 tri_21b = FALSE;
2271 }
2272 if (area_1ba < 0)
2273 {
2274 // Can't use, because it contains point b
2275 tri_21b = FALSE;
2276 }
2277
2278 if (!tri_1a2)
2279 {
2280 use_tri1a2 = FALSE;
2281 }
2282 else if (!tri_21b)
2283 {
2284 use_tri1a2 = TRUE;
2285 }
2286 else
2287 {
2288 LLVector3 d1 = p1 - pa;
2289 LLVector3 d2 = p2 - pb;
2290
2291 if (d1.magVecSquared() < d2.magVecSquared())
2292 {
2293 use_tri1a2 = TRUE;
2294 }
2295 else
2296 {
2297 use_tri1a2 = FALSE;
2298 }
2299 }
2300
2301 if (use_tri1a2)
2302 {
2303 if (count + 3 >= MAX_INDEX)
2304 goto noindices;
2305 index[count++] = pt1 + i;
2306 index[count++] = pt1 + 1 + i;
2307 index[count++] = pt2 + i;
2308 pt1++;
2309 }
2310 else
2311 {
2312 if (count + 3 >= MAX_INDEX)
2313 goto noindices;
2314 index[count++] = pt1 + i;
2315 index[count++] = pt2 - 1 + i;
2316 index[count++] = pt2 + i;
2317 pt2--;
2318 }
2319 }
2320
2321 // Bottom cap
2322 pt1 = 0;
2323 pt2 = size_s-1;
2324 while (pt2 - pt1 > 1)
2325 {
2326 // Use the profile points instead of the mesh, since you want
2327 // the un-transformed profile distances.
2328 LLVector3 p1 = getProfile().mProfile[pt1];
2329 LLVector3 p2 = getProfile().mProfile[pt2];
2330 LLVector3 pa = getProfile().mProfile[pt1+1];
2331 LLVector3 pb = getProfile().mProfile[pt2-1];
2332
2333 p1.mV[VZ] = 0.f;
2334 p2.mV[VZ] = 0.f;
2335 pa.mV[VZ] = 0.f;
2336 pb.mV[VZ] = 0.f;
2337
2338 // Use area of triangle to determine backfacing
2339 F32 area_1a2, area_1ba, area_21b, area_2ab;
2340 area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
2341 (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
2342 (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
2343
2344 area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
2345 (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
2346 (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
2347
2348 area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
2349 (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
2350 (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
2351
2352 area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
2353 (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
2354 (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
2355
2356 BOOL use_tri1a2 = TRUE;
2357 BOOL tri_1a2 = TRUE;
2358 BOOL tri_21b = TRUE;
2359
2360 if (area_1a2 < 0)
2361 {
2362 tri_1a2 = FALSE;
2363 }
2364 if (area_2ab < 0)
2365 {
2366 // Can't use, because it contains point b
2367 tri_1a2 = FALSE;
2368 }
2369 if (area_21b < 0)
2370 {
2371 tri_21b = FALSE;
2372 }
2373 if (area_1ba < 0)
2374 {
2375 // Can't use, because it contains point b
2376 tri_21b = FALSE;
2377 }
2378
2379 if (!tri_1a2)
2380 {
2381 use_tri1a2 = FALSE;
2382 }
2383 else if (!tri_21b)
2384 {
2385 use_tri1a2 = TRUE;
2386 }
2387 else
2388 {
2389 LLVector3 d1 = p1 - pa;
2390 LLVector3 d2 = p2 - pb;
2391
2392 if (d1.magVecSquared() < d2.magVecSquared())
2393 {
2394 use_tri1a2 = TRUE;
2395 }
2396 else
2397 {
2398 use_tri1a2 = FALSE;
2399 }
2400 }
2401
2402 if (use_tri1a2)
2403 {
2404 if (count + 3 >= MAX_INDEX)
2405 goto noindices;
2406 index[count++] = pt1;
2407 index[count++] = pt2;
2408 index[count++] = pt1 + 1;
2409 pt1++;
2410 }
2411 else
2412 {
2413 if (count + 3 >= MAX_INDEX)
2414 goto noindices;
2415 index[count++] = pt1;
2416 index[count++] = pt2;
2417 index[count++] = pt2 - 1;
2418 pt2--;
2419 }
2420 }
2421 }
2422 }
2423 else
2424 {
2425 // Open solid
2426
2427 if ( (size_t - 1) * (((size_s -1) * 6) + 6) >= MAX_INDEX)
2428 goto noindices;
2429
2430 for (t = 0; t < size_t - 1; t++)
2431 {
2432 // Outer face + 1 cut face
2433 for (s = 0; s < size_s - 1; s++)
2434 {
2435 i = s + t*size_s;
2436
2437 index[count++] = i; // x,y
2438 index[count++] = i + 1; // x+1,y
2439 index[count++] = i + size_s; // x,y+1
2440
2441 index[count++] = i + size_s; // x,y+1
2442 index[count++] = i + 1; // x+1,y
2443 index[count++] = i + size_s + 1; // x+1,y+1
2444 }
2445
2446 // The other cut face
2447 index[count++] = (size_s - 1) + (t*size_s); // x,y
2448 index[count++] = 0 + t*size_s; // x+1,y
2449 index[count++] = (size_s - 1) + (t+1)*size_s; // x,y+1
2450
2451 index[count++] = (size_s - 1) + (t+1)*size_s; // x,y+1
2452 index[count++] = 0 + (t*size_s); // x+1,y
2453 index[count++] = 0 + (t+1)*size_s; // x+1,y+1
2454 }
2455
2456 // Do the top and bottom caps, if necessary
2457 if (path_open)
2458 {
2459 if ( count + (size_s - 2) * 3 >= MAX_INDEX)
2460 goto noindices;
2461 for (s = 0; s < size_s - 2; s++)
2462 {
2463 index[count++] = s+1;
2464 index[count++] = s;
2465 index[count++] = size_s - 1;
2466 }
2467
2468 // We've got a top cap
2469 S32 offset = (size_t - 1)*size_s;
2470 if ( count + (size_s - 2) * 3 >= MAX_INDEX)
2471 goto noindices;
2472 for (s = 0; s < size_s - 2; s++)
2473 {
2474 // Inverted ordering from bottom cap.
2475 index[count++] = offset + size_s - 1;
2476 index[count++] = offset + s;
2477 index[count++] = offset + s + 1;
2478 }
2479 }
2480 }
2481 }
2482 else if (hollow)
2483 {
2484 // Closed hollow
2485 // Outer face
2486
2487 if ( (size_t - 1) * (size_s_out - 1) * 6 >= MAX_INDEX)
2488 goto noindices;
2489 for (t = 0; t < size_t - 1; t++)
2490 {
2491 for (s = 0; s < size_s_out - 1; s++)
2492 {
2493 i = s + t*size_s;
2494
2495 index[count++] = i; // x,y
2496 index[count++] = i + 1; // x+1,y
2497 index[count++] = i + size_s; // x,y+1
2498
2499 index[count++] = i + size_s; // x,y+1
2500 index[count++] = i + 1; // x+1,y
2501 index[count++] = i + 1 + size_s; // x+1,y+1
2502 }
2503 }
2504
2505 // Inner face
2506 // Invert facing from outer face
2507 if ( count + (size_t - 1) * ((size_s - 1) - size_s_out) * 6 >= MAX_INDEX)
2508 goto noindices;
2509 for (t = 0; t < size_t - 1; t++)
2510 {
2511 for (s = size_s_out; s < size_s - 1; s++)
2512 {
2513 i = s + t*size_s;
2514
2515 index[count++] = i; // x,y
2516 index[count++] = i + 1; // x+1,y
2517 index[count++] = i + size_s; // x,y+1
2518
2519 index[count++] = i + size_s; // x,y+1
2520 index[count++] = i + 1; // x+1,y
2521 index[count++] = i + 1 + size_s; // x+1,y+1
2522 }
2523 }
2524
2525 // Do the top and bottom caps, if necessary
2526 if (path_open)
2527 {
2528 // Top cap
2529 S32 pt1 = 0;
2530 S32 pt2 = size_s-1;
2531 S32 i = (size_t - 1)*size_s;
2532
2533 while (pt2 - pt1 > 1)
2534 {
2535 // Use the profile points instead of the mesh, since you want
2536 // the un-transformed profile distances.
2537 LLVector3 p1 = getProfile().mProfile[pt1];
2538 LLVector3 p2 = getProfile().mProfile[pt2];
2539 LLVector3 pa = getProfile().mProfile[pt1+1];
2540 LLVector3 pb = getProfile().mProfile[pt2-1];
2541
2542 p1.mV[VZ] = 0.f;
2543 p2.mV[VZ] = 0.f;
2544 pa.mV[VZ] = 0.f;
2545 pb.mV[VZ] = 0.f;
2546
2547 // Use area of triangle to determine backfacing
2548 F32 area_1a2, area_1ba, area_21b, area_2ab;
2549 area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
2550 (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
2551 (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
2552
2553 area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
2554 (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
2555 (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
2556
2557 area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
2558 (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
2559 (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
2560
2561 area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
2562 (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
2563 (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
2564
2565 BOOL use_tri1a2 = TRUE;
2566 BOOL tri_1a2 = TRUE;
2567 BOOL tri_21b = TRUE;
2568
2569 if (area_1a2 < 0)
2570 {
2571 tri_1a2 = FALSE;
2572 }
2573 if (area_2ab < 0)
2574 {
2575 // Can't use, because it contains point b
2576 tri_1a2 = FALSE;
2577 }
2578 if (area_21b < 0)
2579 {
2580 tri_21b = FALSE;
2581 }
2582 if (area_1ba < 0)
2583 {
2584 // Can't use, because it contains point b
2585 tri_21b = FALSE;
2586 }
2587
2588 if (!tri_1a2)
2589 {
2590 use_tri1a2 = FALSE;
2591 }
2592 else if (!tri_21b)
2593 {
2594 use_tri1a2 = TRUE;
2595 }
2596 else
2597 {
2598 LLVector3 d1 = p1 - pa;
2599 LLVector3 d2 = p2 - pb;
2600
2601 if (d1.magVecSquared() < d2.magVecSquared())
2602 {
2603 use_tri1a2 = TRUE;
2604 }
2605 else
2606 {
2607 use_tri1a2 = FALSE;
2608 }
2609 }
2610
2611 if (use_tri1a2)
2612 {
2613 if (count + 3 >= MAX_INDEX)
2614 goto noindices;
2615 index[count++] = pt1 + i;
2616 index[count++] = pt1 + 1 + i;
2617 index[count++] = pt2 + i;
2618 pt1++;
2619 }
2620 else
2621 {
2622 if (count + 3 >= MAX_INDEX)
2623 goto noindices;
2624 index[count++] = pt1 + i;
2625 index[count++] = pt2 - 1 + i;
2626 index[count++] = pt2 + i;
2627 pt2--;
2628 }
2629 }
2630
2631 // Bottom cap
2632 pt1 = 0;
2633 pt2 = size_s-1;
2634 while (pt2 - pt1 > 1)
2635 {
2636 // Use the profile points instead of the mesh, since you want
2637 // the un-transformed profile distances.
2638 LLVector3 p1 = getProfile().mProfile[pt1];
2639 LLVector3 p2 = getProfile().mProfile[pt2];
2640 LLVector3 pa = getProfile().mProfile[pt1+1];
2641 LLVector3 pb = getProfile().mProfile[pt2-1];
2642
2643 p1.mV[VZ] = 0.f;
2644 p2.mV[VZ] = 0.f;
2645 pa.mV[VZ] = 0.f;
2646 pb.mV[VZ] = 0.f;
2647
2648 // Use area of triangle to determine backfacing
2649 F32 area_1a2, area_1ba, area_21b, area_2ab;
2650 area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
2651 (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
2652 (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
2653
2654 area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
2655 (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
2656 (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
2657
2658 area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
2659 (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
2660 (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
2661
2662 area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
2663 (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
2664 (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
2665
2666 BOOL use_tri1a2 = TRUE;
2667 BOOL tri_1a2 = TRUE;
2668 BOOL tri_21b = TRUE;
2669
2670 if (area_1a2 < 0)
2671 {
2672 tri_1a2 = FALSE;
2673 }
2674 if (area_2ab < 0)
2675 {
2676 // Can't use, because it contains point b
2677 tri_1a2 = FALSE;
2678 }
2679 if (area_21b < 0)
2680 {
2681 tri_21b = FALSE;
2682 }
2683 if (area_1ba < 0)
2684 {
2685 // Can't use, because it contains point b
2686 tri_21b = FALSE;
2687 }
2688
2689 if (!tri_1a2)
2690 {
2691 use_tri1a2 = FALSE;
2692 }
2693 else if (!tri_21b)
2694 {
2695 use_tri1a2 = TRUE;
2696 }
2697 else
2698 {
2699 LLVector3 d1 = p1 - pa;
2700 LLVector3 d2 = p2 - pb;
2701
2702 if (d1.magVecSquared() < d2.magVecSquared())
2703 {
2704 use_tri1a2 = TRUE;
2705 }
2706 else
2707 {
2708 use_tri1a2 = FALSE;
2709 }
2710 }
2711
2712 if (use_tri1a2)
2713 {
2714 if (count + 3 >= MAX_INDEX)
2715 goto noindices;
2716 index[count++] = pt1;
2717 index[count++] = pt2;
2718 index[count++] = pt1 + 1;
2719 pt1++;
2720 }
2721 else
2722 {
2723 if (count + 3 >= MAX_INDEX)
2724 goto noindices;
2725 index[count++] = pt1;
2726 index[count++] = pt2;
2727 index[count++] = pt2 - 1;
2728 pt2--;
2729 }
2730 }
2731 }
2732 }
2733 else
2734 {
2735 // Closed solid. Easy case.
2736 if ( (size_t - 1) * (size_s - 1) * 6 > MAX_INDEX)
2737 goto noindices;
2738 for (t = 0; t < size_t - 1; t++)
2739 {
2740 for (s = 0; s < size_s - 1; s++)
2741 {
2742 // Should wrap properly, but for now...
2743 i = s + t*size_s;
2744
2745 index[count++] = i; // x,y
2746 index[count++] = i + 1; // x+1,y
2747 index[count++] = i + size_s; // x,y+1
2748
2749 index[count++] = i + size_s; // x,y+1
2750 index[count++] = i + 1; // x+1,y
2751 index[count++] = i + size_s + 1; // x+1,y+1
2752 }
2753 }
2754
2755 // Do the top and bottom caps, if necessary
2756 if (path_open)
2757 {
2758 // bottom cap
2759 if ( count + (size_s - 2 - 1) * 3 >= MAX_INDEX)
2760 goto noindices;
2761 for (s = 1; s < size_s - 2; s++)
2762 {
2763 index[count++] = s+1;
2764 index[count++] = s;
2765 index[count++] = 0;
2766 }
2767
2768 // top cap
2769 S32 offset = (size_t - 1)*size_s;
2770 if ( count + (size_s - 2 - 1) * 3 >= MAX_INDEX)
2771 goto noindices;
2772 for (s = 1; s < size_s - 2; s++)
2773 {
2774 // Inverted ordering from bottom cap.
2775 index[count++] = offset;
2776 index[count++] = offset + s;
2777 index[count++] = offset + s + 1;
2778 }
2779 }
2780 }
2781
2782#if 0
2783 S32 num_vertices = mMesh.size();
2784 for (i = 0; i < count; i+=3)
2785 {
2786 llinfos << index[i] << ":" << index[i+1] << ":" << index[i+2] << llendl;
2787 llassert(index[i] < num_vertices);
2788 llassert(index[i+1] < num_vertices);
2789 llassert(index[i+2] < num_vertices);
2790 }
2791#endif
2792
2793 indices = new S32[count];
2794noindices:
2795 if (!indices)
2796 {
2797 llwarns << "Couldn't allocate triangle indices" << llendl;
2798 num_indices = 0;
2799 return NULL;
2800 }
2801 num_indices = count;
2802 memcpy(indices, index, count * sizeof(S32));
2803 return indices;
2804}
2805
2806//-----------------------------------------------------------------------------
2807// generateSilhouetteVertices()
2808//-----------------------------------------------------------------------------
2809void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
2810 std::vector<LLVector3> &normals,
2811 std::vector<S32> &segments,
2812 const LLVector3& obj_cam_vec,
2813 const LLMatrix4& mat,
2814 const LLMatrix3& norm_mat)
2815{
2816 vertices.clear();
2817 normals.clear();
2818 segments.clear();
2819
2820 //for each face
2821 for (S32 i = 0; i < getNumFaces(); i++) {
2822 LLVolumeFace face = this->getVolumeFace(i);
2823
2824 if (face.mTypeMask & (LLVolumeFace::CAP_MASK)) {
2825
2826 }
2827 else {
2828
2829 //==============================================
2830 //DEBUG draw edge map instead of silhouette edge
2831 //==============================================
2832
2833#if DEBUG_SILHOUETTE_EDGE_MAP
2834
2835 //for each triangle
2836 U32 count = face.mIndices.size();
2837 for (U32 j = 0; j < count/3; j++) {
2838 //get vertices
2839 S32 v1 = face.mIndices[j*3+0];
2840 S32 v2 = face.mIndices[j*3+1];
2841 S32 v3 = face.mIndices[j*3+2];
2842
2843 //get current face center
2844 LLVector3 cCenter = (face.mVertices[v1].mPosition +
2845 face.mVertices[v2].mPosition +
2846 face.mVertices[v3].mPosition) / 3.0f;
2847
2848 //for each edge
2849 for (S32 k = 0; k < 3; k++) {
2850 S32 nIndex = face.mEdge[j*3+k];
2851 if (nIndex <= -1) {
2852 continue;
2853 }
2854
2855 if (nIndex >= (S32) count/3) {
2856 continue;
2857 }
2858 //get neighbor vertices
2859 v1 = face.mIndices[nIndex*3+0];
2860 v2 = face.mIndices[nIndex*3+1];
2861 v3 = face.mIndices[nIndex*3+2];
2862
2863 //get neighbor face center
2864 LLVector3 nCenter = (face.mVertices[v1].mPosition +
2865 face.mVertices[v2].mPosition +
2866 face.mVertices[v3].mPosition) / 3.0f;
2867
2868 //draw line
2869 vertices.push_back(cCenter);
2870 vertices.push_back(nCenter);
2871 normals.push_back(LLVector3(1,1,1));
2872 normals.push_back(LLVector3(1,1,1));
2873 segments.push_back(vertices.size());
2874 }
2875 }
2876
2877 continue;
2878
2879 //==============================================
2880 //DEBUG
2881 //==============================================
2882
2883 //==============================================
2884 //DEBUG draw normals instead of silhouette edge
2885 //==============================================
2886#elif DEBUG_SILHOUETTE_NORMALS
2887
2888 //for each vertex
2889 for (U32 j = 0; j < face.mVertices.size(); j++) {
2890 vertices.push_back(face.mVertices[j].mPosition);
2891 vertices.push_back(face.mVertices[j].mPosition + face.mVertices[j].mNormal*0.1f);
2892 normals.push_back(LLVector3(0,0,1));
2893 normals.push_back(LLVector3(0,0,1));
2894 segments.push_back(vertices.size());
2895#if DEBUG_SILHOUETTE_BINORMALS
2896 vertices.push_back(face.mVertices[j].mPosition);
2897 vertices.push_back(face.mVertices[j].mPosition + face.mVertices[j].mBinormal*0.1f);
2898 normals.push_back(LLVector3(0,0,1));
2899 normals.push_back(LLVector3(0,0,1));
2900 segments.push_back(vertices.size());
2901#endif
2902 }
2903
2904 continue;
2905#else
2906 //==============================================
2907 //DEBUG
2908 //==============================================
2909
2910 static const U8 AWAY = 0x01,
2911 TOWARDS = 0x02;
2912
2913 //for each triangle
2914 std::vector<U8> fFacing;
2915 vector_append(fFacing, face.mIndices.size()/3);
2916 for (U32 j = 0; j < face.mIndices.size()/3; j++)
2917 {
2918 //approximate normal
2919 S32 v1 = face.mIndices[j*3+0];
2920 S32 v2 = face.mIndices[j*3+1];
2921 S32 v3 = face.mIndices[j*3+2];
2922
2923 LLVector3 norm = (face.mVertices[v1].mPosition - face.mVertices[v2].mPosition) %
2924 (face.mVertices[v2].mPosition - face.mVertices[v3].mPosition);
2925
2926 if (norm.magVecSquared() < 0.00000001f)
2927 {
2928 fFacing[j] = AWAY | TOWARDS;
2929 }
2930 else
2931 {
2932 //get view vector
2933 LLVector3 view = (obj_cam_vec-face.mVertices[v1].mPosition);
2934 bool away = view * norm > 0.0f;
2935 if (away)
2936 {
2937 fFacing[j] = AWAY;
2938 }
2939 else
2940 {
2941 fFacing[j] = TOWARDS;
2942 }
2943 }
2944 }
2945
2946 //for each triangle
2947 for (U32 j = 0; j < face.mIndices.size()/3; j++)
2948 {
2949 if (fFacing[j] == (AWAY | TOWARDS))
2950 { //this is a degenerate triangle
2951 //take neighbor facing (degenerate faces get facing of one of their neighbors)
2952 // *FIX IF NEEDED: this does not deal with neighboring degenerate faces
2953 for (S32 k = 0; k < 3; k++)
2954 {
2955 S32 index = face.mEdge[j*3+k];
2956 if (index != -1)
2957 {
2958 fFacing[j] = fFacing[index];
2959 break;
2960 }
2961 }
2962 continue; //skip degenerate face
2963 }
2964
2965 //for each edge
2966 for (S32 k = 0; k < 3; k++) {
2967 S32 index = face.mEdge[j*3+k];
2968 if (index != -1 && fFacing[index] == (AWAY | TOWARDS)) {
2969 //our neighbor is degenerate, make him face our direction
2970 fFacing[face.mEdge[j*3+k]] = fFacing[j];
2971 continue;
2972 }
2973
2974 if (index == -1 || //edge has no neighbor, MUST be a silhouette edge
2975 (fFacing[index] & fFacing[j]) == 0) { //we found a silhouette edge
2976
2977 S32 v1 = face.mIndices[j*3+k];
2978 S32 v2 = face.mIndices[j*3+((k+1)%3)];
2979
2980 vertices.push_back(face.mVertices[v1].mPosition*mat);
2981 normals.push_back(face.mVertices[v1].mNormal*norm_mat);
2982
2983 vertices.push_back(face.mVertices[v2].mPosition*mat);
2984 normals.push_back(face.mVertices[v2].mNormal*norm_mat);
2985 segments.push_back(vertices.size());
2986 }
2987 }
2988 }
2989#endif
2990 }
2991 }
2992}
2993
2994S32 LLVolume::lineSegmentIntersect(const LLVector3& start, LLVector3& end) const
2995{
2996 S32 ret = -1;
2997
2998 LLVector3 vec = end - start;
2999
3000 for (U32 i = 0; i < (U32)getNumFaces(); i++)
3001 {
3002 LLVolumeFace face = getVolumeFace(i);
3003
3004 for (U32 j = 0; j < face.mIndices.size()/3; j++)
3005 {
3006 //approximate normal
3007 S32 v1 = face.mIndices[j*3+0];
3008 S32 v2 = face.mIndices[j*3+1];
3009 S32 v3 = face.mIndices[j*3+2];
3010
3011 LLVector3 norm = (face.mVertices[v2].mPosition - face.mVertices[v1].mPosition) %
3012 (face.mVertices[v3].mPosition - face.mVertices[v2].mPosition);
3013
3014 if (norm.magVecSquared() >= 0.00000001f)
3015 {
3016 //get view vector
3017 //LLVector3 view = (start-face.mVertices[v1].mPosition);
3018 //if (view * norm < 0.0f)
3019 {
3020 if (LLTriangleLineSegmentIntersect( face.mVertices[v1].mPosition,
3021 face.mVertices[v2].mPosition,
3022 face.mVertices[v3].mPosition,
3023 end,
3024 vec))
3025 {
3026 vec = end-start;
3027 ret = (S32) i;
3028 }
3029 }
3030 }
3031 }
3032 }
3033
3034 return ret;
3035}
3036
3037class LLVertexIndexPair
3038{
3039public:
3040 LLVertexIndexPair(const LLVector3 &vertex, const S32 index);
3041
3042 LLVector3 mVertex;
3043 S32 mIndex;
3044};
3045
3046LLVertexIndexPair::LLVertexIndexPair(const LLVector3 &vertex, const S32 index)
3047{
3048 mVertex = vertex;
3049 mIndex = index;
3050}
3051
3052const F32 VERTEX_SLOP = 0.00001f;
3053const F32 VERTEX_SLOP_SQRD = VERTEX_SLOP * VERTEX_SLOP;
3054
3055struct lessVertex
3056{
3057 bool operator()(const LLVertexIndexPair *a, const LLVertexIndexPair *b)
3058 {
3059 const F32 slop = VERTEX_SLOP;
3060
3061 if (a->mVertex.mV[0] + slop < b->mVertex.mV[0])
3062 {
3063 return TRUE;
3064 }
3065 else if (a->mVertex.mV[0] - slop > b->mVertex.mV[0])
3066 {
3067 return FALSE;
3068 }
3069
3070 if (a->mVertex.mV[1] + slop < b->mVertex.mV[1])
3071 {
3072 return TRUE;
3073 }
3074 else if (a->mVertex.mV[1] - slop > b->mVertex.mV[1])
3075 {
3076 return FALSE;
3077 }
3078
3079 if (a->mVertex.mV[2] + slop < b->mVertex.mV[2])
3080 {
3081 return TRUE;
3082 }
3083 else if (a->mVertex.mV[2] - slop > b->mVertex.mV[2])
3084 {
3085 return FALSE;
3086 }
3087
3088 return FALSE;
3089 }
3090};
3091
3092struct lessTriangle
3093{
3094 bool operator()(const S32 *a, const S32 *b)
3095 {
3096 if (*a < *b)
3097 {
3098 return TRUE;
3099 }
3100 else if (*a > *b)
3101 {
3102 return FALSE;
3103 }
3104
3105 if (*(a+1) < *(b+1))
3106 {
3107 return TRUE;
3108 }
3109 else if (*(a+1) > *(b+1))
3110 {
3111 return FALSE;
3112 }
3113
3114 if (*(a+2) < *(b+2))
3115 {
3116 return TRUE;
3117 }
3118 else if (*(a+2) > *(b+2))
3119 {
3120 return FALSE;
3121 }
3122
3123 return FALSE;
3124 }
3125};
3126
3127BOOL equalTriangle(const S32 *a, const S32 *b)
3128{
3129 if ((*a == *b) && (*(a+1) == *(b+1)) && ((*a+2) == (*b+2)))
3130 {
3131 return TRUE;
3132 }
3133 return FALSE;
3134}
3135
3136BOOL LLVolume::cleanupTriangleData( const S32 num_input_vertices,
3137 const std::vector<Point>& input_vertices,
3138 const S32 num_input_triangles,
3139 S32 *input_triangles,
3140 S32 &num_output_vertices,
3141 LLVector3 **output_vertices,
3142 S32 &num_output_triangles,
3143 S32 **output_triangles)
3144{
3145 // Here's how we do this:
3146 // Create a structure which contains the original vertex index and the
3147 // LLVector3 data.
3148 // "Sort" the data by the vectors
3149 // Create an array the size of the old vertex list, with a mapping of
3150 // old indices to new indices.
3151 // Go through triangles, shift so the lowest index is first
3152 // Sort triangles by first index
3153 // Remove duplicate triangles
3154 // Allocate and pack new triangle data.
3155
3156 //LLTimer cleanupTimer;
3157 //llinfos << "In vertices: " << num_input_vertices << llendl;
3158 //llinfos << "In triangles: " << num_input_triangles << llendl;
3159
3160 S32 i;
3161 typedef std::multiset<LLVertexIndexPair*, lessVertex> vertex_set_t;
3162 vertex_set_t vertex_list;
3163
3164 LLVertexIndexPair *pairp = NULL;
3165 for (i = 0; i < num_input_vertices; i++)
3166 {
3167 LLVertexIndexPair *new_pairp = new LLVertexIndexPair(input_vertices[i].mPos, i);
3168 vertex_list.insert(new_pairp);
3169 }
3170
3171 // Generate the vertex mapping and the list of vertices without
3172 // duplicates. This will crash if there are no vertices.
3173 S32 *vertex_mapping = new S32[num_input_vertices];
3174 LLVector3 *new_vertices = new LLVector3[num_input_vertices];
3175 LLVertexIndexPair *prev_pairp = NULL;
3176
3177 S32 new_num_vertices;
3178
3179 new_num_vertices = 0;
3180 for (vertex_set_t::iterator iter = vertex_list.begin(),
3181 end = vertex_list.end();
3182 iter != end; iter++)
3183 {
3184 pairp = *iter;
3185 if (!prev_pairp || ((pairp->mVertex - prev_pairp->mVertex).magVecSquared() >= VERTEX_SLOP_SQRD))
3186 {
3187 new_vertices[new_num_vertices] = pairp->mVertex;
3188 //llinfos << "Added vertex " << new_num_vertices << " : " << pairp->mVertex << llendl;
3189 new_num_vertices++;
3190 // Update the previous
3191 prev_pairp = pairp;
3192 }
3193 else
3194 {
3195 //llinfos << "Removed duplicate vertex " << pairp->mVertex << llendl;
3196 }
3197 vertex_mapping[pairp->mIndex] = new_num_vertices - 1;
3198 }
3199
3200 // Iterate through triangles and remove degenerates, re-ordering vertices
3201 // along the way.
3202 S32 *new_triangles = new S32[num_input_triangles * 3];
3203 S32 new_num_triangles = 0;
3204
3205 for (i = 0; i < num_input_triangles; i++)
3206 {
3207 //llinfos << "Checking triangle " << input_triangles[i*3] << ":" << input_triangles[i*3+1] << ":" << input_triangles[i*3+2] << llendl;
3208 input_triangles[i*3] = vertex_mapping[input_triangles[i*3]];
3209 input_triangles[i*3+1] = vertex_mapping[input_triangles[i*3+1]];
3210 input_triangles[i*3+2] = vertex_mapping[input_triangles[i*3+2]];
3211
3212 if ((input_triangles[i*3] == input_triangles[i*3+1])
3213 || (input_triangles[i*3] == input_triangles[i*3+2])
3214 || (input_triangles[i*3+1] == input_triangles[i*3+2]))
3215 {
3216 //llinfos << "Removing degenerate triangle " << input_triangles[i*3] << ":" << input_triangles[i*3+1] << ":" << input_triangles[i*3+2] << llendl;
3217 // Degenerate triangle, skip
3218 continue;
3219 }
3220
3221 if (input_triangles[i*3] < input_triangles[i*3+1])
3222 {
3223 if (input_triangles[i*3] < input_triangles[i*3+2])
3224 {
3225 // (0 < 1) && (0 < 2)
3226 new_triangles[new_num_triangles*3] = input_triangles[i*3];
3227 new_triangles[new_num_triangles*3+1] = input_triangles[i*3+1];
3228 new_triangles[new_num_triangles*3+2] = input_triangles[i*3+2];
3229 }
3230 else
3231 {
3232 // (0 < 1) && (2 < 0)
3233 new_triangles[new_num_triangles*3] = input_triangles[i*3+2];
3234 new_triangles[new_num_triangles*3+1] = input_triangles[i*3];
3235 new_triangles[new_num_triangles*3+2] = input_triangles[i*3+1];
3236 }
3237 }
3238 else if (input_triangles[i*3+1] < input_triangles[i*3+2])
3239 {
3240 // (1 < 0) && (1 < 2)
3241 new_triangles[new_num_triangles*3] = input_triangles[i*3+1];
3242 new_triangles[new_num_triangles*3+1] = input_triangles[i*3+2];
3243 new_triangles[new_num_triangles*3+2] = input_triangles[i*3];
3244 }
3245 else
3246 {
3247 // (1 < 0) && (2 < 1)
3248 new_triangles[new_num_triangles*3] = input_triangles[i*3+2];
3249 new_triangles[new_num_triangles*3+1] = input_triangles[i*3];
3250 new_triangles[new_num_triangles*3+2] = input_triangles[i*3+1];
3251 }
3252 new_num_triangles++;
3253 }
3254
3255 if (new_num_triangles == 0)
3256 {
3257 llwarns << "Created volume object with 0 faces." << llendl;
3258 return FALSE;
3259 }
3260
3261 typedef std::set<S32*, lessTriangle> triangle_set_t;
3262 triangle_set_t triangle_list;
3263
3264 for (i = 0; i < new_num_triangles; i++)
3265 {
3266 triangle_list.insert(&new_triangles[i*3]);
3267 }
3268
3269 // Sort through the triangle list, and delete duplicates
3270
3271 S32 *prevp = NULL;
3272 S32 *curp = NULL;
3273
3274 S32 *sorted_tris = new S32[new_num_triangles*3];
3275 S32 cur_tri = 0;
3276 for (triangle_set_t::iterator iter = triangle_list.begin(),
3277 end = triangle_list.end();
3278 iter != end; iter++)
3279 {
3280 curp = *iter;
3281 if (!prevp || !equalTriangle(prevp, curp))
3282 {
3283 //llinfos << "Added triangle " << *curp << ":" << *(curp+1) << ":" << *(curp+2) << llendl;
3284 sorted_tris[cur_tri*3] = *curp;
3285 sorted_tris[cur_tri*3+1] = *(curp+1);
3286 sorted_tris[cur_tri*3+2] = *(curp+2);
3287 cur_tri++;
3288 prevp = curp;
3289 }
3290 else
3291 {
3292 //llinfos << "Skipped triangle " << *curp << ":" << *(curp+1) << ":" << *(curp+2) << llendl;
3293 }
3294 }
3295
3296 *output_vertices = new LLVector3[new_num_vertices];
3297 num_output_vertices = new_num_vertices;
3298 for (i = 0; i < new_num_vertices; i++)
3299 {
3300 (*output_vertices)[i] = new_vertices[i];
3301 }
3302
3303 *output_triangles = new S32[cur_tri*3];
3304 num_output_triangles = cur_tri;
3305 memcpy(*output_triangles, sorted_tris, 3*cur_tri*sizeof(S32));
3306
3307 /*
3308 llinfos << "Out vertices: " << num_output_vertices << llendl;
3309 llinfos << "Out triangles: " << num_output_triangles << llendl;
3310 for (i = 0; i < num_output_vertices; i++)
3311 {
3312 llinfos << i << ":" << (*output_vertices)[i] << llendl;
3313 }
3314 for (i = 0; i < num_output_triangles; i++)
3315 {
3316 llinfos << i << ":" << (*output_triangles)[i*3] << ":" << (*output_triangles)[i*3+1] << ":" << (*output_triangles)[i*3+2] << llendl;
3317 }
3318 */
3319
3320 //llinfos << "Out vertices: " << num_output_vertices << llendl;
3321 //llinfos << "Out triangles: " << num_output_triangles << llendl;
3322 delete[] vertex_mapping;
3323 vertex_mapping = NULL;
3324 delete[] new_vertices;
3325 new_vertices = NULL;
3326 delete[] new_triangles;
3327 new_triangles = NULL;
3328 delete[] sorted_tris;
3329 sorted_tris = NULL;
3330 triangle_list.clear();
3331 std::for_each(vertex_list.begin(), vertex_list.end(), DeletePointer());
3332 vertex_list.clear();
3333
3334 return TRUE;
3335}
3336
3337
3338BOOL LLVolumeParams::importFile(FILE *fp)
3339{
3340 //llinfos << "importing volume" << llendl;
3341 const S32 BUFSIZE = 16384;
3342 char buffer[BUFSIZE];
3343 char keyword[256];
3344 keyword[0] = 0;
3345
3346 while (!feof(fp))
3347 {
3348 fgets(buffer, BUFSIZE, fp);
3349 sscanf(buffer, " %s", keyword);
3350 if (!keyword)
3351 {
3352 continue;
3353 }
3354 if (!strcmp("{", keyword))
3355 {
3356 continue;
3357 }
3358 if (!strcmp("}",keyword))
3359 {
3360 break;
3361 }
3362 else if (!strcmp("profile", keyword))
3363 {
3364 mProfileParams.importFile(fp);
3365 }
3366 else if (!strcmp("path",keyword))
3367 {
3368 mPathParams.importFile(fp);
3369 }
3370 else
3371 {
3372 llwarns << "unknown keyword " << keyword << " in volume import" << llendl;
3373 }
3374 }
3375
3376 return TRUE;
3377}
3378
3379BOOL LLVolumeParams::exportFile(FILE *fp) const
3380{
3381 fprintf(fp,"\tshape 0\n");
3382 fprintf(fp,"\t{\n");
3383 mPathParams.exportFile(fp);
3384 mProfileParams.exportFile(fp);
3385 fprintf(fp, "\t}\n");
3386 return TRUE;
3387}
3388
3389
3390BOOL LLVolumeParams::importLegacyStream(std::istream& input_stream)
3391{
3392 //llinfos << "importing volume" << llendl;
3393 const S32 BUFSIZE = 16384;
3394 char buffer[BUFSIZE];
3395 char keyword[256];
3396 keyword[0] = 0;
3397
3398 while (input_stream.good())
3399 {
3400 input_stream.getline(buffer, BUFSIZE);
3401 sscanf(buffer, " %s", keyword);
3402 if (!keyword)
3403 {
3404 continue;
3405 }
3406 if (!strcmp("{", keyword))
3407 {
3408 continue;
3409 }
3410 if (!strcmp("}",keyword))
3411 {
3412 break;
3413 }
3414 else if (!strcmp("profile", keyword))
3415 {
3416 mProfileParams.importLegacyStream(input_stream);
3417 }
3418 else if (!strcmp("path",keyword))
3419 {
3420 mPathParams.importLegacyStream(input_stream);
3421 }
3422 else
3423 {
3424 llwarns << "unknown keyword " << keyword << " in volume import" << llendl;
3425 }
3426 }
3427
3428 return TRUE;
3429}
3430
3431BOOL LLVolumeParams::exportLegacyStream(std::ostream& output_stream) const
3432{
3433 output_stream <<"\tshape 0\n";
3434 output_stream <<"\t{\n";
3435 mPathParams.exportLegacyStream(output_stream);
3436 mProfileParams.exportLegacyStream(output_stream);
3437 output_stream << "\t}\n";
3438 return TRUE;
3439}
3440
3441LLSD LLVolumeParams::asLLSD() const
3442{
3443 LLSD sd = LLSD();
3444 sd["path"] = mPathParams;
3445 sd["profile"] = mProfileParams;
3446 return sd;
3447}
3448
3449bool LLVolumeParams::fromLLSD(LLSD& sd)
3450{
3451 mPathParams.fromLLSD(sd["path"]);
3452 mProfileParams.fromLLSD(sd["profile"]);
3453 return true;
3454}
3455
3456void LLVolumeParams::reduceS(F32 begin, F32 end)
3457{
3458 begin = llclampf(begin);
3459 end = llclampf(end);
3460 if (begin > end)
3461 {
3462 F32 temp = begin;
3463 begin = end;
3464 end = temp;
3465 }
3466 F32 a = mProfileParams.getBegin();
3467 F32 b = mProfileParams.getEnd();
3468 mProfileParams.setBegin(a + begin * (b - a));
3469 mProfileParams.setEnd(a + end * (b - a));
3470}
3471
3472void LLVolumeParams::reduceT(F32 begin, F32 end)
3473{
3474 begin = llclampf(begin);
3475 end = llclampf(end);
3476 if (begin > end)
3477 {
3478 F32 temp = begin;
3479 begin = end;
3480 end = temp;
3481 }
3482 F32 a = mPathParams.getBegin();
3483 F32 b = mPathParams.getEnd();
3484 mPathParams.setBegin(a + begin * (b - a));
3485 mPathParams.setEnd(a + end * (b - a));
3486}
3487
3488BOOL LLVolumeParams::isConvex() const
3489{
3490 // The logic for determining convexity is a little convoluted.
3491
3492 // Do we need to take getTwistBegin into account? DK 08/12/04
3493 if ( mProfileParams.getHollow() != 0.0f
3494 || mPathParams.getTwist() != mPathParams.getTwistBegin() )
3495 {
3496 // hollow or twist gaurantees concavity
3497 return FALSE;
3498 }
3499
3500 F32 profile_length = mProfileParams.getEnd() - mProfileParams.getBegin();
3501 BOOL concave_profile = (profile_length < 1.0f) && (profile_length > 0.5f);
3502 if (concave_profile)
3503 {
3504 // concave profile
3505 return FALSE;
3506 }
3507
3508 U8 path_type = mPathParams.getCurveType();
3509 if ( LL_PCODE_PATH_LINE == path_type )
3510 {
3511 // straight paths with convex profile
3512 return TRUE;
3513 }
3514
3515 F32 path_length = mPathParams.getEnd() - mPathParams.getBegin();
3516 BOOL concave_path = (path_length < 1.0f) && (path_length > 0.5f);
3517 if (concave_path)
3518 {
3519 return FALSE;
3520 }
3521
3522 // we're left with spheres, toroids and tubes
3523 // only the spheres can be convex
3524 U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
3525 if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type )
3526 {
3527 return TRUE;
3528 }
3529
3530 // it's a toroid or tube
3531 return FALSE;
3532}
3533
3534LLFaceID LLVolume::generateFaceMask()
3535{
3536 LLFaceID new_mask = 0x0000;
3537
3538 switch(mProfilep->mParams.getCurveType() & LL_PCODE_PROFILE_MASK)
3539 {
3540 case LL_PCODE_PROFILE_CIRCLE:
3541 case LL_PCODE_PROFILE_CIRCLE_HALF:
3542 new_mask |= LL_FACE_OUTER_SIDE_0;
3543 break;
3544 case LL_PCODE_PROFILE_SQUARE:
3545 {
3546 for(S32 side = (S32)(mProfilep->mParams.getBegin() * 4.f); side < llceil(mProfilep->mParams.getEnd() * 4.f); side++)
3547 {
3548 new_mask |= LL_FACE_OUTER_SIDE_0 << side;
3549 }
3550 }
3551 break;
3552 case LL_PCODE_PROFILE_ISOTRI:
3553 case LL_PCODE_PROFILE_EQUALTRI:
3554 case LL_PCODE_PROFILE_RIGHTTRI:
3555 {
3556 for(S32 side = (S32)(mProfilep->mParams.getBegin() * 3.f); side < llceil(mProfilep->mParams.getEnd() * 3.f); side++)
3557 {
3558 new_mask |= LL_FACE_OUTER_SIDE_0 << side;
3559 }
3560 }
3561 break;
3562 default:
3563 llerrs << "Unknown profile!" << llendl
3564 break;
3565 }
3566
3567 // handle hollow objects
3568 if (mProfilep->isHollow())
3569 {
3570 new_mask |= LL_FACE_INNER_SIDE;
3571 }
3572
3573 // handle open profile curves
3574 if (mProfilep->isOpen())
3575 {
3576 new_mask |= LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END;
3577 }
3578
3579 // handle open path curves
3580 if (mPathp->isOpen())
3581 {
3582 new_mask |= LL_FACE_PATH_BEGIN | LL_FACE_PATH_END;
3583 }
3584
3585 return new_mask;
3586}
3587
3588BOOL LLVolume::isFaceMaskValid(LLFaceID face_mask)
3589{
3590 LLFaceID test_mask = 0;
3591 for(S32 i = 0; i < getNumFaces(); i++)
3592 {
3593 test_mask |= mProfilep->mFaces[i].mFaceID;
3594 }
3595
3596 return test_mask == face_mask;
3597}
3598
3599BOOL LLVolume::isConvex() const
3600{
3601 // mParams.isConvex() may return FALSE even though the final
3602 // geometry is actually convex due to LOD approximations.
3603 // TODO -- provide LLPath and LLProfile with isConvex() methods
3604 // that correctly determine convexity. -- Leviathan
3605 return mParams.isConvex();
3606}
3607
3608
3609std::ostream& operator<<(std::ostream &s, const LLProfileParams &profile_params)
3610{
3611 s << "{type=" << (U32) profile_params.mCurveType;
3612 s << ", begin=" << profile_params.mBegin;
3613 s << ", end=" << profile_params.mEnd;
3614 s << ", hollow=" << profile_params.mHollow;
3615 s << "}";
3616 return s;
3617}
3618
3619
3620std::ostream& operator<<(std::ostream &s, const LLPathParams &path_params)
3621{
3622 s << "{type=" << (U32) path_params.mCurveType;
3623 s << ", begin=" << path_params.mBegin;
3624 s << ", end=" << path_params.mEnd;
3625 s << ", twist=" << path_params.mTwistEnd;
3626 s << ", scale=" << path_params.mScale;
3627 s << ", shear=" << path_params.mShear;
3628 s << ", twist_begin=" << path_params.mTwistBegin;
3629 s << ", radius_offset=" << path_params.mRadiusOffset;
3630 s << ", taper=" << path_params.mTaper;
3631 s << ", revolutions=" << path_params.mRevolutions;
3632 s << ", skew=" << path_params.mSkew;
3633 s << "}";
3634 return s;
3635}
3636
3637
3638std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params)
3639{
3640 s << "{profileparams = " << volume_params.mProfileParams;
3641 s << ", pathparams = " << volume_params.mPathParams;
3642 s << "}";
3643 return s;
3644}
3645
3646
3647std::ostream& operator<<(std::ostream &s, const LLProfile &profile)
3648{
3649 s << " {open=" << (U32) profile.mOpen;
3650 s << ", dirty=" << profile.mDirty;
3651 s << ", totalout=" << profile.mTotalOut;
3652 s << ", total=" << profile.mTotal;
3653 s << "}";
3654 return s;
3655}
3656
3657
3658std::ostream& operator<<(std::ostream &s, const LLPath &path)
3659{
3660 s << "{open=" << (U32) path.mOpen;
3661 s << ", dirty=" << path.mDirty;
3662 s << ", step=" << path.mStep;
3663 s << ", total=" << path.mTotal;
3664 s << "}";
3665 return s;
3666}
3667
3668std::ostream& operator<<(std::ostream &s, const LLVolume &volume)
3669{
3670 s << "{params = " << volume.mParams;
3671 s << ", path = " << *volume.mPathp;
3672 s << ", profile = " << *volume.mProfilep;
3673 s << "}";
3674 return s;
3675}
3676
3677
3678std::ostream& operator<<(std::ostream &s, const LLVolume *volumep)
3679{
3680 s << "{params = " << volumep->mParams;
3681 s << ", path = " << *(volumep->mPathp);
3682 s << ", profile = " << *(volumep->mProfilep);
3683 s << "}";
3684 return s;
3685}
3686
3687
3688LLVolumeFace::LLVolumeFace()
3689{
3690 mTypeMask = 0;
3691 mID = 0;
3692 mBeginS = 0;
3693 mBeginT = 0;
3694 mNumS = 0;
3695 mNumT = 0;
3696}
3697
3698
3699BOOL LLVolumeFace::create()
3700{
3701 if (mTypeMask & CAP_MASK)
3702 {
3703 return createCap();
3704 }
3705 else if ((mTypeMask & END_MASK) || (mTypeMask & SIDE_MASK))
3706 {
3707 return createSide();
3708 }
3709 else
3710 {
3711 llerrs << "Unknown/uninitialized face type!" << llendl;
3712 return FALSE;
3713 }
3714}
3715
3716void LerpPlanarVertex(LLVolumeFace::VertexData& v0,
3717 LLVolumeFace::VertexData& v1,
3718 LLVolumeFace::VertexData& v2,
3719 LLVolumeFace::VertexData& vout,
3720 F32 coef01,
3721 F32 coef02)
3722{
3723 vout.mPosition = v0.mPosition + ((v1.mPosition-v0.mPosition)*coef01)+((v2.mPosition-v0.mPosition)*coef02);
3724 vout.mTexCoord = v0.mTexCoord + ((v1.mTexCoord-v0.mTexCoord)*coef01)+((v2.mTexCoord-v0.mTexCoord)*coef02);
3725 vout.mNormal = v0.mNormal;
3726 vout.mBinormal = v0.mBinormal;
3727}
3728
3729BOOL LLVolumeFace::createUnCutCubeCap()
3730{
3731 const std::vector<LLVolume::Point>& mesh = mVolumep->getMesh();
3732 const std::vector<LLVector3>& profile = mVolumep->getProfile().mProfile;
3733 S32 max_s = mVolumep->getProfile().getTotal();
3734 S32 max_t = mVolumep->getPath().mPath.size();
3735
3736 // S32 i;
3737 S32 num_vertices = 0, num_indices = 0;
3738 S32 grid_size = (profile.size()-1)/4;
3739 S32 quad_count = (grid_size * grid_size);
3740
3741 num_vertices = (grid_size+1)*(grid_size+1);
3742 num_indices = quad_count * 4;
3743
3744 S32 offset = 0;
3745 if (mTypeMask & TOP_MASK)
3746 offset = (max_t-1) * max_s;
3747 else
3748 offset = mBeginS;
3749
3750 VertexData corners[4];
3751 VertexData baseVert;
3752 for(int t = 0; t < 4; t++){
3753 corners[t].mPosition = mesh[offset + (grid_size*t)].mPos;
3754 corners[t].mTexCoord.mV[0] = profile[grid_size*t].mV[0]+0.5f;
3755 corners[t].mTexCoord.mV[1] = 0.5f - profile[grid_size*t].mV[1];
3756 }
3757 baseVert.mNormal =
3758 ((corners[1].mPosition-corners[0].mPosition) %
3759 (corners[2].mPosition-corners[1].mPosition));
3760 baseVert.mNormal.normVec();
3761 if(!(mTypeMask & TOP_MASK)){
3762 baseVert.mNormal *= -1.0f;
3763 }else{
3764 //Swap the UVs on the U(X) axis for top face
3765 LLVector2 swap;
3766 swap = corners[0].mTexCoord;
3767 corners[0].mTexCoord=corners[3].mTexCoord;
3768 corners[3].mTexCoord=swap;
3769 swap = corners[1].mTexCoord;
3770 corners[1].mTexCoord=corners[2].mTexCoord;
3771 corners[2].mTexCoord=swap;
3772 }
3773 baseVert.mBinormal = calc_binormal_from_triangle(
3774 corners[0].mPosition, corners[0].mTexCoord,
3775 corners[1].mPosition, corners[1].mTexCoord,
3776 corners[2].mPosition, corners[2].mTexCoord);
3777 for(int t = 0; t < 4; t++){
3778 corners[t].mBinormal = baseVert.mBinormal;
3779 corners[t].mNormal = baseVert.mNormal;
3780 }
3781
3782 S32 vtop = mVertices.size();
3783// S32 itop = mIndices.size();
3784/// vector_append(mVertices,4);
3785// vector_append(mIndices,4);
3786// LLVector3 new_pt = lerp(pt1, pt2, t_fraction);
3787#if 0
3788 for(int t=0;t<4;t++){
3789 VertexData vd;
3790 vd.mPosition = corners[t].mPosition;
3791 vd.mNormal =
3792 ((corners[(t+1)%4].mPosition-corners[t].mPosition)%
3793 (corners[(t+2)%4].mPosition-corners[(t+1)%4].mPosition));
3794 vd.mNormal.normVec();
3795
3796 if (mTypeMask & TOP_MASK)
3797 vd.mNormal *= -1.0f;
3798 vd.mBinormal = vd.mNormal;
3799 vd.mTexCoord = corners[t].mTexCoord;
3800 mVertices.push_back(vd);
3801 }
3802 int idxs[] = {0,1,2,2,3,0};
3803 if (mTypeMask & TOP_MASK){
3804 for(int i=0;i<6;i++)mIndices.push_back(vtop+idxs[i]);
3805 }else{
3806 for(int i=5;i>=0;i--)mIndices.push_back(vtop+idxs[i]);
3807 }
3808#else
3809 for(int gx = 0;gx<grid_size+1;gx++){
3810 for(int gy = 0;gy<grid_size+1;gy++){
3811 VertexData newVert;
3812 LerpPlanarVertex(
3813 corners[0],
3814 corners[1],
3815 corners[3],
3816 newVert,
3817 (F32)gx/(F32)grid_size,
3818 (F32)gy/(F32)grid_size);
3819 mVertices.push_back(newVert);
3820 }
3821 }
3822 int idxs[] = {0,1,(grid_size+1)+1,(grid_size+1)+1,(grid_size+1),0};
3823 for(int gx = 0;gx<grid_size;gx++){
3824 for(int gy = 0;gy<grid_size;gy++){
3825 if (mTypeMask & TOP_MASK){
3826 for(int i=5;i>=0;i--)mIndices.push_back(vtop+(gy*(grid_size+1))+gx+idxs[i]);
3827 }else{
3828 for(int i=0;i<6;i++)mIndices.push_back(vtop+(gy*(grid_size+1))+gx+idxs[i]);
3829 }
3830 }
3831 }
3832#endif
3833 return TRUE;
3834}
3835
3836
3837BOOL LLVolumeFace::createCap()
3838{
3839 if (!(mTypeMask & HOLLOW_MASK) &&
3840 !(mTypeMask & OPEN_MASK) &&
3841 ((this->mVolumep->getParams().getPathParams().getBegin()==0.0f)&&
3842 (this->mVolumep->getParams().getPathParams().getEnd()==1.0f))&&
3843 (mVolumep->getProfile().mParams.getCurveType()==LL_PCODE_PROFILE_SQUARE &&
3844 mVolumep->getPath().mParams.getCurveType()==LL_PCODE_PATH_LINE)
3845 ){
3846 return createUnCutCubeCap();
3847 }
3848
3849 S32 i;
3850 S32 num_vertices = 0, num_indices = 0;
3851
3852 const std::vector<LLVolume::Point>& mesh = mVolumep->getMesh();
3853 const std::vector<LLVector3>& profile = mVolumep->getProfile().mProfile;
3854
3855 // All types of caps have the same number of vertices and indices
3856 num_vertices = profile.size();
3857 num_indices = (profile.size() - 2)*3;
3858 vector_append(mVertices,num_vertices);
3859 vector_append(mIndices,num_indices);
3860
3861 S32 max_s = mVolumep->getProfile().getTotal();
3862 S32 max_t = mVolumep->getPath().mPath.size();
3863
3864 mCenter.clearVec();
3865
3866 S32 offset = 0;
3867 if (mTypeMask & TOP_MASK)
3868 {
3869 offset = (max_t-1) * max_s;
3870 }
3871 else
3872 {
3873 offset = mBeginS;
3874 }
3875
3876 // Figure out the normal, assume all caps are flat faces.
3877 // Cross product to get normals.
3878
3879 LLVector2 cuv = LLVector2(0,0);
3880
3881 // Copy the vertices into the array
3882 for (i = 0; i < num_vertices; i++)
3883 {
3884
3885 if (mTypeMask & TOP_MASK)
3886 {
3887 mVertices[i].mTexCoord.mV[0] = profile[i].mV[0]+0.5f;
3888 mVertices[i].mTexCoord.mV[1] = profile[i].mV[1]+0.5f;
3889 }
3890 else
3891 {
3892 // Mirror for underside.
3893 mVertices[i].mTexCoord.mV[0] = profile[i].mV[0]+0.5f;
3894 mVertices[i].mTexCoord.mV[1] = 0.5f - profile[i].mV[1];
3895 }
3896
3897 if(i){
3898 //Dont include the first point of the profile in the average
3899 cuv += mVertices[i].mTexCoord;
3900 mCenter += mVertices[i].mPosition = mesh[i + offset].mPos;
3901 }
3902 else mVertices[i].mPosition = mesh[i + offset].mPos;
3903 //mVertices[i].mNormal = normal;
3904 }
3905
3906 mCenter /= (F32)(num_vertices-1);
3907 cuv /= (F32)(num_vertices-1);
3908
3909 LLVector3 binormal = calc_binormal_from_triangle(
3910 mCenter, cuv,
3911 mVertices[0].mPosition, mVertices[0].mTexCoord,
3912 mVertices[1].mPosition, mVertices[1].mTexCoord);
3913 binormal.normVec();
3914
3915 LLVector3 d0;
3916 LLVector3 d1;
3917 LLVector3 normal;
3918
3919 d0 = mCenter-mVertices[0].mPosition;
3920 d1 = mCenter-mVertices[1].mPosition;
3921
3922 normal = (mTypeMask & TOP_MASK) ? (d0%d1) : (d1%d0);
3923 normal.normVec();
3924
3925 VertexData vd;
3926 vd.mPosition = mCenter;
3927 vd.mNormal = normal;
3928 vd.mBinormal = binormal;
3929 vd.mTexCoord = cuv;
3930
3931 if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK))
3932 {
3933 mVertices.push_back(vd);
3934 num_vertices++;
3935 vector_append(mIndices, 3);
3936 }
3937
3938
3939 for (i = 0; i < num_vertices; i++)
3940 {
3941 mVertices[i].mBinormal = binormal;
3942 mVertices[i].mNormal = normal;
3943 }
3944
3945 if (mTypeMask & HOLLOW_MASK)
3946 {
3947 if (mTypeMask & TOP_MASK)
3948 {
3949 // HOLLOW TOP
3950 // Does it matter if it's open or closed? - djs
3951
3952 S32 pt1 = 0, pt2 = num_vertices - 1;
3953 i = 0;
3954 while (pt2 - pt1 > 1)
3955 {
3956 // Use the profile points instead of the mesh, since you want
3957 // the un-transformed profile distances.
3958 LLVector3 p1 = profile[pt1];
3959 LLVector3 p2 = profile[pt2];
3960 LLVector3 pa = profile[pt1+1];
3961 LLVector3 pb = profile[pt2-1];
3962
3963 p1.mV[VZ] = 0.f;
3964 p2.mV[VZ] = 0.f;
3965 pa.mV[VZ] = 0.f;
3966 pb.mV[VZ] = 0.f;
3967
3968 // Use area of triangle to determine backfacing
3969 F32 area_1a2, area_1ba, area_21b, area_2ab;
3970 area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
3971 (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
3972 (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
3973
3974 area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
3975 (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
3976 (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
3977
3978 area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
3979 (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
3980 (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
3981
3982 area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
3983 (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
3984 (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
3985
3986 BOOL use_tri1a2 = TRUE;
3987 BOOL tri_1a2 = TRUE;
3988 BOOL tri_21b = TRUE;
3989
3990 if (area_1a2 < 0)
3991 {
3992 tri_1a2 = FALSE;
3993 }
3994 if (area_2ab < 0)
3995 {
3996 // Can't use, because it contains point b
3997 tri_1a2 = FALSE;
3998 }
3999 if (area_21b < 0)
4000 {
4001 tri_21b = FALSE;
4002 }
4003 if (area_1ba < 0)
4004 {
4005 // Can't use, because it contains point b
4006 tri_21b = FALSE;
4007 }
4008
4009 if (!tri_1a2)
4010 {
4011 use_tri1a2 = FALSE;
4012 }
4013 else if (!tri_21b)
4014 {
4015 use_tri1a2 = TRUE;
4016 }
4017 else
4018 {
4019 LLVector3 d1 = p1 - pa;
4020 LLVector3 d2 = p2 - pb;
4021
4022 if (d1.magVecSquared() < d2.magVecSquared())
4023 {
4024 use_tri1a2 = TRUE;
4025 }
4026 else
4027 {
4028 use_tri1a2 = FALSE;
4029 }
4030 }
4031
4032 if (use_tri1a2)
4033 {
4034 mIndices[i++] = pt1;
4035 mIndices[i++] = pt1 + 1;
4036 mIndices[i++] = pt2;
4037 pt1++;
4038 }
4039 else
4040 {
4041 mIndices[i++] = pt1;
4042 mIndices[i++] = pt2 - 1;
4043 mIndices[i++] = pt2;
4044 pt2--;
4045 }
4046 }
4047 }
4048 else
4049 {
4050 // HOLLOW BOTTOM
4051 // Does it matter if it's open or closed? - djs
4052
4053 llassert(mTypeMask & BOTTOM_MASK);
4054 S32 pt1 = 0, pt2 = num_vertices - 1;
4055
4056 i = 0;
4057 while (pt2 - pt1 > 1)
4058 {
4059 // Use the profile points instead of the mesh, since you want
4060 // the un-transformed profile distances.
4061 LLVector3 p1 = profile[pt1];
4062 LLVector3 p2 = profile[pt2];
4063 LLVector3 pa = profile[pt1+1];
4064 LLVector3 pb = profile[pt2-1];
4065
4066 p1.mV[VZ] = 0.f;
4067 p2.mV[VZ] = 0.f;
4068 pa.mV[VZ] = 0.f;
4069 pb.mV[VZ] = 0.f;
4070
4071 // Use area of triangle to determine backfacing
4072 F32 area_1a2, area_1ba, area_21b, area_2ab;
4073 area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
4074 (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
4075 (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
4076
4077 area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
4078 (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
4079 (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
4080
4081 area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
4082 (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
4083 (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
4084
4085 area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
4086 (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
4087 (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
4088
4089 BOOL use_tri1a2 = TRUE;
4090 BOOL tri_1a2 = TRUE;
4091 BOOL tri_21b = TRUE;
4092
4093 if (area_1a2 < 0)
4094 {
4095 tri_1a2 = FALSE;
4096 }
4097 if (area_2ab < 0)
4098 {
4099 // Can't use, because it contains point b
4100 tri_1a2 = FALSE;
4101 }
4102 if (area_21b < 0)
4103 {
4104 tri_21b = FALSE;
4105 }
4106 if (area_1ba < 0)
4107 {
4108 // Can't use, because it contains point b
4109 tri_21b = FALSE;
4110 }
4111
4112 if (!tri_1a2)
4113 {
4114 use_tri1a2 = FALSE;
4115 }
4116 else if (!tri_21b)
4117 {
4118 use_tri1a2 = TRUE;
4119 }
4120 else
4121 {
4122 LLVector3 d1 = p1 - pa;
4123 LLVector3 d2 = p2 - pb;
4124
4125 if (d1.magVecSquared() < d2.magVecSquared())
4126 {
4127 use_tri1a2 = TRUE;
4128 }
4129 else
4130 {
4131 use_tri1a2 = FALSE;
4132 }
4133 }
4134
4135 // Flipped backfacing from top
4136 if (use_tri1a2)
4137 {
4138 mIndices[i++] = pt1;
4139 mIndices[i++] = pt2;
4140 mIndices[i++] = pt1 + 1;
4141 pt1++;
4142 }
4143 else
4144 {
4145 mIndices[i++] = pt1;
4146 mIndices[i++] = pt2;
4147 mIndices[i++] = pt2 - 1;
4148 pt2--;
4149 }
4150 }
4151 }
4152 }
4153 else
4154 {
4155 // Not hollow, generate the triangle fan.
4156 if (mTypeMask & TOP_MASK)
4157 {
4158 if (mTypeMask & OPEN_MASK)
4159 {
4160 // SOLID OPEN TOP
4161 // Generate indices
4162 // This is a tri-fan, so we reuse the same first point for all triangles.
4163 for (i = 0; i < (num_vertices - 2); i++)
4164 {
4165 mIndices[3*i] = num_vertices - 1;
4166 mIndices[3*i+1] = i;
4167 mIndices[3*i+2] = i + 1;
4168 }
4169 }
4170 else
4171 {
4172 // SOLID CLOSED TOP
4173 for (i = 0; i < (num_vertices - 2); i++)
4174 {
4175 //MSMSM fix these caps but only for the un-cut case
4176 mIndices[3*i] = num_vertices - 1;
4177 mIndices[3*i+1] = i;
4178 mIndices[3*i+2] = i + 1;
4179 }
4180 }
4181 }
4182 else
4183 {
4184 if (mTypeMask & OPEN_MASK)
4185 {
4186 // SOLID OPEN BOTTOM
4187 // Generate indices
4188 // This is a tri-fan, so we reuse the same first point for all triangles.
4189 for (i = 0; i < (num_vertices - 2); i++)
4190 {
4191 mIndices[3*i] = num_vertices - 1;
4192 mIndices[3*i+1] = i + 1;
4193 mIndices[3*i+2] = i;
4194 }
4195 }
4196 else
4197 {
4198 // SOLID CLOSED BOTTOM
4199 for (i = 0; i < (num_vertices - 2); i++)
4200 {
4201 //MSMSM fix these caps but only for the un-cut case
4202 mIndices[3*i] = num_vertices - 1;
4203 mIndices[3*i+1] = i + 1;
4204 mIndices[3*i+2] = i;
4205 }
4206 }
4207 }
4208 }
4209 return TRUE;
4210}
4211
4212
4213BOOL LLVolumeFace::createSide()
4214{
4215 BOOL flat = mTypeMask & FLAT_MASK;
4216 S32 num_vertices, num_indices;
4217
4218
4219 const std::vector<LLVolume::Point>& mesh = mVolumep->getMesh();
4220 const std::vector<LLVector3>& profile = mVolumep->getProfile().mProfile;
4221 const std::vector<LLPath::PathPt>& path_data = mVolumep->getPath().mPath;
4222
4223 S32 max_s = mVolumep->getProfile().getTotal();
4224
4225 S32 s, t, i;
4226 F32 ss, tt;
4227
4228 num_vertices = mNumS*mNumT;
4229 num_indices = (mNumS-1)*(mNumT-1)*6;
4230 vector_append(mVertices,num_vertices);
4231 vector_append(mIndices,num_indices);
4232 vector_append(mEdge, num_indices);
4233
4234 mCenter.clearVec();
4235
4236 S32 begin_stex = llfloor( profile[mBeginS].mV[2] );
4237 S32 num_s = ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2) ? mNumS/2 : mNumS;
4238
4239 S32 cur_vertex = 0;
4240 // Copy the vertices into the array
4241 for (t = mBeginT; t < mBeginT + mNumT; t++)
4242 {
4243 tt = path_data[t].mTexT;
4244 for (s = 0; s < num_s; s++)
4245 {
4246 if (mTypeMask & END_MASK)
4247 {
4248 if (s)
4249 {
4250 ss = 1.f;
4251 }
4252 else
4253 {
4254 ss = 0.f;
4255 }
4256 }
4257 else
4258 {
4259 // Get s value for tex-coord.
4260 if (!flat)
4261 {
4262 ss = profile[mBeginS + s].mV[2];
4263 }
4264 else
4265 {
4266 ss = profile[mBeginS + s].mV[2] - begin_stex;
4267 }
4268 }
4269
4270 // Check to see if this triangle wraps around the array.
4271 if (mBeginS + s >= max_s)
4272 {
4273 // We're wrapping
4274 i = mBeginS + s + max_s*(t-1);
4275 }
4276 else
4277 {
4278 i = mBeginS + s + max_s*t;
4279 }
4280
4281 mCenter += mVertices[cur_vertex].mPosition = mesh[i].mPos;
4282 mVertices[cur_vertex].mTexCoord = LLVector2(ss,tt);
4283
4284 mVertices[cur_vertex].mNormal = LLVector3(0,0,0);
4285 mVertices[cur_vertex].mBinormal = LLVector3(0,0,0);
4286
4287 cur_vertex++;
4288
4289 if ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2 && s > 0)
4290 {
4291 mCenter += mVertices[cur_vertex].mPosition = mesh[i].mPos;
4292 mVertices[cur_vertex].mTexCoord = LLVector2(ss,tt);
4293
4294 mVertices[cur_vertex].mNormal = LLVector3(0,0,0);
4295 mVertices[cur_vertex].mBinormal = LLVector3(0,0,0);
4296 cur_vertex++;
4297 }
4298 }
4299
4300 if ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2)
4301 {
4302 if (mTypeMask & OPEN_MASK)
4303 {
4304 s = num_s-1;
4305 }
4306 else
4307 {
4308 s = 0;
4309 }
4310
4311 i = mBeginS + s + max_s*t;
4312 ss = profile[mBeginS + s].mV[2] - begin_stex;
4313 mCenter += mVertices[cur_vertex].mPosition = mesh[i].mPos;
4314 mVertices[cur_vertex].mTexCoord = LLVector2(ss,tt);
4315
4316 mVertices[cur_vertex].mNormal = LLVector3(0,0,0);
4317 mVertices[cur_vertex].mBinormal = LLVector3(0,0,0);
4318 cur_vertex++;
4319 }
4320 }
4321 mCenter /= (F32)num_vertices;
4322
4323 S32 cur_index = 0;
4324 S32 cur_edge = 0;
4325 BOOL flat_face = mTypeMask & FLAT_MASK;
4326
4327 // Now we generate the indices.
4328 for (t = 0; t < (mNumT-1); t++)
4329 {
4330 for (s = 0; s < (mNumS-1); s++)
4331 {
4332 mIndices[cur_index++] = s + mNumS*t; //bottom left
4333 mIndices[cur_index++] = s+1 + mNumS*(t+1); //top right
4334 mIndices[cur_index++] = s + mNumS*(t+1); //top left
4335 mIndices[cur_index++] = s + mNumS*t; //bottom left
4336 mIndices[cur_index++] = s+1 + mNumS*t; //bottom right
4337 mIndices[cur_index++] = s+1 + mNumS*(t+1); //top right
4338
4339 mEdge[cur_edge++] = (mNumS-1)*2*t+s*2+1; //bottom left/top right neighbor face
4340 if (t < mNumT-2) { //top right/top left neighbor face
4341 mEdge[cur_edge++] = (mNumS-1)*2*(t+1)+s*2+1;
4342 }
4343 else if (mNumT <= 3 || mVolumep->getPath().isOpen() == TRUE) { //no neighbor
4344 mEdge[cur_edge++] = -1;
4345 }
4346 else { //wrap on T
4347 mEdge[cur_edge++] = s*2+1;
4348 }
4349 if (s > 0) { //top left/bottom left neighbor face
4350 mEdge[cur_edge++] = (mNumS-1)*2*t+s*2-1;
4351 }
4352 else if (flat_face || mVolumep->getProfile().isOpen() == TRUE) { //no neighbor
4353 mEdge[cur_edge++] = -1;
4354 }
4355 else { //wrap on S
4356 mEdge[cur_edge++] = (mNumS-1)*2*t+(mNumS-2)*2+1;
4357 }
4358
4359 if (t > 0) { //bottom left/bottom right neighbor face
4360 mEdge[cur_edge++] = (mNumS-1)*2*(t-1)+s*2;
4361 }
4362 else if (mNumT <= 3 || mVolumep->getPath().isOpen() == TRUE) { //no neighbor
4363 mEdge[cur_edge++] = -1;
4364 }
4365 else { //wrap on T
4366 mEdge[cur_edge++] = (mNumS-1)*2*(mNumT-2)+s*2;
4367 }
4368 if (s < mNumS-2) { //bottom right/top right neighbor face
4369 mEdge[cur_edge++] = (mNumS-1)*2*t+(s+1)*2;
4370 }
4371 else if (flat_face || mVolumep->getProfile().isOpen() == TRUE) { //no neighbor
4372 mEdge[cur_edge++] = -1;
4373 }
4374 else { //wrap on S
4375 mEdge[cur_edge++] = (mNumS-1)*2*t;
4376 }
4377 mEdge[cur_edge++] = (mNumS-1)*2*t+s*2; //top right/bottom left neighbor face
4378 }
4379 }
4380
4381
4382 //generate normals
4383 for (U32 i = 0; i < mIndices.size()/3; i++) { //for each triangle
4384 const VertexData& v0 = mVertices[mIndices[i*3+0]];
4385 const VertexData& v1 = mVertices[mIndices[i*3+1]];
4386 const VertexData& v2 = mVertices[mIndices[i*3+2]];
4387
4388 //calculate triangle normal
4389 LLVector3 norm = (v0.mPosition-v1.mPosition)%
4390 (v0.mPosition-v2.mPosition);
4391
4392 //calculate binormal
4393 LLVector3 binorm = calc_binormal_from_triangle(v0.mPosition, v0.mTexCoord,
4394 v1.mPosition, v1.mTexCoord,
4395 v2.mPosition, v2.mTexCoord);
4396
4397 for (U32 j = 0; j < 3; j++) { //add triangle normal to vertices
4398 mVertices[mIndices[i*3+j]].mNormal += norm; // * (weight_sum - d[j])/weight_sum;
4399 mVertices[mIndices[i*3+j]].mBinormal += binorm; // * (weight_sum - d[j])/weight_sum;
4400 }
4401
4402 //even out quad contributions
4403 if (i % 2 == 0) {
4404 mVertices[mIndices[i*3+2]].mNormal += norm;
4405 mVertices[mIndices[i*3+2]].mBinormal += binorm;
4406 }
4407 else {
4408 mVertices[mIndices[i*3+1]].mNormal += norm;
4409 mVertices[mIndices[i*3+1]].mBinormal += binorm;
4410 }
4411 }
4412
4413 if (mVolumep->getPath().isOpen() == FALSE) { //wrap normals on T
4414 for (S32 i = 0; i < mNumS; i++) {
4415 LLVector3 norm = mVertices[i].mNormal + mVertices[mNumS*(mNumT-1)+i].mNormal;
4416 mVertices[i].mNormal = norm;
4417 mVertices[mNumS*(mNumT-1)+i].mNormal = norm;
4418 }
4419 }
4420
4421 if (mVolumep->getProfile().isOpen() == FALSE) { //wrap normals on S
4422 for (S32 i = 0; i < mNumT; i++) {
4423 LLVector3 norm = mVertices[mNumS*i].mNormal + mVertices[mNumS*i+mNumS-1].mNormal;
4424 mVertices[mNumS * i].mNormal = norm;
4425 mVertices[mNumS * i+mNumS-1].mNormal = norm;
4426 }
4427 }
4428
4429 if (mVolumep->getPathType() == LL_PCODE_PATH_CIRCLE && ((mVolumep->getProfileType() & LL_PCODE_PROFILE_MASK) == LL_PCODE_PROFILE_CIRCLE_HALF)) {
4430 if ((mVertices[0].mPosition - mVertices[mNumS*(mNumT-2)].mPosition).magVecSquared() < 0.000001f)
4431 { //all lower S have same normal
4432 for (S32 i = 0; i < mNumT; i++) {
4433 mVertices[mNumS*i].mNormal = LLVector3(1,0,0);
4434 }
4435 }
4436
4437 if ((mVertices[mNumS-1].mPosition - mVertices[mNumS*(mNumT-2)+mNumS-1].mPosition).magVecSquared() < 0.000001f)
4438 { //all upper T have same normal
4439 for (S32 i = 0; i < mNumT; i++) {
4440 mVertices[mNumS*i+mNumS-1].mNormal = LLVector3(-1,0,0);
4441 }
4442 }
4443 }
4444
4445 //this loop would LOVE OpenMP
4446 LLVector3 min = mVolumep->mBounds[0] - mVolumep->mBounds[1];
4447 LLVector3 max = mVolumep->mBounds[0] + mVolumep->mBounds[1];
4448
4449 if (min == max && min == LLVector3(512,512,512))
4450 {
4451 min = max = mVertices[0].mPosition;
4452 }
4453
4454 for (U32 i = 0; i < mVertices.size(); i++) {
4455 mVertices[i].mNormal.normVec();
4456 mVertices[i].mBinormal.normVec();
4457
4458 for (U32 j = 0; j < 3; j++) {
4459 if (mVertices[i].mPosition.mV[j] > max.mV[j]) {
4460 max.mV[j] = mVertices[i].mPosition.mV[j];
4461 }
4462 if (mVertices[i].mPosition.mV[j] < min.mV[j]) {
4463 min.mV[j] = mVertices[i].mPosition.mV[j];
4464 }
4465 }
4466 }
4467
4468 mVolumep->mBounds[0] = (min + max) * 0.5f; //center
4469 mVolumep->mBounds[1] = (max - min) * 0.5f; //half-height
4470
4471 return TRUE;
4472}
4473
4474// Static
4475BOOL LLVolumeFace::updateColors(LLColor4U *old_colors, const S32 num_old, const LLVolumeFace &old_vf,
4476 LLStrider<LLColor4U> &new_colors, const S32 num_new, const LLVolumeFace &new_vf)
4477{
4478 if (new_vf.mTypeMask & CAP_MASK)
4479 {
4480 // These aren't interpolated correctly. Need to fix when shadows go in...
4481 F32 ratio = (F32)num_old / (F32)num_new;
4482 S32 v = 0;
4483 for (v = 0; v < num_new; v++)
4484 {
4485 new_colors[v] = old_colors[(S32)(v*ratio)];
4486 }
4487 return FALSE;
4488 }
4489 else if (new_vf.mTypeMask & END_MASK)
4490 {
4491 // These aren't interpolated correctly. Need to fix when shadows go in...
4492 F32 ratio = (F32)num_old / (F32)num_new;
4493 S32 v = 0;
4494 for (v = 0; v < num_new; v++)
4495 {
4496 new_colors[v] = old_colors[(S32)(v*ratio)];
4497 }
4498 return FALSE;
4499 }
4500 else if (new_vf.mTypeMask & SIDE_MASK)
4501 {
4502 S32 s, t;
4503 F32 s_ratio = (F32)old_vf.mNumS / (F32)new_vf.mNumS;
4504 F32 t_ratio = (F32)old_vf.mNumT / (F32)new_vf.mNumT;
4505
4506 S32 v = 0;
4507 for (t = 0; t < new_vf.mNumT; t++)
4508 {
4509 F32 t_frac = t * t_ratio;
4510 S32 old_t = (S32)t_frac;
4511 S32 t_target = llmin(old_t + 1, (old_vf.mNumT - 1));
4512 t_frac -= old_t;
4513 for (s = 0; s < new_vf.mNumS; s++)
4514 {
4515 F32 s_frac = s * s_ratio;
4516 S32 old_s = (S32)s_frac;
4517 S32 s_target = llmin(old_s + 1, (old_vf.mNumS - 1));
4518 s_frac -= old_s;
4519
4520 // Interpolate along s, then along t.
4521 LLColor4U s_interp0 = old_colors[old_t * old_vf.mNumS + old_s].multAll(1.f - s_frac).addClampMax(old_colors[old_t * old_vf.mNumS + s_target].multAll(s_frac));
4522 LLColor4U s_interp1 = old_colors[t_target * old_vf.mNumS + old_s].multAll(1.f - s_frac).addClampMax(old_colors[t_target * old_vf.mNumS + s_target].multAll(s_frac));
4523 new_colors[v] = s_interp0.multAll(1.f - t_frac).addClampMax(s_interp1.multAll(t_frac));
4524 v++;
4525 }
4526 }
4527 }
4528 else
4529 {
4530 llerrs << "Unknown/uninitialized face type!" << llendl;
4531 return FALSE;
4532 }
4533 return TRUE;
4534}
4535
4536
4537// Finds binormal based on three vertices with texture coordinates.
4538// Fills in dummy values if the triangle has degenerate texture coordinates.
4539LLVector3 calc_binormal_from_triangle(
4540 const LLVector3& pos0,
4541 const LLVector2& tex0,
4542 const LLVector3& pos1,
4543 const LLVector2& tex1,
4544 const LLVector3& pos2,
4545 const LLVector2& tex2)
4546{
4547 LLVector3 rx0( pos0.mV[VX], tex0.mV[VX], tex0.mV[VY] );
4548 LLVector3 rx1( pos1.mV[VX], tex1.mV[VX], tex1.mV[VY] );
4549 LLVector3 rx2( pos2.mV[VX], tex2.mV[VX], tex2.mV[VY] );
4550
4551 LLVector3 ry0( pos0.mV[VY], tex0.mV[VX], tex0.mV[VY] );
4552 LLVector3 ry1( pos1.mV[VY], tex1.mV[VX], tex1.mV[VY] );
4553 LLVector3 ry2( pos2.mV[VY], tex2.mV[VX], tex2.mV[VY] );
4554
4555 LLVector3 rz0( pos0.mV[VZ], tex0.mV[VX], tex0.mV[VY] );
4556 LLVector3 rz1( pos1.mV[VZ], tex1.mV[VX], tex1.mV[VY] );
4557 LLVector3 rz2( pos2.mV[VZ], tex2.mV[VX], tex2.mV[VY] );
4558
4559 LLVector3 r0 = (rx0 - rx1) % (rx0 - rx2);
4560 LLVector3 r1 = (ry0 - ry1) % (ry0 - ry2);
4561 LLVector3 r2 = (rz0 - rz1) % (rz0 - rz2);
4562
4563 if( r0.mV[VX] && r1.mV[VX] && r2.mV[VX] )
4564 {
4565 LLVector3 binormal(
4566 -r0.mV[VZ] / r0.mV[VX],
4567 -r1.mV[VZ] / r1.mV[VX],
4568 -r2.mV[VZ] / r2.mV[VX]);
4569 //binormal.normVec();
4570 return binormal;
4571 }
4572 else
4573 {
4574 return LLVector3( 0, 1 , 0 );
4575 }
4576}