PageRenderTime 147ms CodeModel.GetById 12ms app.highlight 121ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/llmath/llvolume.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2696 lines | 2066 code | 431 blank | 199 comment | 333 complexity | d1c2743c2bd0c46b7febcb451a45965c MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/** 
   2
   3 * @file llvolume.cpp
   4 *
   5 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27#include "linden_common.h"
  28#include "llmemory.h"
  29#include "llmath.h"
  30
  31#include <set>
  32#if !LL_WINDOWS
  33#include <stdint.h>
  34#endif
  35#include <cmath>
  36
  37#include "llerror.h"
  38#include "llmemtype.h"
  39
  40#include "llvolumemgr.h"
  41#include "v2math.h"
  42#include "v3math.h"
  43#include "v4math.h"
  44#include "m4math.h"
  45#include "m3math.h"
  46#include "llmatrix3a.h"
  47#include "lloctree.h"
  48#include "lldarray.h"
  49#include "llvolume.h"
  50#include "llvolumeoctree.h"
  51#include "llstl.h"
  52#include "llsdserialize.h"
  53#include "llvector4a.h"
  54#include "llmatrix4a.h"
  55#include "lltimer.h"
  56
  57#define DEBUG_SILHOUETTE_BINORMALS 0
  58#define DEBUG_SILHOUETTE_NORMALS 0 // TomY: Use this to display normals using the silhouette
  59#define DEBUG_SILHOUETTE_EDGE_MAP 0 // DaveP: Use this to display edge map using the silhouette
  60
  61const F32 CUT_MIN = 0.f;
  62const F32 CUT_MAX = 1.f;
  63const F32 MIN_CUT_DELTA = 0.02f;
  64
  65const F32 HOLLOW_MIN = 0.f;
  66const F32 HOLLOW_MAX = 0.95f;
  67const F32 HOLLOW_MAX_SQUARE	= 0.7f;
  68
  69const F32 TWIST_MIN = -1.f;
  70const F32 TWIST_MAX =  1.f;
  71
  72const F32 RATIO_MIN = 0.f;
  73const F32 RATIO_MAX = 2.f; // Tom Y: Inverted sense here: 0 = top taper, 2 = bottom taper
  74
  75const F32 HOLE_X_MIN= 0.05f;
  76const F32 HOLE_X_MAX= 1.0f;
  77
  78const F32 HOLE_Y_MIN= 0.05f;
  79const F32 HOLE_Y_MAX= 0.5f;
  80
  81const F32 SHEAR_MIN = -0.5f;
  82const F32 SHEAR_MAX =  0.5f;
  83
  84const F32 REV_MIN = 1.f;
  85const F32 REV_MAX = 4.f;
  86
  87const F32 TAPER_MIN = -1.f;
  88const F32 TAPER_MAX =  1.f;
  89
  90const F32 SKEW_MIN	= -0.95f;
  91const F32 SKEW_MAX	=  0.95f;
  92
  93const F32 SCULPT_MIN_AREA = 0.002f;
  94const S32 SCULPT_MIN_AREA_DETAIL = 1;
  95
  96extern BOOL gDebugGL;
  97
  98void assert_aligned(void* ptr, uintptr_t alignment)
  99{
 100#if 0
 101	uintptr_t t = (uintptr_t) ptr;
 102	if (t%alignment != 0)
 103	{
 104		llerrs << "Alignment check failed." << llendl;
 105	}
 106#endif
 107}
 108
 109BOOL check_same_clock_dir( const LLVector3& pt1, const LLVector3& pt2, const LLVector3& pt3, const LLVector3& norm)
 110{    
 111	LLVector3 test = (pt2-pt1)%(pt3-pt2);
 112
 113	//answer
 114	if(test * norm < 0) 
 115	{
 116		return FALSE;
 117	}
 118	else 
 119	{
 120		return TRUE;
 121	}
 122} 
 123
 124BOOL LLLineSegmentBoxIntersect(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size)
 125{
 126	return LLLineSegmentBoxIntersect(start.mV, end.mV, center.mV, size.mV);
 127}
 128
 129BOOL LLLineSegmentBoxIntersect(const F32* start, const F32* end, const F32* center, const F32* size)
 130{
 131	F32 fAWdU[3];
 132	F32 dir[3];
 133	F32 diff[3];
 134
 135	for (U32 i = 0; i < 3; i++)
 136	{
 137		dir[i] = 0.5f * (end[i] - start[i]);
 138		diff[i] = (0.5f * (end[i] + start[i])) - center[i];
 139		fAWdU[i] = fabsf(dir[i]);
 140		if(fabsf(diff[i])>size[i] + fAWdU[i]) return false;
 141	}
 142
 143	float f;
 144	f = dir[1] * diff[2] - dir[2] * diff[1];    if(fabsf(f)>size[1]*fAWdU[2] + size[2]*fAWdU[1])  return false;
 145	f = dir[2] * diff[0] - dir[0] * diff[2];    if(fabsf(f)>size[0]*fAWdU[2] + size[2]*fAWdU[0])  return false;
 146	f = dir[0] * diff[1] - dir[1] * diff[0];    if(fabsf(f)>size[0]*fAWdU[1] + size[1]*fAWdU[0])  return false;
 147	
 148	return true;
 149}
 150
 151
 152
 153// intersect test between triangle vert0, vert1, vert2 and a ray from orig in direction dir.
 154// returns TRUE if intersecting and returns barycentric coordinates in intersection_a, intersection_b,
 155// and returns the intersection point along dir in intersection_t.
 156
 157// Moller-Trumbore algorithm
 158BOOL LLTriangleRayIntersect(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir,
 159							F32& intersection_a, F32& intersection_b, F32& intersection_t)
 160{
 161	
 162	/* find vectors for two edges sharing vert0 */
 163	LLVector4a edge1;
 164	edge1.setSub(vert1, vert0);
 165	
 166	LLVector4a edge2;
 167	edge2.setSub(vert2, vert0);
 168
 169	/* begin calculating determinant - also used to calculate U parameter */
 170	LLVector4a pvec;
 171	pvec.setCross3(dir, edge2);
 172
 173	/* if determinant is near zero, ray lies in plane of triangle */
 174	LLVector4a det;
 175	det.setAllDot3(edge1, pvec);
 176	
 177	if (det.greaterEqual(LLVector4a::getEpsilon()).getGatheredBits() & 0x7)
 178	{
 179		/* calculate distance from vert0 to ray origin */
 180		LLVector4a tvec;
 181		tvec.setSub(orig, vert0);
 182
 183		/* calculate U parameter and test bounds */
 184		LLVector4a u;
 185		u.setAllDot3(tvec,pvec);
 186
 187		if ((u.greaterEqual(LLVector4a::getZero()).getGatheredBits() & 0x7) &&
 188			(u.lessEqual(det).getGatheredBits() & 0x7))
 189		{
 190			/* prepare to test V parameter */
 191			LLVector4a qvec;
 192			qvec.setCross3(tvec, edge1);
 193			
 194			/* calculate V parameter and test bounds */
 195			LLVector4a v;
 196			v.setAllDot3(dir, qvec);
 197
 198			
 199			//if (!(v < 0.f || u + v > det))
 200
 201			LLVector4a sum_uv;
 202			sum_uv.setAdd(u, v);
 203
 204			S32 v_gequal = v.greaterEqual(LLVector4a::getZero()).getGatheredBits() & 0x7;
 205			S32 sum_lequal = sum_uv.lessEqual(det).getGatheredBits() & 0x7;
 206
 207			if (v_gequal  && sum_lequal)
 208			{
 209				/* calculate t, scale parameters, ray intersects triangle */
 210				LLVector4a t;
 211				t.setAllDot3(edge2,qvec);
 212
 213				t.div(det);
 214				u.div(det);
 215				v.div(det);
 216				
 217				intersection_a = u[0];
 218				intersection_b = v[0];
 219				intersection_t = t[0];
 220				return TRUE;
 221			}
 222		}
 223	}
 224		
 225	return FALSE;
 226} 
 227
 228BOOL LLTriangleRayIntersectTwoSided(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir,
 229							F32& intersection_a, F32& intersection_b, F32& intersection_t)
 230{
 231	F32 u, v, t;
 232	
 233	/* find vectors for two edges sharing vert0 */
 234	LLVector4a edge1;
 235	edge1.setSub(vert1, vert0);
 236	
 237	
 238	LLVector4a edge2;
 239	edge2.setSub(vert2, vert0);
 240
 241	/* begin calculating determinant - also used to calculate U parameter */
 242	LLVector4a pvec;
 243	pvec.setCross3(dir, edge2);
 244
 245	/* if determinant is near zero, ray lies in plane of triangle */
 246	F32 det = edge1.dot3(pvec).getF32();
 247
 248	
 249	if (det > -F_APPROXIMATELY_ZERO && det < F_APPROXIMATELY_ZERO)
 250	{
 251		return FALSE;
 252	}
 253
 254	F32 inv_det = 1.f / det;
 255
 256	/* calculate distance from vert0 to ray origin */
 257	LLVector4a tvec;
 258	tvec.setSub(orig, vert0);
 259	
 260	/* calculate U parameter and test bounds */
 261	u = (tvec.dot3(pvec).getF32()) * inv_det;
 262	if (u < 0.f || u > 1.f)
 263	{
 264		return FALSE;
 265	}
 266
 267	/* prepare to test V parameter */
 268	tvec.sub(edge1);
 269		
 270	/* calculate V parameter and test bounds */
 271	v = (dir.dot3(tvec).getF32()) * inv_det;
 272	
 273	if (v < 0.f || u + v > 1.f)
 274	{
 275		return FALSE;
 276	}
 277
 278	/* calculate t, ray intersects triangle */
 279	t = (edge2.dot3(tvec).getF32()) * inv_det;
 280	
 281	intersection_a = u;
 282	intersection_b = v;
 283	intersection_t = t;
 284	
 285	
 286	return TRUE;
 287} 
 288
 289//helper for non-aligned vectors
 290BOOL LLTriangleRayIntersect(const LLVector3& vert0, const LLVector3& vert1, const LLVector3& vert2, const LLVector3& orig, const LLVector3& dir,
 291							F32& intersection_a, F32& intersection_b, F32& intersection_t, BOOL two_sided)
 292{
 293	LLVector4a vert0a, vert1a, vert2a, origa, dira;
 294	vert0a.load3(vert0.mV);
 295	vert1a.load3(vert1.mV);
 296	vert2a.load3(vert2.mV);
 297	origa.load3(orig.mV);
 298	dira.load3(dir.mV);
 299
 300	if (two_sided)
 301	{
 302		return LLTriangleRayIntersectTwoSided(vert0a, vert1a, vert2a, origa, dira, 
 303				intersection_a, intersection_b, intersection_t);
 304	}
 305	else
 306	{
 307		return LLTriangleRayIntersect(vert0a, vert1a, vert2a, origa, dira, 
 308				intersection_a, intersection_b, intersection_t);
 309	}
 310}
 311
 312class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle>
 313{
 314public:
 315	const LLVolumeFace* mFace;
 316
 317	LLVolumeOctreeRebound(const LLVolumeFace* face)
 318	{
 319		mFace = face;
 320	}
 321
 322	virtual void visit(const LLOctreeNode<LLVolumeTriangle>* branch)
 323	{ //this is a depth first traversal, so it's safe to assum all children have complete
 324		//bounding data
 325
 326		LLVolumeOctreeListener* node = (LLVolumeOctreeListener*) branch->getListener(0);
 327
 328		LLVector4a& min = node->mExtents[0];
 329		LLVector4a& max = node->mExtents[1];
 330
 331		if (!branch->getData().empty())
 332		{ //node has data, find AABB that binds data set
 333			const LLVolumeTriangle* tri = *(branch->getData().begin());
 334			
 335			//initialize min/max to first available vertex
 336			min = *(tri->mV[0]);
 337			max = *(tri->mV[0]);
 338			
 339			for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter = 
 340				branch->getData().begin(); iter != branch->getData().end(); ++iter)
 341			{ //for each triangle in node
 342
 343				//stretch by triangles in node
 344				tri = *iter;
 345				
 346				min.setMin(min, *tri->mV[0]);
 347				min.setMin(min, *tri->mV[1]);
 348				min.setMin(min, *tri->mV[2]);
 349
 350				max.setMax(max, *tri->mV[0]);
 351				max.setMax(max, *tri->mV[1]);
 352				max.setMax(max, *tri->mV[2]);
 353			}
 354		}
 355		else if (!branch->getChildren().empty())
 356		{ //no data, but child nodes exist
 357			LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(0)->getListener(0);
 358
 359			//initialize min/max to extents of first child
 360			min = child->mExtents[0];
 361			max = child->mExtents[1];
 362		}
 363		else
 364		{
 365			llerrs << "Empty leaf" << llendl;
 366		}
 367
 368		for (S32 i = 0; i < branch->getChildCount(); ++i)
 369		{  //stretch by child extents
 370			LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(i)->getListener(0);
 371			min.setMin(min, child->mExtents[0]);
 372			max.setMax(max, child->mExtents[1]);
 373		}
 374
 375		node->mBounds[0].setAdd(min, max);
 376		node->mBounds[0].mul(0.5f);
 377
 378		node->mBounds[1].setSub(max,min);
 379		node->mBounds[1].mul(0.5f);
 380	}
 381};
 382
 383//-------------------------------------------------------------------
 384// statics
 385//-------------------------------------------------------------------
 386
 387
 388//----------------------------------------------------
 389
 390LLProfile::Face* LLProfile::addCap(S16 faceID)
 391{
 392	LLMemType m1(LLMemType::MTYPE_VOLUME);
 393	
 394	Face *face   = vector_append(mFaces, 1);
 395	
 396	face->mIndex = 0;
 397	face->mCount = mTotal;
 398	face->mScaleU= 1.0f;
 399	face->mCap   = TRUE;
 400	face->mFaceID = faceID;
 401	return face;
 402}
 403
 404LLProfile::Face* LLProfile::addFace(S32 i, S32 count, F32 scaleU, S16 faceID, BOOL flat)
 405{
 406	LLMemType m1(LLMemType::MTYPE_VOLUME);
 407	
 408	Face *face   = vector_append(mFaces, 1);
 409	
 410	face->mIndex = i;
 411	face->mCount = count;
 412	face->mScaleU= scaleU;
 413
 414	face->mFlat = flat;
 415	face->mCap   = FALSE;
 416	face->mFaceID = faceID;
 417	return face;
 418}
 419
 420//static
 421S32 LLProfile::getNumNGonPoints(const LLProfileParams& params, S32 sides, F32 offset, F32 bevel, F32 ang_scale, S32 split)
 422{ // this is basically LLProfile::genNGon stripped down to only the operations that influence the number of points
 423	LLMemType m1(LLMemType::MTYPE_VOLUME);
 424	S32 np = 0;
 425
 426	// Generate an n-sided "circular" path.
 427	// 0 is (1,0), and we go counter-clockwise along a circular path from there.
 428	F32 t, t_step, t_first, t_fraction;
 429	
 430	F32 begin  = params.getBegin();
 431	F32 end    = params.getEnd();
 432
 433	t_step = 1.0f / sides;
 434	
 435	t_first = floor(begin * sides) / (F32)sides;
 436
 437	// pt1 is the first point on the fractional face.
 438	// Starting t and ang values for the first face
 439	t = t_first;
 440	
 441	// Increment to the next point.
 442	// pt2 is the end point on the fractional face
 443	t += t_step;
 444	
 445	t_fraction = (begin - t_first)*sides;
 446
 447	// Only use if it's not almost exactly on an edge.
 448	if (t_fraction < 0.9999f)
 449	{
 450		np++;
 451	}
 452
 453	// There's lots of potential here for floating point error to generate unneeded extra points - DJS 04/05/02
 454	while (t < end)
 455	{
 456		// Iterate through all the integer steps of t.
 457		np++;
 458
 459		t += t_step;
 460	}
 461
 462	t_fraction = (end - (t - t_step))*sides;
 463
 464	// Find the fraction that we need to add to the end point.
 465	t_fraction = (end - (t - t_step))*sides;
 466	if (t_fraction > 0.0001f)
 467	{
 468		np++;
 469	}
 470
 471	// If we're sliced, the profile is open.
 472	if ((end - begin)*ang_scale < 0.99f)
 473	{
 474		if (params.getHollow() <= 0)
 475		{
 476			// put center point if not hollow.
 477			np++;
 478		}
 479	}
 480	
 481	return np;
 482}
 483
 484// What is the bevel parameter used for? - DJS 04/05/02
 485// Bevel parameter is currently unused but presumedly would support
 486// filleted and chamfered corners
 487void LLProfile::genNGon(const LLProfileParams& params, S32 sides, F32 offset, F32 bevel, F32 ang_scale, S32 split)
 488{
 489	LLMemType m1(LLMemType::MTYPE_VOLUME);
 490	
 491	// Generate an n-sided "circular" path.
 492	// 0 is (1,0), and we go counter-clockwise along a circular path from there.
 493	const F32 tableScale[] = { 1, 1, 1, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f };
 494	F32 scale = 0.5f;
 495	F32 t, t_step, t_first, t_fraction, ang, ang_step;
 496	LLVector3 pt1,pt2;
 497
 498	F32 begin  = params.getBegin();
 499	F32 end    = params.getEnd();
 500
 501	t_step = 1.0f / sides;
 502	ang_step = 2.0f*F_PI*t_step*ang_scale;
 503
 504	// Scale to have size "match" scale.  Compensates to get object to generally fill bounding box.
 505
 506	S32 total_sides = llround(sides / ang_scale);	// Total number of sides all around
 507
 508	if (total_sides < 8)
 509	{
 510		scale = tableScale[total_sides];
 511	}
 512
 513	t_first = floor(begin * sides) / (F32)sides;
 514
 515	// pt1 is the first point on the fractional face.
 516	// Starting t and ang values for the first face
 517	t = t_first;
 518	ang = 2.0f*F_PI*(t*ang_scale + offset);
 519	pt1.setVec(cos(ang)*scale,sin(ang)*scale, t);
 520
 521	// Increment to the next point.
 522	// pt2 is the end point on the fractional face
 523	t += t_step;
 524	ang += ang_step;
 525	pt2.setVec(cos(ang)*scale,sin(ang)*scale,t);
 526
 527	t_fraction = (begin - t_first)*sides;
 528
 529	// Only use if it's not almost exactly on an edge.
 530	if (t_fraction < 0.9999f)
 531	{
 532		LLVector3 new_pt = lerp(pt1, pt2, t_fraction);
 533		mProfile.push_back(new_pt);
 534	}
 535
 536	// There's lots of potential here for floating point error to generate unneeded extra points - DJS 04/05/02
 537	while (t < end)
 538	{
 539		// Iterate through all the integer steps of t.
 540		pt1.setVec(cos(ang)*scale,sin(ang)*scale,t);
 541
 542		if (mProfile.size() > 0) {
 543			LLVector3 p = mProfile[mProfile.size()-1];
 544			for (S32 i = 0; i < split && mProfile.size() > 0; i++) {
 545				mProfile.push_back(p+(pt1-p) * 1.0f/(float)(split+1) * (float)(i+1));
 546			}
 547		}
 548		mProfile.push_back(pt1);
 549
 550		t += t_step;
 551		ang += ang_step;
 552	}
 553
 554	t_fraction = (end - (t - t_step))*sides;
 555
 556	// pt1 is the first point on the fractional face
 557	// pt2 is the end point on the fractional face
 558	pt2.setVec(cos(ang)*scale,sin(ang)*scale,t);
 559
 560	// Find the fraction that we need to add to the end point.
 561	t_fraction = (end - (t - t_step))*sides;
 562	if (t_fraction > 0.0001f)
 563	{
 564		LLVector3 new_pt = lerp(pt1, pt2, t_fraction);
 565		
 566		if (mProfile.size() > 0) {
 567			LLVector3 p = mProfile[mProfile.size()-1];
 568			for (S32 i = 0; i < split && mProfile.size() > 0; i++) {
 569				mProfile.push_back(p+(new_pt-p) * 1.0f/(float)(split+1) * (float)(i+1));
 570			}
 571		}
 572		mProfile.push_back(new_pt);
 573	}
 574
 575	// If we're sliced, the profile is open.
 576	if ((end - begin)*ang_scale < 0.99f)
 577	{
 578		if ((end - begin)*ang_scale > 0.5f)
 579		{
 580			mConcave = TRUE;
 581		}
 582		else
 583		{
 584			mConcave = FALSE;
 585		}
 586		mOpen = TRUE;
 587		if (params.getHollow() <= 0)
 588		{
 589			// put center point if not hollow.
 590			mProfile.push_back(LLVector3(0,0,0));
 591		}
 592	}
 593	else
 594	{
 595		// The profile isn't open.
 596		mOpen = FALSE;
 597		mConcave = FALSE;
 598	}
 599
 600	mTotal = mProfile.size();
 601}
 602
 603void LLProfile::genNormals(const LLProfileParams& params)
 604{
 605	S32 count = mProfile.size();
 606
 607	S32 outer_count;
 608	if (mTotalOut)
 609	{
 610		outer_count = mTotalOut;
 611	}
 612	else
 613	{
 614		outer_count = mTotal / 2;
 615	}
 616
 617	mEdgeNormals.resize(count * 2);
 618	mEdgeCenters.resize(count * 2);
 619	mNormals.resize(count);
 620
 621	LLVector2 pt0,pt1;
 622
 623	BOOL hollow = (params.getHollow() > 0);
 624
 625	S32 i0, i1, i2, i3, i4;
 626
 627	// Parametrically generate normal
 628	for (i2 = 0; i2 < count; i2++)
 629	{
 630		mNormals[i2].mV[0] = mProfile[i2].mV[0];
 631		mNormals[i2].mV[1] = mProfile[i2].mV[1];
 632		if (hollow && (i2 >= outer_count))
 633		{
 634			mNormals[i2] *= -1.f;
 635		}
 636		if (mNormals[i2].magVec() < 0.001)
 637		{
 638			// Special case for point at center, get adjacent points.
 639			i1 = (i2 - 1) >= 0 ? i2 - 1 : count - 1;
 640			i0 = (i1 - 1) >= 0 ? i1 - 1 : count - 1;
 641			i3 = (i2 + 1) < count ? i2 + 1 : 0;
 642			i4 = (i3 + 1) < count ? i3 + 1 : 0;
 643
 644			pt0.setVec(mProfile[i1].mV[VX] + mProfile[i1].mV[VX] - mProfile[i0].mV[VX], 
 645				mProfile[i1].mV[VY] + mProfile[i1].mV[VY] - mProfile[i0].mV[VY]);
 646			pt1.setVec(mProfile[i3].mV[VX] + mProfile[i3].mV[VX] - mProfile[i4].mV[VX], 
 647				mProfile[i3].mV[VY] + mProfile[i3].mV[VY] - mProfile[i4].mV[VY]);
 648
 649			mNormals[i2] = pt0 + pt1;
 650			mNormals[i2] *= 0.5f;
 651		}
 652		mNormals[i2].normVec();
 653	}
 654
 655	S32 num_normal_sets = isConcave() ? 2 : 1;
 656	for (S32 normal_set = 0; normal_set < num_normal_sets; normal_set++)
 657	{
 658		S32 point_num;
 659		for (point_num = 0; point_num < mTotal; point_num++)
 660		{
 661			LLVector3 point_1 = mProfile[point_num];
 662			point_1.mV[VZ] = 0.f;
 663
 664			LLVector3 point_2;
 665			
 666			if (isConcave() && normal_set == 0 && point_num == (mTotal - 1) / 2)
 667			{
 668				point_2 = mProfile[mTotal - 1];
 669			}
 670			else if (isConcave() && normal_set == 1 && point_num == mTotal - 1)
 671			{
 672				point_2 = mProfile[(mTotal - 1) / 2];
 673			}
 674			else
 675			{
 676				LLVector3 delta_pos;
 677				S32 neighbor_point = (point_num + 1) % mTotal;
 678				while(delta_pos.magVecSquared() < 0.01f * 0.01f)
 679				{
 680					point_2 = mProfile[neighbor_point];
 681					delta_pos = point_2 - point_1;
 682					neighbor_point = (neighbor_point + 1) % mTotal;
 683					if (neighbor_point == point_num)
 684					{
 685						break;
 686					}
 687				}
 688			}
 689
 690			point_2.mV[VZ] = 0.f;
 691			LLVector3 face_normal = (point_2 - point_1) % LLVector3::z_axis;
 692			face_normal.normVec();
 693			mEdgeNormals[normal_set * count + point_num] = face_normal;
 694			mEdgeCenters[normal_set * count + point_num] = lerp(point_1, point_2, 0.5f);
 695		}
 696	}
 697}
 698
 699
 700// Hollow is percent of the original bounding box, not of this particular
 701// profile's geometry.  Thus, a swept triangle needs lower hollow values than
 702// a swept square.
 703LLProfile::Face* LLProfile::addHole(const LLProfileParams& params, BOOL flat, F32 sides, F32 offset, F32 box_hollow, F32 ang_scale, S32 split)
 704{
 705	// Note that addHole will NOT work for non-"circular" profiles, if we ever decide to use them.
 706
 707	// Total add has number of vertices on outside.
 708	mTotalOut = mTotal;
 709
 710	// Why is the "bevel" parameter -1? DJS 04/05/02
 711	genNGon(params, llfloor(sides),offset,-1, ang_scale, split);
 712
 713	Face *face = addFace(mTotalOut, mTotal-mTotalOut,0,LL_FACE_INNER_SIDE, flat);
 714
 715	std::vector<LLVector3> pt;
 716	pt.resize(mTotal) ;
 717
 718	for (S32 i=mTotalOut;i<mTotal;i++)
 719	{
 720		pt[i] = mProfile[i] * box_hollow;
 721	}
 722
 723	S32 j=mTotal-1;
 724	for (S32 i=mTotalOut;i<mTotal;i++)
 725	{
 726		mProfile[i] = pt[j--];
 727	}
 728
 729	for (S32 i=0;i<(S32)mFaces.size();i++) 
 730	{
 731		if (mFaces[i].mCap)
 732		{
 733			mFaces[i].mCount *= 2;
 734		}
 735	}
 736
 737	return face;
 738}
 739
 740//static
 741S32 LLProfile::getNumPoints(const LLProfileParams& params, BOOL path_open,F32 detail, S32 split,
 742						 BOOL is_sculpted, S32 sculpt_size)
 743{ // this is basically LLProfile::generate stripped down to only operations that influence the number of points
 744	LLMemType m1(LLMemType::MTYPE_VOLUME);
 745	
 746	if (detail < MIN_LOD)
 747	{
 748		detail = MIN_LOD;
 749	}
 750
 751	// Generate the face data
 752	F32 hollow = params.getHollow();
 753
 754	S32 np = 0;
 755
 756	switch (params.getCurveType() & LL_PCODE_PROFILE_MASK)
 757	{
 758	case LL_PCODE_PROFILE_SQUARE:
 759		{
 760			np = getNumNGonPoints(params, 4,-0.375, 0, 1, split);
 761		
 762			if (hollow)
 763			{
 764				np *= 2;
 765			}
 766		}
 767		break;
 768	case  LL_PCODE_PROFILE_ISOTRI:
 769	case  LL_PCODE_PROFILE_RIGHTTRI:
 770	case  LL_PCODE_PROFILE_EQUALTRI:
 771		{
 772			np = getNumNGonPoints(params, 3,0, 0, 1, split);
 773						
 774			if (hollow)
 775			{
 776				np *= 2;
 777			}
 778		}
 779		break;
 780	case LL_PCODE_PROFILE_CIRCLE:
 781		{
 782			// If this has a square hollow, we should adjust the
 783			// number of faces a bit so that the geometry lines up.
 784			U8 hole_type=0;
 785			F32 circle_detail = MIN_DETAIL_FACES * detail;
 786			if (hollow)
 787			{
 788				hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK;
 789				if (hole_type == LL_PCODE_HOLE_SQUARE)
 790				{
 791					// Snap to the next multiple of four sides,
 792					// so that corners line up.
 793					circle_detail = llceil(circle_detail / 4.0f) * 4.0f;
 794				}
 795			}
 796
 797			S32 sides = (S32)circle_detail;
 798
 799			if (is_sculpted)
 800				sides = sculpt_size;
 801			
 802			np = getNumNGonPoints(params, sides);
 803			
 804			if (hollow)
 805			{
 806				np *= 2;
 807			}
 808		}
 809		break;
 810	case LL_PCODE_PROFILE_CIRCLE_HALF:
 811		{
 812			// If this has a square hollow, we should adjust the
 813			// number of faces a bit so that the geometry lines up.
 814			U8 hole_type=0;
 815			// Number of faces is cut in half because it's only a half-circle.
 816			F32 circle_detail = MIN_DETAIL_FACES * detail * 0.5f;
 817			if (hollow)
 818			{
 819				hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK;
 820				if (hole_type == LL_PCODE_HOLE_SQUARE)
 821				{
 822					// Snap to the next multiple of four sides (div 2),
 823					// so that corners line up.
 824					circle_detail = llceil(circle_detail / 2.0f) * 2.0f;
 825				}
 826			}
 827			np = getNumNGonPoints(params, llfloor(circle_detail), 0.5f, 0.f, 0.5f);
 828			
 829			if (hollow)
 830			{
 831				np *= 2;
 832			}
 833
 834			// Special case for openness of sphere
 835			if ((params.getEnd() - params.getBegin()) < 1.f)
 836			{
 837			}
 838			else if (!hollow)
 839			{
 840				np++;
 841			}
 842		}
 843		break;
 844	default:
 845	   break;
 846	};
 847
 848	
 849	return np;
 850}
 851
 852
 853BOOL LLProfile::generate(const LLProfileParams& params, BOOL path_open,F32 detail, S32 split,
 854						 BOOL is_sculpted, S32 sculpt_size)
 855{
 856	LLMemType m1(LLMemType::MTYPE_VOLUME);
 857	
 858	if ((!mDirty) && (!is_sculpted))
 859	{
 860		return FALSE;
 861	}
 862	mDirty = FALSE;
 863
 864	if (detail < MIN_LOD)
 865	{
 866		llinfos << "Generating profile with LOD < MIN_LOD.  CLAMPING" << llendl;
 867		detail = MIN_LOD;
 868	}
 869
 870	mProfile.clear();
 871	mFaces.clear();
 872
 873	// Generate the face data
 874	S32 i;
 875	F32 begin = params.getBegin();
 876	F32 end = params.getEnd();
 877	F32 hollow = params.getHollow();
 878
 879	// Quick validation to eliminate some server crashes.
 880	if (begin > end - 0.01f)
 881	{
 882		llwarns << "LLProfile::generate() assertion failed (begin >= end)" << llendl;
 883		return FALSE;
 884	}
 885
 886	S32 face_num = 0;
 887
 888	switch (params.getCurveType() & LL_PCODE_PROFILE_MASK)
 889	{
 890	case LL_PCODE_PROFILE_SQUARE:
 891		{
 892			genNGon(params, 4,-0.375, 0, 1, split);
 893			if (path_open)
 894			{
 895				addCap (LL_FACE_PATH_BEGIN);
 896			}
 897
 898			for (i = llfloor(begin * 4.f); i < llfloor(end * 4.f + .999f); i++)
 899			{
 900				addFace((face_num++) * (split +1), split+2, 1, LL_FACE_OUTER_SIDE_0 << i, TRUE);
 901			}
 902
 903			for (i = 0; i <(S32) mProfile.size(); i++)
 904			{
 905				// Scale by 4 to generate proper tex coords.
 906				mProfile[i].mV[2] *= 4.f;
 907			}
 908
 909			if (hollow)
 910			{
 911				switch (params.getCurveType() & LL_PCODE_HOLE_MASK)
 912				{
 913				case LL_PCODE_HOLE_TRIANGLE:
 914					// This offset is not correct, but we can't change it now... DK 11/17/04
 915				  	addHole(params, TRUE, 3, -0.375f, hollow, 1.f, split);
 916					break;
 917				case LL_PCODE_HOLE_CIRCLE:
 918					// TODO: Compute actual detail levels for cubes
 919				  	addHole(params, FALSE, MIN_DETAIL_FACES * detail, -0.375f, hollow, 1.f);
 920					break;
 921				case LL_PCODE_HOLE_SAME:
 922				case LL_PCODE_HOLE_SQUARE:
 923				default:
 924					addHole(params, TRUE, 4, -0.375f, hollow, 1.f, split);
 925					break;
 926				}
 927			}
 928			
 929			if (path_open) {
 930				mFaces[0].mCount = mTotal;
 931			}
 932		}
 933		break;
 934	case  LL_PCODE_PROFILE_ISOTRI:
 935	case  LL_PCODE_PROFILE_RIGHTTRI:
 936	case  LL_PCODE_PROFILE_EQUALTRI:
 937		{
 938			genNGon(params, 3,0, 0, 1, split);
 939			for (i = 0; i <(S32) mProfile.size(); i++)
 940			{
 941				// Scale by 3 to generate proper tex coords.
 942				mProfile[i].mV[2] *= 3.f;
 943			}
 944
 945			if (path_open)
 946			{
 947				addCap(LL_FACE_PATH_BEGIN);
 948			}
 949
 950			for (i = llfloor(begin * 3.f); i < llfloor(end * 3.f + .999f); i++)
 951			{
 952				addFace((face_num++) * (split +1), split+2, 1, LL_FACE_OUTER_SIDE_0 << i, TRUE);
 953			}
 954			if (hollow)
 955			{
 956				// Swept triangles need smaller hollowness values,
 957				// because the triangle doesn't fill the bounding box.
 958				F32 triangle_hollow = hollow / 2.f;
 959
 960				switch (params.getCurveType() & LL_PCODE_HOLE_MASK)
 961				{
 962				case LL_PCODE_HOLE_CIRCLE:
 963					// TODO: Actually generate level of detail for triangles
 964					addHole(params, FALSE, MIN_DETAIL_FACES * detail, 0, triangle_hollow, 1.f);
 965					break;
 966				case LL_PCODE_HOLE_SQUARE:
 967					addHole(params, TRUE, 4, 0, triangle_hollow, 1.f, split);
 968					break;
 969				case LL_PCODE_HOLE_SAME:
 970				case LL_PCODE_HOLE_TRIANGLE:
 971				default:
 972					addHole(params, TRUE, 3, 0, triangle_hollow, 1.f, split);
 973					break;
 974				}
 975			}
 976		}
 977		break;
 978	case LL_PCODE_PROFILE_CIRCLE:
 979		{
 980			// If this has a square hollow, we should adjust the
 981			// number of faces a bit so that the geometry lines up.
 982			U8 hole_type=0;
 983			F32 circle_detail = MIN_DETAIL_FACES * detail;
 984			if (hollow)
 985			{
 986				hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK;
 987				if (hole_type == LL_PCODE_HOLE_SQUARE)
 988				{
 989					// Snap to the next multiple of four sides,
 990					// so that corners line up.
 991					circle_detail = llceil(circle_detail / 4.0f) * 4.0f;
 992				}
 993			}
 994
 995			S32 sides = (S32)circle_detail;
 996
 997			if (is_sculpted)
 998				sides = sculpt_size;
 999			
1000			genNGon(params, sides);
1001			
1002			if (path_open)
1003			{
1004				addCap (LL_FACE_PATH_BEGIN);
1005			}
1006
1007			if (mOpen && !hollow)
1008			{
1009				addFace(0,mTotal-1,0,LL_FACE_OUTER_SIDE_0, FALSE);
1010			}
1011			else
1012			{
1013				addFace(0,mTotal,0,LL_FACE_OUTER_SIDE_0, FALSE);
1014			}
1015
1016			if (hollow)
1017			{
1018				switch (hole_type)
1019				{
1020				case LL_PCODE_HOLE_SQUARE:
1021					addHole(params, TRUE, 4, 0, hollow, 1.f, split);
1022					break;
1023				case LL_PCODE_HOLE_TRIANGLE:
1024					addHole(params, TRUE, 3, 0, hollow, 1.f, split);
1025					break;
1026				case LL_PCODE_HOLE_CIRCLE:
1027				case LL_PCODE_HOLE_SAME:
1028				default:
1029					addHole(params, FALSE, circle_detail, 0, hollow, 1.f);
1030					break;
1031				}
1032			}
1033		}
1034		break;
1035	case LL_PCODE_PROFILE_CIRCLE_HALF:
1036		{
1037			// If this has a square hollow, we should adjust the
1038			// number of faces a bit so that the geometry lines up.
1039			U8 hole_type=0;
1040			// Number of faces is cut in half because it's only a half-circle.
1041			F32 circle_detail = MIN_DETAIL_FACES * detail * 0.5f;
1042			if (hollow)
1043			{
1044				hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK;
1045				if (hole_type == LL_PCODE_HOLE_SQUARE)
1046				{
1047					// Snap to the next multiple of four sides (div 2),
1048					// so that corners line up.
1049					circle_detail = llceil(circle_detail / 2.0f) * 2.0f;
1050				}
1051			}
1052			genNGon(params, llfloor(circle_detail), 0.5f, 0.f, 0.5f);
1053			if (path_open)
1054			{
1055				addCap(LL_FACE_PATH_BEGIN);
1056			}
1057			if (mOpen && !params.getHollow())
1058			{
1059				addFace(0,mTotal-1,0,LL_FACE_OUTER_SIDE_0, FALSE);
1060			}
1061			else
1062			{
1063				addFace(0,mTotal,0,LL_FACE_OUTER_SIDE_0, FALSE);
1064			}
1065
1066			if (hollow)
1067			{
1068				switch (hole_type)
1069				{
1070				case LL_PCODE_HOLE_SQUARE:
1071					addHole(params, TRUE, 2, 0.5f, hollow, 0.5f, split);
1072					break;
1073				case LL_PCODE_HOLE_TRIANGLE:
1074					addHole(params, TRUE, 3,  0.5f, hollow, 0.5f, split);
1075					break;
1076				case LL_PCODE_HOLE_CIRCLE:
1077				case LL_PCODE_HOLE_SAME:
1078				default:
1079					addHole(params, FALSE, circle_detail,  0.5f, hollow, 0.5f);
1080					break;
1081				}
1082			}
1083
1084			// Special case for openness of sphere
1085			if ((params.getEnd() - params.getBegin()) < 1.f)
1086			{
1087				mOpen = TRUE;
1088			}
1089			else if (!hollow)
1090			{
1091				mOpen = FALSE;
1092				mProfile.push_back(mProfile[0]);
1093				mTotal++;
1094			}
1095		}
1096		break;
1097	default:
1098	    llerrs << "Unknown profile: getCurveType()=" << params.getCurveType() << llendl;
1099		break;
1100	};
1101
1102	if (path_open)
1103	{
1104		addCap(LL_FACE_PATH_END); // bottom
1105	}
1106	
1107	if ( mOpen) // interior edge caps
1108	{
1109		addFace(mTotal-1, 2,0.5,LL_FACE_PROFILE_BEGIN, TRUE); 
1110
1111		if (hollow)
1112		{
1113			addFace(mTotalOut-1, 2,0.5,LL_FACE_PROFILE_END, TRUE);
1114		}
1115		else
1116		{
1117			addFace(mTotal-2, 2,0.5,LL_FACE_PROFILE_END, TRUE);
1118		}
1119	}
1120	
1121	//genNormals(params);
1122
1123	return TRUE;
1124}
1125
1126
1127
1128BOOL LLProfileParams::importFile(LLFILE *fp)
1129{
1130	LLMemType m1(LLMemType::MTYPE_VOLUME);
1131	
1132	const S32 BUFSIZE = 16384;
1133	char buffer[BUFSIZE];	/* Flawfinder: ignore */
1134	// *NOTE: changing the size or type of these buffers will require
1135	// changing the sscanf below.
1136	char keyword[256];	/* Flawfinder: ignore */
1137	char valuestr[256];	/* Flawfinder: ignore */
1138	keyword[0] = 0;
1139	valuestr[0] = 0;
1140	F32 tempF32;
1141	U32 tempU32;
1142
1143	while (!feof(fp))
1144	{
1145		if (fgets(buffer, BUFSIZE, fp) == NULL)
1146		{
1147			buffer[0] = '\0';
1148		}
1149		
1150		sscanf(	/* Flawfinder: ignore */
1151			buffer,
1152			" %255s %255s",
1153			keyword, valuestr);
1154		if (!strcmp("{", keyword))
1155		{
1156			continue;
1157		}
1158		if (!strcmp("}",keyword))
1159		{
1160			break;
1161		}
1162		else if (!strcmp("curve", keyword))
1163		{
1164			sscanf(valuestr,"%d",&tempU32);
1165			setCurveType((U8) tempU32);
1166		}
1167		else if (!strcmp("begin",keyword))
1168		{
1169			sscanf(valuestr,"%g",&tempF32);
1170			setBegin(tempF32);
1171		}
1172		else if (!strcmp("end",keyword))
1173		{
1174			sscanf(valuestr,"%g",&tempF32);
1175			setEnd(tempF32);
1176		}
1177		else if (!strcmp("hollow",keyword))
1178		{
1179			sscanf(valuestr,"%g",&tempF32);
1180			setHollow(tempF32);
1181		}
1182		else
1183		{
1184			llwarns << "unknown keyword " << keyword << " in profile import" << llendl;
1185		}
1186	}
1187
1188	return TRUE;
1189}
1190
1191
1192BOOL LLProfileParams::exportFile(LLFILE *fp) const
1193{
1194	fprintf(fp,"\t\tprofile 0\n");
1195	fprintf(fp,"\t\t{\n");
1196	fprintf(fp,"\t\t\tcurve\t%d\n", getCurveType());
1197	fprintf(fp,"\t\t\tbegin\t%g\n", getBegin());
1198	fprintf(fp,"\t\t\tend\t%g\n", getEnd());
1199	fprintf(fp,"\t\t\thollow\t%g\n", getHollow());
1200	fprintf(fp, "\t\t}\n");
1201	return TRUE;
1202}
1203
1204
1205BOOL LLProfileParams::importLegacyStream(std::istream& input_stream)
1206{
1207	LLMemType m1(LLMemType::MTYPE_VOLUME);
1208	
1209	const S32 BUFSIZE = 16384;
1210	char buffer[BUFSIZE];	/* Flawfinder: ignore */
1211	// *NOTE: changing the size or type of these buffers will require
1212	// changing the sscanf below.
1213	char keyword[256];	/* Flawfinder: ignore */
1214	char valuestr[256];	/* Flawfinder: ignore */
1215	keyword[0] = 0;
1216	valuestr[0] = 0;
1217	F32 tempF32;
1218	U32 tempU32;
1219
1220	while (input_stream.good())
1221	{
1222		input_stream.getline(buffer, BUFSIZE);
1223		sscanf(	/* Flawfinder: ignore */
1224			buffer,
1225			" %255s %255s",
1226			keyword,
1227			valuestr);
1228		if (!strcmp("{", keyword))
1229		{
1230			continue;
1231		}
1232		if (!strcmp("}",keyword))
1233		{
1234			break;
1235		}
1236		else if (!strcmp("curve", keyword))
1237		{
1238			sscanf(valuestr,"%d",&tempU32);
1239			setCurveType((U8) tempU32);
1240		}
1241		else if (!strcmp("begin",keyword))
1242		{
1243			sscanf(valuestr,"%g",&tempF32);
1244			setBegin(tempF32);
1245		}
1246		else if (!strcmp("end",keyword))
1247		{
1248			sscanf(valuestr,"%g",&tempF32);
1249			setEnd(tempF32);
1250		}
1251		else if (!strcmp("hollow",keyword))
1252		{
1253			sscanf(valuestr,"%g",&tempF32);
1254			setHollow(tempF32);
1255		}
1256		else
1257		{
1258 		llwarns << "unknown keyword " << keyword << " in profile import" << llendl;
1259		}
1260	}
1261
1262	return TRUE;
1263}
1264
1265
1266BOOL LLProfileParams::exportLegacyStream(std::ostream& output_stream) const
1267{
1268	output_stream <<"\t\tprofile 0\n";
1269	output_stream <<"\t\t{\n";
1270	output_stream <<"\t\t\tcurve\t" << (S32) getCurveType() << "\n";
1271	output_stream <<"\t\t\tbegin\t" << getBegin() << "\n";
1272	output_stream <<"\t\t\tend\t" << getEnd() << "\n";
1273	output_stream <<"\t\t\thollow\t" << getHollow() << "\n";
1274	output_stream << "\t\t}\n";
1275	return TRUE;
1276}
1277
1278LLSD LLProfileParams::asLLSD() const
1279{
1280	LLSD sd;
1281
1282	sd["curve"] = getCurveType();
1283	sd["begin"] = getBegin();
1284	sd["end"] = getEnd();
1285	sd["hollow"] = getHollow();
1286	return sd;
1287}
1288
1289bool LLProfileParams::fromLLSD(LLSD& sd)
1290{
1291	setCurveType(sd["curve"].asInteger());
1292	setBegin((F32)sd["begin"].asReal());
1293	setEnd((F32)sd["end"].asReal());
1294	setHollow((F32)sd["hollow"].asReal());
1295	return true;
1296}
1297
1298void LLProfileParams::copyParams(const LLProfileParams &params)
1299{
1300	LLMemType m1(LLMemType::MTYPE_VOLUME);
1301	setCurveType(params.getCurveType());
1302	setBegin(params.getBegin());
1303	setEnd(params.getEnd());
1304	setHollow(params.getHollow());
1305}
1306
1307
1308LLPath::~LLPath()
1309{
1310}
1311
1312S32 LLPath::getNumNGonPoints(const LLPathParams& params, S32 sides, F32 startOff, F32 end_scale, F32 twist_scale)
1313{ //this is basically LLPath::genNGon stripped down to only operations that influence the number of points added
1314	S32 ret = 0;
1315
1316	F32 step= 1.0f / sides;
1317	F32 t	= params.getBegin();
1318	ret = 1;
1319	
1320	t+=step;
1321
1322	// Snap to a quantized parameter, so that cut does not
1323	// affect most sample points.
1324	t = ((S32)(t * sides)) / (F32)sides;
1325
1326	// Run through the non-cut dependent points.
1327	while (t < params.getEnd())
1328	{
1329		ret++;
1330		t+=step;
1331	}
1332
1333	ret++;
1334
1335	return ret;
1336}
1337
1338void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 end_scale, F32 twist_scale)
1339{
1340	// Generates a circular path, starting at (1, 0, 0), counterclockwise along the xz plane.
1341	const F32 tableScale[] = { 1, 1, 1, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f };
1342
1343	F32 revolutions = params.getRevolutions();
1344	F32 skew		= params.getSkew();
1345	F32 skew_mag	= fabs(skew);
1346	F32 hole_x		= params.getScaleX() * (1.0f - skew_mag);
1347	F32 hole_y		= params.getScaleY();
1348
1349	// Calculate taper begin/end for x,y (Negative means taper the beginning)
1350	F32 taper_x_begin	= 1.0f;
1351	F32 taper_x_end		= 1.0f - params.getTaperX();
1352	F32	taper_y_begin	= 1.0f;
1353	F32	taper_y_end		= 1.0f - params.getTaperY();
1354
1355	if ( taper_x_end > 1.0f )
1356	{
1357		// Flip tapering.
1358		taper_x_begin	= 2.0f - taper_x_end;
1359		taper_x_end		= 1.0f;
1360	}
1361	if ( taper_y_end > 1.0f )
1362	{
1363		// Flip tapering.
1364		taper_y_begin	= 2.0f - taper_y_end;
1365		taper_y_end		= 1.0f;
1366	}
1367
1368	// For spheres, the radius is usually zero.
1369	F32 radius_start = 0.5f;
1370	if (sides < 8)
1371	{
1372		radius_start = tableScale[sides];
1373	}
1374
1375	// Scale the radius to take the hole size into account.
1376	radius_start *= 1.0f - hole_y;
1377	
1378	// Now check the radius offset to calculate the start,end radius.  (Negative means
1379	// decrease the start radius instead).
1380	F32 radius_end    = radius_start;
1381	F32 radius_offset = params.getRadiusOffset();
1382	if (radius_offset < 0.f)
1383	{
1384		radius_start *= 1.f + radius_offset;
1385	}
1386	else
1387	{
1388		radius_end   *= 1.f - radius_offset;
1389	}	
1390
1391	// Is the path NOT a closed loop?
1392	mOpen = ( (params.getEnd()*end_scale - params.getBegin() < 1.0f) ||
1393		      (skew_mag > 0.001f) ||
1394			  (fabs(taper_x_end - taper_x_begin) > 0.001f) ||
1395			  (fabs(taper_y_end - taper_y_begin) > 0.001f) ||
1396			  (fabs(radius_end - radius_start) > 0.001f) );
1397
1398	F32 ang, c, s;
1399	LLQuaternion twist, qang;
1400	PathPt *pt;
1401	LLVector3 path_axis (1.f, 0.f, 0.f);
1402	//LLVector3 twist_axis(0.f, 0.f, 1.f);
1403	F32 twist_begin = params.getTwistBegin() * twist_scale;
1404	F32 twist_end	= params.getTwist() * twist_scale;
1405
1406	// We run through this once before the main loop, to make sure
1407	// the path begins at the correct cut.
1408	F32 step= 1.0f / sides;
1409	F32 t	= params.getBegin();
1410	pt		= vector_append(mPath, 1);
1411	ang		= 2.0f*F_PI*revolutions * t;
1412	s		= sin(ang)*lerp(radius_start, radius_end, t);	
1413	c		= cos(ang)*lerp(radius_start, radius_end, t);
1414
1415
1416	pt->mPos.setVec(0 + lerp(0,params.getShear().mV[0],s)
1417					  + lerp(-skew ,skew, t) * 0.5f,
1418					c + lerp(0,params.getShear().mV[1],s), 
1419					s);
1420	pt->mScale.mV[VX] = hole_x * lerp(taper_x_begin, taper_x_end, t);
1421	pt->mScale.mV[VY] = hole_y * lerp(taper_y_begin, taper_y_end, t);
1422	pt->mTexT  = t;
1423	
1424	// Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
1425	twist.setQuat  (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
1426	// Rotate the point around the circle's center.
1427	qang.setQuat   (ang,path_axis);
1428	pt->mRot   = twist * qang;
1429
1430	t+=step;
1431
1432	// Snap to a quantized parameter, so that cut does not
1433	// affect most sample points.
1434	t = ((S32)(t * sides)) / (F32)sides;
1435
1436	// Run through the non-cut dependent points.
1437	while (t < params.getEnd())
1438	{
1439		pt		= vector_append(mPath, 1);
1440
1441		ang = 2.0f*F_PI*revolutions * t;
1442		c   = cos(ang)*lerp(radius_start, radius_end, t);
1443		s   = sin(ang)*lerp(radius_start, radius_end, t);
1444
1445		pt->mPos.setVec(0 + lerp(0,params.getShear().mV[0],s)
1446					      + lerp(-skew ,skew, t) * 0.5f,
1447						c + lerp(0,params.getShear().mV[1],s), 
1448						s);
1449
1450		pt->mScale.mV[VX] = hole_x * lerp(taper_x_begin, taper_x_end, t);
1451		pt->mScale.mV[VY] = hole_y * lerp(taper_y_begin, taper_y_end, t);
1452		pt->mTexT  = t;
1453
1454		// Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
1455		twist.setQuat  (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
1456		// Rotate the point around the circle's center.
1457		qang.setQuat   (ang,path_axis);
1458		pt->mRot	= twist * qang;
1459
1460		t+=step;
1461	}
1462
1463	// Make one final pass for the end cut.
1464	t = params.getEnd();
1465	pt		= vector_append(mPath, 1);
1466	ang = 2.0f*F_PI*revolutions * t;
1467	c   = cos(ang)*lerp(radius_start, radius_end, t);
1468	s   = sin(ang)*lerp(radius_start, radius_end, t);
1469
1470	pt->mPos.setVec(0 + lerp(0,params.getShear().mV[0],s)
1471					  + lerp(-skew ,skew, t) * 0.5f,
1472					c + lerp(0,params.getShear().mV[1],s), 
1473					s);
1474	pt->mScale.mV[VX] = hole_x * lerp(taper_x_begin, taper_x_end, t);
1475	pt->mScale.mV[VY] = hole_y * lerp(taper_y_begin, taper_y_end, t);
1476	pt->mTexT  = t;
1477	
1478	// Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
1479	twist.setQuat  (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
1480	// Rotate the point around the circle's center.
1481	qang.setQuat   (ang,path_axis);
1482	pt->mRot   = twist * qang;
1483
1484	mTotal = mPath.size();
1485}
1486
1487const LLVector2 LLPathParams::getBeginScale() const
1488{
1489	LLVector2 begin_scale(1.f, 1.f);
1490	if (getScaleX() > 1)
1491	{
1492		begin_scale.mV[0] = 2-getScaleX();
1493	}
1494	if (getScaleY() > 1)
1495	{
1496		begin_scale.mV[1] = 2-getScaleY();
1497	}
1498	return begin_scale;
1499}
1500
1501const LLVector2 LLPathParams::getEndScale() const
1502{
1503	LLVector2 end_scale(1.f, 1.f);
1504	if (getScaleX() < 1)
1505	{
1506		end_scale.mV[0] = getScaleX();
1507	}
1508	if (getScaleY() < 1)
1509	{
1510		end_scale.mV[1] = getScaleY();
1511	}
1512	return end_scale;
1513}
1514
1515S32 LLPath::getNumPoints(const LLPathParams& params, F32 detail)
1516{ // this is basically LLPath::generate stripped down to only the operations that influence the number of points
1517	LLMemType m1(LLMemType::MTYPE_VOLUME);
1518	
1519	if (detail < MIN_LOD)
1520	{
1521		detail = MIN_LOD;
1522	}
1523
1524	S32 np = 2; // hardcode for line
1525
1526	// Is this 0xf0 mask really necessary?  DK 03/02/05
1527
1528	switch (params.getCurveType() & 0xf0)
1529	{
1530	default:
1531	case LL_PCODE_PATH_LINE:
1532		{
1533			// Take the begin/end twist into account for detail.
1534			np    = llfloor(fabs(params.getTwistBegin() - params.getTwist()) * 3.5f * (detail-0.5f)) + 2;
1535		}
1536		break;
1537
1538	case LL_PCODE_PATH_CIRCLE:
1539		{
1540			// Increase the detail as the revolutions and twist increase.
1541			F32 twist_mag = fabs(params.getTwistBegin() - params.getTwist());
1542
1543			S32 sides = (S32)llfloor(llfloor((MIN_DETAIL_FACES * detail + twist_mag * 3.5f * (detail-0.5f))) * params.getRevolutions());
1544
1545			np = sides;
1546		}
1547		break;
1548
1549	case LL_PCODE_PATH_CIRCLE2:
1550		{
1551			//genNGon(params, llfloor(MIN_DETAIL_FACES * detail), 4.f, 0.f);
1552			np = getNumNGonPoints(params, llfloor(MIN_DETAIL_FACES * detail));
1553		}
1554		break;
1555
1556	case LL_PCODE_PATH_TEST:
1557
1558		np     = 5;
1559		break;
1560	};
1561
1562	return np;
1563}
1564
1565BOOL LLPath::generate(const LLPathParams& params, F32 detail, S32 split,
1566					  BOOL is_sculpted, S32 sculpt_size)
1567{
1568	LLMemType m1(LLMemType::MTYPE_VOLUME);
1569	
1570	if ((!mDirty) && (!is_sculpted))
1571	{
1572		return FALSE;
1573	}
1574
1575	if (detail < MIN_LOD)
1576	{
1577		llinfos << "Generating path with LOD < MIN!  Clamping to 1" << llendl;
1578		detail = MIN_LOD;
1579	}
1580
1581	mDirty = FALSE;
1582	S32 np = 2; // hardcode for line
1583
1584	mPath.clear();
1585	mOpen = TRUE;
1586
1587	// Is this 0xf0 mask really necessary?  DK 03/02/05
1588	switch (params.getCurveType() & 0xf0)
1589	{
1590	default:
1591	case LL_PCODE_PATH_LINE:
1592		{
1593			// Take the begin/end twist into account for detail.
1594			np    = llfloor(fabs(params.getTwistBegin() - params.getTwist()) * 3.5f * (detail-0.5f)) + 2;
1595			if (np < split+2)
1596			{
1597				np = split+2;
1598			}
1599
1600			mStep = 1.0f / (np-1);
1601			
1602			mPath.resize(np);
1603
1604			LLVector2 start_scale = params.getBeginScale();
1605			LLVector2 end_scale = params.getEndScale();
1606
1607			for (S32 i=0;i<np;i++)
1608			{
1609				F32 t = lerp(params.getBegin(),params.getEnd(),(F32)i * mStep);
1610				mPath[i].mPos.setVec(lerp(0,params.getShear().mV[0],t),
1611									 lerp(0,params.getShear().mV[1],t),
1612									 t - 0.5f);
1613				mPath[i].mRot.setQuat(lerp(F_PI * params.getTwistBegin(),F_PI * params.getTwist(),t),0,0,1);
1614				mPath[i].mScale.mV[0] = lerp(start_scale.mV[0],end_scale.mV[0],t);
1615				mPath[i].mScale.mV[1] = lerp(start_scale.mV[1],end_scale.mV[1],t);
1616				mPath[i].mTexT        = t;
1617			}
1618		}
1619		break;
1620
1621	case LL_PCODE_PATH_CIRCLE:
1622		{
1623			// Increase the detail as the revolutions and twist increase.
1624			F32 twist_mag = fabs(params.getTwistBegin() - params.getTwist());
1625
1626			S32 sides = (S32)llfloor(llfloor((MIN_DETAIL_FACES * detail + twist_mag * 3.5f * (detail-0.5f))) * params.getRevolutions());
1627
1628			if (is_sculpted)
1629				sides = sculpt_size;
1630			
1631			genNGon(params, sides);
1632		}
1633		break;
1634
1635	case LL_PCODE_PATH_CIRCLE2:
1636		{
1637			if (params.getEnd() - params.getBegin() >= 0.99f &&
1638				params.getScaleX() >= .99f)
1639			{
1640				mOpen = FALSE;
1641			}
1642
1643			//genNGon(params, llfloor(MIN_DETAIL_FACES * detail), 4.f, 0.f);
1644			genNGon(params, llfloor(MIN_DETAIL_FACES * detail));
1645
1646			F32 t     = 0.f;
1647			F32 tStep = 1.0f / mPath.size();
1648
1649			F32 toggle = 0.5f;
1650			for (S32 i=0;i<(S32)mPath.size();i++)
1651			{
1652				mPath[i].mPos.mV[0] = toggle;
1653				if (toggle == 0.5f)
1654					toggle = -0.5f;
1655				else
1656					toggle = 0.5f;
1657				t += tStep;
1658			}
1659		}
1660
1661		break;
1662
1663	case LL_PCODE_PATH_TEST:
1664
1665		np     = 5;
1666		mStep = 1.0f / (np-1);
1667		
1668		mPath.resize(np);
1669
1670		for (S32 i=0;i<np;i++)
1671		{
1672			F32 t = (F32)i * mStep;
1673			mPath[i].mPos.setVec(0,
1674								lerp(0,   -sin(F_PI*params.getTwist()*t)*0.5f,t),
1675								lerp(-0.5, cos(F_PI*params.getTwist()*t)*0.5f,t));
1676			mPath[i].mScale.mV[0] = lerp(1,params.getScale().mV[0],t);
1677			mPath[i].mScale.mV[1] = lerp(1,params.getScale().mV[1],t);
1678			mPath[i].mTexT  = t;
1679			mPath[i].mRot.setQuat(F_PI * params.getTwist() * t,1,0,0);
1680		}
1681
1682		break;
1683	};
1684
1685	if (params.getTwist() != params.getTwistBegin()) mOpen = TRUE;
1686
1687	//if ((int(fabsf(params.getTwist() - params.getTwistBegin())*100))%100 != 0) {
1688	//	mOpen = TRUE;
1689	//}
1690	
1691	return TRUE;
1692}
1693
1694BOOL LLDynamicPath::generate(const LLPathParams& params, F32 detail, S32 split,
1695							 BOOL is_sculpted, S32 sculpt_size)
1696{
1697	LLMemType m1(LLMemType::MTYPE_VOLUME);
1698	
1699	mOpen = TRUE; // Draw end caps
1700	if (getPathLength() == 0)
1701	{
1702		// Path hasn't been generated yet.
1703		// Some algorithms later assume at least TWO path points.
1704		resizePath(2);
1705		for (U32 i = 0; i < 2; i++)
1706		{
1707			mPath[i].mPos.setVec(0, 0, 0);
1708			mPath[i].mRot.setQuat(0, 0, 0);
1709			mPath[i].mScale.setVec(1, 1);
1710			mPath[i].mTexT = 0;
1711		}
1712	}
1713
1714	return TRUE;
1715}
1716
1717
1718BOOL LLPathParams::importFile(LLFILE *fp)
1719{
1720	LLMemType m1(LLMemType::MTYPE_VOLUME);
1721	
1722	const S32 BUFSIZE = 16384;
1723	char buffer[BUFSIZE];	/* Flawfinder: ignore */
1724	// *NOTE: changing the size or type of these buffers will require
1725	// changing the sscanf below.
1726	char keyword[256];	/* Flawfinder: ignore */
1727	char valuestr[256];	/* Flawfinder: ignore */
1728	keyword[0] = 0;
1729	valuestr[0] = 0;
1730
1731	F32 tempF32;
1732	F32 x, y;
1733	U32 tempU32;
1734
1735	while (!feof(fp))
1736	{
1737		if (fgets(buffer, BUFSIZE, fp) == NULL)
1738		{
1739			buffer[0] = '\0';
1740		}
1741		
1742		sscanf(	/* Flawfinder: ignore */
1743			buffer,
1744			" %255s %255s",
1745			keyword, valuestr);
1746		if (!strcmp("{", keyword))
1747		{
1748			continue;
1749		}
1750		if (!strcmp("}",keyword))
1751		{
1752			break;
1753		}
1754		else if (!strcmp("curve", keyword))
1755		{
1756			sscanf(valuestr,"%d",&tempU32);
1757			setCurveType((U8) tempU32);
1758		}
1759		else if (!strcmp("begin",keyword))
1760		{
1761			sscanf(valuestr,"%g",&tempF32);
1762			setBegin(tempF32);
1763		}
1764		else if (!strcmp("end",keyword))
1765		{
1766			sscanf(valuestr,"%g",&tempF32);
1767			setEnd(tempF32);
1768		}
1769		else if (!strcmp("scale",keyword))
1770		{
1771			// Legacy for one dimensional scale per path
1772			sscanf(valuestr,"%g",&tempF32);
1773			setScale(tempF32, tempF32);
1774		}
1775		else if (!strcmp("scale_x", keyword))
1776		{
1777			sscanf(valuestr, "%g", &x);
1778			setScaleX(x);
1779		}
1780		else if (!strcmp("scale_y", keyword))
1781		{
1782			sscanf(valuestr, "%g", &y);
1783			setScaleY(y);
1784		}
1785		else if (!strcmp("shear_x", keyword))
1786		{
1787			sscanf(valuestr, "%g", &x);
1788			setShearX(x);
1789		}
1790		else if (!strcmp("shear_y", keyword))
1791		{
1792			sscanf(valuestr, "%g", &y);
1793			setShearY(y);
1794		}
1795		else if (!strcmp("twist",keyword))
1796		{
1797			sscanf(valuestr,"%g",&tempF32);
1798			setTwist(tempF32);
1799		}
1800		else if (!strcmp("twist_begin", keyword))
1801		{
1802			sscanf(valuestr, "%g", &y);
1803			setTwistBegin(y);
1804		}
1805		else if (!strcmp("radius_offset", keyword))
1806		{
1807			sscanf(valuestr, "%g", &y);
1808			setRadiusOffset(y);
1809		}
1810		else if (!strcmp("taper_x", keyword))
1811		{
1812			sscanf(valuestr, "%g", &y);
1813			setTaperX(y);
1814		}
1815		else if (!strcmp("taper_y", keyword))
1816		{
1817			sscanf(valuestr, "%g", &y);
1818			setTaperY(y);
1819		}
1820		else if (!strcmp("revolutions", keyword))
1821		{
1822			sscanf(valuestr, "%g", &y);
1823			setRevolutions(y);
1824		}
1825		else if (!strcmp("skew", keyword))
1826		{
1827			sscanf(valuestr, "%g", &y);
1828			setSkew(y);
1829		}
1830		else
1831		{
1832			llwarns << "unknown keyword " << " in path import" << llendl;
1833		}
1834	}
1835	return TRUE;
1836}
1837
1838
1839BOOL LLPathParams::exportFile(LLFILE *fp) const
1840{
1841	fprintf(fp, "\t\tpath 0\n");
1842	fprintf(fp, "\t\t{\n");
1843	fprintf(fp, "\t\t\tcurve\t%d\n", getCurveType());
1844	fprintf(fp, "\t\t\tbegin\t%g\n", getBegin());
1845	fprintf(fp, "\t\t\tend\t%g\n", getEnd());
1846	fprintf(fp, "\t\t\tscale_x\t%g\n", getScaleX() );
1847	fprintf(fp, "\t\t\tscale_y\t%g\n", getScaleY() );
1848	fprintf(fp, "\t\t\tshear_x\t%g\n", getShearX() );
1849	fprintf(fp, "\t\t\tshear_y\t%g\n", getShearY() );
1850	fprintf(fp,"\t\t\ttwist\t%g\n", getTwist());
1851	
1852	fprintf(fp,"\t\t\ttwist_begin\t%g\n", getTwistBegin());
1853	fprintf(fp,"\t\t\tradius_offset\t%g\n", getRadiusOffset());
1854	fprintf(fp,"\t\t\ttaper_x\t%g\n", getTaperX());
1855	fprintf(fp,"\t\t\ttaper_y\t%g\n", getTaperY());
1856	fprintf(fp,"\t\t\trevolutions\t%g\n", getRevolutions());
1857	fprintf(fp,"\t\t\tskew\t%g\n", getSkew());
1858
1859	fprintf(fp, "\t\t}\n");
1860	return TRUE;
1861}
1862
1863
1864BOOL LLPathParams::importLegacyStream(std::istream& input_stream)
1865{
1866	LLMemType m1(LLMemType::MTYPE_VOLUME);
1867	
1868	const S32 BUFSIZE = 16384;
1869	char buffer[BUFSIZE];	/* Flawfinder: ignore */
1870	// *NOTE: changing the size or type of these buffers will require
1871	// changing the sscanf below.
1872	char keyword[256];	/* Flawfinder: ignore */
1873	char valuestr[256];	/* Flawfinder: ignore */
1874	keyword[0] = 0;
1875	valuestr[0] = 0;
1876
1877	F32 tempF32;
1878	F32 x, y;
1879	U32 tempU32;
1880
1881	while (input_stream.good())
1882	{
1883		input_stream.getline(buffer, BUFSIZE);
1884		sscanf(	/* Flawfinder: ignore */
1885			buffer,
1886			" %255s %255s",
1887			keyword, valuestr);
1888		if (!strcmp("{", keyword))
1889		{
1890			continue;
1891		}
1892		if (!strcmp("}",keyword))
1893		{
1894			break;
1895		}
1896		else if (!strcmp("curve", keyword))
1897		{
1898			sscanf(valuestr,"%d",&tempU32);
1899			setCurveType((U8) tempU32);
1900		}
1901		else if (!strcmp("begin",keyword))
1902		{
1903			sscanf(valuestr,"%g",&tempF32);
1904			setBegin(tempF32);
1905		}
1906		else if (!strcmp("end",keyword))
1907		{
1908			sscanf(valuestr,"%g",&tempF32);
1909			setEnd(tempF32);
1910		}
1911		else if (!strcmp("scale",keyword))
1912		{
1913			// Legacy for one dimensional scale per path
1914			sscanf(valuestr,"%g",&tempF32);
1915			setScale(tempF32, tempF32);
1916		}
1917		else if (!strcmp("scale_x", keyword))
1918		{
1919			sscanf(valuestr, "%g", &x);
1920			setScaleX(x);
1921		}
1922		else if (!strcmp("scale_y", keyword))
1923		{
1924			sscanf(valuestr, "%g", &y);
1925			setScaleY(y);
1926		}
1927		else if (!strcmp("shear_x", keyword))
1928		{
1929			sscanf(valuestr, "%g", &x);
1930			setShearX(x);
1931		}
1932		else if (!strcmp("shear_y", keyword))
1933		{
1934			sscanf(valuestr, "%g", &y);
1935			setShearY(y);
1936		}
1937		else if (!strcmp("twist",keyword))
1938		{
1939			sscanf(valuestr,"%g",&tempF32);
1940			setTwist(tempF32);
1941		}
1942		else if (!strcmp("twist_begin", keyword))
1943		{
1944			sscanf(valuestr, "%g", &y);
1945			setTwistBegin(y);
1946		}
1947		else if (!strcmp("radius_offset", keyword))
1948		{
1949			sscanf(valuestr, "%g", &y);
1950			setRadiusOffset(y);
1951		}
1952		else if (!strcmp("taper_x", keyword))
1953		{
1954			sscanf(valuestr, "%g", &y);
1955			setTaperX(y);
1956		}
1957		else if (!strcmp("taper_y", keyword))
1958		{
1959			sscanf(valuestr, "%g", &y);
1960			setTaperY(y);
1961		}
1962		else if (!strcmp("revolutions", keyword))
1963		{
1964			sscanf(valuestr, "%g", &y);
1965			setRevolutions(y);
1966		}
1967		else if (!strcmp("skew", keyword))
1968		{
1969			sscanf(valuestr, "%g", &y);
1970			setSkew(y);
1971		}
1972		else
1973		{
1974			llwarns << "unknown keyword " << " in path import" << llendl;
1975		}
1976	}
1977	return TRUE;
1978}
1979
1980
1981BOOL LLPathParams::exportLegacyStream(std::ostream& output_stream) const
1982{
1983	output_stream << "\t\tpath 0\n";
1984	output_stream << "\t\t{\n";
1985	output_stream << "\t\t\tcurve\t" << (S32) getCurveType() << "\n";
1986	output_stream << "\t\t\tbegin\t" << getBegin() << "\n";
1987	output_stream << "\t\t\tend\t" << getEnd() << "\n";
1988	output_stream << "\t\t\tscale_x\t" << getScaleX()  << "\n";
1989	output_stream << "\t\t\tscale_y\t" << getScaleY()  << "\n";
1990	output_stream << "\t\t\tshear_x\t" << getShearX()  << "\n";
1991	output_stream << "\t\t\tshear_y\t" << getShearY()  << "\n";
1992	output_stream <<"\t\t\ttwist\t" << getTwist() << "\n";
1993	
1994	output_stream <<"\t\t\ttwist_begin\t" << getTwistBegin() << "\n";
1995	output_stream <<"\t\t\tradius_offset\t" << getRadiusOffset() << "\n";
1996	output_stream <<"\t\t\ttaper_x\t" << getTaperX() << "\n";
1997	output_stream <<"\t\t\ttaper_y\t" << getTaperY() << "\n";
1998	output_stream <<"\t\t\trevolutions\t" << getRevolutions() << "\n";
1999	output_stream <<"\t\t\tskew\t" << getSkew() << "\n";
2000
2001	output_stream << "\t\t}\n";
2002	return TRUE;
2003}
2004
2005LLSD LLPathParams::asLLSD() const
2006{
2007	LLSD sd = LLSD();
2008	sd["curve"] = getCurveType();
2009	sd["begin"] = getBegin();
2010	sd["end"] = getEnd();
2011	sd["scale_x"] = getScaleX();
2012	sd["scale_y"] = getScaleY();
2013	sd["shear_x"] = getShearX();
2014	sd["shear_y"] = getShearY();
2015	sd["twist"] = getTwist();
2016	sd["twist_begin"] = getTwistBegin();
2017	sd["radius_offset"] = getRadiusOffset();
2018	sd["taper_x"] = getTaperX();
2019	sd["taper_y"] = getTaperY();
2020	sd["revolutions"] = getRevolutions();
2021	sd["skew"] = getSkew();
2022
2023	return sd;
2024}
2025
2026bool LLPathParams::fromLLSD(LLSD& sd)
2027{
2028	setCurveType(sd["curve"].asInteger());
2029	setBegin((F32)sd["begin"].asReal());
2030	setEnd((F32)sd["end"].asReal());
2031	setScaleX((F32)sd["scale_x"].asReal());
2032	setScaleY((F32)sd["scale_y"].asReal());
2033	setShearX((F32)sd["shear_x"].asReal());
2034	setShearY((F32)sd["shear_y"].asReal());
2035	setTwist((F32)sd["twist"].asReal());
2036	setTwistBegin((F32)sd["twist_begin"].asReal());
2037	setRadiusOffset((F32)sd["radius_offset"].asReal());
2038	setTaperX((F32)sd["taper_x"].asReal());
2039	setTaperY((F32)sd["taper_y"].asReal());
2040	setRevolutions((F32)sd["revolutions"].asReal());
2041	setSkew((F32)sd["skew"].asReal());
2042	return true;
2043}
2044
2045void LLPathParams::copyParams(const LLPathParams &params)
2046{
2047	setCurveType(params.getCurveType());
2048	setBegin(para

Large files files are truncated, but you can click here to view the full file