summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/llmath/llvolume.cpp14466
-rw-r--r--indra/llrender/llglheaders.h12
-rw-r--r--indra/newview/llphysicsshapebuilderutil.cpp44
-rw-r--r--indra/newview/llphysicsshapebuilderutil.h94
-rw-r--r--indra/newview/llvoavatar.h2180
-rw-r--r--indra/newview/llvocache.cpp1512
6 files changed, 9158 insertions, 9150 deletions
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index dc360818d6..70e1e1f312 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -1,7233 +1,7233 @@
-/**
-
- * @file llvolume.cpp
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "llmemory.h"
-#include "llmath.h"
-
-#include <set>
-#if !LL_WINDOWS
-#include <stdint.h>
-#endif
-
-#include "llerror.h"
-#include "llmemtype.h"
-
-#include "llvolumemgr.h"
-#include "v2math.h"
-#include "v3math.h"
-#include "v4math.h"
-#include "m4math.h"
-#include "m3math.h"
-#include "llmatrix3a.h"
-#include "lloctree.h"
-#include "lldarray.h"
-#include "llvolume.h"
-#include "llvolumeoctree.h"
-#include "llstl.h"
-#include "llsdserialize.h"
-#include "llvector4a.h"
-#include "llmatrix4a.h"
-
-#define DEBUG_SILHOUETTE_BINORMALS 0
-#define DEBUG_SILHOUETTE_NORMALS 0 // TomY: Use this to display normals using the silhouette
-#define DEBUG_SILHOUETTE_EDGE_MAP 0 // DaveP: Use this to display edge map using the silhouette
-
-const F32 CUT_MIN = 0.f;
-const F32 CUT_MAX = 1.f;
-const F32 MIN_CUT_DELTA = 0.02f;
-
-const F32 HOLLOW_MIN = 0.f;
-const F32 HOLLOW_MAX = 0.95f;
-const F32 HOLLOW_MAX_SQUARE = 0.7f;
-
-const F32 TWIST_MIN = -1.f;
-const F32 TWIST_MAX = 1.f;
-
-const F32 RATIO_MIN = 0.f;
-const F32 RATIO_MAX = 2.f; // Tom Y: Inverted sense here: 0 = top taper, 2 = bottom taper
-
-const F32 HOLE_X_MIN= 0.05f;
-const F32 HOLE_X_MAX= 1.0f;
-
-const F32 HOLE_Y_MIN= 0.05f;
-const F32 HOLE_Y_MAX= 0.5f;
-
-const F32 SHEAR_MIN = -0.5f;
-const F32 SHEAR_MAX = 0.5f;
-
-const F32 REV_MIN = 1.f;
-const F32 REV_MAX = 4.f;
-
-const F32 TAPER_MIN = -1.f;
-const F32 TAPER_MAX = 1.f;
-
-const F32 SKEW_MIN = -0.95f;
-const F32 SKEW_MAX = 0.95f;
-
-const F32 SCULPT_MIN_AREA = 0.002f;
-const S32 SCULPT_MIN_AREA_DETAIL = 1;
-
-extern BOOL gDebugGL;
-
-void assert_aligned(void* ptr, uintptr_t alignment)
-{
-#if 0
- uintptr_t t = (uintptr_t) ptr;
- if (t%alignment != 0)
- {
- llerrs << "WTF?" << llendl;
- }
-#endif
-}
-
-BOOL check_same_clock_dir( const LLVector3& pt1, const LLVector3& pt2, const LLVector3& pt3, const LLVector3& norm)
-{
- LLVector3 test = (pt2-pt1)%(pt3-pt2);
-
- //answer
- if(test * norm < 0)
- {
- return FALSE;
- }
- else
- {
- return TRUE;
- }
-}
-
-BOOL LLLineSegmentBoxIntersect(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size)
-{
- return LLLineSegmentBoxIntersect(start.mV, end.mV, center.mV, size.mV);
-}
-
-BOOL LLLineSegmentBoxIntersect(const F32* start, const F32* end, const F32* center, const F32* size)
-{
- F32 fAWdU[3];
- F32 dir[3];
- F32 diff[3];
-
- for (U32 i = 0; i < 3; i++)
- {
- dir[i] = 0.5f * (end[i] - start[i]);
- diff[i] = (0.5f * (end[i] + start[i])) - center[i];
- fAWdU[i] = fabsf(dir[i]);
- if(fabsf(diff[i])>size[i] + fAWdU[i]) return false;
- }
-
- float f;
- f = dir[1] * diff[2] - dir[2] * diff[1]; if(fabsf(f)>size[1]*fAWdU[2] + size[2]*fAWdU[1]) return false;
- f = dir[2] * diff[0] - dir[0] * diff[2]; if(fabsf(f)>size[0]*fAWdU[2] + size[2]*fAWdU[0]) return false;
- f = dir[0] * diff[1] - dir[1] * diff[0]; if(fabsf(f)>size[0]*fAWdU[1] + size[1]*fAWdU[0]) return false;
-
- return true;
-}
-
-
-
-// intersect test between triangle vert0, vert1, vert2 and a ray from orig in direction dir.
-// returns TRUE if intersecting and returns barycentric coordinates in intersection_a, intersection_b,
-// and returns the intersection point along dir in intersection_t.
-
-// Moller-Trumbore algorithm
-BOOL LLTriangleRayIntersect(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir,
- F32& intersection_a, F32& intersection_b, F32& intersection_t)
-{
-
- /* find vectors for two edges sharing vert0 */
- LLVector4a edge1;
- edge1.setSub(vert1, vert0);
-
- LLVector4a edge2;
- edge2.setSub(vert2, vert0);
-
- /* begin calculating determinant - also used to calculate U parameter */
- LLVector4a pvec;
- pvec.setCross3(dir, edge2);
-
- /* if determinant is near zero, ray lies in plane of triangle */
- LLVector4a det;
- det.setAllDot3(edge1, pvec);
-
- if (det.greaterEqual(LLVector4a::getEpsilon()).getGatheredBits() & 0x7)
- {
- /* calculate distance from vert0 to ray origin */
- LLVector4a tvec;
- tvec.setSub(orig, vert0);
-
- /* calculate U parameter and test bounds */
- LLVector4a u;
- u.setAllDot3(tvec,pvec);
-
- if ((u.greaterEqual(LLVector4a::getZero()).getGatheredBits() & 0x7) &&
- (u.lessEqual(det).getGatheredBits() & 0x7))
- {
- /* prepare to test V parameter */
- LLVector4a qvec;
- qvec.setCross3(tvec, edge1);
-
- /* calculate V parameter and test bounds */
- LLVector4a v;
- v.setAllDot3(dir, qvec);
-
-
- //if (!(v < 0.f || u + v > det))
-
- LLVector4a sum_uv;
- sum_uv.setAdd(u, v);
-
- S32 v_gequal = v.greaterEqual(LLVector4a::getZero()).getGatheredBits() & 0x7;
- S32 sum_lequal = sum_uv.lessEqual(det).getGatheredBits() & 0x7;
-
- if (v_gequal && sum_lequal)
- {
- /* calculate t, scale parameters, ray intersects triangle */
- LLVector4a t;
- t.setAllDot3(edge2,qvec);
-
- t.div(det);
- u.div(det);
- v.div(det);
-
- intersection_a = u[0];
- intersection_b = v[0];
- intersection_t = t[0];
- return TRUE;
- }
- }
- }
-
- return FALSE;
-}
-
-BOOL LLTriangleRayIntersectTwoSided(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir,
- F32& intersection_a, F32& intersection_b, F32& intersection_t)
-{
- F32 u, v, t;
-
- /* find vectors for two edges sharing vert0 */
- LLVector4a edge1;
- edge1.setSub(vert1, vert0);
-
-
- LLVector4a edge2;
- edge2.setSub(vert2, vert0);
-
- /* begin calculating determinant - also used to calculate U parameter */
- LLVector4a pvec;
- pvec.setCross3(dir, edge2);
-
- /* if determinant is near zero, ray lies in plane of triangle */
- F32 det = edge1.dot3(pvec).getF32();
-
-
- if (det > -F_APPROXIMATELY_ZERO && det < F_APPROXIMATELY_ZERO)
- {
- return FALSE;
- }
-
- F32 inv_det = 1.f / det;
-
- /* calculate distance from vert0 to ray origin */
- LLVector4a tvec;
- tvec.setSub(orig, vert0);
-
- /* calculate U parameter and test bounds */
- u = (tvec.dot3(pvec).getF32()) * inv_det;
- if (u < 0.f || u > 1.f)
- {
- return FALSE;
- }
-
- /* prepare to test V parameter */
- tvec.sub(edge1);
-
- /* calculate V parameter and test bounds */
- v = (dir.dot3(tvec).getF32()) * inv_det;
-
- if (v < 0.f || u + v > 1.f)
- {
- return FALSE;
- }
-
- /* calculate t, ray intersects triangle */
- t = (edge2.dot3(tvec).getF32()) * inv_det;
-
- intersection_a = u;
- intersection_b = v;
- intersection_t = t;
-
-
- return TRUE;
-}
-
-//helper for non-aligned vectors
-BOOL LLTriangleRayIntersect(const LLVector3& vert0, const LLVector3& vert1, const LLVector3& vert2, const LLVector3& orig, const LLVector3& dir,
- F32& intersection_a, F32& intersection_b, F32& intersection_t, BOOL two_sided)
-{
- LLVector4a vert0a, vert1a, vert2a, origa, dira;
- vert0a.load3(vert0.mV);
- vert1a.load3(vert1.mV);
- vert2a.load3(vert2.mV);
- origa.load3(orig.mV);
- dira.load3(dir.mV);
-
- if (two_sided)
- {
- return LLTriangleRayIntersectTwoSided(vert0a, vert1a, vert2a, origa, dira,
- intersection_a, intersection_b, intersection_t);
- }
- else
- {
- return LLTriangleRayIntersect(vert0a, vert1a, vert2a, origa, dira,
- intersection_a, intersection_b, intersection_t);
- }
-}
-
-class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle>
-{
-public:
- const LLVolumeFace* mFace;
-
- LLVolumeOctreeRebound(const LLVolumeFace* face)
- {
- mFace = face;
- }
-
- virtual void visit(const LLOctreeNode<LLVolumeTriangle>* branch)
- { //this is a depth first traversal, so it's safe to assum all children have complete
- //bounding data
-
- LLVolumeOctreeListener* node = (LLVolumeOctreeListener*) branch->getListener(0);
-
- LLVector4a& min = node->mExtents[0];
- LLVector4a& max = node->mExtents[1];
-
- if (!branch->getData().empty())
- { //node has data, find AABB that binds data set
- const LLVolumeTriangle* tri = *(branch->getData().begin());
-
- //initialize min/max to first available vertex
- min = *(tri->mV[0]);
- max = *(tri->mV[0]);
-
- for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter =
- branch->getData().begin(); iter != branch->getData().end(); ++iter)
- { //for each triangle in node
-
- //stretch by triangles in node
- tri = *iter;
-
- min.setMin(min, *tri->mV[0]);
- min.setMin(min, *tri->mV[1]);
- min.setMin(min, *tri->mV[2]);
-
- max.setMax(max, *tri->mV[0]);
- max.setMax(max, *tri->mV[1]);
- max.setMax(max, *tri->mV[2]);
- }
- }
- else if (!branch->getChildren().empty())
- { //no data, but child nodes exist
- LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(0)->getListener(0);
-
- //initialize min/max to extents of first child
- min = child->mExtents[0];
- max = child->mExtents[1];
- }
- else
- {
- llerrs << "WTF? Empty leaf" << llendl;
- }
-
- for (S32 i = 0; i < branch->getChildCount(); ++i)
- { //stretch by child extents
- LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(i)->getListener(0);
- min.setMin(min, child->mExtents[0]);
- max.setMax(max, child->mExtents[1]);
- }
-
- node->mBounds[0].setAdd(min, max);
- node->mBounds[0].mul(0.5f);
-
- node->mBounds[1].setSub(max,min);
- node->mBounds[1].mul(0.5f);
- }
-};
-
-//-------------------------------------------------------------------
-// statics
-//-------------------------------------------------------------------
-
-
-//----------------------------------------------------
-
-LLProfile::Face* LLProfile::addCap(S16 faceID)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- Face *face = vector_append(mFaces, 1);
-
- face->mIndex = 0;
- face->mCount = mTotal;
- face->mScaleU= 1.0f;
- face->mCap = TRUE;
- face->mFaceID = faceID;
- return face;
-}
-
-LLProfile::Face* LLProfile::addFace(S32 i, S32 count, F32 scaleU, S16 faceID, BOOL flat)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- Face *face = vector_append(mFaces, 1);
-
- face->mIndex = i;
- face->mCount = count;
- face->mScaleU= scaleU;
-
- face->mFlat = flat;
- face->mCap = FALSE;
- face->mFaceID = faceID;
- return face;
-}
-
-// What is the bevel parameter used for? - DJS 04/05/02
-// Bevel parameter is currently unused but presumedly would support
-// filleted and chamfered corners
-void LLProfile::genNGon(const LLProfileParams& params, S32 sides, F32 offset, F32 bevel, F32 ang_scale, S32 split)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- // Generate an n-sided "circular" path.
- // 0 is (1,0), and we go counter-clockwise along a circular path from there.
- const F32 tableScale[] = { 1, 1, 1, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f };
- F32 scale = 0.5f;
- F32 t, t_step, t_first, t_fraction, ang, ang_step;
- LLVector3 pt1,pt2;
-
- F32 begin = params.getBegin();
- F32 end = params.getEnd();
-
- t_step = 1.0f / sides;
- ang_step = 2.0f*F_PI*t_step*ang_scale;
-
- // Scale to have size "match" scale. Compensates to get object to generally fill bounding box.
-
- S32 total_sides = llround(sides / ang_scale); // Total number of sides all around
-
- if (total_sides < 8)
- {
- scale = tableScale[total_sides];
- }
-
- t_first = floor(begin * sides) / (F32)sides;
-
- // pt1 is the first point on the fractional face.
- // Starting t and ang values for the first face
- t = t_first;
- ang = 2.0f*F_PI*(t*ang_scale + offset);
- pt1.setVec(cos(ang)*scale,sin(ang)*scale, t);
-
- // Increment to the next point.
- // pt2 is the end point on the fractional face
- t += t_step;
- ang += ang_step;
- pt2.setVec(cos(ang)*scale,sin(ang)*scale,t);
-
- t_fraction = (begin - t_first)*sides;
-
- // Only use if it's not almost exactly on an edge.
- if (t_fraction < 0.9999f)
- {
- LLVector3 new_pt = lerp(pt1, pt2, t_fraction);
- mProfile.push_back(new_pt);
- }
-
- // There's lots of potential here for floating point error to generate unneeded extra points - DJS 04/05/02
- while (t < end)
- {
- // Iterate through all the integer steps of t.
- pt1.setVec(cos(ang)*scale,sin(ang)*scale,t);
-
- if (mProfile.size() > 0) {
- LLVector3 p = mProfile[mProfile.size()-1];
- for (S32 i = 0; i < split && mProfile.size() > 0; i++) {
- mProfile.push_back(p+(pt1-p) * 1.0f/(float)(split+1) * (float)(i+1));
- }
- }
- mProfile.push_back(pt1);
-
- t += t_step;
- ang += ang_step;
- }
-
- t_fraction = (end - (t - t_step))*sides;
-
- // pt1 is the first point on the fractional face
- // pt2 is the end point on the fractional face
- pt2.setVec(cos(ang)*scale,sin(ang)*scale,t);
-
- // Find the fraction that we need to add to the end point.
- t_fraction = (end - (t - t_step))*sides;
- if (t_fraction > 0.0001f)
- {
- LLVector3 new_pt = lerp(pt1, pt2, t_fraction);
-
- if (mProfile.size() > 0) {
- LLVector3 p = mProfile[mProfile.size()-1];
- for (S32 i = 0; i < split && mProfile.size() > 0; i++) {
- mProfile.push_back(p+(new_pt-p) * 1.0f/(float)(split+1) * (float)(i+1));
- }
- }
- mProfile.push_back(new_pt);
- }
-
- // If we're sliced, the profile is open.
- if ((end - begin)*ang_scale < 0.99f)
- {
- if ((end - begin)*ang_scale > 0.5f)
- {
- mConcave = TRUE;
- }
- else
- {
- mConcave = FALSE;
- }
- mOpen = TRUE;
- if (params.getHollow() <= 0)
- {
- // put center point if not hollow.
- mProfile.push_back(LLVector3(0,0,0));
- }
- }
- else
- {
- // The profile isn't open.
- mOpen = FALSE;
- mConcave = FALSE;
- }
-
- mTotal = mProfile.size();
-}
-
-void LLProfile::genNormals(const LLProfileParams& params)
-{
- S32 count = mProfile.size();
-
- S32 outer_count;
- if (mTotalOut)
- {
- outer_count = mTotalOut;
- }
- else
- {
- outer_count = mTotal / 2;
- }
-
- mEdgeNormals.resize(count * 2);
- mEdgeCenters.resize(count * 2);
- mNormals.resize(count);
-
- LLVector2 pt0,pt1;
-
- BOOL hollow = (params.getHollow() > 0);
-
- S32 i0, i1, i2, i3, i4;
-
- // Parametrically generate normal
- for (i2 = 0; i2 < count; i2++)
- {
- mNormals[i2].mV[0] = mProfile[i2].mV[0];
- mNormals[i2].mV[1] = mProfile[i2].mV[1];
- if (hollow && (i2 >= outer_count))
- {
- mNormals[i2] *= -1.f;
- }
- if (mNormals[i2].magVec() < 0.001)
- {
- // Special case for point at center, get adjacent points.
- i1 = (i2 - 1) >= 0 ? i2 - 1 : count - 1;
- i0 = (i1 - 1) >= 0 ? i1 - 1 : count - 1;
- i3 = (i2 + 1) < count ? i2 + 1 : 0;
- i4 = (i3 + 1) < count ? i3 + 1 : 0;
-
- pt0.setVec(mProfile[i1].mV[VX] + mProfile[i1].mV[VX] - mProfile[i0].mV[VX],
- mProfile[i1].mV[VY] + mProfile[i1].mV[VY] - mProfile[i0].mV[VY]);
- pt1.setVec(mProfile[i3].mV[VX] + mProfile[i3].mV[VX] - mProfile[i4].mV[VX],
- mProfile[i3].mV[VY] + mProfile[i3].mV[VY] - mProfile[i4].mV[VY]);
-
- mNormals[i2] = pt0 + pt1;
- mNormals[i2] *= 0.5f;
- }
- mNormals[i2].normVec();
- }
-
- S32 num_normal_sets = isConcave() ? 2 : 1;
- for (S32 normal_set = 0; normal_set < num_normal_sets; normal_set++)
- {
- S32 point_num;
- for (point_num = 0; point_num < mTotal; point_num++)
- {
- LLVector3 point_1 = mProfile[point_num];
- point_1.mV[VZ] = 0.f;
-
- LLVector3 point_2;
-
- if (isConcave() && normal_set == 0 && point_num == (mTotal - 1) / 2)
- {
- point_2 = mProfile[mTotal - 1];
- }
- else if (isConcave() && normal_set == 1 && point_num == mTotal - 1)
- {
- point_2 = mProfile[(mTotal - 1) / 2];
- }
- else
- {
- LLVector3 delta_pos;
- S32 neighbor_point = (point_num + 1) % mTotal;
- while(delta_pos.magVecSquared() < 0.01f * 0.01f)
- {
- point_2 = mProfile[neighbor_point];
- delta_pos = point_2 - point_1;
- neighbor_point = (neighbor_point + 1) % mTotal;
- if (neighbor_point == point_num)
- {
- break;
- }
- }
- }
-
- point_2.mV[VZ] = 0.f;
- LLVector3 face_normal = (point_2 - point_1) % LLVector3::z_axis;
- face_normal.normVec();
- mEdgeNormals[normal_set * count + point_num] = face_normal;
- mEdgeCenters[normal_set * count + point_num] = lerp(point_1, point_2, 0.5f);
- }
- }
-}
-
-
-// Hollow is percent of the original bounding box, not of this particular
-// profile's geometry. Thus, a swept triangle needs lower hollow values than
-// a swept square.
-LLProfile::Face* LLProfile::addHole(const LLProfileParams& params, BOOL flat, F32 sides, F32 offset, F32 box_hollow, F32 ang_scale, S32 split)
-{
- // Note that addHole will NOT work for non-"circular" profiles, if we ever decide to use them.
-
- // Total add has number of vertices on outside.
- mTotalOut = mTotal;
-
- // Why is the "bevel" parameter -1? DJS 04/05/02
- genNGon(params, llfloor(sides),offset,-1, ang_scale, split);
-
- Face *face = addFace(mTotalOut, mTotal-mTotalOut,0,LL_FACE_INNER_SIDE, flat);
-
- std::vector<LLVector3> pt;
- pt.resize(mTotal) ;
-
- for (S32 i=mTotalOut;i<mTotal;i++)
- {
- pt[i] = mProfile[i] * box_hollow;
- }
-
- S32 j=mTotal-1;
- for (S32 i=mTotalOut;i<mTotal;i++)
- {
- mProfile[i] = pt[j--];
- }
-
- for (S32 i=0;i<(S32)mFaces.size();i++)
- {
- if (mFaces[i].mCap)
- {
- mFaces[i].mCount *= 2;
- }
- }
-
- return face;
-}
-
-
-
-BOOL LLProfile::generate(const LLProfileParams& params, BOOL path_open,F32 detail, S32 split,
- BOOL is_sculpted, S32 sculpt_size)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- if ((!mDirty) && (!is_sculpted))
- {
- return FALSE;
- }
- mDirty = FALSE;
-
- if (detail < MIN_LOD)
- {
- llinfos << "Generating profile with LOD < MIN_LOD. CLAMPING" << llendl;
- detail = MIN_LOD;
- }
-
- mProfile.clear();
- mFaces.clear();
-
- // Generate the face data
- S32 i;
- F32 begin = params.getBegin();
- F32 end = params.getEnd();
- F32 hollow = params.getHollow();
-
- // Quick validation to eliminate some server crashes.
- if (begin > end - 0.01f)
- {
- llwarns << "LLProfile::generate() assertion failed (begin >= end)" << llendl;
- return FALSE;
- }
-
- S32 face_num = 0;
-
- switch (params.getCurveType() & LL_PCODE_PROFILE_MASK)
- {
- case LL_PCODE_PROFILE_SQUARE:
- {
- genNGon(params, 4,-0.375, 0, 1, split);
- if (path_open)
- {
- addCap (LL_FACE_PATH_BEGIN);
- }
-
- for (i = llfloor(begin * 4.f); i < llfloor(end * 4.f + .999f); i++)
- {
- addFace((face_num++) * (split +1), split+2, 1, LL_FACE_OUTER_SIDE_0 << i, TRUE);
- }
-
- for (i = 0; i <(S32) mProfile.size(); i++)
- {
- // Scale by 4 to generate proper tex coords.
- mProfile[i].mV[2] *= 4.f;
- }
-
- if (hollow)
- {
- switch (params.getCurveType() & LL_PCODE_HOLE_MASK)
- {
- case LL_PCODE_HOLE_TRIANGLE:
- // This offset is not correct, but we can't change it now... DK 11/17/04
- addHole(params, TRUE, 3, -0.375f, hollow, 1.f, split);
- break;
- case LL_PCODE_HOLE_CIRCLE:
- // TODO: Compute actual detail levels for cubes
- addHole(params, FALSE, MIN_DETAIL_FACES * detail, -0.375f, hollow, 1.f);
- break;
- case LL_PCODE_HOLE_SAME:
- case LL_PCODE_HOLE_SQUARE:
- default:
- addHole(params, TRUE, 4, -0.375f, hollow, 1.f, split);
- break;
- }
- }
-
- if (path_open) {
- mFaces[0].mCount = mTotal;
- }
- }
- break;
- case LL_PCODE_PROFILE_ISOTRI:
- case LL_PCODE_PROFILE_RIGHTTRI:
- case LL_PCODE_PROFILE_EQUALTRI:
- {
- genNGon(params, 3,0, 0, 1, split);
- for (i = 0; i <(S32) mProfile.size(); i++)
- {
- // Scale by 3 to generate proper tex coords.
- mProfile[i].mV[2] *= 3.f;
- }
-
- if (path_open)
- {
- addCap(LL_FACE_PATH_BEGIN);
- }
-
- for (i = llfloor(begin * 3.f); i < llfloor(end * 3.f + .999f); i++)
- {
- addFace((face_num++) * (split +1), split+2, 1, LL_FACE_OUTER_SIDE_0 << i, TRUE);
- }
- if (hollow)
- {
- // Swept triangles need smaller hollowness values,
- // because the triangle doesn't fill the bounding box.
- F32 triangle_hollow = hollow / 2.f;
-
- switch (params.getCurveType() & LL_PCODE_HOLE_MASK)
- {
- case LL_PCODE_HOLE_CIRCLE:
- // TODO: Actually generate level of detail for triangles
- addHole(params, FALSE, MIN_DETAIL_FACES * detail, 0, triangle_hollow, 1.f);
- break;
- case LL_PCODE_HOLE_SQUARE:
- addHole(params, TRUE, 4, 0, triangle_hollow, 1.f, split);
- break;
- case LL_PCODE_HOLE_SAME:
- case LL_PCODE_HOLE_TRIANGLE:
- default:
- addHole(params, TRUE, 3, 0, triangle_hollow, 1.f, split);
- break;
- }
- }
- }
- break;
- case LL_PCODE_PROFILE_CIRCLE:
- {
- // If this has a square hollow, we should adjust the
- // number of faces a bit so that the geometry lines up.
- U8 hole_type=0;
- F32 circle_detail = MIN_DETAIL_FACES * detail;
- if (hollow)
- {
- hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK;
- if (hole_type == LL_PCODE_HOLE_SQUARE)
- {
- // Snap to the next multiple of four sides,
- // so that corners line up.
- circle_detail = llceil(circle_detail / 4.0f) * 4.0f;
- }
- }
-
- S32 sides = (S32)circle_detail;
-
- if (is_sculpted)
- sides = sculpt_size;
-
- genNGon(params, sides);
-
- if (path_open)
- {
- addCap (LL_FACE_PATH_BEGIN);
- }
-
- if (mOpen && !hollow)
- {
- addFace(0,mTotal-1,0,LL_FACE_OUTER_SIDE_0, FALSE);
- }
- else
- {
- addFace(0,mTotal,0,LL_FACE_OUTER_SIDE_0, FALSE);
- }
-
- if (hollow)
- {
- switch (hole_type)
- {
- case LL_PCODE_HOLE_SQUARE:
- addHole(params, TRUE, 4, 0, hollow, 1.f, split);
- break;
- case LL_PCODE_HOLE_TRIANGLE:
- addHole(params, TRUE, 3, 0, hollow, 1.f, split);
- break;
- case LL_PCODE_HOLE_CIRCLE:
- case LL_PCODE_HOLE_SAME:
- default:
- addHole(params, FALSE, circle_detail, 0, hollow, 1.f);
- break;
- }
- }
- }
- break;
- case LL_PCODE_PROFILE_CIRCLE_HALF:
- {
- // If this has a square hollow, we should adjust the
- // number of faces a bit so that the geometry lines up.
- U8 hole_type=0;
- // Number of faces is cut in half because it's only a half-circle.
- F32 circle_detail = MIN_DETAIL_FACES * detail * 0.5f;
- if (hollow)
- {
- hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK;
- if (hole_type == LL_PCODE_HOLE_SQUARE)
- {
- // Snap to the next multiple of four sides (div 2),
- // so that corners line up.
- circle_detail = llceil(circle_detail / 2.0f) * 2.0f;
- }
- }
- genNGon(params, llfloor(circle_detail), 0.5f, 0.f, 0.5f);
- if (path_open)
- {
- addCap(LL_FACE_PATH_BEGIN);
- }
- if (mOpen && !params.getHollow())
- {
- addFace(0,mTotal-1,0,LL_FACE_OUTER_SIDE_0, FALSE);
- }
- else
- {
- addFace(0,mTotal,0,LL_FACE_OUTER_SIDE_0, FALSE);
- }
-
- if (hollow)
- {
- switch (hole_type)
- {
- case LL_PCODE_HOLE_SQUARE:
- addHole(params, TRUE, 2, 0.5f, hollow, 0.5f, split);
- break;
- case LL_PCODE_HOLE_TRIANGLE:
- addHole(params, TRUE, 3, 0.5f, hollow, 0.5f, split);
- break;
- case LL_PCODE_HOLE_CIRCLE:
- case LL_PCODE_HOLE_SAME:
- default:
- addHole(params, FALSE, circle_detail, 0.5f, hollow, 0.5f);
- break;
- }
- }
-
- // Special case for openness of sphere
- if ((params.getEnd() - params.getBegin()) < 1.f)
- {
- mOpen = TRUE;
- }
- else if (!hollow)
- {
- mOpen = FALSE;
- mProfile.push_back(mProfile[0]);
- mTotal++;
- }
- }
- break;
- default:
- llerrs << "Unknown profile: getCurveType()=" << params.getCurveType() << llendl;
- break;
- };
-
- if (path_open)
- {
- addCap(LL_FACE_PATH_END); // bottom
- }
-
- if ( mOpen) // interior edge caps
- {
- addFace(mTotal-1, 2,0.5,LL_FACE_PROFILE_BEGIN, TRUE);
-
- if (hollow)
- {
- addFace(mTotalOut-1, 2,0.5,LL_FACE_PROFILE_END, TRUE);
- }
- else
- {
- addFace(mTotal-2, 2,0.5,LL_FACE_PROFILE_END, TRUE);
- }
- }
-
- //genNormals(params);
-
- return TRUE;
-}
-
-
-
-BOOL LLProfileParams::importFile(LLFILE *fp)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- const S32 BUFSIZE = 16384;
- char buffer[BUFSIZE]; /* Flawfinder: ignore */
- // *NOTE: changing the size or type of these buffers will require
- // changing the sscanf below.
- char keyword[256]; /* Flawfinder: ignore */
- char valuestr[256]; /* Flawfinder: ignore */
- keyword[0] = 0;
- valuestr[0] = 0;
- F32 tempF32;
- U32 tempU32;
-
- while (!feof(fp))
- {
- if (fgets(buffer, BUFSIZE, fp) == NULL)
- {
- buffer[0] = '\0';
- }
-
- sscanf( /* Flawfinder: ignore */
- buffer,
- " %255s %255s",
- keyword, valuestr);
- if (!strcmp("{", keyword))
- {
- continue;
- }
- if (!strcmp("}",keyword))
- {
- break;
- }
- else if (!strcmp("curve", keyword))
- {
- sscanf(valuestr,"%d",&tempU32);
- setCurveType((U8) tempU32);
- }
- else if (!strcmp("begin",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setBegin(tempF32);
- }
- else if (!strcmp("end",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setEnd(tempF32);
- }
- else if (!strcmp("hollow",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setHollow(tempF32);
- }
- else
- {
- llwarns << "unknown keyword " << keyword << " in profile import" << llendl;
- }
- }
-
- return TRUE;
-}
-
-
-BOOL LLProfileParams::exportFile(LLFILE *fp) const
-{
- fprintf(fp,"\t\tprofile 0\n");
- fprintf(fp,"\t\t{\n");
- fprintf(fp,"\t\t\tcurve\t%d\n", getCurveType());
- fprintf(fp,"\t\t\tbegin\t%g\n", getBegin());
- fprintf(fp,"\t\t\tend\t%g\n", getEnd());
- fprintf(fp,"\t\t\thollow\t%g\n", getHollow());
- fprintf(fp, "\t\t}\n");
- return TRUE;
-}
-
-
-BOOL LLProfileParams::importLegacyStream(std::istream& input_stream)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- const S32 BUFSIZE = 16384;
- char buffer[BUFSIZE]; /* Flawfinder: ignore */
- // *NOTE: changing the size or type of these buffers will require
- // changing the sscanf below.
- char keyword[256]; /* Flawfinder: ignore */
- char valuestr[256]; /* Flawfinder: ignore */
- keyword[0] = 0;
- valuestr[0] = 0;
- F32 tempF32;
- U32 tempU32;
-
- while (input_stream.good())
- {
- input_stream.getline(buffer, BUFSIZE);
- sscanf( /* Flawfinder: ignore */
- buffer,
- " %255s %255s",
- keyword,
- valuestr);
- if (!strcmp("{", keyword))
- {
- continue;
- }
- if (!strcmp("}",keyword))
- {
- break;
- }
- else if (!strcmp("curve", keyword))
- {
- sscanf(valuestr,"%d",&tempU32);
- setCurveType((U8) tempU32);
- }
- else if (!strcmp("begin",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setBegin(tempF32);
- }
- else if (!strcmp("end",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setEnd(tempF32);
- }
- else if (!strcmp("hollow",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setHollow(tempF32);
- }
- else
- {
- llwarns << "unknown keyword " << keyword << " in profile import" << llendl;
- }
- }
-
- return TRUE;
-}
-
-
-BOOL LLProfileParams::exportLegacyStream(std::ostream& output_stream) const
-{
- output_stream <<"\t\tprofile 0\n";
- output_stream <<"\t\t{\n";
- output_stream <<"\t\t\tcurve\t" << (S32) getCurveType() << "\n";
- output_stream <<"\t\t\tbegin\t" << getBegin() << "\n";
- output_stream <<"\t\t\tend\t" << getEnd() << "\n";
- output_stream <<"\t\t\thollow\t" << getHollow() << "\n";
- output_stream << "\t\t}\n";
- return TRUE;
-}
-
-LLSD LLProfileParams::asLLSD() const
-{
- LLSD sd;
-
- sd["curve"] = getCurveType();
- sd["begin"] = getBegin();
- sd["end"] = getEnd();
- sd["hollow"] = getHollow();
- return sd;
-}
-
-bool LLProfileParams::fromLLSD(LLSD& sd)
-{
- setCurveType(sd["curve"].asInteger());
- setBegin((F32)sd["begin"].asReal());
- setEnd((F32)sd["end"].asReal());
- setHollow((F32)sd["hollow"].asReal());
- return true;
-}
-
-void LLProfileParams::copyParams(const LLProfileParams &params)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
- setCurveType(params.getCurveType());
- setBegin(params.getBegin());
- setEnd(params.getEnd());
- setHollow(params.getHollow());
-}
-
-
-LLPath::~LLPath()
-{
-}
-
-void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 end_scale, F32 twist_scale)
-{
- // Generates a circular path, starting at (1, 0, 0), counterclockwise along the xz plane.
- const F32 tableScale[] = { 1, 1, 1, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f };
-
- F32 revolutions = params.getRevolutions();
- F32 skew = params.getSkew();
- F32 skew_mag = fabs(skew);
- F32 hole_x = params.getScaleX() * (1.0f - skew_mag);
- F32 hole_y = params.getScaleY();
-
- // Calculate taper begin/end for x,y (Negative means taper the beginning)
- F32 taper_x_begin = 1.0f;
- F32 taper_x_end = 1.0f - params.getTaperX();
- F32 taper_y_begin = 1.0f;
- F32 taper_y_end = 1.0f - params.getTaperY();
-
- if ( taper_x_end > 1.0f )
- {
- // Flip tapering.
- taper_x_begin = 2.0f - taper_x_end;
- taper_x_end = 1.0f;
- }
- if ( taper_y_end > 1.0f )
- {
- // Flip tapering.
- taper_y_begin = 2.0f - taper_y_end;
- taper_y_end = 1.0f;
- }
-
- // For spheres, the radius is usually zero.
- F32 radius_start = 0.5f;
- if (sides < 8)
- {
- radius_start = tableScale[sides];
- }
-
- // Scale the radius to take the hole size into account.
- radius_start *= 1.0f - hole_y;
-
- // Now check the radius offset to calculate the start,end radius. (Negative means
- // decrease the start radius instead).
- F32 radius_end = radius_start;
- F32 radius_offset = params.getRadiusOffset();
- if (radius_offset < 0.f)
- {
- radius_start *= 1.f + radius_offset;
- }
- else
- {
- radius_end *= 1.f - radius_offset;
- }
-
- // Is the path NOT a closed loop?
- mOpen = ( (params.getEnd()*end_scale - params.getBegin() < 1.0f) ||
- (skew_mag > 0.001f) ||
- (fabs(taper_x_end - taper_x_begin) > 0.001f) ||
- (fabs(taper_y_end - taper_y_begin) > 0.001f) ||
- (fabs(radius_end - radius_start) > 0.001f) );
-
- F32 ang, c, s;
- LLQuaternion twist, qang;
- PathPt *pt;
- LLVector3 path_axis (1.f, 0.f, 0.f);
- //LLVector3 twist_axis(0.f, 0.f, 1.f);
- F32 twist_begin = params.getTwistBegin() * twist_scale;
- F32 twist_end = params.getTwist() * twist_scale;
-
- // We run through this once before the main loop, to make sure
- // the path begins at the correct cut.
- F32 step= 1.0f / sides;
- F32 t = params.getBegin();
- pt = vector_append(mPath, 1);
- ang = 2.0f*F_PI*revolutions * t;
- s = sin(ang)*lerp(radius_start, radius_end, t);
- c = cos(ang)*lerp(radius_start, radius_end, t);
-
-
- pt->mPos.setVec(0 + lerp(0,params.getShear().mV[0],s)
- + lerp(-skew ,skew, t) * 0.5f,
- c + lerp(0,params.getShear().mV[1],s),
- s);
- pt->mScale.mV[VX] = hole_x * lerp(taper_x_begin, taper_x_end, t);
- pt->mScale.mV[VY] = hole_y * lerp(taper_y_begin, taper_y_end, t);
- pt->mTexT = t;
-
- // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
- twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
- // Rotate the point around the circle's center.
- qang.setQuat (ang,path_axis);
- pt->mRot = twist * qang;
-
- t+=step;
-
- // Snap to a quantized parameter, so that cut does not
- // affect most sample points.
- t = ((S32)(t * sides)) / (F32)sides;
-
- // Run through the non-cut dependent points.
- while (t < params.getEnd())
- {
- pt = vector_append(mPath, 1);
-
- ang = 2.0f*F_PI*revolutions * t;
- c = cos(ang)*lerp(radius_start, radius_end, t);
- s = sin(ang)*lerp(radius_start, radius_end, t);
-
- pt->mPos.setVec(0 + lerp(0,params.getShear().mV[0],s)
- + lerp(-skew ,skew, t) * 0.5f,
- c + lerp(0,params.getShear().mV[1],s),
- s);
-
- pt->mScale.mV[VX] = hole_x * lerp(taper_x_begin, taper_x_end, t);
- pt->mScale.mV[VY] = hole_y * lerp(taper_y_begin, taper_y_end, t);
- pt->mTexT = t;
-
- // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
- twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
- // Rotate the point around the circle's center.
- qang.setQuat (ang,path_axis);
- pt->mRot = twist * qang;
-
- t+=step;
- }
-
- // Make one final pass for the end cut.
- t = params.getEnd();
- pt = vector_append(mPath, 1);
- ang = 2.0f*F_PI*revolutions * t;
- c = cos(ang)*lerp(radius_start, radius_end, t);
- s = sin(ang)*lerp(radius_start, radius_end, t);
-
- pt->mPos.setVec(0 + lerp(0,params.getShear().mV[0],s)
- + lerp(-skew ,skew, t) * 0.5f,
- c + lerp(0,params.getShear().mV[1],s),
- s);
- pt->mScale.mV[VX] = hole_x * lerp(taper_x_begin, taper_x_end, t);
- pt->mScale.mV[VY] = hole_y * lerp(taper_y_begin, taper_y_end, t);
- pt->mTexT = t;
-
- // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
- twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
- // Rotate the point around the circle's center.
- qang.setQuat (ang,path_axis);
- pt->mRot = twist * qang;
-
- mTotal = mPath.size();
-}
-
-const LLVector2 LLPathParams::getBeginScale() const
-{
- LLVector2 begin_scale(1.f, 1.f);
- if (getScaleX() > 1)
- {
- begin_scale.mV[0] = 2-getScaleX();
- }
- if (getScaleY() > 1)
- {
- begin_scale.mV[1] = 2-getScaleY();
- }
- return begin_scale;
-}
-
-const LLVector2 LLPathParams::getEndScale() const
-{
- LLVector2 end_scale(1.f, 1.f);
- if (getScaleX() < 1)
- {
- end_scale.mV[0] = getScaleX();
- }
- if (getScaleY() < 1)
- {
- end_scale.mV[1] = getScaleY();
- }
- return end_scale;
-}
-
-BOOL LLPath::generate(const LLPathParams& params, F32 detail, S32 split,
- BOOL is_sculpted, S32 sculpt_size)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- if ((!mDirty) && (!is_sculpted))
- {
- return FALSE;
- }
-
- if (detail < MIN_LOD)
- {
- llinfos << "Generating path with LOD < MIN! Clamping to 1" << llendl;
- detail = MIN_LOD;
- }
-
- mDirty = FALSE;
- S32 np = 2; // hardcode for line
-
- mPath.clear();
- mOpen = TRUE;
-
- // Is this 0xf0 mask really necessary? DK 03/02/05
- switch (params.getCurveType() & 0xf0)
- {
- default:
- case LL_PCODE_PATH_LINE:
- {
- // Take the begin/end twist into account for detail.
- np = llfloor(fabs(params.getTwistBegin() - params.getTwist()) * 3.5f * (detail-0.5f)) + 2;
- if (np < split+2)
- {
- np = split+2;
- }
-
- mStep = 1.0f / (np-1);
-
- mPath.resize(np);
-
- LLVector2 start_scale = params.getBeginScale();
- LLVector2 end_scale = params.getEndScale();
-
- for (S32 i=0;i<np;i++)
- {
- F32 t = lerp(params.getBegin(),params.getEnd(),(F32)i * mStep);
- mPath[i].mPos.setVec(lerp(0,params.getShear().mV[0],t),
- lerp(0,params.getShear().mV[1],t),
- t - 0.5f);
- mPath[i].mRot.setQuat(lerp(F_PI * params.getTwistBegin(),F_PI * params.getTwist(),t),0,0,1);
- mPath[i].mScale.mV[0] = lerp(start_scale.mV[0],end_scale.mV[0],t);
- mPath[i].mScale.mV[1] = lerp(start_scale.mV[1],end_scale.mV[1],t);
- mPath[i].mTexT = t;
- }
- }
- break;
-
- case LL_PCODE_PATH_CIRCLE:
- {
- // Increase the detail as the revolutions and twist increase.
- F32 twist_mag = fabs(params.getTwistBegin() - params.getTwist());
-
- S32 sides = (S32)llfloor(llfloor((MIN_DETAIL_FACES * detail + twist_mag * 3.5f * (detail-0.5f))) * params.getRevolutions());
-
- if (is_sculpted)
- sides = sculpt_size;
-
- genNGon(params, sides);
- }
- break;
-
- case LL_PCODE_PATH_CIRCLE2:
- {
- if (params.getEnd() - params.getBegin() >= 0.99f &&
- params.getScaleX() >= .99f)
- {
- mOpen = FALSE;
- }
-
- //genNGon(params, llfloor(MIN_DETAIL_FACES * detail), 4.f, 0.f);
- genNGon(params, llfloor(MIN_DETAIL_FACES * detail));
-
- F32 t = 0.f;
- F32 tStep = 1.0f / mPath.size();
-
- F32 toggle = 0.5f;
- for (S32 i=0;i<(S32)mPath.size();i++)
- {
- mPath[i].mPos.mV[0] = toggle;
- if (toggle == 0.5f)
- toggle = -0.5f;
- else
- toggle = 0.5f;
- t += tStep;
- }
- }
-
- break;
-
- case LL_PCODE_PATH_TEST:
-
- np = 5;
- mStep = 1.0f / (np-1);
-
- mPath.resize(np);
-
- for (S32 i=0;i<np;i++)
- {
- F32 t = (F32)i * mStep;
- mPath[i].mPos.setVec(0,
- lerp(0, -sin(F_PI*params.getTwist()*t)*0.5f,t),
- lerp(-0.5, cos(F_PI*params.getTwist()*t)*0.5f,t));
- mPath[i].mScale.mV[0] = lerp(1,params.getScale().mV[0],t);
- mPath[i].mScale.mV[1] = lerp(1,params.getScale().mV[1],t);
- mPath[i].mTexT = t;
- mPath[i].mRot.setQuat(F_PI * params.getTwist() * t,1,0,0);
- }
-
- break;
- };
-
- if (params.getTwist() != params.getTwistBegin()) mOpen = TRUE;
-
- //if ((int(fabsf(params.getTwist() - params.getTwistBegin())*100))%100 != 0) {
- // mOpen = TRUE;
- //}
-
- return TRUE;
-}
-
-BOOL LLDynamicPath::generate(const LLPathParams& params, F32 detail, S32 split,
- BOOL is_sculpted, S32 sculpt_size)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- mOpen = TRUE; // Draw end caps
- if (getPathLength() == 0)
- {
- // Path hasn't been generated yet.
- // Some algorithms later assume at least TWO path points.
- resizePath(2);
- for (U32 i = 0; i < 2; i++)
- {
- mPath[i].mPos.setVec(0, 0, 0);
- mPath[i].mRot.setQuat(0, 0, 0);
- mPath[i].mScale.setVec(1, 1);
- mPath[i].mTexT = 0;
- }
- }
-
- return TRUE;
-}
-
-
-BOOL LLPathParams::importFile(LLFILE *fp)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- const S32 BUFSIZE = 16384;
- char buffer[BUFSIZE]; /* Flawfinder: ignore */
- // *NOTE: changing the size or type of these buffers will require
- // changing the sscanf below.
- char keyword[256]; /* Flawfinder: ignore */
- char valuestr[256]; /* Flawfinder: ignore */
- keyword[0] = 0;
- valuestr[0] = 0;
-
- F32 tempF32;
- F32 x, y;
- U32 tempU32;
-
- while (!feof(fp))
- {
- if (fgets(buffer, BUFSIZE, fp) == NULL)
- {
- buffer[0] = '\0';
- }
-
- sscanf( /* Flawfinder: ignore */
- buffer,
- " %255s %255s",
- keyword, valuestr);
- if (!strcmp("{", keyword))
- {
- continue;
- }
- if (!strcmp("}",keyword))
- {
- break;
- }
- else if (!strcmp("curve", keyword))
- {
- sscanf(valuestr,"%d",&tempU32);
- setCurveType((U8) tempU32);
- }
- else if (!strcmp("begin",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setBegin(tempF32);
- }
- else if (!strcmp("end",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setEnd(tempF32);
- }
- else if (!strcmp("scale",keyword))
- {
- // Legacy for one dimensional scale per path
- sscanf(valuestr,"%g",&tempF32);
- setScale(tempF32, tempF32);
- }
- else if (!strcmp("scale_x", keyword))
- {
- sscanf(valuestr, "%g", &x);
- setScaleX(x);
- }
- else if (!strcmp("scale_y", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setScaleY(y);
- }
- else if (!strcmp("shear_x", keyword))
- {
- sscanf(valuestr, "%g", &x);
- setShearX(x);
- }
- else if (!strcmp("shear_y", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setShearY(y);
- }
- else if (!strcmp("twist",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setTwist(tempF32);
- }
- else if (!strcmp("twist_begin", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setTwistBegin(y);
- }
- else if (!strcmp("radius_offset", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setRadiusOffset(y);
- }
- else if (!strcmp("taper_x", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setTaperX(y);
- }
- else if (!strcmp("taper_y", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setTaperY(y);
- }
- else if (!strcmp("revolutions", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setRevolutions(y);
- }
- else if (!strcmp("skew", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setSkew(y);
- }
- else
- {
- llwarns << "unknown keyword " << " in path import" << llendl;
- }
- }
- return TRUE;
-}
-
-
-BOOL LLPathParams::exportFile(LLFILE *fp) const
-{
- fprintf(fp, "\t\tpath 0\n");
- fprintf(fp, "\t\t{\n");
- fprintf(fp, "\t\t\tcurve\t%d\n", getCurveType());
- fprintf(fp, "\t\t\tbegin\t%g\n", getBegin());
- fprintf(fp, "\t\t\tend\t%g\n", getEnd());
- fprintf(fp, "\t\t\tscale_x\t%g\n", getScaleX() );
- fprintf(fp, "\t\t\tscale_y\t%g\n", getScaleY() );
- fprintf(fp, "\t\t\tshear_x\t%g\n", getShearX() );
- fprintf(fp, "\t\t\tshear_y\t%g\n", getShearY() );
- fprintf(fp,"\t\t\ttwist\t%g\n", getTwist());
-
- fprintf(fp,"\t\t\ttwist_begin\t%g\n", getTwistBegin());
- fprintf(fp,"\t\t\tradius_offset\t%g\n", getRadiusOffset());
- fprintf(fp,"\t\t\ttaper_x\t%g\n", getTaperX());
- fprintf(fp,"\t\t\ttaper_y\t%g\n", getTaperY());
- fprintf(fp,"\t\t\trevolutions\t%g\n", getRevolutions());
- fprintf(fp,"\t\t\tskew\t%g\n", getSkew());
-
- fprintf(fp, "\t\t}\n");
- return TRUE;
-}
-
-
-BOOL LLPathParams::importLegacyStream(std::istream& input_stream)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- const S32 BUFSIZE = 16384;
- char buffer[BUFSIZE]; /* Flawfinder: ignore */
- // *NOTE: changing the size or type of these buffers will require
- // changing the sscanf below.
- char keyword[256]; /* Flawfinder: ignore */
- char valuestr[256]; /* Flawfinder: ignore */
- keyword[0] = 0;
- valuestr[0] = 0;
-
- F32 tempF32;
- F32 x, y;
- U32 tempU32;
-
- while (input_stream.good())
- {
- input_stream.getline(buffer, BUFSIZE);
- sscanf( /* Flawfinder: ignore */
- buffer,
- " %255s %255s",
- keyword, valuestr);
- if (!strcmp("{", keyword))
- {
- continue;
- }
- if (!strcmp("}",keyword))
- {
- break;
- }
- else if (!strcmp("curve", keyword))
- {
- sscanf(valuestr,"%d",&tempU32);
- setCurveType((U8) tempU32);
- }
- else if (!strcmp("begin",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setBegin(tempF32);
- }
- else if (!strcmp("end",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setEnd(tempF32);
- }
- else if (!strcmp("scale",keyword))
- {
- // Legacy for one dimensional scale per path
- sscanf(valuestr,"%g",&tempF32);
- setScale(tempF32, tempF32);
- }
- else if (!strcmp("scale_x", keyword))
- {
- sscanf(valuestr, "%g", &x);
- setScaleX(x);
- }
- else if (!strcmp("scale_y", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setScaleY(y);
- }
- else if (!strcmp("shear_x", keyword))
- {
- sscanf(valuestr, "%g", &x);
- setShearX(x);
- }
- else if (!strcmp("shear_y", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setShearY(y);
- }
- else if (!strcmp("twist",keyword))
- {
- sscanf(valuestr,"%g",&tempF32);
- setTwist(tempF32);
- }
- else if (!strcmp("twist_begin", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setTwistBegin(y);
- }
- else if (!strcmp("radius_offset", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setRadiusOffset(y);
- }
- else if (!strcmp("taper_x", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setTaperX(y);
- }
- else if (!strcmp("taper_y", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setTaperY(y);
- }
- else if (!strcmp("revolutions", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setRevolutions(y);
- }
- else if (!strcmp("skew", keyword))
- {
- sscanf(valuestr, "%g", &y);
- setSkew(y);
- }
- else
- {
- llwarns << "unknown keyword " << " in path import" << llendl;
- }
- }
- return TRUE;
-}
-
-
-BOOL LLPathParams::exportLegacyStream(std::ostream& output_stream) const
-{
- output_stream << "\t\tpath 0\n";
- output_stream << "\t\t{\n";
- output_stream << "\t\t\tcurve\t" << (S32) getCurveType() << "\n";
- output_stream << "\t\t\tbegin\t" << getBegin() << "\n";
- output_stream << "\t\t\tend\t" << getEnd() << "\n";
- output_stream << "\t\t\tscale_x\t" << getScaleX() << "\n";
- output_stream << "\t\t\tscale_y\t" << getScaleY() << "\n";
- output_stream << "\t\t\tshear_x\t" << getShearX() << "\n";
- output_stream << "\t\t\tshear_y\t" << getShearY() << "\n";
- output_stream <<"\t\t\ttwist\t" << getTwist() << "\n";
-
- output_stream <<"\t\t\ttwist_begin\t" << getTwistBegin() << "\n";
- output_stream <<"\t\t\tradius_offset\t" << getRadiusOffset() << "\n";
- output_stream <<"\t\t\ttaper_x\t" << getTaperX() << "\n";
- output_stream <<"\t\t\ttaper_y\t" << getTaperY() << "\n";
- output_stream <<"\t\t\trevolutions\t" << getRevolutions() << "\n";
- output_stream <<"\t\t\tskew\t" << getSkew() << "\n";
-
- output_stream << "\t\t}\n";
- return TRUE;
-}
-
-LLSD LLPathParams::asLLSD() const
-{
- LLSD sd = LLSD();
- sd["curve"] = getCurveType();
- sd["begin"] = getBegin();
- sd["end"] = getEnd();
- sd["scale_x"] = getScaleX();
- sd["scale_y"] = getScaleY();
- sd["shear_x"] = getShearX();
- sd["shear_y"] = getShearY();
- sd["twist"] = getTwist();
- sd["twist_begin"] = getTwistBegin();
- sd["radius_offset"] = getRadiusOffset();
- sd["taper_x"] = getTaperX();
- sd["taper_y"] = getTaperY();
- sd["revolutions"] = getRevolutions();
- sd["skew"] = getSkew();
-
- return sd;
-}
-
-bool LLPathParams::fromLLSD(LLSD& sd)
-{
- setCurveType(sd["curve"].asInteger());
- setBegin((F32)sd["begin"].asReal());
- setEnd((F32)sd["end"].asReal());
- setScaleX((F32)sd["scale_x"].asReal());
- setScaleY((F32)sd["scale_y"].asReal());
- setShearX((F32)sd["shear_x"].asReal());
- setShearY((F32)sd["shear_y"].asReal());
- setTwist((F32)sd["twist"].asReal());
- setTwistBegin((F32)sd["twist_begin"].asReal());
- setRadiusOffset((F32)sd["radius_offset"].asReal());
- setTaperX((F32)sd["taper_x"].asReal());
- setTaperY((F32)sd["taper_y"].asReal());
- setRevolutions((F32)sd["revolutions"].asReal());
- setSkew((F32)sd["skew"].asReal());
- return true;
-}
-
-void LLPathParams::copyParams(const LLPathParams &params)
-{
- setCurveType(params.getCurveType());
- setBegin(params.getBegin());
- setEnd(params.getEnd());
- setScale(params.getScaleX(), params.getScaleY() );
- setShear(params.getShearX(), params.getShearY() );
- setTwist(params.getTwist());
- setTwistBegin(params.getTwistBegin());
- setRadiusOffset(params.getRadiusOffset());
- setTaper( params.getTaperX(), params.getTaperY() );
- setRevolutions(params.getRevolutions());
- setSkew(params.getSkew());
-}
-
-S32 profile_delete_lock = 1 ;
-LLProfile::~LLProfile()
-{
- if(profile_delete_lock)
- {
- llerrs << "LLProfile should not be deleted here!" << llendl ;
- }
-}
-
-
-S32 LLVolume::sNumMeshPoints = 0;
-
-LLVolume::LLVolume(const LLVolumeParams &params, const F32 detail, const BOOL generate_single_face, const BOOL is_unique)
- : mParams(params)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- mUnique = is_unique;
- mFaceMask = 0x0;
- mDetail = detail;
- mSculptLevel = -2;
- mIsTetrahedron = FALSE;
- mLODScaleBias.setVec(1,1,1);
- mHullPoints = NULL;
- mHullIndices = NULL;
- mNumHullPoints = 0;
- mNumHullIndices = 0;
-
- // set defaults
- if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE)
- {
- mPathp = new LLDynamicPath();
- }
- else
- {
- mPathp = new LLPath();
- }
- mProfilep = new LLProfile();
-
- mGenerateSingleFace = generate_single_face;
-
- generate();
-
- if (mParams.getSculptID().isNull() && mParams.getSculptType() == LL_SCULPT_TYPE_NONE)
- {
- createVolumeFaces();
- }
-}
-
-void LLVolume::resizePath(S32 length)
-{
- mPathp->resizePath(length);
- mVolumeFaces.clear();
-}
-
-void LLVolume::regen()
-{
- generate();
- createVolumeFaces();
-}
-
-void LLVolume::genBinormals(S32 face)
-{
- mVolumeFaces[face].createBinormals();
-}
-
-LLVolume::~LLVolume()
-{
- sNumMeshPoints -= mMesh.size();
- delete mPathp;
-
- profile_delete_lock = 0 ;
- delete mProfilep;
- profile_delete_lock = 1 ;
-
- mPathp = NULL;
- mProfilep = NULL;
- mVolumeFaces.clear();
-
- free(mHullPoints);
- mHullPoints = NULL;
- free(mHullIndices);
- mHullIndices = NULL;
-}
-
-BOOL LLVolume::generate()
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
- llassert_always(mProfilep);
-
- //Added 10.03.05 Dave Parks
- // Split is a parameter to LLProfile::generate that tesselates edges on the profile
- // to prevent lighting and texture interpolation errors on triangles that are
- // stretched due to twisting or scaling on the path.
- S32 split = (S32) ((mDetail)*0.66f);
-
- if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_LINE &&
- (mParams.getPathParams().getScale().mV[0] != 1.0f ||
- mParams.getPathParams().getScale().mV[1] != 1.0f) &&
- (mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_SQUARE ||
- mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_ISOTRI ||
- mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_EQUALTRI ||
- mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_RIGHTTRI))
- {
- split = 0;
- }
-
- mLODScaleBias.setVec(0.5f, 0.5f, 0.5f);
-
- F32 profile_detail = mDetail;
- F32 path_detail = mDetail;
-
- U8 path_type = mParams.getPathParams().getCurveType();
- U8 profile_type = mParams.getProfileParams().getCurveType();
-
- if (path_type == LL_PCODE_PATH_LINE && profile_type == LL_PCODE_PROFILE_CIRCLE)
- { //cylinders don't care about Z-Axis
- mLODScaleBias.setVec(0.6f, 0.6f, 0.0f);
- }
- else if (path_type == LL_PCODE_PATH_CIRCLE)
- {
- mLODScaleBias.setVec(0.6f, 0.6f, 0.6f);
- }
-
- //********************************************************************
- //debug info, to be removed
- if((U32)(mPathp->mPath.size() * mProfilep->mProfile.size()) > (1u << 20))
- {
- llinfos << "sizeS: " << mPathp->mPath.size() << " sizeT: " << mProfilep->mProfile.size() << llendl ;
- llinfos << "path_detail : " << path_detail << " split: " << split << " profile_detail: " << profile_detail << llendl ;
- llinfos << mParams << llendl ;
- llinfos << "more info to check if mProfilep is deleted or not." << llendl ;
- llinfos << mProfilep->mNormals.size() << " : " << mProfilep->mFaces.size() << " : " << mProfilep->mEdgeNormals.size() << " : " << mProfilep->mEdgeCenters.size() << llendl ;
-
- llerrs << "LLVolume corrupted!" << llendl ;
- }
- //********************************************************************
-
- BOOL regenPath = mPathp->generate(mParams.getPathParams(), path_detail, split);
- BOOL regenProf = mProfilep->generate(mParams.getProfileParams(), mPathp->isOpen(),profile_detail, split);
-
- if (regenPath || regenProf )
- {
- S32 sizeS = mPathp->mPath.size();
- S32 sizeT = mProfilep->mProfile.size();
-
- //********************************************************************
- //debug info, to be removed
- if((U32)(sizeS * sizeT) > (1u << 20))
- {
- llinfos << "regenPath: " << (S32)regenPath << " regenProf: " << (S32)regenProf << llendl ;
- llinfos << "sizeS: " << sizeS << " sizeT: " << sizeT << llendl ;
- llinfos << "path_detail : " << path_detail << " split: " << split << " profile_detail: " << profile_detail << llendl ;
- llinfos << mParams << llendl ;
- llinfos << "more info to check if mProfilep is deleted or not." << llendl ;
- llinfos << mProfilep->mNormals.size() << " : " << mProfilep->mFaces.size() << " : " << mProfilep->mEdgeNormals.size() << " : " << mProfilep->mEdgeCenters.size() << llendl ;
-
- llerrs << "LLVolume corrupted!" << llendl ;
- }
- //********************************************************************
-
- sNumMeshPoints -= mMesh.size();
- mMesh.resize(sizeT * sizeS);
- sNumMeshPoints += mMesh.size();
-
- //generate vertex positions
-
- // Run along the path.
- for (S32 s = 0; s < sizeS; ++s)
- {
- LLVector2 scale = mPathp->mPath[s].mScale;
- LLQuaternion rot = mPathp->mPath[s].mRot;
-
- // Run along the profile.
- for (S32 t = 0; t < sizeT; ++t)
- {
- S32 m = s*sizeT + t;
- Point& pt = mMesh[m];
-
- pt.mPos.mV[0] = mProfilep->mProfile[t].mV[0] * scale.mV[0];
- pt.mPos.mV[1] = mProfilep->mProfile[t].mV[1] * scale.mV[1];
- pt.mPos.mV[2] = 0.0f;
- pt.mPos = pt.mPos * rot;
- pt.mPos += mPathp->mPath[s].mPos;
- }
- }
-
- for (std::vector<LLProfile::Face>::iterator iter = mProfilep->mFaces.begin();
- iter != mProfilep->mFaces.end(); ++iter)
- {
- LLFaceID id = iter->mFaceID;
- mFaceMask |= id;
- }
-
- return TRUE;
- }
- return FALSE;
-}
-
-void LLVolumeFace::VertexData::init()
-{
- if (!mData)
- {
- mData = (LLVector4a*) malloc(sizeof(LLVector4a)*2);
- }
-}
-
-LLVolumeFace::VertexData::VertexData()
-{
- mData = NULL;
- init();
-}
-
-LLVolumeFace::VertexData::VertexData(const VertexData& rhs)
-{
- mData = NULL;
- *this = rhs;
-}
-
-const LLVolumeFace::VertexData& LLVolumeFace::VertexData::operator=(const LLVolumeFace::VertexData& rhs)
-{
- if (this != &rhs)
- {
- init();
- LLVector4a::memcpyNonAliased16((F32*) mData, (F32*) rhs.mData, 2*sizeof(LLVector4a));
- mTexCoord = rhs.mTexCoord;
- }
- return *this;
-}
-
-LLVolumeFace::VertexData::~VertexData()
-{
- free(mData);
- mData = NULL;
-}
-
-LLVector4a& LLVolumeFace::VertexData::getPosition()
-{
- return mData[POSITION];
-}
-
-LLVector4a& LLVolumeFace::VertexData::getNormal()
-{
- return mData[NORMAL];
-}
-
-const LLVector4a& LLVolumeFace::VertexData::getPosition() const
-{
- return mData[POSITION];
-}
-
-const LLVector4a& LLVolumeFace::VertexData::getNormal() const
-{
- return mData[NORMAL];
-}
-
-
-void LLVolumeFace::VertexData::setPosition(const LLVector4a& pos)
-{
- mData[POSITION] = pos;
-}
-
-void LLVolumeFace::VertexData::setNormal(const LLVector4a& norm)
-{
- mData[NORMAL] = norm;
-}
-
-bool LLVolumeFace::VertexData::operator<(const LLVolumeFace::VertexData& rhs)const
-{
- const F32* lp = this->getPosition().getF32ptr();
- const F32* rp = rhs.getPosition().getF32ptr();
-
- if (lp[0] != rp[0])
- {
- return lp[0] < rp[0];
- }
-
- if (rp[1] != lp[1])
- {
- return lp[1] < rp[1];
- }
-
- if (rp[2] != lp[2])
- {
- return lp[2] < rp[2];
- }
-
- lp = getNormal().getF32ptr();
- rp = rhs.getNormal().getF32ptr();
-
- if (lp[0] != rp[0])
- {
- return lp[0] < rp[0];
- }
-
- if (rp[1] != lp[1])
- {
- return lp[1] < rp[1];
- }
-
- if (rp[2] != lp[2])
- {
- return lp[2] < rp[2];
- }
-
- if (mTexCoord.mV[0] != rhs.mTexCoord.mV[0])
- {
- return mTexCoord.mV[0] < rhs.mTexCoord.mV[0];
- }
-
- return mTexCoord.mV[1] < rhs.mTexCoord.mV[1];
-}
-
-bool LLVolumeFace::VertexData::operator==(const LLVolumeFace::VertexData& rhs)const
-{
- return mData[POSITION].equals3(rhs.getPosition()) &&
- mData[NORMAL].equals3(rhs.getNormal()) &&
- mTexCoord == rhs.mTexCoord;
-}
-
-bool LLVolumeFace::VertexData::compareNormal(const LLVolumeFace::VertexData& rhs, F32 angle_cutoff) const
-{
- bool retval = false;
- if (rhs.mData[POSITION].equals3(mData[POSITION]) && rhs.mTexCoord == mTexCoord)
- {
- if (angle_cutoff > 1.f)
- {
- retval = (mData[NORMAL].equals3(rhs.mData[NORMAL]));
- }
- else
- {
- F32 cur_angle = rhs.mData[NORMAL].dot3(mData[NORMAL]).getF32();
- retval = cur_angle > angle_cutoff;
- }
- }
-
- return retval;
-}
-
-bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
-{
- //input stream is now pointing at a zlib compressed block of LLSD
- //decompress block
- LLSD mdl;
- if (!unzip_llsd(mdl, is, size))
- {
- llwarns << "not a valid mesh asset!" << llendl;
- return false;
- }
-
- {
- U32 face_count = mdl.size();
-
- if (face_count == 0)
- {
- llerrs << "WTF?" << llendl;
- }
-
- mVolumeFaces.resize(face_count);
-
- for (U32 i = 0; i < face_count; ++i)
- {
- LLSD::Binary pos = mdl[i]["Position"];
- LLSD::Binary norm = mdl[i]["Normal"];
- LLSD::Binary tc = mdl[i]["TexCoord0"];
- LLSD::Binary idx = mdl[i]["TriangleList"];
-
- LLVolumeFace& face = mVolumeFaces[i];
-
- //copy out indices
- face.resizeIndices(idx.size()/2);
-
- if (idx.empty() || face.mNumIndices < 3)
- { //why is there an empty index list?
- llerrs <<"WTF?" << llendl;
- continue;
- }
-
- U16* indices = (U16*) &(idx[0]);
- for (U32 j = 0; j < idx.size()/2; ++j)
- {
- face.mIndices[j] = indices[j];
- }
-
- //copy out vertices
- U32 num_verts = pos.size()/(3*2);
- face.resizeVertices(num_verts);
-
- if (mdl[i].has("Weights"))
- {
- face.allocateWeights(num_verts);
-
- LLSD::Binary weights = mdl[i]["Weights"];
-
- U32 idx = 0;
-
- U32 cur_vertex = 0;
- while (idx < weights.size() && cur_vertex < num_verts)
- {
- const U8 END_INFLUENCES = 0xFF;
- U8 joint = weights[idx++];
-
- U32 cur_influence = 0;
- LLVector4 wght(0,0,0,0);
-
- while (joint != END_INFLUENCES && idx < weights.size())
- {
- U16 influence = weights[idx++];
- influence |= ((U16) weights[idx++] << 8);
-
- F32 w = llclamp((F32) influence / 65535.f, 0.f, 0.99999f);
- wght.mV[cur_influence++] = (F32) joint + w;
-
- if (cur_influence >= 4)
- {
- joint = END_INFLUENCES;
- }
- else
- {
- joint = weights[idx++];
- }
- }
-
- face.mWeights[cur_vertex].loadua(wght.mV);
-
- cur_vertex++;
- }
-
- if (cur_vertex != num_verts || idx != weights.size())
- {
- llwarns << "Vertex weight count does not match vertex count!" << llendl;
- }
-
- }
-
- LLVector3 minp;
- LLVector3 maxp;
- LLVector2 min_tc;
- LLVector2 max_tc;
-
- minp.setValue(mdl[i]["PositionDomain"]["Min"]);
- maxp.setValue(mdl[i]["PositionDomain"]["Max"]);
- LLVector4a min_pos, max_pos;
- min_pos.load3(minp.mV);
- max_pos.load3(maxp.mV);
-
- min_tc.setValue(mdl[i]["TexCoord0Domain"]["Min"]);
- max_tc.setValue(mdl[i]["TexCoord0Domain"]["Max"]);
-
- LLVector4a pos_range;
- pos_range.setSub(max_pos, min_pos);
- LLVector2 tc_range = max_tc - min_tc;
-
- LLVector4a* pos_out = face.mPositions;
- LLVector4a* norm_out = face.mNormals;
- LLVector2* tc_out = face.mTexCoords;
-
- for (U32 j = 0; j < num_verts; ++j)
- {
- U16* v = (U16*) &(pos[j*3*2]);
-
- pos_out->set((F32) v[0], (F32) v[1], (F32) v[2]);
- pos_out->div(65535.f);
- pos_out->mul(pos_range);
- pos_out->add(min_pos);
-
- pos_out++;
-
- U16* n = (U16*) &(norm[j*3*2]);
-
- norm_out->set((F32) n[0], (F32) n[1], (F32) n[2]);
- norm_out->div(65535.f);
- norm_out->mul(2.f);
- norm_out->sub(1.f);
- norm_out++;
-
- U16* t = (U16*) &(tc[j*2*2]);
-
- tc_out->mV[0] = (F32) t[0] / 65535.f * tc_range.mV[0] + min_tc.mV[0];
- tc_out->mV[1] = (F32) t[1] / 65535.f * tc_range.mV[1] + min_tc.mV[1];
-
- tc_out++;
- }
-
-
- // modifier flags?
- bool do_mirror = (mParams.getSculptType() & LL_SCULPT_FLAG_MIRROR);
- bool do_invert = (mParams.getSculptType() &LL_SCULPT_FLAG_INVERT);
-
-
- // translate to actions:
- bool do_reflect_x = false;
- bool do_reverse_triangles = false;
- bool do_invert_normals = false;
-
- if (do_mirror)
- {
- do_reflect_x = true;
- do_reverse_triangles = !do_reverse_triangles;
- }
-
- if (do_invert)
- {
- do_invert_normals = true;
- do_reverse_triangles = !do_reverse_triangles;
- }
-
- // now do the work
-
- if (do_reflect_x)
- {
- LLVector4a* p = (LLVector4a*) face.mPositions;
- LLVector4a* n = (LLVector4a*) face.mNormals;
-
- for (S32 i = 0; i < face.mNumVertices; i++)
- {
- p[i].mul(-1.0f);
- n[i].mul(-1.0f);
- }
- }
-
- if (do_invert_normals)
- {
- LLVector4a* n = (LLVector4a*) face.mNormals;
-
- for (S32 i = 0; i < face.mNumVertices; i++)
- {
- n[i].mul(-1.0f);
- }
- }
-
- if (do_reverse_triangles)
- {
- for (U32 j = 0; j < face.mNumIndices; j += 3)
- {
- // swap the 2nd and 3rd index
- S32 swap = face.mIndices[j+1];
- face.mIndices[j+1] = face.mIndices[j+2];
- face.mIndices[j+2] = swap;
- }
- }
-
- //calculate bounding box
- LLVector4a& min = face.mExtents[0];
- LLVector4a& max = face.mExtents[1];
-
- min.clear();
- max.clear();
- min = max = face.mPositions[0];
-
- for (S32 i = 1; i < face.mNumVertices; ++i)
- {
- min.setMin(min, face.mPositions[i]);
- max.setMax(max, face.mPositions[i]);
- }
- }
- }
-
- mSculptLevel = 0; // success!
-
- cacheOptimize();
-
- return true;
-}
-
-void tetrahedron_set_normal(LLVolumeFace::VertexData* cv)
-{
- LLVector4a v0;
- v0.setSub(cv[1].getPosition(), cv[0].getNormal());
- LLVector4a v1;
- v1.setSub(cv[2].getNormal(), cv[0].getPosition());
-
- cv[0].getNormal().setCross3(v0,v1);
- cv[0].getNormal().normalize3fast();
- cv[1].setNormal(cv[0].getNormal());
- cv[2].setNormal(cv[1].getNormal());
-}
-
-BOOL LLVolume::isTetrahedron()
-{
- return mIsTetrahedron;
-}
-
-void LLVolume::makeTetrahedron()
-{
- mVolumeFaces.clear();
-
- LLVolumeFace face;
-
- F32 x = 0.25f;
- LLVector4a p[] =
- { //unit tetrahedron corners
- LLVector4a(x,x,x),
- LLVector4a(-x,-x,x),
- LLVector4a(-x,x,-x),
- LLVector4a(x,-x,-x)
- };
-
- face.mExtents[0].splat(-x);
- face.mExtents[1].splat(x);
-
- LLVolumeFace::VertexData cv[3];
-
- //set texture coordinates
- cv[0].mTexCoord = LLVector2(0,0);
- cv[1].mTexCoord = LLVector2(1,0);
- cv[2].mTexCoord = LLVector2(0.5f, 0.5f*F_SQRT3);
-
-
- //side 1
- cv[0].setPosition(p[1]);
- cv[1].setPosition(p[0]);
- cv[2].setPosition(p[2]);
-
- tetrahedron_set_normal(cv);
-
- face.resizeVertices(12);
- face.resizeIndices(12);
-
- LLVector4a* v = (LLVector4a*) face.mPositions;
- LLVector4a* n = (LLVector4a*) face.mNormals;
- LLVector2* tc = (LLVector2*) face.mTexCoords;
-
- v[0] = cv[0].getPosition();
- v[1] = cv[1].getPosition();
- v[2] = cv[2].getPosition();
- v += 3;
-
- n[0] = cv[0].getNormal();
- n[1] = cv[1].getNormal();
- n[2] = cv[2].getNormal();
- n += 3;
-
- tc[0] = cv[0].mTexCoord;
- tc[1] = cv[1].mTexCoord;
- tc[2] = cv[2].mTexCoord;
- tc += 3;
-
-
- //side 2
- cv[0].setPosition(p[3]);
- cv[1].setPosition(p[0]);
- cv[2].setPosition(p[1]);
-
- tetrahedron_set_normal(cv);
-
- v[0] = cv[0].getPosition();
- v[1] = cv[1].getPosition();
- v[2] = cv[2].getPosition();
- v += 3;
-
- n[0] = cv[0].getNormal();
- n[1] = cv[1].getNormal();
- n[2] = cv[2].getNormal();
- n += 3;
-
- tc[0] = cv[0].mTexCoord;
- tc[1] = cv[1].mTexCoord;
- tc[2] = cv[2].mTexCoord;
- tc += 3;
-
- //side 3
- cv[0].setPosition(p[3]);
- cv[1].setPosition(p[1]);
- cv[2].setPosition(p[2]);
-
- tetrahedron_set_normal(cv);
-
- v[0] = cv[0].getPosition();
- v[1] = cv[1].getPosition();
- v[2] = cv[2].getPosition();
- v += 3;
-
- n[0] = cv[0].getNormal();
- n[1] = cv[1].getNormal();
- n[2] = cv[2].getNormal();
- n += 3;
-
- tc[0] = cv[0].mTexCoord;
- tc[1] = cv[1].mTexCoord;
- tc[2] = cv[2].mTexCoord;
- tc += 3;
-
- //side 4
- cv[0].setPosition(p[2]);
- cv[1].setPosition(p[0]);
- cv[2].setPosition(p[3]);
-
- tetrahedron_set_normal(cv);
-
- v[0] = cv[0].getPosition();
- v[1] = cv[1].getPosition();
- v[2] = cv[2].getPosition();
- v += 3;
-
- n[0] = cv[0].getNormal();
- n[1] = cv[1].getNormal();
- n[2] = cv[2].getNormal();
- n += 3;
-
- tc[0] = cv[0].mTexCoord;
- tc[1] = cv[1].mTexCoord;
- tc[2] = cv[2].mTexCoord;
- tc += 3;
-
- //set index buffer
- for (U16 i = 0; i < 12; i++)
- {
- face.mIndices[i] = i;
- }
-
- mVolumeFaces.push_back(face);
- mSculptLevel = 0;
- mIsTetrahedron = TRUE;
-}
-
-void LLVolume::copyVolumeFaces(const LLVolume* volume)
-{
- mVolumeFaces = volume->mVolumeFaces;
- mSculptLevel = 0;
- mIsTetrahedron = FALSE;
-}
-
-void LLVolume::cacheOptimize()
-{
- for (S32 i = 0; i < mVolumeFaces.size(); ++i)
- {
- mVolumeFaces[i].cacheOptimize();
- }
-}
-
-
-S32 LLVolume::getNumFaces() const
-{
- U8 sculpt_type = (mParams.getSculptType() & LL_SCULPT_TYPE_MASK);
-
- if (sculpt_type == LL_SCULPT_TYPE_MESH)
- {
- return LL_SCULPT_MESH_MAX_FACES;
- }
-
- return (S32)mProfilep->mFaces.size();
-}
-
-
-void LLVolume::createVolumeFaces()
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- if (mGenerateSingleFace)
- {
- // do nothing
- }
- else
- {
- S32 num_faces = getNumFaces();
- BOOL partial_build = TRUE;
- if (num_faces != mVolumeFaces.size())
- {
- partial_build = FALSE;
- mVolumeFaces.resize(num_faces);
- }
- // Initialize volume faces with parameter data
- for (S32 i = 0; i < (S32)mVolumeFaces.size(); i++)
- {
- LLVolumeFace& vf = mVolumeFaces[i];
- LLProfile::Face& face = mProfilep->mFaces[i];
- vf.mBeginS = face.mIndex;
- vf.mNumS = face.mCount;
- if (vf.mNumS < 0)
- {
- llerrs << "Volume face corruption detected." << llendl;
- }
-
- vf.mBeginT = 0;
- vf.mNumT= getPath().mPath.size();
- vf.mID = i;
-
- // Set the type mask bits correctly
- if (mParams.getProfileParams().getHollow() > 0)
- {
- vf.mTypeMask |= LLVolumeFace::HOLLOW_MASK;
- }
- if (mProfilep->isOpen())
- {
- vf.mTypeMask |= LLVolumeFace::OPEN_MASK;
- }
- if (face.mCap)
- {
- vf.mTypeMask |= LLVolumeFace::CAP_MASK;
- if (face.mFaceID == LL_FACE_PATH_BEGIN)
- {
- vf.mTypeMask |= LLVolumeFace::TOP_MASK;
- }
- else
- {
- llassert(face.mFaceID == LL_FACE_PATH_END);
- vf.mTypeMask |= LLVolumeFace::BOTTOM_MASK;
- }
- }
- else if (face.mFaceID & (LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END))
- {
- vf.mTypeMask |= LLVolumeFace::FLAT_MASK | LLVolumeFace::END_MASK;
- }
- else
- {
- vf.mTypeMask |= LLVolumeFace::SIDE_MASK;
- if (face.mFlat)
- {
- vf.mTypeMask |= LLVolumeFace::FLAT_MASK;
- }
- if (face.mFaceID & LL_FACE_INNER_SIDE)
- {
- vf.mTypeMask |= LLVolumeFace::INNER_MASK;
- if (face.mFlat && vf.mNumS > 2)
- { //flat inner faces have to copy vert normals
- vf.mNumS = vf.mNumS*2;
- if (vf.mNumS < 0)
- {
- llerrs << "Volume face corruption detected." << llendl;
- }
- }
- }
- else
- {
- vf.mTypeMask |= LLVolumeFace::OUTER_MASK;
- }
- }
- }
-
- for (face_list_t::iterator iter = mVolumeFaces.begin();
- iter != mVolumeFaces.end(); ++iter)
- {
- (*iter).create(this, partial_build);
- }
- }
-}
-
-
-inline LLVector3 sculpt_rgb_to_vector(U8 r, U8 g, U8 b)
-{
- // maps RGB values to vector values [0..255] -> [-0.5..0.5]
- LLVector3 value;
- value.mV[VX] = r / 255.f - 0.5f;
- value.mV[VY] = g / 255.f - 0.5f;
- value.mV[VZ] = b / 255.f - 0.5f;
-
- return value;
-}
-
-inline U32 sculpt_xy_to_index(U32 x, U32 y, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components)
-{
- U32 index = (x + y * sculpt_width) * sculpt_components;
- return index;
-}
-
-
-inline U32 sculpt_st_to_index(S32 s, S32 t, S32 size_s, S32 size_t, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components)
-{
- U32 x = (U32) ((F32)s/(size_s) * (F32) sculpt_width);
- U32 y = (U32) ((F32)t/(size_t) * (F32) sculpt_height);
-
- return sculpt_xy_to_index(x, y, sculpt_width, sculpt_height, sculpt_components);
-}
-
-
-inline LLVector3 sculpt_index_to_vector(U32 index, const U8* sculpt_data)
-{
- LLVector3 v = sculpt_rgb_to_vector(sculpt_data[index], sculpt_data[index+1], sculpt_data[index+2]);
-
- return v;
-}
-
-inline LLVector3 sculpt_st_to_vector(S32 s, S32 t, S32 size_s, S32 size_t, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data)
-{
- U32 index = sculpt_st_to_index(s, t, size_s, size_t, sculpt_width, sculpt_height, sculpt_components);
-
- return sculpt_index_to_vector(index, sculpt_data);
-}
-
-inline LLVector3 sculpt_xy_to_vector(U32 x, U32 y, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data)
-{
- U32 index = sculpt_xy_to_index(x, y, sculpt_width, sculpt_height, sculpt_components);
-
- return sculpt_index_to_vector(index, sculpt_data);
-}
-
-
-F32 LLVolume::sculptGetSurfaceArea()
-{
- // test to see if image has enough variation to create non-degenerate geometry
-
- F32 area = 0;
-
- S32 sizeS = mPathp->mPath.size();
- S32 sizeT = mProfilep->mProfile.size();
-
- for (S32 s = 0; s < sizeS-1; s++)
- {
- for (S32 t = 0; t < sizeT-1; t++)
- {
- // get four corners of quad
- LLVector3 p1 = mMesh[(s )*sizeT + (t )].mPos;
- LLVector3 p2 = mMesh[(s+1)*sizeT + (t )].mPos;
- LLVector3 p3 = mMesh[(s )*sizeT + (t+1)].mPos;
- LLVector3 p4 = mMesh[(s+1)*sizeT + (t+1)].mPos;
-
- // compute the area of the quad by taking the length of the cross product of the two triangles
- LLVector3 cross1 = (p1 - p2) % (p1 - p3);
- LLVector3 cross2 = (p4 - p2) % (p4 - p3);
- area += (cross1.magVec() + cross2.magVec()) / 2.0;
- }
- }
-
- return area;
-}
-
-// create placeholder shape
-void LLVolume::sculptGeneratePlaceholder()
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- S32 sizeS = mPathp->mPath.size();
- S32 sizeT = mProfilep->mProfile.size();
-
- S32 line = 0;
-
- // for now, this is a sphere.
- for (S32 s = 0; s < sizeS; s++)
- {
- for (S32 t = 0; t < sizeT; t++)
- {
- S32 i = t + line;
- Point& pt = mMesh[i];
-
-
- F32 u = (F32)s/(sizeS-1);
- F32 v = (F32)t/(sizeT-1);
-
- const F32 RADIUS = (F32) 0.3;
-
- pt.mPos.mV[0] = (F32)(sin(F_PI * v) * cos(2.0 * F_PI * u) * RADIUS);
- pt.mPos.mV[1] = (F32)(sin(F_PI * v) * sin(2.0 * F_PI * u) * RADIUS);
- pt.mPos.mV[2] = (F32)(cos(F_PI * v) * RADIUS);
-
- }
- line += sizeT;
- }
-}
-
-// create the vertices from the map
-void LLVolume::sculptGenerateMapVertices(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, U8 sculpt_type)
-{
- U8 sculpt_stitching = sculpt_type & LL_SCULPT_TYPE_MASK;
- BOOL sculpt_invert = sculpt_type & LL_SCULPT_FLAG_INVERT;
- BOOL sculpt_mirror = sculpt_type & LL_SCULPT_FLAG_MIRROR;
- BOOL reverse_horizontal = (sculpt_invert ? !sculpt_mirror : sculpt_mirror); // XOR
-
-
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- S32 sizeS = mPathp->mPath.size();
- S32 sizeT = mProfilep->mProfile.size();
-
- S32 line = 0;
- for (S32 s = 0; s < sizeS; s++)
- {
- // Run along the profile.
- for (S32 t = 0; t < sizeT; t++)
- {
- S32 i = t + line;
- Point& pt = mMesh[i];
-
- S32 reversed_t = t;
-
- if (reverse_horizontal)
- {
- reversed_t = sizeT - t - 1;
- }
-
- U32 x = (U32) ((F32)reversed_t/(sizeT-1) * (F32) sculpt_width);
- U32 y = (U32) ((F32)s/(sizeS-1) * (F32) sculpt_height);
-
-
- if (y == 0) // top row stitching
- {
- // pinch?
- if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE)
- {
- x = sculpt_width / 2;
- }
- }
-
- if (y == sculpt_height) // bottom row stitching
- {
- // wrap?
- if (sculpt_stitching == LL_SCULPT_TYPE_TORUS)
- {
- y = 0;
- }
- else
- {
- y = sculpt_height - 1;
- }
-
- // pinch?
- if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE)
- {
- x = sculpt_width / 2;
- }
- }
-
- if (x == sculpt_width) // side stitching
- {
- // wrap?
- if ((sculpt_stitching == LL_SCULPT_TYPE_SPHERE) ||
- (sculpt_stitching == LL_SCULPT_TYPE_TORUS) ||
- (sculpt_stitching == LL_SCULPT_TYPE_CYLINDER))
- {
- x = 0;
- }
-
- else
- {
- x = sculpt_width - 1;
- }
- }
-
- pt.mPos = sculpt_xy_to_vector(x, y, sculpt_width, sculpt_height, sculpt_components, sculpt_data);
-
- if (sculpt_mirror)
- {
- pt.mPos.mV[VX] *= -1.f;
- }
- }
-
- line += sizeT;
- }
-}
-
-
-const S32 SCULPT_REZ_1 = 6; // changed from 4 to 6 - 6 looks round whereas 4 looks square
-const S32 SCULPT_REZ_2 = 8;
-const S32 SCULPT_REZ_3 = 16;
-const S32 SCULPT_REZ_4 = 32;
-
-S32 sculpt_sides(F32 detail)
-{
-
- // detail is usually one of: 1, 1.5, 2.5, 4.0.
-
- if (detail <= 1.0)
- {
- return SCULPT_REZ_1;
- }
- if (detail <= 2.0)
- {
- return SCULPT_REZ_2;
- }
- if (detail <= 3.0)
- {
- return SCULPT_REZ_3;
- }
- else
- {
- return SCULPT_REZ_4;
- }
-}
-
-
-
-// determine the number of vertices in both s and t direction for this sculpt
-void sculpt_calc_mesh_resolution(U16 width, U16 height, U8 type, F32 detail, S32& s, S32& t)
-{
- // this code has the following properties:
- // 1) the aspect ratio of the mesh is as close as possible to the ratio of the map
- // while still using all available verts
- // 2) the mesh cannot have more verts than is allowed by LOD
- // 3) the mesh cannot have more verts than is allowed by the map
-
- S32 max_vertices_lod = (S32)pow((double)sculpt_sides(detail), 2.0);
- S32 max_vertices_map = width * height / 4;
-
- S32 vertices;
- if (max_vertices_map > 0)
- vertices = llmin(max_vertices_lod, max_vertices_map);
- else
- vertices = max_vertices_lod;
-
-
- F32 ratio;
- if ((width == 0) || (height == 0))
- ratio = 1.f;
- else
- ratio = (F32) width / (F32) height;
-
-
- s = (S32)(F32) sqrt(((F32)vertices / ratio));
-
- s = llmax(s, 4); // no degenerate sizes, please
- t = vertices / s;
-
- t = llmax(t, 4); // no degenerate sizes, please
- s = vertices / t;
-}
-
-// sculpt replaces generate() for sculpted surfaces
-void LLVolume::sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, S32 sculpt_level)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
- U8 sculpt_type = mParams.getSculptType();
-
- BOOL data_is_empty = FALSE;
-
- if (sculpt_width == 0 || sculpt_height == 0 || sculpt_components < 3 || sculpt_data == NULL)
- {
- sculpt_level = -1;
- data_is_empty = TRUE;
- }
-
- S32 requested_sizeS = 0;
- S32 requested_sizeT = 0;
-
- sculpt_calc_mesh_resolution(sculpt_width, sculpt_height, sculpt_type, mDetail, requested_sizeS, requested_sizeT);
-
- mPathp->generate(mParams.getPathParams(), mDetail, 0, TRUE, requested_sizeS);
- mProfilep->generate(mParams.getProfileParams(), mPathp->isOpen(), mDetail, 0, TRUE, requested_sizeT);
-
- S32 sizeS = mPathp->mPath.size(); // we requested a specific size, now see what we really got
- S32 sizeT = mProfilep->mProfile.size(); // we requested a specific size, now see what we really got
-
- // weird crash bug - DEV-11158 - trying to collect more data:
- if ((sizeS == 0) || (sizeT == 0))
- {
- llwarns << "sculpt bad mesh size " << sizeS << " " << sizeT << llendl;
- }
-
- sNumMeshPoints -= mMesh.size();
- mMesh.resize(sizeS * sizeT);
- sNumMeshPoints += mMesh.size();
-
- //generate vertex positions
- if (!data_is_empty)
- {
- sculptGenerateMapVertices(sculpt_width, sculpt_height, sculpt_components, sculpt_data, sculpt_type);
-
- // don't test lowest LOD to support legacy content DEV-33670
- if (mDetail > SCULPT_MIN_AREA_DETAIL)
- {
- if (sculptGetSurfaceArea() < SCULPT_MIN_AREA)
- {
- data_is_empty = TRUE;
- }
- }
- }
-
- if (data_is_empty)
- {
- sculptGeneratePlaceholder();
- }
-
-
-
- for (S32 i = 0; i < (S32)mProfilep->mFaces.size(); i++)
- {
- mFaceMask |= mProfilep->mFaces[i].mFaceID;
- }
-
- mSculptLevel = sculpt_level;
-
- // Delete any existing faces so that they get regenerated
- mVolumeFaces.clear();
-
- createVolumeFaces();
-}
-
-
-
-
-BOOL LLVolume::isCap(S32 face)
-{
- return mProfilep->mFaces[face].mCap;
-}
-
-BOOL LLVolume::isFlat(S32 face)
-{
- return mProfilep->mFaces[face].mFlat;
-}
-
-
-bool LLVolumeParams::isSculpt() const
-{
- return mSculptID.notNull();
-}
-
-bool LLVolumeParams::isMeshSculpt() const
-{
- return isSculpt() && ((mSculptType & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH);
-}
-
-bool LLVolumeParams::operator==(const LLVolumeParams &params) const
-{
- return ( (getPathParams() == params.getPathParams()) &&
- (getProfileParams() == params.getProfileParams()) &&
- (mSculptID == params.mSculptID) &&
- (mSculptType == params.mSculptType) );
-}
-
-bool LLVolumeParams::operator!=(const LLVolumeParams &params) const
-{
- return ( (getPathParams() != params.getPathParams()) ||
- (getProfileParams() != params.getProfileParams()) ||
- (mSculptID != params.mSculptID) ||
- (mSculptType != params.mSculptType) );
-}
-
-bool LLVolumeParams::operator<(const LLVolumeParams &params) const
-{
- if( getPathParams() != params.getPathParams() )
- {
- return getPathParams() < params.getPathParams();
- }
-
- if (getProfileParams() != params.getProfileParams())
- {
- return getProfileParams() < params.getProfileParams();
- }
-
- if (mSculptID != params.mSculptID)
- {
- return mSculptID < params.mSculptID;
- }
-
- return mSculptType < params.mSculptType;
-
-
-}
-
-void LLVolumeParams::copyParams(const LLVolumeParams &params)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
- mProfileParams.copyParams(params.mProfileParams);
- mPathParams.copyParams(params.mPathParams);
- mSculptID = params.getSculptID();
- mSculptType = params.getSculptType();
-}
-
-// Less restricitve approx 0 for volumes
-const F32 APPROXIMATELY_ZERO = 0.001f;
-bool approx_zero( F32 f, F32 tolerance = APPROXIMATELY_ZERO)
-{
- return (f >= -tolerance) && (f <= tolerance);
-}
-
-// return true if in range (or nearly so)
-static bool limit_range(F32& v, F32 min, F32 max, F32 tolerance = APPROXIMATELY_ZERO)
-{
- F32 min_delta = v - min;
- if (min_delta < 0.f)
- {
- v = min;
- if (!approx_zero(min_delta, tolerance))
- return false;
- }
- F32 max_delta = max - v;
- if (max_delta < 0.f)
- {
- v = max;
- if (!approx_zero(max_delta, tolerance))
- return false;
- }
- return true;
-}
-
-bool LLVolumeParams::setBeginAndEndS(const F32 b, const F32 e)
-{
- bool valid = true;
-
- // First, clamp to valid ranges.
- F32 begin = b;
- valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA);
-
- F32 end = e;
- if (end >= .0149f && end < MIN_CUT_DELTA) end = MIN_CUT_DELTA; // eliminate warning for common rounding error
- valid &= limit_range(end, MIN_CUT_DELTA, 1.f);
-
- valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA, .01f);
-
- // Now set them.
- mProfileParams.setBegin(begin);
- mProfileParams.setEnd(end);
-
- return valid;
-}
-
-bool LLVolumeParams::setBeginAndEndT(const F32 b, const F32 e)
-{
- bool valid = true;
-
- // First, clamp to valid ranges.
- F32 begin = b;
- valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA);
-
- F32 end = e;
- valid &= limit_range(end, MIN_CUT_DELTA, 1.f);
-
- valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA, .01f);
-
- // Now set them.
- mPathParams.setBegin(begin);
- mPathParams.setEnd(end);
-
- return valid;
-}
-
-bool LLVolumeParams::setHollow(const F32 h)
-{
- // Validate the hollow based on path and profile.
- U8 profile = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
- U8 hole_type = mProfileParams.getCurveType() & LL_PCODE_HOLE_MASK;
-
- F32 max_hollow = HOLLOW_MAX;
-
- // Only square holes have trouble.
- if (LL_PCODE_HOLE_SQUARE == hole_type)
- {
- switch(profile)
- {
- case LL_PCODE_PROFILE_CIRCLE:
- case LL_PCODE_PROFILE_CIRCLE_HALF:
- case LL_PCODE_PROFILE_EQUALTRI:
- max_hollow = HOLLOW_MAX_SQUARE;
- }
- }
-
- F32 hollow = h;
- bool valid = limit_range(hollow, HOLLOW_MIN, max_hollow);
- mProfileParams.setHollow(hollow);
-
- return valid;
-}
-
-bool LLVolumeParams::setTwistBegin(const F32 b)
-{
- F32 twist_begin = b;
- bool valid = limit_range(twist_begin, TWIST_MIN, TWIST_MAX);
- mPathParams.setTwistBegin(twist_begin);
- return valid;
-}
-
-bool LLVolumeParams::setTwistEnd(const F32 e)
-{
- F32 twist_end = e;
- bool valid = limit_range(twist_end, TWIST_MIN, TWIST_MAX);
- mPathParams.setTwistEnd(twist_end);
- return valid;
-}
-
-bool LLVolumeParams::setRatio(const F32 x, const F32 y)
-{
- F32 min_x = RATIO_MIN;
- F32 max_x = RATIO_MAX;
- F32 min_y = RATIO_MIN;
- F32 max_y = RATIO_MAX;
- // If this is a circular path (and not a sphere) then 'ratio' is actually hole size.
- U8 path_type = mPathParams.getCurveType();
- U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
- if ( LL_PCODE_PATH_CIRCLE == path_type &&
- LL_PCODE_PROFILE_CIRCLE_HALF != profile_type)
- {
- // Holes are more restricted...
- min_x = HOLE_X_MIN;
- max_x = HOLE_X_MAX;
- min_y = HOLE_Y_MIN;
- max_y = HOLE_Y_MAX;
- }
-
- F32 ratio_x = x;
- bool valid = limit_range(ratio_x, min_x, max_x);
- F32 ratio_y = y;
- valid &= limit_range(ratio_y, min_y, max_y);
-
- mPathParams.setScale(ratio_x, ratio_y);
-
- return valid;
-}
-
-bool LLVolumeParams::setShear(const F32 x, const F32 y)
-{
- F32 shear_x = x;
- bool valid = limit_range(shear_x, SHEAR_MIN, SHEAR_MAX);
- F32 shear_y = y;
- valid &= limit_range(shear_y, SHEAR_MIN, SHEAR_MAX);
- mPathParams.setShear(shear_x, shear_y);
- return valid;
-}
-
-bool LLVolumeParams::setTaperX(const F32 v)
-{
- F32 taper = v;
- bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX);
- mPathParams.setTaperX(taper);
- return valid;
-}
-
-bool LLVolumeParams::setTaperY(const F32 v)
-{
- F32 taper = v;
- bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX);
- mPathParams.setTaperY(taper);
- return valid;
-}
-
-bool LLVolumeParams::setRevolutions(const F32 r)
-{
- F32 revolutions = r;
- bool valid = limit_range(revolutions, REV_MIN, REV_MAX);
- mPathParams.setRevolutions(revolutions);
- return valid;
-}
-
-bool LLVolumeParams::setRadiusOffset(const F32 offset)
-{
- bool valid = true;
-
- // If this is a sphere, just set it to 0 and get out.
- U8 path_type = mPathParams.getCurveType();
- U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
- if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type ||
- LL_PCODE_PATH_CIRCLE != path_type )
- {
- mPathParams.setRadiusOffset(0.f);
- return true;
- }
-
- // Limit radius offset, based on taper and hole size y.
- F32 radius_offset = offset;
- F32 taper_y = getTaperY();
- F32 radius_mag = fabs(radius_offset);
- F32 hole_y_mag = fabs(getRatioY());
- F32 taper_y_mag = fabs(taper_y);
- // Check to see if the taper effects us.
- if ( (radius_offset > 0.f && taper_y < 0.f) ||
- (radius_offset < 0.f && taper_y > 0.f) )
- {
- // The taper does not help increase the radius offset range.
- taper_y_mag = 0.f;
- }
- F32 max_radius_mag = 1.f - hole_y_mag * (1.f - taper_y_mag) / (1.f - hole_y_mag);
-
- // Enforce the maximum magnitude.
- F32 delta = max_radius_mag - radius_mag;
- if (delta < 0.f)
- {
- // Check radius offset sign.
- if (radius_offset < 0.f)
- {
- radius_offset = -max_radius_mag;
- }
- else
- {
- radius_offset = max_radius_mag;
- }
- valid = approx_zero(delta, .1f);
- }
-
- mPathParams.setRadiusOffset(radius_offset);
- return valid;
-}
-
-bool LLVolumeParams::setSkew(const F32 skew_value)
-{
- bool valid = true;
-
- // Check the skew value against the revolutions.
- F32 skew = llclamp(skew_value, SKEW_MIN, SKEW_MAX);
- F32 skew_mag = fabs(skew);
- F32 revolutions = getRevolutions();
- F32 scale_x = getRatioX();
- F32 min_skew_mag = 1.0f - 1.0f / (revolutions * scale_x + 1.0f);
- // Discontinuity; A revolution of 1 allows skews below 0.5.
- if ( fabs(revolutions - 1.0f) < 0.001)
- min_skew_mag = 0.0f;
-
- // Clip skew.
- F32 delta = skew_mag - min_skew_mag;
- if (delta < 0.f)
- {
- // Check skew sign.
- if (skew < 0.0f)
- {
- skew = -min_skew_mag;
- }
- else
- {
- skew = min_skew_mag;
- }
- valid = approx_zero(delta, .01f);
- }
-
- mPathParams.setSkew(skew);
- return valid;
-}
-
-bool LLVolumeParams::setSculptID(const LLUUID sculpt_id, U8 sculpt_type)
-{
- mSculptID = sculpt_id;
- mSculptType = sculpt_type;
- return true;
-}
-
-bool LLVolumeParams::setType(U8 profile, U8 path)
-{
- bool result = true;
- // First, check profile and path for validity.
- U8 profile_type = profile & LL_PCODE_PROFILE_MASK;
- U8 hole_type = (profile & LL_PCODE_HOLE_MASK) >> 4;
- U8 path_type = path >> 4;
-
- if (profile_type > LL_PCODE_PROFILE_MAX)
- {
- // Bad profile. Make it square.
- profile = LL_PCODE_PROFILE_SQUARE;
- result = false;
- llwarns << "LLVolumeParams::setType changing bad profile type (" << profile_type
- << ") to be LL_PCODE_PROFILE_SQUARE" << llendl;
- }
- else if (hole_type > LL_PCODE_HOLE_MAX)
- {
- // Bad hole. Make it the same.
- profile = profile_type;
- result = false;
- llwarns << "LLVolumeParams::setType changing bad hole type (" << hole_type
- << ") to be LL_PCODE_HOLE_SAME" << llendl;
- }
-
- if (path_type < LL_PCODE_PATH_MIN ||
- path_type > LL_PCODE_PATH_MAX)
- {
- // Bad path. Make it linear.
- result = false;
- llwarns << "LLVolumeParams::setType changing bad path (" << path
- << ") to be LL_PCODE_PATH_LINE" << llendl;
- path = LL_PCODE_PATH_LINE;
- }
-
- mProfileParams.setCurveType(profile);
- mPathParams.setCurveType(path);
- return result;
-}
-
-// static
-bool LLVolumeParams::validate(U8 prof_curve, F32 prof_begin, F32 prof_end, F32 hollow,
- U8 path_curve, F32 path_begin, F32 path_end,
- F32 scx, F32 scy, F32 shx, F32 shy,
- F32 twistend, F32 twistbegin, F32 radiusoffset,
- F32 tx, F32 ty, F32 revolutions, F32 skew)
-{
- LLVolumeParams test_params;
- if (!test_params.setType (prof_curve, path_curve))
- {
- return false;
- }
- if (!test_params.setBeginAndEndS (prof_begin, prof_end))
- {
- return false;
- }
- if (!test_params.setBeginAndEndT (path_begin, path_end))
- {
- return false;
- }
- if (!test_params.setHollow (hollow))
- {
- return false;
- }
- if (!test_params.setTwistBegin (twistbegin))
- {
- return false;
- }
- if (!test_params.setTwistEnd (twistend))
- {
- return false;
- }
- if (!test_params.setRatio (scx, scy))
- {
- return false;
- }
- if (!test_params.setShear (shx, shy))
- {
- return false;
- }
- if (!test_params.setTaper (tx, ty))
- {
- return false;
- }
- if (!test_params.setRevolutions (revolutions))
- {
- return false;
- }
- if (!test_params.setRadiusOffset (radiusoffset))
- {
- return false;
- }
- if (!test_params.setSkew (skew))
- {
- return false;
- }
- return true;
-}
-
-S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- S32 expected_num_triangle_indices = getNumTriangleIndices();
- if (expected_num_triangle_indices > MAX_VOLUME_TRIANGLE_INDICES)
- {
- // we don't allow LLVolumes with this many vertices
- llwarns << "Couldn't allocate triangle indices" << llendl;
- num_indices = 0;
- return NULL;
- }
-
- S32* index = new S32[expected_num_triangle_indices];
- S32 count = 0;
-
- // Let's do this totally diffently, as we don't care about faces...
- // Counter-clockwise triangles are forward facing...
-
- BOOL open = getProfile().isOpen();
- BOOL hollow = (mParams.getProfileParams().getHollow() > 0);
- BOOL path_open = getPath().isOpen();
- S32 size_s, size_s_out, size_t;
- S32 s, t, i;
- size_s = getProfile().getTotal();
- size_s_out = getProfile().getTotalOut();
- size_t = getPath().mPath.size();
-
- // NOTE -- if the construction of the triangles below ever changes
- // then getNumTriangleIndices() method may also have to be updated.
-
- if (open) /* Flawfinder: ignore */
- {
- if (hollow)
- {
- // Open hollow -- much like the closed solid, except we
- // we need to stitch up the gap between s=0 and s=size_s-1
-
- for (t = 0; t < size_t - 1; t++)
- {
- // The outer face, first cut, and inner face
- for (s = 0; s < size_s - 1; s++)
- {
- i = s + t*size_s;
- index[count++] = i; // x,y
- index[count++] = i + 1; // x+1,y
- index[count++] = i + size_s; // x,y+1
-
- index[count++] = i + size_s; // x,y+1
- index[count++] = i + 1; // x+1,y
- index[count++] = i + size_s + 1; // x+1,y+1
- }
-
- // The other cut face
- index[count++] = s + t*size_s; // x,y
- index[count++] = 0 + t*size_s; // x+1,y
- index[count++] = s + (t+1)*size_s; // x,y+1
-
- index[count++] = s + (t+1)*size_s; // x,y+1
- index[count++] = 0 + t*size_s; // x+1,y
- index[count++] = 0 + (t+1)*size_s; // x+1,y+1
- }
-
- // Do the top and bottom caps, if necessary
- if (path_open)
- {
- // Top cap
- S32 pt1 = 0;
- S32 pt2 = size_s-1;
- S32 i = (size_t - 1)*size_s;
-
- while (pt2 - pt1 > 1)
- {
- // Use the profile points instead of the mesh, since you want
- // the un-transformed profile distances.
- LLVector3 p1 = getProfile().mProfile[pt1];
- LLVector3 p2 = getProfile().mProfile[pt2];
- LLVector3 pa = getProfile().mProfile[pt1+1];
- LLVector3 pb = getProfile().mProfile[pt2-1];
-
- p1.mV[VZ] = 0.f;
- p2.mV[VZ] = 0.f;
- pa.mV[VZ] = 0.f;
- pb.mV[VZ] = 0.f;
-
- // Use area of triangle to determine backfacing
- F32 area_1a2, area_1ba, area_21b, area_2ab;
- area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
- (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
- (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
-
- area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
- (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
- (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
-
- area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
- (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
- (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
-
- area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
- (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
- (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
-
- BOOL use_tri1a2 = TRUE;
- BOOL tri_1a2 = TRUE;
- BOOL tri_21b = TRUE;
-
- if (area_1a2 < 0)
- {
- tri_1a2 = FALSE;
- }
- if (area_2ab < 0)
- {
- // Can't use, because it contains point b
- tri_1a2 = FALSE;
- }
- if (area_21b < 0)
- {
- tri_21b = FALSE;
- }
- if (area_1ba < 0)
- {
- // Can't use, because it contains point b
- tri_21b = FALSE;
- }
-
- if (!tri_1a2)
- {
- use_tri1a2 = FALSE;
- }
- else if (!tri_21b)
- {
- use_tri1a2 = TRUE;
- }
- else
- {
- LLVector3 d1 = p1 - pa;
- LLVector3 d2 = p2 - pb;
-
- if (d1.magVecSquared() < d2.magVecSquared())
- {
- use_tri1a2 = TRUE;
- }
- else
- {
- use_tri1a2 = FALSE;
- }
- }
-
- if (use_tri1a2)
- {
- index[count++] = pt1 + i;
- index[count++] = pt1 + 1 + i;
- index[count++] = pt2 + i;
- pt1++;
- }
- else
- {
- index[count++] = pt1 + i;
- index[count++] = pt2 - 1 + i;
- index[count++] = pt2 + i;
- pt2--;
- }
- }
-
- // Bottom cap
- pt1 = 0;
- pt2 = size_s-1;
- while (pt2 - pt1 > 1)
- {
- // Use the profile points instead of the mesh, since you want
- // the un-transformed profile distances.
- LLVector3 p1 = getProfile().mProfile[pt1];
- LLVector3 p2 = getProfile().mProfile[pt2];
- LLVector3 pa = getProfile().mProfile[pt1+1];
- LLVector3 pb = getProfile().mProfile[pt2-1];
-
- p1.mV[VZ] = 0.f;
- p2.mV[VZ] = 0.f;
- pa.mV[VZ] = 0.f;
- pb.mV[VZ] = 0.f;
-
- // Use area of triangle to determine backfacing
- F32 area_1a2, area_1ba, area_21b, area_2ab;
- area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
- (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
- (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
-
- area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
- (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
- (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
-
- area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
- (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
- (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
-
- area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
- (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
- (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
-
- BOOL use_tri1a2 = TRUE;
- BOOL tri_1a2 = TRUE;
- BOOL tri_21b = TRUE;
-
- if (area_1a2 < 0)
- {
- tri_1a2 = FALSE;
- }
- if (area_2ab < 0)
- {
- // Can't use, because it contains point b
- tri_1a2 = FALSE;
- }
- if (area_21b < 0)
- {
- tri_21b = FALSE;
- }
- if (area_1ba < 0)
- {
- // Can't use, because it contains point b
- tri_21b = FALSE;
- }
-
- if (!tri_1a2)
- {
- use_tri1a2 = FALSE;
- }
- else if (!tri_21b)
- {
- use_tri1a2 = TRUE;
- }
- else
- {
- LLVector3 d1 = p1 - pa;
- LLVector3 d2 = p2 - pb;
-
- if (d1.magVecSquared() < d2.magVecSquared())
- {
- use_tri1a2 = TRUE;
- }
- else
- {
- use_tri1a2 = FALSE;
- }
- }
-
- if (use_tri1a2)
- {
- index[count++] = pt1;
- index[count++] = pt2;
- index[count++] = pt1 + 1;
- pt1++;
- }
- else
- {
- index[count++] = pt1;
- index[count++] = pt2;
- index[count++] = pt2 - 1;
- pt2--;
- }
- }
- }
- }
- else
- {
- // Open solid
-
- for (t = 0; t < size_t - 1; t++)
- {
- // Outer face + 1 cut face
- for (s = 0; s < size_s - 1; s++)
- {
- i = s + t*size_s;
-
- index[count++] = i; // x,y
- index[count++] = i + 1; // x+1,y
- index[count++] = i + size_s; // x,y+1
-
- index[count++] = i + size_s; // x,y+1
- index[count++] = i + 1; // x+1,y
- index[count++] = i + size_s + 1; // x+1,y+1
- }
-
- // The other cut face
- index[count++] = (size_s - 1) + (t*size_s); // x,y
- index[count++] = 0 + t*size_s; // x+1,y
- index[count++] = (size_s - 1) + (t+1)*size_s; // x,y+1
-
- index[count++] = (size_s - 1) + (t+1)*size_s; // x,y+1
- index[count++] = 0 + (t*size_s); // x+1,y
- index[count++] = 0 + (t+1)*size_s; // x+1,y+1
- }
-
- // Do the top and bottom caps, if necessary
- if (path_open)
- {
- for (s = 0; s < size_s - 2; s++)
- {
- index[count++] = s+1;
- index[count++] = s;
- index[count++] = size_s - 1;
- }
-
- // We've got a top cap
- S32 offset = (size_t - 1)*size_s;
- for (s = 0; s < size_s - 2; s++)
- {
- // Inverted ordering from bottom cap.
- index[count++] = offset + size_s - 1;
- index[count++] = offset + s;
- index[count++] = offset + s + 1;
- }
- }
- }
- }
- else if (hollow)
- {
- // Closed hollow
- // Outer face
-
- for (t = 0; t < size_t - 1; t++)
- {
- for (s = 0; s < size_s_out - 1; s++)
- {
- i = s + t*size_s;
-
- index[count++] = i; // x,y
- index[count++] = i + 1; // x+1,y
- index[count++] = i + size_s; // x,y+1
-
- index[count++] = i + size_s; // x,y+1
- index[count++] = i + 1; // x+1,y
- index[count++] = i + 1 + size_s; // x+1,y+1
- }
- }
-
- // Inner face
- // Invert facing from outer face
- for (t = 0; t < size_t - 1; t++)
- {
- for (s = size_s_out; s < size_s - 1; s++)
- {
- i = s + t*size_s;
-
- index[count++] = i; // x,y
- index[count++] = i + 1; // x+1,y
- index[count++] = i + size_s; // x,y+1
-
- index[count++] = i + size_s; // x,y+1
- index[count++] = i + 1; // x+1,y
- index[count++] = i + 1 + size_s; // x+1,y+1
- }
- }
-
- // Do the top and bottom caps, if necessary
- if (path_open)
- {
- // Top cap
- S32 pt1 = 0;
- S32 pt2 = size_s-1;
- S32 i = (size_t - 1)*size_s;
-
- while (pt2 - pt1 > 1)
- {
- // Use the profile points instead of the mesh, since you want
- // the un-transformed profile distances.
- LLVector3 p1 = getProfile().mProfile[pt1];
- LLVector3 p2 = getProfile().mProfile[pt2];
- LLVector3 pa = getProfile().mProfile[pt1+1];
- LLVector3 pb = getProfile().mProfile[pt2-1];
-
- p1.mV[VZ] = 0.f;
- p2.mV[VZ] = 0.f;
- pa.mV[VZ] = 0.f;
- pb.mV[VZ] = 0.f;
-
- // Use area of triangle to determine backfacing
- F32 area_1a2, area_1ba, area_21b, area_2ab;
- area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
- (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
- (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
-
- area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
- (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
- (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
-
- area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
- (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
- (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
-
- area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
- (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
- (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
-
- BOOL use_tri1a2 = TRUE;
- BOOL tri_1a2 = TRUE;
- BOOL tri_21b = TRUE;
-
- if (area_1a2 < 0)
- {
- tri_1a2 = FALSE;
- }
- if (area_2ab < 0)
- {
- // Can't use, because it contains point b
- tri_1a2 = FALSE;
- }
- if (area_21b < 0)
- {
- tri_21b = FALSE;
- }
- if (area_1ba < 0)
- {
- // Can't use, because it contains point b
- tri_21b = FALSE;
- }
-
- if (!tri_1a2)
- {
- use_tri1a2 = FALSE;
- }
- else if (!tri_21b)
- {
- use_tri1a2 = TRUE;
- }
- else
- {
- LLVector3 d1 = p1 - pa;
- LLVector3 d2 = p2 - pb;
-
- if (d1.magVecSquared() < d2.magVecSquared())
- {
- use_tri1a2 = TRUE;
- }
- else
- {
- use_tri1a2 = FALSE;
- }
- }
-
- if (use_tri1a2)
- {
- index[count++] = pt1 + i;
- index[count++] = pt1 + 1 + i;
- index[count++] = pt2 + i;
- pt1++;
- }
- else
- {
- index[count++] = pt1 + i;
- index[count++] = pt2 - 1 + i;
- index[count++] = pt2 + i;
- pt2--;
- }
- }
-
- // Bottom cap
- pt1 = 0;
- pt2 = size_s-1;
- while (pt2 - pt1 > 1)
- {
- // Use the profile points instead of the mesh, since you want
- // the un-transformed profile distances.
- LLVector3 p1 = getProfile().mProfile[pt1];
- LLVector3 p2 = getProfile().mProfile[pt2];
- LLVector3 pa = getProfile().mProfile[pt1+1];
- LLVector3 pb = getProfile().mProfile[pt2-1];
-
- p1.mV[VZ] = 0.f;
- p2.mV[VZ] = 0.f;
- pa.mV[VZ] = 0.f;
- pb.mV[VZ] = 0.f;
-
- // Use area of triangle to determine backfacing
- F32 area_1a2, area_1ba, area_21b, area_2ab;
- area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
- (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
- (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
-
- area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
- (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
- (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
-
- area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
- (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
- (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
-
- area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
- (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
- (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
-
- BOOL use_tri1a2 = TRUE;
- BOOL tri_1a2 = TRUE;
- BOOL tri_21b = TRUE;
-
- if (area_1a2 < 0)
- {
- tri_1a2 = FALSE;
- }
- if (area_2ab < 0)
- {
- // Can't use, because it contains point b
- tri_1a2 = FALSE;
- }
- if (area_21b < 0)
- {
- tri_21b = FALSE;
- }
- if (area_1ba < 0)
- {
- // Can't use, because it contains point b
- tri_21b = FALSE;
- }
-
- if (!tri_1a2)
- {
- use_tri1a2 = FALSE;
- }
- else if (!tri_21b)
- {
- use_tri1a2 = TRUE;
- }
- else
- {
- LLVector3 d1 = p1 - pa;
- LLVector3 d2 = p2 - pb;
-
- if (d1.magVecSquared() < d2.magVecSquared())
- {
- use_tri1a2 = TRUE;
- }
- else
- {
- use_tri1a2 = FALSE;
- }
- }
-
- if (use_tri1a2)
- {
- index[count++] = pt1;
- index[count++] = pt2;
- index[count++] = pt1 + 1;
- pt1++;
- }
- else
- {
- index[count++] = pt1;
- index[count++] = pt2;
- index[count++] = pt2 - 1;
- pt2--;
- }
- }
- }
- }
- else
- {
- // Closed solid. Easy case.
- for (t = 0; t < size_t - 1; t++)
- {
- for (s = 0; s < size_s - 1; s++)
- {
- // Should wrap properly, but for now...
- i = s + t*size_s;
-
- index[count++] = i; // x,y
- index[count++] = i + 1; // x+1,y
- index[count++] = i + size_s; // x,y+1
-
- index[count++] = i + size_s; // x,y+1
- index[count++] = i + 1; // x+1,y
- index[count++] = i + size_s + 1; // x+1,y+1
- }
- }
-
- // Do the top and bottom caps, if necessary
- if (path_open)
- {
- // bottom cap
- for (s = 1; s < size_s - 2; s++)
- {
- index[count++] = s+1;
- index[count++] = s;
- index[count++] = 0;
- }
-
- // top cap
- S32 offset = (size_t - 1)*size_s;
- for (s = 1; s < size_s - 2; s++)
- {
- // Inverted ordering from bottom cap.
- index[count++] = offset;
- index[count++] = offset + s;
- index[count++] = offset + s + 1;
- }
- }
- }
-
-#ifdef LL_DEBUG
- // assert that we computed the correct number of indices
- if (count != expected_num_triangle_indices )
- {
- llerrs << "bad index count prediciton:"
- << " expected=" << expected_num_triangle_indices
- << " actual=" << count << llendl;
- }
-#endif
-
-#if 0
- // verify that each index does not point beyond the size of the mesh
- S32 num_vertices = mMesh.size();
- for (i = 0; i < count; i+=3)
- {
- llinfos << index[i] << ":" << index[i+1] << ":" << index[i+2] << llendl;
- llassert(index[i] < num_vertices);
- llassert(index[i+1] < num_vertices);
- llassert(index[i+2] < num_vertices);
- }
-#endif
-
- num_indices = count;
- return index;
-}
-
-S32 LLVolume::getNumTriangleIndices() const
-{
- BOOL profile_open = getProfile().isOpen();
- BOOL hollow = (mParams.getProfileParams().getHollow() > 0);
- BOOL path_open = getPath().isOpen();
-
- S32 size_s, size_s_out, size_t;
- size_s = getProfile().getTotal();
- size_s_out = getProfile().getTotalOut();
- size_t = getPath().mPath.size();
-
- S32 count = 0;
- if (profile_open) /* Flawfinder: ignore */
- {
- if (hollow)
- {
- // Open hollow -- much like the closed solid, except we
- // we need to stitch up the gap between s=0 and s=size_s-1
- count = (size_t - 1) * (((size_s -1) * 6) + 6);
- }
- else
- {
- count = (size_t - 1) * (((size_s -1) * 6) + 6);
- }
- }
- else if (hollow)
- {
- // Closed hollow
- // Outer face
- count = (size_t - 1) * (size_s_out - 1) * 6;
-
- // Inner face
- count += (size_t - 1) * ((size_s - 1) - size_s_out) * 6;
- }
- else
- {
- // Closed solid. Easy case.
- count = (size_t - 1) * (size_s - 1) * 6;
- }
-
- if (path_open)
- {
- S32 cap_triangle_count = size_s - 3;
- if ( profile_open
- || hollow )
- {
- cap_triangle_count = size_s - 2;
- }
- if ( cap_triangle_count > 0 )
- {
- // top and bottom caps
- count += cap_triangle_count * 2 * 3;
- }
- }
- return count;
-}
-
-
-S32 LLVolume::getNumTriangles() const
-{
- U32 triangle_count = 0;
-
- for (S32 i = 0; i < getNumVolumeFaces(); ++i)
- {
- triangle_count += getVolumeFace(i).mNumIndices/3;
- }
-
- return triangle_count;
-}
-
-
-//-----------------------------------------------------------------------------
-// generateSilhouetteVertices()
-//-----------------------------------------------------------------------------
-void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
- std::vector<LLVector3> &normals,
- const LLVector3& obj_cam_vec_in,
- const LLMatrix4& mat_in,
- const LLMatrix3& norm_mat_in,
- S32 face_mask)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- LLMatrix4a mat;
- mat.loadu(mat_in);
-
- LLMatrix4a norm_mat;
- norm_mat.loadu(norm_mat_in);
-
- LLVector4a obj_cam_vec;
- obj_cam_vec.load3(obj_cam_vec_in.mV);
-
- vertices.clear();
- normals.clear();
-
- if ((mParams.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
- {
- return;
- }
-
- S32 cur_index = 0;
- //for each face
- for (face_list_t::iterator iter = mVolumeFaces.begin();
- iter != mVolumeFaces.end(); ++iter)
- {
- LLVolumeFace& face = *iter;
-
- if (!(face_mask & (0x1 << cur_index++)) ||
- face.mNumIndices == 0 || face.mEdge.empty())
- {
- continue;
- }
-
- if (face.mTypeMask & (LLVolumeFace::CAP_MASK)) {
-
- }
- else {
-
- //==============================================
- //DEBUG draw edge map instead of silhouette edge
- //==============================================
-
-#if DEBUG_SILHOUETTE_EDGE_MAP
-
- //for each triangle
- U32 count = face.mNumIndices;
- for (U32 j = 0; j < count/3; j++) {
- //get vertices
- S32 v1 = face.mIndices[j*3+0];
- S32 v2 = face.mIndices[j*3+1];
- S32 v3 = face.mIndices[j*3+2];
-
- //get current face center
- LLVector3 cCenter = (face.mVertices[v1].getPosition() +
- face.mVertices[v2].getPosition() +
- face.mVertices[v3].getPosition()) / 3.0f;
-
- //for each edge
- for (S32 k = 0; k < 3; k++) {
- S32 nIndex = face.mEdge[j*3+k];
- if (nIndex <= -1) {
- continue;
- }
-
- if (nIndex >= (S32) count/3) {
- continue;
- }
- //get neighbor vertices
- v1 = face.mIndices[nIndex*3+0];
- v2 = face.mIndices[nIndex*3+1];
- v3 = face.mIndices[nIndex*3+2];
-
- //get neighbor face center
- LLVector3 nCenter = (face.mVertices[v1].getPosition() +
- face.mVertices[v2].getPosition() +
- face.mVertices[v3].getPosition()) / 3.0f;
-
- //draw line
- vertices.push_back(cCenter);
- vertices.push_back(nCenter);
- normals.push_back(LLVector3(1,1,1));
- normals.push_back(LLVector3(1,1,1));
- segments.push_back(vertices.size());
- }
- }
-
- continue;
-
- //==============================================
- //DEBUG
- //==============================================
-
- //==============================================
- //DEBUG draw normals instead of silhouette edge
- //==============================================
-#elif DEBUG_SILHOUETTE_NORMALS
-
- //for each vertex
- for (U32 j = 0; j < face.mNumVertices; j++) {
- vertices.push_back(face.mVertices[j].getPosition());
- vertices.push_back(face.mVertices[j].getPosition() + face.mVertices[j].getNormal()*0.1f);
- normals.push_back(LLVector3(0,0,1));
- normals.push_back(LLVector3(0,0,1));
- segments.push_back(vertices.size());
-#if DEBUG_SILHOUETTE_BINORMALS
- vertices.push_back(face.mVertices[j].getPosition());
- vertices.push_back(face.mVertices[j].getPosition() + face.mVertices[j].mBinormal*0.1f);
- normals.push_back(LLVector3(0,0,1));
- normals.push_back(LLVector3(0,0,1));
- segments.push_back(vertices.size());
-#endif
- }
-
- continue;
-#else
- //==============================================
- //DEBUG
- //==============================================
-
- static const U8 AWAY = 0x01,
- TOWARDS = 0x02;
-
- //for each triangle
- std::vector<U8> fFacing;
- vector_append(fFacing, face.mNumIndices/3);
-
- LLVector4a* v = (LLVector4a*) face.mPositions;
- LLVector4a* n = (LLVector4a*) face.mNormals;
-
- for (U32 j = 0; j < face.mNumIndices/3; j++)
- {
- //approximate normal
- S32 v1 = face.mIndices[j*3+0];
- S32 v2 = face.mIndices[j*3+1];
- S32 v3 = face.mIndices[j*3+2];
-
- LLVector4a c1,c2;
- c1.setSub(v[v1], v[v2]);
- c2.setSub(v[v2], v[v3]);
-
- LLVector4a norm;
-
- norm.setCross3(c1, c2);
-
- if (norm.dot3(norm) < 0.00000001f)
- {
- fFacing[j] = AWAY | TOWARDS;
- }
- else
- {
- //get view vector
- LLVector4a view;
- view.setSub(obj_cam_vec, v[v1]);
- bool away = view.dot3(norm) > 0.0f;
- if (away)
- {
- fFacing[j] = AWAY;
- }
- else
- {
- fFacing[j] = TOWARDS;
- }
- }
- }
-
- //for each triangle
- for (U32 j = 0; j < face.mNumIndices/3; j++)
- {
- if (fFacing[j] == (AWAY | TOWARDS))
- { //this is a degenerate triangle
- //take neighbor facing (degenerate faces get facing of one of their neighbors)
- // *FIX IF NEEDED: this does not deal with neighboring degenerate faces
- for (S32 k = 0; k < 3; k++)
- {
- S32 index = face.mEdge[j*3+k];
- if (index != -1)
- {
- fFacing[j] = fFacing[index];
- break;
- }
- }
- continue; //skip degenerate face
- }
-
- //for each edge
- for (S32 k = 0; k < 3; k++) {
- S32 index = face.mEdge[j*3+k];
- if (index != -1 && fFacing[index] == (AWAY | TOWARDS)) {
- //our neighbor is degenerate, make him face our direction
- fFacing[face.mEdge[j*3+k]] = fFacing[j];
- continue;
- }
-
- if (index == -1 || //edge has no neighbor, MUST be a silhouette edge
- (fFacing[index] & fFacing[j]) == 0) { //we found a silhouette edge
-
- S32 v1 = face.mIndices[j*3+k];
- S32 v2 = face.mIndices[j*3+((k+1)%3)];
-
- LLVector4a t;
- mat.affineTransform(v[v1], t);
- vertices.push_back(LLVector3(t[0], t[1], t[2]));
-
- norm_mat.rotate(n[v1], t);
-
- t.normalize3fast();
- normals.push_back(LLVector3(t[0], t[1], t[2]));
-
- mat.affineTransform(v[v2], t);
- vertices.push_back(LLVector3(t[0], t[1], t[2]));
-
- norm_mat.rotate(n[v2], t);
- t.normalize3fast();
- normals.push_back(LLVector3(t[0], t[1], t[2]));
- }
- }
- }
-#endif
- }
- }
-}
-
-S32 LLVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
- S32 face,
- LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
-{
- LLVector4a starta, enda;
- starta.load3(start.mV);
- enda.load3(end.mV);
-
- return lineSegmentIntersect(starta, enda, face, intersection, tex_coord, normal, bi_normal);
-
-}
-
-
-S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
- S32 face,
- LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
-{
- S32 hit_face = -1;
-
- S32 start_face;
- S32 end_face;
-
- if (face == -1) // ALL_SIDES
- {
- start_face = 0;
- end_face = getNumVolumeFaces() - 1;
- }
- else
- {
- start_face = face;
- end_face = face;
- }
-
- LLVector4a dir;
- dir.setSub(end, start);
-
- F32 closest_t = 2.f; // must be larger than 1
-
- end_face = llmin(end_face, getNumVolumeFaces()-1);
-
- for (S32 i = start_face; i <= end_face; i++)
- {
- LLVolumeFace &face = mVolumeFaces[i];
-
- LLVector4a box_center;
- box_center.setAdd(face.mExtents[0], face.mExtents[1]);
- box_center.mul(0.5f);
-
- LLVector4a box_size;
- box_size.setSub(face.mExtents[1], face.mExtents[0]);
-
- if (LLLineSegmentBoxIntersect(start, end, box_center, box_size))
- {
- if (bi_normal != NULL) // if the caller wants binormals, we may need to generate them
- {
- genBinormals(i);
- }
-
- if (!face.mOctree)
- {
- face.createOctree();
- }
-
- //LLVector4a* p = (LLVector4a*) face.mPositions;
-
- LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, bi_normal);
- intersect.traverse(face.mOctree);
- if (intersect.mHitFace)
- {
- hit_face = i;
- }
- }
- }
-
-
- return hit_face;
-}
-
-class LLVertexIndexPair
-{
-public:
- LLVertexIndexPair(const LLVector3 &vertex, const S32 index);
-
- LLVector3 mVertex;
- S32 mIndex;
-};
-
-LLVertexIndexPair::LLVertexIndexPair(const LLVector3 &vertex, const S32 index)
-{
- mVertex = vertex;
- mIndex = index;
-}
-
-const F32 VERTEX_SLOP = 0.00001f;
-const F32 VERTEX_SLOP_SQRD = VERTEX_SLOP * VERTEX_SLOP;
-
-struct lessVertex
-{
- bool operator()(const LLVertexIndexPair *a, const LLVertexIndexPair *b)
- {
- const F32 slop = VERTEX_SLOP;
-
- if (a->mVertex.mV[0] + slop < b->mVertex.mV[0])
- {
- return TRUE;
- }
- else if (a->mVertex.mV[0] - slop > b->mVertex.mV[0])
- {
- return FALSE;
- }
-
- if (a->mVertex.mV[1] + slop < b->mVertex.mV[1])
- {
- return TRUE;
- }
- else if (a->mVertex.mV[1] - slop > b->mVertex.mV[1])
- {
- return FALSE;
- }
-
- if (a->mVertex.mV[2] + slop < b->mVertex.mV[2])
- {
- return TRUE;
- }
- else if (a->mVertex.mV[2] - slop > b->mVertex.mV[2])
- {
- return FALSE;
- }
-
- return FALSE;
- }
-};
-
-struct lessTriangle
-{
- bool operator()(const S32 *a, const S32 *b)
- {
- if (*a < *b)
- {
- return TRUE;
- }
- else if (*a > *b)
- {
- return FALSE;
- }
-
- if (*(a+1) < *(b+1))
- {
- return TRUE;
- }
- else if (*(a+1) > *(b+1))
- {
- return FALSE;
- }
-
- if (*(a+2) < *(b+2))
- {
- return TRUE;
- }
- else if (*(a+2) > *(b+2))
- {
- return FALSE;
- }
-
- return FALSE;
- }
-};
-
-BOOL equalTriangle(const S32 *a, const S32 *b)
-{
- if ((*a == *b) && (*(a+1) == *(b+1)) && (*(a+2) == *(b+2)))
- {
- return TRUE;
- }
- return FALSE;
-}
-
-BOOL LLVolume::cleanupTriangleData( const S32 num_input_vertices,
- const std::vector<Point>& input_vertices,
- const S32 num_input_triangles,
- S32 *input_triangles,
- S32 &num_output_vertices,
- LLVector3 **output_vertices,
- S32 &num_output_triangles,
- S32 **output_triangles)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- /* Testing: avoid any cleanup
- static BOOL skip_cleanup = TRUE;
- if ( skip_cleanup )
- {
- num_output_vertices = num_input_vertices;
- num_output_triangles = num_input_triangles;
-
- *output_vertices = new LLVector3[num_input_vertices];
- for (S32 index = 0; index < num_input_vertices; index++)
- {
- (*output_vertices)[index] = input_vertices[index].mPos;
- }
-
- *output_triangles = new S32[num_input_triangles*3];
- memcpy(*output_triangles, input_triangles, 3*num_input_triangles*sizeof(S32)); // Flawfinder: ignore
- return TRUE;
- }
- */
-
- // Here's how we do this:
- // Create a structure which contains the original vertex index and the
- // LLVector3 data.
- // "Sort" the data by the vectors
- // Create an array the size of the old vertex list, with a mapping of
- // old indices to new indices.
- // Go through triangles, shift so the lowest index is first
- // Sort triangles by first index
- // Remove duplicate triangles
- // Allocate and pack new triangle data.
-
- //LLTimer cleanupTimer;
- //llinfos << "In vertices: " << num_input_vertices << llendl;
- //llinfos << "In triangles: " << num_input_triangles << llendl;
-
- S32 i;
- typedef std::multiset<LLVertexIndexPair*, lessVertex> vertex_set_t;
- vertex_set_t vertex_list;
-
- LLVertexIndexPair *pairp = NULL;
- for (i = 0; i < num_input_vertices; i++)
- {
- LLVertexIndexPair *new_pairp = new LLVertexIndexPair(input_vertices[i].mPos, i);
- vertex_list.insert(new_pairp);
- }
-
- // Generate the vertex mapping and the list of vertices without
- // duplicates. This will crash if there are no vertices.
- llassert(num_input_vertices > 0); // check for no vertices!
- S32 *vertex_mapping = new S32[num_input_vertices];
- LLVector3 *new_vertices = new LLVector3[num_input_vertices];
- LLVertexIndexPair *prev_pairp = NULL;
-
- S32 new_num_vertices;
-
- new_num_vertices = 0;
- for (vertex_set_t::iterator iter = vertex_list.begin(),
- end = vertex_list.end();
- iter != end; iter++)
- {
- pairp = *iter;
- if (!prev_pairp || ((pairp->mVertex - prev_pairp->mVertex).magVecSquared() >= VERTEX_SLOP_SQRD))
- {
- new_vertices[new_num_vertices] = pairp->mVertex;
- //llinfos << "Added vertex " << new_num_vertices << " : " << pairp->mVertex << llendl;
- new_num_vertices++;
- // Update the previous
- prev_pairp = pairp;
- }
- else
- {
- //llinfos << "Removed duplicate vertex " << pairp->mVertex << ", distance magVecSquared() is " << (pairp->mVertex - prev_pairp->mVertex).magVecSquared() << llendl;
- }
- vertex_mapping[pairp->mIndex] = new_num_vertices - 1;
- }
-
- // Iterate through triangles and remove degenerates, re-ordering vertices
- // along the way.
- S32 *new_triangles = new S32[num_input_triangles * 3];
- S32 new_num_triangles = 0;
-
- for (i = 0; i < num_input_triangles; i++)
- {
- S32 v1 = i*3;
- S32 v2 = v1 + 1;
- S32 v3 = v1 + 2;
-
- //llinfos << "Checking triangle " << input_triangles[v1] << ":" << input_triangles[v2] << ":" << input_triangles[v3] << llendl;
- input_triangles[v1] = vertex_mapping[input_triangles[v1]];
- input_triangles[v2] = vertex_mapping[input_triangles[v2]];
- input_triangles[v3] = vertex_mapping[input_triangles[v3]];
-
- if ((input_triangles[v1] == input_triangles[v2])
- || (input_triangles[v1] == input_triangles[v3])
- || (input_triangles[v2] == input_triangles[v3]))
- {
- //llinfos << "Removing degenerate triangle " << input_triangles[v1] << ":" << input_triangles[v2] << ":" << input_triangles[v3] << llendl;
- // Degenerate triangle, skip
- continue;
- }
-
- if (input_triangles[v1] < input_triangles[v2])
- {
- if (input_triangles[v1] < input_triangles[v3])
- {
- // (0 < 1) && (0 < 2)
- new_triangles[new_num_triangles*3] = input_triangles[v1];
- new_triangles[new_num_triangles*3+1] = input_triangles[v2];
- new_triangles[new_num_triangles*3+2] = input_triangles[v3];
- }
- else
- {
- // (0 < 1) && (2 < 0)
- new_triangles[new_num_triangles*3] = input_triangles[v3];
- new_triangles[new_num_triangles*3+1] = input_triangles[v1];
- new_triangles[new_num_triangles*3+2] = input_triangles[v2];
- }
- }
- else if (input_triangles[v2] < input_triangles[v3])
- {
- // (1 < 0) && (1 < 2)
- new_triangles[new_num_triangles*3] = input_triangles[v2];
- new_triangles[new_num_triangles*3+1] = input_triangles[v3];
- new_triangles[new_num_triangles*3+2] = input_triangles[v1];
- }
- else
- {
- // (1 < 0) && (2 < 1)
- new_triangles[new_num_triangles*3] = input_triangles[v3];
- new_triangles[new_num_triangles*3+1] = input_triangles[v1];
- new_triangles[new_num_triangles*3+2] = input_triangles[v2];
- }
- new_num_triangles++;
- }
-
- if (new_num_triangles == 0)
- {
- llwarns << "Created volume object with 0 faces." << llendl;
- delete[] new_triangles;
- delete[] vertex_mapping;
- delete[] new_vertices;
- return FALSE;
- }
-
- typedef std::set<S32*, lessTriangle> triangle_set_t;
- triangle_set_t triangle_list;
-
- for (i = 0; i < new_num_triangles; i++)
- {
- triangle_list.insert(&new_triangles[i*3]);
- }
-
- // Sort through the triangle list, and delete duplicates
-
- S32 *prevp = NULL;
- S32 *curp = NULL;
-
- S32 *sorted_tris = new S32[new_num_triangles*3];
- S32 cur_tri = 0;
- for (triangle_set_t::iterator iter = triangle_list.begin(),
- end = triangle_list.end();
- iter != end; iter++)
- {
- curp = *iter;
- if (!prevp || !equalTriangle(prevp, curp))
- {
- //llinfos << "Added triangle " << *curp << ":" << *(curp+1) << ":" << *(curp+2) << llendl;
- sorted_tris[cur_tri*3] = *curp;
- sorted_tris[cur_tri*3+1] = *(curp+1);
- sorted_tris[cur_tri*3+2] = *(curp+2);
- cur_tri++;
- prevp = curp;
- }
- else
- {
- //llinfos << "Skipped triangle " << *curp << ":" << *(curp+1) << ":" << *(curp+2) << llendl;
- }
- }
-
- *output_vertices = new LLVector3[new_num_vertices];
- num_output_vertices = new_num_vertices;
- for (i = 0; i < new_num_vertices; i++)
- {
- (*output_vertices)[i] = new_vertices[i];
- }
-
- *output_triangles = new S32[cur_tri*3];
- num_output_triangles = cur_tri;
- memcpy(*output_triangles, sorted_tris, 3*cur_tri*sizeof(S32)); /* Flawfinder: ignore */
-
- /*
- llinfos << "Out vertices: " << num_output_vertices << llendl;
- llinfos << "Out triangles: " << num_output_triangles << llendl;
- for (i = 0; i < num_output_vertices; i++)
- {
- llinfos << i << ":" << (*output_vertices)[i] << llendl;
- }
- for (i = 0; i < num_output_triangles; i++)
- {
- llinfos << i << ":" << (*output_triangles)[i*3] << ":" << (*output_triangles)[i*3+1] << ":" << (*output_triangles)[i*3+2] << llendl;
- }
- */
-
- //llinfos << "Out vertices: " << num_output_vertices << llendl;
- //llinfos << "Out triangles: " << num_output_triangles << llendl;
- delete[] vertex_mapping;
- vertex_mapping = NULL;
- delete[] new_vertices;
- new_vertices = NULL;
- delete[] new_triangles;
- new_triangles = NULL;
- delete[] sorted_tris;
- sorted_tris = NULL;
- triangle_list.clear();
- std::for_each(vertex_list.begin(), vertex_list.end(), DeletePointer());
- vertex_list.clear();
-
- return TRUE;
-}
-
-
-BOOL LLVolumeParams::importFile(LLFILE *fp)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- //llinfos << "importing volume" << llendl;
- const S32 BUFSIZE = 16384;
- char buffer[BUFSIZE]; /* Flawfinder: ignore */
- // *NOTE: changing the size or type of this buffer will require
- // changing the sscanf below.
- char keyword[256]; /* Flawfinder: ignore */
- keyword[0] = 0;
-
- while (!feof(fp))
- {
- if (fgets(buffer, BUFSIZE, fp) == NULL)
- {
- buffer[0] = '\0';
- }
-
- sscanf(buffer, " %255s", keyword); /* Flawfinder: ignore */
- if (!strcmp("{", keyword))
- {
- continue;
- }
- if (!strcmp("}",keyword))
- {
- break;
- }
- else if (!strcmp("profile", keyword))
- {
- mProfileParams.importFile(fp);
- }
- else if (!strcmp("path",keyword))
- {
- mPathParams.importFile(fp);
- }
- else
- {
- llwarns << "unknown keyword " << keyword << " in volume import" << llendl;
- }
- }
-
- return TRUE;
-}
-
-BOOL LLVolumeParams::exportFile(LLFILE *fp) const
-{
- fprintf(fp,"\tshape 0\n");
- fprintf(fp,"\t{\n");
- mPathParams.exportFile(fp);
- mProfileParams.exportFile(fp);
- fprintf(fp, "\t}\n");
- return TRUE;
-}
-
-
-BOOL LLVolumeParams::importLegacyStream(std::istream& input_stream)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- //llinfos << "importing volume" << llendl;
- const S32 BUFSIZE = 16384;
- // *NOTE: changing the size or type of this buffer will require
- // changing the sscanf below.
- char buffer[BUFSIZE]; /* Flawfinder: ignore */
- char keyword[256]; /* Flawfinder: ignore */
- keyword[0] = 0;
-
- while (input_stream.good())
- {
- input_stream.getline(buffer, BUFSIZE);
- sscanf(buffer, " %255s", keyword);
- if (!strcmp("{", keyword))
- {
- continue;
- }
- if (!strcmp("}",keyword))
- {
- break;
- }
- else if (!strcmp("profile", keyword))
- {
- mProfileParams.importLegacyStream(input_stream);
- }
- else if (!strcmp("path",keyword))
- {
- mPathParams.importLegacyStream(input_stream);
- }
- else
- {
- llwarns << "unknown keyword " << keyword << " in volume import" << llendl;
- }
- }
-
- return TRUE;
-}
-
-BOOL LLVolumeParams::exportLegacyStream(std::ostream& output_stream) const
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- output_stream <<"\tshape 0\n";
- output_stream <<"\t{\n";
- mPathParams.exportLegacyStream(output_stream);
- mProfileParams.exportLegacyStream(output_stream);
- output_stream << "\t}\n";
- return TRUE;
-}
-
-LLSD LLVolumeParams::sculptAsLLSD() const
-{
- LLSD sd = LLSD();
- sd["id"] = getSculptID();
- sd["type"] = getSculptType();
-
- return sd;
-}
-
-bool LLVolumeParams::sculptFromLLSD(LLSD& sd)
-{
- setSculptID(sd["id"].asUUID(), (U8)sd["type"].asInteger());
- return true;
-}
-
-LLSD LLVolumeParams::asLLSD() const
-{
- LLSD sd = LLSD();
- sd["path"] = mPathParams;
- sd["profile"] = mProfileParams;
- sd["sculpt"] = sculptAsLLSD();
-
- return sd;
-}
-
-bool LLVolumeParams::fromLLSD(LLSD& sd)
-{
- mPathParams.fromLLSD(sd["path"]);
- mProfileParams.fromLLSD(sd["profile"]);
- sculptFromLLSD(sd["sculpt"]);
-
- return true;
-}
-
-void LLVolumeParams::reduceS(F32 begin, F32 end)
-{
- begin = llclampf(begin);
- end = llclampf(end);
- if (begin > end)
- {
- F32 temp = begin;
- begin = end;
- end = temp;
- }
- F32 a = mProfileParams.getBegin();
- F32 b = mProfileParams.getEnd();
- mProfileParams.setBegin(a + begin * (b - a));
- mProfileParams.setEnd(a + end * (b - a));
-}
-
-void LLVolumeParams::reduceT(F32 begin, F32 end)
-{
- begin = llclampf(begin);
- end = llclampf(end);
- if (begin > end)
- {
- F32 temp = begin;
- begin = end;
- end = temp;
- }
- F32 a = mPathParams.getBegin();
- F32 b = mPathParams.getEnd();
- mPathParams.setBegin(a + begin * (b - a));
- mPathParams.setEnd(a + end * (b - a));
-}
-
-const F32 MIN_CONCAVE_PROFILE_WEDGE = 0.125f; // 1/8 unity
-const F32 MIN_CONCAVE_PATH_WEDGE = 0.111111f; // 1/9 unity
-
-// returns TRUE if the shape can be approximated with a convex shape
-// for collison purposes
-BOOL LLVolumeParams::isConvex() const
-{
- if (!getSculptID().isNull())
- {
- // can't determine, be safe and say no:
- return FALSE;
- }
-
- F32 path_length = mPathParams.getEnd() - mPathParams.getBegin();
- F32 hollow = mProfileParams.getHollow();
-
- U8 path_type = mPathParams.getCurveType();
- if ( path_length > MIN_CONCAVE_PATH_WEDGE
- && ( mPathParams.getTwist() != mPathParams.getTwistBegin()
- || (hollow > 0.f
- && LL_PCODE_PATH_LINE != path_type) ) )
- {
- // twist along a "not too short" path is concave
- return FALSE;
- }
-
- F32 profile_length = mProfileParams.getEnd() - mProfileParams.getBegin();
- BOOL same_hole = hollow == 0.f
- || (mProfileParams.getCurveType() & LL_PCODE_HOLE_MASK) == LL_PCODE_HOLE_SAME;
-
- F32 min_profile_wedge = MIN_CONCAVE_PROFILE_WEDGE;
- U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
- if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type )
- {
- // it is a sphere and spheres get twice the minimum profile wedge
- min_profile_wedge = 2.f * MIN_CONCAVE_PROFILE_WEDGE;
- }
-
- BOOL convex_profile = ( ( profile_length == 1.f
- || profile_length <= 0.5f )
- && hollow == 0.f ) // trivially convex
- || ( profile_length <= min_profile_wedge
- && same_hole ); // effectvely convex (even when hollow)
-
- if (!convex_profile)
- {
- // profile is concave
- return FALSE;
- }
-
- if ( LL_PCODE_PATH_LINE == path_type )
- {
- // straight paths with convex profile
- return TRUE;
- }
-
- BOOL concave_path = (path_length < 1.0f) && (path_length > 0.5f);
- if (concave_path)
- {
- return FALSE;
- }
-
- // we're left with spheres, toroids and tubes
- if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type )
- {
- // at this stage all spheres must be convex
- return TRUE;
- }
-
- // it's a toroid or tube
- if ( path_length <= MIN_CONCAVE_PATH_WEDGE )
- {
- // effectively convex
- return TRUE;
- }
-
- return FALSE;
-}
-
-// debug
-void LLVolumeParams::setCube()
-{
- mProfileParams.setCurveType(LL_PCODE_PROFILE_SQUARE);
- mProfileParams.setBegin(0.f);
- mProfileParams.setEnd(1.f);
- mProfileParams.setHollow(0.f);
-
- mPathParams.setBegin(0.f);
- mPathParams.setEnd(1.f);
- mPathParams.setScale(1.f, 1.f);
- mPathParams.setShear(0.f, 0.f);
- mPathParams.setCurveType(LL_PCODE_PATH_LINE);
- mPathParams.setTwistBegin(0.f);
- mPathParams.setTwistEnd(0.f);
- mPathParams.setRadiusOffset(0.f);
- mPathParams.setTaper(0.f, 0.f);
- mPathParams.setRevolutions(0.f);
- mPathParams.setSkew(0.f);
-}
-
-LLFaceID LLVolume::generateFaceMask()
-{
- LLFaceID new_mask = 0x0000;
-
- switch(mParams.getProfileParams().getCurveType() & LL_PCODE_PROFILE_MASK)
- {
- case LL_PCODE_PROFILE_CIRCLE:
- case LL_PCODE_PROFILE_CIRCLE_HALF:
- new_mask |= LL_FACE_OUTER_SIDE_0;
- break;
- case LL_PCODE_PROFILE_SQUARE:
- {
- for(S32 side = (S32)(mParams.getProfileParams().getBegin() * 4.f); side < llceil(mParams.getProfileParams().getEnd() * 4.f); side++)
- {
- new_mask |= LL_FACE_OUTER_SIDE_0 << side;
- }
- }
- break;
- case LL_PCODE_PROFILE_ISOTRI:
- case LL_PCODE_PROFILE_EQUALTRI:
- case LL_PCODE_PROFILE_RIGHTTRI:
- {
- for(S32 side = (S32)(mParams.getProfileParams().getBegin() * 3.f); side < llceil(mParams.getProfileParams().getEnd() * 3.f); side++)
- {
- new_mask |= LL_FACE_OUTER_SIDE_0 << side;
- }
- }
- break;
- default:
- llerrs << "Unknown profile!" << llendl;
- break;
- }
-
- // handle hollow objects
- if (mParams.getProfileParams().getHollow() > 0)
- {
- new_mask |= LL_FACE_INNER_SIDE;
- }
-
- // handle open profile curves
- if (mProfilep->isOpen())
- {
- new_mask |= LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END;
- }
-
- // handle open path curves
- if (mPathp->isOpen())
- {
- new_mask |= LL_FACE_PATH_BEGIN | LL_FACE_PATH_END;
- }
-
- return new_mask;
-}
-
-BOOL LLVolume::isFaceMaskValid(LLFaceID face_mask)
-{
- LLFaceID test_mask = 0;
- for(S32 i = 0; i < getNumFaces(); i++)
- {
- test_mask |= mProfilep->mFaces[i].mFaceID;
- }
-
- return test_mask == face_mask;
-}
-
-BOOL LLVolume::isConvex() const
-{
- // mParams.isConvex() may return FALSE even though the final
- // geometry is actually convex due to LOD approximations.
- // TODO -- provide LLPath and LLProfile with isConvex() methods
- // that correctly determine convexity. -- Leviathan
- return mParams.isConvex();
-}
-
-
-std::ostream& operator<<(std::ostream &s, const LLProfileParams &profile_params)
-{
- s << "{type=" << (U32) profile_params.mCurveType;
- s << ", begin=" << profile_params.mBegin;
- s << ", end=" << profile_params.mEnd;
- s << ", hollow=" << profile_params.mHollow;
- s << "}";
- return s;
-}
-
-
-std::ostream& operator<<(std::ostream &s, const LLPathParams &path_params)
-{
- s << "{type=" << (U32) path_params.mCurveType;
- s << ", begin=" << path_params.mBegin;
- s << ", end=" << path_params.mEnd;
- s << ", twist=" << path_params.mTwistEnd;
- s << ", scale=" << path_params.mScale;
- s << ", shear=" << path_params.mShear;
- s << ", twist_begin=" << path_params.mTwistBegin;
- s << ", radius_offset=" << path_params.mRadiusOffset;
- s << ", taper=" << path_params.mTaper;
- s << ", revolutions=" << path_params.mRevolutions;
- s << ", skew=" << path_params.mSkew;
- s << "}";
- return s;
-}
-
-
-std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params)
-{
- s << "{profileparams = " << volume_params.mProfileParams;
- s << ", pathparams = " << volume_params.mPathParams;
- s << "}";
- return s;
-}
-
-
-std::ostream& operator<<(std::ostream &s, const LLProfile &profile)
-{
- s << " {open=" << (U32) profile.mOpen;
- s << ", dirty=" << profile.mDirty;
- s << ", totalout=" << profile.mTotalOut;
- s << ", total=" << profile.mTotal;
- s << "}";
- return s;
-}
-
-
-std::ostream& operator<<(std::ostream &s, const LLPath &path)
-{
- s << "{open=" << (U32) path.mOpen;
- s << ", dirty=" << path.mDirty;
- s << ", step=" << path.mStep;
- s << ", total=" << path.mTotal;
- s << "}";
- return s;
-}
-
-std::ostream& operator<<(std::ostream &s, const LLVolume &volume)
-{
- s << "{params = " << volume.getParams();
- s << ", path = " << *volume.mPathp;
- s << ", profile = " << *volume.mProfilep;
- s << "}";
- return s;
-}
-
-
-std::ostream& operator<<(std::ostream &s, const LLVolume *volumep)
-{
- s << "{params = " << volumep->getParams();
- s << ", path = " << *(volumep->mPathp);
- s << ", profile = " << *(volumep->mProfilep);
- s << "}";
- return s;
-}
-
-LLVolumeFace::LLVolumeFace() :
- mID(0),
- mTypeMask(0),
- mBeginS(0),
- mBeginT(0),
- mNumS(0),
- mNumT(0),
- mNumVertices(0),
- mNumIndices(0),
- mPositions(NULL),
- mNormals(NULL),
- mBinormals(NULL),
- mTexCoords(NULL),
- mIndices(NULL),
- mWeights(NULL),
- mOctree(NULL)
-{
- mExtents = (LLVector4a*) malloc(sizeof(LLVector4a)*3);
- mCenter = mExtents+2;
-}
-
-LLVolumeFace::LLVolumeFace(const LLVolumeFace& src)
-: mID(0),
- mTypeMask(0),
- mBeginS(0),
- mBeginT(0),
- mNumS(0),
- mNumT(0),
- mNumVertices(0),
- mNumIndices(0),
- mPositions(NULL),
- mNormals(NULL),
- mBinormals(NULL),
- mTexCoords(NULL),
- mIndices(NULL),
- mWeights(NULL),
- mOctree(NULL)
-{
- mExtents = (LLVector4a*) malloc(sizeof(LLVector4a)*3);
- mCenter = mExtents+2;
- *this = src;
-}
-
-LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src)
-{
- if (&src == this)
- { //self assignment, do nothing
- return *this;
- }
-
- mID = src.mID;
- mTypeMask = src.mTypeMask;
- mBeginS = src.mBeginS;
- mBeginT = src.mBeginT;
- mNumS = src.mNumS;
- mNumT = src.mNumT;
-
- mExtents[0] = src.mExtents[0];
- mExtents[1] = src.mExtents[1];
- *mCenter = *src.mCenter;
-
- mNumVertices = 0;
- mNumIndices = 0;
-
- freeData();
-
- LLVector4a::memcpyNonAliased16((F32*) mExtents, (F32*) src.mExtents, 3*sizeof(LLVector4a));
-
- resizeVertices(src.mNumVertices);
- resizeIndices(src.mNumIndices);
-
- if (mNumVertices)
- {
- S32 vert_size = mNumVertices*sizeof(LLVector4a);
- S32 tc_size = (mNumVertices*sizeof(LLVector2)+0xF) & ~0xF;
-
- LLVector4a::memcpyNonAliased16((F32*) mPositions, (F32*) src.mPositions, vert_size);
- LLVector4a::memcpyNonAliased16((F32*) mNormals, (F32*) src.mNormals, vert_size);
- LLVector4a::memcpyNonAliased16((F32*) mTexCoords, (F32*) src.mTexCoords, tc_size);
-
-
- if (src.mBinormals)
- {
- allocateBinormals(src.mNumVertices);
- LLVector4a::memcpyNonAliased16((F32*) mBinormals, (F32*) src.mBinormals, vert_size);
- }
- else
- {
- free(mBinormals);
- mBinormals = NULL;
- }
-
- if (src.mWeights)
- {
- allocateWeights(src.mNumVertices);
- LLVector4a::memcpyNonAliased16((F32*) mWeights, (F32*) src.mWeights, vert_size);
- }
- else
- {
- free(mWeights);
- mWeights = NULL;
- }
- }
-
- if (mNumIndices)
- {
- S32 idx_size = (mNumIndices*sizeof(U16)+0xF) & ~0xF;
-
- LLVector4a::memcpyNonAliased16((F32*) mIndices, (F32*) src.mIndices, idx_size);
- }
-
- //delete
- return *this;
-}
-
-LLVolumeFace::~LLVolumeFace()
-{
- free(mExtents);
- mExtents = NULL;
-
- freeData();
-}
-
-void LLVolumeFace::freeData()
-{
- free(mPositions);
- mPositions = NULL;
- free( mNormals);
- mNormals = NULL;
- free(mTexCoords);
- mTexCoords = NULL;
- free(mIndices);
- mIndices = NULL;
- free(mBinormals);
- mBinormals = NULL;
- free(mWeights);
- mWeights = NULL;
-
- delete mOctree;
- mOctree = NULL;
-}
-
-BOOL LLVolumeFace::create(LLVolume* volume, BOOL partial_build)
-{
- //tree for this face is no longer valid
- delete mOctree;
- mOctree = NULL;
-
- BOOL ret = FALSE ;
- if (mTypeMask & CAP_MASK)
- {
- ret = createCap(volume, partial_build);
- }
- else if ((mTypeMask & END_MASK) || (mTypeMask & SIDE_MASK))
- {
- ret = createSide(volume, partial_build);
- }
- else
- {
- llerrs << "Unknown/uninitialized face type!" << llendl;
- }
-
- //update the range of the texture coordinates
- if(ret)
- {
- mTexCoordExtents[0].setVec(1.f, 1.f) ;
- mTexCoordExtents[1].setVec(0.f, 0.f) ;
-
- for(U32 i = 0 ; i < mNumVertices ; i++)
- {
- if(mTexCoordExtents[0].mV[0] > mTexCoords[i].mV[0])
- {
- mTexCoordExtents[0].mV[0] = mTexCoords[i].mV[0] ;
- }
- if(mTexCoordExtents[1].mV[0] < mTexCoords[i].mV[0])
- {
- mTexCoordExtents[1].mV[0] = mTexCoords[i].mV[0] ;
- }
-
- if(mTexCoordExtents[0].mV[1] > mTexCoords[i].mV[1])
- {
- mTexCoordExtents[0].mV[1] = mTexCoords[i].mV[1] ;
- }
- if(mTexCoordExtents[1].mV[1] < mTexCoords[i].mV[1])
- {
- mTexCoordExtents[1].mV[1] = mTexCoords[i].mV[1] ;
- }
- }
- mTexCoordExtents[0].mV[0] = llmax(0.f, mTexCoordExtents[0].mV[0]) ;
- mTexCoordExtents[0].mV[1] = llmax(0.f, mTexCoordExtents[0].mV[1]) ;
- mTexCoordExtents[1].mV[0] = llmin(1.f, mTexCoordExtents[1].mV[0]) ;
- mTexCoordExtents[1].mV[1] = llmin(1.f, mTexCoordExtents[1].mV[1]) ;
- }
-
- return ret ;
-}
-
-void LLVolumeFace::getVertexData(U16 index, LLVolumeFace::VertexData& cv)
-{
- cv.setPosition(mPositions[index]);
- cv.setNormal(mNormals[index]);
- cv.mTexCoord = mTexCoords[index];
-}
-
-bool LLVolumeFace::VertexMapData::operator==(const LLVolumeFace::VertexData& rhs) const
-{
- return getPosition().equals3(rhs.getPosition()) &&
- mTexCoord == rhs.mTexCoord &&
- getNormal().equals3(rhs.getNormal());
-}
-
-bool LLVolumeFace::VertexMapData::ComparePosition::operator()(const LLVector3& a, const LLVector3& b) const
-{
- if (a.mV[0] != b.mV[0])
- {
- return a.mV[0] < b.mV[0];
- }
-
- if (a.mV[1] != b.mV[1])
- {
- return a.mV[1] < b.mV[1];
- }
-
- return a.mV[2] < b.mV[2];
-}
-
-void LLVolumeFace::optimize(F32 angle_cutoff)
-{
- LLVolumeFace new_face;
-
- //map of points to vector of vertices at that point
- VertexMapData::PointMap point_map;
-
- //remove redundant vertices
- for (U32 i = 0; i < mNumIndices; ++i)
- {
- U16 index = mIndices[i];
-
- LLVolumeFace::VertexData cv;
- getVertexData(index, cv);
-
- BOOL found = FALSE;
- VertexMapData::PointMap::iterator point_iter = point_map.find(LLVector3(cv.getPosition().getF32ptr()));
- if (point_iter != point_map.end())
- { //duplicate point might exist
- for (U32 j = 0; j < point_iter->second.size(); ++j)
- {
- LLVolumeFace::VertexData& tv = (point_iter->second)[j];
- if (tv.compareNormal(cv, angle_cutoff))
- {
- found = TRUE;
- new_face.pushIndex((point_iter->second)[j].mIndex);
- break;
- }
- }
- }
-
- if (!found)
- {
- new_face.pushVertex(cv);
- U16 index = (U16) new_face.mNumVertices-1;
- new_face.pushIndex(index);
-
- VertexMapData d;
- d.setPosition(cv.getPosition());
- d.mTexCoord = cv.mTexCoord;
- d.setNormal(cv.getNormal());
- d.mIndex = index;
- if (point_iter != point_map.end())
- {
- point_iter->second.push_back(d);
- }
- else
- {
- point_map[LLVector3(d.getPosition().getF32ptr())].push_back(d);
- }
- }
- }
-
- swapData(new_face);
-}
-
-class LLVCacheTriangleData;
-
-class LLVCacheVertexData
-{
-public:
- S32 mIdx;
- S32 mCacheTag;
- F32 mScore;
- U32 mActiveTriangles;
- std::vector<LLVCacheTriangleData*> mTriangles;
-
- LLVCacheVertexData()
- {
- mCacheTag = -1;
- mScore = 0.f;
- mActiveTriangles = 0;
- mIdx = -1;
- }
-};
-
-class LLVCacheTriangleData
-{
-public:
- bool mActive;
- F32 mScore;
- LLVCacheVertexData* mVertex[3];
-
- LLVCacheTriangleData()
- {
- mActive = true;
- mScore = 0.f;
- mVertex[0] = mVertex[1] = mVertex[2] = NULL;
- }
-
- void complete()
- {
- mActive = false;
- for (S32 i = 0; i < 3; ++i)
- {
- if (mVertex[i])
- {
- llassert_always(mVertex[i]->mActiveTriangles > 0);
- mVertex[i]->mActiveTriangles--;
- }
- }
- }
-
- bool operator<(const LLVCacheTriangleData& rhs) const
- { //highest score first
- return rhs.mScore < mScore;
- }
-};
-
-const F32 FindVertexScore_CacheDecayPower = 1.5f;
-const F32 FindVertexScore_LastTriScore = 0.75f;
-const F32 FindVertexScore_ValenceBoostScale = 2.0f;
-const F32 FindVertexScore_ValenceBoostPower = 0.5f;
-const U32 MaxSizeVertexCache = 32;
-
-F32 find_vertex_score(LLVCacheVertexData& data)
-{
- if (data.mActiveTriangles == 0)
- { //no triangle references this vertex
- return -1.f;
- }
-
- F32 score = 0.f;
-
- S32 cache_idx = data.mCacheTag;
-
- if (cache_idx < 0)
- {
- //not in cache
- }
- else
- {
- if (cache_idx < 3)
- { //vertex was in the last triangle
- score = FindVertexScore_LastTriScore;
- }
- else
- { //more points for being higher in the cache
- F32 scaler = 1.f/(MaxSizeVertexCache-3);
- score = 1.f-((cache_idx-3)*scaler);
- score = powf(score, FindVertexScore_CacheDecayPower);
- }
- }
-
- //bonus points for having low valence
- F32 valence_boost = powf(data.mActiveTriangles, -FindVertexScore_ValenceBoostPower);
- score += FindVertexScore_ValenceBoostScale * valence_boost;
-
- return score;
-}
-
-class LLVCacheFIFO
-{
-public:
- LLVCacheVertexData* mCache[MaxSizeVertexCache];
- U32 mMisses;
-
- LLVCacheFIFO()
- {
- mMisses = 0;
- for (U32 i = 0; i < MaxSizeVertexCache; ++i)
- {
- mCache[i] = NULL;
- }
- }
-
- void addVertex(LLVCacheVertexData* data)
- {
- if (data->mCacheTag == -1)
- {
- mMisses++;
-
- S32 end = MaxSizeVertexCache-1;
-
- if (mCache[end])
- {
- mCache[end]->mCacheTag = -1;
- }
-
- for (S32 i = end; i > 0; --i)
- {
- mCache[i] = mCache[i-1];
- if (mCache[i])
- {
- mCache[i]->mCacheTag = i;
- }
- }
-
- mCache[0] = data;
- data->mCacheTag = 0;
- }
- }
-};
-
-class LLVCacheLRU
-{
-public:
- LLVCacheVertexData* mCache[MaxSizeVertexCache+3];
-
- LLVCacheTriangleData* mBestTriangle;
-
- U32 mMisses;
-
- LLVCacheLRU()
- {
- for (U32 i = 0; i < MaxSizeVertexCache+3; ++i)
- {
- mCache[i] = NULL;
- }
-
- mBestTriangle = NULL;
- mMisses = 0;
- }
-
- void addVertex(LLVCacheVertexData* data)
- {
- S32 end = MaxSizeVertexCache+2;
- if (data->mCacheTag != -1)
- { //just moving a vertex to the front of the cache
- end = data->mCacheTag;
- }
- else
- {
- mMisses++;
- if (mCache[end])
- { //adding a new vertex, vertex at end of cache falls off
- mCache[end]->mCacheTag = -1;
- }
- }
-
- for (S32 i = end; i > 0; --i)
- { //adjust cache pointers and tags
- mCache[i] = mCache[i-1];
-
- if (mCache[i])
- {
- mCache[i]->mCacheTag = i;
- }
- }
-
- mCache[0] = data;
- mCache[0]->mCacheTag = 0;
- }
-
- void addTriangle(LLVCacheTriangleData* data)
- {
- addVertex(data->mVertex[0]);
- addVertex(data->mVertex[1]);
- addVertex(data->mVertex[2]);
- }
-
- void updateScores()
- {
- for (U32 i = MaxSizeVertexCache; i < MaxSizeVertexCache+3; ++i)
- { //trailing 3 vertices aren't actually in the cache for scoring purposes
- if (mCache[i])
- {
- mCache[i]->mCacheTag = -1;
- }
- }
-
- for (U32 i = 0; i < MaxSizeVertexCache; ++i)
- { //update scores of vertices in cache
- if (mCache[i])
- {
- mCache[i]->mScore = find_vertex_score(*(mCache[i]));
- llassert_always(mCache[i]->mCacheTag == i);
- }
- }
-
- mBestTriangle = NULL;
- //update triangle scores
- for (U32 i = 0; i < MaxSizeVertexCache+3; ++i)
- {
- if (mCache[i])
- {
- for (U32 j = 0; j < mCache[i]->mTriangles.size(); ++j)
- {
- LLVCacheTriangleData* tri = mCache[i]->mTriangles[j];
- if (tri->mActive)
- {
- tri->mScore = tri->mVertex[0]->mScore;
- tri->mScore += tri->mVertex[1]->mScore;
- tri->mScore += tri->mVertex[2]->mScore;
-
- if (!mBestTriangle || mBestTriangle->mScore < tri->mScore)
- {
- mBestTriangle = tri;
- }
- }
- }
- }
- }
-
- //knock trailing 3 vertices off the cache
- for (U32 i = MaxSizeVertexCache; i < MaxSizeVertexCache+3; ++i)
- {
- if (mCache[i])
- {
- llassert_always(mCache[i]->mCacheTag == -1);
- mCache[i] = NULL;
- }
- }
- }
-};
-
-
-void LLVolumeFace::cacheOptimize()
-{ //optimize for vertex cache according to Forsyth method:
- // http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
-
- LLVCacheLRU cache;
-
- //mapping of vertices to triangles and indices
- std::vector<LLVCacheVertexData> vertex_data;
-
- //mapping of triangles do vertices
- std::vector<LLVCacheTriangleData> triangle_data;
-
- triangle_data.resize(mNumIndices/3);
- vertex_data.resize(mNumVertices);
-
- for (U32 i = 0; i < mNumIndices; i++)
- { //populate vertex data and triangle data arrays
- U16 idx = mIndices[i];
- U32 tri_idx = i/3;
-
- vertex_data[idx].mTriangles.push_back(&(triangle_data[tri_idx]));
- vertex_data[idx].mIdx = idx;
- triangle_data[tri_idx].mVertex[i%3] = &(vertex_data[idx]);
- }
-
- /*F32 pre_acmr = 1.f;
- //measure cache misses from before rebuild
- {
- LLVCacheFIFO test_cache;
- for (U32 i = 0; i < mNumIndices; ++i)
- {
- test_cache.addVertex(&vertex_data[mIndices[i]]);
- }
-
- for (U32 i = 0; i < mNumVertices; i++)
- {
- vertex_data[i].mCacheTag = -1;
- }
-
- pre_acmr = (F32) test_cache.mMisses/(mNumIndices/3);
- }*/
-
- for (U32 i = 0; i < mNumVertices; i++)
- { //initialize score values (no cache -- might try a fifo cache here)
- vertex_data[i].mScore = find_vertex_score(vertex_data[i]);
- vertex_data[i].mActiveTriangles = vertex_data[i].mTriangles.size();
-
- for (U32 j = 0; j < vertex_data[i].mTriangles.size(); ++j)
- {
- vertex_data[i].mTriangles[j]->mScore += vertex_data[i].mScore;
- }
- }
-
- //sort triangle data by score
- std::sort(triangle_data.begin(), triangle_data.end());
-
- std::vector<U16> new_indices;
-
- LLVCacheTriangleData* tri;
-
- //prime pump by adding first triangle to cache;
- tri = &(triangle_data[0]);
- cache.addTriangle(tri);
- new_indices.push_back(tri->mVertex[0]->mIdx);
- new_indices.push_back(tri->mVertex[1]->mIdx);
- new_indices.push_back(tri->mVertex[2]->mIdx);
- tri->complete();
-
- U32 breaks = 0;
- for (U32 i = 1; i < mNumIndices/3; ++i)
- {
- cache.updateScores();
- tri = cache.mBestTriangle;
- if (!tri)
- {
- breaks++;
- for (U32 j = 0; j < triangle_data.size(); ++j)
- {
- if (triangle_data[j].mActive)
- {
- tri = &(triangle_data[j]);
- break;
- }
- }
- }
-
- cache.addTriangle(tri);
- new_indices.push_back(tri->mVertex[0]->mIdx);
- new_indices.push_back(tri->mVertex[1]->mIdx);
- new_indices.push_back(tri->mVertex[2]->mIdx);
- tri->complete();
- }
-
- for (U32 i = 0; i < mNumIndices; ++i)
- {
- mIndices[i] = new_indices[i];
- }
-
- /*F32 post_acmr = 1.f;
- //measure cache misses from after rebuild
- {
- LLVCacheFIFO test_cache;
- for (U32 i = 0; i < mNumVertices; i++)
- {
- vertex_data[i].mCacheTag = -1;
- }
-
- for (U32 i = 0; i < mNumIndices; ++i)
- {
- test_cache.addVertex(&vertex_data[mIndices[i]]);
- }
-
- post_acmr = (F32) test_cache.mMisses/(mNumIndices/3);
- }*/
-
- //optimize for pre-TnL cache
-
- //allocate space for new buffer
- S32 num_verts = mNumVertices;
- LLVector4a* pos = (LLVector4a*) malloc(sizeof(LLVector4a)*num_verts);
- LLVector4a* norm = (LLVector4a*) malloc(sizeof(LLVector4a)*num_verts);
- S32 size = ((num_verts*sizeof(LLVector2)) + 0xF) & ~0xF;
- LLVector2* tc = (LLVector2*) malloc(size);
-
- LLVector4a* wght = NULL;
- if (mWeights)
- {
- wght = (LLVector4a*) malloc(sizeof(LLVector4a)*num_verts);
- }
-
- LLVector4a* binorm = NULL;
- if (mBinormals)
- {
- binorm = (LLVector4a*) malloc(sizeof(LLVector4a)*num_verts);
- }
-
- //allocate mapping of old indices to new indices
- std::vector<S32> new_idx;
- new_idx.resize(mNumVertices, -1);
-
- S32 cur_idx = 0;
- for (U32 i = 0; i < mNumIndices; ++i)
- {
- U16 idx = mIndices[i];
- if (new_idx[idx] == -1)
- { //this vertex hasn't been added yet
- new_idx[idx] = cur_idx;
-
- //copy vertex data
- pos[cur_idx] = mPositions[idx];
- norm[cur_idx] = mNormals[idx];
- tc[cur_idx] = mTexCoords[idx];
- if (mWeights)
- {
- wght[cur_idx] = mWeights[idx];
- }
- if (mBinormals)
- {
- binorm[cur_idx] = mBinormals[idx];
- }
-
- cur_idx++;
- }
- }
-
- for (U32 i = 0; i < mNumIndices; ++i)
- {
- mIndices[i] = new_idx[mIndices[i]];
- }
-
- free(mPositions);
- free(mNormals);
- free(mTexCoords);
- free(mWeights);
- free(mBinormals);
-
- mPositions = pos;
- mNormals = norm;
- mTexCoords = tc;
- mWeights = wght;
- mBinormals = binorm;
-
- //std::string result = llformat("ACMR pre/post: %.3f/%.3f -- %d triangles %d breaks", pre_acmr, post_acmr, mNumIndices/3, breaks);
- //llinfos << result << llendl;
-
-}
-
-void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVector4a& size)
-{
- if (mOctree)
- {
- return;
- }
-
- mOctree = new LLOctreeRoot<LLVolumeTriangle>(center, size, NULL);
- new LLVolumeOctreeListener(mOctree);
-
- for (U32 i = 0; i < mNumIndices; i+= 3)
- { //for each triangle
- LLPointer<LLVolumeTriangle> tri = new LLVolumeTriangle();
-
- const LLVector4a& v0 = mPositions[mIndices[i]];
- const LLVector4a& v1 = mPositions[mIndices[i+1]];
- const LLVector4a& v2 = mPositions[mIndices[i+2]];
-
- //store pointers to vertex data
- tri->mV[0] = &v0;
- tri->mV[1] = &v1;
- tri->mV[2] = &v2;
-
- //store indices
- tri->mIndex[0] = mIndices[i];
- tri->mIndex[1] = mIndices[i+1];
- tri->mIndex[2] = mIndices[i+2];
-
- //get minimum point
- LLVector4a min = v0;
- min.setMin(min, v1);
- min.setMin(min, v2);
-
- //get maximum point
- LLVector4a max = v0;
- max.setMax(max, v1);
- max.setMax(max, v2);
-
- //compute center
- LLVector4a center;
- center.setAdd(min, max);
- center.mul(0.5f);
-
- tri->mPositionGroup = center;
-
- //compute "radius"
- LLVector4a size;
- size.setSub(max,min);
-
- tri->mRadius = size.getLength3().getF32() * scaler;
-
- //insert
- mOctree->insert(tri);
- }
-
- //remove unneeded octree layers
- while (!mOctree->balance()) { }
-
- //calculate AABB for each node
- LLVolumeOctreeRebound rebound(this);
- rebound.traverse(mOctree);
-
- if (gDebugGL)
- {
- LLVolumeOctreeValidate validate;
- validate.traverse(mOctree);
- }
-}
-
-
-void LLVolumeFace::swapData(LLVolumeFace& rhs)
-{
- llswap(rhs.mPositions, mPositions);
- llswap(rhs.mNormals, mNormals);
- llswap(rhs.mBinormals, mBinormals);
- llswap(rhs.mTexCoords, mTexCoords);
- llswap(rhs.mIndices,mIndices);
- llswap(rhs.mNumVertices, mNumVertices);
- llswap(rhs.mNumIndices, mNumIndices);
-}
-
-void LerpPlanarVertex(LLVolumeFace::VertexData& v0,
- LLVolumeFace::VertexData& v1,
- LLVolumeFace::VertexData& v2,
- LLVolumeFace::VertexData& vout,
- F32 coef01,
- F32 coef02)
-{
-
- LLVector4a lhs;
- lhs.setSub(v1.getPosition(), v0.getPosition());
- lhs.mul(coef01);
- LLVector4a rhs;
- rhs.setSub(v2.getPosition(), v0.getPosition());
- rhs.mul(coef02);
-
- rhs.add(lhs);
- rhs.add(v0.getPosition());
-
- vout.setPosition(rhs);
-
- vout.mTexCoord = v0.mTexCoord + ((v1.mTexCoord-v0.mTexCoord)*coef01)+((v2.mTexCoord-v0.mTexCoord)*coef02);
- vout.setNormal(v0.getNormal());
-}
-
-BOOL LLVolumeFace::createUnCutCubeCap(LLVolume* volume, BOOL partial_build)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- const std::vector<LLVolume::Point>& mesh = volume->getMesh();
- const std::vector<LLVector3>& profile = volume->getProfile().mProfile;
- S32 max_s = volume->getProfile().getTotal();
- S32 max_t = volume->getPath().mPath.size();
-
- // S32 i;
- S32 num_vertices = 0, num_indices = 0;
- S32 grid_size = (profile.size()-1)/4;
- S32 quad_count = (grid_size * grid_size);
-
- num_vertices = (grid_size+1)*(grid_size+1);
- num_indices = quad_count * 4;
-
- LLVector4a& min = mExtents[0];
- LLVector4a& max = mExtents[1];
-
- S32 offset = 0;
- if (mTypeMask & TOP_MASK)
- {
- offset = (max_t-1) * max_s;
- }
- else
- {
- offset = mBeginS;
- }
-
- {
- VertexData corners[4];
- VertexData baseVert;
- for(S32 t = 0; t < 4; t++)
- {
- corners[t].getPosition().load3( mesh[offset + (grid_size*t)].mPos.mV);
- corners[t].mTexCoord.mV[0] = profile[grid_size*t].mV[0]+0.5f;
- corners[t].mTexCoord.mV[1] = 0.5f - profile[grid_size*t].mV[1];
- }
-
- {
- LLVector4a lhs;
- lhs.setSub(corners[1].getPosition(), corners[0].getPosition());
- LLVector4a rhs;
- rhs.setSub(corners[2].getPosition(), corners[1].getPosition());
- baseVert.getNormal().setCross3(lhs, rhs);
- baseVert.getNormal().normalize3fast();
- }
-
- if(!(mTypeMask & TOP_MASK))
- {
- baseVert.getNormal().mul(-1.0f);
- }
- else
- {
- //Swap the UVs on the U(X) axis for top face
- LLVector2 swap;
- swap = corners[0].mTexCoord;
- corners[0].mTexCoord=corners[3].mTexCoord;
- corners[3].mTexCoord=swap;
- swap = corners[1].mTexCoord;
- corners[1].mTexCoord=corners[2].mTexCoord;
- corners[2].mTexCoord=swap;
- }
-
- LLVector4a binormal;
-
- calc_binormal_from_triangle( binormal,
- corners[0].getPosition(), corners[0].mTexCoord,
- corners[1].getPosition(), corners[1].mTexCoord,
- corners[2].getPosition(), corners[2].mTexCoord);
-
- binormal.normalize3fast();
-
- S32 size = (grid_size+1)*(grid_size+1);
- resizeVertices(size);
- allocateBinormals(size);
-
- LLVector4a* pos = (LLVector4a*) mPositions;
- LLVector4a* norm = (LLVector4a*) mNormals;
- LLVector4a* binorm = (LLVector4a*) mBinormals;
- LLVector2* tc = (LLVector2*) mTexCoords;
-
- for(int gx = 0;gx<grid_size+1;gx++)
- {
- for(int gy = 0;gy<grid_size+1;gy++)
- {
- VertexData newVert;
- LerpPlanarVertex(
- corners[0],
- corners[1],
- corners[3],
- newVert,
- (F32)gx/(F32)grid_size,
- (F32)gy/(F32)grid_size);
-
- *pos++ = newVert.getPosition();
- *norm++ = baseVert.getNormal();
- *tc++ = newVert.mTexCoord;
- *binorm++ = binormal;
-
- if (gx == 0 && gy == 0)
- {
- min = newVert.getPosition();
- max = min;
- }
- else
- {
- min.setMin(min, newVert.getPosition());
- max.setMax(max, newVert.getPosition());
- }
- }
- }
-
- mCenter->setAdd(min, max);
- mCenter->mul(0.5f);
- }
-
- if (!partial_build)
- {
- resizeIndices(grid_size*grid_size*6);
-
- U16* out = mIndices;
-
- S32 idxs[] = {0,1,(grid_size+1)+1,(grid_size+1)+1,(grid_size+1),0};
- for(S32 gx = 0;gx<grid_size;gx++)
- {
-
- for(S32 gy = 0;gy<grid_size;gy++)
- {
- if (mTypeMask & TOP_MASK)
- {
- for(S32 i=5;i>=0;i--)
- {
- *out++ = ((gy*(grid_size+1))+gx+idxs[i]);
- }
- }
- else
- {
- for(S32 i=0;i<6;i++)
- {
- *out++ = ((gy*(grid_size+1))+gx+idxs[i]);
- }
- }
- }
- }
- }
-
- return TRUE;
-}
-
-
-BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- if (!(mTypeMask & HOLLOW_MASK) &&
- !(mTypeMask & OPEN_MASK) &&
- ((volume->getParams().getPathParams().getBegin()==0.0f)&&
- (volume->getParams().getPathParams().getEnd()==1.0f))&&
- (volume->getParams().getProfileParams().getCurveType()==LL_PCODE_PROFILE_SQUARE &&
- volume->getParams().getPathParams().getCurveType()==LL_PCODE_PATH_LINE)
- ){
- return createUnCutCubeCap(volume, partial_build);
- }
-
- S32 num_vertices = 0, num_indices = 0;
-
- const std::vector<LLVolume::Point>& mesh = volume->getMesh();
- const std::vector<LLVector3>& profile = volume->getProfile().mProfile;
-
- // All types of caps have the same number of vertices and indices
- num_vertices = profile.size();
- num_indices = (profile.size() - 2)*3;
-
- if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK))
- {
- resizeVertices(num_vertices+1);
- allocateBinormals(num_vertices+1);
-
- if (!partial_build)
- {
- resizeIndices(num_indices+3);
- }
- }
- else
- {
- resizeVertices(num_vertices);
- allocateBinormals(num_vertices);
-
- if (!partial_build)
- {
- resizeIndices(num_indices);
- }
- }
-
- S32 max_s = volume->getProfile().getTotal();
- S32 max_t = volume->getPath().mPath.size();
-
- mCenter->clear();
-
- S32 offset = 0;
- if (mTypeMask & TOP_MASK)
- {
- offset = (max_t-1) * max_s;
- }
- else
- {
- offset = mBeginS;
- }
-
- // Figure out the normal, assume all caps are flat faces.
- // Cross product to get normals.
-
- LLVector2 cuv;
- LLVector2 min_uv, max_uv;
-
- LLVector4a& min = mExtents[0];
- LLVector4a& max = mExtents[1];
-
- LLVector2* tc = (LLVector2*) mTexCoords;
- LLVector4a* pos = (LLVector4a*) mPositions;
- LLVector4a* norm = (LLVector4a*) mNormals;
- LLVector4a* binorm = (LLVector4a*) mBinormals;
-
- // Copy the vertices into the array
- for (S32 i = 0; i < num_vertices; i++)
- {
- if (mTypeMask & TOP_MASK)
- {
- tc[i].mV[0] = profile[i].mV[0]+0.5f;
- tc[i].mV[1] = profile[i].mV[1]+0.5f;
- }
- else
- {
- // Mirror for underside.
- tc[i].mV[0] = profile[i].mV[0]+0.5f;
- tc[i].mV[1] = 0.5f - profile[i].mV[1];
- }
-
- pos[i].load3(mesh[i + offset].mPos.mV);
-
- if (i == 0)
- {
- max = pos[i];
- min = max;
- min_uv = max_uv = tc[i];
- }
- else
- {
- update_min_max(min,max,pos[i]);
- update_min_max(min_uv, max_uv, tc[i]);
- }
- }
-
- mCenter->setAdd(min, max);
- mCenter->mul(0.5f);
-
- cuv = (min_uv + max_uv)*0.5f;
-
- LLVector4a binormal;
- calc_binormal_from_triangle(binormal,
- *mCenter, cuv,
- pos[0], tc[0],
- pos[1], tc[1]);
- binormal.normalize3fast();
-
- LLVector4a normal;
- LLVector4a d0, d1;
-
-
- d0.setSub(*mCenter, pos[0]);
- d1.setSub(*mCenter, pos[1]);
-
- if (mTypeMask & TOP_MASK)
- {
- normal.setCross3(d0, d1);
- }
- else
- {
- normal.setCross3(d1, d0);
- }
-
- normal.normalize3fast();
-
- VertexData vd;
- vd.setPosition(*mCenter);
- vd.mTexCoord = cuv;
-
- if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK))
- {
- pos[num_vertices] = *mCenter;
- tc[num_vertices] = cuv;
- num_vertices++;
- }
-
- for (S32 i = 0; i < num_vertices; i++)
- {
- binorm[i].load4a(binormal.getF32ptr());
- norm[i].load4a(normal.getF32ptr());
- }
-
- if (partial_build)
- {
- return TRUE;
- }
-
- if (mTypeMask & HOLLOW_MASK)
- {
- if (mTypeMask & TOP_MASK)
- {
- // HOLLOW TOP
- // Does it matter if it's open or closed? - djs
-
- S32 pt1 = 0, pt2 = num_vertices - 1;
- S32 i = 0;
- while (pt2 - pt1 > 1)
- {
- // Use the profile points instead of the mesh, since you want
- // the un-transformed profile distances.
- LLVector3 p1 = profile[pt1];
- LLVector3 p2 = profile[pt2];
- LLVector3 pa = profile[pt1+1];
- LLVector3 pb = profile[pt2-1];
-
- p1.mV[VZ] = 0.f;
- p2.mV[VZ] = 0.f;
- pa.mV[VZ] = 0.f;
- pb.mV[VZ] = 0.f;
-
- // Use area of triangle to determine backfacing
- F32 area_1a2, area_1ba, area_21b, area_2ab;
- area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
- (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
- (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
-
- area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
- (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
- (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
-
- area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
- (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
- (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
-
- area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
- (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
- (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
-
- BOOL use_tri1a2 = TRUE;
- BOOL tri_1a2 = TRUE;
- BOOL tri_21b = TRUE;
-
- if (area_1a2 < 0)
- {
- tri_1a2 = FALSE;
- }
- if (area_2ab < 0)
- {
- // Can't use, because it contains point b
- tri_1a2 = FALSE;
- }
- if (area_21b < 0)
- {
- tri_21b = FALSE;
- }
- if (area_1ba < 0)
- {
- // Can't use, because it contains point b
- tri_21b = FALSE;
- }
-
- if (!tri_1a2)
- {
- use_tri1a2 = FALSE;
- }
- else if (!tri_21b)
- {
- use_tri1a2 = TRUE;
- }
- else
- {
- LLVector3 d1 = p1 - pa;
- LLVector3 d2 = p2 - pb;
-
- if (d1.magVecSquared() < d2.magVecSquared())
- {
- use_tri1a2 = TRUE;
- }
- else
- {
- use_tri1a2 = FALSE;
- }
- }
-
- if (use_tri1a2)
- {
- mIndices[i++] = pt1;
- mIndices[i++] = pt1 + 1;
- mIndices[i++] = pt2;
- pt1++;
- }
- else
- {
- mIndices[i++] = pt1;
- mIndices[i++] = pt2 - 1;
- mIndices[i++] = pt2;
- pt2--;
- }
- }
- }
- else
- {
- // HOLLOW BOTTOM
- // Does it matter if it's open or closed? - djs
-
- llassert(mTypeMask & BOTTOM_MASK);
- S32 pt1 = 0, pt2 = num_vertices - 1;
-
- S32 i = 0;
- while (pt2 - pt1 > 1)
- {
- // Use the profile points instead of the mesh, since you want
- // the un-transformed profile distances.
- LLVector3 p1 = profile[pt1];
- LLVector3 p2 = profile[pt2];
- LLVector3 pa = profile[pt1+1];
- LLVector3 pb = profile[pt2-1];
-
- p1.mV[VZ] = 0.f;
- p2.mV[VZ] = 0.f;
- pa.mV[VZ] = 0.f;
- pb.mV[VZ] = 0.f;
-
- // Use area of triangle to determine backfacing
- F32 area_1a2, area_1ba, area_21b, area_2ab;
- area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
- (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
- (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
-
- area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
- (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
- (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
-
- area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
- (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
- (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
-
- area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
- (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
- (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
-
- BOOL use_tri1a2 = TRUE;
- BOOL tri_1a2 = TRUE;
- BOOL tri_21b = TRUE;
-
- if (area_1a2 < 0)
- {
- tri_1a2 = FALSE;
- }
- if (area_2ab < 0)
- {
- // Can't use, because it contains point b
- tri_1a2 = FALSE;
- }
- if (area_21b < 0)
- {
- tri_21b = FALSE;
- }
- if (area_1ba < 0)
- {
- // Can't use, because it contains point b
- tri_21b = FALSE;
- }
-
- if (!tri_1a2)
- {
- use_tri1a2 = FALSE;
- }
- else if (!tri_21b)
- {
- use_tri1a2 = TRUE;
- }
- else
- {
- LLVector3 d1 = p1 - pa;
- LLVector3 d2 = p2 - pb;
-
- if (d1.magVecSquared() < d2.magVecSquared())
- {
- use_tri1a2 = TRUE;
- }
- else
- {
- use_tri1a2 = FALSE;
- }
- }
-
- // Flipped backfacing from top
- if (use_tri1a2)
- {
- mIndices[i++] = pt1;
- mIndices[i++] = pt2;
- mIndices[i++] = pt1 + 1;
- pt1++;
- }
- else
- {
- mIndices[i++] = pt1;
- mIndices[i++] = pt2;
- mIndices[i++] = pt2 - 1;
- pt2--;
- }
- }
- }
- }
- else
- {
- // Not hollow, generate the triangle fan.
- U16 v1 = 2;
- U16 v2 = 1;
-
- if (mTypeMask & TOP_MASK)
- {
- v1 = 1;
- v2 = 2;
- }
-
- for (S32 i = 0; i < (num_vertices - 2); i++)
- {
- mIndices[3*i] = num_vertices - 1;
- mIndices[3*i+v1] = i;
- mIndices[3*i+v2] = i + 1;
- }
-
-
- }
-
- return TRUE;
-}
-
-void LLVolumeFace::createBinormals()
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- if (!mBinormals)
- {
- allocateBinormals(mNumVertices);
-
- //generate binormals
- LLVector4a* pos = mPositions;
- LLVector2* tc = (LLVector2*) mTexCoords;
- LLVector4a* binorm = (LLVector4a*) mBinormals;
-
- LLVector4a* end = mBinormals+mNumVertices;
- while (binorm < end)
- {
- (*binorm++).clear();
- }
-
- binorm = mBinormals;
-
- for (U32 i = 0; i < mNumIndices/3; i++)
- { //for each triangle
- const U16& i0 = mIndices[i*3+0];
- const U16& i1 = mIndices[i*3+1];
- const U16& i2 = mIndices[i*3+2];
-
- //calculate binormal
- LLVector4a binormal;
- calc_binormal_from_triangle(binormal,
- pos[i0], tc[i0],
- pos[i1], tc[i1],
- pos[i2], tc[i2]);
-
-
- //add triangle normal to vertices
- binorm[i0].add(binormal);
- binorm[i1].add(binormal);
- binorm[i2].add(binormal);
-
- //even out quad contributions
- if (i % 2 == 0)
- {
- binorm[i2].add(binormal);
- }
- else
- {
- binorm[i1].add(binormal);
- }
- }
-
- //normalize binormals
- for (U32 i = 0; i < mNumVertices; i++)
- {
- binorm[i].normalize3fast();
- //bump map/planar projection code requires normals to be normalized
- mNormals[i].normalize3fast();
- }
- }
-}
-
-void LLVolumeFace::resizeVertices(S32 num_verts)
-{
- free(mPositions);
- free(mNormals);
- free(mBinormals);
- free(mTexCoords);
-
- mBinormals = NULL;
-
- if (num_verts)
- {
- mPositions = (LLVector4a*) malloc(sizeof(LLVector4a)*num_verts);
- assert_aligned(mPositions, 16);
- mNormals = (LLVector4a*) malloc(sizeof(LLVector4a)*num_verts);
- assert_aligned(mNormals, 16);
-
- //pad texture coordinate block end to allow for QWORD reads
- S32 size = ((num_verts*sizeof(LLVector2)) + 0xF) & ~0xF;
- mTexCoords = (LLVector2*) malloc(size);
- assert_aligned(mTexCoords, 16);
- }
- else
- {
- mPositions = NULL;
- mNormals = NULL;
- mTexCoords = NULL;
- }
-
- mNumVertices = num_verts;
-}
-
-void LLVolumeFace::pushVertex(const LLVolumeFace::VertexData& cv)
-{
- pushVertex(cv.getPosition(), cv.getNormal(), cv.mTexCoord);
-}
-
-void LLVolumeFace::pushVertex(const LLVector4a& pos, const LLVector4a& norm, const LLVector2& tc)
-{
- S32 new_verts = mNumVertices+1;
- S32 new_size = new_verts*16;
-// S32 old_size = mNumVertices*16;
-
- //positions
- mPositions = (LLVector4a*) realloc(mPositions, new_size);
-
- //normals
- mNormals = (LLVector4a*) realloc(mNormals, new_size);
-
- //tex coords
- new_size = ((new_verts*8)+0xF) & ~0xF;
- mTexCoords = (LLVector2*) realloc(mTexCoords, new_size);
-
-
- //just clear binormals
- free(mBinormals);
- mBinormals = NULL;
-
- mPositions[mNumVertices] = pos;
- mNormals[mNumVertices] = norm;
- mTexCoords[mNumVertices] = tc;
-
- mNumVertices++;
-}
-
-void LLVolumeFace::allocateBinormals(S32 num_verts)
-{
- free(mBinormals);
- mBinormals = (LLVector4a*) malloc(sizeof(LLVector4a)*num_verts);
-}
-
-void LLVolumeFace::allocateWeights(S32 num_verts)
-{
- free(mWeights);
- mWeights = (LLVector4a*) malloc(sizeof(LLVector4a)*num_verts);
-}
-
-void LLVolumeFace::resizeIndices(S32 num_indices)
-{
- free(mIndices);
-
- if (num_indices)
- {
- //pad index block end to allow for QWORD reads
- S32 size = ((num_indices*sizeof(U16)) + 0xF) & ~0xF;
-
- mIndices = (U16*) malloc(size);
- }
- else
- {
- mIndices = NULL;
- }
-
- mNumIndices = num_indices;
-}
-
-void LLVolumeFace::pushIndex(const U16& idx)
-{
- S32 new_count = mNumIndices + 1;
- S32 new_size = ((new_count*2)+0xF) & ~0xF;
-
- S32 old_size = ((mNumIndices*2)+0xF) & ~0xF;
- if (new_size != old_size)
- {
- mIndices = (U16*) realloc(mIndices, new_size);
- }
-
- mIndices[mNumIndices++] = idx;
-}
-
-void LLVolumeFace::fillFromLegacyData(std::vector<LLVolumeFace::VertexData>& v, std::vector<U16>& idx)
-{
- resizeVertices(v.size());
- resizeIndices(idx.size());
-
- for (U32 i = 0; i < v.size(); ++i)
- {
- mPositions[i] = v[i].getPosition();
- mNormals[i] = v[i].getNormal();
- mTexCoords[i] = v[i].mTexCoord;
- }
-
- for (U32 i = 0; i < idx.size(); ++i)
- {
- mIndices[i] = idx[i];
- }
-}
-
-void LLVolumeFace::appendFace(const LLVolumeFace& face, LLMatrix4& mat_in, LLMatrix4& norm_mat_in)
-{
- U16 offset = mNumVertices;
-
- S32 new_count = face.mNumVertices + mNumVertices;
-
- if (new_count > 65536)
- {
- llerrs << "Cannot append face -- 16-bit overflow will occur." << llendl;
- }
-
- if (face.mNumVertices == 0)
- {
- llerrs << "Cannot append empty face." << llendl;
- }
-
- //allocate new buffer space
- mPositions = (LLVector4a*) realloc(mPositions, new_count*sizeof(LLVector4a));
- assert_aligned(mPositions, 16);
- mNormals = (LLVector4a*) realloc(mNormals, new_count*sizeof(LLVector4a));
- assert_aligned(mNormals, 16);
- mTexCoords = (LLVector2*) realloc(mTexCoords, (new_count*sizeof(LLVector2)+0xF) & ~0xF);
- assert_aligned(mTexCoords, 16);
-
- mNumVertices = new_count;
-
- //get destination address of appended face
- LLVector4a* dst_pos = mPositions+offset;
- LLVector2* dst_tc = mTexCoords+offset;
- LLVector4a* dst_norm = mNormals+offset;
-
- //get source addresses of appended face
- const LLVector4a* src_pos = face.mPositions;
- const LLVector2* src_tc = face.mTexCoords;
- const LLVector4a* src_norm = face.mNormals;
-
- //load aligned matrices
- LLMatrix4a mat, norm_mat;
- mat.loadu(mat_in);
- norm_mat.loadu(norm_mat_in);
-
- for (U32 i = 0; i < face.mNumVertices; ++i)
- {
- //transform appended face position and store
- mat.affineTransform(src_pos[i], dst_pos[i]);
-
- //transform appended face normal and store
- norm_mat.rotate(src_norm[i], dst_norm[i]);
- dst_norm[i].normalize3fast();
-
- //copy appended face texture coordinate
- dst_tc[i] = src_tc[i];
-
- if (offset == 0 && i == 0)
- { //initialize bounding box
- mExtents[0] = mExtents[1] = dst_pos[i];
- }
- else
- {
- //stretch bounding box
- update_min_max(mExtents[0], mExtents[1], dst_pos[i]);
- }
- }
-
-
- new_count = mNumIndices + face.mNumIndices;
-
- //allocate new index buffer
- mIndices = (U16*) realloc(mIndices, (new_count*sizeof(U16)+0xF) & ~0xF);
-
- //get destination address into new index buffer
- U16* dst_idx = mIndices+mNumIndices;
- mNumIndices = new_count;
-
- for (U32 i = 0; i < face.mNumIndices; ++i)
- { //copy indices, offsetting by old vertex count
- dst_idx[i] = face.mIndices[i]+offset;
- }
-}
-
-BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
-{
- LLMemType m1(LLMemType::MTYPE_VOLUME);
-
- BOOL flat = mTypeMask & FLAT_MASK;
-
- U8 sculpt_type = volume->getParams().getSculptType();
- U8 sculpt_stitching = sculpt_type & LL_SCULPT_TYPE_MASK;
- BOOL sculpt_invert = sculpt_type & LL_SCULPT_FLAG_INVERT;
- BOOL sculpt_mirror = sculpt_type & LL_SCULPT_FLAG_MIRROR;
- BOOL sculpt_reverse_horizontal = (sculpt_invert ? !sculpt_mirror : sculpt_mirror); // XOR
-
- S32 num_vertices, num_indices;
-
- const std::vector<LLVolume::Point>& mesh = volume->getMesh();
- const std::vector<LLVector3>& profile = volume->getProfile().mProfile;
- const std::vector<LLPath::PathPt>& path_data = volume->getPath().mPath;
-
- S32 max_s = volume->getProfile().getTotal();
-
- S32 s, t, i;
- F32 ss, tt;
-
- num_vertices = mNumS*mNumT;
- num_indices = (mNumS-1)*(mNumT-1)*6;
-
- if (!partial_build)
- {
- resizeVertices(num_vertices);
- resizeIndices(num_indices);
-
- if ((volume->getParams().getSculptType() & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_MESH)
- {
- mEdge.resize(num_indices);
- }
- }
-
- LLVector4a* pos = (LLVector4a*) mPositions;
- LLVector4a* norm = (LLVector4a*) mNormals;
- LLVector2* tc = (LLVector2*) mTexCoords;
- S32 begin_stex = llfloor( profile[mBeginS].mV[2] );
- S32 num_s = ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2) ? mNumS/2 : mNumS;
-
- S32 cur_vertex = 0;
- // Copy the vertices into the array
- for (t = mBeginT; t < mBeginT + mNumT; t++)
- {
- tt = path_data[t].mTexT;
- for (s = 0; s < num_s; s++)
- {
- if (mTypeMask & END_MASK)
- {
- if (s)
- {
- ss = 1.f;
- }
- else
- {
- ss = 0.f;
- }
- }
- else
- {
- // Get s value for tex-coord.
- if (!flat)
- {
- ss = profile[mBeginS + s].mV[2];
- }
- else
- {
- ss = profile[mBeginS + s].mV[2] - begin_stex;
- }
- }
-
- if (sculpt_reverse_horizontal)
- {
- ss = 1.f - ss;
- }
-
- // Check to see if this triangle wraps around the array.
- if (mBeginS + s >= max_s)
- {
- // We're wrapping
- i = mBeginS + s + max_s*(t-1);
- }
- else
- {
- i = mBeginS + s + max_s*t;
- }
-
- pos[cur_vertex].load3(mesh[i].mPos.mV);
- tc[cur_vertex] = LLVector2(ss,tt);
-
- norm[cur_vertex].clear();
- cur_vertex++;
-
- if ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2 && s > 0)
- {
-
- pos[cur_vertex].load3(mesh[i].mPos.mV);
- tc[cur_vertex] = LLVector2(ss,tt);
-
- norm[cur_vertex].clear();
-
- cur_vertex++;
- }
- }
-
- if ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2)
- {
- if (mTypeMask & OPEN_MASK)
- {
- s = num_s-1;
- }
- else
- {
- s = 0;
- }
-
- i = mBeginS + s + max_s*t;
- ss = profile[mBeginS + s].mV[2] - begin_stex;
- pos[cur_vertex].load3(mesh[i].mPos.mV);
- tc[cur_vertex] = LLVector2(ss,tt);
- norm[cur_vertex].clear();
-
- cur_vertex++;
- }
- }
-
-
- //get bounding box for this side
- LLVector4a& face_min = mExtents[0];
- LLVector4a& face_max = mExtents[1];
- mCenter->clear();
-
- face_min = face_max = pos[0];
-
- for (U32 i = 1; i < mNumVertices; ++i)
- {
- update_min_max(face_min, face_max, pos[i]);
- }
-
- mCenter->setAdd(face_min, face_max);
- mCenter->mul(0.5f);
-
- S32 cur_index = 0;
- S32 cur_edge = 0;
- BOOL flat_face = mTypeMask & FLAT_MASK;
-
- if (!partial_build)
- {
- // Now we generate the indices.
- for (t = 0; t < (mNumT-1); t++)
- {
- for (s = 0; s < (mNumS-1); s++)
- {
- mIndices[cur_index++] = s + mNumS*t; //bottom left
- mIndices[cur_index++] = s+1 + mNumS*(t+1); //top right
- mIndices[cur_index++] = s + mNumS*(t+1); //top left
- mIndices[cur_index++] = s + mNumS*t; //bottom left
- mIndices[cur_index++] = s+1 + mNumS*t; //bottom right
- mIndices[cur_index++] = s+1 + mNumS*(t+1); //top right
-
- mEdge[cur_edge++] = (mNumS-1)*2*t+s*2+1; //bottom left/top right neighbor face
- if (t < mNumT-2) { //top right/top left neighbor face
- mEdge[cur_edge++] = (mNumS-1)*2*(t+1)+s*2+1;
- }
- else if (mNumT <= 3 || volume->getPath().isOpen() == TRUE) { //no neighbor
- mEdge[cur_edge++] = -1;
- }
- else { //wrap on T
- mEdge[cur_edge++] = s*2+1;
- }
- if (s > 0) { //top left/bottom left neighbor face
- mEdge[cur_edge++] = (mNumS-1)*2*t+s*2-1;
- }
- else if (flat_face || volume->getProfile().isOpen() == TRUE) { //no neighbor
- mEdge[cur_edge++] = -1;
- }
- else { //wrap on S
- mEdge[cur_edge++] = (mNumS-1)*2*t+(mNumS-2)*2+1;
- }
-
- if (t > 0) { //bottom left/bottom right neighbor face
- mEdge[cur_edge++] = (mNumS-1)*2*(t-1)+s*2;
- }
- else if (mNumT <= 3 || volume->getPath().isOpen() == TRUE) { //no neighbor
- mEdge[cur_edge++] = -1;
- }
- else { //wrap on T
- mEdge[cur_edge++] = (mNumS-1)*2*(mNumT-2)+s*2;
- }
- if (s < mNumS-2) { //bottom right/top right neighbor face
- mEdge[cur_edge++] = (mNumS-1)*2*t+(s+1)*2;
- }
- else if (flat_face || volume->getProfile().isOpen() == TRUE) { //no neighbor
- mEdge[cur_edge++] = -1;
- }
- else { //wrap on S
- mEdge[cur_edge++] = (mNumS-1)*2*t;
- }
- mEdge[cur_edge++] = (mNumS-1)*2*t+s*2; //top right/bottom left neighbor face
- }
- }
- }
-
- //clear normals
- for (U32 i = 0; i < mNumVertices; i++)
- {
- mNormals[i].clear();
- }
-
- //generate normals
- for (U32 i = 0; i < mNumIndices/3; i++) //for each triangle
- {
- const U16* idx = &(mIndices[i*3]);
-
-
- LLVector4a* v[] =
- { pos+idx[0], pos+idx[1], pos+idx[2] };
-
- LLVector4a* n[] =
- { norm+idx[0], norm+idx[1], norm+idx[2] };
-
- //calculate triangle normal
- LLVector4a a, b, c;
-
- a.setSub(*v[0], *v[1]);
- b.setSub(*v[0], *v[2]);
- c.setCross3(a,b);
-
- n[0]->add(c);
- n[1]->add(c);
- n[2]->add(c);
-
- //even out quad contributions
- n[i%2+1]->add(c);
- }
-
- // adjust normals based on wrapping and stitching
-
- LLVector4a top;
- top.setSub(pos[0], pos[mNumS*(mNumT-2)]);
- BOOL s_bottom_converges = (top.dot3(top) < 0.000001f);
-
- top.setSub(pos[mNumS-1], pos[mNumS*(mNumT-2)+mNumS-1]);
- BOOL s_top_converges = (top.dot3(top) < 0.000001f);
-
- if (sculpt_stitching == LL_SCULPT_TYPE_NONE) // logic for non-sculpt volumes
- {
- if (volume->getPath().isOpen() == FALSE)
- { //wrap normals on T
- for (S32 i = 0; i < mNumS; i++)
- {
- LLVector4a n;
- n.setAdd(norm[i], norm[mNumS*(mNumT-1)+i]);
- norm[i] = n;
- norm[mNumS*(mNumT-1)+i] = n;
- }
- }
-
- if ((volume->getProfile().isOpen() == FALSE) && !(s_bottom_converges))
- { //wrap normals on S
- for (S32 i = 0; i < mNumT; i++)
- {
- LLVector4a n;
- n.setAdd(norm[mNumS*i], norm[mNumS*i+mNumS-1]);
- norm[mNumS * i] = n;
- norm[mNumS * i+mNumS-1] = n;
- }
- }
-
- if (volume->getPathType() == LL_PCODE_PATH_CIRCLE &&
- ((volume->getProfileType() & LL_PCODE_PROFILE_MASK) == LL_PCODE_PROFILE_CIRCLE_HALF))
- {
- if (s_bottom_converges)
- { //all lower S have same normal
- for (S32 i = 0; i < mNumT; i++)
- {
- norm[mNumS*i].set(1,0,0);
- }
- }
-
- if (s_top_converges)
- { //all upper S have same normal
- for (S32 i = 0; i < mNumT; i++)
- {
- norm[mNumS*i+mNumS-1].set(-1,0,0);
- }
- }
- }
- }
- else // logic for sculpt volumes
- {
- BOOL average_poles = FALSE;
- BOOL wrap_s = FALSE;
- BOOL wrap_t = FALSE;
-
- if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE)
- average_poles = TRUE;
-
- if ((sculpt_stitching == LL_SCULPT_TYPE_SPHERE) ||
- (sculpt_stitching == LL_SCULPT_TYPE_TORUS) ||
- (sculpt_stitching == LL_SCULPT_TYPE_CYLINDER))
- wrap_s = TRUE;
-
- if (sculpt_stitching == LL_SCULPT_TYPE_TORUS)
- wrap_t = TRUE;
-
-
- if (average_poles)
- {
- // average normals for north pole
-
- LLVector4a average;
- average.clear();
-
- for (S32 i = 0; i < mNumS; i++)
- {
- average.add(norm[i]);
- }
-
- // set average
- for (S32 i = 0; i < mNumS; i++)
- {
- norm[i] = average;
- }
-
- // average normals for south pole
-
- average.clear();
-
- for (S32 i = 0; i < mNumS; i++)
- {
- average.add(norm[i + mNumS * (mNumT - 1)]);
- }
-
- // set average
- for (S32 i = 0; i < mNumS; i++)
- {
- norm[i + mNumS * (mNumT - 1)] = average;
- }
-
- }
-
-
- if (wrap_s)
- {
- for (S32 i = 0; i < mNumT; i++)
- {
- LLVector4a n;
- n.setAdd(norm[mNumS*i], norm[mNumS*i+mNumS-1]);
- norm[mNumS * i] = n;
- norm[mNumS * i+mNumS-1] = n;
- }
- }
-
- if (wrap_t)
- {
- for (S32 i = 0; i < mNumS; i++)
- {
- LLVector4a n;
- n.setAdd(norm[i], norm[mNumS*(mNumT-1)+i]);
- norm[i] = n;
- norm[mNumS*(mNumT-1)+i] = n;
- }
- }
-
- }
-
- return TRUE;
-}
-
-// Finds binormal based on three vertices with texture coordinates.
-// Fills in dummy values if the triangle has degenerate texture coordinates.
-void calc_binormal_from_triangle(LLVector4a& binormal,
-
- const LLVector4a& pos0,
- const LLVector2& tex0,
- const LLVector4a& pos1,
- const LLVector2& tex1,
- const LLVector4a& pos2,
- const LLVector2& tex2)
-{
- LLVector4a rx0( pos0[VX], tex0.mV[VX], tex0.mV[VY] );
- LLVector4a rx1( pos1[VX], tex1.mV[VX], tex1.mV[VY] );
- LLVector4a rx2( pos2[VX], tex2.mV[VX], tex2.mV[VY] );
-
- LLVector4a ry0( pos0[VY], tex0.mV[VX], tex0.mV[VY] );
- LLVector4a ry1( pos1[VY], tex1.mV[VX], tex1.mV[VY] );
- LLVector4a ry2( pos2[VY], tex2.mV[VX], tex2.mV[VY] );
-
- LLVector4a rz0( pos0[VZ], tex0.mV[VX], tex0.mV[VY] );
- LLVector4a rz1( pos1[VZ], tex1.mV[VX], tex1.mV[VY] );
- LLVector4a rz2( pos2[VZ], tex2.mV[VX], tex2.mV[VY] );
-
- LLVector4a lhs, rhs;
-
- LLVector4a r0;
- lhs.setSub(rx0, rx1); rhs.setSub(rx0, rx2);
- r0.setCross3(lhs, rhs);
-
- LLVector4a r1;
- lhs.setSub(ry0, ry1); rhs.setSub(ry0, ry2);
- r1.setCross3(lhs, rhs);
-
- LLVector4a r2;
- lhs.setSub(rz0, rz1); rhs.setSub(rz0, rz2);
- r2.setCross3(lhs, rhs);
-
- if( r0[VX] && r1[VX] && r2[VX] )
- {
- binormal.set(
- -r0[VZ] / r0[VX],
- -r1[VZ] / r1[VX],
- -r2[VZ] / r2[VX]);
- // binormal.normVec();
- }
- else
- {
- binormal.set( 0, 1 , 0 );
- }
-}
+/**
+
+ * @file llvolume.cpp
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llmemory.h"
+#include "llmath.h"
+
+#include <set>
+#if !LL_WINDOWS
+#include <stdint.h>
+#endif
+
+#include "llerror.h"
+#include "llmemtype.h"
+
+#include "llvolumemgr.h"
+#include "v2math.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "m3math.h"
+#include "llmatrix3a.h"
+#include "lloctree.h"
+#include "lldarray.h"
+#include "llvolume.h"
+#include "llvolumeoctree.h"
+#include "llstl.h"
+#include "llsdserialize.h"
+#include "llvector4a.h"
+#include "llmatrix4a.h"
+
+#define DEBUG_SILHOUETTE_BINORMALS 0
+#define DEBUG_SILHOUETTE_NORMALS 0 // TomY: Use this to display normals using the silhouette
+#define DEBUG_SILHOUETTE_EDGE_MAP 0 // DaveP: Use this to display edge map using the silhouette
+
+const F32 CUT_MIN = 0.f;
+const F32 CUT_MAX = 1.f;
+const F32 MIN_CUT_DELTA = 0.02f;
+
+const F32 HOLLOW_MIN = 0.f;
+const F32 HOLLOW_MAX = 0.95f;
+const F32 HOLLOW_MAX_SQUARE = 0.7f;
+
+const F32 TWIST_MIN = -1.f;
+const F32 TWIST_MAX = 1.f;
+
+const F32 RATIO_MIN = 0.f;
+const F32 RATIO_MAX = 2.f; // Tom Y: Inverted sense here: 0 = top taper, 2 = bottom taper
+
+const F32 HOLE_X_MIN= 0.05f;
+const F32 HOLE_X_MAX= 1.0f;
+
+const F32 HOLE_Y_MIN= 0.05f;
+const F32 HOLE_Y_MAX= 0.5f;
+
+const F32 SHEAR_MIN = -0.5f;
+const F32 SHEAR_MAX = 0.5f;
+
+const F32 REV_MIN = 1.f;
+const F32 REV_MAX = 4.f;
+
+const F32 TAPER_MIN = -1.f;
+const F32 TAPER_MAX = 1.f;
+
+const F32 SKEW_MIN = -0.95f;
+const F32 SKEW_MAX = 0.95f;
+
+const F32 SCULPT_MIN_AREA = 0.002f;
+const S32 SCULPT_MIN_AREA_DETAIL = 1;
+
+extern BOOL gDebugGL;
+
+void assert_aligned(void* ptr, uintptr_t alignment)
+{
+#if 0
+ uintptr_t t = (uintptr_t) ptr;
+ if (t%alignment != 0)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+#endif
+}
+
+BOOL check_same_clock_dir( const LLVector3& pt1, const LLVector3& pt2, const LLVector3& pt3, const LLVector3& norm)
+{
+ LLVector3 test = (pt2-pt1)%(pt3-pt2);
+
+ //answer
+ if(test * norm < 0)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+BOOL LLLineSegmentBoxIntersect(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size)
+{
+ return LLLineSegmentBoxIntersect(start.mV, end.mV, center.mV, size.mV);
+}
+
+BOOL LLLineSegmentBoxIntersect(const F32* start, const F32* end, const F32* center, const F32* size)
+{
+ F32 fAWdU[3];
+ F32 dir[3];
+ F32 diff[3];
+
+ for (U32 i = 0; i < 3; i++)
+ {
+ dir[i] = 0.5f * (end[i] - start[i]);
+ diff[i] = (0.5f * (end[i] + start[i])) - center[i];
+ fAWdU[i] = fabsf(dir[i]);
+ if(fabsf(diff[i])>size[i] + fAWdU[i]) return false;
+ }
+
+ float f;
+ f = dir[1] * diff[2] - dir[2] * diff[1]; if(fabsf(f)>size[1]*fAWdU[2] + size[2]*fAWdU[1]) return false;
+ f = dir[2] * diff[0] - dir[0] * diff[2]; if(fabsf(f)>size[0]*fAWdU[2] + size[2]*fAWdU[0]) return false;
+ f = dir[0] * diff[1] - dir[1] * diff[0]; if(fabsf(f)>size[0]*fAWdU[1] + size[1]*fAWdU[0]) return false;
+
+ return true;
+}
+
+
+
+// intersect test between triangle vert0, vert1, vert2 and a ray from orig in direction dir.
+// returns TRUE if intersecting and returns barycentric coordinates in intersection_a, intersection_b,
+// and returns the intersection point along dir in intersection_t.
+
+// Moller-Trumbore algorithm
+BOOL LLTriangleRayIntersect(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir,
+ F32& intersection_a, F32& intersection_b, F32& intersection_t)
+{
+
+ /* find vectors for two edges sharing vert0 */
+ LLVector4a edge1;
+ edge1.setSub(vert1, vert0);
+
+ LLVector4a edge2;
+ edge2.setSub(vert2, vert0);
+
+ /* begin calculating determinant - also used to calculate U parameter */
+ LLVector4a pvec;
+ pvec.setCross3(dir, edge2);
+
+ /* if determinant is near zero, ray lies in plane of triangle */
+ LLVector4a det;
+ det.setAllDot3(edge1, pvec);
+
+ if (det.greaterEqual(LLVector4a::getEpsilon()).getGatheredBits() & 0x7)
+ {
+ /* calculate distance from vert0 to ray origin */
+ LLVector4a tvec;
+ tvec.setSub(orig, vert0);
+
+ /* calculate U parameter and test bounds */
+ LLVector4a u;
+ u.setAllDot3(tvec,pvec);
+
+ if ((u.greaterEqual(LLVector4a::getZero()).getGatheredBits() & 0x7) &&
+ (u.lessEqual(det).getGatheredBits() & 0x7))
+ {
+ /* prepare to test V parameter */
+ LLVector4a qvec;
+ qvec.setCross3(tvec, edge1);
+
+ /* calculate V parameter and test bounds */
+ LLVector4a v;
+ v.setAllDot3(dir, qvec);
+
+
+ //if (!(v < 0.f || u + v > det))
+
+ LLVector4a sum_uv;
+ sum_uv.setAdd(u, v);
+
+ S32 v_gequal = v.greaterEqual(LLVector4a::getZero()).getGatheredBits() & 0x7;
+ S32 sum_lequal = sum_uv.lessEqual(det).getGatheredBits() & 0x7;
+
+ if (v_gequal && sum_lequal)
+ {
+ /* calculate t, scale parameters, ray intersects triangle */
+ LLVector4a t;
+ t.setAllDot3(edge2,qvec);
+
+ t.div(det);
+ u.div(det);
+ v.div(det);
+
+ intersection_a = u[0];
+ intersection_b = v[0];
+ intersection_t = t[0];
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL LLTriangleRayIntersectTwoSided(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir,
+ F32& intersection_a, F32& intersection_b, F32& intersection_t)
+{
+ F32 u, v, t;
+
+ /* find vectors for two edges sharing vert0 */
+ LLVector4a edge1;
+ edge1.setSub(vert1, vert0);
+
+
+ LLVector4a edge2;
+ edge2.setSub(vert2, vert0);
+
+ /* begin calculating determinant - also used to calculate U parameter */
+ LLVector4a pvec;
+ pvec.setCross3(dir, edge2);
+
+ /* if determinant is near zero, ray lies in plane of triangle */
+ F32 det = edge1.dot3(pvec).getF32();
+
+
+ if (det > -F_APPROXIMATELY_ZERO && det < F_APPROXIMATELY_ZERO)
+ {
+ return FALSE;
+ }
+
+ F32 inv_det = 1.f / det;
+
+ /* calculate distance from vert0 to ray origin */
+ LLVector4a tvec;
+ tvec.setSub(orig, vert0);
+
+ /* calculate U parameter and test bounds */
+ u = (tvec.dot3(pvec).getF32()) * inv_det;
+ if (u < 0.f || u > 1.f)
+ {
+ return FALSE;
+ }
+
+ /* prepare to test V parameter */
+ tvec.sub(edge1);
+
+ /* calculate V parameter and test bounds */
+ v = (dir.dot3(tvec).getF32()) * inv_det;
+
+ if (v < 0.f || u + v > 1.f)
+ {
+ return FALSE;
+ }
+
+ /* calculate t, ray intersects triangle */
+ t = (edge2.dot3(tvec).getF32()) * inv_det;
+
+ intersection_a = u;
+ intersection_b = v;
+ intersection_t = t;
+
+
+ return TRUE;
+}
+
+//helper for non-aligned vectors
+BOOL LLTriangleRayIntersect(const LLVector3& vert0, const LLVector3& vert1, const LLVector3& vert2, const LLVector3& orig, const LLVector3& dir,
+ F32& intersection_a, F32& intersection_b, F32& intersection_t, BOOL two_sided)
+{
+ LLVector4a vert0a, vert1a, vert2a, origa, dira;
+ vert0a.load3(vert0.mV);
+ vert1a.load3(vert1.mV);
+ vert2a.load3(vert2.mV);
+ origa.load3(orig.mV);
+ dira.load3(dir.mV);
+
+ if (two_sided)
+ {
+ return LLTriangleRayIntersectTwoSided(vert0a, vert1a, vert2a, origa, dira,
+ intersection_a, intersection_b, intersection_t);
+ }
+ else
+ {
+ return LLTriangleRayIntersect(vert0a, vert1a, vert2a, origa, dira,
+ intersection_a, intersection_b, intersection_t);
+ }
+}
+
+class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle>
+{
+public:
+ const LLVolumeFace* mFace;
+
+ LLVolumeOctreeRebound(const LLVolumeFace* face)
+ {
+ mFace = face;
+ }
+
+ virtual void visit(const LLOctreeNode<LLVolumeTriangle>* branch)
+ { //this is a depth first traversal, so it's safe to assum all children have complete
+ //bounding data
+
+ LLVolumeOctreeListener* node = (LLVolumeOctreeListener*) branch->getListener(0);
+
+ LLVector4a& min = node->mExtents[0];
+ LLVector4a& max = node->mExtents[1];
+
+ if (!branch->getData().empty())
+ { //node has data, find AABB that binds data set
+ const LLVolumeTriangle* tri = *(branch->getData().begin());
+
+ //initialize min/max to first available vertex
+ min = *(tri->mV[0]);
+ max = *(tri->mV[0]);
+
+ for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter =
+ branch->getData().begin(); iter != branch->getData().end(); ++iter)
+ { //for each triangle in node
+
+ //stretch by triangles in node
+ tri = *iter;
+
+ min.setMin(min, *tri->mV[0]);
+ min.setMin(min, *tri->mV[1]);
+ min.setMin(min, *tri->mV[2]);
+
+ max.setMax(max, *tri->mV[0]);
+ max.setMax(max, *tri->mV[1]);
+ max.setMax(max, *tri->mV[2]);
+ }
+ }
+ else if (!branch->getChildren().empty())
+ { //no data, but child nodes exist
+ LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(0)->getListener(0);
+
+ //initialize min/max to extents of first child
+ min = child->mExtents[0];
+ max = child->mExtents[1];
+ }
+ else
+ {
+ llerrs << "WTF? Empty leaf" << llendl;
+ }
+
+ for (S32 i = 0; i < branch->getChildCount(); ++i)
+ { //stretch by child extents
+ LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(i)->getListener(0);
+ min.setMin(min, child->mExtents[0]);
+ max.setMax(max, child->mExtents[1]);
+ }
+
+ node->mBounds[0].setAdd(min, max);
+ node->mBounds[0].mul(0.5f);
+
+ node->mBounds[1].setSub(max,min);
+ node->mBounds[1].mul(0.5f);
+ }
+};
+
+//-------------------------------------------------------------------
+// statics
+//-------------------------------------------------------------------
+
+
+//----------------------------------------------------
+
+LLProfile::Face* LLProfile::addCap(S16 faceID)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ Face *face = vector_append(mFaces, 1);
+
+ face->mIndex = 0;
+ face->mCount = mTotal;
+ face->mScaleU= 1.0f;
+ face->mCap = TRUE;
+ face->mFaceID = faceID;
+ return face;
+}
+
+LLProfile::Face* LLProfile::addFace(S32 i, S32 count, F32 scaleU, S16 faceID, BOOL flat)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ Face *face = vector_append(mFaces, 1);
+
+ face->mIndex = i;
+ face->mCount = count;
+ face->mScaleU= scaleU;
+
+ face->mFlat = flat;
+ face->mCap = FALSE;
+ face->mFaceID = faceID;
+ return face;
+}
+
+// What is the bevel parameter used for? - DJS 04/05/02
+// Bevel parameter is currently unused but presumedly would support
+// filleted and chamfered corners
+void LLProfile::genNGon(const LLProfileParams& params, S32 sides, F32 offset, F32 bevel, F32 ang_scale, S32 split)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ // Generate an n-sided "circular" path.
+ // 0 is (1,0), and we go counter-clockwise along a circular path from there.
+ const F32 tableScale[] = { 1, 1, 1, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f };
+ F32 scale = 0.5f;
+ F32 t, t_step, t_first, t_fraction, ang, ang_step;
+ LLVector3 pt1,pt2;
+
+ F32 begin = params.getBegin();
+ F32 end = params.getEnd();
+
+ t_step = 1.0f / sides;
+ ang_step = 2.0f*F_PI*t_step*ang_scale;
+
+ // Scale to have size "match" scale. Compensates to get object to generally fill bounding box.
+
+ S32 total_sides = llround(sides / ang_scale); // Total number of sides all around
+
+ if (total_sides < 8)
+ {
+ scale = tableScale[total_sides];
+ }
+
+ t_first = floor(begin * sides) / (F32)sides;
+
+ // pt1 is the first point on the fractional face.
+ // Starting t and ang values for the first face
+ t = t_first;
+ ang = 2.0f*F_PI*(t*ang_scale + offset);
+ pt1.setVec(cos(ang)*scale,sin(ang)*scale, t);
+
+ // Increment to the next point.
+ // pt2 is the end point on the fractional face
+ t += t_step;
+ ang += ang_step;
+ pt2.setVec(cos(ang)*scale,sin(ang)*scale,t);
+
+ t_fraction = (begin - t_first)*sides;
+
+ // Only use if it's not almost exactly on an edge.
+ if (t_fraction < 0.9999f)
+ {
+ LLVector3 new_pt = lerp(pt1, pt2, t_fraction);
+ mProfile.push_back(new_pt);
+ }
+
+ // There's lots of potential here for floating point error to generate unneeded extra points - DJS 04/05/02
+ while (t < end)
+ {
+ // Iterate through all the integer steps of t.
+ pt1.setVec(cos(ang)*scale,sin(ang)*scale,t);
+
+ if (mProfile.size() > 0) {
+ LLVector3 p = mProfile[mProfile.size()-1];
+ for (S32 i = 0; i < split && mProfile.size() > 0; i++) {
+ mProfile.push_back(p+(pt1-p) * 1.0f/(float)(split+1) * (float)(i+1));
+ }
+ }
+ mProfile.push_back(pt1);
+
+ t += t_step;
+ ang += ang_step;
+ }
+
+ t_fraction = (end - (t - t_step))*sides;
+
+ // pt1 is the first point on the fractional face
+ // pt2 is the end point on the fractional face
+ pt2.setVec(cos(ang)*scale,sin(ang)*scale,t);
+
+ // Find the fraction that we need to add to the end point.
+ t_fraction = (end - (t - t_step))*sides;
+ if (t_fraction > 0.0001f)
+ {
+ LLVector3 new_pt = lerp(pt1, pt2, t_fraction);
+
+ if (mProfile.size() > 0) {
+ LLVector3 p = mProfile[mProfile.size()-1];
+ for (S32 i = 0; i < split && mProfile.size() > 0; i++) {
+ mProfile.push_back(p+(new_pt-p) * 1.0f/(float)(split+1) * (float)(i+1));
+ }
+ }
+ mProfile.push_back(new_pt);
+ }
+
+ // If we're sliced, the profile is open.
+ if ((end - begin)*ang_scale < 0.99f)
+ {
+ if ((end - begin)*ang_scale > 0.5f)
+ {
+ mConcave = TRUE;
+ }
+ else
+ {
+ mConcave = FALSE;
+ }
+ mOpen = TRUE;
+ if (params.getHollow() <= 0)
+ {
+ // put center point if not hollow.
+ mProfile.push_back(LLVector3(0,0,0));
+ }
+ }
+ else
+ {
+ // The profile isn't open.
+ mOpen = FALSE;
+ mConcave = FALSE;
+ }
+
+ mTotal = mProfile.size();
+}
+
+void LLProfile::genNormals(const LLProfileParams& params)
+{
+ S32 count = mProfile.size();
+
+ S32 outer_count;
+ if (mTotalOut)
+ {
+ outer_count = mTotalOut;
+ }
+ else
+ {
+ outer_count = mTotal / 2;
+ }
+
+ mEdgeNormals.resize(count * 2);
+ mEdgeCenters.resize(count * 2);
+ mNormals.resize(count);
+
+ LLVector2 pt0,pt1;
+
+ BOOL hollow = (params.getHollow() > 0);
+
+ S32 i0, i1, i2, i3, i4;
+
+ // Parametrically generate normal
+ for (i2 = 0; i2 < count; i2++)
+ {
+ mNormals[i2].mV[0] = mProfile[i2].mV[0];
+ mNormals[i2].mV[1] = mProfile[i2].mV[1];
+ if (hollow && (i2 >= outer_count))
+ {
+ mNormals[i2] *= -1.f;
+ }
+ if (mNormals[i2].magVec() < 0.001)
+ {
+ // Special case for point at center, get adjacent points.
+ i1 = (i2 - 1) >= 0 ? i2 - 1 : count - 1;
+ i0 = (i1 - 1) >= 0 ? i1 - 1 : count - 1;
+ i3 = (i2 + 1) < count ? i2 + 1 : 0;
+ i4 = (i3 + 1) < count ? i3 + 1 : 0;
+
+ pt0.setVec(mProfile[i1].mV[VX] + mProfile[i1].mV[VX] - mProfile[i0].mV[VX],
+ mProfile[i1].mV[VY] + mProfile[i1].mV[VY] - mProfile[i0].mV[VY]);
+ pt1.setVec(mProfile[i3].mV[VX] + mProfile[i3].mV[VX] - mProfile[i4].mV[VX],
+ mProfile[i3].mV[VY] + mProfile[i3].mV[VY] - mProfile[i4].mV[VY]);
+
+ mNormals[i2] = pt0 + pt1;
+ mNormals[i2] *= 0.5f;
+ }
+ mNormals[i2].normVec();
+ }
+
+ S32 num_normal_sets = isConcave() ? 2 : 1;
+ for (S32 normal_set = 0; normal_set < num_normal_sets; normal_set++)
+ {
+ S32 point_num;
+ for (point_num = 0; point_num < mTotal; point_num++)
+ {
+ LLVector3 point_1 = mProfile[point_num];
+ point_1.mV[VZ] = 0.f;
+
+ LLVector3 point_2;
+
+ if (isConcave() && normal_set == 0 && point_num == (mTotal - 1) / 2)
+ {
+ point_2 = mProfile[mTotal - 1];
+ }
+ else if (isConcave() && normal_set == 1 && point_num == mTotal - 1)
+ {
+ point_2 = mProfile[(mTotal - 1) / 2];
+ }
+ else
+ {
+ LLVector3 delta_pos;
+ S32 neighbor_point = (point_num + 1) % mTotal;
+ while(delta_pos.magVecSquared() < 0.01f * 0.01f)
+ {
+ point_2 = mProfile[neighbor_point];
+ delta_pos = point_2 - point_1;
+ neighbor_point = (neighbor_point + 1) % mTotal;
+ if (neighbor_point == point_num)
+ {
+ break;
+ }
+ }
+ }
+
+ point_2.mV[VZ] = 0.f;
+ LLVector3 face_normal = (point_2 - point_1) % LLVector3::z_axis;
+ face_normal.normVec();
+ mEdgeNormals[normal_set * count + point_num] = face_normal;
+ mEdgeCenters[normal_set * count + point_num] = lerp(point_1, point_2, 0.5f);
+ }
+ }
+}
+
+
+// Hollow is percent of the original bounding box, not of this particular
+// profile's geometry. Thus, a swept triangle needs lower hollow values than
+// a swept square.
+LLProfile::Face* LLProfile::addHole(const LLProfileParams& params, BOOL flat, F32 sides, F32 offset, F32 box_hollow, F32 ang_scale, S32 split)
+{
+ // Note that addHole will NOT work for non-"circular" profiles, if we ever decide to use them.
+
+ // Total add has number of vertices on outside.
+ mTotalOut = mTotal;
+
+ // Why is the "bevel" parameter -1? DJS 04/05/02
+ genNGon(params, llfloor(sides),offset,-1, ang_scale, split);
+
+ Face *face = addFace(mTotalOut, mTotal-mTotalOut,0,LL_FACE_INNER_SIDE, flat);
+
+ std::vector<LLVector3> pt;
+ pt.resize(mTotal) ;
+
+ for (S32 i=mTotalOut;i<mTotal;i++)
+ {
+ pt[i] = mProfile[i] * box_hollow;
+ }
+
+ S32 j=mTotal-1;
+ for (S32 i=mTotalOut;i<mTotal;i++)
+ {
+ mProfile[i] = pt[j--];
+ }
+
+ for (S32 i=0;i<(S32)mFaces.size();i++)
+ {
+ if (mFaces[i].mCap)
+ {
+ mFaces[i].mCount *= 2;
+ }
+ }
+
+ return face;
+}
+
+
+
+BOOL LLProfile::generate(const LLProfileParams& params, BOOL path_open,F32 detail, S32 split,
+ BOOL is_sculpted, S32 sculpt_size)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ if ((!mDirty) && (!is_sculpted))
+ {
+ return FALSE;
+ }
+ mDirty = FALSE;
+
+ if (detail < MIN_LOD)
+ {
+ llinfos << "Generating profile with LOD < MIN_LOD. CLAMPING" << llendl;
+ detail = MIN_LOD;
+ }
+
+ mProfile.clear();
+ mFaces.clear();
+
+ // Generate the face data
+ S32 i;
+ F32 begin = params.getBegin();
+ F32 end = params.getEnd();
+ F32 hollow = params.getHollow();
+
+ // Quick validation to eliminate some server crashes.
+ if (begin > end - 0.01f)
+ {
+ llwarns << "LLProfile::generate() assertion failed (begin >= end)" << llendl;
+ return FALSE;
+ }
+
+ S32 face_num = 0;
+
+ switch (params.getCurveType() & LL_PCODE_PROFILE_MASK)
+ {
+ case LL_PCODE_PROFILE_SQUARE:
+ {
+ genNGon(params, 4,-0.375, 0, 1, split);
+ if (path_open)
+ {
+ addCap (LL_FACE_PATH_BEGIN);
+ }
+
+ for (i = llfloor(begin * 4.f); i < llfloor(end * 4.f + .999f); i++)
+ {
+ addFace((face_num++) * (split +1), split+2, 1, LL_FACE_OUTER_SIDE_0 << i, TRUE);
+ }
+
+ for (i = 0; i <(S32) mProfile.size(); i++)
+ {
+ // Scale by 4 to generate proper tex coords.
+ mProfile[i].mV[2] *= 4.f;
+ }
+
+ if (hollow)
+ {
+ switch (params.getCurveType() & LL_PCODE_HOLE_MASK)
+ {
+ case LL_PCODE_HOLE_TRIANGLE:
+ // This offset is not correct, but we can't change it now... DK 11/17/04
+ addHole(params, TRUE, 3, -0.375f, hollow, 1.f, split);
+ break;
+ case LL_PCODE_HOLE_CIRCLE:
+ // TODO: Compute actual detail levels for cubes
+ addHole(params, FALSE, MIN_DETAIL_FACES * detail, -0.375f, hollow, 1.f);
+ break;
+ case LL_PCODE_HOLE_SAME:
+ case LL_PCODE_HOLE_SQUARE:
+ default:
+ addHole(params, TRUE, 4, -0.375f, hollow, 1.f, split);
+ break;
+ }
+ }
+
+ if (path_open) {
+ mFaces[0].mCount = mTotal;
+ }
+ }
+ break;
+ case LL_PCODE_PROFILE_ISOTRI:
+ case LL_PCODE_PROFILE_RIGHTTRI:
+ case LL_PCODE_PROFILE_EQUALTRI:
+ {
+ genNGon(params, 3,0, 0, 1, split);
+ for (i = 0; i <(S32) mProfile.size(); i++)
+ {
+ // Scale by 3 to generate proper tex coords.
+ mProfile[i].mV[2] *= 3.f;
+ }
+
+ if (path_open)
+ {
+ addCap(LL_FACE_PATH_BEGIN);
+ }
+
+ for (i = llfloor(begin * 3.f); i < llfloor(end * 3.f + .999f); i++)
+ {
+ addFace((face_num++) * (split +1), split+2, 1, LL_FACE_OUTER_SIDE_0 << i, TRUE);
+ }
+ if (hollow)
+ {
+ // Swept triangles need smaller hollowness values,
+ // because the triangle doesn't fill the bounding box.
+ F32 triangle_hollow = hollow / 2.f;
+
+ switch (params.getCurveType() & LL_PCODE_HOLE_MASK)
+ {
+ case LL_PCODE_HOLE_CIRCLE:
+ // TODO: Actually generate level of detail for triangles
+ addHole(params, FALSE, MIN_DETAIL_FACES * detail, 0, triangle_hollow, 1.f);
+ break;
+ case LL_PCODE_HOLE_SQUARE:
+ addHole(params, TRUE, 4, 0, triangle_hollow, 1.f, split);
+ break;
+ case LL_PCODE_HOLE_SAME:
+ case LL_PCODE_HOLE_TRIANGLE:
+ default:
+ addHole(params, TRUE, 3, 0, triangle_hollow, 1.f, split);
+ break;
+ }
+ }
+ }
+ break;
+ case LL_PCODE_PROFILE_CIRCLE:
+ {
+ // If this has a square hollow, we should adjust the
+ // number of faces a bit so that the geometry lines up.
+ U8 hole_type=0;
+ F32 circle_detail = MIN_DETAIL_FACES * detail;
+ if (hollow)
+ {
+ hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK;
+ if (hole_type == LL_PCODE_HOLE_SQUARE)
+ {
+ // Snap to the next multiple of four sides,
+ // so that corners line up.
+ circle_detail = llceil(circle_detail / 4.0f) * 4.0f;
+ }
+ }
+
+ S32 sides = (S32)circle_detail;
+
+ if (is_sculpted)
+ sides = sculpt_size;
+
+ genNGon(params, sides);
+
+ if (path_open)
+ {
+ addCap (LL_FACE_PATH_BEGIN);
+ }
+
+ if (mOpen && !hollow)
+ {
+ addFace(0,mTotal-1,0,LL_FACE_OUTER_SIDE_0, FALSE);
+ }
+ else
+ {
+ addFace(0,mTotal,0,LL_FACE_OUTER_SIDE_0, FALSE);
+ }
+
+ if (hollow)
+ {
+ switch (hole_type)
+ {
+ case LL_PCODE_HOLE_SQUARE:
+ addHole(params, TRUE, 4, 0, hollow, 1.f, split);
+ break;
+ case LL_PCODE_HOLE_TRIANGLE:
+ addHole(params, TRUE, 3, 0, hollow, 1.f, split);
+ break;
+ case LL_PCODE_HOLE_CIRCLE:
+ case LL_PCODE_HOLE_SAME:
+ default:
+ addHole(params, FALSE, circle_detail, 0, hollow, 1.f);
+ break;
+ }
+ }
+ }
+ break;
+ case LL_PCODE_PROFILE_CIRCLE_HALF:
+ {
+ // If this has a square hollow, we should adjust the
+ // number of faces a bit so that the geometry lines up.
+ U8 hole_type=0;
+ // Number of faces is cut in half because it's only a half-circle.
+ F32 circle_detail = MIN_DETAIL_FACES * detail * 0.5f;
+ if (hollow)
+ {
+ hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK;
+ if (hole_type == LL_PCODE_HOLE_SQUARE)
+ {
+ // Snap to the next multiple of four sides (div 2),
+ // so that corners line up.
+ circle_detail = llceil(circle_detail / 2.0f) * 2.0f;
+ }
+ }
+ genNGon(params, llfloor(circle_detail), 0.5f, 0.f, 0.5f);
+ if (path_open)
+ {
+ addCap(LL_FACE_PATH_BEGIN);
+ }
+ if (mOpen && !params.getHollow())
+ {
+ addFace(0,mTotal-1,0,LL_FACE_OUTER_SIDE_0, FALSE);
+ }
+ else
+ {
+ addFace(0,mTotal,0,LL_FACE_OUTER_SIDE_0, FALSE);
+ }
+
+ if (hollow)
+ {
+ switch (hole_type)
+ {
+ case LL_PCODE_HOLE_SQUARE:
+ addHole(params, TRUE, 2, 0.5f, hollow, 0.5f, split);
+ break;
+ case LL_PCODE_HOLE_TRIANGLE:
+ addHole(params, TRUE, 3, 0.5f, hollow, 0.5f, split);
+ break;
+ case LL_PCODE_HOLE_CIRCLE:
+ case LL_PCODE_HOLE_SAME:
+ default:
+ addHole(params, FALSE, circle_detail, 0.5f, hollow, 0.5f);
+ break;
+ }
+ }
+
+ // Special case for openness of sphere
+ if ((params.getEnd() - params.getBegin()) < 1.f)
+ {
+ mOpen = TRUE;
+ }
+ else if (!hollow)
+ {
+ mOpen = FALSE;
+ mProfile.push_back(mProfile[0]);
+ mTotal++;
+ }
+ }
+ break;
+ default:
+ llerrs << "Unknown profile: getCurveType()=" << params.getCurveType() << llendl;
+ break;
+ };
+
+ if (path_open)
+ {
+ addCap(LL_FACE_PATH_END); // bottom
+ }
+
+ if ( mOpen) // interior edge caps
+ {
+ addFace(mTotal-1, 2,0.5,LL_FACE_PROFILE_BEGIN, TRUE);
+
+ if (hollow)
+ {
+ addFace(mTotalOut-1, 2,0.5,LL_FACE_PROFILE_END, TRUE);
+ }
+ else
+ {
+ addFace(mTotal-2, 2,0.5,LL_FACE_PROFILE_END, TRUE);
+ }
+ }
+
+ //genNormals(params);
+
+ return TRUE;
+}
+
+
+
+BOOL LLProfileParams::importFile(LLFILE *fp)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE]; /* Flawfinder: ignore */
+ // *NOTE: changing the size or type of these buffers will require
+ // changing the sscanf below.
+ char keyword[256]; /* Flawfinder: ignore */
+ char valuestr[256]; /* Flawfinder: ignore */
+ keyword[0] = 0;
+ valuestr[0] = 0;
+ F32 tempF32;
+ U32 tempU32;
+
+ while (!feof(fp))
+ {
+ if (fgets(buffer, BUFSIZE, fp) == NULL)
+ {
+ buffer[0] = '\0';
+ }
+
+ sscanf( /* Flawfinder: ignore */
+ buffer,
+ " %255s %255s",
+ keyword, valuestr);
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("curve", keyword))
+ {
+ sscanf(valuestr,"%d",&tempU32);
+ setCurveType((U8) tempU32);
+ }
+ else if (!strcmp("begin",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setBegin(tempF32);
+ }
+ else if (!strcmp("end",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setEnd(tempF32);
+ }
+ else if (!strcmp("hollow",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setHollow(tempF32);
+ }
+ else
+ {
+ llwarns << "unknown keyword " << keyword << " in profile import" << llendl;
+ }
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLProfileParams::exportFile(LLFILE *fp) const
+{
+ fprintf(fp,"\t\tprofile 0\n");
+ fprintf(fp,"\t\t{\n");
+ fprintf(fp,"\t\t\tcurve\t%d\n", getCurveType());
+ fprintf(fp,"\t\t\tbegin\t%g\n", getBegin());
+ fprintf(fp,"\t\t\tend\t%g\n", getEnd());
+ fprintf(fp,"\t\t\thollow\t%g\n", getHollow());
+ fprintf(fp, "\t\t}\n");
+ return TRUE;
+}
+
+
+BOOL LLProfileParams::importLegacyStream(std::istream& input_stream)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE]; /* Flawfinder: ignore */
+ // *NOTE: changing the size or type of these buffers will require
+ // changing the sscanf below.
+ char keyword[256]; /* Flawfinder: ignore */
+ char valuestr[256]; /* Flawfinder: ignore */
+ keyword[0] = 0;
+ valuestr[0] = 0;
+ F32 tempF32;
+ U32 tempU32;
+
+ while (input_stream.good())
+ {
+ input_stream.getline(buffer, BUFSIZE);
+ sscanf( /* Flawfinder: ignore */
+ buffer,
+ " %255s %255s",
+ keyword,
+ valuestr);
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("curve", keyword))
+ {
+ sscanf(valuestr,"%d",&tempU32);
+ setCurveType((U8) tempU32);
+ }
+ else if (!strcmp("begin",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setBegin(tempF32);
+ }
+ else if (!strcmp("end",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setEnd(tempF32);
+ }
+ else if (!strcmp("hollow",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setHollow(tempF32);
+ }
+ else
+ {
+ llwarns << "unknown keyword " << keyword << " in profile import" << llendl;
+ }
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLProfileParams::exportLegacyStream(std::ostream& output_stream) const
+{
+ output_stream <<"\t\tprofile 0\n";
+ output_stream <<"\t\t{\n";
+ output_stream <<"\t\t\tcurve\t" << (S32) getCurveType() << "\n";
+ output_stream <<"\t\t\tbegin\t" << getBegin() << "\n";
+ output_stream <<"\t\t\tend\t" << getEnd() << "\n";
+ output_stream <<"\t\t\thollow\t" << getHollow() << "\n";
+ output_stream << "\t\t}\n";
+ return TRUE;
+}
+
+LLSD LLProfileParams::asLLSD() const
+{
+ LLSD sd;
+
+ sd["curve"] = getCurveType();
+ sd["begin"] = getBegin();
+ sd["end"] = getEnd();
+ sd["hollow"] = getHollow();
+ return sd;
+}
+
+bool LLProfileParams::fromLLSD(LLSD& sd)
+{
+ setCurveType(sd["curve"].asInteger());
+ setBegin((F32)sd["begin"].asReal());
+ setEnd((F32)sd["end"].asReal());
+ setHollow((F32)sd["hollow"].asReal());
+ return true;
+}
+
+void LLProfileParams::copyParams(const LLProfileParams &params)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+ setCurveType(params.getCurveType());
+ setBegin(params.getBegin());
+ setEnd(params.getEnd());
+ setHollow(params.getHollow());
+}
+
+
+LLPath::~LLPath()
+{
+}
+
+void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 end_scale, F32 twist_scale)
+{
+ // Generates a circular path, starting at (1, 0, 0), counterclockwise along the xz plane.
+ const F32 tableScale[] = { 1, 1, 1, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f };
+
+ F32 revolutions = params.getRevolutions();
+ F32 skew = params.getSkew();
+ F32 skew_mag = fabs(skew);
+ F32 hole_x = params.getScaleX() * (1.0f - skew_mag);
+ F32 hole_y = params.getScaleY();
+
+ // Calculate taper begin/end for x,y (Negative means taper the beginning)
+ F32 taper_x_begin = 1.0f;
+ F32 taper_x_end = 1.0f - params.getTaperX();
+ F32 taper_y_begin = 1.0f;
+ F32 taper_y_end = 1.0f - params.getTaperY();
+
+ if ( taper_x_end > 1.0f )
+ {
+ // Flip tapering.
+ taper_x_begin = 2.0f - taper_x_end;
+ taper_x_end = 1.0f;
+ }
+ if ( taper_y_end > 1.0f )
+ {
+ // Flip tapering.
+ taper_y_begin = 2.0f - taper_y_end;
+ taper_y_end = 1.0f;
+ }
+
+ // For spheres, the radius is usually zero.
+ F32 radius_start = 0.5f;
+ if (sides < 8)
+ {
+ radius_start = tableScale[sides];
+ }
+
+ // Scale the radius to take the hole size into account.
+ radius_start *= 1.0f - hole_y;
+
+ // Now check the radius offset to calculate the start,end radius. (Negative means
+ // decrease the start radius instead).
+ F32 radius_end = radius_start;
+ F32 radius_offset = params.getRadiusOffset();
+ if (radius_offset < 0.f)
+ {
+ radius_start *= 1.f + radius_offset;
+ }
+ else
+ {
+ radius_end *= 1.f - radius_offset;
+ }
+
+ // Is the path NOT a closed loop?
+ mOpen = ( (params.getEnd()*end_scale - params.getBegin() < 1.0f) ||
+ (skew_mag > 0.001f) ||
+ (fabs(taper_x_end - taper_x_begin) > 0.001f) ||
+ (fabs(taper_y_end - taper_y_begin) > 0.001f) ||
+ (fabs(radius_end - radius_start) > 0.001f) );
+
+ F32 ang, c, s;
+ LLQuaternion twist, qang;
+ PathPt *pt;
+ LLVector3 path_axis (1.f, 0.f, 0.f);
+ //LLVector3 twist_axis(0.f, 0.f, 1.f);
+ F32 twist_begin = params.getTwistBegin() * twist_scale;
+ F32 twist_end = params.getTwist() * twist_scale;
+
+ // We run through this once before the main loop, to make sure
+ // the path begins at the correct cut.
+ F32 step= 1.0f / sides;
+ F32 t = params.getBegin();
+ pt = vector_append(mPath, 1);
+ ang = 2.0f*F_PI*revolutions * t;
+ s = sin(ang)*lerp(radius_start, radius_end, t);
+ c = cos(ang)*lerp(radius_start, radius_end, t);
+
+
+ pt->mPos.setVec(0 + lerp(0,params.getShear().mV[0],s)
+ + lerp(-skew ,skew, t) * 0.5f,
+ c + lerp(0,params.getShear().mV[1],s),
+ s);
+ pt->mScale.mV[VX] = hole_x * lerp(taper_x_begin, taper_x_end, t);
+ pt->mScale.mV[VY] = hole_y * lerp(taper_y_begin, taper_y_end, t);
+ pt->mTexT = t;
+
+ // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
+ twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
+ // Rotate the point around the circle's center.
+ qang.setQuat (ang,path_axis);
+ pt->mRot = twist * qang;
+
+ t+=step;
+
+ // Snap to a quantized parameter, so that cut does not
+ // affect most sample points.
+ t = ((S32)(t * sides)) / (F32)sides;
+
+ // Run through the non-cut dependent points.
+ while (t < params.getEnd())
+ {
+ pt = vector_append(mPath, 1);
+
+ ang = 2.0f*F_PI*revolutions * t;
+ c = cos(ang)*lerp(radius_start, radius_end, t);
+ s = sin(ang)*lerp(radius_start, radius_end, t);
+
+ pt->mPos.setVec(0 + lerp(0,params.getShear().mV[0],s)
+ + lerp(-skew ,skew, t) * 0.5f,
+ c + lerp(0,params.getShear().mV[1],s),
+ s);
+
+ pt->mScale.mV[VX] = hole_x * lerp(taper_x_begin, taper_x_end, t);
+ pt->mScale.mV[VY] = hole_y * lerp(taper_y_begin, taper_y_end, t);
+ pt->mTexT = t;
+
+ // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
+ twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
+ // Rotate the point around the circle's center.
+ qang.setQuat (ang,path_axis);
+ pt->mRot = twist * qang;
+
+ t+=step;
+ }
+
+ // Make one final pass for the end cut.
+ t = params.getEnd();
+ pt = vector_append(mPath, 1);
+ ang = 2.0f*F_PI*revolutions * t;
+ c = cos(ang)*lerp(radius_start, radius_end, t);
+ s = sin(ang)*lerp(radius_start, radius_end, t);
+
+ pt->mPos.setVec(0 + lerp(0,params.getShear().mV[0],s)
+ + lerp(-skew ,skew, t) * 0.5f,
+ c + lerp(0,params.getShear().mV[1],s),
+ s);
+ pt->mScale.mV[VX] = hole_x * lerp(taper_x_begin, taper_x_end, t);
+ pt->mScale.mV[VY] = hole_y * lerp(taper_y_begin, taper_y_end, t);
+ pt->mTexT = t;
+
+ // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
+ twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
+ // Rotate the point around the circle's center.
+ qang.setQuat (ang,path_axis);
+ pt->mRot = twist * qang;
+
+ mTotal = mPath.size();
+}
+
+const LLVector2 LLPathParams::getBeginScale() const
+{
+ LLVector2 begin_scale(1.f, 1.f);
+ if (getScaleX() > 1)
+ {
+ begin_scale.mV[0] = 2-getScaleX();
+ }
+ if (getScaleY() > 1)
+ {
+ begin_scale.mV[1] = 2-getScaleY();
+ }
+ return begin_scale;
+}
+
+const LLVector2 LLPathParams::getEndScale() const
+{
+ LLVector2 end_scale(1.f, 1.f);
+ if (getScaleX() < 1)
+ {
+ end_scale.mV[0] = getScaleX();
+ }
+ if (getScaleY() < 1)
+ {
+ end_scale.mV[1] = getScaleY();
+ }
+ return end_scale;
+}
+
+BOOL LLPath::generate(const LLPathParams& params, F32 detail, S32 split,
+ BOOL is_sculpted, S32 sculpt_size)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ if ((!mDirty) && (!is_sculpted))
+ {
+ return FALSE;
+ }
+
+ if (detail < MIN_LOD)
+ {
+ llinfos << "Generating path with LOD < MIN! Clamping to 1" << llendl;
+ detail = MIN_LOD;
+ }
+
+ mDirty = FALSE;
+ S32 np = 2; // hardcode for line
+
+ mPath.clear();
+ mOpen = TRUE;
+
+ // Is this 0xf0 mask really necessary? DK 03/02/05
+ switch (params.getCurveType() & 0xf0)
+ {
+ default:
+ case LL_PCODE_PATH_LINE:
+ {
+ // Take the begin/end twist into account for detail.
+ np = llfloor(fabs(params.getTwistBegin() - params.getTwist()) * 3.5f * (detail-0.5f)) + 2;
+ if (np < split+2)
+ {
+ np = split+2;
+ }
+
+ mStep = 1.0f / (np-1);
+
+ mPath.resize(np);
+
+ LLVector2 start_scale = params.getBeginScale();
+ LLVector2 end_scale = params.getEndScale();
+
+ for (S32 i=0;i<np;i++)
+ {
+ F32 t = lerp(params.getBegin(),params.getEnd(),(F32)i * mStep);
+ mPath[i].mPos.setVec(lerp(0,params.getShear().mV[0],t),
+ lerp(0,params.getShear().mV[1],t),
+ t - 0.5f);
+ mPath[i].mRot.setQuat(lerp(F_PI * params.getTwistBegin(),F_PI * params.getTwist(),t),0,0,1);
+ mPath[i].mScale.mV[0] = lerp(start_scale.mV[0],end_scale.mV[0],t);
+ mPath[i].mScale.mV[1] = lerp(start_scale.mV[1],end_scale.mV[1],t);
+ mPath[i].mTexT = t;
+ }
+ }
+ break;
+
+ case LL_PCODE_PATH_CIRCLE:
+ {
+ // Increase the detail as the revolutions and twist increase.
+ F32 twist_mag = fabs(params.getTwistBegin() - params.getTwist());
+
+ S32 sides = (S32)llfloor(llfloor((MIN_DETAIL_FACES * detail + twist_mag * 3.5f * (detail-0.5f))) * params.getRevolutions());
+
+ if (is_sculpted)
+ sides = sculpt_size;
+
+ genNGon(params, sides);
+ }
+ break;
+
+ case LL_PCODE_PATH_CIRCLE2:
+ {
+ if (params.getEnd() - params.getBegin() >= 0.99f &&
+ params.getScaleX() >= .99f)
+ {
+ mOpen = FALSE;
+ }
+
+ //genNGon(params, llfloor(MIN_DETAIL_FACES * detail), 4.f, 0.f);
+ genNGon(params, llfloor(MIN_DETAIL_FACES * detail));
+
+ F32 t = 0.f;
+ F32 tStep = 1.0f / mPath.size();
+
+ F32 toggle = 0.5f;
+ for (S32 i=0;i<(S32)mPath.size();i++)
+ {
+ mPath[i].mPos.mV[0] = toggle;
+ if (toggle == 0.5f)
+ toggle = -0.5f;
+ else
+ toggle = 0.5f;
+ t += tStep;
+ }
+ }
+
+ break;
+
+ case LL_PCODE_PATH_TEST:
+
+ np = 5;
+ mStep = 1.0f / (np-1);
+
+ mPath.resize(np);
+
+ for (S32 i=0;i<np;i++)
+ {
+ F32 t = (F32)i * mStep;
+ mPath[i].mPos.setVec(0,
+ lerp(0, -sin(F_PI*params.getTwist()*t)*0.5f,t),
+ lerp(-0.5, cos(F_PI*params.getTwist()*t)*0.5f,t));
+ mPath[i].mScale.mV[0] = lerp(1,params.getScale().mV[0],t);
+ mPath[i].mScale.mV[1] = lerp(1,params.getScale().mV[1],t);
+ mPath[i].mTexT = t;
+ mPath[i].mRot.setQuat(F_PI * params.getTwist() * t,1,0,0);
+ }
+
+ break;
+ };
+
+ if (params.getTwist() != params.getTwistBegin()) mOpen = TRUE;
+
+ //if ((int(fabsf(params.getTwist() - params.getTwistBegin())*100))%100 != 0) {
+ // mOpen = TRUE;
+ //}
+
+ return TRUE;
+}
+
+BOOL LLDynamicPath::generate(const LLPathParams& params, F32 detail, S32 split,
+ BOOL is_sculpted, S32 sculpt_size)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ mOpen = TRUE; // Draw end caps
+ if (getPathLength() == 0)
+ {
+ // Path hasn't been generated yet.
+ // Some algorithms later assume at least TWO path points.
+ resizePath(2);
+ for (U32 i = 0; i < 2; i++)
+ {
+ mPath[i].mPos.setVec(0, 0, 0);
+ mPath[i].mRot.setQuat(0, 0, 0);
+ mPath[i].mScale.setVec(1, 1);
+ mPath[i].mTexT = 0;
+ }
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLPathParams::importFile(LLFILE *fp)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE]; /* Flawfinder: ignore */
+ // *NOTE: changing the size or type of these buffers will require
+ // changing the sscanf below.
+ char keyword[256]; /* Flawfinder: ignore */
+ char valuestr[256]; /* Flawfinder: ignore */
+ keyword[0] = 0;
+ valuestr[0] = 0;
+
+ F32 tempF32;
+ F32 x, y;
+ U32 tempU32;
+
+ while (!feof(fp))
+ {
+ if (fgets(buffer, BUFSIZE, fp) == NULL)
+ {
+ buffer[0] = '\0';
+ }
+
+ sscanf( /* Flawfinder: ignore */
+ buffer,
+ " %255s %255s",
+ keyword, valuestr);
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("curve", keyword))
+ {
+ sscanf(valuestr,"%d",&tempU32);
+ setCurveType((U8) tempU32);
+ }
+ else if (!strcmp("begin",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setBegin(tempF32);
+ }
+ else if (!strcmp("end",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setEnd(tempF32);
+ }
+ else if (!strcmp("scale",keyword))
+ {
+ // Legacy for one dimensional scale per path
+ sscanf(valuestr,"%g",&tempF32);
+ setScale(tempF32, tempF32);
+ }
+ else if (!strcmp("scale_x", keyword))
+ {
+ sscanf(valuestr, "%g", &x);
+ setScaleX(x);
+ }
+ else if (!strcmp("scale_y", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setScaleY(y);
+ }
+ else if (!strcmp("shear_x", keyword))
+ {
+ sscanf(valuestr, "%g", &x);
+ setShearX(x);
+ }
+ else if (!strcmp("shear_y", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setShearY(y);
+ }
+ else if (!strcmp("twist",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setTwist(tempF32);
+ }
+ else if (!strcmp("twist_begin", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setTwistBegin(y);
+ }
+ else if (!strcmp("radius_offset", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setRadiusOffset(y);
+ }
+ else if (!strcmp("taper_x", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setTaperX(y);
+ }
+ else if (!strcmp("taper_y", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setTaperY(y);
+ }
+ else if (!strcmp("revolutions", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setRevolutions(y);
+ }
+ else if (!strcmp("skew", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setSkew(y);
+ }
+ else
+ {
+ llwarns << "unknown keyword " << " in path import" << llendl;
+ }
+ }
+ return TRUE;
+}
+
+
+BOOL LLPathParams::exportFile(LLFILE *fp) const
+{
+ fprintf(fp, "\t\tpath 0\n");
+ fprintf(fp, "\t\t{\n");
+ fprintf(fp, "\t\t\tcurve\t%d\n", getCurveType());
+ fprintf(fp, "\t\t\tbegin\t%g\n", getBegin());
+ fprintf(fp, "\t\t\tend\t%g\n", getEnd());
+ fprintf(fp, "\t\t\tscale_x\t%g\n", getScaleX() );
+ fprintf(fp, "\t\t\tscale_y\t%g\n", getScaleY() );
+ fprintf(fp, "\t\t\tshear_x\t%g\n", getShearX() );
+ fprintf(fp, "\t\t\tshear_y\t%g\n", getShearY() );
+ fprintf(fp,"\t\t\ttwist\t%g\n", getTwist());
+
+ fprintf(fp,"\t\t\ttwist_begin\t%g\n", getTwistBegin());
+ fprintf(fp,"\t\t\tradius_offset\t%g\n", getRadiusOffset());
+ fprintf(fp,"\t\t\ttaper_x\t%g\n", getTaperX());
+ fprintf(fp,"\t\t\ttaper_y\t%g\n", getTaperY());
+ fprintf(fp,"\t\t\trevolutions\t%g\n", getRevolutions());
+ fprintf(fp,"\t\t\tskew\t%g\n", getSkew());
+
+ fprintf(fp, "\t\t}\n");
+ return TRUE;
+}
+
+
+BOOL LLPathParams::importLegacyStream(std::istream& input_stream)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE]; /* Flawfinder: ignore */
+ // *NOTE: changing the size or type of these buffers will require
+ // changing the sscanf below.
+ char keyword[256]; /* Flawfinder: ignore */
+ char valuestr[256]; /* Flawfinder: ignore */
+ keyword[0] = 0;
+ valuestr[0] = 0;
+
+ F32 tempF32;
+ F32 x, y;
+ U32 tempU32;
+
+ while (input_stream.good())
+ {
+ input_stream.getline(buffer, BUFSIZE);
+ sscanf( /* Flawfinder: ignore */
+ buffer,
+ " %255s %255s",
+ keyword, valuestr);
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("curve", keyword))
+ {
+ sscanf(valuestr,"%d",&tempU32);
+ setCurveType((U8) tempU32);
+ }
+ else if (!strcmp("begin",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setBegin(tempF32);
+ }
+ else if (!strcmp("end",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setEnd(tempF32);
+ }
+ else if (!strcmp("scale",keyword))
+ {
+ // Legacy for one dimensional scale per path
+ sscanf(valuestr,"%g",&tempF32);
+ setScale(tempF32, tempF32);
+ }
+ else if (!strcmp("scale_x", keyword))
+ {
+ sscanf(valuestr, "%g", &x);
+ setScaleX(x);
+ }
+ else if (!strcmp("scale_y", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setScaleY(y);
+ }
+ else if (!strcmp("shear_x", keyword))
+ {
+ sscanf(valuestr, "%g", &x);
+ setShearX(x);
+ }
+ else if (!strcmp("shear_y", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setShearY(y);
+ }
+ else if (!strcmp("twist",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setTwist(tempF32);
+ }
+ else if (!strcmp("twist_begin", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setTwistBegin(y);
+ }
+ else if (!strcmp("radius_offset", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setRadiusOffset(y);
+ }
+ else if (!strcmp("taper_x", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setTaperX(y);
+ }
+ else if (!strcmp("taper_y", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setTaperY(y);
+ }
+ else if (!strcmp("revolutions", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setRevolutions(y);
+ }
+ else if (!strcmp("skew", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setSkew(y);
+ }
+ else
+ {
+ llwarns << "unknown keyword " << " in path import" << llendl;
+ }
+ }
+ return TRUE;
+}
+
+
+BOOL LLPathParams::exportLegacyStream(std::ostream& output_stream) const
+{
+ output_stream << "\t\tpath 0\n";
+ output_stream << "\t\t{\n";
+ output_stream << "\t\t\tcurve\t" << (S32) getCurveType() << "\n";
+ output_stream << "\t\t\tbegin\t" << getBegin() << "\n";
+ output_stream << "\t\t\tend\t" << getEnd() << "\n";
+ output_stream << "\t\t\tscale_x\t" << getScaleX() << "\n";
+ output_stream << "\t\t\tscale_y\t" << getScaleY() << "\n";
+ output_stream << "\t\t\tshear_x\t" << getShearX() << "\n";
+ output_stream << "\t\t\tshear_y\t" << getShearY() << "\n";
+ output_stream <<"\t\t\ttwist\t" << getTwist() << "\n";
+
+ output_stream <<"\t\t\ttwist_begin\t" << getTwistBegin() << "\n";
+ output_stream <<"\t\t\tradius_offset\t" << getRadiusOffset() << "\n";
+ output_stream <<"\t\t\ttaper_x\t" << getTaperX() << "\n";
+ output_stream <<"\t\t\ttaper_y\t" << getTaperY() << "\n";
+ output_stream <<"\t\t\trevolutions\t" << getRevolutions() << "\n";
+ output_stream <<"\t\t\tskew\t" << getSkew() << "\n";
+
+ output_stream << "\t\t}\n";
+ return TRUE;
+}
+
+LLSD LLPathParams::asLLSD() const
+{
+ LLSD sd = LLSD();
+ sd["curve"] = getCurveType();
+ sd["begin"] = getBegin();
+ sd["end"] = getEnd();
+ sd["scale_x"] = getScaleX();
+ sd["scale_y"] = getScaleY();
+ sd["shear_x"] = getShearX();
+ sd["shear_y"] = getShearY();
+ sd["twist"] = getTwist();
+ sd["twist_begin"] = getTwistBegin();
+ sd["radius_offset"] = getRadiusOffset();
+ sd["taper_x"] = getTaperX();
+ sd["taper_y"] = getTaperY();
+ sd["revolutions"] = getRevolutions();
+ sd["skew"] = getSkew();
+
+ return sd;
+}
+
+bool LLPathParams::fromLLSD(LLSD& sd)
+{
+ setCurveType(sd["curve"].asInteger());
+ setBegin((F32)sd["begin"].asReal());
+ setEnd((F32)sd["end"].asReal());
+ setScaleX((F32)sd["scale_x"].asReal());
+ setScaleY((F32)sd["scale_y"].asReal());
+ setShearX((F32)sd["shear_x"].asReal());
+ setShearY((F32)sd["shear_y"].asReal());
+ setTwist((F32)sd["twist"].asReal());
+ setTwistBegin((F32)sd["twist_begin"].asReal());
+ setRadiusOffset((F32)sd["radius_offset"].asReal());
+ setTaperX((F32)sd["taper_x"].asReal());
+ setTaperY((F32)sd["taper_y"].asReal());
+ setRevolutions((F32)sd["revolutions"].asReal());
+ setSkew((F32)sd["skew"].asReal());
+ return true;
+}
+
+void LLPathParams::copyParams(const LLPathParams &params)
+{
+ setCurveType(params.getCurveType());
+ setBegin(params.getBegin());
+ setEnd(params.getEnd());
+ setScale(params.getScaleX(), params.getScaleY() );
+ setShear(params.getShearX(), params.getShearY() );
+ setTwist(params.getTwist());
+ setTwistBegin(params.getTwistBegin());
+ setRadiusOffset(params.getRadiusOffset());
+ setTaper( params.getTaperX(), params.getTaperY() );
+ setRevolutions(params.getRevolutions());
+ setSkew(params.getSkew());
+}
+
+S32 profile_delete_lock = 1 ;
+LLProfile::~LLProfile()
+{
+ if(profile_delete_lock)
+ {
+ llerrs << "LLProfile should not be deleted here!" << llendl ;
+ }
+}
+
+
+S32 LLVolume::sNumMeshPoints = 0;
+
+LLVolume::LLVolume(const LLVolumeParams &params, const F32 detail, const BOOL generate_single_face, const BOOL is_unique)
+ : mParams(params)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ mUnique = is_unique;
+ mFaceMask = 0x0;
+ mDetail = detail;
+ mSculptLevel = -2;
+ mIsTetrahedron = FALSE;
+ mLODScaleBias.setVec(1,1,1);
+ mHullPoints = NULL;
+ mHullIndices = NULL;
+ mNumHullPoints = 0;
+ mNumHullIndices = 0;
+
+ // set defaults
+ if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE)
+ {
+ mPathp = new LLDynamicPath();
+ }
+ else
+ {
+ mPathp = new LLPath();
+ }
+ mProfilep = new LLProfile();
+
+ mGenerateSingleFace = generate_single_face;
+
+ generate();
+
+ if (mParams.getSculptID().isNull() && mParams.getSculptType() == LL_SCULPT_TYPE_NONE)
+ {
+ createVolumeFaces();
+ }
+}
+
+void LLVolume::resizePath(S32 length)
+{
+ mPathp->resizePath(length);
+ mVolumeFaces.clear();
+}
+
+void LLVolume::regen()
+{
+ generate();
+ createVolumeFaces();
+}
+
+void LLVolume::genBinormals(S32 face)
+{
+ mVolumeFaces[face].createBinormals();
+}
+
+LLVolume::~LLVolume()
+{
+ sNumMeshPoints -= mMesh.size();
+ delete mPathp;
+
+ profile_delete_lock = 0 ;
+ delete mProfilep;
+ profile_delete_lock = 1 ;
+
+ mPathp = NULL;
+ mProfilep = NULL;
+ mVolumeFaces.clear();
+
+ free(mHullPoints);
+ mHullPoints = NULL;
+ free(mHullIndices);
+ mHullIndices = NULL;
+}
+
+BOOL LLVolume::generate()
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+ llassert_always(mProfilep);
+
+ //Added 10.03.05 Dave Parks
+ // Split is a parameter to LLProfile::generate that tesselates edges on the profile
+ // to prevent lighting and texture interpolation errors on triangles that are
+ // stretched due to twisting or scaling on the path.
+ S32 split = (S32) ((mDetail)*0.66f);
+
+ if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_LINE &&
+ (mParams.getPathParams().getScale().mV[0] != 1.0f ||
+ mParams.getPathParams().getScale().mV[1] != 1.0f) &&
+ (mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_SQUARE ||
+ mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_ISOTRI ||
+ mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_EQUALTRI ||
+ mParams.getProfileParams().getCurveType() == LL_PCODE_PROFILE_RIGHTTRI))
+ {
+ split = 0;
+ }
+
+ mLODScaleBias.setVec(0.5f, 0.5f, 0.5f);
+
+ F32 profile_detail = mDetail;
+ F32 path_detail = mDetail;
+
+ U8 path_type = mParams.getPathParams().getCurveType();
+ U8 profile_type = mParams.getProfileParams().getCurveType();
+
+ if (path_type == LL_PCODE_PATH_LINE && profile_type == LL_PCODE_PROFILE_CIRCLE)
+ { //cylinders don't care about Z-Axis
+ mLODScaleBias.setVec(0.6f, 0.6f, 0.0f);
+ }
+ else if (path_type == LL_PCODE_PATH_CIRCLE)
+ {
+ mLODScaleBias.setVec(0.6f, 0.6f, 0.6f);
+ }
+
+ //********************************************************************
+ //debug info, to be removed
+ if((U32)(mPathp->mPath.size() * mProfilep->mProfile.size()) > (1u << 20))
+ {
+ llinfos << "sizeS: " << mPathp->mPath.size() << " sizeT: " << mProfilep->mProfile.size() << llendl ;
+ llinfos << "path_detail : " << path_detail << " split: " << split << " profile_detail: " << profile_detail << llendl ;
+ llinfos << mParams << llendl ;
+ llinfos << "more info to check if mProfilep is deleted or not." << llendl ;
+ llinfos << mProfilep->mNormals.size() << " : " << mProfilep->mFaces.size() << " : " << mProfilep->mEdgeNormals.size() << " : " << mProfilep->mEdgeCenters.size() << llendl ;
+
+ llerrs << "LLVolume corrupted!" << llendl ;
+ }
+ //********************************************************************
+
+ BOOL regenPath = mPathp->generate(mParams.getPathParams(), path_detail, split);
+ BOOL regenProf = mProfilep->generate(mParams.getProfileParams(), mPathp->isOpen(),profile_detail, split);
+
+ if (regenPath || regenProf )
+ {
+ S32 sizeS = mPathp->mPath.size();
+ S32 sizeT = mProfilep->mProfile.size();
+
+ //********************************************************************
+ //debug info, to be removed
+ if((U32)(sizeS * sizeT) > (1u << 20))
+ {
+ llinfos << "regenPath: " << (S32)regenPath << " regenProf: " << (S32)regenProf << llendl ;
+ llinfos << "sizeS: " << sizeS << " sizeT: " << sizeT << llendl ;
+ llinfos << "path_detail : " << path_detail << " split: " << split << " profile_detail: " << profile_detail << llendl ;
+ llinfos << mParams << llendl ;
+ llinfos << "more info to check if mProfilep is deleted or not." << llendl ;
+ llinfos << mProfilep->mNormals.size() << " : " << mProfilep->mFaces.size() << " : " << mProfilep->mEdgeNormals.size() << " : " << mProfilep->mEdgeCenters.size() << llendl ;
+
+ llerrs << "LLVolume corrupted!" << llendl ;
+ }
+ //********************************************************************
+
+ sNumMeshPoints -= mMesh.size();
+ mMesh.resize(sizeT * sizeS);
+ sNumMeshPoints += mMesh.size();
+
+ //generate vertex positions
+
+ // Run along the path.
+ for (S32 s = 0; s < sizeS; ++s)
+ {
+ LLVector2 scale = mPathp->mPath[s].mScale;
+ LLQuaternion rot = mPathp->mPath[s].mRot;
+
+ // Run along the profile.
+ for (S32 t = 0; t < sizeT; ++t)
+ {
+ S32 m = s*sizeT + t;
+ Point& pt = mMesh[m];
+
+ pt.mPos.mV[0] = mProfilep->mProfile[t].mV[0] * scale.mV[0];
+ pt.mPos.mV[1] = mProfilep->mProfile[t].mV[1] * scale.mV[1];
+ pt.mPos.mV[2] = 0.0f;
+ pt.mPos = pt.mPos * rot;
+ pt.mPos += mPathp->mPath[s].mPos;
+ }
+ }
+
+ for (std::vector<LLProfile::Face>::iterator iter = mProfilep->mFaces.begin();
+ iter != mProfilep->mFaces.end(); ++iter)
+ {
+ LLFaceID id = iter->mFaceID;
+ mFaceMask |= id;
+ }
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLVolumeFace::VertexData::init()
+{
+ if (!mData)
+ {
+ mData = (LLVector4a*) malloc(sizeof(LLVector4a)*2);
+ }
+}
+
+LLVolumeFace::VertexData::VertexData()
+{
+ mData = NULL;
+ init();
+}
+
+LLVolumeFace::VertexData::VertexData(const VertexData& rhs)
+{
+ mData = NULL;
+ *this = rhs;
+}
+
+const LLVolumeFace::VertexData& LLVolumeFace::VertexData::operator=(const LLVolumeFace::VertexData& rhs)
+{
+ if (this != &rhs)
+ {
+ init();
+ LLVector4a::memcpyNonAliased16((F32*) mData, (F32*) rhs.mData, 2*sizeof(LLVector4a));
+ mTexCoord = rhs.mTexCoord;
+ }
+ return *this;
+}
+
+LLVolumeFace::VertexData::~VertexData()
+{
+ free(mData);
+ mData = NULL;
+}
+
+LLVector4a& LLVolumeFace::VertexData::getPosition()
+{
+ return mData[POSITION];
+}
+
+LLVector4a& LLVolumeFace::VertexData::getNormal()
+{
+ return mData[NORMAL];
+}
+
+const LLVector4a& LLVolumeFace::VertexData::getPosition() const
+{
+ return mData[POSITION];
+}
+
+const LLVector4a& LLVolumeFace::VertexData::getNormal() const
+{
+ return mData[NORMAL];
+}
+
+
+void LLVolumeFace::VertexData::setPosition(const LLVector4a& pos)
+{
+ mData[POSITION] = pos;
+}
+
+void LLVolumeFace::VertexData::setNormal(const LLVector4a& norm)
+{
+ mData[NORMAL] = norm;
+}
+
+bool LLVolumeFace::VertexData::operator<(const LLVolumeFace::VertexData& rhs)const
+{
+ const F32* lp = this->getPosition().getF32ptr();
+ const F32* rp = rhs.getPosition().getF32ptr();
+
+ if (lp[0] != rp[0])
+ {
+ return lp[0] < rp[0];
+ }
+
+ if (rp[1] != lp[1])
+ {
+ return lp[1] < rp[1];
+ }
+
+ if (rp[2] != lp[2])
+ {
+ return lp[2] < rp[2];
+ }
+
+ lp = getNormal().getF32ptr();
+ rp = rhs.getNormal().getF32ptr();
+
+ if (lp[0] != rp[0])
+ {
+ return lp[0] < rp[0];
+ }
+
+ if (rp[1] != lp[1])
+ {
+ return lp[1] < rp[1];
+ }
+
+ if (rp[2] != lp[2])
+ {
+ return lp[2] < rp[2];
+ }
+
+ if (mTexCoord.mV[0] != rhs.mTexCoord.mV[0])
+ {
+ return mTexCoord.mV[0] < rhs.mTexCoord.mV[0];
+ }
+
+ return mTexCoord.mV[1] < rhs.mTexCoord.mV[1];
+}
+
+bool LLVolumeFace::VertexData::operator==(const LLVolumeFace::VertexData& rhs)const
+{
+ return mData[POSITION].equals3(rhs.getPosition()) &&
+ mData[NORMAL].equals3(rhs.getNormal()) &&
+ mTexCoord == rhs.mTexCoord;
+}
+
+bool LLVolumeFace::VertexData::compareNormal(const LLVolumeFace::VertexData& rhs, F32 angle_cutoff) const
+{
+ bool retval = false;
+ if (rhs.mData[POSITION].equals3(mData[POSITION]) && rhs.mTexCoord == mTexCoord)
+ {
+ if (angle_cutoff > 1.f)
+ {
+ retval = (mData[NORMAL].equals3(rhs.mData[NORMAL]));
+ }
+ else
+ {
+ F32 cur_angle = rhs.mData[NORMAL].dot3(mData[NORMAL]).getF32();
+ retval = cur_angle > angle_cutoff;
+ }
+ }
+
+ return retval;
+}
+
+bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
+{
+ //input stream is now pointing at a zlib compressed block of LLSD
+ //decompress block
+ LLSD mdl;
+ if (!unzip_llsd(mdl, is, size))
+ {
+ llwarns << "not a valid mesh asset!" << llendl;
+ return false;
+ }
+
+ {
+ U32 face_count = mdl.size();
+
+ if (face_count == 0)
+ {
+ llerrs << "WTF?" << llendl;
+ }
+
+ mVolumeFaces.resize(face_count);
+
+ for (U32 i = 0; i < face_count; ++i)
+ {
+ LLSD::Binary pos = mdl[i]["Position"];
+ LLSD::Binary norm = mdl[i]["Normal"];
+ LLSD::Binary tc = mdl[i]["TexCoord0"];
+ LLSD::Binary idx = mdl[i]["TriangleList"];
+
+ LLVolumeFace& face = mVolumeFaces[i];
+
+ //copy out indices
+ face.resizeIndices(idx.size()/2);
+
+ if (idx.empty() || face.mNumIndices < 3)
+ { //why is there an empty index list?
+ llerrs <<"WTF?" << llendl;
+ continue;
+ }
+
+ U16* indices = (U16*) &(idx[0]);
+ for (U32 j = 0; j < idx.size()/2; ++j)
+ {
+ face.mIndices[j] = indices[j];
+ }
+
+ //copy out vertices
+ U32 num_verts = pos.size()/(3*2);
+ face.resizeVertices(num_verts);
+
+ if (mdl[i].has("Weights"))
+ {
+ face.allocateWeights(num_verts);
+
+ LLSD::Binary weights = mdl[i]["Weights"];
+
+ U32 idx = 0;
+
+ U32 cur_vertex = 0;
+ while (idx < weights.size() && cur_vertex < num_verts)
+ {
+ const U8 END_INFLUENCES = 0xFF;
+ U8 joint = weights[idx++];
+
+ U32 cur_influence = 0;
+ LLVector4 wght(0,0,0,0);
+
+ while (joint != END_INFLUENCES && idx < weights.size())
+ {
+ U16 influence = weights[idx++];
+ influence |= ((U16) weights[idx++] << 8);
+
+ F32 w = llclamp((F32) influence / 65535.f, 0.f, 0.99999f);
+ wght.mV[cur_influence++] = (F32) joint + w;
+
+ if (cur_influence >= 4)
+ {
+ joint = END_INFLUENCES;
+ }
+ else
+ {
+ joint = weights[idx++];
+ }
+ }
+
+ face.mWeights[cur_vertex].loadua(wght.mV);
+
+ cur_vertex++;
+ }
+
+ if (cur_vertex != num_verts || idx != weights.size())
+ {
+ llwarns << "Vertex weight count does not match vertex count!" << llendl;
+ }
+
+ }
+
+ LLVector3 minp;
+ LLVector3 maxp;
+ LLVector2 min_tc;
+ LLVector2 max_tc;
+
+ minp.setValue(mdl[i]["PositionDomain"]["Min"]);
+ maxp.setValue(mdl[i]["PositionDomain"]["Max"]);
+ LLVector4a min_pos, max_pos;
+ min_pos.load3(minp.mV);
+ max_pos.load3(maxp.mV);
+
+ min_tc.setValue(mdl[i]["TexCoord0Domain"]["Min"]);
+ max_tc.setValue(mdl[i]["TexCoord0Domain"]["Max"]);
+
+ LLVector4a pos_range;
+ pos_range.setSub(max_pos, min_pos);
+ LLVector2 tc_range = max_tc - min_tc;
+
+ LLVector4a* pos_out = face.mPositions;
+ LLVector4a* norm_out = face.mNormals;
+ LLVector2* tc_out = face.mTexCoords;
+
+ for (U32 j = 0; j < num_verts; ++j)
+ {
+ U16* v = (U16*) &(pos[j*3*2]);
+
+ pos_out->set((F32) v[0], (F32) v[1], (F32) v[2]);
+ pos_out->div(65535.f);
+ pos_out->mul(pos_range);
+ pos_out->add(min_pos);
+
+ pos_out++;
+
+ U16* n = (U16*) &(norm[j*3*2]);
+
+ norm_out->set((F32) n[0], (F32) n[1], (F32) n[2]);
+ norm_out->div(65535.f);
+ norm_out->mul(2.f);
+ norm_out->sub(1.f);
+ norm_out++;
+
+ U16* t = (U16*) &(tc[j*2*2]);
+
+ tc_out->mV[0] = (F32) t[0] / 65535.f * tc_range.mV[0] + min_tc.mV[0];
+ tc_out->mV[1] = (F32) t[1] / 65535.f * tc_range.mV[1] + min_tc.mV[1];
+
+ tc_out++;
+ }
+
+
+ // modifier flags?
+ bool do_mirror = (mParams.getSculptType() & LL_SCULPT_FLAG_MIRROR);
+ bool do_invert = (mParams.getSculptType() &LL_SCULPT_FLAG_INVERT);
+
+
+ // translate to actions:
+ bool do_reflect_x = false;
+ bool do_reverse_triangles = false;
+ bool do_invert_normals = false;
+
+ if (do_mirror)
+ {
+ do_reflect_x = true;
+ do_reverse_triangles = !do_reverse_triangles;
+ }
+
+ if (do_invert)
+ {
+ do_invert_normals = true;
+ do_reverse_triangles = !do_reverse_triangles;
+ }
+
+ // now do the work
+
+ if (do_reflect_x)
+ {
+ LLVector4a* p = (LLVector4a*) face.mPositions;
+ LLVector4a* n = (LLVector4a*) face.mNormals;
+
+ for (S32 i = 0; i < face.mNumVertices; i++)
+ {
+ p[i].mul(-1.0f);
+ n[i].mul(-1.0f);
+ }
+ }
+
+ if (do_invert_normals)
+ {
+ LLVector4a* n = (LLVector4a*) face.mNormals;
+
+ for (S32 i = 0; i < face.mNumVertices; i++)
+ {
+ n[i].mul(-1.0f);
+ }
+ }
+
+ if (do_reverse_triangles)
+ {
+ for (U32 j = 0; j < face.mNumIndices; j += 3)
+ {
+ // swap the 2nd and 3rd index
+ S32 swap = face.mIndices[j+1];
+ face.mIndices[j+1] = face.mIndices[j+2];
+ face.mIndices[j+2] = swap;
+ }
+ }
+
+ //calculate bounding box
+ LLVector4a& min = face.mExtents[0];
+ LLVector4a& max = face.mExtents[1];
+
+ min.clear();
+ max.clear();
+ min = max = face.mPositions[0];
+
+ for (S32 i = 1; i < face.mNumVertices; ++i)
+ {
+ min.setMin(min, face.mPositions[i]);
+ max.setMax(max, face.mPositions[i]);
+ }
+ }
+ }
+
+ mSculptLevel = 0; // success!
+
+ cacheOptimize();
+
+ return true;
+}
+
+void tetrahedron_set_normal(LLVolumeFace::VertexData* cv)
+{
+ LLVector4a v0;
+ v0.setSub(cv[1].getPosition(), cv[0].getNormal());
+ LLVector4a v1;
+ v1.setSub(cv[2].getNormal(), cv[0].getPosition());
+
+ cv[0].getNormal().setCross3(v0,v1);
+ cv[0].getNormal().normalize3fast();
+ cv[1].setNormal(cv[0].getNormal());
+ cv[2].setNormal(cv[1].getNormal());
+}
+
+BOOL LLVolume::isTetrahedron()
+{
+ return mIsTetrahedron;
+}
+
+void LLVolume::makeTetrahedron()
+{
+ mVolumeFaces.clear();
+
+ LLVolumeFace face;
+
+ F32 x = 0.25f;
+ LLVector4a p[] =
+ { //unit tetrahedron corners
+ LLVector4a(x,x,x),
+ LLVector4a(-x,-x,x),
+ LLVector4a(-x,x,-x),
+ LLVector4a(x,-x,-x)
+ };
+
+ face.mExtents[0].splat(-x);
+ face.mExtents[1].splat(x);
+
+ LLVolumeFace::VertexData cv[3];
+
+ //set texture coordinates
+ cv[0].mTexCoord = LLVector2(0,0);
+ cv[1].mTexCoord = LLVector2(1,0);
+ cv[2].mTexCoord = LLVector2(0.5f, 0.5f*F_SQRT3);
+
+
+ //side 1
+ cv[0].setPosition(p[1]);
+ cv[1].setPosition(p[0]);
+ cv[2].setPosition(p[2]);
+
+ tetrahedron_set_normal(cv);
+
+ face.resizeVertices(12);
+ face.resizeIndices(12);
+
+ LLVector4a* v = (LLVector4a*) face.mPositions;
+ LLVector4a* n = (LLVector4a*) face.mNormals;
+ LLVector2* tc = (LLVector2*) face.mTexCoords;
+
+ v[0] = cv[0].getPosition();
+ v[1] = cv[1].getPosition();
+ v[2] = cv[2].getPosition();
+ v += 3;
+
+ n[0] = cv[0].getNormal();
+ n[1] = cv[1].getNormal();
+ n[2] = cv[2].getNormal();
+ n += 3;
+
+ tc[0] = cv[0].mTexCoord;
+ tc[1] = cv[1].mTexCoord;
+ tc[2] = cv[2].mTexCoord;
+ tc += 3;
+
+
+ //side 2
+ cv[0].setPosition(p[3]);
+ cv[1].setPosition(p[0]);
+ cv[2].setPosition(p[1]);
+
+ tetrahedron_set_normal(cv);
+
+ v[0] = cv[0].getPosition();
+ v[1] = cv[1].getPosition();
+ v[2] = cv[2].getPosition();
+ v += 3;
+
+ n[0] = cv[0].getNormal();
+ n[1] = cv[1].getNormal();
+ n[2] = cv[2].getNormal();
+ n += 3;
+
+ tc[0] = cv[0].mTexCoord;
+ tc[1] = cv[1].mTexCoord;
+ tc[2] = cv[2].mTexCoord;
+ tc += 3;
+
+ //side 3
+ cv[0].setPosition(p[3]);
+ cv[1].setPosition(p[1]);
+ cv[2].setPosition(p[2]);
+
+ tetrahedron_set_normal(cv);
+
+ v[0] = cv[0].getPosition();
+ v[1] = cv[1].getPosition();
+ v[2] = cv[2].getPosition();
+ v += 3;
+
+ n[0] = cv[0].getNormal();
+ n[1] = cv[1].getNormal();
+ n[2] = cv[2].getNormal();
+ n += 3;
+
+ tc[0] = cv[0].mTexCoord;
+ tc[1] = cv[1].mTexCoord;
+ tc[2] = cv[2].mTexCoord;
+ tc += 3;
+
+ //side 4
+ cv[0].setPosition(p[2]);
+ cv[1].setPosition(p[0]);
+ cv[2].setPosition(p[3]);
+
+ tetrahedron_set_normal(cv);
+
+ v[0] = cv[0].getPosition();
+ v[1] = cv[1].getPosition();
+ v[2] = cv[2].getPosition();
+ v += 3;
+
+ n[0] = cv[0].getNormal();
+ n[1] = cv[1].getNormal();
+ n[2] = cv[2].getNormal();
+ n += 3;
+
+ tc[0] = cv[0].mTexCoord;
+ tc[1] = cv[1].mTexCoord;
+ tc[2] = cv[2].mTexCoord;
+ tc += 3;
+
+ //set index buffer
+ for (U16 i = 0; i < 12; i++)
+ {
+ face.mIndices[i] = i;
+ }
+
+ mVolumeFaces.push_back(face);
+ mSculptLevel = 0;
+ mIsTetrahedron = TRUE;
+}
+
+void LLVolume::copyVolumeFaces(const LLVolume* volume)
+{
+ mVolumeFaces = volume->mVolumeFaces;
+ mSculptLevel = 0;
+ mIsTetrahedron = FALSE;
+}
+
+void LLVolume::cacheOptimize()
+{
+ for (S32 i = 0; i < mVolumeFaces.size(); ++i)
+ {
+ mVolumeFaces[i].cacheOptimize();
+ }
+}
+
+
+S32 LLVolume::getNumFaces() const
+{
+ U8 sculpt_type = (mParams.getSculptType() & LL_SCULPT_TYPE_MASK);
+
+ if (sculpt_type == LL_SCULPT_TYPE_MESH)
+ {
+ return LL_SCULPT_MESH_MAX_FACES;
+ }
+
+ return (S32)mProfilep->mFaces.size();
+}
+
+
+void LLVolume::createVolumeFaces()
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ if (mGenerateSingleFace)
+ {
+ // do nothing
+ }
+ else
+ {
+ S32 num_faces = getNumFaces();
+ BOOL partial_build = TRUE;
+ if (num_faces != mVolumeFaces.size())
+ {
+ partial_build = FALSE;
+ mVolumeFaces.resize(num_faces);
+ }
+ // Initialize volume faces with parameter data
+ for (S32 i = 0; i < (S32)mVolumeFaces.size(); i++)
+ {
+ LLVolumeFace& vf = mVolumeFaces[i];
+ LLProfile::Face& face = mProfilep->mFaces[i];
+ vf.mBeginS = face.mIndex;
+ vf.mNumS = face.mCount;
+ if (vf.mNumS < 0)
+ {
+ llerrs << "Volume face corruption detected." << llendl;
+ }
+
+ vf.mBeginT = 0;
+ vf.mNumT= getPath().mPath.size();
+ vf.mID = i;
+
+ // Set the type mask bits correctly
+ if (mParams.getProfileParams().getHollow() > 0)
+ {
+ vf.mTypeMask |= LLVolumeFace::HOLLOW_MASK;
+ }
+ if (mProfilep->isOpen())
+ {
+ vf.mTypeMask |= LLVolumeFace::OPEN_MASK;
+ }
+ if (face.mCap)
+ {
+ vf.mTypeMask |= LLVolumeFace::CAP_MASK;
+ if (face.mFaceID == LL_FACE_PATH_BEGIN)
+ {
+ vf.mTypeMask |= LLVolumeFace::TOP_MASK;
+ }
+ else
+ {
+ llassert(face.mFaceID == LL_FACE_PATH_END);
+ vf.mTypeMask |= LLVolumeFace::BOTTOM_MASK;
+ }
+ }
+ else if (face.mFaceID & (LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END))
+ {
+ vf.mTypeMask |= LLVolumeFace::FLAT_MASK | LLVolumeFace::END_MASK;
+ }
+ else
+ {
+ vf.mTypeMask |= LLVolumeFace::SIDE_MASK;
+ if (face.mFlat)
+ {
+ vf.mTypeMask |= LLVolumeFace::FLAT_MASK;
+ }
+ if (face.mFaceID & LL_FACE_INNER_SIDE)
+ {
+ vf.mTypeMask |= LLVolumeFace::INNER_MASK;
+ if (face.mFlat && vf.mNumS > 2)
+ { //flat inner faces have to copy vert normals
+ vf.mNumS = vf.mNumS*2;
+ if (vf.mNumS < 0)
+ {
+ llerrs << "Volume face corruption detected." << llendl;
+ }
+ }
+ }
+ else
+ {
+ vf.mTypeMask |= LLVolumeFace::OUTER_MASK;
+ }
+ }
+ }
+
+ for (face_list_t::iterator iter = mVolumeFaces.begin();
+ iter != mVolumeFaces.end(); ++iter)
+ {
+ (*iter).create(this, partial_build);
+ }
+ }
+}
+
+
+inline LLVector3 sculpt_rgb_to_vector(U8 r, U8 g, U8 b)
+{
+ // maps RGB values to vector values [0..255] -> [-0.5..0.5]
+ LLVector3 value;
+ value.mV[VX] = r / 255.f - 0.5f;
+ value.mV[VY] = g / 255.f - 0.5f;
+ value.mV[VZ] = b / 255.f - 0.5f;
+
+ return value;
+}
+
+inline U32 sculpt_xy_to_index(U32 x, U32 y, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components)
+{
+ U32 index = (x + y * sculpt_width) * sculpt_components;
+ return index;
+}
+
+
+inline U32 sculpt_st_to_index(S32 s, S32 t, S32 size_s, S32 size_t, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components)
+{
+ U32 x = (U32) ((F32)s/(size_s) * (F32) sculpt_width);
+ U32 y = (U32) ((F32)t/(size_t) * (F32) sculpt_height);
+
+ return sculpt_xy_to_index(x, y, sculpt_width, sculpt_height, sculpt_components);
+}
+
+
+inline LLVector3 sculpt_index_to_vector(U32 index, const U8* sculpt_data)
+{
+ LLVector3 v = sculpt_rgb_to_vector(sculpt_data[index], sculpt_data[index+1], sculpt_data[index+2]);
+
+ return v;
+}
+
+inline LLVector3 sculpt_st_to_vector(S32 s, S32 t, S32 size_s, S32 size_t, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data)
+{
+ U32 index = sculpt_st_to_index(s, t, size_s, size_t, sculpt_width, sculpt_height, sculpt_components);
+
+ return sculpt_index_to_vector(index, sculpt_data);
+}
+
+inline LLVector3 sculpt_xy_to_vector(U32 x, U32 y, U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data)
+{
+ U32 index = sculpt_xy_to_index(x, y, sculpt_width, sculpt_height, sculpt_components);
+
+ return sculpt_index_to_vector(index, sculpt_data);
+}
+
+
+F32 LLVolume::sculptGetSurfaceArea()
+{
+ // test to see if image has enough variation to create non-degenerate geometry
+
+ F32 area = 0;
+
+ S32 sizeS = mPathp->mPath.size();
+ S32 sizeT = mProfilep->mProfile.size();
+
+ for (S32 s = 0; s < sizeS-1; s++)
+ {
+ for (S32 t = 0; t < sizeT-1; t++)
+ {
+ // get four corners of quad
+ LLVector3 p1 = mMesh[(s )*sizeT + (t )].mPos;
+ LLVector3 p2 = mMesh[(s+1)*sizeT + (t )].mPos;
+ LLVector3 p3 = mMesh[(s )*sizeT + (t+1)].mPos;
+ LLVector3 p4 = mMesh[(s+1)*sizeT + (t+1)].mPos;
+
+ // compute the area of the quad by taking the length of the cross product of the two triangles
+ LLVector3 cross1 = (p1 - p2) % (p1 - p3);
+ LLVector3 cross2 = (p4 - p2) % (p4 - p3);
+ area += (cross1.magVec() + cross2.magVec()) / 2.0;
+ }
+ }
+
+ return area;
+}
+
+// create placeholder shape
+void LLVolume::sculptGeneratePlaceholder()
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ S32 sizeS = mPathp->mPath.size();
+ S32 sizeT = mProfilep->mProfile.size();
+
+ S32 line = 0;
+
+ // for now, this is a sphere.
+ for (S32 s = 0; s < sizeS; s++)
+ {
+ for (S32 t = 0; t < sizeT; t++)
+ {
+ S32 i = t + line;
+ Point& pt = mMesh[i];
+
+
+ F32 u = (F32)s/(sizeS-1);
+ F32 v = (F32)t/(sizeT-1);
+
+ const F32 RADIUS = (F32) 0.3;
+
+ pt.mPos.mV[0] = (F32)(sin(F_PI * v) * cos(2.0 * F_PI * u) * RADIUS);
+ pt.mPos.mV[1] = (F32)(sin(F_PI * v) * sin(2.0 * F_PI * u) * RADIUS);
+ pt.mPos.mV[2] = (F32)(cos(F_PI * v) * RADIUS);
+
+ }
+ line += sizeT;
+ }
+}
+
+// create the vertices from the map
+void LLVolume::sculptGenerateMapVertices(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, U8 sculpt_type)
+{
+ U8 sculpt_stitching = sculpt_type & LL_SCULPT_TYPE_MASK;
+ BOOL sculpt_invert = sculpt_type & LL_SCULPT_FLAG_INVERT;
+ BOOL sculpt_mirror = sculpt_type & LL_SCULPT_FLAG_MIRROR;
+ BOOL reverse_horizontal = (sculpt_invert ? !sculpt_mirror : sculpt_mirror); // XOR
+
+
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ S32 sizeS = mPathp->mPath.size();
+ S32 sizeT = mProfilep->mProfile.size();
+
+ S32 line = 0;
+ for (S32 s = 0; s < sizeS; s++)
+ {
+ // Run along the profile.
+ for (S32 t = 0; t < sizeT; t++)
+ {
+ S32 i = t + line;
+ Point& pt = mMesh[i];
+
+ S32 reversed_t = t;
+
+ if (reverse_horizontal)
+ {
+ reversed_t = sizeT - t - 1;
+ }
+
+ U32 x = (U32) ((F32)reversed_t/(sizeT-1) * (F32) sculpt_width);
+ U32 y = (U32) ((F32)s/(sizeS-1) * (F32) sculpt_height);
+
+
+ if (y == 0) // top row stitching
+ {
+ // pinch?
+ if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE)
+ {
+ x = sculpt_width / 2;
+ }
+ }
+
+ if (y == sculpt_height) // bottom row stitching
+ {
+ // wrap?
+ if (sculpt_stitching == LL_SCULPT_TYPE_TORUS)
+ {
+ y = 0;
+ }
+ else
+ {
+ y = sculpt_height - 1;
+ }
+
+ // pinch?
+ if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE)
+ {
+ x = sculpt_width / 2;
+ }
+ }
+
+ if (x == sculpt_width) // side stitching
+ {
+ // wrap?
+ if ((sculpt_stitching == LL_SCULPT_TYPE_SPHERE) ||
+ (sculpt_stitching == LL_SCULPT_TYPE_TORUS) ||
+ (sculpt_stitching == LL_SCULPT_TYPE_CYLINDER))
+ {
+ x = 0;
+ }
+
+ else
+ {
+ x = sculpt_width - 1;
+ }
+ }
+
+ pt.mPos = sculpt_xy_to_vector(x, y, sculpt_width, sculpt_height, sculpt_components, sculpt_data);
+
+ if (sculpt_mirror)
+ {
+ pt.mPos.mV[VX] *= -1.f;
+ }
+ }
+
+ line += sizeT;
+ }
+}
+
+
+const S32 SCULPT_REZ_1 = 6; // changed from 4 to 6 - 6 looks round whereas 4 looks square
+const S32 SCULPT_REZ_2 = 8;
+const S32 SCULPT_REZ_3 = 16;
+const S32 SCULPT_REZ_4 = 32;
+
+S32 sculpt_sides(F32 detail)
+{
+
+ // detail is usually one of: 1, 1.5, 2.5, 4.0.
+
+ if (detail <= 1.0)
+ {
+ return SCULPT_REZ_1;
+ }
+ if (detail <= 2.0)
+ {
+ return SCULPT_REZ_2;
+ }
+ if (detail <= 3.0)
+ {
+ return SCULPT_REZ_3;
+ }
+ else
+ {
+ return SCULPT_REZ_4;
+ }
+}
+
+
+
+// determine the number of vertices in both s and t direction for this sculpt
+void sculpt_calc_mesh_resolution(U16 width, U16 height, U8 type, F32 detail, S32& s, S32& t)
+{
+ // this code has the following properties:
+ // 1) the aspect ratio of the mesh is as close as possible to the ratio of the map
+ // while still using all available verts
+ // 2) the mesh cannot have more verts than is allowed by LOD
+ // 3) the mesh cannot have more verts than is allowed by the map
+
+ S32 max_vertices_lod = (S32)pow((double)sculpt_sides(detail), 2.0);
+ S32 max_vertices_map = width * height / 4;
+
+ S32 vertices;
+ if (max_vertices_map > 0)
+ vertices = llmin(max_vertices_lod, max_vertices_map);
+ else
+ vertices = max_vertices_lod;
+
+
+ F32 ratio;
+ if ((width == 0) || (height == 0))
+ ratio = 1.f;
+ else
+ ratio = (F32) width / (F32) height;
+
+
+ s = (S32)(F32) sqrt(((F32)vertices / ratio));
+
+ s = llmax(s, 4); // no degenerate sizes, please
+ t = vertices / s;
+
+ t = llmax(t, 4); // no degenerate sizes, please
+ s = vertices / t;
+}
+
+// sculpt replaces generate() for sculpted surfaces
+void LLVolume::sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, S32 sculpt_level)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+ U8 sculpt_type = mParams.getSculptType();
+
+ BOOL data_is_empty = FALSE;
+
+ if (sculpt_width == 0 || sculpt_height == 0 || sculpt_components < 3 || sculpt_data == NULL)
+ {
+ sculpt_level = -1;
+ data_is_empty = TRUE;
+ }
+
+ S32 requested_sizeS = 0;
+ S32 requested_sizeT = 0;
+
+ sculpt_calc_mesh_resolution(sculpt_width, sculpt_height, sculpt_type, mDetail, requested_sizeS, requested_sizeT);
+
+ mPathp->generate(mParams.getPathParams(), mDetail, 0, TRUE, requested_sizeS);
+ mProfilep->generate(mParams.getProfileParams(), mPathp->isOpen(), mDetail, 0, TRUE, requested_sizeT);
+
+ S32 sizeS = mPathp->mPath.size(); // we requested a specific size, now see what we really got
+ S32 sizeT = mProfilep->mProfile.size(); // we requested a specific size, now see what we really got
+
+ // weird crash bug - DEV-11158 - trying to collect more data:
+ if ((sizeS == 0) || (sizeT == 0))
+ {
+ llwarns << "sculpt bad mesh size " << sizeS << " " << sizeT << llendl;
+ }
+
+ sNumMeshPoints -= mMesh.size();
+ mMesh.resize(sizeS * sizeT);
+ sNumMeshPoints += mMesh.size();
+
+ //generate vertex positions
+ if (!data_is_empty)
+ {
+ sculptGenerateMapVertices(sculpt_width, sculpt_height, sculpt_components, sculpt_data, sculpt_type);
+
+ // don't test lowest LOD to support legacy content DEV-33670
+ if (mDetail > SCULPT_MIN_AREA_DETAIL)
+ {
+ if (sculptGetSurfaceArea() < SCULPT_MIN_AREA)
+ {
+ data_is_empty = TRUE;
+ }
+ }
+ }
+
+ if (data_is_empty)
+ {
+ sculptGeneratePlaceholder();
+ }
+
+
+
+ for (S32 i = 0; i < (S32)mProfilep->mFaces.size(); i++)
+ {
+ mFaceMask |= mProfilep->mFaces[i].mFaceID;
+ }
+
+ mSculptLevel = sculpt_level;
+
+ // Delete any existing faces so that they get regenerated
+ mVolumeFaces.clear();
+
+ createVolumeFaces();
+}
+
+
+
+
+BOOL LLVolume::isCap(S32 face)
+{
+ return mProfilep->mFaces[face].mCap;
+}
+
+BOOL LLVolume::isFlat(S32 face)
+{
+ return mProfilep->mFaces[face].mFlat;
+}
+
+
+bool LLVolumeParams::isSculpt() const
+{
+ return mSculptID.notNull();
+}
+
+bool LLVolumeParams::isMeshSculpt() const
+{
+ return isSculpt() && ((mSculptType & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH);
+}
+
+bool LLVolumeParams::operator==(const LLVolumeParams &params) const
+{
+ return ( (getPathParams() == params.getPathParams()) &&
+ (getProfileParams() == params.getProfileParams()) &&
+ (mSculptID == params.mSculptID) &&
+ (mSculptType == params.mSculptType) );
+}
+
+bool LLVolumeParams::operator!=(const LLVolumeParams &params) const
+{
+ return ( (getPathParams() != params.getPathParams()) ||
+ (getProfileParams() != params.getProfileParams()) ||
+ (mSculptID != params.mSculptID) ||
+ (mSculptType != params.mSculptType) );
+}
+
+bool LLVolumeParams::operator<(const LLVolumeParams &params) const
+{
+ if( getPathParams() != params.getPathParams() )
+ {
+ return getPathParams() < params.getPathParams();
+ }
+
+ if (getProfileParams() != params.getProfileParams())
+ {
+ return getProfileParams() < params.getProfileParams();
+ }
+
+ if (mSculptID != params.mSculptID)
+ {
+ return mSculptID < params.mSculptID;
+ }
+
+ return mSculptType < params.mSculptType;
+
+
+}
+
+void LLVolumeParams::copyParams(const LLVolumeParams &params)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+ mProfileParams.copyParams(params.mProfileParams);
+ mPathParams.copyParams(params.mPathParams);
+ mSculptID = params.getSculptID();
+ mSculptType = params.getSculptType();
+}
+
+// Less restricitve approx 0 for volumes
+const F32 APPROXIMATELY_ZERO = 0.001f;
+bool approx_zero( F32 f, F32 tolerance = APPROXIMATELY_ZERO)
+{
+ return (f >= -tolerance) && (f <= tolerance);
+}
+
+// return true if in range (or nearly so)
+static bool limit_range(F32& v, F32 min, F32 max, F32 tolerance = APPROXIMATELY_ZERO)
+{
+ F32 min_delta = v - min;
+ if (min_delta < 0.f)
+ {
+ v = min;
+ if (!approx_zero(min_delta, tolerance))
+ return false;
+ }
+ F32 max_delta = max - v;
+ if (max_delta < 0.f)
+ {
+ v = max;
+ if (!approx_zero(max_delta, tolerance))
+ return false;
+ }
+ return true;
+}
+
+bool LLVolumeParams::setBeginAndEndS(const F32 b, const F32 e)
+{
+ bool valid = true;
+
+ // First, clamp to valid ranges.
+ F32 begin = b;
+ valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA);
+
+ F32 end = e;
+ if (end >= .0149f && end < MIN_CUT_DELTA) end = MIN_CUT_DELTA; // eliminate warning for common rounding error
+ valid &= limit_range(end, MIN_CUT_DELTA, 1.f);
+
+ valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA, .01f);
+
+ // Now set them.
+ mProfileParams.setBegin(begin);
+ mProfileParams.setEnd(end);
+
+ return valid;
+}
+
+bool LLVolumeParams::setBeginAndEndT(const F32 b, const F32 e)
+{
+ bool valid = true;
+
+ // First, clamp to valid ranges.
+ F32 begin = b;
+ valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA);
+
+ F32 end = e;
+ valid &= limit_range(end, MIN_CUT_DELTA, 1.f);
+
+ valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA, .01f);
+
+ // Now set them.
+ mPathParams.setBegin(begin);
+ mPathParams.setEnd(end);
+
+ return valid;
+}
+
+bool LLVolumeParams::setHollow(const F32 h)
+{
+ // Validate the hollow based on path and profile.
+ U8 profile = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
+ U8 hole_type = mProfileParams.getCurveType() & LL_PCODE_HOLE_MASK;
+
+ F32 max_hollow = HOLLOW_MAX;
+
+ // Only square holes have trouble.
+ if (LL_PCODE_HOLE_SQUARE == hole_type)
+ {
+ switch(profile)
+ {
+ case LL_PCODE_PROFILE_CIRCLE:
+ case LL_PCODE_PROFILE_CIRCLE_HALF:
+ case LL_PCODE_PROFILE_EQUALTRI:
+ max_hollow = HOLLOW_MAX_SQUARE;
+ }
+ }
+
+ F32 hollow = h;
+ bool valid = limit_range(hollow, HOLLOW_MIN, max_hollow);
+ mProfileParams.setHollow(hollow);
+
+ return valid;
+}
+
+bool LLVolumeParams::setTwistBegin(const F32 b)
+{
+ F32 twist_begin = b;
+ bool valid = limit_range(twist_begin, TWIST_MIN, TWIST_MAX);
+ mPathParams.setTwistBegin(twist_begin);
+ return valid;
+}
+
+bool LLVolumeParams::setTwistEnd(const F32 e)
+{
+ F32 twist_end = e;
+ bool valid = limit_range(twist_end, TWIST_MIN, TWIST_MAX);
+ mPathParams.setTwistEnd(twist_end);
+ return valid;
+}
+
+bool LLVolumeParams::setRatio(const F32 x, const F32 y)
+{
+ F32 min_x = RATIO_MIN;
+ F32 max_x = RATIO_MAX;
+ F32 min_y = RATIO_MIN;
+ F32 max_y = RATIO_MAX;
+ // If this is a circular path (and not a sphere) then 'ratio' is actually hole size.
+ U8 path_type = mPathParams.getCurveType();
+ U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
+ if ( LL_PCODE_PATH_CIRCLE == path_type &&
+ LL_PCODE_PROFILE_CIRCLE_HALF != profile_type)
+ {
+ // Holes are more restricted...
+ min_x = HOLE_X_MIN;
+ max_x = HOLE_X_MAX;
+ min_y = HOLE_Y_MIN;
+ max_y = HOLE_Y_MAX;
+ }
+
+ F32 ratio_x = x;
+ bool valid = limit_range(ratio_x, min_x, max_x);
+ F32 ratio_y = y;
+ valid &= limit_range(ratio_y, min_y, max_y);
+
+ mPathParams.setScale(ratio_x, ratio_y);
+
+ return valid;
+}
+
+bool LLVolumeParams::setShear(const F32 x, const F32 y)
+{
+ F32 shear_x = x;
+ bool valid = limit_range(shear_x, SHEAR_MIN, SHEAR_MAX);
+ F32 shear_y = y;
+ valid &= limit_range(shear_y, SHEAR_MIN, SHEAR_MAX);
+ mPathParams.setShear(shear_x, shear_y);
+ return valid;
+}
+
+bool LLVolumeParams::setTaperX(const F32 v)
+{
+ F32 taper = v;
+ bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX);
+ mPathParams.setTaperX(taper);
+ return valid;
+}
+
+bool LLVolumeParams::setTaperY(const F32 v)
+{
+ F32 taper = v;
+ bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX);
+ mPathParams.setTaperY(taper);
+ return valid;
+}
+
+bool LLVolumeParams::setRevolutions(const F32 r)
+{
+ F32 revolutions = r;
+ bool valid = limit_range(revolutions, REV_MIN, REV_MAX);
+ mPathParams.setRevolutions(revolutions);
+ return valid;
+}
+
+bool LLVolumeParams::setRadiusOffset(const F32 offset)
+{
+ bool valid = true;
+
+ // If this is a sphere, just set it to 0 and get out.
+ U8 path_type = mPathParams.getCurveType();
+ U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
+ if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type ||
+ LL_PCODE_PATH_CIRCLE != path_type )
+ {
+ mPathParams.setRadiusOffset(0.f);
+ return true;
+ }
+
+ // Limit radius offset, based on taper and hole size y.
+ F32 radius_offset = offset;
+ F32 taper_y = getTaperY();
+ F32 radius_mag = fabs(radius_offset);
+ F32 hole_y_mag = fabs(getRatioY());
+ F32 taper_y_mag = fabs(taper_y);
+ // Check to see if the taper effects us.
+ if ( (radius_offset > 0.f && taper_y < 0.f) ||
+ (radius_offset < 0.f && taper_y > 0.f) )
+ {
+ // The taper does not help increase the radius offset range.
+ taper_y_mag = 0.f;
+ }
+ F32 max_radius_mag = 1.f - hole_y_mag * (1.f - taper_y_mag) / (1.f - hole_y_mag);
+
+ // Enforce the maximum magnitude.
+ F32 delta = max_radius_mag - radius_mag;
+ if (delta < 0.f)
+ {
+ // Check radius offset sign.
+ if (radius_offset < 0.f)
+ {
+ radius_offset = -max_radius_mag;
+ }
+ else
+ {
+ radius_offset = max_radius_mag;
+ }
+ valid = approx_zero(delta, .1f);
+ }
+
+ mPathParams.setRadiusOffset(radius_offset);
+ return valid;
+}
+
+bool LLVolumeParams::setSkew(const F32 skew_value)
+{
+ bool valid = true;
+
+ // Check the skew value against the revolutions.
+ F32 skew = llclamp(skew_value, SKEW_MIN, SKEW_MAX);
+ F32 skew_mag = fabs(skew);
+ F32 revolutions = getRevolutions();
+ F32 scale_x = getRatioX();
+ F32 min_skew_mag = 1.0f - 1.0f / (revolutions * scale_x + 1.0f);
+ // Discontinuity; A revolution of 1 allows skews below 0.5.
+ if ( fabs(revolutions - 1.0f) < 0.001)
+ min_skew_mag = 0.0f;
+
+ // Clip skew.
+ F32 delta = skew_mag - min_skew_mag;
+ if (delta < 0.f)
+ {
+ // Check skew sign.
+ if (skew < 0.0f)
+ {
+ skew = -min_skew_mag;
+ }
+ else
+ {
+ skew = min_skew_mag;
+ }
+ valid = approx_zero(delta, .01f);
+ }
+
+ mPathParams.setSkew(skew);
+ return valid;
+}
+
+bool LLVolumeParams::setSculptID(const LLUUID sculpt_id, U8 sculpt_type)
+{
+ mSculptID = sculpt_id;
+ mSculptType = sculpt_type;
+ return true;
+}
+
+bool LLVolumeParams::setType(U8 profile, U8 path)
+{
+ bool result = true;
+ // First, check profile and path for validity.
+ U8 profile_type = profile & LL_PCODE_PROFILE_MASK;
+ U8 hole_type = (profile & LL_PCODE_HOLE_MASK) >> 4;
+ U8 path_type = path >> 4;
+
+ if (profile_type > LL_PCODE_PROFILE_MAX)
+ {
+ // Bad profile. Make it square.
+ profile = LL_PCODE_PROFILE_SQUARE;
+ result = false;
+ llwarns << "LLVolumeParams::setType changing bad profile type (" << profile_type
+ << ") to be LL_PCODE_PROFILE_SQUARE" << llendl;
+ }
+ else if (hole_type > LL_PCODE_HOLE_MAX)
+ {
+ // Bad hole. Make it the same.
+ profile = profile_type;
+ result = false;
+ llwarns << "LLVolumeParams::setType changing bad hole type (" << hole_type
+ << ") to be LL_PCODE_HOLE_SAME" << llendl;
+ }
+
+ if (path_type < LL_PCODE_PATH_MIN ||
+ path_type > LL_PCODE_PATH_MAX)
+ {
+ // Bad path. Make it linear.
+ result = false;
+ llwarns << "LLVolumeParams::setType changing bad path (" << path
+ << ") to be LL_PCODE_PATH_LINE" << llendl;
+ path = LL_PCODE_PATH_LINE;
+ }
+
+ mProfileParams.setCurveType(profile);
+ mPathParams.setCurveType(path);
+ return result;
+}
+
+// static
+bool LLVolumeParams::validate(U8 prof_curve, F32 prof_begin, F32 prof_end, F32 hollow,
+ U8 path_curve, F32 path_begin, F32 path_end,
+ F32 scx, F32 scy, F32 shx, F32 shy,
+ F32 twistend, F32 twistbegin, F32 radiusoffset,
+ F32 tx, F32 ty, F32 revolutions, F32 skew)
+{
+ LLVolumeParams test_params;
+ if (!test_params.setType (prof_curve, path_curve))
+ {
+ return false;
+ }
+ if (!test_params.setBeginAndEndS (prof_begin, prof_end))
+ {
+ return false;
+ }
+ if (!test_params.setBeginAndEndT (path_begin, path_end))
+ {
+ return false;
+ }
+ if (!test_params.setHollow (hollow))
+ {
+ return false;
+ }
+ if (!test_params.setTwistBegin (twistbegin))
+ {
+ return false;
+ }
+ if (!test_params.setTwistEnd (twistend))
+ {
+ return false;
+ }
+ if (!test_params.setRatio (scx, scy))
+ {
+ return false;
+ }
+ if (!test_params.setShear (shx, shy))
+ {
+ return false;
+ }
+ if (!test_params.setTaper (tx, ty))
+ {
+ return false;
+ }
+ if (!test_params.setRevolutions (revolutions))
+ {
+ return false;
+ }
+ if (!test_params.setRadiusOffset (radiusoffset))
+ {
+ return false;
+ }
+ if (!test_params.setSkew (skew))
+ {
+ return false;
+ }
+ return true;
+}
+
+S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ S32 expected_num_triangle_indices = getNumTriangleIndices();
+ if (expected_num_triangle_indices > MAX_VOLUME_TRIANGLE_INDICES)
+ {
+ // we don't allow LLVolumes with this many vertices
+ llwarns << "Couldn't allocate triangle indices" << llendl;
+ num_indices = 0;
+ return NULL;
+ }
+
+ S32* index = new S32[expected_num_triangle_indices];
+ S32 count = 0;
+
+ // Let's do this totally diffently, as we don't care about faces...
+ // Counter-clockwise triangles are forward facing...
+
+ BOOL open = getProfile().isOpen();
+ BOOL hollow = (mParams.getProfileParams().getHollow() > 0);
+ BOOL path_open = getPath().isOpen();
+ S32 size_s, size_s_out, size_t;
+ S32 s, t, i;
+ size_s = getProfile().getTotal();
+ size_s_out = getProfile().getTotalOut();
+ size_t = getPath().mPath.size();
+
+ // NOTE -- if the construction of the triangles below ever changes
+ // then getNumTriangleIndices() method may also have to be updated.
+
+ if (open) /* Flawfinder: ignore */
+ {
+ if (hollow)
+ {
+ // Open hollow -- much like the closed solid, except we
+ // we need to stitch up the gap between s=0 and s=size_s-1
+
+ for (t = 0; t < size_t - 1; t++)
+ {
+ // The outer face, first cut, and inner face
+ for (s = 0; s < size_s - 1; s++)
+ {
+ i = s + t*size_s;
+ index[count++] = i; // x,y
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s; // x,y+1
+
+ index[count++] = i + size_s; // x,y+1
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s + 1; // x+1,y+1
+ }
+
+ // The other cut face
+ index[count++] = s + t*size_s; // x,y
+ index[count++] = 0 + t*size_s; // x+1,y
+ index[count++] = s + (t+1)*size_s; // x,y+1
+
+ index[count++] = s + (t+1)*size_s; // x,y+1
+ index[count++] = 0 + t*size_s; // x+1,y
+ index[count++] = 0 + (t+1)*size_s; // x+1,y+1
+ }
+
+ // Do the top and bottom caps, if necessary
+ if (path_open)
+ {
+ // Top cap
+ S32 pt1 = 0;
+ S32 pt2 = size_s-1;
+ S32 i = (size_t - 1)*size_s;
+
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ LLVector3 p1 = getProfile().mProfile[pt1];
+ LLVector3 p2 = getProfile().mProfile[pt2];
+ LLVector3 pa = getProfile().mProfile[pt1+1];
+ LLVector3 pb = getProfile().mProfile[pt2-1];
+
+ p1.mV[VZ] = 0.f;
+ p2.mV[VZ] = 0.f;
+ pa.mV[VZ] = 0.f;
+ pb.mV[VZ] = 0.f;
+
+ // Use area of triangle to determine backfacing
+ F32 area_1a2, area_1ba, area_21b, area_2ab;
+ area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+ (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+ (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+
+ area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+ (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+
+ area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+ (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+ (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ BOOL use_tri1a2 = TRUE;
+ BOOL tri_1a2 = TRUE;
+ BOOL tri_21b = TRUE;
+
+ if (area_1a2 < 0)
+ {
+ tri_1a2 = FALSE;
+ }
+ if (area_2ab < 0)
+ {
+ // Can't use, because it contains point b
+ tri_1a2 = FALSE;
+ }
+ if (area_21b < 0)
+ {
+ tri_21b = FALSE;
+ }
+ if (area_1ba < 0)
+ {
+ // Can't use, because it contains point b
+ tri_21b = FALSE;
+ }
+
+ if (!tri_1a2)
+ {
+ use_tri1a2 = FALSE;
+ }
+ else if (!tri_21b)
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ LLVector3 d1 = p1 - pa;
+ LLVector3 d2 = p2 - pb;
+
+ if (d1.magVecSquared() < d2.magVecSquared())
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ use_tri1a2 = FALSE;
+ }
+ }
+
+ if (use_tri1a2)
+ {
+ index[count++] = pt1 + i;
+ index[count++] = pt1 + 1 + i;
+ index[count++] = pt2 + i;
+ pt1++;
+ }
+ else
+ {
+ index[count++] = pt1 + i;
+ index[count++] = pt2 - 1 + i;
+ index[count++] = pt2 + i;
+ pt2--;
+ }
+ }
+
+ // Bottom cap
+ pt1 = 0;
+ pt2 = size_s-1;
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ LLVector3 p1 = getProfile().mProfile[pt1];
+ LLVector3 p2 = getProfile().mProfile[pt2];
+ LLVector3 pa = getProfile().mProfile[pt1+1];
+ LLVector3 pb = getProfile().mProfile[pt2-1];
+
+ p1.mV[VZ] = 0.f;
+ p2.mV[VZ] = 0.f;
+ pa.mV[VZ] = 0.f;
+ pb.mV[VZ] = 0.f;
+
+ // Use area of triangle to determine backfacing
+ F32 area_1a2, area_1ba, area_21b, area_2ab;
+ area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+ (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+ (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+
+ area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+ (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+
+ area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+ (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+ (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ BOOL use_tri1a2 = TRUE;
+ BOOL tri_1a2 = TRUE;
+ BOOL tri_21b = TRUE;
+
+ if (area_1a2 < 0)
+ {
+ tri_1a2 = FALSE;
+ }
+ if (area_2ab < 0)
+ {
+ // Can't use, because it contains point b
+ tri_1a2 = FALSE;
+ }
+ if (area_21b < 0)
+ {
+ tri_21b = FALSE;
+ }
+ if (area_1ba < 0)
+ {
+ // Can't use, because it contains point b
+ tri_21b = FALSE;
+ }
+
+ if (!tri_1a2)
+ {
+ use_tri1a2 = FALSE;
+ }
+ else if (!tri_21b)
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ LLVector3 d1 = p1 - pa;
+ LLVector3 d2 = p2 - pb;
+
+ if (d1.magVecSquared() < d2.magVecSquared())
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ use_tri1a2 = FALSE;
+ }
+ }
+
+ if (use_tri1a2)
+ {
+ index[count++] = pt1;
+ index[count++] = pt2;
+ index[count++] = pt1 + 1;
+ pt1++;
+ }
+ else
+ {
+ index[count++] = pt1;
+ index[count++] = pt2;
+ index[count++] = pt2 - 1;
+ pt2--;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Open solid
+
+ for (t = 0; t < size_t - 1; t++)
+ {
+ // Outer face + 1 cut face
+ for (s = 0; s < size_s - 1; s++)
+ {
+ i = s + t*size_s;
+
+ index[count++] = i; // x,y
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s; // x,y+1
+
+ index[count++] = i + size_s; // x,y+1
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s + 1; // x+1,y+1
+ }
+
+ // The other cut face
+ index[count++] = (size_s - 1) + (t*size_s); // x,y
+ index[count++] = 0 + t*size_s; // x+1,y
+ index[count++] = (size_s - 1) + (t+1)*size_s; // x,y+1
+
+ index[count++] = (size_s - 1) + (t+1)*size_s; // x,y+1
+ index[count++] = 0 + (t*size_s); // x+1,y
+ index[count++] = 0 + (t+1)*size_s; // x+1,y+1
+ }
+
+ // Do the top and bottom caps, if necessary
+ if (path_open)
+ {
+ for (s = 0; s < size_s - 2; s++)
+ {
+ index[count++] = s+1;
+ index[count++] = s;
+ index[count++] = size_s - 1;
+ }
+
+ // We've got a top cap
+ S32 offset = (size_t - 1)*size_s;
+ for (s = 0; s < size_s - 2; s++)
+ {
+ // Inverted ordering from bottom cap.
+ index[count++] = offset + size_s - 1;
+ index[count++] = offset + s;
+ index[count++] = offset + s + 1;
+ }
+ }
+ }
+ }
+ else if (hollow)
+ {
+ // Closed hollow
+ // Outer face
+
+ for (t = 0; t < size_t - 1; t++)
+ {
+ for (s = 0; s < size_s_out - 1; s++)
+ {
+ i = s + t*size_s;
+
+ index[count++] = i; // x,y
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s; // x,y+1
+
+ index[count++] = i + size_s; // x,y+1
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + 1 + size_s; // x+1,y+1
+ }
+ }
+
+ // Inner face
+ // Invert facing from outer face
+ for (t = 0; t < size_t - 1; t++)
+ {
+ for (s = size_s_out; s < size_s - 1; s++)
+ {
+ i = s + t*size_s;
+
+ index[count++] = i; // x,y
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s; // x,y+1
+
+ index[count++] = i + size_s; // x,y+1
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + 1 + size_s; // x+1,y+1
+ }
+ }
+
+ // Do the top and bottom caps, if necessary
+ if (path_open)
+ {
+ // Top cap
+ S32 pt1 = 0;
+ S32 pt2 = size_s-1;
+ S32 i = (size_t - 1)*size_s;
+
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ LLVector3 p1 = getProfile().mProfile[pt1];
+ LLVector3 p2 = getProfile().mProfile[pt2];
+ LLVector3 pa = getProfile().mProfile[pt1+1];
+ LLVector3 pb = getProfile().mProfile[pt2-1];
+
+ p1.mV[VZ] = 0.f;
+ p2.mV[VZ] = 0.f;
+ pa.mV[VZ] = 0.f;
+ pb.mV[VZ] = 0.f;
+
+ // Use area of triangle to determine backfacing
+ F32 area_1a2, area_1ba, area_21b, area_2ab;
+ area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+ (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+ (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+
+ area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+ (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+
+ area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+ (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+ (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ BOOL use_tri1a2 = TRUE;
+ BOOL tri_1a2 = TRUE;
+ BOOL tri_21b = TRUE;
+
+ if (area_1a2 < 0)
+ {
+ tri_1a2 = FALSE;
+ }
+ if (area_2ab < 0)
+ {
+ // Can't use, because it contains point b
+ tri_1a2 = FALSE;
+ }
+ if (area_21b < 0)
+ {
+ tri_21b = FALSE;
+ }
+ if (area_1ba < 0)
+ {
+ // Can't use, because it contains point b
+ tri_21b = FALSE;
+ }
+
+ if (!tri_1a2)
+ {
+ use_tri1a2 = FALSE;
+ }
+ else if (!tri_21b)
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ LLVector3 d1 = p1 - pa;
+ LLVector3 d2 = p2 - pb;
+
+ if (d1.magVecSquared() < d2.magVecSquared())
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ use_tri1a2 = FALSE;
+ }
+ }
+
+ if (use_tri1a2)
+ {
+ index[count++] = pt1 + i;
+ index[count++] = pt1 + 1 + i;
+ index[count++] = pt2 + i;
+ pt1++;
+ }
+ else
+ {
+ index[count++] = pt1 + i;
+ index[count++] = pt2 - 1 + i;
+ index[count++] = pt2 + i;
+ pt2--;
+ }
+ }
+
+ // Bottom cap
+ pt1 = 0;
+ pt2 = size_s-1;
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ LLVector3 p1 = getProfile().mProfile[pt1];
+ LLVector3 p2 = getProfile().mProfile[pt2];
+ LLVector3 pa = getProfile().mProfile[pt1+1];
+ LLVector3 pb = getProfile().mProfile[pt2-1];
+
+ p1.mV[VZ] = 0.f;
+ p2.mV[VZ] = 0.f;
+ pa.mV[VZ] = 0.f;
+ pb.mV[VZ] = 0.f;
+
+ // Use area of triangle to determine backfacing
+ F32 area_1a2, area_1ba, area_21b, area_2ab;
+ area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+ (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+ (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+
+ area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+ (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+
+ area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+ (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+ (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ BOOL use_tri1a2 = TRUE;
+ BOOL tri_1a2 = TRUE;
+ BOOL tri_21b = TRUE;
+
+ if (area_1a2 < 0)
+ {
+ tri_1a2 = FALSE;
+ }
+ if (area_2ab < 0)
+ {
+ // Can't use, because it contains point b
+ tri_1a2 = FALSE;
+ }
+ if (area_21b < 0)
+ {
+ tri_21b = FALSE;
+ }
+ if (area_1ba < 0)
+ {
+ // Can't use, because it contains point b
+ tri_21b = FALSE;
+ }
+
+ if (!tri_1a2)
+ {
+ use_tri1a2 = FALSE;
+ }
+ else if (!tri_21b)
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ LLVector3 d1 = p1 - pa;
+ LLVector3 d2 = p2 - pb;
+
+ if (d1.magVecSquared() < d2.magVecSquared())
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ use_tri1a2 = FALSE;
+ }
+ }
+
+ if (use_tri1a2)
+ {
+ index[count++] = pt1;
+ index[count++] = pt2;
+ index[count++] = pt1 + 1;
+ pt1++;
+ }
+ else
+ {
+ index[count++] = pt1;
+ index[count++] = pt2;
+ index[count++] = pt2 - 1;
+ pt2--;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Closed solid. Easy case.
+ for (t = 0; t < size_t - 1; t++)
+ {
+ for (s = 0; s < size_s - 1; s++)
+ {
+ // Should wrap properly, but for now...
+ i = s + t*size_s;
+
+ index[count++] = i; // x,y
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s; // x,y+1
+
+ index[count++] = i + size_s; // x,y+1
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s + 1; // x+1,y+1
+ }
+ }
+
+ // Do the top and bottom caps, if necessary
+ if (path_open)
+ {
+ // bottom cap
+ for (s = 1; s < size_s - 2; s++)
+ {
+ index[count++] = s+1;
+ index[count++] = s;
+ index[count++] = 0;
+ }
+
+ // top cap
+ S32 offset = (size_t - 1)*size_s;
+ for (s = 1; s < size_s - 2; s++)
+ {
+ // Inverted ordering from bottom cap.
+ index[count++] = offset;
+ index[count++] = offset + s;
+ index[count++] = offset + s + 1;
+ }
+ }
+ }
+
+#ifdef LL_DEBUG
+ // assert that we computed the correct number of indices
+ if (count != expected_num_triangle_indices )
+ {
+ llerrs << "bad index count prediciton:"
+ << " expected=" << expected_num_triangle_indices
+ << " actual=" << count << llendl;
+ }
+#endif
+
+#if 0
+ // verify that each index does not point beyond the size of the mesh
+ S32 num_vertices = mMesh.size();
+ for (i = 0; i < count; i+=3)
+ {
+ llinfos << index[i] << ":" << index[i+1] << ":" << index[i+2] << llendl;
+ llassert(index[i] < num_vertices);
+ llassert(index[i+1] < num_vertices);
+ llassert(index[i+2] < num_vertices);
+ }
+#endif
+
+ num_indices = count;
+ return index;
+}
+
+S32 LLVolume::getNumTriangleIndices() const
+{
+ BOOL profile_open = getProfile().isOpen();
+ BOOL hollow = (mParams.getProfileParams().getHollow() > 0);
+ BOOL path_open = getPath().isOpen();
+
+ S32 size_s, size_s_out, size_t;
+ size_s = getProfile().getTotal();
+ size_s_out = getProfile().getTotalOut();
+ size_t = getPath().mPath.size();
+
+ S32 count = 0;
+ if (profile_open) /* Flawfinder: ignore */
+ {
+ if (hollow)
+ {
+ // Open hollow -- much like the closed solid, except we
+ // we need to stitch up the gap between s=0 and s=size_s-1
+ count = (size_t - 1) * (((size_s -1) * 6) + 6);
+ }
+ else
+ {
+ count = (size_t - 1) * (((size_s -1) * 6) + 6);
+ }
+ }
+ else if (hollow)
+ {
+ // Closed hollow
+ // Outer face
+ count = (size_t - 1) * (size_s_out - 1) * 6;
+
+ // Inner face
+ count += (size_t - 1) * ((size_s - 1) - size_s_out) * 6;
+ }
+ else
+ {
+ // Closed solid. Easy case.
+ count = (size_t - 1) * (size_s - 1) * 6;
+ }
+
+ if (path_open)
+ {
+ S32 cap_triangle_count = size_s - 3;
+ if ( profile_open
+ || hollow )
+ {
+ cap_triangle_count = size_s - 2;
+ }
+ if ( cap_triangle_count > 0 )
+ {
+ // top and bottom caps
+ count += cap_triangle_count * 2 * 3;
+ }
+ }
+ return count;
+}
+
+
+S32 LLVolume::getNumTriangles() const
+{
+ U32 triangle_count = 0;
+
+ for (S32 i = 0; i < getNumVolumeFaces(); ++i)
+ {
+ triangle_count += getVolumeFace(i).mNumIndices/3;
+ }
+
+ return triangle_count;
+}
+
+
+//-----------------------------------------------------------------------------
+// generateSilhouetteVertices()
+//-----------------------------------------------------------------------------
+void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
+ std::vector<LLVector3> &normals,
+ const LLVector3& obj_cam_vec_in,
+ const LLMatrix4& mat_in,
+ const LLMatrix3& norm_mat_in,
+ S32 face_mask)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ LLMatrix4a mat;
+ mat.loadu(mat_in);
+
+ LLMatrix4a norm_mat;
+ norm_mat.loadu(norm_mat_in);
+
+ LLVector4a obj_cam_vec;
+ obj_cam_vec.load3(obj_cam_vec_in.mV);
+
+ vertices.clear();
+ normals.clear();
+
+ if ((mParams.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
+ {
+ return;
+ }
+
+ S32 cur_index = 0;
+ //for each face
+ for (face_list_t::iterator iter = mVolumeFaces.begin();
+ iter != mVolumeFaces.end(); ++iter)
+ {
+ LLVolumeFace& face = *iter;
+
+ if (!(face_mask & (0x1 << cur_index++)) ||
+ face.mNumIndices == 0 || face.mEdge.empty())
+ {
+ continue;
+ }
+
+ if (face.mTypeMask & (LLVolumeFace::CAP_MASK)) {
+
+ }
+ else {
+
+ //==============================================
+ //DEBUG draw edge map instead of silhouette edge
+ //==============================================
+
+#if DEBUG_SILHOUETTE_EDGE_MAP
+
+ //for each triangle
+ U32 count = face.mNumIndices;
+ for (U32 j = 0; j < count/3; j++) {
+ //get vertices
+ S32 v1 = face.mIndices[j*3+0];
+ S32 v2 = face.mIndices[j*3+1];
+ S32 v3 = face.mIndices[j*3+2];
+
+ //get current face center
+ LLVector3 cCenter = (face.mVertices[v1].getPosition() +
+ face.mVertices[v2].getPosition() +
+ face.mVertices[v3].getPosition()) / 3.0f;
+
+ //for each edge
+ for (S32 k = 0; k < 3; k++) {
+ S32 nIndex = face.mEdge[j*3+k];
+ if (nIndex <= -1) {
+ continue;
+ }
+
+ if (nIndex >= (S32) count/3) {
+ continue;
+ }
+ //get neighbor vertices
+ v1 = face.mIndices[nIndex*3+0];
+ v2 = face.mIndices[nIndex*3+1];
+ v3 = face.mIndices[nIndex*3+2];
+
+ //get neighbor face center
+ LLVector3 nCenter = (face.mVertices[v1].getPosition() +
+ face.mVertices[v2].getPosition() +
+ face.mVertices[v3].getPosition()) / 3.0f;
+
+ //draw line
+ vertices.push_back(cCenter);
+ vertices.push_back(nCenter);
+ normals.push_back(LLVector3(1,1,1));
+ normals.push_back(LLVector3(1,1,1));
+ segments.push_back(vertices.size());
+ }
+ }
+
+ continue;
+
+ //==============================================
+ //DEBUG
+ //==============================================
+
+ //==============================================
+ //DEBUG draw normals instead of silhouette edge
+ //==============================================
+#elif DEBUG_SILHOUETTE_NORMALS
+
+ //for each vertex
+ for (U32 j = 0; j < face.mNumVertices; j++) {
+ vertices.push_back(face.mVertices[j].getPosition());
+ vertices.push_back(face.mVertices[j].getPosition() + face.mVertices[j].getNormal()*0.1f);
+ normals.push_back(LLVector3(0,0,1));
+ normals.push_back(LLVector3(0,0,1));
+ segments.push_back(vertices.size());
+#if DEBUG_SILHOUETTE_BINORMALS
+ vertices.push_back(face.mVertices[j].getPosition());
+ vertices.push_back(face.mVertices[j].getPosition() + face.mVertices[j].mBinormal*0.1f);
+ normals.push_back(LLVector3(0,0,1));
+ normals.push_back(LLVector3(0,0,1));
+ segments.push_back(vertices.size());
+#endif
+ }
+
+ continue;
+#else
+ //==============================================
+ //DEBUG
+ //==============================================
+
+ static const U8 AWAY = 0x01,
+ TOWARDS = 0x02;
+
+ //for each triangle
+ std::vector<U8> fFacing;
+ vector_append(fFacing, face.mNumIndices/3);
+
+ LLVector4a* v = (LLVector4a*) face.mPositions;
+ LLVector4a* n = (LLVector4a*) face.mNormals;
+
+ for (U32 j = 0; j < face.mNumIndices/3; j++)
+ {
+ //approximate normal
+ S32 v1 = face.mIndices[j*3+0];
+ S32 v2 = face.mIndices[j*3+1];
+ S32 v3 = face.mIndices[j*3+2];
+
+ LLVector4a c1,c2;
+ c1.setSub(v[v1], v[v2]);
+ c2.setSub(v[v2], v[v3]);
+
+ LLVector4a norm;
+
+ norm.setCross3(c1, c2);
+
+ if (norm.dot3(norm) < 0.00000001f)
+ {
+ fFacing[j] = AWAY | TOWARDS;
+ }
+ else
+ {
+ //get view vector
+ LLVector4a view;
+ view.setSub(obj_cam_vec, v[v1]);
+ bool away = view.dot3(norm) > 0.0f;
+ if (away)
+ {
+ fFacing[j] = AWAY;
+ }
+ else
+ {
+ fFacing[j] = TOWARDS;
+ }
+ }
+ }
+
+ //for each triangle
+ for (U32 j = 0; j < face.mNumIndices/3; j++)
+ {
+ if (fFacing[j] == (AWAY | TOWARDS))
+ { //this is a degenerate triangle
+ //take neighbor facing (degenerate faces get facing of one of their neighbors)
+ // *FIX IF NEEDED: this does not deal with neighboring degenerate faces
+ for (S32 k = 0; k < 3; k++)
+ {
+ S32 index = face.mEdge[j*3+k];
+ if (index != -1)
+ {
+ fFacing[j] = fFacing[index];
+ break;
+ }
+ }
+ continue; //skip degenerate face
+ }
+
+ //for each edge
+ for (S32 k = 0; k < 3; k++) {
+ S32 index = face.mEdge[j*3+k];
+ if (index != -1 && fFacing[index] == (AWAY | TOWARDS)) {
+ //our neighbor is degenerate, make him face our direction
+ fFacing[face.mEdge[j*3+k]] = fFacing[j];
+ continue;
+ }
+
+ if (index == -1 || //edge has no neighbor, MUST be a silhouette edge
+ (fFacing[index] & fFacing[j]) == 0) { //we found a silhouette edge
+
+ S32 v1 = face.mIndices[j*3+k];
+ S32 v2 = face.mIndices[j*3+((k+1)%3)];
+
+ LLVector4a t;
+ mat.affineTransform(v[v1], t);
+ vertices.push_back(LLVector3(t[0], t[1], t[2]));
+
+ norm_mat.rotate(n[v1], t);
+
+ t.normalize3fast();
+ normals.push_back(LLVector3(t[0], t[1], t[2]));
+
+ mat.affineTransform(v[v2], t);
+ vertices.push_back(LLVector3(t[0], t[1], t[2]));
+
+ norm_mat.rotate(n[v2], t);
+ t.normalize3fast();
+ normals.push_back(LLVector3(t[0], t[1], t[2]));
+ }
+ }
+ }
+#endif
+ }
+ }
+}
+
+S32 LLVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
+ S32 face,
+ LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
+{
+ LLVector4a starta, enda;
+ starta.load3(start.mV);
+ enda.load3(end.mV);
+
+ return lineSegmentIntersect(starta, enda, face, intersection, tex_coord, normal, bi_normal);
+
+}
+
+
+S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
+ S32 face,
+ LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
+{
+ S32 hit_face = -1;
+
+ S32 start_face;
+ S32 end_face;
+
+ if (face == -1) // ALL_SIDES
+ {
+ start_face = 0;
+ end_face = getNumVolumeFaces() - 1;
+ }
+ else
+ {
+ start_face = face;
+ end_face = face;
+ }
+
+ LLVector4a dir;
+ dir.setSub(end, start);
+
+ F32 closest_t = 2.f; // must be larger than 1
+
+ end_face = llmin(end_face, getNumVolumeFaces()-1);
+
+ for (S32 i = start_face; i <= end_face; i++)
+ {
+ LLVolumeFace &face = mVolumeFaces[i];
+
+ LLVector4a box_center;
+ box_center.setAdd(face.mExtents[0], face.mExtents[1]);
+ box_center.mul(0.5f);
+
+ LLVector4a box_size;
+ box_size.setSub(face.mExtents[1], face.mExtents[0]);
+
+ if (LLLineSegmentBoxIntersect(start, end, box_center, box_size))
+ {
+ if (bi_normal != NULL) // if the caller wants binormals, we may need to generate them
+ {
+ genBinormals(i);
+ }
+
+ if (!face.mOctree)
+ {
+ face.createOctree();
+ }
+
+ //LLVector4a* p = (LLVector4a*) face.mPositions;
+
+ LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, bi_normal);
+ intersect.traverse(face.mOctree);
+ if (intersect.mHitFace)
+ {
+ hit_face = i;
+ }
+ }
+ }
+
+
+ return hit_face;
+}
+
+class LLVertexIndexPair
+{
+public:
+ LLVertexIndexPair(const LLVector3 &vertex, const S32 index);
+
+ LLVector3 mVertex;
+ S32 mIndex;
+};
+
+LLVertexIndexPair::LLVertexIndexPair(const LLVector3 &vertex, const S32 index)
+{
+ mVertex = vertex;
+ mIndex = index;
+}
+
+const F32 VERTEX_SLOP = 0.00001f;
+const F32 VERTEX_SLOP_SQRD = VERTEX_SLOP * VERTEX_SLOP;
+
+struct lessVertex
+{
+ bool operator()(const LLVertexIndexPair *a, const LLVertexIndexPair *b)
+ {
+ const F32 slop = VERTEX_SLOP;
+
+ if (a->mVertex.mV[0] + slop < b->mVertex.mV[0])
+ {
+ return TRUE;
+ }
+ else if (a->mVertex.mV[0] - slop > b->mVertex.mV[0])
+ {
+ return FALSE;
+ }
+
+ if (a->mVertex.mV[1] + slop < b->mVertex.mV[1])
+ {
+ return TRUE;
+ }
+ else if (a->mVertex.mV[1] - slop > b->mVertex.mV[1])
+ {
+ return FALSE;
+ }
+
+ if (a->mVertex.mV[2] + slop < b->mVertex.mV[2])
+ {
+ return TRUE;
+ }
+ else if (a->mVertex.mV[2] - slop > b->mVertex.mV[2])
+ {
+ return FALSE;
+ }
+
+ return FALSE;
+ }
+};
+
+struct lessTriangle
+{
+ bool operator()(const S32 *a, const S32 *b)
+ {
+ if (*a < *b)
+ {
+ return TRUE;
+ }
+ else if (*a > *b)
+ {
+ return FALSE;
+ }
+
+ if (*(a+1) < *(b+1))
+ {
+ return TRUE;
+ }
+ else if (*(a+1) > *(b+1))
+ {
+ return FALSE;
+ }
+
+ if (*(a+2) < *(b+2))
+ {
+ return TRUE;
+ }
+ else if (*(a+2) > *(b+2))
+ {
+ return FALSE;
+ }
+
+ return FALSE;
+ }
+};
+
+BOOL equalTriangle(const S32 *a, const S32 *b)
+{
+ if ((*a == *b) && (*(a+1) == *(b+1)) && (*(a+2) == *(b+2)))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLVolume::cleanupTriangleData( const S32 num_input_vertices,
+ const std::vector<Point>& input_vertices,
+ const S32 num_input_triangles,
+ S32 *input_triangles,
+ S32 &num_output_vertices,
+ LLVector3 **output_vertices,
+ S32 &num_output_triangles,
+ S32 **output_triangles)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ /* Testing: avoid any cleanup
+ static BOOL skip_cleanup = TRUE;
+ if ( skip_cleanup )
+ {
+ num_output_vertices = num_input_vertices;
+ num_output_triangles = num_input_triangles;
+
+ *output_vertices = new LLVector3[num_input_vertices];
+ for (S32 index = 0; index < num_input_vertices; index++)
+ {
+ (*output_vertices)[index] = input_vertices[index].mPos;
+ }
+
+ *output_triangles = new S32[num_input_triangles*3];
+ memcpy(*output_triangles, input_triangles, 3*num_input_triangles*sizeof(S32)); // Flawfinder: ignore
+ return TRUE;
+ }
+ */
+
+ // Here's how we do this:
+ // Create a structure which contains the original vertex index and the
+ // LLVector3 data.
+ // "Sort" the data by the vectors
+ // Create an array the size of the old vertex list, with a mapping of
+ // old indices to new indices.
+ // Go through triangles, shift so the lowest index is first
+ // Sort triangles by first index
+ // Remove duplicate triangles
+ // Allocate and pack new triangle data.
+
+ //LLTimer cleanupTimer;
+ //llinfos << "In vertices: " << num_input_vertices << llendl;
+ //llinfos << "In triangles: " << num_input_triangles << llendl;
+
+ S32 i;
+ typedef std::multiset<LLVertexIndexPair*, lessVertex> vertex_set_t;
+ vertex_set_t vertex_list;
+
+ LLVertexIndexPair *pairp = NULL;
+ for (i = 0; i < num_input_vertices; i++)
+ {
+ LLVertexIndexPair *new_pairp = new LLVertexIndexPair(input_vertices[i].mPos, i);
+ vertex_list.insert(new_pairp);
+ }
+
+ // Generate the vertex mapping and the list of vertices without
+ // duplicates. This will crash if there are no vertices.
+ llassert(num_input_vertices > 0); // check for no vertices!
+ S32 *vertex_mapping = new S32[num_input_vertices];
+ LLVector3 *new_vertices = new LLVector3[num_input_vertices];
+ LLVertexIndexPair *prev_pairp = NULL;
+
+ S32 new_num_vertices;
+
+ new_num_vertices = 0;
+ for (vertex_set_t::iterator iter = vertex_list.begin(),
+ end = vertex_list.end();
+ iter != end; iter++)
+ {
+ pairp = *iter;
+ if (!prev_pairp || ((pairp->mVertex - prev_pairp->mVertex).magVecSquared() >= VERTEX_SLOP_SQRD))
+ {
+ new_vertices[new_num_vertices] = pairp->mVertex;
+ //llinfos << "Added vertex " << new_num_vertices << " : " << pairp->mVertex << llendl;
+ new_num_vertices++;
+ // Update the previous
+ prev_pairp = pairp;
+ }
+ else
+ {
+ //llinfos << "Removed duplicate vertex " << pairp->mVertex << ", distance magVecSquared() is " << (pairp->mVertex - prev_pairp->mVertex).magVecSquared() << llendl;
+ }
+ vertex_mapping[pairp->mIndex] = new_num_vertices - 1;
+ }
+
+ // Iterate through triangles and remove degenerates, re-ordering vertices
+ // along the way.
+ S32 *new_triangles = new S32[num_input_triangles * 3];
+ S32 new_num_triangles = 0;
+
+ for (i = 0; i < num_input_triangles; i++)
+ {
+ S32 v1 = i*3;
+ S32 v2 = v1 + 1;
+ S32 v3 = v1 + 2;
+
+ //llinfos << "Checking triangle " << input_triangles[v1] << ":" << input_triangles[v2] << ":" << input_triangles[v3] << llendl;
+ input_triangles[v1] = vertex_mapping[input_triangles[v1]];
+ input_triangles[v2] = vertex_mapping[input_triangles[v2]];
+ input_triangles[v3] = vertex_mapping[input_triangles[v3]];
+
+ if ((input_triangles[v1] == input_triangles[v2])
+ || (input_triangles[v1] == input_triangles[v3])
+ || (input_triangles[v2] == input_triangles[v3]))
+ {
+ //llinfos << "Removing degenerate triangle " << input_triangles[v1] << ":" << input_triangles[v2] << ":" << input_triangles[v3] << llendl;
+ // Degenerate triangle, skip
+ continue;
+ }
+
+ if (input_triangles[v1] < input_triangles[v2])
+ {
+ if (input_triangles[v1] < input_triangles[v3])
+ {
+ // (0 < 1) && (0 < 2)
+ new_triangles[new_num_triangles*3] = input_triangles[v1];
+ new_triangles[new_num_triangles*3+1] = input_triangles[v2];
+ new_triangles[new_num_triangles*3+2] = input_triangles[v3];
+ }
+ else
+ {
+ // (0 < 1) && (2 < 0)
+ new_triangles[new_num_triangles*3] = input_triangles[v3];
+ new_triangles[new_num_triangles*3+1] = input_triangles[v1];
+ new_triangles[new_num_triangles*3+2] = input_triangles[v2];
+ }
+ }
+ else if (input_triangles[v2] < input_triangles[v3])
+ {
+ // (1 < 0) && (1 < 2)
+ new_triangles[new_num_triangles*3] = input_triangles[v2];
+ new_triangles[new_num_triangles*3+1] = input_triangles[v3];
+ new_triangles[new_num_triangles*3+2] = input_triangles[v1];
+ }
+ else
+ {
+ // (1 < 0) && (2 < 1)
+ new_triangles[new_num_triangles*3] = input_triangles[v3];
+ new_triangles[new_num_triangles*3+1] = input_triangles[v1];
+ new_triangles[new_num_triangles*3+2] = input_triangles[v2];
+ }
+ new_num_triangles++;
+ }
+
+ if (new_num_triangles == 0)
+ {
+ llwarns << "Created volume object with 0 faces." << llendl;
+ delete[] new_triangles;
+ delete[] vertex_mapping;
+ delete[] new_vertices;
+ return FALSE;
+ }
+
+ typedef std::set<S32*, lessTriangle> triangle_set_t;
+ triangle_set_t triangle_list;
+
+ for (i = 0; i < new_num_triangles; i++)
+ {
+ triangle_list.insert(&new_triangles[i*3]);
+ }
+
+ // Sort through the triangle list, and delete duplicates
+
+ S32 *prevp = NULL;
+ S32 *curp = NULL;
+
+ S32 *sorted_tris = new S32[new_num_triangles*3];
+ S32 cur_tri = 0;
+ for (triangle_set_t::iterator iter = triangle_list.begin(),
+ end = triangle_list.end();
+ iter != end; iter++)
+ {
+ curp = *iter;
+ if (!prevp || !equalTriangle(prevp, curp))
+ {
+ //llinfos << "Added triangle " << *curp << ":" << *(curp+1) << ":" << *(curp+2) << llendl;
+ sorted_tris[cur_tri*3] = *curp;
+ sorted_tris[cur_tri*3+1] = *(curp+1);
+ sorted_tris[cur_tri*3+2] = *(curp+2);
+ cur_tri++;
+ prevp = curp;
+ }
+ else
+ {
+ //llinfos << "Skipped triangle " << *curp << ":" << *(curp+1) << ":" << *(curp+2) << llendl;
+ }
+ }
+
+ *output_vertices = new LLVector3[new_num_vertices];
+ num_output_vertices = new_num_vertices;
+ for (i = 0; i < new_num_vertices; i++)
+ {
+ (*output_vertices)[i] = new_vertices[i];
+ }
+
+ *output_triangles = new S32[cur_tri*3];
+ num_output_triangles = cur_tri;
+ memcpy(*output_triangles, sorted_tris, 3*cur_tri*sizeof(S32)); /* Flawfinder: ignore */
+
+ /*
+ llinfos << "Out vertices: " << num_output_vertices << llendl;
+ llinfos << "Out triangles: " << num_output_triangles << llendl;
+ for (i = 0; i < num_output_vertices; i++)
+ {
+ llinfos << i << ":" << (*output_vertices)[i] << llendl;
+ }
+ for (i = 0; i < num_output_triangles; i++)
+ {
+ llinfos << i << ":" << (*output_triangles)[i*3] << ":" << (*output_triangles)[i*3+1] << ":" << (*output_triangles)[i*3+2] << llendl;
+ }
+ */
+
+ //llinfos << "Out vertices: " << num_output_vertices << llendl;
+ //llinfos << "Out triangles: " << num_output_triangles << llendl;
+ delete[] vertex_mapping;
+ vertex_mapping = NULL;
+ delete[] new_vertices;
+ new_vertices = NULL;
+ delete[] new_triangles;
+ new_triangles = NULL;
+ delete[] sorted_tris;
+ sorted_tris = NULL;
+ triangle_list.clear();
+ std::for_each(vertex_list.begin(), vertex_list.end(), DeletePointer());
+ vertex_list.clear();
+
+ return TRUE;
+}
+
+
+BOOL LLVolumeParams::importFile(LLFILE *fp)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ //llinfos << "importing volume" << llendl;
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE]; /* Flawfinder: ignore */
+ // *NOTE: changing the size or type of this buffer will require
+ // changing the sscanf below.
+ char keyword[256]; /* Flawfinder: ignore */
+ keyword[0] = 0;
+
+ while (!feof(fp))
+ {
+ if (fgets(buffer, BUFSIZE, fp) == NULL)
+ {
+ buffer[0] = '\0';
+ }
+
+ sscanf(buffer, " %255s", keyword); /* Flawfinder: ignore */
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("profile", keyword))
+ {
+ mProfileParams.importFile(fp);
+ }
+ else if (!strcmp("path",keyword))
+ {
+ mPathParams.importFile(fp);
+ }
+ else
+ {
+ llwarns << "unknown keyword " << keyword << " in volume import" << llendl;
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL LLVolumeParams::exportFile(LLFILE *fp) const
+{
+ fprintf(fp,"\tshape 0\n");
+ fprintf(fp,"\t{\n");
+ mPathParams.exportFile(fp);
+ mProfileParams.exportFile(fp);
+ fprintf(fp, "\t}\n");
+ return TRUE;
+}
+
+
+BOOL LLVolumeParams::importLegacyStream(std::istream& input_stream)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ //llinfos << "importing volume" << llendl;
+ const S32 BUFSIZE = 16384;
+ // *NOTE: changing the size or type of this buffer will require
+ // changing the sscanf below.
+ char buffer[BUFSIZE]; /* Flawfinder: ignore */
+ char keyword[256]; /* Flawfinder: ignore */
+ keyword[0] = 0;
+
+ while (input_stream.good())
+ {
+ input_stream.getline(buffer, BUFSIZE);
+ sscanf(buffer, " %255s", keyword);
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("profile", keyword))
+ {
+ mProfileParams.importLegacyStream(input_stream);
+ }
+ else if (!strcmp("path",keyword))
+ {
+ mPathParams.importLegacyStream(input_stream);
+ }
+ else
+ {
+ llwarns << "unknown keyword " << keyword << " in volume import" << llendl;
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL LLVolumeParams::exportLegacyStream(std::ostream& output_stream) const
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ output_stream <<"\tshape 0\n";
+ output_stream <<"\t{\n";
+ mPathParams.exportLegacyStream(output_stream);
+ mProfileParams.exportLegacyStream(output_stream);
+ output_stream << "\t}\n";
+ return TRUE;
+}
+
+LLSD LLVolumeParams::sculptAsLLSD() const
+{
+ LLSD sd = LLSD();
+ sd["id"] = getSculptID();
+ sd["type"] = getSculptType();
+
+ return sd;
+}
+
+bool LLVolumeParams::sculptFromLLSD(LLSD& sd)
+{
+ setSculptID(sd["id"].asUUID(), (U8)sd["type"].asInteger());
+ return true;
+}
+
+LLSD LLVolumeParams::asLLSD() const
+{
+ LLSD sd = LLSD();
+ sd["path"] = mPathParams;
+ sd["profile"] = mProfileParams;
+ sd["sculpt"] = sculptAsLLSD();
+
+ return sd;
+}
+
+bool LLVolumeParams::fromLLSD(LLSD& sd)
+{
+ mPathParams.fromLLSD(sd["path"]);
+ mProfileParams.fromLLSD(sd["profile"]);
+ sculptFromLLSD(sd["sculpt"]);
+
+ return true;
+}
+
+void LLVolumeParams::reduceS(F32 begin, F32 end)
+{
+ begin = llclampf(begin);
+ end = llclampf(end);
+ if (begin > end)
+ {
+ F32 temp = begin;
+ begin = end;
+ end = temp;
+ }
+ F32 a = mProfileParams.getBegin();
+ F32 b = mProfileParams.getEnd();
+ mProfileParams.setBegin(a + begin * (b - a));
+ mProfileParams.setEnd(a + end * (b - a));
+}
+
+void LLVolumeParams::reduceT(F32 begin, F32 end)
+{
+ begin = llclampf(begin);
+ end = llclampf(end);
+ if (begin > end)
+ {
+ F32 temp = begin;
+ begin = end;
+ end = temp;
+ }
+ F32 a = mPathParams.getBegin();
+ F32 b = mPathParams.getEnd();
+ mPathParams.setBegin(a + begin * (b - a));
+ mPathParams.setEnd(a + end * (b - a));
+}
+
+const F32 MIN_CONCAVE_PROFILE_WEDGE = 0.125f; // 1/8 unity
+const F32 MIN_CONCAVE_PATH_WEDGE = 0.111111f; // 1/9 unity
+
+// returns TRUE if the shape can be approximated with a convex shape
+// for collison purposes
+BOOL LLVolumeParams::isConvex() const
+{
+ if (!getSculptID().isNull())
+ {
+ // can't determine, be safe and say no:
+ return FALSE;
+ }
+
+ F32 path_length = mPathParams.getEnd() - mPathParams.getBegin();
+ F32 hollow = mProfileParams.getHollow();
+
+ U8 path_type = mPathParams.getCurveType();
+ if ( path_length > MIN_CONCAVE_PATH_WEDGE
+ && ( mPathParams.getTwist() != mPathParams.getTwistBegin()
+ || (hollow > 0.f
+ && LL_PCODE_PATH_LINE != path_type) ) )
+ {
+ // twist along a "not too short" path is concave
+ return FALSE;
+ }
+
+ F32 profile_length = mProfileParams.getEnd() - mProfileParams.getBegin();
+ BOOL same_hole = hollow == 0.f
+ || (mProfileParams.getCurveType() & LL_PCODE_HOLE_MASK) == LL_PCODE_HOLE_SAME;
+
+ F32 min_profile_wedge = MIN_CONCAVE_PROFILE_WEDGE;
+ U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
+ if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type )
+ {
+ // it is a sphere and spheres get twice the minimum profile wedge
+ min_profile_wedge = 2.f * MIN_CONCAVE_PROFILE_WEDGE;
+ }
+
+ BOOL convex_profile = ( ( profile_length == 1.f
+ || profile_length <= 0.5f )
+ && hollow == 0.f ) // trivially convex
+ || ( profile_length <= min_profile_wedge
+ && same_hole ); // effectvely convex (even when hollow)
+
+ if (!convex_profile)
+ {
+ // profile is concave
+ return FALSE;
+ }
+
+ if ( LL_PCODE_PATH_LINE == path_type )
+ {
+ // straight paths with convex profile
+ return TRUE;
+ }
+
+ BOOL concave_path = (path_length < 1.0f) && (path_length > 0.5f);
+ if (concave_path)
+ {
+ return FALSE;
+ }
+
+ // we're left with spheres, toroids and tubes
+ if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type )
+ {
+ // at this stage all spheres must be convex
+ return TRUE;
+ }
+
+ // it's a toroid or tube
+ if ( path_length <= MIN_CONCAVE_PATH_WEDGE )
+ {
+ // effectively convex
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// debug
+void LLVolumeParams::setCube()
+{
+ mProfileParams.setCurveType(LL_PCODE_PROFILE_SQUARE);
+ mProfileParams.setBegin(0.f);
+ mProfileParams.setEnd(1.f);
+ mProfileParams.setHollow(0.f);
+
+ mPathParams.setBegin(0.f);
+ mPathParams.setEnd(1.f);
+ mPathParams.setScale(1.f, 1.f);
+ mPathParams.setShear(0.f, 0.f);
+ mPathParams.setCurveType(LL_PCODE_PATH_LINE);
+ mPathParams.setTwistBegin(0.f);
+ mPathParams.setTwistEnd(0.f);
+ mPathParams.setRadiusOffset(0.f);
+ mPathParams.setTaper(0.f, 0.f);
+ mPathParams.setRevolutions(0.f);
+ mPathParams.setSkew(0.f);
+}
+
+LLFaceID LLVolume::generateFaceMask()
+{
+ LLFaceID new_mask = 0x0000;
+
+ switch(mParams.getProfileParams().getCurveType() & LL_PCODE_PROFILE_MASK)
+ {
+ case LL_PCODE_PROFILE_CIRCLE:
+ case LL_PCODE_PROFILE_CIRCLE_HALF:
+ new_mask |= LL_FACE_OUTER_SIDE_0;
+ break;
+ case LL_PCODE_PROFILE_SQUARE:
+ {
+ for(S32 side = (S32)(mParams.getProfileParams().getBegin() * 4.f); side < llceil(mParams.getProfileParams().getEnd() * 4.f); side++)
+ {
+ new_mask |= LL_FACE_OUTER_SIDE_0 << side;
+ }
+ }
+ break;
+ case LL_PCODE_PROFILE_ISOTRI:
+ case LL_PCODE_PROFILE_EQUALTRI:
+ case LL_PCODE_PROFILE_RIGHTTRI:
+ {
+ for(S32 side = (S32)(mParams.getProfileParams().getBegin() * 3.f); side < llceil(mParams.getProfileParams().getEnd() * 3.f); side++)
+ {
+ new_mask |= LL_FACE_OUTER_SIDE_0 << side;
+ }
+ }
+ break;
+ default:
+ llerrs << "Unknown profile!" << llendl;
+ break;
+ }
+
+ // handle hollow objects
+ if (mParams.getProfileParams().getHollow() > 0)
+ {
+ new_mask |= LL_FACE_INNER_SIDE;
+ }
+
+ // handle open profile curves
+ if (mProfilep->isOpen())
+ {
+ new_mask |= LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END;
+ }
+
+ // handle open path curves
+ if (mPathp->isOpen())
+ {
+ new_mask |= LL_FACE_PATH_BEGIN | LL_FACE_PATH_END;
+ }
+
+ return new_mask;
+}
+
+BOOL LLVolume::isFaceMaskValid(LLFaceID face_mask)
+{
+ LLFaceID test_mask = 0;
+ for(S32 i = 0; i < getNumFaces(); i++)
+ {
+ test_mask |= mProfilep->mFaces[i].mFaceID;
+ }
+
+ return test_mask == face_mask;
+}
+
+BOOL LLVolume::isConvex() const
+{
+ // mParams.isConvex() may return FALSE even though the final
+ // geometry is actually convex due to LOD approximations.
+ // TODO -- provide LLPath and LLProfile with isConvex() methods
+ // that correctly determine convexity. -- Leviathan
+ return mParams.isConvex();
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLProfileParams &profile_params)
+{
+ s << "{type=" << (U32) profile_params.mCurveType;
+ s << ", begin=" << profile_params.mBegin;
+ s << ", end=" << profile_params.mEnd;
+ s << ", hollow=" << profile_params.mHollow;
+ s << "}";
+ return s;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLPathParams &path_params)
+{
+ s << "{type=" << (U32) path_params.mCurveType;
+ s << ", begin=" << path_params.mBegin;
+ s << ", end=" << path_params.mEnd;
+ s << ", twist=" << path_params.mTwistEnd;
+ s << ", scale=" << path_params.mScale;
+ s << ", shear=" << path_params.mShear;
+ s << ", twist_begin=" << path_params.mTwistBegin;
+ s << ", radius_offset=" << path_params.mRadiusOffset;
+ s << ", taper=" << path_params.mTaper;
+ s << ", revolutions=" << path_params.mRevolutions;
+ s << ", skew=" << path_params.mSkew;
+ s << "}";
+ return s;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params)
+{
+ s << "{profileparams = " << volume_params.mProfileParams;
+ s << ", pathparams = " << volume_params.mPathParams;
+ s << "}";
+ return s;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLProfile &profile)
+{
+ s << " {open=" << (U32) profile.mOpen;
+ s << ", dirty=" << profile.mDirty;
+ s << ", totalout=" << profile.mTotalOut;
+ s << ", total=" << profile.mTotal;
+ s << "}";
+ return s;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLPath &path)
+{
+ s << "{open=" << (U32) path.mOpen;
+ s << ", dirty=" << path.mDirty;
+ s << ", step=" << path.mStep;
+ s << ", total=" << path.mTotal;
+ s << "}";
+ return s;
+}
+
+std::ostream& operator<<(std::ostream &s, const LLVolume &volume)
+{
+ s << "{params = " << volume.getParams();
+ s << ", path = " << *volume.mPathp;
+ s << ", profile = " << *volume.mProfilep;
+ s << "}";
+ return s;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLVolume *volumep)
+{
+ s << "{params = " << volumep->getParams();
+ s << ", path = " << *(volumep->mPathp);
+ s << ", profile = " << *(volumep->mProfilep);
+ s << "}";
+ return s;
+}
+
+LLVolumeFace::LLVolumeFace() :
+ mID(0),
+ mTypeMask(0),
+ mBeginS(0),
+ mBeginT(0),
+ mNumS(0),
+ mNumT(0),
+ mNumVertices(0),
+ mNumIndices(0),
+ mPositions(NULL),
+ mNormals(NULL),
+ mBinormals(NULL),
+ mTexCoords(NULL),
+ mIndices(NULL),
+ mWeights(NULL),
+ mOctree(NULL)
+{
+ mExtents = (LLVector4a*) malloc(sizeof(LLVector4a)*3);
+ mCenter = mExtents+2;
+}
+
+LLVolumeFace::LLVolumeFace(const LLVolumeFace& src)
+: mID(0),
+ mTypeMask(0),
+ mBeginS(0),
+ mBeginT(0),
+ mNumS(0),
+ mNumT(0),
+ mNumVertices(0),
+ mNumIndices(0),
+ mPositions(NULL),
+ mNormals(NULL),
+ mBinormals(NULL),
+ mTexCoords(NULL),
+ mIndices(NULL),
+ mWeights(NULL),
+ mOctree(NULL)
+{
+ mExtents = (LLVector4a*) malloc(sizeof(LLVector4a)*3);
+ mCenter = mExtents+2;
+ *this = src;
+}
+
+LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src)
+{
+ if (&src == this)
+ { //self assignment, do nothing
+ return *this;
+ }
+
+ mID = src.mID;
+ mTypeMask = src.mTypeMask;
+ mBeginS = src.mBeginS;
+ mBeginT = src.mBeginT;
+ mNumS = src.mNumS;
+ mNumT = src.mNumT;
+
+ mExtents[0] = src.mExtents[0];
+ mExtents[1] = src.mExtents[1];
+ *mCenter = *src.mCenter;
+
+ mNumVertices = 0;
+ mNumIndices = 0;
+
+ freeData();
+
+ LLVector4a::memcpyNonAliased16((F32*) mExtents, (F32*) src.mExtents, 3*sizeof(LLVector4a));
+
+ resizeVertices(src.mNumVertices);
+ resizeIndices(src.mNumIndices);
+
+ if (mNumVertices)
+ {
+ S32 vert_size = mNumVertices*sizeof(LLVector4a);
+ S32 tc_size = (mNumVertices*sizeof(LLVector2)+0xF) & ~0xF;
+
+ LLVector4a::memcpyNonAliased16((F32*) mPositions, (F32*) src.mPositions, vert_size);
+ LLVector4a::memcpyNonAliased16((F32*) mNormals, (F32*) src.mNormals, vert_size);
+ LLVector4a::memcpyNonAliased16((F32*) mTexCoords, (F32*) src.mTexCoords, tc_size);
+
+
+ if (src.mBinormals)
+ {
+ allocateBinormals(src.mNumVertices);
+ LLVector4a::memcpyNonAliased16((F32*) mBinormals, (F32*) src.mBinormals, vert_size);
+ }
+ else
+ {
+ free(mBinormals);
+ mBinormals = NULL;
+ }
+
+ if (src.mWeights)
+ {
+ allocateWeights(src.mNumVertices);
+ LLVector4a::memcpyNonAliased16((F32*) mWeights, (F32*) src.mWeights, vert_size);
+ }
+ else
+ {
+ free(mWeights);
+ mWeights = NULL;
+ }
+ }
+
+ if (mNumIndices)
+ {
+ S32 idx_size = (mNumIndices*sizeof(U16)+0xF) & ~0xF;
+
+ LLVector4a::memcpyNonAliased16((F32*) mIndices, (F32*) src.mIndices, idx_size);
+ }
+
+ //delete
+ return *this;
+}
+
+LLVolumeFace::~LLVolumeFace()
+{
+ free(mExtents);
+ mExtents = NULL;
+
+ freeData();
+}
+
+void LLVolumeFace::freeData()
+{
+ free(mPositions);
+ mPositions = NULL;
+ free( mNormals);
+ mNormals = NULL;
+ free(mTexCoords);
+ mTexCoords = NULL;
+ free(mIndices);
+ mIndices = NULL;
+ free(mBinormals);
+ mBinormals = NULL;
+ free(mWeights);
+ mWeights = NULL;
+
+ delete mOctree;
+ mOctree = NULL;
+}
+
+BOOL LLVolumeFace::create(LLVolume* volume, BOOL partial_build)
+{
+ //tree for this face is no longer valid
+ delete mOctree;
+ mOctree = NULL;
+
+ BOOL ret = FALSE ;
+ if (mTypeMask & CAP_MASK)
+ {
+ ret = createCap(volume, partial_build);
+ }
+ else if ((mTypeMask & END_MASK) || (mTypeMask & SIDE_MASK))
+ {
+ ret = createSide(volume, partial_build);
+ }
+ else
+ {
+ llerrs << "Unknown/uninitialized face type!" << llendl;
+ }
+
+ //update the range of the texture coordinates
+ if(ret)
+ {
+ mTexCoordExtents[0].setVec(1.f, 1.f) ;
+ mTexCoordExtents[1].setVec(0.f, 0.f) ;
+
+ for(U32 i = 0 ; i < mNumVertices ; i++)
+ {
+ if(mTexCoordExtents[0].mV[0] > mTexCoords[i].mV[0])
+ {
+ mTexCoordExtents[0].mV[0] = mTexCoords[i].mV[0] ;
+ }
+ if(mTexCoordExtents[1].mV[0] < mTexCoords[i].mV[0])
+ {
+ mTexCoordExtents[1].mV[0] = mTexCoords[i].mV[0] ;
+ }
+
+ if(mTexCoordExtents[0].mV[1] > mTexCoords[i].mV[1])
+ {
+ mTexCoordExtents[0].mV[1] = mTexCoords[i].mV[1] ;
+ }
+ if(mTexCoordExtents[1].mV[1] < mTexCoords[i].mV[1])
+ {
+ mTexCoordExtents[1].mV[1] = mTexCoords[i].mV[1] ;
+ }
+ }
+ mTexCoordExtents[0].mV[0] = llmax(0.f, mTexCoordExtents[0].mV[0]) ;
+ mTexCoordExtents[0].mV[1] = llmax(0.f, mTexCoordExtents[0].mV[1]) ;
+ mTexCoordExtents[1].mV[0] = llmin(1.f, mTexCoordExtents[1].mV[0]) ;
+ mTexCoordExtents[1].mV[1] = llmin(1.f, mTexCoordExtents[1].mV[1]) ;
+ }
+
+ return ret ;
+}
+
+void LLVolumeFace::getVertexData(U16 index, LLVolumeFace::VertexData& cv)
+{
+ cv.setPosition(mPositions[index]);
+ cv.setNormal(mNormals[index]);
+ cv.mTexCoord = mTexCoords[index];
+}
+
+bool LLVolumeFace::VertexMapData::operator==(const LLVolumeFace::VertexData& rhs) const
+{
+ return getPosition().equals3(rhs.getPosition()) &&
+ mTexCoord == rhs.mTexCoord &&
+ getNormal().equals3(rhs.getNormal());
+}
+
+bool LLVolumeFace::VertexMapData::ComparePosition::operator()(const LLVector3& a, const LLVector3& b) const
+{
+ if (a.mV[0] != b.mV[0])
+ {
+ return a.mV[0] < b.mV[0];
+ }
+
+ if (a.mV[1] != b.mV[1])
+ {
+ return a.mV[1] < b.mV[1];
+ }
+
+ return a.mV[2] < b.mV[2];
+}
+
+void LLVolumeFace::optimize(F32 angle_cutoff)
+{
+ LLVolumeFace new_face;
+
+ //map of points to vector of vertices at that point
+ VertexMapData::PointMap point_map;
+
+ //remove redundant vertices
+ for (U32 i = 0; i < mNumIndices; ++i)
+ {
+ U16 index = mIndices[i];
+
+ LLVolumeFace::VertexData cv;
+ getVertexData(index, cv);
+
+ BOOL found = FALSE;
+ VertexMapData::PointMap::iterator point_iter = point_map.find(LLVector3(cv.getPosition().getF32ptr()));
+ if (point_iter != point_map.end())
+ { //duplicate point might exist
+ for (U32 j = 0; j < point_iter->second.size(); ++j)
+ {
+ LLVolumeFace::VertexData& tv = (point_iter->second)[j];
+ if (tv.compareNormal(cv, angle_cutoff))
+ {
+ found = TRUE;
+ new_face.pushIndex((point_iter->second)[j].mIndex);
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ new_face.pushVertex(cv);
+ U16 index = (U16) new_face.mNumVertices-1;
+ new_face.pushIndex(index);
+
+ VertexMapData d;
+ d.setPosition(cv.getPosition());
+ d.mTexCoord = cv.mTexCoord;
+ d.setNormal(cv.getNormal());
+ d.mIndex = index;
+ if (point_iter != point_map.end())
+ {
+ point_iter->second.push_back(d);
+ }
+ else
+ {
+ point_map[LLVector3(d.getPosition().getF32ptr())].push_back(d);
+ }
+ }
+ }
+
+ swapData(new_face);
+}
+
+class LLVCacheTriangleData;
+
+class LLVCacheVertexData
+{
+public:
+ S32 mIdx;
+ S32 mCacheTag;
+ F32 mScore;
+ U32 mActiveTriangles;
+ std::vector<LLVCacheTriangleData*> mTriangles;
+
+ LLVCacheVertexData()
+ {
+ mCacheTag = -1;
+ mScore = 0.f;
+ mActiveTriangles = 0;
+ mIdx = -1;
+ }
+};
+
+class LLVCacheTriangleData
+{
+public:
+ bool mActive;
+ F32 mScore;
+ LLVCacheVertexData* mVertex[3];
+
+ LLVCacheTriangleData()
+ {
+ mActive = true;
+ mScore = 0.f;
+ mVertex[0] = mVertex[1] = mVertex[2] = NULL;
+ }
+
+ void complete()
+ {
+ mActive = false;
+ for (S32 i = 0; i < 3; ++i)
+ {
+ if (mVertex[i])
+ {
+ llassert_always(mVertex[i]->mActiveTriangles > 0);
+ mVertex[i]->mActiveTriangles--;
+ }
+ }
+ }
+
+ bool operator<(const LLVCacheTriangleData& rhs) const
+ { //highest score first
+ return rhs.mScore < mScore;
+ }
+};
+
+const F32 FindVertexScore_CacheDecayPower = 1.5f;
+const F32 FindVertexScore_LastTriScore = 0.75f;
+const F32 FindVertexScore_ValenceBoostScale = 2.0f;
+const F32 FindVertexScore_ValenceBoostPower = 0.5f;
+const U32 MaxSizeVertexCache = 32;
+
+F32 find_vertex_score(LLVCacheVertexData& data)
+{
+ if (data.mActiveTriangles == 0)
+ { //no triangle references this vertex
+ return -1.f;
+ }
+
+ F32 score = 0.f;
+
+ S32 cache_idx = data.mCacheTag;
+
+ if (cache_idx < 0)
+ {
+ //not in cache
+ }
+ else
+ {
+ if (cache_idx < 3)
+ { //vertex was in the last triangle
+ score = FindVertexScore_LastTriScore;
+ }
+ else
+ { //more points for being higher in the cache
+ F32 scaler = 1.f/(MaxSizeVertexCache-3);
+ score = 1.f-((cache_idx-3)*scaler);
+ score = powf(score, FindVertexScore_CacheDecayPower);
+ }
+ }
+
+ //bonus points for having low valence
+ F32 valence_boost = powf(data.mActiveTriangles, -FindVertexScore_ValenceBoostPower);
+ score += FindVertexScore_ValenceBoostScale * valence_boost;
+
+ return score;
+}
+
+class LLVCacheFIFO
+{
+public:
+ LLVCacheVertexData* mCache[MaxSizeVertexCache];
+ U32 mMisses;
+
+ LLVCacheFIFO()
+ {
+ mMisses = 0;
+ for (U32 i = 0; i < MaxSizeVertexCache; ++i)
+ {
+ mCache[i] = NULL;
+ }
+ }
+
+ void addVertex(LLVCacheVertexData* data)
+ {
+ if (data->mCacheTag == -1)
+ {
+ mMisses++;
+
+ S32 end = MaxSizeVertexCache-1;
+
+ if (mCache[end])
+ {
+ mCache[end]->mCacheTag = -1;
+ }
+
+ for (S32 i = end; i > 0; --i)
+ {
+ mCache[i] = mCache[i-1];
+ if (mCache[i])
+ {
+ mCache[i]->mCacheTag = i;
+ }
+ }
+
+ mCache[0] = data;
+ data->mCacheTag = 0;
+ }
+ }
+};
+
+class LLVCacheLRU
+{
+public:
+ LLVCacheVertexData* mCache[MaxSizeVertexCache+3];
+
+ LLVCacheTriangleData* mBestTriangle;
+
+ U32 mMisses;
+
+ LLVCacheLRU()
+ {
+ for (U32 i = 0; i < MaxSizeVertexCache+3; ++i)
+ {
+ mCache[i] = NULL;
+ }
+
+ mBestTriangle = NULL;
+ mMisses = 0;
+ }
+
+ void addVertex(LLVCacheVertexData* data)
+ {
+ S32 end = MaxSizeVertexCache+2;
+ if (data->mCacheTag != -1)
+ { //just moving a vertex to the front of the cache
+ end = data->mCacheTag;
+ }
+ else
+ {
+ mMisses++;
+ if (mCache[end])
+ { //adding a new vertex, vertex at end of cache falls off
+ mCache[end]->mCacheTag = -1;
+ }
+ }
+
+ for (S32 i = end; i > 0; --i)
+ { //adjust cache pointers and tags
+ mCache[i] = mCache[i-1];
+
+ if (mCache[i])
+ {
+ mCache[i]->mCacheTag = i;
+ }
+ }
+
+ mCache[0] = data;
+ mCache[0]->mCacheTag = 0;
+ }
+
+ void addTriangle(LLVCacheTriangleData* data)
+ {
+ addVertex(data->mVertex[0]);
+ addVertex(data->mVertex[1]);
+ addVertex(data->mVertex[2]);
+ }
+
+ void updateScores()
+ {
+ for (U32 i = MaxSizeVertexCache; i < MaxSizeVertexCache+3; ++i)
+ { //trailing 3 vertices aren't actually in the cache for scoring purposes
+ if (mCache[i])
+ {
+ mCache[i]->mCacheTag = -1;
+ }
+ }
+
+ for (U32 i = 0; i < MaxSizeVertexCache; ++i)
+ { //update scores of vertices in cache
+ if (mCache[i])
+ {
+ mCache[i]->mScore = find_vertex_score(*(mCache[i]));
+ llassert_always(mCache[i]->mCacheTag == i);
+ }
+ }
+
+ mBestTriangle = NULL;
+ //update triangle scores
+ for (U32 i = 0; i < MaxSizeVertexCache+3; ++i)
+ {
+ if (mCache[i])
+ {
+ for (U32 j = 0; j < mCache[i]->mTriangles.size(); ++j)
+ {
+ LLVCacheTriangleData* tri = mCache[i]->mTriangles[j];
+ if (tri->mActive)
+ {
+ tri->mScore = tri->mVertex[0]->mScore;
+ tri->mScore += tri->mVertex[1]->mScore;
+ tri->mScore += tri->mVertex[2]->mScore;
+
+ if (!mBestTriangle || mBestTriangle->mScore < tri->mScore)
+ {
+ mBestTriangle = tri;
+ }
+ }
+ }
+ }
+ }
+
+ //knock trailing 3 vertices off the cache
+ for (U32 i = MaxSizeVertexCache; i < MaxSizeVertexCache+3; ++i)
+ {
+ if (mCache[i])
+ {
+ llassert_always(mCache[i]->mCacheTag == -1);
+ mCache[i] = NULL;
+ }
+ }
+ }
+};
+
+
+void LLVolumeFace::cacheOptimize()
+{ //optimize for vertex cache according to Forsyth method:
+ // http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
+
+ LLVCacheLRU cache;
+
+ //mapping of vertices to triangles and indices
+ std::vector<LLVCacheVertexData> vertex_data;
+
+ //mapping of triangles do vertices
+ std::vector<LLVCacheTriangleData> triangle_data;
+
+ triangle_data.resize(mNumIndices/3);
+ vertex_data.resize(mNumVertices);
+
+ for (U32 i = 0; i < mNumIndices; i++)
+ { //populate vertex data and triangle data arrays
+ U16 idx = mIndices[i];
+ U32 tri_idx = i/3;
+
+ vertex_data[idx].mTriangles.push_back(&(triangle_data[tri_idx]));
+ vertex_data[idx].mIdx = idx;
+ triangle_data[tri_idx].mVertex[i%3] = &(vertex_data[idx]);
+ }
+
+ /*F32 pre_acmr = 1.f;
+ //measure cache misses from before rebuild
+ {
+ LLVCacheFIFO test_cache;
+ for (U32 i = 0; i < mNumIndices; ++i)
+ {
+ test_cache.addVertex(&vertex_data[mIndices[i]]);
+ }
+
+ for (U32 i = 0; i < mNumVertices; i++)
+ {
+ vertex_data[i].mCacheTag = -1;
+ }
+
+ pre_acmr = (F32) test_cache.mMisses/(mNumIndices/3);
+ }*/
+
+ for (U32 i = 0; i < mNumVertices; i++)
+ { //initialize score values (no cache -- might try a fifo cache here)
+ vertex_data[i].mScore = find_vertex_score(vertex_data[i]);
+ vertex_data[i].mActiveTriangles = vertex_data[i].mTriangles.size();
+
+ for (U32 j = 0; j < vertex_data[i].mTriangles.size(); ++j)
+ {
+ vertex_data[i].mTriangles[j]->mScore += vertex_data[i].mScore;
+ }
+ }
+
+ //sort triangle data by score
+ std::sort(triangle_data.begin(), triangle_data.end());
+
+ std::vector<U16> new_indices;
+
+ LLVCacheTriangleData* tri;
+
+ //prime pump by adding first triangle to cache;
+ tri = &(triangle_data[0]);
+ cache.addTriangle(tri);
+ new_indices.push_back(tri->mVertex[0]->mIdx);
+ new_indices.push_back(tri->mVertex[1]->mIdx);
+ new_indices.push_back(tri->mVertex[2]->mIdx);
+ tri->complete();
+
+ U32 breaks = 0;
+ for (U32 i = 1; i < mNumIndices/3; ++i)
+ {
+ cache.updateScores();
+ tri = cache.mBestTriangle;
+ if (!tri)
+ {
+ breaks++;
+ for (U32 j = 0; j < triangle_data.size(); ++j)
+ {
+ if (triangle_data[j].mActive)
+ {
+ tri = &(triangle_data[j]);
+ break;
+ }
+ }
+ }
+
+ cache.addTriangle(tri);
+ new_indices.push_back(tri->mVertex[0]->mIdx);
+ new_indices.push_back(tri->mVertex[1]->mIdx);
+ new_indices.push_back(tri->mVertex[2]->mIdx);
+ tri->complete();
+ }
+
+ for (U32 i = 0; i < mNumIndices; ++i)
+ {
+ mIndices[i] = new_indices[i];
+ }
+
+ /*F32 post_acmr = 1.f;
+ //measure cache misses from after rebuild
+ {
+ LLVCacheFIFO test_cache;
+ for (U32 i = 0; i < mNumVertices; i++)
+ {
+ vertex_data[i].mCacheTag = -1;
+ }
+
+ for (U32 i = 0; i < mNumIndices; ++i)
+ {
+ test_cache.addVertex(&vertex_data[mIndices[i]]);
+ }
+
+ post_acmr = (F32) test_cache.mMisses/(mNumIndices/3);
+ }*/
+
+ //optimize for pre-TnL cache
+
+ //allocate space for new buffer
+ S32 num_verts = mNumVertices;
+ LLVector4a* pos = (LLVector4a*) malloc(sizeof(LLVector4a)*num_verts);
+ LLVector4a* norm = (LLVector4a*) malloc(sizeof(LLVector4a)*num_verts);
+ S32 size = ((num_verts*sizeof(LLVector2)) + 0xF) & ~0xF;
+ LLVector2* tc = (LLVector2*) malloc(size);
+
+ LLVector4a* wght = NULL;
+ if (mWeights)
+ {
+ wght = (LLVector4a*) malloc(sizeof(LLVector4a)*num_verts);
+ }
+
+ LLVector4a* binorm = NULL;
+ if (mBinormals)
+ {
+ binorm = (LLVector4a*) malloc(sizeof(LLVector4a)*num_verts);
+ }
+
+ //allocate mapping of old indices to new indices
+ std::vector<S32> new_idx;
+ new_idx.resize(mNumVertices, -1);
+
+ S32 cur_idx = 0;
+ for (U32 i = 0; i < mNumIndices; ++i)
+ {
+ U16 idx = mIndices[i];
+ if (new_idx[idx] == -1)
+ { //this vertex hasn't been added yet
+ new_idx[idx] = cur_idx;
+
+ //copy vertex data
+ pos[cur_idx] = mPositions[idx];
+ norm[cur_idx] = mNormals[idx];
+ tc[cur_idx] = mTexCoords[idx];
+ if (mWeights)
+ {
+ wght[cur_idx] = mWeights[idx];
+ }
+ if (mBinormals)
+ {
+ binorm[cur_idx] = mBinormals[idx];
+ }
+
+ cur_idx++;
+ }
+ }
+
+ for (U32 i = 0; i < mNumIndices; ++i)
+ {
+ mIndices[i] = new_idx[mIndices[i]];
+ }
+
+ free(mPositions);
+ free(mNormals);
+ free(mTexCoords);
+ free(mWeights);
+ free(mBinormals);
+
+ mPositions = pos;
+ mNormals = norm;
+ mTexCoords = tc;
+ mWeights = wght;
+ mBinormals = binorm;
+
+ //std::string result = llformat("ACMR pre/post: %.3f/%.3f -- %d triangles %d breaks", pre_acmr, post_acmr, mNumIndices/3, breaks);
+ //llinfos << result << llendl;
+
+}
+
+void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVector4a& size)
+{
+ if (mOctree)
+ {
+ return;
+ }
+
+ mOctree = new LLOctreeRoot<LLVolumeTriangle>(center, size, NULL);
+ new LLVolumeOctreeListener(mOctree);
+
+ for (U32 i = 0; i < mNumIndices; i+= 3)
+ { //for each triangle
+ LLPointer<LLVolumeTriangle> tri = new LLVolumeTriangle();
+
+ const LLVector4a& v0 = mPositions[mIndices[i]];
+ const LLVector4a& v1 = mPositions[mIndices[i+1]];
+ const LLVector4a& v2 = mPositions[mIndices[i+2]];
+
+ //store pointers to vertex data
+ tri->mV[0] = &v0;
+ tri->mV[1] = &v1;
+ tri->mV[2] = &v2;
+
+ //store indices
+ tri->mIndex[0] = mIndices[i];
+ tri->mIndex[1] = mIndices[i+1];
+ tri->mIndex[2] = mIndices[i+2];
+
+ //get minimum point
+ LLVector4a min = v0;
+ min.setMin(min, v1);
+ min.setMin(min, v2);
+
+ //get maximum point
+ LLVector4a max = v0;
+ max.setMax(max, v1);
+ max.setMax(max, v2);
+
+ //compute center
+ LLVector4a center;
+ center.setAdd(min, max);
+ center.mul(0.5f);
+
+ tri->mPositionGroup = center;
+
+ //compute "radius"
+ LLVector4a size;
+ size.setSub(max,min);
+
+ tri->mRadius = size.getLength3().getF32() * scaler;
+
+ //insert
+ mOctree->insert(tri);
+ }
+
+ //remove unneeded octree layers
+ while (!mOctree->balance()) { }
+
+ //calculate AABB for each node
+ LLVolumeOctreeRebound rebound(this);
+ rebound.traverse(mOctree);
+
+ if (gDebugGL)
+ {
+ LLVolumeOctreeValidate validate;
+ validate.traverse(mOctree);
+ }
+}
+
+
+void LLVolumeFace::swapData(LLVolumeFace& rhs)
+{
+ llswap(rhs.mPositions, mPositions);
+ llswap(rhs.mNormals, mNormals);
+ llswap(rhs.mBinormals, mBinormals);
+ llswap(rhs.mTexCoords, mTexCoords);
+ llswap(rhs.mIndices,mIndices);
+ llswap(rhs.mNumVertices, mNumVertices);
+ llswap(rhs.mNumIndices, mNumIndices);
+}
+
+void LerpPlanarVertex(LLVolumeFace::VertexData& v0,
+ LLVolumeFace::VertexData& v1,
+ LLVolumeFace::VertexData& v2,
+ LLVolumeFace::VertexData& vout,
+ F32 coef01,
+ F32 coef02)
+{
+
+ LLVector4a lhs;
+ lhs.setSub(v1.getPosition(), v0.getPosition());
+ lhs.mul(coef01);
+ LLVector4a rhs;
+ rhs.setSub(v2.getPosition(), v0.getPosition());
+ rhs.mul(coef02);
+
+ rhs.add(lhs);
+ rhs.add(v0.getPosition());
+
+ vout.setPosition(rhs);
+
+ vout.mTexCoord = v0.mTexCoord + ((v1.mTexCoord-v0.mTexCoord)*coef01)+((v2.mTexCoord-v0.mTexCoord)*coef02);
+ vout.setNormal(v0.getNormal());
+}
+
+BOOL LLVolumeFace::createUnCutCubeCap(LLVolume* volume, BOOL partial_build)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ const std::vector<LLVolume::Point>& mesh = volume->getMesh();
+ const std::vector<LLVector3>& profile = volume->getProfile().mProfile;
+ S32 max_s = volume->getProfile().getTotal();
+ S32 max_t = volume->getPath().mPath.size();
+
+ // S32 i;
+ S32 num_vertices = 0, num_indices = 0;
+ S32 grid_size = (profile.size()-1)/4;
+ S32 quad_count = (grid_size * grid_size);
+
+ num_vertices = (grid_size+1)*(grid_size+1);
+ num_indices = quad_count * 4;
+
+ LLVector4a& min = mExtents[0];
+ LLVector4a& max = mExtents[1];
+
+ S32 offset = 0;
+ if (mTypeMask & TOP_MASK)
+ {
+ offset = (max_t-1) * max_s;
+ }
+ else
+ {
+ offset = mBeginS;
+ }
+
+ {
+ VertexData corners[4];
+ VertexData baseVert;
+ for(S32 t = 0; t < 4; t++)
+ {
+ corners[t].getPosition().load3( mesh[offset + (grid_size*t)].mPos.mV);
+ corners[t].mTexCoord.mV[0] = profile[grid_size*t].mV[0]+0.5f;
+ corners[t].mTexCoord.mV[1] = 0.5f - profile[grid_size*t].mV[1];
+ }
+
+ {
+ LLVector4a lhs;
+ lhs.setSub(corners[1].getPosition(), corners[0].getPosition());
+ LLVector4a rhs;
+ rhs.setSub(corners[2].getPosition(), corners[1].getPosition());
+ baseVert.getNormal().setCross3(lhs, rhs);
+ baseVert.getNormal().normalize3fast();
+ }
+
+ if(!(mTypeMask & TOP_MASK))
+ {
+ baseVert.getNormal().mul(-1.0f);
+ }
+ else
+ {
+ //Swap the UVs on the U(X) axis for top face
+ LLVector2 swap;
+ swap = corners[0].mTexCoord;
+ corners[0].mTexCoord=corners[3].mTexCoord;
+ corners[3].mTexCoord=swap;
+ swap = corners[1].mTexCoord;
+ corners[1].mTexCoord=corners[2].mTexCoord;
+ corners[2].mTexCoord=swap;
+ }
+
+ LLVector4a binormal;
+
+ calc_binormal_from_triangle( binormal,
+ corners[0].getPosition(), corners[0].mTexCoord,
+ corners[1].getPosition(), corners[1].mTexCoord,
+ corners[2].getPosition(), corners[2].mTexCoord);
+
+ binormal.normalize3fast();
+
+ S32 size = (grid_size+1)*(grid_size+1);
+ resizeVertices(size);
+ allocateBinormals(size);
+
+ LLVector4a* pos = (LLVector4a*) mPositions;
+ LLVector4a* norm = (LLVector4a*) mNormals;
+ LLVector4a* binorm = (LLVector4a*) mBinormals;
+ LLVector2* tc = (LLVector2*) mTexCoords;
+
+ for(int gx = 0;gx<grid_size+1;gx++)
+ {
+ for(int gy = 0;gy<grid_size+1;gy++)
+ {
+ VertexData newVert;
+ LerpPlanarVertex(
+ corners[0],
+ corners[1],
+ corners[3],
+ newVert,
+ (F32)gx/(F32)grid_size,
+ (F32)gy/(F32)grid_size);
+
+ *pos++ = newVert.getPosition();
+ *norm++ = baseVert.getNormal();
+ *tc++ = newVert.mTexCoord;
+ *binorm++ = binormal;
+
+ if (gx == 0 && gy == 0)
+ {
+ min = newVert.getPosition();
+ max = min;
+ }
+ else
+ {
+ min.setMin(min, newVert.getPosition());
+ max.setMax(max, newVert.getPosition());
+ }
+ }
+ }
+
+ mCenter->setAdd(min, max);
+ mCenter->mul(0.5f);
+ }
+
+ if (!partial_build)
+ {
+ resizeIndices(grid_size*grid_size*6);
+
+ U16* out = mIndices;
+
+ S32 idxs[] = {0,1,(grid_size+1)+1,(grid_size+1)+1,(grid_size+1),0};
+ for(S32 gx = 0;gx<grid_size;gx++)
+ {
+
+ for(S32 gy = 0;gy<grid_size;gy++)
+ {
+ if (mTypeMask & TOP_MASK)
+ {
+ for(S32 i=5;i>=0;i--)
+ {
+ *out++ = ((gy*(grid_size+1))+gx+idxs[i]);
+ }
+ }
+ else
+ {
+ for(S32 i=0;i<6;i++)
+ {
+ *out++ = ((gy*(grid_size+1))+gx+idxs[i]);
+ }
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ if (!(mTypeMask & HOLLOW_MASK) &&
+ !(mTypeMask & OPEN_MASK) &&
+ ((volume->getParams().getPathParams().getBegin()==0.0f)&&
+ (volume->getParams().getPathParams().getEnd()==1.0f))&&
+ (volume->getParams().getProfileParams().getCurveType()==LL_PCODE_PROFILE_SQUARE &&
+ volume->getParams().getPathParams().getCurveType()==LL_PCODE_PATH_LINE)
+ ){
+ return createUnCutCubeCap(volume, partial_build);
+ }
+
+ S32 num_vertices = 0, num_indices = 0;
+
+ const std::vector<LLVolume::Point>& mesh = volume->getMesh();
+ const std::vector<LLVector3>& profile = volume->getProfile().mProfile;
+
+ // All types of caps have the same number of vertices and indices
+ num_vertices = profile.size();
+ num_indices = (profile.size() - 2)*3;
+
+ if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK))
+ {
+ resizeVertices(num_vertices+1);
+ allocateBinormals(num_vertices+1);
+
+ if (!partial_build)
+ {
+ resizeIndices(num_indices+3);
+ }
+ }
+ else
+ {
+ resizeVertices(num_vertices);
+ allocateBinormals(num_vertices);
+
+ if (!partial_build)
+ {
+ resizeIndices(num_indices);
+ }
+ }
+
+ S32 max_s = volume->getProfile().getTotal();
+ S32 max_t = volume->getPath().mPath.size();
+
+ mCenter->clear();
+
+ S32 offset = 0;
+ if (mTypeMask & TOP_MASK)
+ {
+ offset = (max_t-1) * max_s;
+ }
+ else
+ {
+ offset = mBeginS;
+ }
+
+ // Figure out the normal, assume all caps are flat faces.
+ // Cross product to get normals.
+
+ LLVector2 cuv;
+ LLVector2 min_uv, max_uv;
+
+ LLVector4a& min = mExtents[0];
+ LLVector4a& max = mExtents[1];
+
+ LLVector2* tc = (LLVector2*) mTexCoords;
+ LLVector4a* pos = (LLVector4a*) mPositions;
+ LLVector4a* norm = (LLVector4a*) mNormals;
+ LLVector4a* binorm = (LLVector4a*) mBinormals;
+
+ // Copy the vertices into the array
+ for (S32 i = 0; i < num_vertices; i++)
+ {
+ if (mTypeMask & TOP_MASK)
+ {
+ tc[i].mV[0] = profile[i].mV[0]+0.5f;
+ tc[i].mV[1] = profile[i].mV[1]+0.5f;
+ }
+ else
+ {
+ // Mirror for underside.
+ tc[i].mV[0] = profile[i].mV[0]+0.5f;
+ tc[i].mV[1] = 0.5f - profile[i].mV[1];
+ }
+
+ pos[i].load3(mesh[i + offset].mPos.mV);
+
+ if (i == 0)
+ {
+ max = pos[i];
+ min = max;
+ min_uv = max_uv = tc[i];
+ }
+ else
+ {
+ update_min_max(min,max,pos[i]);
+ update_min_max(min_uv, max_uv, tc[i]);
+ }
+ }
+
+ mCenter->setAdd(min, max);
+ mCenter->mul(0.5f);
+
+ cuv = (min_uv + max_uv)*0.5f;
+
+ LLVector4a binormal;
+ calc_binormal_from_triangle(binormal,
+ *mCenter, cuv,
+ pos[0], tc[0],
+ pos[1], tc[1]);
+ binormal.normalize3fast();
+
+ LLVector4a normal;
+ LLVector4a d0, d1;
+
+
+ d0.setSub(*mCenter, pos[0]);
+ d1.setSub(*mCenter, pos[1]);
+
+ if (mTypeMask & TOP_MASK)
+ {
+ normal.setCross3(d0, d1);
+ }
+ else
+ {
+ normal.setCross3(d1, d0);
+ }
+
+ normal.normalize3fast();
+
+ VertexData vd;
+ vd.setPosition(*mCenter);
+ vd.mTexCoord = cuv;
+
+ if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK))
+ {
+ pos[num_vertices] = *mCenter;
+ tc[num_vertices] = cuv;
+ num_vertices++;
+ }
+
+ for (S32 i = 0; i < num_vertices; i++)
+ {
+ binorm[i].load4a(binormal.getF32ptr());
+ norm[i].load4a(normal.getF32ptr());
+ }
+
+ if (partial_build)
+ {
+ return TRUE;
+ }
+
+ if (mTypeMask & HOLLOW_MASK)
+ {
+ if (mTypeMask & TOP_MASK)
+ {
+ // HOLLOW TOP
+ // Does it matter if it's open or closed? - djs
+
+ S32 pt1 = 0, pt2 = num_vertices - 1;
+ S32 i = 0;
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ LLVector3 p1 = profile[pt1];
+ LLVector3 p2 = profile[pt2];
+ LLVector3 pa = profile[pt1+1];
+ LLVector3 pb = profile[pt2-1];
+
+ p1.mV[VZ] = 0.f;
+ p2.mV[VZ] = 0.f;
+ pa.mV[VZ] = 0.f;
+ pb.mV[VZ] = 0.f;
+
+ // Use area of triangle to determine backfacing
+ F32 area_1a2, area_1ba, area_21b, area_2ab;
+ area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+ (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+ (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+
+ area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+ (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+
+ area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+ (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+ (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ BOOL use_tri1a2 = TRUE;
+ BOOL tri_1a2 = TRUE;
+ BOOL tri_21b = TRUE;
+
+ if (area_1a2 < 0)
+ {
+ tri_1a2 = FALSE;
+ }
+ if (area_2ab < 0)
+ {
+ // Can't use, because it contains point b
+ tri_1a2 = FALSE;
+ }
+ if (area_21b < 0)
+ {
+ tri_21b = FALSE;
+ }
+ if (area_1ba < 0)
+ {
+ // Can't use, because it contains point b
+ tri_21b = FALSE;
+ }
+
+ if (!tri_1a2)
+ {
+ use_tri1a2 = FALSE;
+ }
+ else if (!tri_21b)
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ LLVector3 d1 = p1 - pa;
+ LLVector3 d2 = p2 - pb;
+
+ if (d1.magVecSquared() < d2.magVecSquared())
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ use_tri1a2 = FALSE;
+ }
+ }
+
+ if (use_tri1a2)
+ {
+ mIndices[i++] = pt1;
+ mIndices[i++] = pt1 + 1;
+ mIndices[i++] = pt2;
+ pt1++;
+ }
+ else
+ {
+ mIndices[i++] = pt1;
+ mIndices[i++] = pt2 - 1;
+ mIndices[i++] = pt2;
+ pt2--;
+ }
+ }
+ }
+ else
+ {
+ // HOLLOW BOTTOM
+ // Does it matter if it's open or closed? - djs
+
+ llassert(mTypeMask & BOTTOM_MASK);
+ S32 pt1 = 0, pt2 = num_vertices - 1;
+
+ S32 i = 0;
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ LLVector3 p1 = profile[pt1];
+ LLVector3 p2 = profile[pt2];
+ LLVector3 pa = profile[pt1+1];
+ LLVector3 pb = profile[pt2-1];
+
+ p1.mV[VZ] = 0.f;
+ p2.mV[VZ] = 0.f;
+ pa.mV[VZ] = 0.f;
+ pb.mV[VZ] = 0.f;
+
+ // Use area of triangle to determine backfacing
+ F32 area_1a2, area_1ba, area_21b, area_2ab;
+ area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+ (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+ (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+
+ area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+ (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+
+ area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+ (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+ (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ BOOL use_tri1a2 = TRUE;
+ BOOL tri_1a2 = TRUE;
+ BOOL tri_21b = TRUE;
+
+ if (area_1a2 < 0)
+ {
+ tri_1a2 = FALSE;
+ }
+ if (area_2ab < 0)
+ {
+ // Can't use, because it contains point b
+ tri_1a2 = FALSE;
+ }
+ if (area_21b < 0)
+ {
+ tri_21b = FALSE;
+ }
+ if (area_1ba < 0)
+ {
+ // Can't use, because it contains point b
+ tri_21b = FALSE;
+ }
+
+ if (!tri_1a2)
+ {
+ use_tri1a2 = FALSE;
+ }
+ else if (!tri_21b)
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ LLVector3 d1 = p1 - pa;
+ LLVector3 d2 = p2 - pb;
+
+ if (d1.magVecSquared() < d2.magVecSquared())
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ use_tri1a2 = FALSE;
+ }
+ }
+
+ // Flipped backfacing from top
+ if (use_tri1a2)
+ {
+ mIndices[i++] = pt1;
+ mIndices[i++] = pt2;
+ mIndices[i++] = pt1 + 1;
+ pt1++;
+ }
+ else
+ {
+ mIndices[i++] = pt1;
+ mIndices[i++] = pt2;
+ mIndices[i++] = pt2 - 1;
+ pt2--;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Not hollow, generate the triangle fan.
+ U16 v1 = 2;
+ U16 v2 = 1;
+
+ if (mTypeMask & TOP_MASK)
+ {
+ v1 = 1;
+ v2 = 2;
+ }
+
+ for (S32 i = 0; i < (num_vertices - 2); i++)
+ {
+ mIndices[3*i] = num_vertices - 1;
+ mIndices[3*i+v1] = i;
+ mIndices[3*i+v2] = i + 1;
+ }
+
+
+ }
+
+ return TRUE;
+}
+
+void LLVolumeFace::createBinormals()
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ if (!mBinormals)
+ {
+ allocateBinormals(mNumVertices);
+
+ //generate binormals
+ LLVector4a* pos = mPositions;
+ LLVector2* tc = (LLVector2*) mTexCoords;
+ LLVector4a* binorm = (LLVector4a*) mBinormals;
+
+ LLVector4a* end = mBinormals+mNumVertices;
+ while (binorm < end)
+ {
+ (*binorm++).clear();
+ }
+
+ binorm = mBinormals;
+
+ for (U32 i = 0; i < mNumIndices/3; i++)
+ { //for each triangle
+ const U16& i0 = mIndices[i*3+0];
+ const U16& i1 = mIndices[i*3+1];
+ const U16& i2 = mIndices[i*3+2];
+
+ //calculate binormal
+ LLVector4a binormal;
+ calc_binormal_from_triangle(binormal,
+ pos[i0], tc[i0],
+ pos[i1], tc[i1],
+ pos[i2], tc[i2]);
+
+
+ //add triangle normal to vertices
+ binorm[i0].add(binormal);
+ binorm[i1].add(binormal);
+ binorm[i2].add(binormal);
+
+ //even out quad contributions
+ if (i % 2 == 0)
+ {
+ binorm[i2].add(binormal);
+ }
+ else
+ {
+ binorm[i1].add(binormal);
+ }
+ }
+
+ //normalize binormals
+ for (U32 i = 0; i < mNumVertices; i++)
+ {
+ binorm[i].normalize3fast();
+ //bump map/planar projection code requires normals to be normalized
+ mNormals[i].normalize3fast();
+ }
+ }
+}
+
+void LLVolumeFace::resizeVertices(S32 num_verts)
+{
+ free(mPositions);
+ free(mNormals);
+ free(mBinormals);
+ free(mTexCoords);
+
+ mBinormals = NULL;
+
+ if (num_verts)
+ {
+ mPositions = (LLVector4a*) malloc(sizeof(LLVector4a)*num_verts);
+ assert_aligned(mPositions, 16);
+ mNormals = (LLVector4a*) malloc(sizeof(LLVector4a)*num_verts);
+ assert_aligned(mNormals, 16);
+
+ //pad texture coordinate block end to allow for QWORD reads
+ S32 size = ((num_verts*sizeof(LLVector2)) + 0xF) & ~0xF;
+ mTexCoords = (LLVector2*) malloc(size);
+ assert_aligned(mTexCoords, 16);
+ }
+ else
+ {
+ mPositions = NULL;
+ mNormals = NULL;
+ mTexCoords = NULL;
+ }
+
+ mNumVertices = num_verts;
+}
+
+void LLVolumeFace::pushVertex(const LLVolumeFace::VertexData& cv)
+{
+ pushVertex(cv.getPosition(), cv.getNormal(), cv.mTexCoord);
+}
+
+void LLVolumeFace::pushVertex(const LLVector4a& pos, const LLVector4a& norm, const LLVector2& tc)
+{
+ S32 new_verts = mNumVertices+1;
+ S32 new_size = new_verts*16;
+// S32 old_size = mNumVertices*16;
+
+ //positions
+ mPositions = (LLVector4a*) realloc(mPositions, new_size);
+
+ //normals
+ mNormals = (LLVector4a*) realloc(mNormals, new_size);
+
+ //tex coords
+ new_size = ((new_verts*8)+0xF) & ~0xF;
+ mTexCoords = (LLVector2*) realloc(mTexCoords, new_size);
+
+
+ //just clear binormals
+ free(mBinormals);
+ mBinormals = NULL;
+
+ mPositions[mNumVertices] = pos;
+ mNormals[mNumVertices] = norm;
+ mTexCoords[mNumVertices] = tc;
+
+ mNumVertices++;
+}
+
+void LLVolumeFace::allocateBinormals(S32 num_verts)
+{
+ free(mBinormals);
+ mBinormals = (LLVector4a*) malloc(sizeof(LLVector4a)*num_verts);
+}
+
+void LLVolumeFace::allocateWeights(S32 num_verts)
+{
+ free(mWeights);
+ mWeights = (LLVector4a*) malloc(sizeof(LLVector4a)*num_verts);
+}
+
+void LLVolumeFace::resizeIndices(S32 num_indices)
+{
+ free(mIndices);
+
+ if (num_indices)
+ {
+ //pad index block end to allow for QWORD reads
+ S32 size = ((num_indices*sizeof(U16)) + 0xF) & ~0xF;
+
+ mIndices = (U16*) malloc(size);
+ }
+ else
+ {
+ mIndices = NULL;
+ }
+
+ mNumIndices = num_indices;
+}
+
+void LLVolumeFace::pushIndex(const U16& idx)
+{
+ S32 new_count = mNumIndices + 1;
+ S32 new_size = ((new_count*2)+0xF) & ~0xF;
+
+ S32 old_size = ((mNumIndices*2)+0xF) & ~0xF;
+ if (new_size != old_size)
+ {
+ mIndices = (U16*) realloc(mIndices, new_size);
+ }
+
+ mIndices[mNumIndices++] = idx;
+}
+
+void LLVolumeFace::fillFromLegacyData(std::vector<LLVolumeFace::VertexData>& v, std::vector<U16>& idx)
+{
+ resizeVertices(v.size());
+ resizeIndices(idx.size());
+
+ for (U32 i = 0; i < v.size(); ++i)
+ {
+ mPositions[i] = v[i].getPosition();
+ mNormals[i] = v[i].getNormal();
+ mTexCoords[i] = v[i].mTexCoord;
+ }
+
+ for (U32 i = 0; i < idx.size(); ++i)
+ {
+ mIndices[i] = idx[i];
+ }
+}
+
+void LLVolumeFace::appendFace(const LLVolumeFace& face, LLMatrix4& mat_in, LLMatrix4& norm_mat_in)
+{
+ U16 offset = mNumVertices;
+
+ S32 new_count = face.mNumVertices + mNumVertices;
+
+ if (new_count > 65536)
+ {
+ llerrs << "Cannot append face -- 16-bit overflow will occur." << llendl;
+ }
+
+ if (face.mNumVertices == 0)
+ {
+ llerrs << "Cannot append empty face." << llendl;
+ }
+
+ //allocate new buffer space
+ mPositions = (LLVector4a*) realloc(mPositions, new_count*sizeof(LLVector4a));
+ assert_aligned(mPositions, 16);
+ mNormals = (LLVector4a*) realloc(mNormals, new_count*sizeof(LLVector4a));
+ assert_aligned(mNormals, 16);
+ mTexCoords = (LLVector2*) realloc(mTexCoords, (new_count*sizeof(LLVector2)+0xF) & ~0xF);
+ assert_aligned(mTexCoords, 16);
+
+ mNumVertices = new_count;
+
+ //get destination address of appended face
+ LLVector4a* dst_pos = mPositions+offset;
+ LLVector2* dst_tc = mTexCoords+offset;
+ LLVector4a* dst_norm = mNormals+offset;
+
+ //get source addresses of appended face
+ const LLVector4a* src_pos = face.mPositions;
+ const LLVector2* src_tc = face.mTexCoords;
+ const LLVector4a* src_norm = face.mNormals;
+
+ //load aligned matrices
+ LLMatrix4a mat, norm_mat;
+ mat.loadu(mat_in);
+ norm_mat.loadu(norm_mat_in);
+
+ for (U32 i = 0; i < face.mNumVertices; ++i)
+ {
+ //transform appended face position and store
+ mat.affineTransform(src_pos[i], dst_pos[i]);
+
+ //transform appended face normal and store
+ norm_mat.rotate(src_norm[i], dst_norm[i]);
+ dst_norm[i].normalize3fast();
+
+ //copy appended face texture coordinate
+ dst_tc[i] = src_tc[i];
+
+ if (offset == 0 && i == 0)
+ { //initialize bounding box
+ mExtents[0] = mExtents[1] = dst_pos[i];
+ }
+ else
+ {
+ //stretch bounding box
+ update_min_max(mExtents[0], mExtents[1], dst_pos[i]);
+ }
+ }
+
+
+ new_count = mNumIndices + face.mNumIndices;
+
+ //allocate new index buffer
+ mIndices = (U16*) realloc(mIndices, (new_count*sizeof(U16)+0xF) & ~0xF);
+
+ //get destination address into new index buffer
+ U16* dst_idx = mIndices+mNumIndices;
+ mNumIndices = new_count;
+
+ for (U32 i = 0; i < face.mNumIndices; ++i)
+ { //copy indices, offsetting by old vertex count
+ dst_idx[i] = face.mIndices[i]+offset;
+ }
+}
+
+BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
+{
+ LLMemType m1(LLMemType::MTYPE_VOLUME);
+
+ BOOL flat = mTypeMask & FLAT_MASK;
+
+ U8 sculpt_type = volume->getParams().getSculptType();
+ U8 sculpt_stitching = sculpt_type & LL_SCULPT_TYPE_MASK;
+ BOOL sculpt_invert = sculpt_type & LL_SCULPT_FLAG_INVERT;
+ BOOL sculpt_mirror = sculpt_type & LL_SCULPT_FLAG_MIRROR;
+ BOOL sculpt_reverse_horizontal = (sculpt_invert ? !sculpt_mirror : sculpt_mirror); // XOR
+
+ S32 num_vertices, num_indices;
+
+ const std::vector<LLVolume::Point>& mesh = volume->getMesh();
+ const std::vector<LLVector3>& profile = volume->getProfile().mProfile;
+ const std::vector<LLPath::PathPt>& path_data = volume->getPath().mPath;
+
+ S32 max_s = volume->getProfile().getTotal();
+
+ S32 s, t, i;
+ F32 ss, tt;
+
+ num_vertices = mNumS*mNumT;
+ num_indices = (mNumS-1)*(mNumT-1)*6;
+
+ if (!partial_build)
+ {
+ resizeVertices(num_vertices);
+ resizeIndices(num_indices);
+
+ if ((volume->getParams().getSculptType() & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_MESH)
+ {
+ mEdge.resize(num_indices);
+ }
+ }
+
+ LLVector4a* pos = (LLVector4a*) mPositions;
+ LLVector4a* norm = (LLVector4a*) mNormals;
+ LLVector2* tc = (LLVector2*) mTexCoords;
+ S32 begin_stex = llfloor( profile[mBeginS].mV[2] );
+ S32 num_s = ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2) ? mNumS/2 : mNumS;
+
+ S32 cur_vertex = 0;
+ // Copy the vertices into the array
+ for (t = mBeginT; t < mBeginT + mNumT; t++)
+ {
+ tt = path_data[t].mTexT;
+ for (s = 0; s < num_s; s++)
+ {
+ if (mTypeMask & END_MASK)
+ {
+ if (s)
+ {
+ ss = 1.f;
+ }
+ else
+ {
+ ss = 0.f;
+ }
+ }
+ else
+ {
+ // Get s value for tex-coord.
+ if (!flat)
+ {
+ ss = profile[mBeginS + s].mV[2];
+ }
+ else
+ {
+ ss = profile[mBeginS + s].mV[2] - begin_stex;
+ }
+ }
+
+ if (sculpt_reverse_horizontal)
+ {
+ ss = 1.f - ss;
+ }
+
+ // Check to see if this triangle wraps around the array.
+ if (mBeginS + s >= max_s)
+ {
+ // We're wrapping
+ i = mBeginS + s + max_s*(t-1);
+ }
+ else
+ {
+ i = mBeginS + s + max_s*t;
+ }
+
+ pos[cur_vertex].load3(mesh[i].mPos.mV);
+ tc[cur_vertex] = LLVector2(ss,tt);
+
+ norm[cur_vertex].clear();
+ cur_vertex++;
+
+ if ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2 && s > 0)
+ {
+
+ pos[cur_vertex].load3(mesh[i].mPos.mV);
+ tc[cur_vertex] = LLVector2(ss,tt);
+
+ norm[cur_vertex].clear();
+
+ cur_vertex++;
+ }
+ }
+
+ if ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2)
+ {
+ if (mTypeMask & OPEN_MASK)
+ {
+ s = num_s-1;
+ }
+ else
+ {
+ s = 0;
+ }
+
+ i = mBeginS + s + max_s*t;
+ ss = profile[mBeginS + s].mV[2] - begin_stex;
+ pos[cur_vertex].load3(mesh[i].mPos.mV);
+ tc[cur_vertex] = LLVector2(ss,tt);
+ norm[cur_vertex].clear();
+
+ cur_vertex++;
+ }
+ }
+
+
+ //get bounding box for this side
+ LLVector4a& face_min = mExtents[0];
+ LLVector4a& face_max = mExtents[1];
+ mCenter->clear();
+
+ face_min = face_max = pos[0];
+
+ for (U32 i = 1; i < mNumVertices; ++i)
+ {
+ update_min_max(face_min, face_max, pos[i]);
+ }
+
+ mCenter->setAdd(face_min, face_max);
+ mCenter->mul(0.5f);
+
+ S32 cur_index = 0;
+ S32 cur_edge = 0;
+ BOOL flat_face = mTypeMask & FLAT_MASK;
+
+ if (!partial_build)
+ {
+ // Now we generate the indices.
+ for (t = 0; t < (mNumT-1); t++)
+ {
+ for (s = 0; s < (mNumS-1); s++)
+ {
+ mIndices[cur_index++] = s + mNumS*t; //bottom left
+ mIndices[cur_index++] = s+1 + mNumS*(t+1); //top right
+ mIndices[cur_index++] = s + mNumS*(t+1); //top left
+ mIndices[cur_index++] = s + mNumS*t; //bottom left
+ mIndices[cur_index++] = s+1 + mNumS*t; //bottom right
+ mIndices[cur_index++] = s+1 + mNumS*(t+1); //top right
+
+ mEdge[cur_edge++] = (mNumS-1)*2*t+s*2+1; //bottom left/top right neighbor face
+ if (t < mNumT-2) { //top right/top left neighbor face
+ mEdge[cur_edge++] = (mNumS-1)*2*(t+1)+s*2+1;
+ }
+ else if (mNumT <= 3 || volume->getPath().isOpen() == TRUE) { //no neighbor
+ mEdge[cur_edge++] = -1;
+ }
+ else { //wrap on T
+ mEdge[cur_edge++] = s*2+1;
+ }
+ if (s > 0) { //top left/bottom left neighbor face
+ mEdge[cur_edge++] = (mNumS-1)*2*t+s*2-1;
+ }
+ else if (flat_face || volume->getProfile().isOpen() == TRUE) { //no neighbor
+ mEdge[cur_edge++] = -1;
+ }
+ else { //wrap on S
+ mEdge[cur_edge++] = (mNumS-1)*2*t+(mNumS-2)*2+1;
+ }
+
+ if (t > 0) { //bottom left/bottom right neighbor face
+ mEdge[cur_edge++] = (mNumS-1)*2*(t-1)+s*2;
+ }
+ else if (mNumT <= 3 || volume->getPath().isOpen() == TRUE) { //no neighbor
+ mEdge[cur_edge++] = -1;
+ }
+ else { //wrap on T
+ mEdge[cur_edge++] = (mNumS-1)*2*(mNumT-2)+s*2;
+ }
+ if (s < mNumS-2) { //bottom right/top right neighbor face
+ mEdge[cur_edge++] = (mNumS-1)*2*t+(s+1)*2;
+ }
+ else if (flat_face || volume->getProfile().isOpen() == TRUE) { //no neighbor
+ mEdge[cur_edge++] = -1;
+ }
+ else { //wrap on S
+ mEdge[cur_edge++] = (mNumS-1)*2*t;
+ }
+ mEdge[cur_edge++] = (mNumS-1)*2*t+s*2; //top right/bottom left neighbor face
+ }
+ }
+ }
+
+ //clear normals
+ for (U32 i = 0; i < mNumVertices; i++)
+ {
+ mNormals[i].clear();
+ }
+
+ //generate normals
+ for (U32 i = 0; i < mNumIndices/3; i++) //for each triangle
+ {
+ const U16* idx = &(mIndices[i*3]);
+
+
+ LLVector4a* v[] =
+ { pos+idx[0], pos+idx[1], pos+idx[2] };
+
+ LLVector4a* n[] =
+ { norm+idx[0], norm+idx[1], norm+idx[2] };
+
+ //calculate triangle normal
+ LLVector4a a, b, c;
+
+ a.setSub(*v[0], *v[1]);
+ b.setSub(*v[0], *v[2]);
+ c.setCross3(a,b);
+
+ n[0]->add(c);
+ n[1]->add(c);
+ n[2]->add(c);
+
+ //even out quad contributions
+ n[i%2+1]->add(c);
+ }
+
+ // adjust normals based on wrapping and stitching
+
+ LLVector4a top;
+ top.setSub(pos[0], pos[mNumS*(mNumT-2)]);
+ BOOL s_bottom_converges = (top.dot3(top) < 0.000001f);
+
+ top.setSub(pos[mNumS-1], pos[mNumS*(mNumT-2)+mNumS-1]);
+ BOOL s_top_converges = (top.dot3(top) < 0.000001f);
+
+ if (sculpt_stitching == LL_SCULPT_TYPE_NONE) // logic for non-sculpt volumes
+ {
+ if (volume->getPath().isOpen() == FALSE)
+ { //wrap normals on T
+ for (S32 i = 0; i < mNumS; i++)
+ {
+ LLVector4a n;
+ n.setAdd(norm[i], norm[mNumS*(mNumT-1)+i]);
+ norm[i] = n;
+ norm[mNumS*(mNumT-1)+i] = n;
+ }
+ }
+
+ if ((volume->getProfile().isOpen() == FALSE) && !(s_bottom_converges))
+ { //wrap normals on S
+ for (S32 i = 0; i < mNumT; i++)
+ {
+ LLVector4a n;
+ n.setAdd(norm[mNumS*i], norm[mNumS*i+mNumS-1]);
+ norm[mNumS * i] = n;
+ norm[mNumS * i+mNumS-1] = n;
+ }
+ }
+
+ if (volume->getPathType() == LL_PCODE_PATH_CIRCLE &&
+ ((volume->getProfileType() & LL_PCODE_PROFILE_MASK) == LL_PCODE_PROFILE_CIRCLE_HALF))
+ {
+ if (s_bottom_converges)
+ { //all lower S have same normal
+ for (S32 i = 0; i < mNumT; i++)
+ {
+ norm[mNumS*i].set(1,0,0);
+ }
+ }
+
+ if (s_top_converges)
+ { //all upper S have same normal
+ for (S32 i = 0; i < mNumT; i++)
+ {
+ norm[mNumS*i+mNumS-1].set(-1,0,0);
+ }
+ }
+ }
+ }
+ else // logic for sculpt volumes
+ {
+ BOOL average_poles = FALSE;
+ BOOL wrap_s = FALSE;
+ BOOL wrap_t = FALSE;
+
+ if (sculpt_stitching == LL_SCULPT_TYPE_SPHERE)
+ average_poles = TRUE;
+
+ if ((sculpt_stitching == LL_SCULPT_TYPE_SPHERE) ||
+ (sculpt_stitching == LL_SCULPT_TYPE_TORUS) ||
+ (sculpt_stitching == LL_SCULPT_TYPE_CYLINDER))
+ wrap_s = TRUE;
+
+ if (sculpt_stitching == LL_SCULPT_TYPE_TORUS)
+ wrap_t = TRUE;
+
+
+ if (average_poles)
+ {
+ // average normals for north pole
+
+ LLVector4a average;
+ average.clear();
+
+ for (S32 i = 0; i < mNumS; i++)
+ {
+ average.add(norm[i]);
+ }
+
+ // set average
+ for (S32 i = 0; i < mNumS; i++)
+ {
+ norm[i] = average;
+ }
+
+ // average normals for south pole
+
+ average.clear();
+
+ for (S32 i = 0; i < mNumS; i++)
+ {
+ average.add(norm[i + mNumS * (mNumT - 1)]);
+ }
+
+ // set average
+ for (S32 i = 0; i < mNumS; i++)
+ {
+ norm[i + mNumS * (mNumT - 1)] = average;
+ }
+
+ }
+
+
+ if (wrap_s)
+ {
+ for (S32 i = 0; i < mNumT; i++)
+ {
+ LLVector4a n;
+ n.setAdd(norm[mNumS*i], norm[mNumS*i+mNumS-1]);
+ norm[mNumS * i] = n;
+ norm[mNumS * i+mNumS-1] = n;
+ }
+ }
+
+ if (wrap_t)
+ {
+ for (S32 i = 0; i < mNumS; i++)
+ {
+ LLVector4a n;
+ n.setAdd(norm[i], norm[mNumS*(mNumT-1)+i]);
+ norm[i] = n;
+ norm[mNumS*(mNumT-1)+i] = n;
+ }
+ }
+
+ }
+
+ return TRUE;
+}
+
+// Finds binormal based on three vertices with texture coordinates.
+// Fills in dummy values if the triangle has degenerate texture coordinates.
+void calc_binormal_from_triangle(LLVector4a& binormal,
+
+ const LLVector4a& pos0,
+ const LLVector2& tex0,
+ const LLVector4a& pos1,
+ const LLVector2& tex1,
+ const LLVector4a& pos2,
+ const LLVector2& tex2)
+{
+ LLVector4a rx0( pos0[VX], tex0.mV[VX], tex0.mV[VY] );
+ LLVector4a rx1( pos1[VX], tex1.mV[VX], tex1.mV[VY] );
+ LLVector4a rx2( pos2[VX], tex2.mV[VX], tex2.mV[VY] );
+
+ LLVector4a ry0( pos0[VY], tex0.mV[VX], tex0.mV[VY] );
+ LLVector4a ry1( pos1[VY], tex1.mV[VX], tex1.mV[VY] );
+ LLVector4a ry2( pos2[VY], tex2.mV[VX], tex2.mV[VY] );
+
+ LLVector4a rz0( pos0[VZ], tex0.mV[VX], tex0.mV[VY] );
+ LLVector4a rz1( pos1[VZ], tex1.mV[VX], tex1.mV[VY] );
+ LLVector4a rz2( pos2[VZ], tex2.mV[VX], tex2.mV[VY] );
+
+ LLVector4a lhs, rhs;
+
+ LLVector4a r0;
+ lhs.setSub(rx0, rx1); rhs.setSub(rx0, rx2);
+ r0.setCross3(lhs, rhs);
+
+ LLVector4a r1;
+ lhs.setSub(ry0, ry1); rhs.setSub(ry0, ry2);
+ r1.setCross3(lhs, rhs);
+
+ LLVector4a r2;
+ lhs.setSub(rz0, rz1); rhs.setSub(rz0, rz2);
+ r2.setCross3(lhs, rhs);
+
+ if( r0[VX] && r1[VX] && r2[VX] )
+ {
+ binormal.set(
+ -r0[VZ] / r0[VX],
+ -r1[VZ] / r1[VX],
+ -r2[VZ] / r2[VX]);
+ // binormal.normVec();
+ }
+ else
+ {
+ binormal.set( 0, 1 , 0 );
+ }
+}
diff --git a/indra/llrender/llglheaders.h b/indra/llrender/llglheaders.h
index c48e2bb5fa..d8140a124d 100644
--- a/indra/llrender/llglheaders.h
+++ b/indra/llrender/llglheaders.h
@@ -840,18 +840,18 @@ extern void glGetBufferPointervARB (GLenum, GLenum, GLvoid* *);
//GL_NVX_gpu_memory_info constants
#ifndef GL_NVX_gpu_memory_info
#define GL_NVX_gpu_memory_info
-#define GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX 0x9047
-#define GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX 0x9048
-#define GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX 0x9049
-#define GL_GPU_MEMORY_INFO_EVICTION_COUNT_NVX 0x904A
+#define GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX 0x9047
+#define GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX 0x9048
+#define GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX 0x9049
+#define GL_GPU_MEMORY_INFO_EVICTION_COUNT_NVX 0x904A
#define GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX 0x904B
#endif
//GL_ATI_meminfo constants
#ifndef GL_ATI_meminfo
#define GL_ATI_meminfo
-#define GL_VBO_FREE_MEMORY_ATI 0x87FB
-#define GL_TEXTURE_FREE_MEMORY_ATI 0x87FC
+#define GL_VBO_FREE_MEMORY_ATI 0x87FB
+#define GL_TEXTURE_FREE_MEMORY_ATI 0x87FC
#define GL_RENDERBUFFER_FREE_MEMORY_ATI 0x87FD
#endif
diff --git a/indra/newview/llphysicsshapebuilderutil.cpp b/indra/newview/llphysicsshapebuilderutil.cpp
index 54d54bfcb9..5bfe5c9941 100644
--- a/indra/newview/llphysicsshapebuilderutil.cpp
+++ b/indra/newview/llphysicsshapebuilderutil.cpp
@@ -1,24 +1,28 @@
/**
-* @file llphysicsshapebuilder.cpp
-* @brief Generic system to convert LL(Physics)VolumeParams to physics shapes
-* @author falcon@lindenlab.com
-*
-* $LicenseInfo:firstyear=2010&license=internal$
-*
-* Copyright (c) 2010, Linden Research, Inc.
-*
-* The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
-* this source code is governed by the Linden Lab Source Code Disclosure
-* Agreement ("Agreement") previously entered between you and Linden
-* Lab. By accessing, using, copying, modifying or distributing this
-* software, you acknowledge that you have been informed of your
-* obligations under the Agreement and agree to abide by those obligations.
-*
-* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
-* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
-* COMPLETENESS OR PERFORMANCE.
-* $/LicenseInfo$
-*/
+ * @file llphysicsshapebuilder.cpp
+ * @brief Generic system to convert LL(Physics)VolumeParams to physics shapes
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
#include "llviewerprecompiledheaders.h"
diff --git a/indra/newview/llphysicsshapebuilderutil.h b/indra/newview/llphysicsshapebuilderutil.h
index 3de9afcb25..7dedfb05e2 100644
--- a/indra/newview/llphysicsshapebuilderutil.h
+++ b/indra/newview/llphysicsshapebuilderutil.h
@@ -1,22 +1,26 @@
/**
* @file llphysicsshapebuilder.h
- * @author falcon@lindenlab.com
* @brief Generic system to convert LL(Physics)VolumeParams to physics shapes
*
- * $LicenseInfo:firstyear=2010&license=internal$
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * Copyright (c) 2010, Linden Research, Inc.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
- * this source code is governed by the Linden Lab Source Code Disclosure
- * Agreement ("Agreement") previously entered between you and Linden
- * Lab. By accessing, using, copying, modifying or distributing this
- * software, you acknowledge that you have been informed of your
- * obligations under the Agreement and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -42,39 +46,39 @@ const F32 SHAPE_BUILDER_ENTRY_SNAP_PARAMETER_BIN_SIZE = 0.010f;
const F32 SHAPE_BUILDER_CONVEXIFICATION_SIZE = 2.f * COLLISION_TOLERANCE;
const F32 SHAPE_BUILDER_MIN_GEOMETRY_SIZE = 0.5f * COLLISION_TOLERANCE;
-class LLPhysicsVolumeParams : public LLVolumeParams
-{
-public:
-
- LLPhysicsVolumeParams( const LLVolumeParams& params, bool forceConvex ) :
- LLVolumeParams( params ),
- mForceConvex(forceConvex) {}
-
- bool operator==(const LLPhysicsVolumeParams &params) const
- {
- return ( LLVolumeParams::operator==(params) && (mForceConvex == params.mForceConvex) );
- }
-
- bool operator!=(const LLPhysicsVolumeParams &params) const
- {
- return !operator==(params);
- }
-
- bool operator<(const LLPhysicsVolumeParams &params) const
- {
- if ( LLVolumeParams::operator!=(params) )
- {
- return LLVolumeParams::operator<(params);
- }
- return (params.mForceConvex == false) && (mForceConvex == true);
- }
-
- bool shouldForceConvex() const { return mForceConvex; }
-
-private:
- bool mForceConvex;
-};
-
+class LLPhysicsVolumeParams : public LLVolumeParams
+{
+public:
+
+ LLPhysicsVolumeParams( const LLVolumeParams& params, bool forceConvex ) :
+ LLVolumeParams( params ),
+ mForceConvex(forceConvex) {}
+
+ bool operator==(const LLPhysicsVolumeParams &params) const
+ {
+ return ( LLVolumeParams::operator==(params) && (mForceConvex == params.mForceConvex) );
+ }
+
+ bool operator!=(const LLPhysicsVolumeParams &params) const
+ {
+ return !operator==(params);
+ }
+
+ bool operator<(const LLPhysicsVolumeParams &params) const
+ {
+ if ( LLVolumeParams::operator!=(params) )
+ {
+ return LLVolumeParams::operator<(params);
+ }
+ return (params.mForceConvex == false) && (mForceConvex == true);
+ }
+
+ bool shouldForceConvex() const { return mForceConvex; }
+
+private:
+ bool mForceConvex;
+};
+
class LLPhysicsShapeBuilderUtil
{
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index fdf4f5bdaa..295799fd24 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -1,1090 +1,1090 @@
-/**
- * @file llvoavatar.h
- * @brief Declaration of LLVOAvatar class which is a derivation of
- * LLViewerObject
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLVOAVATAR_H
-#define LL_LLVOAVATAR_H
-
-#include <map>
-#include <deque>
-#include <string>
-#include <vector>
-
-#include <boost/signals2.hpp>
-
-#include "imageids.h" // IMG_INVISIBLE
-#include "llchat.h"
-#include "lldrawpoolalpha.h"
-#include "llviewerobject.h"
-#include "llcharacter.h"
-#include "llviewerjointmesh.h"
-#include "llviewerjointattachment.h"
-#include "llrendertarget.h"
-#include "llvoavatardefines.h"
-#include "lltexglobalcolor.h"
-#include "lldriverparam.h"
-#include "material_codes.h" // LL_MCODE_END
-
-extern const LLUUID ANIM_AGENT_BODY_NOISE;
-extern const LLUUID ANIM_AGENT_BREATHE_ROT;
-extern const LLUUID ANIM_AGENT_PHYSICS_MOTION;
-extern const LLUUID ANIM_AGENT_EDITING;
-extern const LLUUID ANIM_AGENT_EYE;
-extern const LLUUID ANIM_AGENT_FLY_ADJUST;
-extern const LLUUID ANIM_AGENT_HAND_MOTION;
-extern const LLUUID ANIM_AGENT_HEAD_ROT;
-extern const LLUUID ANIM_AGENT_PELVIS_FIX;
-extern const LLUUID ANIM_AGENT_TARGET;
-extern const LLUUID ANIM_AGENT_WALK_ADJUST;
-
-class LLTexLayerSet;
-class LLVoiceVisualizer;
-class LLHUDNameTag;
-class LLHUDEffectSpiral;
-class LLTexGlobalColor;
-class LLVOAvatarBoneInfo;
-class LLVOAvatarSkeletonInfo;
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// LLVOAvatar
-//
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class LLVOAvatar :
- public LLViewerObject,
- public LLCharacter,
- public boost::signals2::trackable
-{
-public:
- friend class LLVOAvatarSelf;
-protected:
- struct LLVOAvatarXmlInfo;
- struct LLMaskedMorph;
-
-/********************************************************************************
- ** **
- ** INITIALIZATION
- **/
-
-public:
- LLVOAvatar(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
- virtual void markDead();
- static void initClass(); // Initialize data that's only init'd once per class.
- static void cleanupClass(); // Cleanup data that's only init'd once per class.
- virtual void initInstance(); // Called after construction to initialize the class.
-protected:
- virtual ~LLVOAvatar();
- BOOL loadSkeletonNode();
- BOOL loadMeshNodes();
- virtual BOOL loadLayersets();
-
-/** Initialization
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** INHERITED
- **/
-
- //--------------------------------------------------------------------
- // LLViewerObject interface and related
- //--------------------------------------------------------------------
-public:
- virtual void updateGL();
- virtual LLVOAvatar* asAvatar();
- virtual U32 processUpdateMessage(LLMessageSystem *mesgsys,
- void **user_data,
- U32 block_num,
- const EObjectUpdateType update_type,
- LLDataPacker *dp);
- virtual BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
- virtual BOOL updateLOD();
- BOOL updateJointLODs();
- void updateLODRiggedAttachments( void );
- virtual BOOL isActive() const; // Whether this object needs to do an idleUpdate.
- virtual void updateTextures();
- virtual S32 setTETexture(const U8 te, const LLUUID& uuid); // If setting a baked texture, need to request it from a non-local sim.
- virtual void onShift(const LLVector4a& shift_vector);
- virtual U32 getPartitionType() const;
- virtual const LLVector3 getRenderPosition() const;
- virtual void updateDrawable(BOOL force_damped);
- virtual LLDrawable* createDrawable(LLPipeline *pipeline);
- virtual BOOL updateGeometry(LLDrawable *drawable);
- virtual void setPixelAreaAndAngle(LLAgent &agent);
- virtual void updateRegion(LLViewerRegion *regionp);
- virtual void updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax);
- virtual void getSpatialExtents(LLVector4a& newMin, LLVector4a& newMax);
- virtual BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
- S32 face = -1, // which face to check, -1 = ALL_SIDES
- BOOL pick_transparent = FALSE,
- S32* face_hit = NULL, // which face was hit
- LLVector3* intersection = NULL, // return the intersection point
- LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point
- LLVector3* normal = NULL, // return the surface normal at the intersection point
- LLVector3* bi_normal = NULL); // return the surface bi-normal at the intersection point
-
- //--------------------------------------------------------------------
- // LLCharacter interface and related
- //--------------------------------------------------------------------
-public:
- virtual LLVector3 getCharacterPosition();
- virtual LLQuaternion getCharacterRotation();
- virtual LLVector3 getCharacterVelocity();
- virtual LLVector3 getCharacterAngularVelocity();
- virtual LLJoint* getCharacterJoint(U32 num);
- virtual BOOL allocateCharacterJoints(U32 num);
-
- virtual LLUUID remapMotionID(const LLUUID& id);
- virtual BOOL startMotion(const LLUUID& id, F32 time_offset = 0.f);
- virtual BOOL stopMotion(const LLUUID& id, BOOL stop_immediate = FALSE);
- virtual void stopMotionFromSource(const LLUUID& source_id);
- virtual void requestStopMotion(LLMotion* motion);
- LLMotion* findMotion(const LLUUID& id) const;
- void startDefaultMotions();
- void dumpAnimationState();
-
- virtual LLJoint* getJoint(const std::string &name);
- virtual LLJoint* getRootJoint() { return &mRoot; }
-
- void resetJointPositions( void );
- void resetJointPositionsToDefault( void );
- void resetSpecificJointPosition( const std::string& name );
-
- virtual const char* getAnimationPrefix() { return "avatar"; }
- virtual const LLUUID& getID();
- virtual LLVector3 getVolumePos(S32 joint_index, LLVector3& volume_offset);
- virtual LLJoint* findCollisionVolume(U32 volume_id);
- virtual S32 getCollisionVolumeID(std::string &name);
- virtual void addDebugText(const std::string& text);
- virtual F32 getTimeDilation();
- virtual void getGround(const LLVector3 &inPos, LLVector3 &outPos, LLVector3 &outNorm);
- virtual F32 getPixelArea() const;
- virtual LLPolyMesh* getHeadMesh();
- virtual LLPolyMesh* getUpperBodyMesh();
- virtual LLVector3d getPosGlobalFromAgent(const LLVector3 &position);
- virtual LLVector3 getPosAgentFromGlobal(const LLVector3d &position);
- virtual void updateVisualParams();
-
-
-/** Inherited
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** STATE
- **/
-
-public:
- virtual bool isSelf() const { return false; } // True if this avatar is for this viewer's agent
- bool isBuilt() const { return mIsBuilt; }
-
-private: //aligned members
- LLVector4a mImpostorExtents[2];
-
-private:
- BOOL mSupportsAlphaLayers; // For backwards compatibility, TRUE for 1.23+ clients
-
- //--------------------------------------------------------------------
- // Updates
- //--------------------------------------------------------------------
-public:
- virtual BOOL updateCharacter(LLAgent &agent);
- void idleUpdateVoiceVisualizer(bool voice_enabled);
- void idleUpdateMisc(bool detailed_update);
- virtual void idleUpdateAppearanceAnimation();
- void idleUpdateLipSync(bool voice_enabled);
- void idleUpdateLoadingEffect();
- void idleUpdateWindEffect();
- void idleUpdateNameTag(const LLVector3& root_pos_last);
- void idleUpdateNameTagText(BOOL new_name);
- LLVector3 idleUpdateNameTagPosition(const LLVector3& root_pos_last);
- void idleUpdateNameTagAlpha(BOOL new_name, F32 alpha);
- LLColor4 getNameTagColor(bool is_friend);
- void clearNameTag();
- static void invalidateNameTag(const LLUUID& agent_id);
- // force all name tags to rebuild, useful when display names turned on/off
- static void invalidateNameTags();
- void addNameTagLine(const std::string& line, const LLColor4& color, S32 style, const LLFontGL* font);
- void idleUpdateRenderCost();
- void idleUpdateBelowWater();
-
- //--------------------------------------------------------------------
- // Static preferences (controlled by user settings/menus)
- //--------------------------------------------------------------------
-public:
- static S32 sRenderName;
- static BOOL sRenderGroupTitles;
- static U32 sMaxVisible; //(affected by control "RenderAvatarMaxVisible")
- static F32 sRenderDistance; //distance at which avatars will render.
- static BOOL sShowAnimationDebug; // show animation debug info
- static BOOL sUseImpostors; //use impostors for far away avatars
- static BOOL sShowFootPlane; // show foot collision plane reported by server
- static BOOL sShowCollisionVolumes; // show skeletal collision volumes
- static BOOL sVisibleInFirstPerson;
- static S32 sNumLODChangesThisFrame;
- static S32 sNumVisibleChatBubbles;
- static BOOL sDebugInvisible;
- static BOOL sShowAttachmentPoints;
- static F32 sLODFactor; // user-settable LOD factor
- static F32 sPhysicsLODFactor; // user-settable physics LOD factor
- static BOOL sJointDebug; // output total number of joints being touched for each avatar
- static BOOL sDebugAvatarRotation;
-
- //--------------------------------------------------------------------
- // Region state
- //--------------------------------------------------------------------
-public:
- LLHost getObjectHost() const;
-
- //--------------------------------------------------------------------
- // Loading state
- //--------------------------------------------------------------------
-public:
- BOOL isFullyLoaded() const;
- bool visualParamWeightsAreDefault();
-protected:
- virtual BOOL getIsCloud();
- BOOL updateIsFullyLoaded();
- BOOL processFullyLoadedChange(bool loading);
- void updateRuthTimer(bool loading);
- F32 calcMorphAmount();
-private:
- BOOL mFullyLoaded;
- BOOL mPreviousFullyLoaded;
- BOOL mFullyLoadedInitialized;
- S32 mFullyLoadedFrameCounter;
- LLFrameTimer mFullyLoadedTimer;
- LLFrameTimer mRuthTimer;
-protected:
- LLFrameTimer mInvisibleTimer;
-
-/** State
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** SKELETON
- **/
-
-public:
- void updateHeadOffset();
- F32 getPelvisToFoot() const { return mPelvisToFoot; }
- void setPelvisOffset( bool hasOffset, const LLVector3& translation, F32 offset ) ;
- bool hasPelvisOffset( void ) { return mHasPelvisOffset; }
- void postPelvisSetRecalc( void );
- void setPelvisOffset( F32 pelvixFixupAmount );
-
- bool mHasPelvisOffset;
- LLVector3 mPelvisOffset;
- F32 mLastPelvisToFoot;
- F32 mPelvisFixup;
- F32 mLastPelvisFixup;
-
- LLVector3 mHeadOffset; // current head position
- LLViewerJoint mRoot;
-protected:
- static BOOL parseSkeletonFile(const std::string& filename);
- void buildCharacter();
- virtual BOOL loadAvatar();
-
- BOOL setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent, S32 &current_volume_num, S32 &current_joint_num);
- BOOL buildSkeleton(const LLVOAvatarSkeletonInfo *info);
-private:
- BOOL mIsBuilt; // state of deferred character building
- S32 mNumJoints;
- LLViewerJoint* mSkeleton;
-
- //--------------------------------------------------------------------
- // Pelvis height adjustment members.
- //--------------------------------------------------------------------
-public:
- LLVector3 mBodySize;
- S32 mLastSkeletonSerialNum;
-private:
- F32 mPelvisToFoot;
-
- //--------------------------------------------------------------------
- // Cached pointers to well known joints
- //--------------------------------------------------------------------
-public:
- LLViewerJoint* mPelvisp;
- LLViewerJoint* mTorsop;
- LLViewerJoint* mChestp;
- LLViewerJoint* mNeckp;
- LLViewerJoint* mHeadp;
- LLViewerJoint* mSkullp;
- LLViewerJoint* mEyeLeftp;
- LLViewerJoint* mEyeRightp;
- LLViewerJoint* mHipLeftp;
- LLViewerJoint* mHipRightp;
- LLViewerJoint* mKneeLeftp;
- LLViewerJoint* mKneeRightp;
- LLViewerJoint* mAnkleLeftp;
- LLViewerJoint* mAnkleRightp;
- LLViewerJoint* mFootLeftp;
- LLViewerJoint* mFootRightp;
- LLViewerJoint* mWristLeftp;
- LLViewerJoint* mWristRightp;
-
- //--------------------------------------------------------------------
- // XML parse tree
- //--------------------------------------------------------------------
-private:
- static LLXmlTree sXMLTree; // avatar config file
- static LLXmlTree sSkeletonXMLTree; // avatar skeleton file
-
-/** Skeleton
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** RENDERING
- **/
-
-public:
- U32 renderImpostor(LLColor4U color = LLColor4U(255,255,255,255), S32 diffuse_channel = 0);
- U32 renderRigid();
- U32 renderSkinned(EAvatarRenderPass pass);
- F32 getLastSkinTime() { return mLastSkinTime; }
- U32 renderSkinnedAttachments();
- U32 renderTransparent(BOOL first_pass);
- void renderCollisionVolumes();
- static void deleteCachedImages(bool clearAll=true);
- static void destroyGL();
- static void restoreGL();
- BOOL mIsDummy; // for special views
- S32 mSpecialRenderMode; // special lighting
-private:
- bool shouldAlphaMask();
-
- BOOL mNeedsSkin; // avatar has been animated and verts have not been updated
- F32 mLastSkinTime; //value of gFrameTimeSeconds at last skin update
-
- S32 mUpdatePeriod;
- S32 mNumInitFaces; //number of faces generated when creating the avatar drawable, does not inculde splitted faces due to long vertex buffer.
-
- //--------------------------------------------------------------------
- // Morph masks
- //--------------------------------------------------------------------
-public:
- BOOL morphMaskNeedsUpdate(LLVOAvatarDefines::EBakedTextureIndex index = LLVOAvatarDefines::BAKED_NUM_INDICES);
- void addMaskedMorph(LLVOAvatarDefines::EBakedTextureIndex index, LLPolyMorphTarget* morph_target, BOOL invert, std::string layer);
- void applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components, LLVOAvatarDefines::EBakedTextureIndex index = LLVOAvatarDefines::BAKED_NUM_INDICES);
-
- //--------------------------------------------------------------------
- // Visibility
- //--------------------------------------------------------------------
-protected:
- void updateVisibility();
-private:
- U32 mVisibilityRank;
- BOOL mVisible;
-
- //--------------------------------------------------------------------
- // Shadowing
- //--------------------------------------------------------------------
-public:
- void updateShadowFaces();
- LLDrawable* mShadow;
-private:
- LLFace* mShadow0Facep;
- LLFace* mShadow1Facep;
- LLPointer<LLViewerTexture> mShadowImagep;
-
- //--------------------------------------------------------------------
- // Impostors
- //--------------------------------------------------------------------
-public:
- BOOL isImpostor() const;
- BOOL needsImpostorUpdate() const;
- const LLVector3& getImpostorOffset() const;
- const LLVector2& getImpostorDim() const;
- void getImpostorValues(LLVector4a* extents, LLVector3& angle, F32& distance) const;
- void cacheImpostorValues();
- void setImpostorDim(const LLVector2& dim);
- static void resetImpostors();
- static void updateImpostors();
- LLRenderTarget mImpostor;
- BOOL mNeedsImpostorUpdate;
-private:
- LLVector3 mImpostorOffset;
- LLVector2 mImpostorDim;
- BOOL mNeedsAnimUpdate;
- LLVector3 mImpostorAngle;
- F32 mImpostorDistance;
- F32 mImpostorPixelArea;
- LLVector3 mLastAnimExtents[2];
-
- //--------------------------------------------------------------------
- // Wind rippling in clothes
- //--------------------------------------------------------------------
-public:
- LLVector4 mWindVec;
- F32 mRipplePhase;
- BOOL mBelowWater;
-private:
- F32 mWindFreq;
- LLFrameTimer mRippleTimer;
- F32 mRippleTimeLast;
- LLVector3 mRippleAccel;
- LLVector3 mLastVel;
-
- //--------------------------------------------------------------------
- // Culling
- //--------------------------------------------------------------------
-public:
- static void cullAvatarsByPixelArea();
- BOOL isCulled() const { return mCulled; }
-private:
- BOOL mCulled;
-
- //--------------------------------------------------------------------
- // Freeze counter
- //--------------------------------------------------------------------
-public:
- static void updateFreezeCounter(S32 counter = 0);
-private:
- static S32 sFreezeCounter;
-
- //--------------------------------------------------------------------
- // Constants
- //--------------------------------------------------------------------
-public:
- virtual LLViewerTexture::EBoostLevel getAvatarBoostLevel() const { return LLViewerTexture::BOOST_AVATAR; }
- virtual LLViewerTexture::EBoostLevel getAvatarBakedBoostLevel() const { return LLViewerTexture::BOOST_AVATAR_BAKED; }
- virtual S32 getTexImageSize() const;
- virtual S32 getTexImageArea() const { return getTexImageSize()*getTexImageSize(); }
-
-/** Rendering
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** TEXTURES
- **/
-
- //--------------------------------------------------------------------
- // Loading status
- //--------------------------------------------------------------------
-public:
- virtual BOOL isTextureDefined(LLVOAvatarDefines::ETextureIndex type, U32 index = 0) const;
- virtual BOOL isTextureVisible(LLVOAvatarDefines::ETextureIndex type, U32 index = 0) const;
- virtual BOOL isTextureVisible(LLVOAvatarDefines::ETextureIndex type, LLWearable *wearable) const;
-
-protected:
- BOOL isFullyBaked();
- static BOOL areAllNearbyInstancesBaked(S32& grey_avatars);
-
- //--------------------------------------------------------------------
- // Baked textures
- //--------------------------------------------------------------------
-public:
- void releaseComponentTextures(); // ! BACKWARDS COMPATIBILITY !
-protected:
- static void onBakedTextureMasksLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
- static void onInitialBakedTextureLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
- static void onBakedTextureLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
- virtual void removeMissingBakedTextures();
- void useBakedTexture(const LLUUID& id);
-
- typedef std::deque<LLMaskedMorph *> morph_list_t;
- struct BakedTextureData
- {
- LLUUID mLastTextureIndex;
- LLTexLayerSet* mTexLayerSet; // Only exists for self
- bool mIsLoaded;
- bool mIsUsed;
- LLVOAvatarDefines::ETextureIndex mTextureIndex;
- U32 mMaskTexName;
- // Stores pointers to the joint meshes that this baked texture deals with
- std::vector< LLViewerJointMesh * > mMeshes; // std::vector<LLViewerJointMesh> mJoints[i]->mMeshParts
- morph_list_t mMaskedMorphs;
- };
- typedef std::vector<BakedTextureData> bakedtexturedata_vec_t;
- bakedtexturedata_vec_t mBakedTextureDatas;
- LLLoadedCallbackEntry::source_callback_list_t mCallbackTextureList ;
- BOOL mLoadedCallbacksPaused;
- //--------------------------------------------------------------------
- // Local Textures
- //--------------------------------------------------------------------
-protected:
- virtual void setLocalTexture(LLVOAvatarDefines::ETextureIndex type, LLViewerTexture* tex, BOOL baked_version_exits, U32 index = 0);
- virtual void addLocalTextureStats(LLVOAvatarDefines::ETextureIndex type, LLViewerFetchedTexture* imagep, F32 texel_area_ratio, BOOL rendered, BOOL covered_by_baked, U32 index = 0);
- // MULTI-WEARABLE: make self-only?
- virtual void setBakedReady(LLVOAvatarDefines::ETextureIndex type, BOOL baked_version_exists, U32 index = 0);
-
- //--------------------------------------------------------------------
- // Texture accessors
- //--------------------------------------------------------------------
-private:
- virtual void setImage(const U8 te, LLViewerTexture *imagep, const U32 index);
- virtual LLViewerTexture* getImage(const U8 te, const U32 index) const;
-
- virtual const LLTextureEntry* getTexEntry(const U8 te_num) const;
- virtual void setTexEntry(const U8 index, const LLTextureEntry &te);
-
- void checkTextureLoading() ;
- //--------------------------------------------------------------------
- // Layers
- //--------------------------------------------------------------------
-protected:
- void deleteLayerSetCaches(bool clearAll = true);
- void addBakedTextureStats(LLViewerFetchedTexture* imagep, F32 pixel_area, F32 texel_area_ratio, S32 boost_level);
-
- //--------------------------------------------------------------------
- // Composites
- //--------------------------------------------------------------------
-public:
- virtual void invalidateComposite(LLTexLayerSet* layerset, BOOL upload_result);
- virtual void invalidateAll();
- virtual void setCompositeUpdatesEnabled(bool b) {}
- virtual void setCompositeUpdatesEnabled(U32 index, bool b) {}
- virtual bool isCompositeUpdateEnabled(U32 index) { return false; }
-
- //--------------------------------------------------------------------
- // Static texture/mesh/baked dictionary
- //--------------------------------------------------------------------
-public:
- static BOOL isIndexLocalTexture(LLVOAvatarDefines::ETextureIndex i);
- static BOOL isIndexBakedTexture(LLVOAvatarDefines::ETextureIndex i);
-private:
- static const LLVOAvatarDefines::LLVOAvatarDictionary *getDictionary() { return sAvatarDictionary; }
- static LLVOAvatarDefines::LLVOAvatarDictionary* sAvatarDictionary;
- static LLVOAvatarSkeletonInfo* sAvatarSkeletonInfo;
- static LLVOAvatarXmlInfo* sAvatarXmlInfo;
-
- //--------------------------------------------------------------------
- // Messaging
- //--------------------------------------------------------------------
-public:
- void onFirstTEMessageReceived();
-private:
- BOOL mFirstTEMessageReceived;
- BOOL mFirstAppearanceMessageReceived;
-
-/** Textures
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** MESHES
- **/
-
-public:
- void updateMeshTextures();
- void updateSexDependentLayerSets(BOOL upload_bake);
- void dirtyMesh(); // Dirty the avatar mesh
- void updateMeshData();
-protected:
- void releaseMeshData();
- virtual void restoreMeshData();
-private:
- void dirtyMesh(S32 priority); // Dirty the avatar mesh, with priority
- S32 mDirtyMesh; // 0 -- not dirty, 1 -- morphed, 2 -- LOD
- BOOL mMeshTexturesDirty;
-
- typedef std::multimap<std::string, LLPolyMesh*> polymesh_map_t;
- polymesh_map_t mMeshes;
- std::vector<LLViewerJoint *> mMeshLOD;
-
- //--------------------------------------------------------------------
- // Destroy invisible mesh
- //--------------------------------------------------------------------
-protected:
- BOOL mMeshValid;
- LLFrameTimer mMeshInvisibleTime;
-
-/** Meshes
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** APPEARANCE
- **/
-
-public:
- void processAvatarAppearance(LLMessageSystem* mesgsys);
- void hideSkirt();
- void startAppearanceAnimation();
-
- //--------------------------------------------------------------------
- // Appearance morphing
- //--------------------------------------------------------------------
-public:
- BOOL getIsAppearanceAnimating() const { return mAppearanceAnimating; }
-private:
- BOOL mAppearanceAnimating;
- LLFrameTimer mAppearanceMorphTimer;
- F32 mLastAppearanceBlendTime;
-
- //--------------------------------------------------------------------
- // Clothing colors (convenience functions to access visual parameters)
- //--------------------------------------------------------------------
-public:
- void setClothesColor(LLVOAvatarDefines::ETextureIndex te, const LLColor4& new_color, BOOL upload_bake);
- LLColor4 getClothesColor(LLVOAvatarDefines::ETextureIndex te);
- static BOOL teToColorParams(LLVOAvatarDefines::ETextureIndex te, U32 *param_name);
-
- //--------------------------------------------------------------------
- // Global colors
- //--------------------------------------------------------------------
-public:
- LLColor4 getGlobalColor(const std::string& color_name ) const;
- void onGlobalColorChanged(const LLTexGlobalColor* global_color, BOOL upload_bake);
-private:
- LLTexGlobalColor* mTexSkinColor;
- LLTexGlobalColor* mTexHairColor;
- LLTexGlobalColor* mTexEyeColor;
-
- //--------------------------------------------------------------------
- // Visibility
- //--------------------------------------------------------------------
-public:
- BOOL isVisible() const;
- void setVisibilityRank(U32 rank);
- U32 getVisibilityRank() const { return mVisibilityRank; } // unused
- static S32 sNumVisibleAvatars; // Number of instances of this class
- static LLColor4 getDummyColor();
-/** Appearance
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** WEARABLES
- **/
-
-public:
- virtual BOOL isWearingWearableType(LLWearableType::EType type ) const;
-
- //--------------------------------------------------------------------
- // Attachments
- //--------------------------------------------------------------------
-public:
- void clampAttachmentPositions();
- virtual const LLViewerJointAttachment* attachObject(LLViewerObject *viewer_object);
- virtual BOOL detachObject(LLViewerObject *viewer_object);
- void cleanupAttachedMesh( LLViewerObject* pVO );
- static LLVOAvatar* findAvatarFromAttachment(LLViewerObject* obj);
-protected:
- LLViewerJointAttachment* getTargetAttachmentPoint(LLViewerObject* viewer_object);
- void lazyAttach();
- void rebuildRiggedAttachments( void );
-
- //--------------------------------------------------------------------
- // Map of attachment points, by ID
- //--------------------------------------------------------------------
-public:
- S32 getAttachmentCount(); // Warning: order(N) not order(1) // currently used only by -self
- typedef std::map<S32, LLViewerJointAttachment*> attachment_map_t;
- attachment_map_t mAttachmentPoints;
- std::vector<LLPointer<LLViewerObject> > mPendingAttachment;
-
- //--------------------------------------------------------------------
- // HUD functions
- //--------------------------------------------------------------------
-public:
- BOOL hasHUDAttachment() const;
- LLBBox getHUDBBox() const;
- void rebuildHUD();
- void resetHUDAttachments();
- BOOL canAttachMoreObjects() const;
- BOOL canAttachMoreObjects(U32 n) const;
-protected:
- U32 getNumAttachments() const; // O(N), not O(1)
-
-/** Wearables
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** ACTIONS
- **/
-
- //--------------------------------------------------------------------
- // Animations
- //--------------------------------------------------------------------
-public:
- BOOL isAnyAnimationSignaled(const LLUUID *anim_array, const S32 num_anims) const;
- void processAnimationStateChanges();
-protected:
- BOOL processSingleAnimationStateChange(const LLUUID &anim_id, BOOL start);
- void resetAnimations();
-private:
- LLTimer mAnimTimer;
- F32 mTimeLast;
-
- //--------------------------------------------------------------------
- // Animation state data
- //--------------------------------------------------------------------
-public:
- typedef std::map<LLUUID, S32>::iterator AnimIterator;
- std::map<LLUUID, S32> mSignaledAnimations; // requested state of Animation name/value
- std::map<LLUUID, S32> mPlayingAnimations; // current state of Animation name/value
-
- typedef std::multimap<LLUUID, LLUUID> AnimationSourceMap;
- typedef AnimationSourceMap::iterator AnimSourceIterator;
- AnimationSourceMap mAnimationSources; // object ids that triggered anim ids
-
- //--------------------------------------------------------------------
- // Chat
- //--------------------------------------------------------------------
-public:
- void addChat(const LLChat& chat);
- void clearChat();
- void startTyping() { mTyping = TRUE; mTypingTimer.reset(); }
- void stopTyping() { mTyping = FALSE; }
-private:
- BOOL mVisibleChat;
-
- //--------------------------------------------------------------------
- // Lip synch morphs
- //--------------------------------------------------------------------
-private:
- bool mLipSyncActive; // we're morphing for lip sync
- LLVisualParam* mOohMorph; // cached pointers morphs for lip sync
- LLVisualParam* mAahMorph; // cached pointers morphs for lip sync
-
- //--------------------------------------------------------------------
- // Flight
- //--------------------------------------------------------------------
-public:
- BOOL mInAir;
- LLFrameTimer mTimeInAir;
-
-/** Actions
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** PHYSICS
- **/
-
-private:
- F32 mSpeedAccum; // measures speed (for diagnostics mostly).
- BOOL mTurning; // controls hysteresis on avatar rotation
- F32 mSpeed; // misc. animation repeated state
-
- //--------------------------------------------------------------------
- // Collision volumes
- //--------------------------------------------------------------------
-public:
- S32 mNumCollisionVolumes;
- LLViewerJointCollisionVolume* mCollisionVolumes;
-protected:
- BOOL allocateCollisionVolumes(U32 num);
-
- //--------------------------------------------------------------------
- // Dimensions
- //--------------------------------------------------------------------
-public:
- void resolveHeightGlobal(const LLVector3d &inPos, LLVector3d &outPos, LLVector3 &outNorm);
- bool distanceToGround( const LLVector3d &startPoint, LLVector3d &collisionPoint, F32 distToIntersectionAlongRay );
- void resolveHeightAgent(const LLVector3 &inPos, LLVector3 &outPos, LLVector3 &outNorm);
- void resolveRayCollisionAgent(const LLVector3d start_pt, const LLVector3d end_pt, LLVector3d &out_pos, LLVector3 &out_norm);
- void slamPosition(); // Slam position to transmitted position (for teleport);
-protected:
- void computeBodySize();
-
- //--------------------------------------------------------------------
- // Material being stepped on
- //--------------------------------------------------------------------
-private:
- BOOL mStepOnLand;
- U8 mStepMaterial;
- LLVector3 mStepObjectVelocity;
-
-/** Physics
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** HIERARCHY
- **/
-
-public:
- virtual BOOL setParent(LLViewerObject* parent);
- virtual void addChild(LLViewerObject *childp);
- virtual void removeChild(LLViewerObject *childp);
-
- //--------------------------------------------------------------------
- // Sitting
- //--------------------------------------------------------------------
-public:
- void sitDown(BOOL bSitting);
- BOOL isSitting(){return mIsSitting;}
- void sitOnObject(LLViewerObject *sit_object);
- void getOffObject();
-private:
- // set this property only with LLVOAvatar::sitDown method
- BOOL mIsSitting;
-
-/** Hierarchy
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** NAME
- **/
-
-public:
- std::string getFullname() const; // Returns "FirstName LastName"
-protected:
- static void getAnimLabels(LLDynamicArray<std::string>* labels);
- static void getAnimNames(LLDynamicArray<std::string>* names);
-private:
- std::string mNameString; // UTF-8 title + name + status
- std::string mTitle;
- bool mNameAway;
- bool mNameBusy;
- bool mNameMute;
- bool mNameAppearance;
- bool mNameFriend;
- bool mNameCloud;
- F32 mNameAlpha;
- BOOL mRenderGroupTitles;
-
- //--------------------------------------------------------------------
- // Display the name (then optionally fade it out)
- //--------------------------------------------------------------------
-public:
- LLFrameTimer mChatTimer;
- LLPointer<LLHUDNameTag> mNameText;
-private:
- LLFrameTimer mTimeVisible;
- std::deque<LLChat> mChats;
- BOOL mTyping;
- LLFrameTimer mTypingTimer;
-
-/** Name
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** SOUNDS
- **/
-
- //--------------------------------------------------------------------
- // Voice visualizer
- //--------------------------------------------------------------------
-public:
- // Responsible for detecting the user's voice signal (and when the
- // user speaks, it puts a voice symbol over the avatar's head) and gesticulations
- LLPointer<LLVoiceVisualizer> mVoiceVisualizer;
- int mCurrentGesticulationLevel;
-
- //--------------------------------------------------------------------
- // Step sound
- //--------------------------------------------------------------------
-protected:
- const LLUUID& getStepSound() const;
-private:
- // Global table of sound ids per material, and the ground
- const static LLUUID sStepSounds[LL_MCODE_END];
- const static LLUUID sStepSoundOnLand;
-
- //--------------------------------------------------------------------
- // Foot step state (for generating sounds)
- //--------------------------------------------------------------------
-public:
- void setFootPlane(const LLVector4 &plane) { mFootPlane = plane; }
- LLVector4 mFootPlane;
-private:
- BOOL mWasOnGroundLeft;
- BOOL mWasOnGroundRight;
-
-/** Sounds
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** DIAGNOSTICS
- **/
-
- //--------------------------------------------------------------------
- // General
- //--------------------------------------------------------------------
-public:
- static void dumpArchetypeXML(void*);
- static void dumpBakedStatus();
- const std::string getBakedStatusForPrintout() const;
- void dumpAvatarTEs(const std::string& context) const;
-
- static F32 sUnbakedTime; // Total seconds with >=1 unbaked avatars
- static F32 sUnbakedUpdateTime; // Last time stats were updated (to prevent multiple updates per frame)
- static F32 sGreyTime; // Total seconds with >=1 grey avatars
- static F32 sGreyUpdateTime; // Last time stats were updated (to prevent multiple updates per frame)
-protected:
- S32 getUnbakedPixelAreaRank();
- BOOL mHasGrey;
-private:
- F32 mMinPixelArea;
- F32 mMaxPixelArea;
- F32 mAdjustedPixelArea;
- std::string mDebugText;
-
-
- //--------------------------------------------------------------------
- // Avatar Rez Metrics
- //--------------------------------------------------------------------
-public:
- F32 debugGetExistenceTimeElapsedF32() const { return mDebugExistenceTimer.getElapsedTimeF32(); }
-protected:
- LLFrameTimer mRuthDebugTimer; // For tracking how long it takes for av to rez
- LLFrameTimer mDebugExistenceTimer; // Debugging for how long the avatar has been in memory.
-
-/** Diagnostics
- ** **
- *******************************************************************************/
-
-/********************************************************************************
- ** **
- ** SUPPORT CLASSES
- **/
-
-protected: // Shared with LLVOAvatarSelf
-
- struct LLVOAvatarXmlInfo
- {
- LLVOAvatarXmlInfo();
- ~LLVOAvatarXmlInfo();
-
- BOOL parseXmlSkeletonNode(LLXmlTreeNode* root);
- BOOL parseXmlMeshNodes(LLXmlTreeNode* root);
- BOOL parseXmlColorNodes(LLXmlTreeNode* root);
- BOOL parseXmlLayerNodes(LLXmlTreeNode* root);
- BOOL parseXmlDriverNodes(LLXmlTreeNode* root);
- BOOL parseXmlMorphNodes(LLXmlTreeNode* root);
-
- struct LLVOAvatarMeshInfo
- {
- typedef std::pair<LLPolyMorphTargetInfo*,BOOL> morph_info_pair_t;
- typedef std::vector<morph_info_pair_t> morph_info_list_t;
-
- LLVOAvatarMeshInfo() : mLOD(0), mMinPixelArea(.1f) {}
- ~LLVOAvatarMeshInfo()
- {
- morph_info_list_t::iterator iter;
- for (iter = mPolyMorphTargetInfoList.begin(); iter != mPolyMorphTargetInfoList.end(); iter++)
- {
- delete iter->first;
- }
- mPolyMorphTargetInfoList.clear();
- }
-
- std::string mType;
- S32 mLOD;
- std::string mMeshFileName;
- std::string mReferenceMeshName;
- F32 mMinPixelArea;
- morph_info_list_t mPolyMorphTargetInfoList;
- };
- typedef std::vector<LLVOAvatarMeshInfo*> mesh_info_list_t;
- mesh_info_list_t mMeshInfoList;
-
- typedef std::vector<LLPolySkeletalDistortionInfo*> skeletal_distortion_info_list_t;
- skeletal_distortion_info_list_t mSkeletalDistortionInfoList;
-
- struct LLVOAvatarAttachmentInfo
- {
- LLVOAvatarAttachmentInfo()
- : mGroup(-1), mAttachmentID(-1), mPieMenuSlice(-1), mVisibleFirstPerson(FALSE),
- mIsHUDAttachment(FALSE), mHasPosition(FALSE), mHasRotation(FALSE) {}
- std::string mName;
- std::string mJointName;
- LLVector3 mPosition;
- LLVector3 mRotationEuler;
- S32 mGroup;
- S32 mAttachmentID;
- S32 mPieMenuSlice;
- BOOL mVisibleFirstPerson;
- BOOL mIsHUDAttachment;
- BOOL mHasPosition;
- BOOL mHasRotation;
- };
- typedef std::vector<LLVOAvatarAttachmentInfo*> attachment_info_list_t;
- attachment_info_list_t mAttachmentInfoList;
-
- LLTexGlobalColorInfo *mTexSkinColorInfo;
- LLTexGlobalColorInfo *mTexHairColorInfo;
- LLTexGlobalColorInfo *mTexEyeColorInfo;
-
- typedef std::vector<LLTexLayerSetInfo*> layer_info_list_t;
- layer_info_list_t mLayerInfoList;
-
- typedef std::vector<LLDriverParamInfo*> driver_info_list_t;
- driver_info_list_t mDriverInfoList;
-
- struct LLVOAvatarMorphInfo
- {
- LLVOAvatarMorphInfo()
- : mInvert(FALSE) {}
- std::string mName;
- std::string mRegion;
- std::string mLayer;
- BOOL mInvert;
- };
-
- typedef std::vector<LLVOAvatarMorphInfo*> morph_info_list_t;
- morph_info_list_t mMorphMaskInfoList;
- };
-
- struct LLMaskedMorph
- {
- LLMaskedMorph(LLPolyMorphTarget *morph_target, BOOL invert, std::string layer) :
- mMorphTarget(morph_target),
- mInvert(invert),
- mLayer(layer)
- {
- morph_target->addPendingMorphMask();
- }
-
- LLPolyMorphTarget *mMorphTarget;
- BOOL mInvert;
- std::string mLayer;
- };
-
-/** Support classes
- ** **
- *******************************************************************************/
-
-}; // LLVOAvatar
-extern const F32 SELF_ADDITIONAL_PRI;
-extern const S32 MAX_TEXTURE_VIRTURE_SIZE_RESET_INTERVAL;
-
-#endif // LL_VO_AVATAR_H
+/**
+ * @file llvoavatar.h
+ * @brief Declaration of LLVOAvatar class which is a derivation of
+ * LLViewerObject
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLVOAVATAR_H
+#define LL_LLVOAVATAR_H
+
+#include <map>
+#include <deque>
+#include <string>
+#include <vector>
+
+#include <boost/signals2.hpp>
+
+#include "imageids.h" // IMG_INVISIBLE
+#include "llchat.h"
+#include "lldrawpoolalpha.h"
+#include "llviewerobject.h"
+#include "llcharacter.h"
+#include "llviewerjointmesh.h"
+#include "llviewerjointattachment.h"
+#include "llrendertarget.h"
+#include "llvoavatardefines.h"
+#include "lltexglobalcolor.h"
+#include "lldriverparam.h"
+#include "material_codes.h" // LL_MCODE_END
+
+extern const LLUUID ANIM_AGENT_BODY_NOISE;
+extern const LLUUID ANIM_AGENT_BREATHE_ROT;
+extern const LLUUID ANIM_AGENT_PHYSICS_MOTION;
+extern const LLUUID ANIM_AGENT_EDITING;
+extern const LLUUID ANIM_AGENT_EYE;
+extern const LLUUID ANIM_AGENT_FLY_ADJUST;
+extern const LLUUID ANIM_AGENT_HAND_MOTION;
+extern const LLUUID ANIM_AGENT_HEAD_ROT;
+extern const LLUUID ANIM_AGENT_PELVIS_FIX;
+extern const LLUUID ANIM_AGENT_TARGET;
+extern const LLUUID ANIM_AGENT_WALK_ADJUST;
+
+class LLTexLayerSet;
+class LLVoiceVisualizer;
+class LLHUDNameTag;
+class LLHUDEffectSpiral;
+class LLTexGlobalColor;
+class LLVOAvatarBoneInfo;
+class LLVOAvatarSkeletonInfo;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// LLVOAvatar
+//
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLVOAvatar :
+ public LLViewerObject,
+ public LLCharacter,
+ public boost::signals2::trackable
+{
+public:
+ friend class LLVOAvatarSelf;
+protected:
+ struct LLVOAvatarXmlInfo;
+ struct LLMaskedMorph;
+
+/********************************************************************************
+ ** **
+ ** INITIALIZATION
+ **/
+
+public:
+ LLVOAvatar(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual void markDead();
+ static void initClass(); // Initialize data that's only init'd once per class.
+ static void cleanupClass(); // Cleanup data that's only init'd once per class.
+ virtual void initInstance(); // Called after construction to initialize the class.
+protected:
+ virtual ~LLVOAvatar();
+ BOOL loadSkeletonNode();
+ BOOL loadMeshNodes();
+ virtual BOOL loadLayersets();
+
+/** Initialization
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** INHERITED
+ **/
+
+ //--------------------------------------------------------------------
+ // LLViewerObject interface and related
+ //--------------------------------------------------------------------
+public:
+ virtual void updateGL();
+ virtual LLVOAvatar* asAvatar();
+ virtual U32 processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num,
+ const EObjectUpdateType update_type,
+ LLDataPacker *dp);
+ virtual BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+ virtual BOOL updateLOD();
+ BOOL updateJointLODs();
+ void updateLODRiggedAttachments( void );
+ virtual BOOL isActive() const; // Whether this object needs to do an idleUpdate.
+ virtual void updateTextures();
+ virtual S32 setTETexture(const U8 te, const LLUUID& uuid); // If setting a baked texture, need to request it from a non-local sim.
+ virtual void onShift(const LLVector4a& shift_vector);
+ virtual U32 getPartitionType() const;
+ virtual const LLVector3 getRenderPosition() const;
+ virtual void updateDrawable(BOOL force_damped);
+ virtual LLDrawable* createDrawable(LLPipeline *pipeline);
+ virtual BOOL updateGeometry(LLDrawable *drawable);
+ virtual void setPixelAreaAndAngle(LLAgent &agent);
+ virtual void updateRegion(LLViewerRegion *regionp);
+ virtual void updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax);
+ virtual void getSpatialExtents(LLVector4a& newMin, LLVector4a& newMax);
+ virtual BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
+ S32 face = -1, // which face to check, -1 = ALL_SIDES
+ BOOL pick_transparent = FALSE,
+ S32* face_hit = NULL, // which face was hit
+ LLVector3* intersection = NULL, // return the intersection point
+ LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point
+ LLVector3* normal = NULL, // return the surface normal at the intersection point
+ LLVector3* bi_normal = NULL); // return the surface bi-normal at the intersection point
+
+ //--------------------------------------------------------------------
+ // LLCharacter interface and related
+ //--------------------------------------------------------------------
+public:
+ virtual LLVector3 getCharacterPosition();
+ virtual LLQuaternion getCharacterRotation();
+ virtual LLVector3 getCharacterVelocity();
+ virtual LLVector3 getCharacterAngularVelocity();
+ virtual LLJoint* getCharacterJoint(U32 num);
+ virtual BOOL allocateCharacterJoints(U32 num);
+
+ virtual LLUUID remapMotionID(const LLUUID& id);
+ virtual BOOL startMotion(const LLUUID& id, F32 time_offset = 0.f);
+ virtual BOOL stopMotion(const LLUUID& id, BOOL stop_immediate = FALSE);
+ virtual void stopMotionFromSource(const LLUUID& source_id);
+ virtual void requestStopMotion(LLMotion* motion);
+ LLMotion* findMotion(const LLUUID& id) const;
+ void startDefaultMotions();
+ void dumpAnimationState();
+
+ virtual LLJoint* getJoint(const std::string &name);
+ virtual LLJoint* getRootJoint() { return &mRoot; }
+
+ void resetJointPositions( void );
+ void resetJointPositionsToDefault( void );
+ void resetSpecificJointPosition( const std::string& name );
+
+ virtual const char* getAnimationPrefix() { return "avatar"; }
+ virtual const LLUUID& getID();
+ virtual LLVector3 getVolumePos(S32 joint_index, LLVector3& volume_offset);
+ virtual LLJoint* findCollisionVolume(U32 volume_id);
+ virtual S32 getCollisionVolumeID(std::string &name);
+ virtual void addDebugText(const std::string& text);
+ virtual F32 getTimeDilation();
+ virtual void getGround(const LLVector3 &inPos, LLVector3 &outPos, LLVector3 &outNorm);
+ virtual F32 getPixelArea() const;
+ virtual LLPolyMesh* getHeadMesh();
+ virtual LLPolyMesh* getUpperBodyMesh();
+ virtual LLVector3d getPosGlobalFromAgent(const LLVector3 &position);
+ virtual LLVector3 getPosAgentFromGlobal(const LLVector3d &position);
+ virtual void updateVisualParams();
+
+
+/** Inherited
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** STATE
+ **/
+
+public:
+ virtual bool isSelf() const { return false; } // True if this avatar is for this viewer's agent
+ bool isBuilt() const { return mIsBuilt; }
+
+private: //aligned members
+ LLVector4a mImpostorExtents[2];
+
+private:
+ BOOL mSupportsAlphaLayers; // For backwards compatibility, TRUE for 1.23+ clients
+
+ //--------------------------------------------------------------------
+ // Updates
+ //--------------------------------------------------------------------
+public:
+ virtual BOOL updateCharacter(LLAgent &agent);
+ void idleUpdateVoiceVisualizer(bool voice_enabled);
+ void idleUpdateMisc(bool detailed_update);
+ virtual void idleUpdateAppearanceAnimation();
+ void idleUpdateLipSync(bool voice_enabled);
+ void idleUpdateLoadingEffect();
+ void idleUpdateWindEffect();
+ void idleUpdateNameTag(const LLVector3& root_pos_last);
+ void idleUpdateNameTagText(BOOL new_name);
+ LLVector3 idleUpdateNameTagPosition(const LLVector3& root_pos_last);
+ void idleUpdateNameTagAlpha(BOOL new_name, F32 alpha);
+ LLColor4 getNameTagColor(bool is_friend);
+ void clearNameTag();
+ static void invalidateNameTag(const LLUUID& agent_id);
+ // force all name tags to rebuild, useful when display names turned on/off
+ static void invalidateNameTags();
+ void addNameTagLine(const std::string& line, const LLColor4& color, S32 style, const LLFontGL* font);
+ void idleUpdateRenderCost();
+ void idleUpdateBelowWater();
+
+ //--------------------------------------------------------------------
+ // Static preferences (controlled by user settings/menus)
+ //--------------------------------------------------------------------
+public:
+ static S32 sRenderName;
+ static BOOL sRenderGroupTitles;
+ static U32 sMaxVisible; //(affected by control "RenderAvatarMaxVisible")
+ static F32 sRenderDistance; //distance at which avatars will render.
+ static BOOL sShowAnimationDebug; // show animation debug info
+ static BOOL sUseImpostors; //use impostors for far away avatars
+ static BOOL sShowFootPlane; // show foot collision plane reported by server
+ static BOOL sShowCollisionVolumes; // show skeletal collision volumes
+ static BOOL sVisibleInFirstPerson;
+ static S32 sNumLODChangesThisFrame;
+ static S32 sNumVisibleChatBubbles;
+ static BOOL sDebugInvisible;
+ static BOOL sShowAttachmentPoints;
+ static F32 sLODFactor; // user-settable LOD factor
+ static F32 sPhysicsLODFactor; // user-settable physics LOD factor
+ static BOOL sJointDebug; // output total number of joints being touched for each avatar
+ static BOOL sDebugAvatarRotation;
+
+ //--------------------------------------------------------------------
+ // Region state
+ //--------------------------------------------------------------------
+public:
+ LLHost getObjectHost() const;
+
+ //--------------------------------------------------------------------
+ // Loading state
+ //--------------------------------------------------------------------
+public:
+ BOOL isFullyLoaded() const;
+ bool visualParamWeightsAreDefault();
+protected:
+ virtual BOOL getIsCloud();
+ BOOL updateIsFullyLoaded();
+ BOOL processFullyLoadedChange(bool loading);
+ void updateRuthTimer(bool loading);
+ F32 calcMorphAmount();
+private:
+ BOOL mFullyLoaded;
+ BOOL mPreviousFullyLoaded;
+ BOOL mFullyLoadedInitialized;
+ S32 mFullyLoadedFrameCounter;
+ LLFrameTimer mFullyLoadedTimer;
+ LLFrameTimer mRuthTimer;
+protected:
+ LLFrameTimer mInvisibleTimer;
+
+/** State
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** SKELETON
+ **/
+
+public:
+ void updateHeadOffset();
+ F32 getPelvisToFoot() const { return mPelvisToFoot; }
+ void setPelvisOffset( bool hasOffset, const LLVector3& translation, F32 offset ) ;
+ bool hasPelvisOffset( void ) { return mHasPelvisOffset; }
+ void postPelvisSetRecalc( void );
+ void setPelvisOffset( F32 pelvixFixupAmount );
+
+ bool mHasPelvisOffset;
+ LLVector3 mPelvisOffset;
+ F32 mLastPelvisToFoot;
+ F32 mPelvisFixup;
+ F32 mLastPelvisFixup;
+
+ LLVector3 mHeadOffset; // current head position
+ LLViewerJoint mRoot;
+protected:
+ static BOOL parseSkeletonFile(const std::string& filename);
+ void buildCharacter();
+ virtual BOOL loadAvatar();
+
+ BOOL setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent, S32 &current_volume_num, S32 &current_joint_num);
+ BOOL buildSkeleton(const LLVOAvatarSkeletonInfo *info);
+private:
+ BOOL mIsBuilt; // state of deferred character building
+ S32 mNumJoints;
+ LLViewerJoint* mSkeleton;
+
+ //--------------------------------------------------------------------
+ // Pelvis height adjustment members.
+ //--------------------------------------------------------------------
+public:
+ LLVector3 mBodySize;
+ S32 mLastSkeletonSerialNum;
+private:
+ F32 mPelvisToFoot;
+
+ //--------------------------------------------------------------------
+ // Cached pointers to well known joints
+ //--------------------------------------------------------------------
+public:
+ LLViewerJoint* mPelvisp;
+ LLViewerJoint* mTorsop;
+ LLViewerJoint* mChestp;
+ LLViewerJoint* mNeckp;
+ LLViewerJoint* mHeadp;
+ LLViewerJoint* mSkullp;
+ LLViewerJoint* mEyeLeftp;
+ LLViewerJoint* mEyeRightp;
+ LLViewerJoint* mHipLeftp;
+ LLViewerJoint* mHipRightp;
+ LLViewerJoint* mKneeLeftp;
+ LLViewerJoint* mKneeRightp;
+ LLViewerJoint* mAnkleLeftp;
+ LLViewerJoint* mAnkleRightp;
+ LLViewerJoint* mFootLeftp;
+ LLViewerJoint* mFootRightp;
+ LLViewerJoint* mWristLeftp;
+ LLViewerJoint* mWristRightp;
+
+ //--------------------------------------------------------------------
+ // XML parse tree
+ //--------------------------------------------------------------------
+private:
+ static LLXmlTree sXMLTree; // avatar config file
+ static LLXmlTree sSkeletonXMLTree; // avatar skeleton file
+
+/** Skeleton
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** RENDERING
+ **/
+
+public:
+ U32 renderImpostor(LLColor4U color = LLColor4U(255,255,255,255), S32 diffuse_channel = 0);
+ U32 renderRigid();
+ U32 renderSkinned(EAvatarRenderPass pass);
+ F32 getLastSkinTime() { return mLastSkinTime; }
+ U32 renderSkinnedAttachments();
+ U32 renderTransparent(BOOL first_pass);
+ void renderCollisionVolumes();
+ static void deleteCachedImages(bool clearAll=true);
+ static void destroyGL();
+ static void restoreGL();
+ BOOL mIsDummy; // for special views
+ S32 mSpecialRenderMode; // special lighting
+private:
+ bool shouldAlphaMask();
+
+ BOOL mNeedsSkin; // avatar has been animated and verts have not been updated
+ F32 mLastSkinTime; //value of gFrameTimeSeconds at last skin update
+
+ S32 mUpdatePeriod;
+ S32 mNumInitFaces; //number of faces generated when creating the avatar drawable, does not inculde splitted faces due to long vertex buffer.
+
+ //--------------------------------------------------------------------
+ // Morph masks
+ //--------------------------------------------------------------------
+public:
+ BOOL morphMaskNeedsUpdate(LLVOAvatarDefines::EBakedTextureIndex index = LLVOAvatarDefines::BAKED_NUM_INDICES);
+ void addMaskedMorph(LLVOAvatarDefines::EBakedTextureIndex index, LLPolyMorphTarget* morph_target, BOOL invert, std::string layer);
+ void applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components, LLVOAvatarDefines::EBakedTextureIndex index = LLVOAvatarDefines::BAKED_NUM_INDICES);
+
+ //--------------------------------------------------------------------
+ // Visibility
+ //--------------------------------------------------------------------
+protected:
+ void updateVisibility();
+private:
+ U32 mVisibilityRank;
+ BOOL mVisible;
+
+ //--------------------------------------------------------------------
+ // Shadowing
+ //--------------------------------------------------------------------
+public:
+ void updateShadowFaces();
+ LLDrawable* mShadow;
+private:
+ LLFace* mShadow0Facep;
+ LLFace* mShadow1Facep;
+ LLPointer<LLViewerTexture> mShadowImagep;
+
+ //--------------------------------------------------------------------
+ // Impostors
+ //--------------------------------------------------------------------
+public:
+ BOOL isImpostor() const;
+ BOOL needsImpostorUpdate() const;
+ const LLVector3& getImpostorOffset() const;
+ const LLVector2& getImpostorDim() const;
+ void getImpostorValues(LLVector4a* extents, LLVector3& angle, F32& distance) const;
+ void cacheImpostorValues();
+ void setImpostorDim(const LLVector2& dim);
+ static void resetImpostors();
+ static void updateImpostors();
+ LLRenderTarget mImpostor;
+ BOOL mNeedsImpostorUpdate;
+private:
+ LLVector3 mImpostorOffset;
+ LLVector2 mImpostorDim;
+ BOOL mNeedsAnimUpdate;
+ LLVector3 mImpostorAngle;
+ F32 mImpostorDistance;
+ F32 mImpostorPixelArea;
+ LLVector3 mLastAnimExtents[2];
+
+ //--------------------------------------------------------------------
+ // Wind rippling in clothes
+ //--------------------------------------------------------------------
+public:
+ LLVector4 mWindVec;
+ F32 mRipplePhase;
+ BOOL mBelowWater;
+private:
+ F32 mWindFreq;
+ LLFrameTimer mRippleTimer;
+ F32 mRippleTimeLast;
+ LLVector3 mRippleAccel;
+ LLVector3 mLastVel;
+
+ //--------------------------------------------------------------------
+ // Culling
+ //--------------------------------------------------------------------
+public:
+ static void cullAvatarsByPixelArea();
+ BOOL isCulled() const { return mCulled; }
+private:
+ BOOL mCulled;
+
+ //--------------------------------------------------------------------
+ // Freeze counter
+ //--------------------------------------------------------------------
+public:
+ static void updateFreezeCounter(S32 counter = 0);
+private:
+ static S32 sFreezeCounter;
+
+ //--------------------------------------------------------------------
+ // Constants
+ //--------------------------------------------------------------------
+public:
+ virtual LLViewerTexture::EBoostLevel getAvatarBoostLevel() const { return LLViewerTexture::BOOST_AVATAR; }
+ virtual LLViewerTexture::EBoostLevel getAvatarBakedBoostLevel() const { return LLViewerTexture::BOOST_AVATAR_BAKED; }
+ virtual S32 getTexImageSize() const;
+ virtual S32 getTexImageArea() const { return getTexImageSize()*getTexImageSize(); }
+
+/** Rendering
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** TEXTURES
+ **/
+
+ //--------------------------------------------------------------------
+ // Loading status
+ //--------------------------------------------------------------------
+public:
+ virtual BOOL isTextureDefined(LLVOAvatarDefines::ETextureIndex type, U32 index = 0) const;
+ virtual BOOL isTextureVisible(LLVOAvatarDefines::ETextureIndex type, U32 index = 0) const;
+ virtual BOOL isTextureVisible(LLVOAvatarDefines::ETextureIndex type, LLWearable *wearable) const;
+
+protected:
+ BOOL isFullyBaked();
+ static BOOL areAllNearbyInstancesBaked(S32& grey_avatars);
+
+ //--------------------------------------------------------------------
+ // Baked textures
+ //--------------------------------------------------------------------
+public:
+ void releaseComponentTextures(); // ! BACKWARDS COMPATIBILITY !
+protected:
+ static void onBakedTextureMasksLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
+ static void onInitialBakedTextureLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
+ static void onBakedTextureLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
+ virtual void removeMissingBakedTextures();
+ void useBakedTexture(const LLUUID& id);
+
+ typedef std::deque<LLMaskedMorph *> morph_list_t;
+ struct BakedTextureData
+ {
+ LLUUID mLastTextureIndex;
+ LLTexLayerSet* mTexLayerSet; // Only exists for self
+ bool mIsLoaded;
+ bool mIsUsed;
+ LLVOAvatarDefines::ETextureIndex mTextureIndex;
+ U32 mMaskTexName;
+ // Stores pointers to the joint meshes that this baked texture deals with
+ std::vector< LLViewerJointMesh * > mMeshes; // std::vector<LLViewerJointMesh> mJoints[i]->mMeshParts
+ morph_list_t mMaskedMorphs;
+ };
+ typedef std::vector<BakedTextureData> bakedtexturedata_vec_t;
+ bakedtexturedata_vec_t mBakedTextureDatas;
+ LLLoadedCallbackEntry::source_callback_list_t mCallbackTextureList ;
+ BOOL mLoadedCallbacksPaused;
+ //--------------------------------------------------------------------
+ // Local Textures
+ //--------------------------------------------------------------------
+protected:
+ virtual void setLocalTexture(LLVOAvatarDefines::ETextureIndex type, LLViewerTexture* tex, BOOL baked_version_exits, U32 index = 0);
+ virtual void addLocalTextureStats(LLVOAvatarDefines::ETextureIndex type, LLViewerFetchedTexture* imagep, F32 texel_area_ratio, BOOL rendered, BOOL covered_by_baked, U32 index = 0);
+ // MULTI-WEARABLE: make self-only?
+ virtual void setBakedReady(LLVOAvatarDefines::ETextureIndex type, BOOL baked_version_exists, U32 index = 0);
+
+ //--------------------------------------------------------------------
+ // Texture accessors
+ //--------------------------------------------------------------------
+private:
+ virtual void setImage(const U8 te, LLViewerTexture *imagep, const U32 index);
+ virtual LLViewerTexture* getImage(const U8 te, const U32 index) const;
+
+ virtual const LLTextureEntry* getTexEntry(const U8 te_num) const;
+ virtual void setTexEntry(const U8 index, const LLTextureEntry &te);
+
+ void checkTextureLoading() ;
+ //--------------------------------------------------------------------
+ // Layers
+ //--------------------------------------------------------------------
+protected:
+ void deleteLayerSetCaches(bool clearAll = true);
+ void addBakedTextureStats(LLViewerFetchedTexture* imagep, F32 pixel_area, F32 texel_area_ratio, S32 boost_level);
+
+ //--------------------------------------------------------------------
+ // Composites
+ //--------------------------------------------------------------------
+public:
+ virtual void invalidateComposite(LLTexLayerSet* layerset, BOOL upload_result);
+ virtual void invalidateAll();
+ virtual void setCompositeUpdatesEnabled(bool b) {}
+ virtual void setCompositeUpdatesEnabled(U32 index, bool b) {}
+ virtual bool isCompositeUpdateEnabled(U32 index) { return false; }
+
+ //--------------------------------------------------------------------
+ // Static texture/mesh/baked dictionary
+ //--------------------------------------------------------------------
+public:
+ static BOOL isIndexLocalTexture(LLVOAvatarDefines::ETextureIndex i);
+ static BOOL isIndexBakedTexture(LLVOAvatarDefines::ETextureIndex i);
+private:
+ static const LLVOAvatarDefines::LLVOAvatarDictionary *getDictionary() { return sAvatarDictionary; }
+ static LLVOAvatarDefines::LLVOAvatarDictionary* sAvatarDictionary;
+ static LLVOAvatarSkeletonInfo* sAvatarSkeletonInfo;
+ static LLVOAvatarXmlInfo* sAvatarXmlInfo;
+
+ //--------------------------------------------------------------------
+ // Messaging
+ //--------------------------------------------------------------------
+public:
+ void onFirstTEMessageReceived();
+private:
+ BOOL mFirstTEMessageReceived;
+ BOOL mFirstAppearanceMessageReceived;
+
+/** Textures
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** MESHES
+ **/
+
+public:
+ void updateMeshTextures();
+ void updateSexDependentLayerSets(BOOL upload_bake);
+ void dirtyMesh(); // Dirty the avatar mesh
+ void updateMeshData();
+protected:
+ void releaseMeshData();
+ virtual void restoreMeshData();
+private:
+ void dirtyMesh(S32 priority); // Dirty the avatar mesh, with priority
+ S32 mDirtyMesh; // 0 -- not dirty, 1 -- morphed, 2 -- LOD
+ BOOL mMeshTexturesDirty;
+
+ typedef std::multimap<std::string, LLPolyMesh*> polymesh_map_t;
+ polymesh_map_t mMeshes;
+ std::vector<LLViewerJoint *> mMeshLOD;
+
+ //--------------------------------------------------------------------
+ // Destroy invisible mesh
+ //--------------------------------------------------------------------
+protected:
+ BOOL mMeshValid;
+ LLFrameTimer mMeshInvisibleTime;
+
+/** Meshes
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** APPEARANCE
+ **/
+
+public:
+ void processAvatarAppearance(LLMessageSystem* mesgsys);
+ void hideSkirt();
+ void startAppearanceAnimation();
+
+ //--------------------------------------------------------------------
+ // Appearance morphing
+ //--------------------------------------------------------------------
+public:
+ BOOL getIsAppearanceAnimating() const { return mAppearanceAnimating; }
+private:
+ BOOL mAppearanceAnimating;
+ LLFrameTimer mAppearanceMorphTimer;
+ F32 mLastAppearanceBlendTime;
+
+ //--------------------------------------------------------------------
+ // Clothing colors (convenience functions to access visual parameters)
+ //--------------------------------------------------------------------
+public:
+ void setClothesColor(LLVOAvatarDefines::ETextureIndex te, const LLColor4& new_color, BOOL upload_bake);
+ LLColor4 getClothesColor(LLVOAvatarDefines::ETextureIndex te);
+ static BOOL teToColorParams(LLVOAvatarDefines::ETextureIndex te, U32 *param_name);
+
+ //--------------------------------------------------------------------
+ // Global colors
+ //--------------------------------------------------------------------
+public:
+ LLColor4 getGlobalColor(const std::string& color_name ) const;
+ void onGlobalColorChanged(const LLTexGlobalColor* global_color, BOOL upload_bake);
+private:
+ LLTexGlobalColor* mTexSkinColor;
+ LLTexGlobalColor* mTexHairColor;
+ LLTexGlobalColor* mTexEyeColor;
+
+ //--------------------------------------------------------------------
+ // Visibility
+ //--------------------------------------------------------------------
+public:
+ BOOL isVisible() const;
+ void setVisibilityRank(U32 rank);
+ U32 getVisibilityRank() const { return mVisibilityRank; } // unused
+ static S32 sNumVisibleAvatars; // Number of instances of this class
+ static LLColor4 getDummyColor();
+/** Appearance
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** WEARABLES
+ **/
+
+public:
+ virtual BOOL isWearingWearableType(LLWearableType::EType type ) const;
+
+ //--------------------------------------------------------------------
+ // Attachments
+ //--------------------------------------------------------------------
+public:
+ void clampAttachmentPositions();
+ virtual const LLViewerJointAttachment* attachObject(LLViewerObject *viewer_object);
+ virtual BOOL detachObject(LLViewerObject *viewer_object);
+ void cleanupAttachedMesh( LLViewerObject* pVO );
+ static LLVOAvatar* findAvatarFromAttachment(LLViewerObject* obj);
+protected:
+ LLViewerJointAttachment* getTargetAttachmentPoint(LLViewerObject* viewer_object);
+ void lazyAttach();
+ void rebuildRiggedAttachments( void );
+
+ //--------------------------------------------------------------------
+ // Map of attachment points, by ID
+ //--------------------------------------------------------------------
+public:
+ S32 getAttachmentCount(); // Warning: order(N) not order(1) // currently used only by -self
+ typedef std::map<S32, LLViewerJointAttachment*> attachment_map_t;
+ attachment_map_t mAttachmentPoints;
+ std::vector<LLPointer<LLViewerObject> > mPendingAttachment;
+
+ //--------------------------------------------------------------------
+ // HUD functions
+ //--------------------------------------------------------------------
+public:
+ BOOL hasHUDAttachment() const;
+ LLBBox getHUDBBox() const;
+ void rebuildHUD();
+ void resetHUDAttachments();
+ BOOL canAttachMoreObjects() const;
+ BOOL canAttachMoreObjects(U32 n) const;
+protected:
+ U32 getNumAttachments() const; // O(N), not O(1)
+
+/** Wearables
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** ACTIONS
+ **/
+
+ //--------------------------------------------------------------------
+ // Animations
+ //--------------------------------------------------------------------
+public:
+ BOOL isAnyAnimationSignaled(const LLUUID *anim_array, const S32 num_anims) const;
+ void processAnimationStateChanges();
+protected:
+ BOOL processSingleAnimationStateChange(const LLUUID &anim_id, BOOL start);
+ void resetAnimations();
+private:
+ LLTimer mAnimTimer;
+ F32 mTimeLast;
+
+ //--------------------------------------------------------------------
+ // Animation state data
+ //--------------------------------------------------------------------
+public:
+ typedef std::map<LLUUID, S32>::iterator AnimIterator;
+ std::map<LLUUID, S32> mSignaledAnimations; // requested state of Animation name/value
+ std::map<LLUUID, S32> mPlayingAnimations; // current state of Animation name/value
+
+ typedef std::multimap<LLUUID, LLUUID> AnimationSourceMap;
+ typedef AnimationSourceMap::iterator AnimSourceIterator;
+ AnimationSourceMap mAnimationSources; // object ids that triggered anim ids
+
+ //--------------------------------------------------------------------
+ // Chat
+ //--------------------------------------------------------------------
+public:
+ void addChat(const LLChat& chat);
+ void clearChat();
+ void startTyping() { mTyping = TRUE; mTypingTimer.reset(); }
+ void stopTyping() { mTyping = FALSE; }
+private:
+ BOOL mVisibleChat;
+
+ //--------------------------------------------------------------------
+ // Lip synch morphs
+ //--------------------------------------------------------------------
+private:
+ bool mLipSyncActive; // we're morphing for lip sync
+ LLVisualParam* mOohMorph; // cached pointers morphs for lip sync
+ LLVisualParam* mAahMorph; // cached pointers morphs for lip sync
+
+ //--------------------------------------------------------------------
+ // Flight
+ //--------------------------------------------------------------------
+public:
+ BOOL mInAir;
+ LLFrameTimer mTimeInAir;
+
+/** Actions
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** PHYSICS
+ **/
+
+private:
+ F32 mSpeedAccum; // measures speed (for diagnostics mostly).
+ BOOL mTurning; // controls hysteresis on avatar rotation
+ F32 mSpeed; // misc. animation repeated state
+
+ //--------------------------------------------------------------------
+ // Collision volumes
+ //--------------------------------------------------------------------
+public:
+ S32 mNumCollisionVolumes;
+ LLViewerJointCollisionVolume* mCollisionVolumes;
+protected:
+ BOOL allocateCollisionVolumes(U32 num);
+
+ //--------------------------------------------------------------------
+ // Dimensions
+ //--------------------------------------------------------------------
+public:
+ void resolveHeightGlobal(const LLVector3d &inPos, LLVector3d &outPos, LLVector3 &outNorm);
+ bool distanceToGround( const LLVector3d &startPoint, LLVector3d &collisionPoint, F32 distToIntersectionAlongRay );
+ void resolveHeightAgent(const LLVector3 &inPos, LLVector3 &outPos, LLVector3 &outNorm);
+ void resolveRayCollisionAgent(const LLVector3d start_pt, const LLVector3d end_pt, LLVector3d &out_pos, LLVector3 &out_norm);
+ void slamPosition(); // Slam position to transmitted position (for teleport);
+protected:
+ void computeBodySize();
+
+ //--------------------------------------------------------------------
+ // Material being stepped on
+ //--------------------------------------------------------------------
+private:
+ BOOL mStepOnLand;
+ U8 mStepMaterial;
+ LLVector3 mStepObjectVelocity;
+
+/** Physics
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** HIERARCHY
+ **/
+
+public:
+ virtual BOOL setParent(LLViewerObject* parent);
+ virtual void addChild(LLViewerObject *childp);
+ virtual void removeChild(LLViewerObject *childp);
+
+ //--------------------------------------------------------------------
+ // Sitting
+ //--------------------------------------------------------------------
+public:
+ void sitDown(BOOL bSitting);
+ BOOL isSitting(){return mIsSitting;}
+ void sitOnObject(LLViewerObject *sit_object);
+ void getOffObject();
+private:
+ // set this property only with LLVOAvatar::sitDown method
+ BOOL mIsSitting;
+
+/** Hierarchy
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** NAME
+ **/
+
+public:
+ std::string getFullname() const; // Returns "FirstName LastName"
+protected:
+ static void getAnimLabels(LLDynamicArray<std::string>* labels);
+ static void getAnimNames(LLDynamicArray<std::string>* names);
+private:
+ std::string mNameString; // UTF-8 title + name + status
+ std::string mTitle;
+ bool mNameAway;
+ bool mNameBusy;
+ bool mNameMute;
+ bool mNameAppearance;
+ bool mNameFriend;
+ bool mNameCloud;
+ F32 mNameAlpha;
+ BOOL mRenderGroupTitles;
+
+ //--------------------------------------------------------------------
+ // Display the name (then optionally fade it out)
+ //--------------------------------------------------------------------
+public:
+ LLFrameTimer mChatTimer;
+ LLPointer<LLHUDNameTag> mNameText;
+private:
+ LLFrameTimer mTimeVisible;
+ std::deque<LLChat> mChats;
+ BOOL mTyping;
+ LLFrameTimer mTypingTimer;
+
+/** Name
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** SOUNDS
+ **/
+
+ //--------------------------------------------------------------------
+ // Voice visualizer
+ //--------------------------------------------------------------------
+public:
+ // Responsible for detecting the user's voice signal (and when the
+ // user speaks, it puts a voice symbol over the avatar's head) and gesticulations
+ LLPointer<LLVoiceVisualizer> mVoiceVisualizer;
+ int mCurrentGesticulationLevel;
+
+ //--------------------------------------------------------------------
+ // Step sound
+ //--------------------------------------------------------------------
+protected:
+ const LLUUID& getStepSound() const;
+private:
+ // Global table of sound ids per material, and the ground
+ const static LLUUID sStepSounds[LL_MCODE_END];
+ const static LLUUID sStepSoundOnLand;
+
+ //--------------------------------------------------------------------
+ // Foot step state (for generating sounds)
+ //--------------------------------------------------------------------
+public:
+ void setFootPlane(const LLVector4 &plane) { mFootPlane = plane; }
+ LLVector4 mFootPlane;
+private:
+ BOOL mWasOnGroundLeft;
+ BOOL mWasOnGroundRight;
+
+/** Sounds
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** DIAGNOSTICS
+ **/
+
+ //--------------------------------------------------------------------
+ // General
+ //--------------------------------------------------------------------
+public:
+ static void dumpArchetypeXML(void*);
+ static void dumpBakedStatus();
+ const std::string getBakedStatusForPrintout() const;
+ void dumpAvatarTEs(const std::string& context) const;
+
+ static F32 sUnbakedTime; // Total seconds with >=1 unbaked avatars
+ static F32 sUnbakedUpdateTime; // Last time stats were updated (to prevent multiple updates per frame)
+ static F32 sGreyTime; // Total seconds with >=1 grey avatars
+ static F32 sGreyUpdateTime; // Last time stats were updated (to prevent multiple updates per frame)
+protected:
+ S32 getUnbakedPixelAreaRank();
+ BOOL mHasGrey;
+private:
+ F32 mMinPixelArea;
+ F32 mMaxPixelArea;
+ F32 mAdjustedPixelArea;
+ std::string mDebugText;
+
+
+ //--------------------------------------------------------------------
+ // Avatar Rez Metrics
+ //--------------------------------------------------------------------
+public:
+ F32 debugGetExistenceTimeElapsedF32() const { return mDebugExistenceTimer.getElapsedTimeF32(); }
+protected:
+ LLFrameTimer mRuthDebugTimer; // For tracking how long it takes for av to rez
+ LLFrameTimer mDebugExistenceTimer; // Debugging for how long the avatar has been in memory.
+
+/** Diagnostics
+ ** **
+ *******************************************************************************/
+
+/********************************************************************************
+ ** **
+ ** SUPPORT CLASSES
+ **/
+
+protected: // Shared with LLVOAvatarSelf
+
+ struct LLVOAvatarXmlInfo
+ {
+ LLVOAvatarXmlInfo();
+ ~LLVOAvatarXmlInfo();
+
+ BOOL parseXmlSkeletonNode(LLXmlTreeNode* root);
+ BOOL parseXmlMeshNodes(LLXmlTreeNode* root);
+ BOOL parseXmlColorNodes(LLXmlTreeNode* root);
+ BOOL parseXmlLayerNodes(LLXmlTreeNode* root);
+ BOOL parseXmlDriverNodes(LLXmlTreeNode* root);
+ BOOL parseXmlMorphNodes(LLXmlTreeNode* root);
+
+ struct LLVOAvatarMeshInfo
+ {
+ typedef std::pair<LLPolyMorphTargetInfo*,BOOL> morph_info_pair_t;
+ typedef std::vector<morph_info_pair_t> morph_info_list_t;
+
+ LLVOAvatarMeshInfo() : mLOD(0), mMinPixelArea(.1f) {}
+ ~LLVOAvatarMeshInfo()
+ {
+ morph_info_list_t::iterator iter;
+ for (iter = mPolyMorphTargetInfoList.begin(); iter != mPolyMorphTargetInfoList.end(); iter++)
+ {
+ delete iter->first;
+ }
+ mPolyMorphTargetInfoList.clear();
+ }
+
+ std::string mType;
+ S32 mLOD;
+ std::string mMeshFileName;
+ std::string mReferenceMeshName;
+ F32 mMinPixelArea;
+ morph_info_list_t mPolyMorphTargetInfoList;
+ };
+ typedef std::vector<LLVOAvatarMeshInfo*> mesh_info_list_t;
+ mesh_info_list_t mMeshInfoList;
+
+ typedef std::vector<LLPolySkeletalDistortionInfo*> skeletal_distortion_info_list_t;
+ skeletal_distortion_info_list_t mSkeletalDistortionInfoList;
+
+ struct LLVOAvatarAttachmentInfo
+ {
+ LLVOAvatarAttachmentInfo()
+ : mGroup(-1), mAttachmentID(-1), mPieMenuSlice(-1), mVisibleFirstPerson(FALSE),
+ mIsHUDAttachment(FALSE), mHasPosition(FALSE), mHasRotation(FALSE) {}
+ std::string mName;
+ std::string mJointName;
+ LLVector3 mPosition;
+ LLVector3 mRotationEuler;
+ S32 mGroup;
+ S32 mAttachmentID;
+ S32 mPieMenuSlice;
+ BOOL mVisibleFirstPerson;
+ BOOL mIsHUDAttachment;
+ BOOL mHasPosition;
+ BOOL mHasRotation;
+ };
+ typedef std::vector<LLVOAvatarAttachmentInfo*> attachment_info_list_t;
+ attachment_info_list_t mAttachmentInfoList;
+
+ LLTexGlobalColorInfo *mTexSkinColorInfo;
+ LLTexGlobalColorInfo *mTexHairColorInfo;
+ LLTexGlobalColorInfo *mTexEyeColorInfo;
+
+ typedef std::vector<LLTexLayerSetInfo*> layer_info_list_t;
+ layer_info_list_t mLayerInfoList;
+
+ typedef std::vector<LLDriverParamInfo*> driver_info_list_t;
+ driver_info_list_t mDriverInfoList;
+
+ struct LLVOAvatarMorphInfo
+ {
+ LLVOAvatarMorphInfo()
+ : mInvert(FALSE) {}
+ std::string mName;
+ std::string mRegion;
+ std::string mLayer;
+ BOOL mInvert;
+ };
+
+ typedef std::vector<LLVOAvatarMorphInfo*> morph_info_list_t;
+ morph_info_list_t mMorphMaskInfoList;
+ };
+
+ struct LLMaskedMorph
+ {
+ LLMaskedMorph(LLPolyMorphTarget *morph_target, BOOL invert, std::string layer) :
+ mMorphTarget(morph_target),
+ mInvert(invert),
+ mLayer(layer)
+ {
+ morph_target->addPendingMorphMask();
+ }
+
+ LLPolyMorphTarget *mMorphTarget;
+ BOOL mInvert;
+ std::string mLayer;
+ };
+
+/** Support classes
+ ** **
+ *******************************************************************************/
+
+}; // LLVOAvatar
+extern const F32 SELF_ADDITIONAL_PRI;
+extern const S32 MAX_TEXTURE_VIRTURE_SIZE_RESET_INTERVAL;
+
+#endif // LL_VO_AVATAR_H
diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp
index 1f9be20c75..b888a263d0 100644
--- a/indra/newview/llvocache.cpp
+++ b/indra/newview/llvocache.cpp
@@ -1,756 +1,756 @@
-/**
- * @file llvocache.cpp
- * @brief Cache of objects on the viewer.
- *
- * $LicenseInfo:firstyear=2003&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "llviewerprecompiledheaders.h"
-#include "llvocache.h"
-#include "llerror.h"
-#include "llregionhandle.h"
-#include "llviewercontrol.h"
-
-BOOL check_read(LLAPRFile* apr_file, void* src, S32 n_bytes)
-{
- return apr_file->read(src, n_bytes) == n_bytes ;
-}
-
-BOOL check_write(LLAPRFile* apr_file, void* src, S32 n_bytes)
-{
- return apr_file->write(src, n_bytes) == n_bytes ;
-}
-
-
-//---------------------------------------------------------------------------
-// LLVOCacheEntry
-//---------------------------------------------------------------------------
-
-LLVOCacheEntry::LLVOCacheEntry(U32 local_id, U32 crc, LLDataPackerBinaryBuffer &dp)
- :
- mLocalID(local_id),
- mCRC(crc),
- mHitCount(0),
- mDupeCount(0),
- mCRCChangeCount(0)
-{
- mBuffer = new U8[dp.getBufferSize()];
- mDP.assignBuffer(mBuffer, dp.getBufferSize());
- mDP = dp;
-}
-
-LLVOCacheEntry::LLVOCacheEntry()
- :
- mLocalID(0),
- mCRC(0),
- mHitCount(0),
- mDupeCount(0),
- mCRCChangeCount(0),
- mBuffer(NULL)
-{
- mDP.assignBuffer(mBuffer, 0);
-}
-
-LLVOCacheEntry::LLVOCacheEntry(LLAPRFile* apr_file)
- : mBuffer(NULL)
-{
- S32 size = -1;
- BOOL success;
-
- success = check_read(apr_file, &mLocalID, sizeof(U32));
- if(success)
- {
- success = check_read(apr_file, &mCRC, sizeof(U32));
- }
- if(success)
- {
- success = check_read(apr_file, &mHitCount, sizeof(S32));
- }
- if(success)
- {
- success = check_read(apr_file, &mDupeCount, sizeof(S32));
- }
- if(success)
- {
- success = check_read(apr_file, &mCRCChangeCount, sizeof(S32));
- }
- if(success)
- {
- success = check_read(apr_file, &size, sizeof(S32));
-
- // Corruption in the cache entries
- if ((size > 10000) || (size < 1))
- {
- // We've got a bogus size, skip reading it.
- // We won't bother seeking, because the rest of this file
- // is likely bogus, and will be tossed anyway.
- llwarns << "Bogus cache entry, size " << size << ", aborting!" << llendl;
- success = FALSE;
- }
- }
- if(success && size > 0)
- {
- mBuffer = new U8[size];
- success = check_read(apr_file, mBuffer, size);
-
- if(success)
- {
- mDP.assignBuffer(mBuffer, size);
- }
- else
- {
- delete[] mBuffer ;
- mBuffer = NULL ;
- }
- }
-
- if(!success)
- {
- mLocalID = 0;
- mCRC = 0;
- mHitCount = 0;
- mDupeCount = 0;
- mCRCChangeCount = 0;
- mBuffer = NULL;
- }
-}
-
-LLVOCacheEntry::~LLVOCacheEntry()
-{
- if(mBuffer)
- {
- delete[] mBuffer;
- }
-}
-
-
-// New CRC means the object has changed.
-void LLVOCacheEntry::assignCRC(U32 crc, LLDataPackerBinaryBuffer &dp)
-{
- if ( (mCRC != crc)
- ||(mDP.getBufferSize() == 0))
- {
- mCRC = crc;
- mHitCount = 0;
- mCRCChangeCount++;
-
- mDP.freeBuffer();
- mBuffer = new U8[dp.getBufferSize()];
- mDP.assignBuffer(mBuffer, dp.getBufferSize());
- mDP = dp;
- }
-}
-
-LLDataPackerBinaryBuffer *LLVOCacheEntry::getDP(U32 crc)
-{
- if ( (mCRC != crc)
- ||(mDP.getBufferSize() == 0))
- {
- //llinfos << "Not getting cache entry, invalid!" << llendl;
- return NULL;
- }
- mHitCount++;
- return &mDP;
-}
-
-
-void LLVOCacheEntry::recordHit()
-{
- mHitCount++;
-}
-
-
-void LLVOCacheEntry::dump() const
-{
- llinfos << "local " << mLocalID
- << " crc " << mCRC
- << " hits " << mHitCount
- << " dupes " << mDupeCount
- << " change " << mCRCChangeCount
- << llendl;
-}
-
-BOOL LLVOCacheEntry::writeToFile(LLAPRFile* apr_file) const
-{
- BOOL success;
- success = check_write(apr_file, (void*)&mLocalID, sizeof(U32));
- if(success)
- {
- success = check_write(apr_file, (void*)&mCRC, sizeof(U32));
- }
- if(success)
- {
- success = check_write(apr_file, (void*)&mHitCount, sizeof(S32));
- }
- if(success)
- {
- success = check_write(apr_file, (void*)&mDupeCount, sizeof(S32));
- }
- if(success)
- {
- success = check_write(apr_file, (void*)&mCRCChangeCount, sizeof(S32));
- }
- if(success)
- {
- S32 size = mDP.getBufferSize();
- success = check_write(apr_file, (void*)&size, sizeof(S32));
-
- if(success)
- {
- success = check_write(apr_file, (void*)mBuffer, size);
- }
- }
-
- return success ;
-}
-
-//-------------------------------------------------------------------
-//LLVOCache
-//-------------------------------------------------------------------
-// Format string used to construct filename for the object cache
-static const char OBJECT_CACHE_FILENAME[] = "objects_%d_%d.slc";
-
-const U32 MAX_NUM_OBJECT_ENTRIES = 128 ;
-const U32 MIN_ENTRIES_TO_PURGE = 16 ;
-const U32 INVALID_TIME = 0 ;
-const char* object_cache_dirname = "objectcache";
-const char* header_filename = "object.cache";
-
-LLVOCache* LLVOCache::sInstance = NULL;
-
-//static
-LLVOCache* LLVOCache::getInstance()
-{
- if(!sInstance)
- {
- sInstance = new LLVOCache() ;
- }
- return sInstance ;
-}
-
-//static
-BOOL LLVOCache::hasInstance()
-{
- return sInstance != NULL ;
-}
-
-//static
-void LLVOCache::destroyClass()
-{
- if(sInstance)
- {
- delete sInstance ;
- sInstance = NULL ;
- }
-}
-
-LLVOCache::LLVOCache():
- mInitialized(FALSE),
- mReadOnly(TRUE),
- mNumEntries(0),
- mCacheSize(1)
-{
- mEnabled = gSavedSettings.getBOOL("ObjectCacheEnabled");
- mLocalAPRFilePoolp = new LLVolatileAPRPool() ;
-}
-
-LLVOCache::~LLVOCache()
-{
- if(mEnabled)
- {
- writeCacheHeader();
- clearCacheInMemory();
- }
- delete mLocalAPRFilePoolp;
-}
-
-void LLVOCache::setDirNames(ELLPath location)
-{
- std::string delem = gDirUtilp->getDirDelimiter();
-
- mHeaderFileName = gDirUtilp->getExpandedFilename(location, object_cache_dirname, header_filename);
- mObjectCacheDirName = gDirUtilp->getExpandedFilename(location, object_cache_dirname);
-}
-
-void LLVOCache::initCache(ELLPath location, U32 size, U32 cache_version)
-{
- if(!mEnabled)
- {
- llwarns << "Not initializing cache: Cache is currently disabled." << llendl;
- return ;
- }
-
- if(mInitialized)
- {
- llwarns << "Cache already initialized." << llendl;
- return ;
- }
- mInitialized = TRUE ;
-
- setDirNames(location);
- if (!mReadOnly)
- {
- LLFile::mkdir(mObjectCacheDirName);
- }
- mCacheSize = llclamp(size, MIN_ENTRIES_TO_PURGE, MAX_NUM_OBJECT_ENTRIES);
- mMetaInfo.mVersion = cache_version;
- readCacheHeader();
-
- if(mMetaInfo.mVersion != cache_version)
- {
- mMetaInfo.mVersion = cache_version ;
- if(mReadOnly) //disable cache
- {
- clearCacheInMemory();
- }
- else //delete the current cache if the format does not match.
- {
- removeCache();
- }
- }
-}
-
-void LLVOCache::removeCache(ELLPath location)
-{
- if(mReadOnly)
- {
- llwarns << "Not removing cache at " << location << ": Cache is currently in read-only mode." << llendl;
- return ;
- }
-
- llinfos << "about to remove the object cache due to settings." << llendl ;
-
- std::string delem = gDirUtilp->getDirDelimiter();
- std::string mask = delem + "*";
- std::string cache_dir = gDirUtilp->getExpandedFilename(location, object_cache_dirname);
- llinfos << "Removing cache at " << cache_dir << llendl;
- gDirUtilp->deleteFilesInDir(cache_dir, mask); //delete all files
- LLFile::rmdir(cache_dir);
-
- clearCacheInMemory();
- mInitialized = FALSE ;
-}
-
-void LLVOCache::removeCache()
-{
- llassert_always(mInitialized) ;
- if(mReadOnly)
- {
- llwarns << "Not clearing object cache: Cache is currently in read-only mode." << llendl;
- return ;
- }
-
- llinfos << "about to remove the object cache due to some error." << llendl ;
-
- std::string delem = gDirUtilp->getDirDelimiter();
- std::string mask = delem + "*";
- llinfos << "Removing cache at " << mObjectCacheDirName << llendl;
- gDirUtilp->deleteFilesInDir(mObjectCacheDirName, mask);
-
- clearCacheInMemory() ;
- writeCacheHeader();
-}
-
-void LLVOCache::removeEntry(HeaderEntryInfo* entry)
-{
- llassert_always(mInitialized) ;
- if(mReadOnly)
- {
- return ;
- }
- if(!entry)
- {
- return ;
- }
-
- header_entry_queue_t::iterator iter = mHeaderEntryQueue.find(entry) ;
- if(iter != mHeaderEntryQueue.end())
- {
- mHandleEntryMap.erase(entry->mHandle) ;
- mHeaderEntryQueue.erase(iter) ;
- removeFromCache(entry) ;
- delete entry ;
-
- mNumEntries = mHandleEntryMap.size() ;
- }
-}
-
-void LLVOCache::removeEntry(U64 handle)
-{
- handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
- if(iter == mHandleEntryMap.end()) //no cache
- {
- return ;
- }
- HeaderEntryInfo* entry = iter->second ;
- removeEntry(entry) ;
-}
-
-void LLVOCache::clearCacheInMemory()
-{
- if(!mHeaderEntryQueue.empty())
- {
- for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin(); iter != mHeaderEntryQueue.end(); ++iter)
- {
- delete *iter ;
- }
- mHeaderEntryQueue.clear();
- mHandleEntryMap.clear();
- mNumEntries = 0 ;
- }
-
-}
-
-void LLVOCache::getObjectCacheFilename(U64 handle, std::string& filename)
-{
- U32 region_x, region_y;
-
- grid_from_region_handle(handle, &region_x, &region_y);
- filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, object_cache_dirname,
- llformat(OBJECT_CACHE_FILENAME, region_x, region_y));
-
- return ;
-}
-
-void LLVOCache::removeFromCache(HeaderEntryInfo* entry)
-{
- if(mReadOnly)
- {
- llwarns << "Not removing cache for handle " << entry->mHandle << ": Cache is currently in read-only mode." << llendl;
- return ;
- }
-
- std::string filename;
- getObjectCacheFilename(entry->mHandle, filename);
- LLAPRFile::remove(filename, mLocalAPRFilePoolp);
- entry->mTime = INVALID_TIME ;
- updateEntry(entry) ; //update the head file.
-}
-
-void LLVOCache::readCacheHeader()
-{
- if(!mEnabled)
- {
- llwarns << "Not reading cache header: Cache is currently disabled." << llendl;
- return;
- }
-
- //clear stale info.
- clearCacheInMemory();
-
- bool success = true ;
- if (LLAPRFile::isExist(mHeaderFileName, mLocalAPRFilePoolp))
- {
- LLAPRFile apr_file(mHeaderFileName, APR_READ|APR_BINARY, mLocalAPRFilePoolp);
-
- //read the meta element
- success = check_read(&apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)) ;
-
- if(success)
- {
- HeaderEntryInfo* entry = NULL ;
- mNumEntries = 0 ;
- U32 num_read = 0 ;
- while(num_read++ < MAX_NUM_OBJECT_ENTRIES)
- {
- if(!entry)
- {
- entry = new HeaderEntryInfo() ;
- }
- success = check_read(&apr_file, entry, sizeof(HeaderEntryInfo));
-
- if(!success) //failed
- {
- llwarns << "Error reading cache header entry. (entry_index=" << mNumEntries << ")" << llendl;
- delete entry ;
- entry = NULL ;
- break ;
- }
- else if(entry->mTime == INVALID_TIME)
- {
- continue ; //an empty entry
- }
-
- entry->mIndex = mNumEntries++ ;
- mHeaderEntryQueue.insert(entry) ;
- mHandleEntryMap[entry->mHandle] = entry ;
- entry = NULL ;
- }
- if(entry)
- {
- delete entry ;
- }
- }
-
- //---------
- //debug code
- //----------
- //std::string name ;
- //for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ; success && iter != mHeaderEntryQueue.end(); ++iter)
- //{
- // getObjectCacheFilename((*iter)->mHandle, name) ;
- // llinfos << name << llendl ;
- //}
- //-----------
- }
- else
- {
- writeCacheHeader() ;
- }
-
- if(!success)
- {
- removeCache() ; //failed to read header, clear the cache
- }
- else if(mNumEntries >= mCacheSize)
- {
- purgeEntries(mCacheSize) ;
- }
-
- return ;
-}
-
-void LLVOCache::writeCacheHeader()
-{
- if (!mEnabled)
- {
- llwarns << "Not writing cache header: Cache is currently disabled." << llendl;
- return;
- }
-
- if(mReadOnly)
- {
- llwarns << "Not writing cache header: Cache is currently in read-only mode." << llendl;
- return;
- }
-
- bool success = true ;
- {
- LLAPRFile apr_file(mHeaderFileName, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
-
- //write the meta element
- success = check_write(&apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)) ;
-
-
- mNumEntries = 0 ;
- for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ; success && iter != mHeaderEntryQueue.end(); ++iter)
- {
- (*iter)->mIndex = mNumEntries++ ;
- success = check_write(&apr_file, (void*)*iter, sizeof(HeaderEntryInfo));
- }
-
- mNumEntries = mHeaderEntryQueue.size() ;
- if(success && mNumEntries < MAX_NUM_OBJECT_ENTRIES)
- {
- HeaderEntryInfo* entry = new HeaderEntryInfo() ;
- entry->mTime = INVALID_TIME ;
- for(S32 i = mNumEntries ; success && i < MAX_NUM_OBJECT_ENTRIES ; i++)
- {
- //fill the cache with the default entry.
- success = check_write(&apr_file, entry, sizeof(HeaderEntryInfo)) ;
-
- }
- delete entry ;
- }
- }
-
- if(!success)
- {
- clearCacheInMemory() ;
- mReadOnly = TRUE ; //disable the cache.
- }
- return ;
-}
-
-BOOL LLVOCache::updateEntry(const HeaderEntryInfo* entry)
-{
- LLAPRFile apr_file(mHeaderFileName, APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
- apr_file.seek(APR_SET, entry->mIndex * sizeof(HeaderEntryInfo) + sizeof(HeaderMetaInfo)) ;
-
- return check_write(&apr_file, (void*)entry, sizeof(HeaderEntryInfo)) ;
-}
-
-void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_entry_map_t& cache_entry_map)
-{
- if(!mEnabled)
- {
- llwarns << "Not reading cache for handle " << handle << "): Cache is currently disabled." << llendl;
- return ;
- }
- llassert_always(mInitialized);
-
- handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
- if(iter == mHandleEntryMap.end()) //no cache
- {
- llwarns << "No handle map entry for " << handle << llendl;
- return ;
- }
-
- bool success = true ;
- {
- std::string filename;
- getObjectCacheFilename(handle, filename);
- LLAPRFile apr_file(filename, APR_READ|APR_BINARY, mLocalAPRFilePoolp);
-
- LLUUID cache_id ;
- success = check_read(&apr_file, cache_id.mData, UUID_BYTES) ;
-
- if(success)
- {
- if(cache_id != id)
- {
- llinfos << "Cache ID doesn't match for this region, discarding"<< llendl;
- success = false ;
- }
-
- if(success)
- {
- S32 num_entries;
- success = check_read(&apr_file, &num_entries, sizeof(S32)) ;
-
- for (S32 i = 0; success && i < num_entries; i++)
- {
- LLVOCacheEntry* entry = new LLVOCacheEntry(&apr_file);
- if (!entry->getLocalID())
- {
- llwarns << "Aborting cache file load for " << filename << ", cache file corruption!" << llendl;
- delete entry ;
- success = false ;
- }
- cache_entry_map[entry->getLocalID()] = entry;
- }
- }
- }
- }
-
- if(!success)
- {
- if(cache_entry_map.empty())
- {
- removeEntry(iter->second) ;
- }
- }
-
- return ;
-}
-
-void LLVOCache::purgeEntries(U32 size)
-{
- while(mHeaderEntryQueue.size() > size)
- {
- header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ;
- HeaderEntryInfo* entry = *iter ;
- mHandleEntryMap.erase(entry->mHandle);
- mHeaderEntryQueue.erase(iter) ;
- removeFromCache(entry) ;
- delete entry;
- }
- mNumEntries = mHandleEntryMap.size() ;
-}
-
-void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry::vocache_entry_map_t& cache_entry_map, BOOL dirty_cache)
-{
- if(!mEnabled)
- {
- llwarns << "Not writing cache for handle " << handle << "): Cache is currently disabled." << llendl;
- return ;
- }
- llassert_always(mInitialized);
-
- if(mReadOnly)
- {
- llwarns << "Not writing cache for handle " << handle << "): Cache is currently in read-only mode." << llendl;
- return ;
- }
-
- HeaderEntryInfo* entry;
- handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
- if(iter == mHandleEntryMap.end()) //new entry
- {
- if(mNumEntries >= mCacheSize - 1)
- {
- purgeEntries(mCacheSize - 1) ;
- }
-
- entry = new HeaderEntryInfo();
- entry->mHandle = handle ;
- entry->mTime = time(NULL) ;
- entry->mIndex = mNumEntries++;
- mHeaderEntryQueue.insert(entry) ;
- mHandleEntryMap[handle] = entry ;
- }
- else
- {
- // Update access time.
- entry = iter->second ;
-
- //resort
- mHeaderEntryQueue.erase(entry) ;
-
- entry->mTime = time(NULL) ;
- mHeaderEntryQueue.insert(entry) ;
- }
-
- //update cache header
- if(!updateEntry(entry))
- {
- llwarns << "Failed to update cache header index " << entry->mIndex << ". handle = " << handle << llendl;
- return ; //update failed.
- }
-
- if(!dirty_cache)
- {
- llwarns << "Skipping write to cache for handle " << handle << ": cache not dirty" << llendl;
- return ; //nothing changed, no need to update.
- }
-
- //write to cache file
- bool success = true ;
- {
- std::string filename;
- getObjectCacheFilename(handle, filename);
- LLAPRFile apr_file(filename, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
-
- success = check_write(&apr_file, (void*)id.mData, UUID_BYTES) ;
-
-
- if(success)
- {
- S32 num_entries = cache_entry_map.size() ;
- success = check_write(&apr_file, &num_entries, sizeof(S32));
-
- for (LLVOCacheEntry::vocache_entry_map_t::const_iterator iter = cache_entry_map.begin(); success && iter != cache_entry_map.end(); ++iter)
- {
- success = iter->second->writeToFile(&apr_file) ;
- }
- }
- }
-
- if(!success)
- {
- removeEntry(entry) ;
-
- }
-
- return ;
-}
-
+/**
+ * @file llvocache.cpp
+ * @brief Cache of objects on the viewer.
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llvocache.h"
+#include "llerror.h"
+#include "llregionhandle.h"
+#include "llviewercontrol.h"
+
+BOOL check_read(LLAPRFile* apr_file, void* src, S32 n_bytes)
+{
+ return apr_file->read(src, n_bytes) == n_bytes ;
+}
+
+BOOL check_write(LLAPRFile* apr_file, void* src, S32 n_bytes)
+{
+ return apr_file->write(src, n_bytes) == n_bytes ;
+}
+
+
+//---------------------------------------------------------------------------
+// LLVOCacheEntry
+//---------------------------------------------------------------------------
+
+LLVOCacheEntry::LLVOCacheEntry(U32 local_id, U32 crc, LLDataPackerBinaryBuffer &dp)
+ :
+ mLocalID(local_id),
+ mCRC(crc),
+ mHitCount(0),
+ mDupeCount(0),
+ mCRCChangeCount(0)
+{
+ mBuffer = new U8[dp.getBufferSize()];
+ mDP.assignBuffer(mBuffer, dp.getBufferSize());
+ mDP = dp;
+}
+
+LLVOCacheEntry::LLVOCacheEntry()
+ :
+ mLocalID(0),
+ mCRC(0),
+ mHitCount(0),
+ mDupeCount(0),
+ mCRCChangeCount(0),
+ mBuffer(NULL)
+{
+ mDP.assignBuffer(mBuffer, 0);
+}
+
+LLVOCacheEntry::LLVOCacheEntry(LLAPRFile* apr_file)
+ : mBuffer(NULL)
+{
+ S32 size = -1;
+ BOOL success;
+
+ success = check_read(apr_file, &mLocalID, sizeof(U32));
+ if(success)
+ {
+ success = check_read(apr_file, &mCRC, sizeof(U32));
+ }
+ if(success)
+ {
+ success = check_read(apr_file, &mHitCount, sizeof(S32));
+ }
+ if(success)
+ {
+ success = check_read(apr_file, &mDupeCount, sizeof(S32));
+ }
+ if(success)
+ {
+ success = check_read(apr_file, &mCRCChangeCount, sizeof(S32));
+ }
+ if(success)
+ {
+ success = check_read(apr_file, &size, sizeof(S32));
+
+ // Corruption in the cache entries
+ if ((size > 10000) || (size < 1))
+ {
+ // We've got a bogus size, skip reading it.
+ // We won't bother seeking, because the rest of this file
+ // is likely bogus, and will be tossed anyway.
+ llwarns << "Bogus cache entry, size " << size << ", aborting!" << llendl;
+ success = FALSE;
+ }
+ }
+ if(success && size > 0)
+ {
+ mBuffer = new U8[size];
+ success = check_read(apr_file, mBuffer, size);
+
+ if(success)
+ {
+ mDP.assignBuffer(mBuffer, size);
+ }
+ else
+ {
+ delete[] mBuffer ;
+ mBuffer = NULL ;
+ }
+ }
+
+ if(!success)
+ {
+ mLocalID = 0;
+ mCRC = 0;
+ mHitCount = 0;
+ mDupeCount = 0;
+ mCRCChangeCount = 0;
+ mBuffer = NULL;
+ }
+}
+
+LLVOCacheEntry::~LLVOCacheEntry()
+{
+ if(mBuffer)
+ {
+ delete[] mBuffer;
+ }
+}
+
+
+// New CRC means the object has changed.
+void LLVOCacheEntry::assignCRC(U32 crc, LLDataPackerBinaryBuffer &dp)
+{
+ if ( (mCRC != crc)
+ ||(mDP.getBufferSize() == 0))
+ {
+ mCRC = crc;
+ mHitCount = 0;
+ mCRCChangeCount++;
+
+ mDP.freeBuffer();
+ mBuffer = new U8[dp.getBufferSize()];
+ mDP.assignBuffer(mBuffer, dp.getBufferSize());
+ mDP = dp;
+ }
+}
+
+LLDataPackerBinaryBuffer *LLVOCacheEntry::getDP(U32 crc)
+{
+ if ( (mCRC != crc)
+ ||(mDP.getBufferSize() == 0))
+ {
+ //llinfos << "Not getting cache entry, invalid!" << llendl;
+ return NULL;
+ }
+ mHitCount++;
+ return &mDP;
+}
+
+
+void LLVOCacheEntry::recordHit()
+{
+ mHitCount++;
+}
+
+
+void LLVOCacheEntry::dump() const
+{
+ llinfos << "local " << mLocalID
+ << " crc " << mCRC
+ << " hits " << mHitCount
+ << " dupes " << mDupeCount
+ << " change " << mCRCChangeCount
+ << llendl;
+}
+
+BOOL LLVOCacheEntry::writeToFile(LLAPRFile* apr_file) const
+{
+ BOOL success;
+ success = check_write(apr_file, (void*)&mLocalID, sizeof(U32));
+ if(success)
+ {
+ success = check_write(apr_file, (void*)&mCRC, sizeof(U32));
+ }
+ if(success)
+ {
+ success = check_write(apr_file, (void*)&mHitCount, sizeof(S32));
+ }
+ if(success)
+ {
+ success = check_write(apr_file, (void*)&mDupeCount, sizeof(S32));
+ }
+ if(success)
+ {
+ success = check_write(apr_file, (void*)&mCRCChangeCount, sizeof(S32));
+ }
+ if(success)
+ {
+ S32 size = mDP.getBufferSize();
+ success = check_write(apr_file, (void*)&size, sizeof(S32));
+
+ if(success)
+ {
+ success = check_write(apr_file, (void*)mBuffer, size);
+ }
+ }
+
+ return success ;
+}
+
+//-------------------------------------------------------------------
+//LLVOCache
+//-------------------------------------------------------------------
+// Format string used to construct filename for the object cache
+static const char OBJECT_CACHE_FILENAME[] = "objects_%d_%d.slc";
+
+const U32 MAX_NUM_OBJECT_ENTRIES = 128 ;
+const U32 MIN_ENTRIES_TO_PURGE = 16 ;
+const U32 INVALID_TIME = 0 ;
+const char* object_cache_dirname = "objectcache";
+const char* header_filename = "object.cache";
+
+LLVOCache* LLVOCache::sInstance = NULL;
+
+//static
+LLVOCache* LLVOCache::getInstance()
+{
+ if(!sInstance)
+ {
+ sInstance = new LLVOCache() ;
+ }
+ return sInstance ;
+}
+
+//static
+BOOL LLVOCache::hasInstance()
+{
+ return sInstance != NULL ;
+}
+
+//static
+void LLVOCache::destroyClass()
+{
+ if(sInstance)
+ {
+ delete sInstance ;
+ sInstance = NULL ;
+ }
+}
+
+LLVOCache::LLVOCache():
+ mInitialized(FALSE),
+ mReadOnly(TRUE),
+ mNumEntries(0),
+ mCacheSize(1)
+{
+ mEnabled = gSavedSettings.getBOOL("ObjectCacheEnabled");
+ mLocalAPRFilePoolp = new LLVolatileAPRPool() ;
+}
+
+LLVOCache::~LLVOCache()
+{
+ if(mEnabled)
+ {
+ writeCacheHeader();
+ clearCacheInMemory();
+ }
+ delete mLocalAPRFilePoolp;
+}
+
+void LLVOCache::setDirNames(ELLPath location)
+{
+ std::string delem = gDirUtilp->getDirDelimiter();
+
+ mHeaderFileName = gDirUtilp->getExpandedFilename(location, object_cache_dirname, header_filename);
+ mObjectCacheDirName = gDirUtilp->getExpandedFilename(location, object_cache_dirname);
+}
+
+void LLVOCache::initCache(ELLPath location, U32 size, U32 cache_version)
+{
+ if(!mEnabled)
+ {
+ llwarns << "Not initializing cache: Cache is currently disabled." << llendl;
+ return ;
+ }
+
+ if(mInitialized)
+ {
+ llwarns << "Cache already initialized." << llendl;
+ return ;
+ }
+ mInitialized = TRUE ;
+
+ setDirNames(location);
+ if (!mReadOnly)
+ {
+ LLFile::mkdir(mObjectCacheDirName);
+ }
+ mCacheSize = llclamp(size, MIN_ENTRIES_TO_PURGE, MAX_NUM_OBJECT_ENTRIES);
+ mMetaInfo.mVersion = cache_version;
+ readCacheHeader();
+
+ if(mMetaInfo.mVersion != cache_version)
+ {
+ mMetaInfo.mVersion = cache_version ;
+ if(mReadOnly) //disable cache
+ {
+ clearCacheInMemory();
+ }
+ else //delete the current cache if the format does not match.
+ {
+ removeCache();
+ }
+ }
+}
+
+void LLVOCache::removeCache(ELLPath location)
+{
+ if(mReadOnly)
+ {
+ llwarns << "Not removing cache at " << location << ": Cache is currently in read-only mode." << llendl;
+ return ;
+ }
+
+ llinfos << "about to remove the object cache due to settings." << llendl ;
+
+ std::string delem = gDirUtilp->getDirDelimiter();
+ std::string mask = delem + "*";
+ std::string cache_dir = gDirUtilp->getExpandedFilename(location, object_cache_dirname);
+ llinfos << "Removing cache at " << cache_dir << llendl;
+ gDirUtilp->deleteFilesInDir(cache_dir, mask); //delete all files
+ LLFile::rmdir(cache_dir);
+
+ clearCacheInMemory();
+ mInitialized = FALSE ;
+}
+
+void LLVOCache::removeCache()
+{
+ llassert_always(mInitialized) ;
+ if(mReadOnly)
+ {
+ llwarns << "Not clearing object cache: Cache is currently in read-only mode." << llendl;
+ return ;
+ }
+
+ llinfos << "about to remove the object cache due to some error." << llendl ;
+
+ std::string delem = gDirUtilp->getDirDelimiter();
+ std::string mask = delem + "*";
+ llinfos << "Removing cache at " << mObjectCacheDirName << llendl;
+ gDirUtilp->deleteFilesInDir(mObjectCacheDirName, mask);
+
+ clearCacheInMemory() ;
+ writeCacheHeader();
+}
+
+void LLVOCache::removeEntry(HeaderEntryInfo* entry)
+{
+ llassert_always(mInitialized) ;
+ if(mReadOnly)
+ {
+ return ;
+ }
+ if(!entry)
+ {
+ return ;
+ }
+
+ header_entry_queue_t::iterator iter = mHeaderEntryQueue.find(entry) ;
+ if(iter != mHeaderEntryQueue.end())
+ {
+ mHandleEntryMap.erase(entry->mHandle) ;
+ mHeaderEntryQueue.erase(iter) ;
+ removeFromCache(entry) ;
+ delete entry ;
+
+ mNumEntries = mHandleEntryMap.size() ;
+ }
+}
+
+void LLVOCache::removeEntry(U64 handle)
+{
+ handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
+ if(iter == mHandleEntryMap.end()) //no cache
+ {
+ return ;
+ }
+ HeaderEntryInfo* entry = iter->second ;
+ removeEntry(entry) ;
+}
+
+void LLVOCache::clearCacheInMemory()
+{
+ if(!mHeaderEntryQueue.empty())
+ {
+ for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin(); iter != mHeaderEntryQueue.end(); ++iter)
+ {
+ delete *iter ;
+ }
+ mHeaderEntryQueue.clear();
+ mHandleEntryMap.clear();
+ mNumEntries = 0 ;
+ }
+
+}
+
+void LLVOCache::getObjectCacheFilename(U64 handle, std::string& filename)
+{
+ U32 region_x, region_y;
+
+ grid_from_region_handle(handle, &region_x, &region_y);
+ filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, object_cache_dirname,
+ llformat(OBJECT_CACHE_FILENAME, region_x, region_y));
+
+ return ;
+}
+
+void LLVOCache::removeFromCache(HeaderEntryInfo* entry)
+{
+ if(mReadOnly)
+ {
+ llwarns << "Not removing cache for handle " << entry->mHandle << ": Cache is currently in read-only mode." << llendl;
+ return ;
+ }
+
+ std::string filename;
+ getObjectCacheFilename(entry->mHandle, filename);
+ LLAPRFile::remove(filename, mLocalAPRFilePoolp);
+ entry->mTime = INVALID_TIME ;
+ updateEntry(entry) ; //update the head file.
+}
+
+void LLVOCache::readCacheHeader()
+{
+ if(!mEnabled)
+ {
+ llwarns << "Not reading cache header: Cache is currently disabled." << llendl;
+ return;
+ }
+
+ //clear stale info.
+ clearCacheInMemory();
+
+ bool success = true ;
+ if (LLAPRFile::isExist(mHeaderFileName, mLocalAPRFilePoolp))
+ {
+ LLAPRFile apr_file(mHeaderFileName, APR_READ|APR_BINARY, mLocalAPRFilePoolp);
+
+ //read the meta element
+ success = check_read(&apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)) ;
+
+ if(success)
+ {
+ HeaderEntryInfo* entry = NULL ;
+ mNumEntries = 0 ;
+ U32 num_read = 0 ;
+ while(num_read++ < MAX_NUM_OBJECT_ENTRIES)
+ {
+ if(!entry)
+ {
+ entry = new HeaderEntryInfo() ;
+ }
+ success = check_read(&apr_file, entry, sizeof(HeaderEntryInfo));
+
+ if(!success) //failed
+ {
+ llwarns << "Error reading cache header entry. (entry_index=" << mNumEntries << ")" << llendl;
+ delete entry ;
+ entry = NULL ;
+ break ;
+ }
+ else if(entry->mTime == INVALID_TIME)
+ {
+ continue ; //an empty entry
+ }
+
+ entry->mIndex = mNumEntries++ ;
+ mHeaderEntryQueue.insert(entry) ;
+ mHandleEntryMap[entry->mHandle] = entry ;
+ entry = NULL ;
+ }
+ if(entry)
+ {
+ delete entry ;
+ }
+ }
+
+ //---------
+ //debug code
+ //----------
+ //std::string name ;
+ //for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ; success && iter != mHeaderEntryQueue.end(); ++iter)
+ //{
+ // getObjectCacheFilename((*iter)->mHandle, name) ;
+ // llinfos << name << llendl ;
+ //}
+ //-----------
+ }
+ else
+ {
+ writeCacheHeader() ;
+ }
+
+ if(!success)
+ {
+ removeCache() ; //failed to read header, clear the cache
+ }
+ else if(mNumEntries >= mCacheSize)
+ {
+ purgeEntries(mCacheSize) ;
+ }
+
+ return ;
+}
+
+void LLVOCache::writeCacheHeader()
+{
+ if (!mEnabled)
+ {
+ llwarns << "Not writing cache header: Cache is currently disabled." << llendl;
+ return;
+ }
+
+ if(mReadOnly)
+ {
+ llwarns << "Not writing cache header: Cache is currently in read-only mode." << llendl;
+ return;
+ }
+
+ bool success = true ;
+ {
+ LLAPRFile apr_file(mHeaderFileName, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
+
+ //write the meta element
+ success = check_write(&apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)) ;
+
+
+ mNumEntries = 0 ;
+ for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ; success && iter != mHeaderEntryQueue.end(); ++iter)
+ {
+ (*iter)->mIndex = mNumEntries++ ;
+ success = check_write(&apr_file, (void*)*iter, sizeof(HeaderEntryInfo));
+ }
+
+ mNumEntries = mHeaderEntryQueue.size() ;
+ if(success && mNumEntries < MAX_NUM_OBJECT_ENTRIES)
+ {
+ HeaderEntryInfo* entry = new HeaderEntryInfo() ;
+ entry->mTime = INVALID_TIME ;
+ for(S32 i = mNumEntries ; success && i < MAX_NUM_OBJECT_ENTRIES ; i++)
+ {
+ //fill the cache with the default entry.
+ success = check_write(&apr_file, entry, sizeof(HeaderEntryInfo)) ;
+
+ }
+ delete entry ;
+ }
+ }
+
+ if(!success)
+ {
+ clearCacheInMemory() ;
+ mReadOnly = TRUE ; //disable the cache.
+ }
+ return ;
+}
+
+BOOL LLVOCache::updateEntry(const HeaderEntryInfo* entry)
+{
+ LLAPRFile apr_file(mHeaderFileName, APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
+ apr_file.seek(APR_SET, entry->mIndex * sizeof(HeaderEntryInfo) + sizeof(HeaderMetaInfo)) ;
+
+ return check_write(&apr_file, (void*)entry, sizeof(HeaderEntryInfo)) ;
+}
+
+void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_entry_map_t& cache_entry_map)
+{
+ if(!mEnabled)
+ {
+ llwarns << "Not reading cache for handle " << handle << "): Cache is currently disabled." << llendl;
+ return ;
+ }
+ llassert_always(mInitialized);
+
+ handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
+ if(iter == mHandleEntryMap.end()) //no cache
+ {
+ llwarns << "No handle map entry for " << handle << llendl;
+ return ;
+ }
+
+ bool success = true ;
+ {
+ std::string filename;
+ getObjectCacheFilename(handle, filename);
+ LLAPRFile apr_file(filename, APR_READ|APR_BINARY, mLocalAPRFilePoolp);
+
+ LLUUID cache_id ;
+ success = check_read(&apr_file, cache_id.mData, UUID_BYTES) ;
+
+ if(success)
+ {
+ if(cache_id != id)
+ {
+ llinfos << "Cache ID doesn't match for this region, discarding"<< llendl;
+ success = false ;
+ }
+
+ if(success)
+ {
+ S32 num_entries;
+ success = check_read(&apr_file, &num_entries, sizeof(S32)) ;
+
+ for (S32 i = 0; success && i < num_entries; i++)
+ {
+ LLVOCacheEntry* entry = new LLVOCacheEntry(&apr_file);
+ if (!entry->getLocalID())
+ {
+ llwarns << "Aborting cache file load for " << filename << ", cache file corruption!" << llendl;
+ delete entry ;
+ success = false ;
+ }
+ cache_entry_map[entry->getLocalID()] = entry;
+ }
+ }
+ }
+ }
+
+ if(!success)
+ {
+ if(cache_entry_map.empty())
+ {
+ removeEntry(iter->second) ;
+ }
+ }
+
+ return ;
+}
+
+void LLVOCache::purgeEntries(U32 size)
+{
+ while(mHeaderEntryQueue.size() > size)
+ {
+ header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ;
+ HeaderEntryInfo* entry = *iter ;
+ mHandleEntryMap.erase(entry->mHandle);
+ mHeaderEntryQueue.erase(iter) ;
+ removeFromCache(entry) ;
+ delete entry;
+ }
+ mNumEntries = mHandleEntryMap.size() ;
+}
+
+void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry::vocache_entry_map_t& cache_entry_map, BOOL dirty_cache)
+{
+ if(!mEnabled)
+ {
+ llwarns << "Not writing cache for handle " << handle << "): Cache is currently disabled." << llendl;
+ return ;
+ }
+ llassert_always(mInitialized);
+
+ if(mReadOnly)
+ {
+ llwarns << "Not writing cache for handle " << handle << "): Cache is currently in read-only mode." << llendl;
+ return ;
+ }
+
+ HeaderEntryInfo* entry;
+ handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
+ if(iter == mHandleEntryMap.end()) //new entry
+ {
+ if(mNumEntries >= mCacheSize - 1)
+ {
+ purgeEntries(mCacheSize - 1) ;
+ }
+
+ entry = new HeaderEntryInfo();
+ entry->mHandle = handle ;
+ entry->mTime = time(NULL) ;
+ entry->mIndex = mNumEntries++;
+ mHeaderEntryQueue.insert(entry) ;
+ mHandleEntryMap[handle] = entry ;
+ }
+ else
+ {
+ // Update access time.
+ entry = iter->second ;
+
+ //resort
+ mHeaderEntryQueue.erase(entry) ;
+
+ entry->mTime = time(NULL) ;
+ mHeaderEntryQueue.insert(entry) ;
+ }
+
+ //update cache header
+ if(!updateEntry(entry))
+ {
+ llwarns << "Failed to update cache header index " << entry->mIndex << ". handle = " << handle << llendl;
+ return ; //update failed.
+ }
+
+ if(!dirty_cache)
+ {
+ llwarns << "Skipping write to cache for handle " << handle << ": cache not dirty" << llendl;
+ return ; //nothing changed, no need to update.
+ }
+
+ //write to cache file
+ bool success = true ;
+ {
+ std::string filename;
+ getObjectCacheFilename(handle, filename);
+ LLAPRFile apr_file(filename, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
+
+ success = check_write(&apr_file, (void*)id.mData, UUID_BYTES) ;
+
+
+ if(success)
+ {
+ S32 num_entries = cache_entry_map.size() ;
+ success = check_write(&apr_file, &num_entries, sizeof(S32));
+
+ for (LLVOCacheEntry::vocache_entry_map_t::const_iterator iter = cache_entry_map.begin(); success && iter != cache_entry_map.end(); ++iter)
+ {
+ success = iter->second->writeToFile(&apr_file) ;
+ }
+ }
+ }
+
+ if(!success)
+ {
+ removeEntry(entry) ;
+
+ }
+
+ return ;
+}
+