From a399452d20eebca112e9ec926e8d69a114a3d3eb Mon Sep 17 00:00:00 2001 From: Shyotl Date: Wed, 20 Jul 2011 23:52:23 -0500 Subject: [PATCH 01/22] Null joint states are getting in the joint map somehow. Added assertions to hunt this down. --- indra/llcharacter/llpose.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/indra/llcharacter/llpose.cpp b/indra/llcharacter/llpose.cpp index 8c7cba0a1..b770b05a7 100644 --- a/indra/llcharacter/llpose.cpp +++ b/indra/llcharacter/llpose.cpp @@ -89,6 +89,7 @@ LLJointState *LLPose::getNextJointState() //----------------------------------------------------------------------------- BOOL LLPose::addJointState(const LLPointer& jointState) { + llassert_always(jointState.notNull()); if (mJointMap.find(jointState->getJoint()->getName()) == mJointMap.end()) { mJointMap[jointState->getJoint()->getName()] = jointState; @@ -161,6 +162,9 @@ void LLPose::setWeight(F32 weight) // // there was a crash here // + llassert_always(iter->second.notNull()); + if(!iter->second) //uhoh... + continue; iter->second->setWeight(weight); } mWeight = weight; From dfa850931ac9e4a0cdc7dee0e1be83a026ca4dd2 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Wed, 20 Jul 2011 23:53:09 -0500 Subject: [PATCH 02/22] Fleshed out llvolume a bit more. Added some utility functions. --- indra/llmath/llvolume.cpp | 319 +++++++++++++++++++++++++++++++++++++- indra/llmath/llvolume.h | 17 +- 2 files changed, 332 insertions(+), 4 deletions(-) diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 674ffa4cb..5ad15bd9b 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -405,6 +405,70 @@ LLProfile::Face* LLProfile::addFace(S32 i, S32 count, F32 scaleU, S16 faceID, BO return face; } +//static +S32 LLProfile::getNumNGonPoints(const LLProfileParams& params, S32 sides, F32 offset, F32 bevel, F32 ang_scale, S32 split) +{ // this is basically LLProfile::genNGon stripped down to only the operations that influence the number of points + LLMemType m1(LLMemType::MTYPE_VOLUME); + S32 np = 0; + + // Generate an n-sided "circular" path. + // 0 is (1,0), and we go counter-clockwise along a circular path from there. + F32 t, t_step, t_first, t_fraction; + + F32 begin = params.getBegin(); + F32 end = params.getEnd(); + + t_step = 1.0f / 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; + + // Increment to the next point. + // pt2 is the end point on the fractional face + t += t_step; + + t_fraction = (begin - t_first)*sides; + + // Only use if it's not almost exactly on an edge. + if (t_fraction < 0.9999f) + { + np++; + } + + // 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. + np++; + + t += t_step; + } + + t_fraction = (end - (t - t_step))*sides; + + // Find the fraction that we need to add to the end point. + t_fraction = (end - (t - t_step))*sides; + if (t_fraction > 0.0001f) + { + np++; + } + + // If we're sliced, the profile is open. + if ((end - begin)*ang_scale < 0.99f) + { + if (params.getHollow() <= 0) + { + // put center point if not hollow. + np++; + } + } + + return np; +} + // What is the bevel parameter used for? - DJS 04/05/02 // Bevel parameter is currently unused but presumedly would support // filleted and chamfered corners @@ -661,6 +725,117 @@ LLProfile::Face* LLProfile::addHole(const LLProfileParams& params, BOOL flat, F3 return face; } +//static +S32 LLProfile::getNumPoints(const LLProfileParams& params, BOOL path_open,F32 detail, S32 split, + BOOL is_sculpted, S32 sculpt_size) +{ // this is basically LLProfile::generate stripped down to only operations that influence the number of points + LLMemType m1(LLMemType::MTYPE_VOLUME); + + if (detail < MIN_LOD) + { + detail = MIN_LOD; + } + + // Generate the face data + F32 hollow = params.getHollow(); + + S32 np = 0; + + switch (params.getCurveType() & LL_PCODE_PROFILE_MASK) + { + case LL_PCODE_PROFILE_SQUARE: + { + np = getNumNGonPoints(params, 4,-0.375, 0, 1, split); + + if (hollow) + { + np *= 2; + } + } + break; + case LL_PCODE_PROFILE_ISOTRI: + case LL_PCODE_PROFILE_RIGHTTRI: + case LL_PCODE_PROFILE_EQUALTRI: + { + np = getNumNGonPoints(params, 3,0, 0, 1, split); + + if (hollow) + { + np *= 2; + } + } + 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; + + np = getNumNGonPoints(params, sides); + + if (hollow) + { + np *= 2; + } + } + 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; + } + } + np = getNumNGonPoints(params, llfloor(circle_detail), 0.5f, 0.f, 0.5f); + + if (hollow) + { + np *= 2; + } + + // Special case for openness of sphere + if ((params.getEnd() - params.getBegin()) < 1.f) + { + } + else if (!hollow) + { + np++; + } + } + break; + default: + break; + }; + + + return np; +} BOOL LLProfile::generate(const LLProfileParams& params, BOOL path_open,F32 detail, S32 split, @@ -1122,6 +1297,32 @@ LLPath::~LLPath() { } +S32 LLPath::getNumNGonPoints(const LLPathParams& params, S32 sides, F32 startOff, F32 end_scale, F32 twist_scale) +{ //this is basically LLPath::genNGon stripped down to only operations that influence the number of points added + S32 ret = 0; + + F32 step= 1.0f / sides; + F32 t = params.getBegin(); + ret = 1; + + 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()) + { + ret++; + t+=step; + } + + ret++; + + return ret; +} + 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. @@ -1299,6 +1500,56 @@ const LLVector2 LLPathParams::getEndScale() const return end_scale; } +S32 LLPath::getNumPoints(const LLPathParams& params, F32 detail) +{ // this is basically LLPath::generate stripped down to only the operations that influence the number of points + LLMemType m1(LLMemType::MTYPE_VOLUME); + + if (detail < MIN_LOD) + { + detail = MIN_LOD; + } + + S32 np = 2; // hardcode for line + + // 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; + } + 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()); + + np = sides; + } + break; + + case LL_PCODE_PATH_CIRCLE2: + { + //genNGon(params, llfloor(MIN_DETAIL_FACES * detail), 4.f, 0.f); + np = getNumNGonPoints(params, llfloor(MIN_DETAIL_FACES * detail)); + } + break; + + case LL_PCODE_PATH_TEST: + + np = 5; + break; + }; + + return np; +} + BOOL LLPath::generate(const LLPathParams& params, F32 detail, S32 split, BOOL is_sculpted, S32 sculpt_size) { @@ -2394,7 +2645,11 @@ void LLVolume::sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, // don't test lowest LOD to support legacy content DEV-33670 if (mDetail > SCULPT_MIN_AREA_DETAIL) { - if (sculptGetSurfaceArea() < SCULPT_MIN_AREA) + F32 area = sculptGetSurfaceArea(); + + const F32 SCULPT_MAX_AREA = 384.f; + + if (area < SCULPT_MIN_AREA || area > SCULPT_MAX_AREA) { data_is_empty = TRUE; } @@ -2435,6 +2690,11 @@ BOOL LLVolume::isFlat(S32 face) } +bool LLVolumeParams::isSculpt() const +{ + return mSculptID.notNull(); +} + bool LLVolumeParams::operator==(const LLVolumeParams ¶ms) const { return ( (getPathParams() == params.getPathParams()) && @@ -2468,7 +2728,6 @@ bool LLVolumeParams::operator<(const LLVolumeParams ¶ms) const return mSculptID < params.mSculptID; } - return mSculptType < params.mSculptType; @@ -3469,6 +3728,23 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const return index; } +void LLVolume::getLoDTriangleCounts(const LLVolumeParams& params, S32* counts) +{ //attempt to approximate the number of triangles that will result from generating a volume LoD set for the + //supplied LLVolumeParams -- inaccurate, but a close enough approximation for determining streaming cost + F32 detail[] = {1.f, 1.5f, 2.5f, 4.f}; + for (S32 i = 0; i < 4; i++) + { + S32 count = 0; + S32 path_points = LLPath::getNumPoints(params.getPathParams(), detail[i]); + S32 profile_points = LLProfile::getNumPoints(params.getProfileParams(), false, detail[i]); + + count = (profile_points-1)*2*(path_points-1); + count += profile_points*2; + + counts[i] = count; + } +} + S32 LLVolume::getNumTriangleIndices() const { BOOL profile_open = getProfile().isOpen(); @@ -3526,6 +3802,20 @@ S32 LLVolume::getNumTriangleIndices() const return count; } + +S32 LLVolume::getNumTriangles() const +{ + U32 triangle_count = 0; + + for (S32 i = 0; i < getNumVolumeFaces(); ++i) + { + triangle_count += getVolumeFace(i).mIndices.size()/3; + } + + return triangle_count; +} + + //----------------------------------------------------------------------------- // generateSilhouetteVertices() //----------------------------------------------------------------------------- @@ -4311,11 +4601,28 @@ BOOL LLVolumeParams::exportLegacyStream(std::ostream& output_stream) const 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; } @@ -4323,6 +4630,8 @@ bool LLVolumeParams::fromLLSD(LLSD& sd) { mPathParams.fromLLSD(sd["path"]); mProfileParams.fromLLSD(sd["profile"]); + sculptFromLLSD(sd["sculpt"]); + return true; } @@ -4365,6 +4674,12 @@ const F32 MIN_CONCAVE_PATH_WEDGE = 0.111111f; // 1/9 unity // 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(); diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index 052ce5662..03498881d 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -578,6 +578,9 @@ public: BOOL importLegacyStream(std::istream& input_stream); BOOL exportLegacyStream(std::ostream& output_stream) const; + LLSD sculptAsLLSD() const; + bool sculptFromLLSD(LLSD& sd); + LLSD asLLSD() const; operator LLSD() const { return asLLSD(); } bool fromLLSD(LLSD& sd); @@ -637,7 +640,7 @@ public: const F32& getSkew() const { return mPathParams.getSkew(); } const LLUUID& getSculptID() const { return mSculptID; } const U8& getSculptType() const { return mSculptType; } - + bool isSculpt() const; BOOL isConvex() const; // 'begin' and 'end' should be in range [0, 1] (they will be clamped) @@ -686,6 +689,9 @@ public: BOOL isFlat(S32 face) const { return (mFaces[face].mCount == 2); } BOOL isOpen() const { return mOpen; } void setDirty() { mDirty = TRUE; } + + static S32 getNumPoints(const LLProfileParams& params, BOOL path_open, F32 detail = 1.0f, S32 split = 0, + BOOL is_sculpted = FALSE, S32 sculpt_size = 0); BOOL generate(const LLProfileParams& params, BOOL path_open, F32 detail = 1.0f, S32 split = 0, BOOL is_sculpted = FALSE, S32 sculpt_size = 0); BOOL isConcave() const { return mConcave; } @@ -710,6 +716,7 @@ public: protected: void genNormals(const LLProfileParams& params); + static S32 getNumNGonPoints(const LLProfileParams& params, S32 sides, F32 offset=0.0f, F32 bevel = 0.0f, F32 ang_scale = 1.f, S32 split = 0); void genNGon(const LLProfileParams& params, S32 sides, F32 offset=0.0f, F32 bevel = 0.0f, F32 ang_scale = 1.f, S32 split = 0); Face* addHole(const LLProfileParams& params, BOOL flat, F32 sides, F32 offset, F32 box_hollow, F32 ang_scale, S32 split = 0); @@ -752,6 +759,9 @@ public: virtual ~LLPath(); + static S32 getNumPoints(const LLPathParams& params, F32 detail); + static S32 getNumNGonPoints(const LLPathParams& params, S32 sides, F32 offset=0.0f, F32 end_scale = 1.f, F32 twist_scale = 1.f); + void genNGon(const LLPathParams& params, S32 sides, F32 offset=0.0f, F32 end_scale = 1.f, F32 twist_scale = 1.f); virtual BOOL generate(const LLPathParams& params, F32 detail=1.0f, S32 split = 0, BOOL is_sculpted = FALSE, S32 sculpt_size = 0); @@ -895,11 +905,14 @@ public: S32 getSculptLevel() const { return mSculptLevel; } void setSculptLevel(S32 level) { mSculptLevel = level; } - + S32 *getTriangleIndices(U32 &num_indices) const; // returns number of triangle indeces required for path/profile mesh S32 getNumTriangleIndices() const; + static void getLoDTriangleCounts(const LLVolumeParams& params, S32* counts); + + S32 getNumTriangles() const; void generateSilhouetteVertices(std::vector &vertices, std::vector &normals, From cc19a4c2da7ba69fba844840e43ae3b136a677f7 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Wed, 20 Jul 2011 23:54:16 -0500 Subject: [PATCH 03/22] Aligned strided vertexbuffers. --- indra/llrender/llvertexbuffer.cpp | 72 +++++++++++++++++++------------ indra/llrender/llvertexbuffer.h | 4 +- 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index d697b95dd..6062abcf4 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -40,7 +40,7 @@ #include "llglheaders.h" #include "llmemtype.h" #include "llrender.h" - +#include "llvector4a.h" #include "llcontrol.h" //============================================================================ @@ -488,6 +488,8 @@ LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) : mTypeMask = typemask; mStride = stride; + mAlignedOffset = 0; + mAlignedIndexOffset = 0; sCount++; } @@ -619,8 +621,8 @@ void LLVertexBuffer::createGLBuffer() else { static int gl_buffer_idx = 0; - mGLBuffer = ++gl_buffer_idx; - mMappedData = new U8[size]; + mGLBuffer = ++gl_buffer_idx; + mMappedData = (U8*) ll_aligned_malloc_16(size); if(!sOmitBlank) memset((void*)mMappedData, 0, size); } } @@ -642,15 +644,20 @@ void LLVertexBuffer::createGLIndices() mEmpty = TRUE; + //pad by 16 bytes for aligned copies + size += 16; + if (useVBOs()) { + //pad by another 16 bytes for VBO pointer adjustment + size += 16; mMappedIndexData = NULL; genIndices(); mResized = TRUE; } else { - mMappedIndexData = new U8[size]; + mMappedIndexData = (U8*) ll_aligned_malloc_16(size); if(!sOmitBlank) memset((void*)mMappedIndexData, 0, size); static int gl_buffer_idx = 0; mGLIndices = ++gl_buffer_idx; @@ -674,7 +681,7 @@ void LLVertexBuffer::destroyGLBuffer() } else { - delete [] mMappedData; + ll_aligned_free_16((void*)mMappedData); mMappedData = NULL; mEmpty = TRUE; } @@ -703,7 +710,7 @@ void LLVertexBuffer::destroyGLIndices() } else { - delete [] mMappedIndexData; + ll_aligned_free_16((void*)mMappedIndexData); mMappedIndexData = NULL; mEmpty = TRUE; } @@ -728,7 +735,7 @@ void LLVertexBuffer::updateNumVerts(S32 nverts) } mRequestedNumVerts = nverts; - + if (!mDynamicSize) { mNumVerts = nverts; @@ -779,6 +786,7 @@ void LLVertexBuffer::allocateBuffer(S32 nverts, S32 nindices, bool create) { llerrs << "Bad vertex buffer allocation: " << nverts << " : " << nindices << llendl; } + updateNumVerts(nverts); updateNumIndices(nindices); @@ -840,7 +848,7 @@ void LLVertexBuffer::resizeBuffer(S32 newnverts, S32 newnindices) if (!useVBOs()) { volatile U8* old = mMappedData; - mMappedData = new U8[newsize]; + mMappedData = (U8*) ll_aligned_malloc_16(newsize); if (old) { memcpy((void*)mMappedData, (void*)old, llmin(newsize, oldsize)); @@ -849,7 +857,7 @@ void LLVertexBuffer::resizeBuffer(S32 newnverts, S32 newnindices) memset((void*)(mMappedData+oldsize), 0, newsize-oldsize); } - delete [] old; + ll_aligned_free_16((void*)old); } else { @@ -877,7 +885,7 @@ void LLVertexBuffer::resizeBuffer(S32 newnverts, S32 newnindices) { //delete old buffer, keep GL buffer for now volatile U8* old = mMappedIndexData; - mMappedIndexData = new U8[new_index_size]; + mMappedIndexData = (U8*) ll_aligned_malloc_16(new_index_size); if (old) { @@ -886,7 +894,7 @@ void LLVertexBuffer::resizeBuffer(S32 newnverts, S32 newnindices) { memset((void*)(mMappedIndexData+old_index_size), 0, new_index_size - old_index_size); } - delete [] old; + ll_aligned_free_16((void*)old); } else { @@ -924,8 +932,8 @@ void LLVertexBuffer::freeClientBuffer() { if(useVBOs() && sDisableVBOMapping && (mMappedData || mMappedIndexData)) { - delete[] mMappedData ; - delete[] mMappedIndexData ; + ll_aligned_free_16((void*)mMappedData) ; + ll_aligned_free_16((void*)mMappedIndexData) ; mMappedData = NULL ; mMappedIndexData = NULL ; } @@ -935,9 +943,8 @@ void LLVertexBuffer::allocateClientVertexBuffer() { if(!mMappedData) { - U32 size = getSize() ; - mMappedData = new U8[size]; - memset((void*)mMappedData, 0, size); + mMappedData = (U8*)ll_aligned_malloc_16(getSize()); + if (!sOmitBlank) memset((void*)mMappedData, 0, getSize()); } } @@ -945,9 +952,8 @@ void LLVertexBuffer::allocateClientIndexBuffer() { if(!mMappedIndexData) { - U32 size = getIndicesSize(); - mMappedIndexData = new U8[size]; - memset((void*)mMappedIndexData, 0, size); + mMappedIndexData = (U8*)ll_aligned_malloc_16(getIndicesSize()); + if (!sOmitBlank) memset((void*)mMappedIndexData, 0, getIndicesSize()); } } @@ -977,7 +983,12 @@ volatile U8* LLVertexBuffer::mapVertexBuffer(S32 type, S32 access) } else { - mMappedData = (U8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); + U8* src = NULL; + { + src = (U8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); + } + mMappedData = LL_NEXT_ALIGNED_ADDRESS(src); + mAlignedOffset = mMappedData - src; } stop_glerror(); } @@ -1049,7 +1060,13 @@ volatile U8* LLVertexBuffer::mapIndexBuffer(S32 access) } else { - mMappedIndexData = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); + U8* src = NULL; + { + src = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); + } + + mMappedIndexData = src; //LL_NEXT_ALIGNED_ADDRESS(src); + mAlignedIndexOffset = mMappedIndexData - src; stop_glerror(); } } @@ -1165,29 +1182,30 @@ template struct VertexBufferStrider { if (type == LLVertexBuffer::TYPE_INDEX) { - S32 stride = sizeof(T); + volatile U8* ptr = vbo.mapIndexBuffer(); - if (vbo.mapIndexBuffer() == NULL) + if (ptr == NULL) { llwarns << "mapIndexBuffer failed!" << llendl; return FALSE; } - strider = (T*)(vbo.getMappedIndices() + index*stride); + strider = (T*)(ptr + index*sizeof(T)); strider.setStride(0); return TRUE; } else if (vbo.hasDataType(type)) { S32 stride = vbo.getStride(); + volatile U8* ptr = vbo.mapVertexBuffer(type); - if (vbo.mapVertexBuffer(type) == NULL) + if (ptr == NULL) { llwarns << "mapVertexBuffer failed!" << llendl; return FALSE; } - strider = (T*)(vbo.getMappedData() + vbo.getOffset(type) + index*stride); + strider = (T*)(ptr + vbo.getOffset(type) + index*stride); strider.setStride(stride); return TRUE; } @@ -1466,7 +1484,7 @@ void LLVertexBuffer::setupVertexBuffer(U32 data_mask) const { LLMemType mt(LLMemType::MTYPE_VERTEX_DATA); stop_glerror(); - volatile U8* base = useVBOs() ? NULL : mMappedData; + volatile U8* base = useVBOs() ? (U8*) mAlignedOffset : mMappedData; S32 stride = mStride; if ((data_mask & mTypeMask) != data_mask) diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h index 76080558c..67e5e6933 100644 --- a/indra/llrender/llvertexbuffer.h +++ b/indra/llrender/llvertexbuffer.h @@ -228,6 +228,8 @@ protected: S32 mRequestedNumVerts; // Number of vertices requested S32 mRequestedNumIndices; // Number of indices requested + ptrdiff_t mAlignedOffset; + ptrdiff_t mAlignedIndexOffset; S32 mStride; U32 mTypeMask; S32 mUsage; // GL usage @@ -272,7 +274,7 @@ public: static S32 sTypeOffsets[TYPE_MAX]; static U32 sGLMode[LLRender::NUM_MODES]; static U32 sGLRenderBuffer; - static U32 sGLRenderIndices; + static U32 sGLRenderIndices; static BOOL sVBOActive; static BOOL sIBOActive; static U32 sLastMask; From 48ef9904c1ee4150f83fa9f110ebbe534ad8b008 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Thu, 21 Jul 2011 01:48:46 -0500 Subject: [PATCH 04/22] A pass at cleaning up LLVertexBuffer a bit. Also removing extra unbinds that caused synchronization issues with mapped vbos. --- indra/llrender/llvertexbuffer.cpp | 10 +++++----- indra/llrender/llvertexbuffer.h | 25 ++++++------------------- indra/newview/lldrawpoolsky.cpp | 1 + indra/newview/lldrawpoolterrain.cpp | 2 ++ 4 files changed, 14 insertions(+), 24 deletions(-) diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index 6062abcf4..e0c469544 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -88,7 +88,7 @@ void LLVBOPool::releaseName(GLuint name) } -S32 LLVertexBuffer::sTypeOffsets[LLVertexBuffer::TYPE_MAX] = +S32 LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_MAX] = { sizeof(LLVector4), // TYPE_VERTEX, sizeof(LLVector4), // TYPE_NORMAL, @@ -506,7 +506,7 @@ S32 LLVertexBuffer::calcStride(const U32& typemask, S32* offsets) { offsets[i] = stride; } - stride += sTypeOffsets[i]; + stride += sTypeSize[i]; } } @@ -690,7 +690,7 @@ void LLVertexBuffer::destroyGLBuffer() } mGLBuffer = 0; - unbind(); + //unbind(); } void LLVertexBuffer::destroyGLIndices() @@ -719,7 +719,7 @@ void LLVertexBuffer::destroyGLIndices() } mGLIndices = 0; - unbind(); + //unbind(); } void LLVertexBuffer::updateNumVerts(S32 nverts) @@ -1277,7 +1277,7 @@ void LLVertexBuffer::setStride(S32 type, S32 new_stride) llerrs << "LLVertexBuffer::setOffset called with mNumVerts = " << mNumVerts << llendl; } // This code assumes that setStride() will only be called once per VBO per type. - S32 delta = new_stride - sTypeOffsets[type]; + S32 delta = new_stride - sTypeSize[type]; for (S32 i=type+1; i& pos, const std::vector& norm); + static void clientCopy(F64 max_time = 0.005); //copy data from client to GL static void unbind(); //unbind any bound vertex buffer @@ -197,8 +199,8 @@ public: S32 getRequestedVerts() const { return mRequestedNumVerts; } S32 getRequestedIndices() const { return mRequestedNumIndices; } - volatile U8* getIndicesPointer() const { return useVBOs() ? NULL : mMappedIndexData; } - volatile U8* getVerticesPointer() const { return useVBOs() ? NULL : mMappedData; } + volatile U8* getIndicesPointer() const { return useVBOs() ? (U8*) mAlignedIndexOffset : mMappedIndexData; } + volatile U8* getVerticesPointer() const { return useVBOs() ? (U8*) mAlignedOffset : mMappedData; } S32 getStride() const { return mStride; } S32 getTypeMask() const { return mTypeMask; } BOOL hasDataType(S32 type) const { return ((1 << type) & getTypeMask()) ? TRUE : FALSE; } @@ -246,21 +248,6 @@ protected: BOOL mDynamicSize; // if TRUE, buffer has been resized at least once (and should be padded) S32 mOffsets[TYPE_MAX]; - class DirtyRegion - { - public: - U32 mIndex; - U32 mCount; - U32 mIndicesIndex; - U32 mIndicesCount; - - DirtyRegion(U32 vi, U32 vc, U32 ii, U32 ic) - : mIndex(vi), mCount(vc), mIndicesIndex(ii), mIndicesCount(ic) - { } - }; - - std::vector mDirtyRegions; //vector of dirty regions to rebuild - public: static S32 sCount; static S32 sGLCount; @@ -271,7 +258,7 @@ public: static BOOL sDisableVBOMapping; //disable glMapBufferARB static BOOL sEnableVBOs; - static S32 sTypeOffsets[TYPE_MAX]; + static S32 sTypeSize[TYPE_MAX]; static U32 sGLMode[LLRender::NUM_MODES]; static U32 sGLRenderBuffer; static U32 sGLRenderIndices; diff --git a/indra/newview/lldrawpoolsky.cpp b/indra/newview/lldrawpoolsky.cpp index b5cc0d31d..f3f0d1a4a 100644 --- a/indra/newview/lldrawpoolsky.cpp +++ b/indra/newview/lldrawpoolsky.cpp @@ -70,6 +70,7 @@ void LLDrawPoolSky::prerender() void LLDrawPoolSky::render(S32 pass) { gGL.flush(); + if (mDrawFace.empty()) { return; diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp index 97af1285b..b9654c57c 100644 --- a/indra/newview/lldrawpoolterrain.cpp +++ b/indra/newview/lldrawpoolterrain.cpp @@ -222,6 +222,8 @@ void LLDrawPoolTerrain::render(S32 pass) sShader->unbind(); sShader = &gObjectFullbrightProgram; sShader->bind(); + LLGLEnable polyOffset(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(-1.0f, -1.0f); renderOwnership(); sShader = old_shader; sShader->bind(); From 07c892480a51daa8a6e965ccc38f7b49f7d4b607 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Fri, 22 Jul 2011 04:29:19 -0500 Subject: [PATCH 05/22] Vectorized llvolumeface while maintining strided vertexbuffers. --- indra/llmath/lloctree.h | 12 +- indra/llmath/llvolume.cpp | 1643 +++++++++++++++++++---- indra/llmath/llvolume.h | 102 +- indra/llmath/llvolumeoctree.cpp | 6 +- indra/llmath/llvolumeoctree.h | 3 +- indra/llui/lllineeditor.h | 6 +- indra/newview/floatersculptpreview.cpp | 10 +- indra/newview/llface.cpp | 555 +++++--- indra/newview/llfloaterimagepreview.cpp | 38 +- indra/newview/llpolymesh.cpp | 22 +- indra/newview/llspatialpartition.cpp | 5 +- indra/newview/llspatialpartition.h | 26 +- indra/newview/llviewercamera.cpp | 5 +- indra/newview/llvotextbubble.cpp | 22 +- indra/newview/llvovolume.cpp | 83 +- indra/newview/llvovolume.h | 2 + 16 files changed, 1956 insertions(+), 584 deletions(-) diff --git a/indra/llmath/lloctree.h b/indra/llmath/lloctree.h index c4fc3d5e6..f49c657c0 100644 --- a/indra/llmath/lloctree.h +++ b/indra/llmath/lloctree.h @@ -299,7 +299,7 @@ public: { if (data == NULL) { - //OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE BRANCH !!!" << llendl; + OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE BRANCH !!!" << llendl; return false; } LLOctreeNode* parent = getOctParent(); @@ -389,7 +389,7 @@ public: else { //it's not in here, give it to the root - //OCT_ERRS << "Octree insertion failed, starting over from root!" << llendl; + OCT_ERRS << "Octree insertion failed, starting over from root!" << llendl; oct_node* node = this; @@ -590,7 +590,7 @@ public: } } - //OCT_ERRS << "Octree failed to delete requested child." << llendl; + OCT_ERRS << "Octree failed to delete requested child." << llendl; } protected: @@ -670,13 +670,13 @@ public: { if (data == NULL) { - //OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE ROOT !!!" << llendl; + OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE ROOT !!!" << llendl; return false; } if (data->getBinRadius() > 4096.0) { - //OCT_ERRS << "!!! ELEMENT EXCEEDS MAXIMUM SIZE IN OCTREE ROOT !!!" << llendl; + OCT_ERRS << "!!! ELEMENT EXCEEDS MAXIMUM SIZE IN OCTREE ROOT !!!" << llendl; return false; } @@ -692,7 +692,7 @@ public: if (lt != 0x7) { - //OCT_ERRS << "!!! ELEMENT EXCEEDS RANGE OF SPATIAL PARTITION !!!" << llendl; + OCT_ERRS << "!!! ELEMENT EXCEEDS RANGE OF SPATIAL PARTITION !!!" << llendl; return false; } diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 5ad15bd9b..46a22749a 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -48,6 +48,7 @@ #include "lloctree.h" #include "lldarray.h" #include "llvolume.h" +#include "llvolumeoctree.h" #include "llstl.h" #include "llsdserialize.h" #include "llvector4a.h" @@ -93,6 +94,19 @@ 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 << "Alignment check failed." << 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); @@ -296,7 +310,6 @@ BOOL LLTriangleRayIntersect(const LLVector3& vert0, const LLVector3& vert1, cons } } -/* class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst { public: @@ -353,7 +366,7 @@ public: llerrs << "Empty leaf" << llendl; } - for (S32 i = 0; i < branch->getChildCount(); ++i) + for (U32 i = 0; i < branch->getChildCount(); ++i) { //stretch by child extents LLVolumeOctreeListener* child = (LLVolumeOctreeListener*) branch->getChild(i)->getListener(0); min.setMin(min, child->mExtents[0]); @@ -366,7 +379,7 @@ public: node->mBounds[1].setSub(max,min); node->mBounds[1].mul(0.5f); } -};*/ +}; //------------------------------------------------------------------- // statics @@ -2233,6 +2246,152 @@ BOOL LLVolume::generate() return FALSE; } +void LLVolumeFace::VertexData::init() +{ + if (!mData) + { + mData = (LLVector4a*) ll_aligned_malloc_16(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() +{ + ll_aligned_free_16(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; +} + + +S32 LLVolume::getNumFaces() const +{ + return (S32)mProfilep->mFaces.size(); +} + void LLVolume::createVolumeFaces() { @@ -3809,7 +3968,7 @@ S32 LLVolume::getNumTriangles() const for (S32 i = 0; i < getNumVolumeFaces(); ++i) { - triangle_count += getVolumeFace(i).mIndices.size()/3; + triangle_count += getVolumeFace(i).mNumIndices/3; } return triangle_count; @@ -3850,7 +4009,7 @@ void LLVolume::generateSilhouetteVertices(std::vector &vertices, const LLVolumeFace& face = *iter; if (!(face_mask & (0x1 << cur_index++)) || - face.mIndices.empty() || face.mEdge.empty()) + face.mNumIndices == 0 || face.mEdge.empty()) { continue; } @@ -3867,7 +4026,7 @@ void LLVolume::generateSilhouetteVertices(std::vector &vertices, #if DEBUG_SILHOUETTE_EDGE_MAP //for each triangle - U32 count = face.mIndices.size(); + U32 count = face.mNumIndices; for (U32 j = 0; j < count/3; j++) { //get vertices S32 v1 = face.mIndices[j*3+0]; @@ -3875,9 +4034,9 @@ void LLVolume::generateSilhouetteVertices(std::vector &vertices, S32 v3 = face.mIndices[j*3+2]; //get current face center - LLVector3 cCenter = (face.mVertices[v1].mPosition + - face.mVertices[v2].mPosition + - face.mVertices[v3].mPosition) / 3.0f; + 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++) { @@ -3895,9 +4054,9 @@ void LLVolume::generateSilhouetteVertices(std::vector &vertices, v3 = face.mIndices[nIndex*3+2]; //get neighbor face center - LLVector3 nCenter = (face.mVertices[v1].mPosition + - face.mVertices[v2].mPosition + - face.mVertices[v3].mPosition) / 3.0f; + LLVector3 nCenter = (face.mVertices[v1].getPosition() + + face.mVertices[v2].getPosition() + + face.mVertices[v3].getPosition()) / 3.0f; //draw line vertices.push_back(cCenter); @@ -3920,15 +4079,15 @@ void LLVolume::generateSilhouetteVertices(std::vector &vertices, #elif DEBUG_SILHOUETTE_NORMALS //for each vertex - for (U32 j = 0; j < face.mVertices.size(); j++) { - vertices.push_back(face.mVertices[j].mPosition); - vertices.push_back(face.mVertices[j].mPosition + face.mVertices[j].mNormal*0.1f); + 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].mPosition); - vertices.push_back(face.mVertices[j].mPosition + face.mVertices[j].mBinormal*0.1f); + 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()); @@ -3946,23 +4105,21 @@ void LLVolume::generateSilhouetteVertices(std::vector &vertices, //for each triangle std::vector fFacing; - vector_append(fFacing, face.mIndices.size()/3); - for (U32 j = 0; j < face.mIndices.size()/3; j++) + vector_append(fFacing, face.mNumIndices/3); + + LLVector4a* v = (LLVector4a*) face.mPositions; + LLVector4a* n = (LLVector4a*) face.mNormals; + + for (U32 j = 0; j < (U32)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]; - //ew. Face verts arent aligned yet. - LLVector4a va1,va3; - va1.load3(face.mVertices[v1].mPosition.mV); - va3.load3(face.mVertices[v3].mPosition.mV); - LLVector4a c1,c2; - c2.load3(face.mVertices[v2].mPosition.mV); - c1.setSub(va1,c2); - c2.sub(va3); + c1.setSub(v[v1], v[v2]); + c2.setSub(v[v2], v[v3]); LLVector4a norm; @@ -3976,7 +4133,7 @@ void LLVolume::generateSilhouetteVertices(std::vector &vertices, { //get view vector LLVector4a view; - view.setSub(obj_cam_vec, va1); + view.setSub(obj_cam_vec, v[v1]); bool away = view.dot3(norm) > 0.0f; if (away) { @@ -3990,7 +4147,7 @@ void LLVolume::generateSilhouetteVertices(std::vector &vertices, } //for each triangle - for (U32 j = 0; j < face.mIndices.size()/3; j++) + for (U32 j = 0; j < (U32)face.mNumIndices/3; j++) { if (fFacing[j] == (AWAY | TOWARDS)) { //this is a degenerate triangle @@ -4023,15 +4180,21 @@ void LLVolume::generateSilhouetteVertices(std::vector &vertices, S32 v1 = face.mIndices[j*3+k]; S32 v2 = face.mIndices[j*3+((k+1)%3)]; - vertices.push_back(face.mVertices[v1].mPosition*mat_in); - LLVector3 norm1 = face.mVertices[v1].mNormal * norm_mat_in; - norm1.normVec(); - normals.push_back(norm1); + LLVector4a t; + mat.affineTransform(v[v1], t); + vertices.push_back(LLVector3(t[0], t[1], t[2])); - vertices.push_back(face.mVertices[v2].mPosition*mat_in); - LLVector3 norm2 = face.mVertices[v2].mNormal * norm_mat_in; - norm2.normVec(); - normals.push_back(norm2); + 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])); segments.push_back(vertices.size()); } @@ -4080,9 +4243,11 @@ S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& en 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++) { - const LLVolumeFace &face = getVolumeFace((U32)i); + LLVolumeFace &face = mVolumeFaces[i]; LLVector4a box_center; box_center.setAdd(face.mExtents[0], face.mExtents[1]); @@ -4097,63 +4262,19 @@ S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& en { genBinormals(i); } - - for (U32 tri = 0; tri < face.mIndices.size()/3; tri++) + + if (!face.mOctree) { - S32 index1 = face.mIndices[tri*3+0]; - S32 index2 = face.mIndices[tri*3+1]; - S32 index3 = face.mIndices[tri*3+2]; - - F32 a, b, t; - - LLVector4a pos[3]; - pos[0].load3(face.mVertices[index1].mPosition.mV); - pos[1].load3(face.mVertices[index2].mPosition.mV); - pos[2].load3(face.mVertices[index3].mPosition.mV); - if (LLTriangleRayIntersect(pos[0], - pos[1], - pos[2], - start, dir, a, b, t)) - { - if ((t >= 0.f) && // if hit is after start - (t <= 1.f) && // and before end - (t < closest_t)) // and this hit is closer - { - closest_t = t; - hit_face = i; - - if (intersection != NULL) - { - LLVector4a v; - v.setMul(dir,closest_t); - v.add(start); - intersection->set(v.getF32ptr()); - } + face.createOctree(); + } - if (tex_coord != NULL) + //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) { - *tex_coord = ((1.f - a - b) * face.mVertices[index1].mTexCoord + - a * face.mVertices[index2].mTexCoord + - b * face.mVertices[index3].mTexCoord); - - } - - if (normal != NULL) - { - *normal = ((1.f - a - b) * face.mVertices[index1].mNormal + - a * face.mVertices[index2].mNormal + - b * face.mVertices[index3].mNormal); - } - - if (bi_normal != NULL) - { - *bi_normal = ((1.f - a - b) * face.mVertices[index1].mBinormal + - a * face.mVertices[index2].mBinormal + - b * face.mVertices[index3].mBinormal); - } - - } - } + hit_face = i; } } } @@ -4923,11 +5044,18 @@ std::ostream& operator<<(std::ostream &s, const LLVolume *volumep) LLVolumeFace::LLVolumeFace() : mID(0), mTypeMask(0), - mHasBinormals(FALSE), mBeginS(0), mBeginT(0), mNumS(0), - mNumT(0) + mNumT(0), + mNumVertices(0), + mNumIndices(0), + mPositions(NULL), + mNormals(NULL), + mBinormals(NULL), + mTexCoords(NULL), + mIndices(NULL), + mOctree(NULL) { mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3); mExtents[0].splat(-0.5f); @@ -4938,11 +5066,18 @@ LLVolumeFace::LLVolumeFace() : LLVolumeFace::LLVolumeFace(const LLVolumeFace& src) : mID(0), mTypeMask(0), - mHasBinormals(FALSE), mBeginS(0), mBeginT(0), mNumS(0), - mNumT(0) + mNumT(0), + mNumVertices(0), + mNumIndices(0), + mPositions(NULL), + mNormals(NULL), + mBinormals(NULL), + mTexCoords(NULL), + mIndices(NULL), + mOctree(NULL) { mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3); mCenter = mExtents+2; @@ -4958,7 +5093,6 @@ LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src) mID = src.mID; mTypeMask = src.mTypeMask; - mHasBinormals = src.mHasBinormals, mBeginS = src.mBeginS; mBeginT = src.mBeginT; mNumS = src.mNumS; @@ -4968,23 +5102,80 @@ LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src) mExtents[1] = src.mExtents[1]; *mCenter = *src.mCenter; - + mNumVertices = 0; + mNumIndices = 0; - LLVector4a::memcpyNonAliased16((F32*) mExtents, (F32*) src.mExtents, 3*sizeof(LLVector4a)); + freeData(); - mVertices = src.mVertices; - mIndices = src.mIndices; - mEdge = src.mEdge; + 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 + { + ll_aligned_free_16(mBinormals); + mBinormals = 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() { ll_aligned_free_16(mExtents); mExtents = NULL; + freeData(); } + +void LLVolumeFace::freeData() +{ + ll_aligned_free_16(mPositions); + mPositions = NULL; + ll_aligned_free_16( mNormals); + mNormals = NULL; + ll_aligned_free_16(mTexCoords); + mTexCoords = NULL; + ll_aligned_free_16(mIndices); + mIndices = NULL; + ll_aligned_free_16(mBinormals); + mBinormals = 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) { @@ -5005,25 +5196,24 @@ BOOL LLVolumeFace::create(LLVolume* volume, BOOL partial_build) mTexCoordExtents[0].setVec(1.f, 1.f) ; mTexCoordExtents[1].setVec(0.f, 0.f) ; - U32 end = mVertices.size() ; - for(U32 i = 0 ; i < end ; i++) + for(U32 i = 0 ; i < (U32)mNumVertices ; i++) { - if(mTexCoordExtents[0].mV[0] > mVertices[i].mTexCoord.mV[0]) + if(mTexCoordExtents[0].mV[0] > mTexCoords[i].mV[0]) { - mTexCoordExtents[0].mV[0] = mVertices[i].mTexCoord.mV[0] ; + mTexCoordExtents[0].mV[0] = mTexCoords[i].mV[0] ; } - if(mTexCoordExtents[1].mV[0] < mVertices[i].mTexCoord.mV[0]) + if(mTexCoordExtents[1].mV[0] < mTexCoords[i].mV[0]) { - mTexCoordExtents[1].mV[0] = mVertices[i].mTexCoord.mV[0] ; + mTexCoordExtents[1].mV[0] = mTexCoords[i].mV[0] ; } - if(mTexCoordExtents[0].mV[1] > mVertices[i].mTexCoord.mV[1]) + if(mTexCoordExtents[0].mV[1] > mTexCoords[i].mV[1]) { - mTexCoordExtents[0].mV[1] = mVertices[i].mTexCoord.mV[1] ; + mTexCoordExtents[0].mV[1] = mTexCoords[i].mV[1] ; } - if(mTexCoordExtents[1].mV[1] < mVertices[i].mTexCoord.mV[1]) + if(mTexCoordExtents[1].mV[1] < mTexCoords[i].mV[1]) { - mTexCoordExtents[1].mV[1] = mVertices[i].mTexCoord.mV[1] ; + mTexCoordExtents[1].mV[1] = mTexCoords[i].mV[1] ; } } mTexCoordExtents[0].mV[0] = llmax(0.f, mTexCoordExtents[0].mV[0]) ; @@ -5035,6 +5225,603 @@ BOOL LLVolumeFace::create(LLVolume* volume, BOOL partial_build) 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 < (U32)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 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; + + if (mNumVertices < 3) + { //nothing to do + return; + } + + //mapping of vertices to triangles and indices + std::vector vertex_data; + + //mapping of triangles do vertices + std::vector triangle_data; + + triangle_data.resize(mNumIndices/3); + vertex_data.resize(mNumVertices); + + for (U32 i = 0; i < (U32)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 < (U32)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 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 < (U32)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 < (U32)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*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts); + LLVector4a* norm = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts); + S32 size = ((num_verts*sizeof(LLVector2)) + 0xF) & ~0xF; + LLVector2* tc = (LLVector2*) ll_aligned_malloc_16(size); + + + LLVector4a* binorm = NULL; + if (mBinormals) + { + binorm = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts); + } + + //allocate mapping of old indices to new indices + std::vector new_idx; + new_idx.resize(mNumVertices, -1); + + S32 cur_idx = 0; + for (U32 i = 0; i < (U32)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 (mBinormals) + { + binorm[cur_idx] = mBinormals[idx]; + } + + cur_idx++; + } + } + + for (U32 i = 0; i < (U32)mNumIndices; ++i) + { + mIndices[i] = new_idx[mIndices[i]]; + } + + ll_aligned_free_16(mPositions); + ll_aligned_free_16(mNormals); + ll_aligned_free_16(mTexCoords); + ll_aligned_free_16(mBinormals); + + mPositions = pos; + mNormals = norm; + mTexCoords = tc; + 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(center, size, NULL); + new LLVolumeOctreeListener(mOctree); + + for (U32 i = 0; i < (U32)mNumIndices; i+= 3) + { //for each triangle + LLPointer 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, @@ -5042,10 +5829,21 @@ void LerpPlanarVertex(LLVolumeFace::VertexData& v0, F32 coef01, F32 coef02) { - vout.mPosition = v0.mPosition + ((v1.mPosition-v0.mPosition)*coef01)+((v2.mPosition-v0.mPosition)*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.mNormal = v0.mNormal; - vout.mBinormal = v0.mBinormal; + vout.setNormal(v0.getNormal()); } BOOL LLVolumeFace::createUnCutCubeCap(LLVolume* volume, BOOL partial_build) @@ -5078,26 +5876,28 @@ BOOL LLVolumeFace::createUnCutCubeCap(LLVolume* volume, BOOL partial_build) offset = mBeginS; } - S32 vtop; { VertexData corners[4]; VertexData baseVert; - for(S32 t = 0; t < 4; t++){ - corners[t].mPosition = mesh[offset + (grid_size*t)].mPos; + 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]; } { - baseVert.mNormal = - ((corners[1].mPosition-corners[0].mPosition) % - (corners[2].mPosition-corners[1].mPosition)); - baseVert.mNormal.normalize(); + 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.mNormal *= -.1f; + baseVert.getNormal().mul(-1.0f); } else { @@ -5110,22 +5910,24 @@ BOOL LLVolumeFace::createUnCutCubeCap(LLVolume* volume, BOOL partial_build) corners[1].mTexCoord=corners[2].mTexCoord; corners[2].mTexCoord=swap; } - baseVert.mBinormal = calc_binormal_from_triangle( - corners[0].mPosition, corners[0].mTexCoord, - corners[1].mPosition, corners[1].mTexCoord, - corners[2].mPosition, corners[2].mTexCoord); - for(int t = 0; t < 4; t++){ - corners[t].mBinormal = baseVert.mBinormal; - corners[t].mNormal = baseVert.mNormal; - } - mHasBinormals = TRUE; - if (partial_build) - { - mVertices.clear(); - } + LLVector4a binormal; - vtop = mVertices.size(); + 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=0;i--) { - mIndices.push_back(vtop+(gy*(grid_size+1))+gx+idxs[i]); - } + *out++ = ((gy*(grid_size+1))+gx+idxs[i]); + } } else { for(S32 i=0;i<6;i++) { - mIndices.push_back(vtop+(gy*(grid_size+1))+gx+idxs[i]); + *out++ = ((gy*(grid_size+1))+gx+idxs[i]); } } } @@ -5212,11 +6021,25 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build) num_vertices = profile.size(); num_indices = (profile.size() - 2)*3; - mVertices.resize(num_vertices); - - if (!partial_build) + if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK)) { - mIndices.resize(num_indices); + 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(); @@ -5243,35 +6066,38 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build) 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) { - mVertices[i].mTexCoord.mV[0] = profile[i].mV[0]+0.5f; - mVertices[i].mTexCoord.mV[1] = profile[i].mV[1]+0.5f; + tc[i].mV[0] = profile[i].mV[0]+0.5f; + tc[i].mV[1] = profile[i].mV[1]+0.5f; } else { // Mirror for underside. - mVertices[i].mTexCoord.mV[0] = profile[i].mV[0]+0.5f; - mVertices[i].mTexCoord.mV[1] = 0.5f - profile[i].mV[1]; + tc[i].mV[0] = profile[i].mV[0]+0.5f; + tc[i].mV[1] = 0.5f - profile[i].mV[1]; } - mVertices[i].mPosition = mesh[i + offset].mPos; + pos[i].load3(mesh[i + offset].mPos.mV); if (i == 0) { - max.load3(mVertices[i].mPosition.mV); + max = pos[i]; min = max; - min_uv = max_uv = mVertices[i].mTexCoord; + min_uv = max_uv = tc[i]; } else { - LLVector4a pos; - pos.load3(mVertices[i].mPosition.mV); - update_min_max(min,max, pos); - update_min_max(min_uv, max_uv, mVertices[i].mTexCoord); + update_min_max(min,max,pos[i]); + update_min_max(min_uv, max_uv, tc[i]); } } @@ -5280,15 +6106,13 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build) cuv = (min_uv + max_uv)*0.5f; - LLVector3 binormal = calc_binormal_from_triangle( - LLVector3(mCenter->getF32ptr()), cuv, - mVertices[0].mPosition, mVertices[0].mTexCoord, - mVertices[1].mPosition, mVertices[1].mTexCoord); - binormal.normVec(); + LLVector4a binormal; + calc_binormal_from_triangle(binormal, + *mCenter, cuv, + pos[0], tc[0], + pos[1], tc[1]); + binormal.normalize3fast(); - LLVector4a pos[2]; - pos[0].load3(mVertices[0].mPosition.mV); - pos[1].load3(mVertices[1].mPosition.mV); LLVector4a normal; LLVector4a d0, d1; @@ -5308,30 +6132,22 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build) normal.normalize3fast(); VertexData vd; - vd.mPosition.set(mCenter->getF32ptr()); - vd.mNormal.set(normal.getF32ptr()); - vd.mBinormal = binormal; + vd.setPosition(*mCenter); vd.mTexCoord = cuv; if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK)) { - mVertices.push_back(vd); + pos[num_vertices] = *mCenter; + tc[num_vertices] = cuv; num_vertices++; - if (!partial_build) - { - vector_append(mIndices, 3); - } } - for (S32 i = 0; i < num_vertices; i++) { - mVertices[i].mBinormal = binormal; - mVertices[i].mNormal.set(normal.getF32ptr()); + binorm[i].load4a(binormal.getF32ptr()); + norm[i].load4a(normal.getF32ptr()); } - mHasBinormals = TRUE; - if (partial_build) { return TRUE; @@ -5563,7 +6379,10 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build) mIndices[3*i+v1] = i; mIndices[3*i+v2] = i + 1; } + + } + return TRUE; } @@ -5571,44 +6390,261 @@ void LLVolumeFace::createBinormals() { LLMemType m1(LLMemType::MTYPE_VOLUME); - if (!mHasBinormals) + if (!mBinormals) { + allocateBinormals(mNumVertices); + //generate binormals - for (U32 i = 0; i < mIndices.size()/3; i++) + 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 < (U32)mNumIndices/3; i++) { //for each triangle - const VertexData& v0 = mVertices[mIndices[i*3+0]]; - const VertexData& v1 = mVertices[mIndices[i*3+1]]; - const VertexData& v2 = mVertices[mIndices[i*3+2]]; + const U16& i0 = mIndices[i*3+0]; + const U16& i1 = mIndices[i*3+1]; + const U16& i2 = mIndices[i*3+2]; //calculate binormal - LLVector3 binorm = calc_binormal_from_triangle(v0.mPosition, v0.mTexCoord, - v1.mPosition, v1.mTexCoord, - v2.mPosition, v2.mTexCoord); + LLVector4a binormal; + calc_binormal_from_triangle(binormal, + pos[i0], tc[i0], + pos[i1], tc[i1], + pos[i2], tc[i2]); - for (U32 j = 0; j < 3; j++) - { //add triangle normal to vertices - mVertices[mIndices[i*3+j]].mBinormal += binorm; // * (weight_sum - d[j])/weight_sum; - } + + //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) { - mVertices[mIndices[i*3+2]].mBinormal += binorm; + binorm[i2].add(binormal); } else { - mVertices[mIndices[i*3+1]].mBinormal += binorm; + binorm[i1].add(binormal); } } //normalize binormals - for (U32 i = 0; i < mVertices.size(); i++) + for (U32 i = 0; i < (U32)mNumVertices; i++) { - mVertices[i].mBinormal.normVec(); - mVertices[i].mNormal.normVec(); + binorm[i].normalize3fast(); + //bump map/planar projection code requires normals to be normalized + mNormals[i].normalize3fast(); } + } +} - mHasBinormals = TRUE; +void LLVolumeFace::resizeVertices(S32 num_verts) +{ + ll_aligned_free_16(mPositions); + ll_aligned_free_16(mNormals); + ll_aligned_free_16(mBinormals); + ll_aligned_free_16(mTexCoords); + + mBinormals = NULL; + + if (num_verts) + { + mPositions = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts); + assert_aligned(mPositions, 16); + mNormals = (LLVector4a*) ll_aligned_malloc_16(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*) ll_aligned_malloc_16(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 + ll_aligned_free_16(mBinormals); + mBinormals = NULL; + + mPositions[mNumVertices] = pos; + mNormals[mNumVertices] = norm; + mTexCoords[mNumVertices] = tc; + + mNumVertices++; +} + +void LLVolumeFace::allocateBinormals(S32 num_verts) +{ + ll_aligned_free_16(mBinormals); + mBinormals = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts); +} + +void LLVolumeFace::resizeIndices(S32 num_indices) +{ + ll_aligned_free_16(mIndices); + + if (num_indices) + { + //pad index block end to allow for QWORD reads + S32 size = ((num_indices*sizeof(U16)) + 0xF) & ~0xF; + + mIndices = (U16*) ll_aligned_malloc_16(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& v, std::vector& 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 < (U32)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 < (U32)face.mNumIndices; ++i) + { //copy indices, offsetting by old vertex count + dst_idx[i] = face.mIndices[i]+offset; } } @@ -5638,18 +6674,20 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build) num_vertices = mNumS*mNumT; num_indices = (mNumS-1)*(mNumT-1)*6; - mVertices.resize(num_vertices); - if (!partial_build) { - mIndices.resize(num_indices); - mEdge.resize(num_indices); - } - else - { - mHasBinormals = FALSE; + 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; @@ -5700,21 +6738,20 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build) i = mBeginS + s + max_s*t; } - mVertices[cur_vertex].mPosition = mesh[i].mPos; - mVertices[cur_vertex].mTexCoord = LLVector2(ss,tt); + pos[cur_vertex].load3(mesh[i].mPos.mV); + tc[cur_vertex] = LLVector2(ss,tt); - mVertices[cur_vertex].mNormal = LLVector3(0,0,0); - mVertices[cur_vertex].mBinormal = LLVector3(0,0,0); - + norm[cur_vertex].clear(); cur_vertex++; if ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2 && s > 0) { - mVertices[cur_vertex].mPosition = mesh[i].mPos; - mVertices[cur_vertex].mTexCoord = LLVector2(ss,tt); + + pos[cur_vertex].load3(mesh[i].mPos.mV); + tc[cur_vertex] = LLVector2(ss,tt); - mVertices[cur_vertex].mNormal = LLVector3(0,0,0); - mVertices[cur_vertex].mBinormal = LLVector3(0,0,0); + norm[cur_vertex].clear(); + cur_vertex++; } } @@ -5732,12 +6769,10 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build) i = mBeginS + s + max_s*t; ss = profile[mBeginS + s].mV[2] - begin_stex; - mVertices[cur_vertex].mPosition = mesh[i].mPos; - mVertices[cur_vertex].mTexCoord = LLVector2(ss,tt); - - mVertices[cur_vertex].mNormal = LLVector3(0,0,0); - mVertices[cur_vertex].mBinormal = LLVector3(0,0,0); - + pos[cur_vertex].load3(mesh[i].mPos.mV); + tc[cur_vertex] = LLVector2(ss,tt); + norm[cur_vertex].clear(); + cur_vertex++; } } @@ -5748,13 +6783,11 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build) LLVector4a& face_max = mExtents[1]; mCenter->clear(); - face_max.load3(mVertices[0].mPosition.mV); - face_min = face_max; - for (U32 i = 1; i < mVertices.size(); ++i) + face_min = face_max = pos[0]; + + for (U32 i = 1; i < (U32)mNumVertices; ++i) { - LLVector4a pos; - pos.load3(mVertices[i].mPosition.mV); - update_min_max(face_min, face_max, pos); + update_min_max(face_min, face_max, pos[i]); } mCenter->setAdd(face_min, face_max); @@ -5821,38 +6854,58 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build) } } + //clear normals + for (U32 i = 0; i < (U32)mNumVertices; i++) + { + mNormals[i].clear(); + } + //generate normals - for (U32 i = 0; i < mIndices.size()/3; i++) //for each triangle + for (U32 i = 0; i < (U32)mNumIndices/3; i++) //for each triangle { const U16* idx = &(mIndices[i*3]); - - VertexData* v[] = - { &mVertices[idx[0]], &mVertices[idx[1]], &mVertices[idx[2]] }; - + + + 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 - LLVector3 norm = (v[0]->mPosition-v[1]->mPosition) % (v[0]->mPosition-v[2]->mPosition); - - v[0]->mNormal += norm; - v[1]->mNormal += norm; - v[2]->mNormal += norm; + 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 - v[i%2+1]->mNormal += norm; + n[i%2+1]->add(c); } // adjust normals based on wrapping and stitching - BOOL s_bottom_converges = ((mVertices[0].mPosition - mVertices[mNumS*(mNumT-2)].mPosition).magVecSquared() < 0.000001f); - BOOL s_top_converges = ((mVertices[mNumS-1].mPosition - mVertices[mNumS*(mNumT-2)+mNumS-1].mPosition).magVecSquared() < 0.000001f); + 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++) { - LLVector3 norm = mVertices[i].mNormal + mVertices[mNumS*(mNumT-1)+i].mNormal; - mVertices[i].mNormal = norm; - mVertices[mNumS*(mNumT-1)+i].mNormal = norm; + LLVector4a n; + n.setAdd(norm[i], norm[mNumS*(mNumT-1)+i]); + norm[i] = n; + norm[mNumS*(mNumT-1)+i] = n; } } @@ -5860,9 +6913,10 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build) { //wrap normals on S for (S32 i = 0; i < mNumT; i++) { - LLVector3 norm = mVertices[mNumS*i].mNormal + mVertices[mNumS*i+mNumS-1].mNormal; - mVertices[mNumS * i].mNormal = norm; - mVertices[mNumS * i+mNumS-1].mNormal = norm; + LLVector4a n; + n.setAdd(norm[mNumS*i], norm[mNumS*i+mNumS-1]); + norm[mNumS * i] = n; + norm[mNumS * i+mNumS-1] = n; } } @@ -5873,7 +6927,7 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build) { //all lower S have same normal for (S32 i = 0; i < mNumT; i++) { - mVertices[mNumS*i].mNormal = LLVector3(1,0,0); + norm[mNumS*i].set(1,0,0); } } @@ -5881,12 +6935,11 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build) { //all upper S have same normal for (S32 i = 0; i < mNumT; i++) { - mVertices[mNumS*i+mNumS-1].mNormal = LLVector3(-1,0,0); + norm[mNumS*i+mNumS-1].set(-1,0,0); } } } } - else // logic for sculpt volumes { BOOL average_poles = FALSE; @@ -5909,30 +6962,33 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build) { // average normals for north pole - LLVector3 average(0.0, 0.0, 0.0); + LLVector4a average; + average.clear(); + for (S32 i = 0; i < mNumS; i++) { - average += mVertices[i].mNormal; + average.add(norm[i]); } // set average for (S32 i = 0; i < mNumS; i++) { - mVertices[i].mNormal = average; + norm[i] = average; } // average normals for south pole - average = LLVector3(0.0, 0.0, 0.0); + average.clear(); + for (S32 i = 0; i < mNumS; i++) { - average += mVertices[i + mNumS * (mNumT - 1)].mNormal; + average.add(norm[i + mNumS * (mNumT - 1)]); } // set average for (S32 i = 0; i < mNumS; i++) { - mVertices[i + mNumS * (mNumT - 1)].mNormal = average; + norm[i + mNumS * (mNumT - 1)] = average; } } @@ -5942,23 +6998,22 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build) { for (S32 i = 0; i < mNumT; i++) { - LLVector3 norm = mVertices[mNumS*i].mNormal + mVertices[mNumS*i+mNumS-1].mNormal; - mVertices[mNumS * i].mNormal = norm; - mVertices[mNumS * i+mNumS-1].mNormal = norm; + 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++) { - LLVector3 norm = mVertices[i].mNormal + mVertices[mNumS*(mNumT-1)+i].mNormal; - mVertices[i].mNormal = norm; - mVertices[mNumS*(mNumT-1)+i].mNormal = norm; + LLVector4a n; + n.setAdd(norm[i], norm[mNumS*(mNumT-1)+i]); + norm[i] = n; + norm[mNumS*(mNumT-1)+i] = n; } - } } @@ -5968,41 +7023,51 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build) // Finds binormal based on three vertices with texture coordinates. // Fills in dummy values if the triangle has degenerate texture coordinates. -LLVector3 calc_binormal_from_triangle( - const LLVector3& pos0, +void calc_binormal_from_triangle(LLVector4a& binormal, + + const LLVector4a& pos0, const LLVector2& tex0, - const LLVector3& pos1, + const LLVector4a& pos1, const LLVector2& tex1, - const LLVector3& pos2, + const LLVector4a& pos2, const LLVector2& tex2) { - LLVector3 rx0( pos0.mV[VX], tex0.mV[VX], tex0.mV[VY] ); - LLVector3 rx1( pos1.mV[VX], tex1.mV[VX], tex1.mV[VY] ); - LLVector3 rx2( pos2.mV[VX], tex2.mV[VX], tex2.mV[VY] ); + 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] ); - LLVector3 ry0( pos0.mV[VY], tex0.mV[VX], tex0.mV[VY] ); - LLVector3 ry1( pos1.mV[VY], tex1.mV[VX], tex1.mV[VY] ); - LLVector3 ry2( pos2.mV[VY], 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] ); - LLVector3 rz0( pos0.mV[VZ], tex0.mV[VX], tex0.mV[VY] ); - LLVector3 rz1( pos1.mV[VZ], tex1.mV[VX], tex1.mV[VY] ); - LLVector3 rz2( pos2.mV[VZ], 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] ); - LLVector3 r0 = (rx0 - rx1) % (rx0 - rx2); - LLVector3 r1 = (ry0 - ry1) % (ry0 - ry2); - LLVector3 r2 = (rz0 - rz1) % (rz0 - rz2); + LLVector4a lhs, rhs; - if( r0.mV[VX] && r1.mV[VX] && r2.mV[VX] ) + 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] ) { - LLVector3 binormal( - -r0.mV[VZ] / r0.mV[VX], - -r1.mV[VZ] / r1.mV[VX], - -r2.mV[VZ] / r2.mV[VX]); + binormal.set( + -r0[VZ] / r0[VX], + -r1[VZ] / r1[VX], + -r2[VZ] / r2[VX]); // binormal.normVec(); - return binormal; } else { - return LLVector3( 0, 1 , 0 ); + binormal.set( 0, 1 , 0 ); } } diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index 03498881d..2f0dcb616 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -40,8 +40,13 @@ class LLPathParams; class LLVolumeParams; class LLProfile; class LLPath; + +template class LLOctreeNode; + +class LLVector4a; class LLVolumeFace; class LLVolume; +class LLVolumeTriangle; #include "lldarray.h" #include "lluuid.h" @@ -800,20 +805,84 @@ class LLVolumeFace public: class VertexData { + enum + { + POSITION = 0, + NORMAL = 1 + }; + + private: + void init(); public: - LLVector3 mPosition; - LLVector3 mNormal; - LLVector3 mBinormal; + VertexData(); + VertexData(const VertexData& rhs); + const VertexData& operator=(const VertexData& rhs); + + ~VertexData(); + LLVector4a& getPosition(); + LLVector4a& getNormal(); + const LLVector4a& getPosition() const; + const LLVector4a& getNormal() const; + void setPosition(const LLVector4a& pos); + void setNormal(const LLVector4a& norm); + + LLVector2 mTexCoord; + + bool operator<(const VertexData& rhs) const; + bool operator==(const VertexData& rhs) const; + bool compareNormal(const VertexData& rhs, F32 angle_cutoff) const; + + private: + LLVector4a* mData; }; + LLVolumeFace(); LLVolumeFace(const LLVolumeFace& src); LLVolumeFace& operator=(const LLVolumeFace& rhs); ~LLVolumeFace(); +private: + void freeData(); +public: + BOOL create(LLVolume* volume, BOOL partial_build = FALSE); void createBinormals(); + + void appendFace(const LLVolumeFace& face, LLMatrix4& transform, LLMatrix4& normal_tranform); + void resizeVertices(S32 num_verts); + void allocateBinormals(S32 num_verts); + void resizeIndices(S32 num_indices); + void fillFromLegacyData(std::vector& v, std::vector& idx); + + void pushVertex(const VertexData& cv); + void pushVertex(const LLVector4a& pos, const LLVector4a& norm, const LLVector2& tc); + void pushIndex(const U16& idx); + + void swapData(LLVolumeFace& rhs); + + void getVertexData(U16 indx, LLVolumeFace::VertexData& cv); + + class VertexMapData : public LLVolumeFace::VertexData + { + public: + U16 mIndex; + + bool operator==(const LLVolumeFace::VertexData& rhs) const; + + struct ComparePosition + { + bool operator()(const LLVector3& a, const LLVector3& b) const; + }; + + typedef std::map, VertexMapData::ComparePosition > PointMap; + }; + + void optimize(F32 angle_cutoff = 2.f); + void cacheOptimize(); + + void createOctree(F32 scaler = 0.25f, const LLVector4a& center = LLVector4a(0,0,0), const LLVector4a& size = LLVector4a(0.5f,0.5f,0.5f)); enum { @@ -833,7 +902,6 @@ public: public: S32 mID; U32 mTypeMask; - BOOL mHasBinormals; // Only used for INNER/OUTER faces S32 mBeginS; @@ -845,9 +913,17 @@ public: LLVector4a* mCenter; LLVector2 mTexCoordExtents[2]; //minimum and maximum of texture coordinates of the face. - std::vector mVertices; - std::vector mIndices; + S32 mNumVertices; + S32 mNumIndices; + + LLVector4a* mPositions; + LLVector4a* mNormals; + LLVector4a* mBinormals; + LLVector2* mTexCoords; + U16* mIndices; + std::vector mEdge; + LLOctreeNode* mOctree; private: BOOL createUnCutCubeCap(LLVolume* volume, BOOL partial_build = FALSE); @@ -882,7 +958,7 @@ public: U8 getProfileType() const { return mParams.getProfileParams().getCurveType(); } U8 getPathType() const { return mParams.getPathParams().getCurveType(); } - S32 getNumFaces() const { return (S32)mProfilep->mFaces.size(); } + S32 getNumFaces() const; S32 getNumVolumeFaces() const { return mVolumeFaces.size(); } F32 getDetail() const { return mDetail; } const LLVolumeParams& getParams() const { return mParams; } @@ -965,6 +1041,9 @@ public: LLVector3 mLODScaleBias; // vector for biasing LOD based on scale void sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, S32 sculpt_level); + void copyVolumeFaces(const LLVolume* volume); + void cacheOptimize(); + private: void sculptGenerateMapVertices(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, U8 sculpt_type); F32 sculptGetSurfaceArea(); @@ -993,12 +1072,13 @@ protected: std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params); -LLVector3 calc_binormal_from_triangle( - const LLVector3& pos0, +void calc_binormal_from_triangle( + LLVector4a& binormal, + const LLVector4a& pos0, const LLVector2& tex0, - const LLVector3& pos1, + const LLVector4a& pos1, const LLVector2& tex1, - const LLVector3& pos2, + const LLVector4a& pos2, const LLVector2& tex2); BOOL LLLineSegmentBoxIntersect(const F32* start, const F32* end, const F32* center, const F32* size); diff --git a/indra/llmath/llvolumeoctree.cpp b/indra/llmath/llvolumeoctree.cpp index 0ac6b1350..a420c8e51 100644 --- a/indra/llmath/llvolumeoctree.cpp +++ b/indra/llmath/llvolumeoctree.cpp @@ -74,8 +74,6 @@ BOOL LLLineSegmentBoxIntersect(const LLVector4a& start, const LLVector4a& end, c return (grt & 0x7) ? false : true; } - -#if 0 //MESH LLVolumeOctreeListener::LLVolumeOctreeListener(LLOctreeNode* node) { node->addListener(this); @@ -122,7 +120,7 @@ void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode if (LLLineSegmentBoxIntersect(mStart.getF32ptr(), mEnd.getF32ptr(), vl->mBounds[0].getF32ptr(), vl->mBounds[1].getF32ptr())) { node->accept(this); - for (S32 i = 0; i < node->getChildCount(); ++i) + for (U32 i = 0; i < node->getChildCount(); ++i) { traverse(node->getChild(i)); } @@ -253,5 +251,5 @@ void LLVolumeOctreeValidate::visit(const LLOctreeNode* branch) } } } -#endif + diff --git a/indra/llmath/llvolumeoctree.h b/indra/llmath/llvolumeoctree.h index 31da2acbf..688d91dc4 100644 --- a/indra/llmath/llvolumeoctree.h +++ b/indra/llmath/llvolumeoctree.h @@ -34,7 +34,6 @@ #include "llvolume.h" #include "llvector4a.h" -#if 0 //MESH class LLVolumeTriangle : public LLRefCount { public: @@ -131,5 +130,5 @@ class LLVolumeOctreeValidate : public LLOctreeTraveler { virtual void visit(const LLOctreeNode* branch); }; -#endif //0 + #endif diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index 2272c450b..8c8229050 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -431,7 +431,8 @@ public: void setText(const LLStringExplicit &new_text) { mSearchEdit->setText(new_text); } - void setSearchCallback(void (*search_callback)(const std::string& search_string, void* user_data), void* data) { mSearchCallback = search_callback; mCallbackUserData = data; } + typedef boost::function search_callback_t; + void setSearchCallback(search_callback_t cb,void *user_data) { mSearchCallback = boost::bind(cb,_1,user_data); } // LLUICtrl interface virtual void setValue(const LLSD& value ); @@ -446,7 +447,8 @@ private: LLLineEditor* mSearchEdit; class LLButton* mClearSearchButton; - void (*mSearchCallback)(const std::string& search_string, void* user_data); + + search_callback_t mSearchCallback; }; diff --git a/indra/newview/floatersculptpreview.cpp b/indra/newview/floatersculptpreview.cpp index da8d33331..c6b85561f 100644 --- a/indra/newview/floatersculptpreview.cpp +++ b/indra/newview/floatersculptpreview.cpp @@ -730,8 +730,8 @@ void LLPreviewSculpted::setPreviewTarget(LLImageRaw* imagep, F32 distance) } const LLVolumeFace &vf = mVolume->getVolumeFace(0); - U32 num_indices = vf.mIndices.size(); - U32 num_vertices = vf.mVertices.size(); + U32 num_indices = vf.mNumIndices; + U32 num_vertices = vf.mNumVertices; mVertexBuffer = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL, 0); mVertexBuffer->allocateBuffer(num_vertices, num_indices, TRUE); @@ -747,8 +747,8 @@ void LLPreviewSculpted::setPreviewTarget(LLImageRaw* imagep, F32 distance) // build vertices and normals for (U32 i = 0; (S32)i < num_vertices; i++) { - *(vertex_strider++) = vf.mVertices[i].mPosition; - LLVector3 normal = vf.mVertices[i].mNormal; + (vertex_strider++)->set(vf.mPositions[i].getF32ptr()); + LLVector3 normal(vf.mNormals[i].getF32ptr()); normal.normalize(); *(normal_strider++) = normal; } @@ -812,7 +812,7 @@ BOOL LLPreviewSculpted::render() LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, mFullWidth, mFullHeight, FALSE); const LLVolumeFace &vf = mVolume->getVolumeFace(0); - U32 num_indices = vf.mIndices.size(); + U32 num_indices = vf.mNumIndices; mVertexBuffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL); diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 918637a23..f977b41c4 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -924,10 +924,8 @@ void LLFace::getPlanarProjectedParams(LLQuaternion* face_rot, LLVector3* face_po { const LLMatrix4& vol_mat = getWorldMatrix(); const LLVolumeFace& vf = getViewerObject()->getVolume()->getVolumeFace(mTEOffset); - LLVector4a normal4a; - normal4a.load3(vf.mVertices[0].mNormal.mV); - LLVector4a binormal4a; - binormal4a.load3(vf.mVertices[0].mBinormal.mV); + const LLVector4a& normal4a = vf.mNormals[0]; + const LLVector4a& binormal4a = vf.mBinormals[0]; LLVector2 projected_binormal; planarProjection(projected_binormal, normal4a, *vf.mCenter, binormal4a); projected_binormal -= LLVector2(0.5f, 0.5f); // this normally happens in xform() @@ -1041,14 +1039,14 @@ bool LLFace::canRenderAsMask() BOOL LLFace::getGeometryVolume(const LLVolume& volume, const S32 &f, - const LLMatrix4& mat_vert, const LLMatrix3& mat_normal, + const LLMatrix4& mat_vert_in, const LLMatrix3& mat_norm_in, const U16 &index_offset, bool force_rebuild) { llassert(verify()); const LLVolumeFace &vf = volume.getVolumeFace(f); - S32 num_vertices = (S32)vf.mVertices.size(); - S32 num_indices = (S32)vf.mIndices.size(); + S32 num_vertices = (S32)vf.mNumVertices; + S32 num_indices = (S32) vf.mNumIndices; if (mVertexBuffer.notNull()) { @@ -1081,7 +1079,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, LLStrider binormals; LLStrider indicesp; - BOOL full_rebuild = mDrawablep->isState(LLDrawable::REBUILD_VOLUME); + BOOL full_rebuild = force_rebuild || mDrawablep->isState(LLDrawable::REBUILD_VOLUME); BOOL global_volume = mDrawablep->getVOVolume()->isVolumeGlobal(); LLVector3 scale; @@ -1094,11 +1092,12 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, scale = mVObjp->getScale(); } - BOOL rebuild_pos = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_POSITION); - BOOL rebuild_color = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_COLOR); - BOOL rebuild_tcoord = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_TCOORD); - BOOL rebuild_normal = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_NORMAL); - BOOL rebuild_binormal = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_BINORMAL); + bool rebuild_pos = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_POSITION); + bool rebuild_color = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_COLOR); + bool rebuild_tcoord = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_TCOORD); + bool rebuild_normal = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_NORMAL); + bool rebuild_binormal = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_BINORMAL); + const LLTextureEntry *tep = mVObjp->getTE(f); if (!tep) rebuild_color = FALSE; // can't get color when tep is NULL @@ -1130,7 +1129,6 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, mVertexBuffer->getColorStrider(colors, mGeomIndex); } - F32 r = 0, os = 0, ot = 0, ms = 0, mt = 0, cos_ang = 0, sin_ang = 0; BOOL is_static = mDrawablep->isStatic(); BOOL is_global = is_static; @@ -1146,59 +1144,6 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, clearState(GLOBAL); } - LLVector2 tmin, tmax; - - - - if (rebuild_tcoord) - { - if (tep) - { - r = tep->getRotation(); - os = tep->mOffsetS; - ot = tep->mOffsetT; - ms = tep->mScaleS; - mt = tep->mScaleT; - cos_ang = cos(r); - sin_ang = sin(r); - } - else - { - cos_ang = 1.0f; - sin_ang = 0.0f; - os = 0.0f; - ot = 0.0f; - ms = 1.0f; - mt = 1.0f; - } - } - - U8 tex_mode = 0; - - if (isState(TEXTURE_ANIM)) - { - LLVOVolume* vobj = (LLVOVolume*) (LLViewerObject*) mVObjp; - tex_mode = vobj->mTexAnimMode; - - if (!tex_mode) - { - clearState(TEXTURE_ANIM); - } - else - { - os = ot = 0.f; - r = 0.f; - cos_ang = 1.f; - sin_ang = 0.f; - ms = mt = 1.f; - } - - if (getVirtualSize() >= MIN_TEX_ANIM_SIZE) - { //don't override texture transform during tc bake - tex_mode = 0; - } - } - LLColor4U color = (tep ? LLColor4U(tep->getColor()) : LLColor4U::white); if (rebuild_color) // FALSE if tep == NULL @@ -1226,167 +1171,361 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, mVertexBuffer->getIndexStrider(indicesp, mIndicesIndex); for (U32 i = 0; i < (U32) num_indices; i++) { - *indicesp++ = vf.mIndices[i] + index_offset; + indicesp[i] = vf.mIndices[i] + index_offset; } } + LLMatrix4a mat_normal; + mat_normal.loadu(mat_norm_in); - //bump setup - LLVector3 binormal_dir( -sin_ang, cos_ang, 0 ); - LLVector3 bump_s_primary_light_ray; - LLVector3 bump_t_primary_light_ray; + //if it's not fullbright and has no normals, bake sunlight based on face normal + //bool bake_sunlight = !getTextureEntry()->getFullbright() && + // !mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_NORMAL); - LLQuaternion bump_quat; - if (mDrawablep->isActive()) + F32 r = 0, os = 0, ot = 0, ms = 0, mt = 0, cos_ang = 0, sin_ang = 0; + + if (rebuild_tcoord) { - bump_quat = LLQuaternion(mDrawablep->getRenderMatrix()); - } - - if (bump_code) - { - mVObjp->getVolume()->genBinormals(f); - F32 offset_multiple; - switch( bump_code ) + bool do_xform; + + if (tep) { - case BE_NO_BUMP: - offset_multiple = 0.f; - break; - case BE_BRIGHTNESS: - case BE_DARKNESS: - if( mTexture.notNull() && mTexture->hasGLTexture()) + r = tep->getRotation(); + os = tep->mOffsetS; + ot = tep->mOffsetT; + ms = tep->mScaleS; + mt = tep->mScaleT; + cos_ang = cos(r); + sin_ang = sin(r); + + if (cos_ang != 1.f || + sin_ang != 0.f || + os != 0.f || + ot != 0.f || + ms != 1.f || + mt != 1.f) { - // Offset by approximately one texel - S32 cur_discard = mTexture->getDiscardLevel(); - S32 max_size = llmax( mTexture->getWidth(), mTexture->getHeight() ); - max_size <<= cur_discard; - const F32 ARTIFICIAL_OFFSET = 2.f; - offset_multiple = ARTIFICIAL_OFFSET / (F32)max_size; + do_xform = true; } else { - offset_multiple = 1.f/256; - } - break; - - default: // Standard bumpmap textures. Assumed to be 256x256 - offset_multiple = 1.f / 256; - break; - } - - F32 s_scale = 1.f; - F32 t_scale = 1.f; - if( tep ) - { - tep->getScale( &s_scale, &t_scale ); - } - // Use the nudged south when coming from above sun angle, such - // that emboss mapping always shows up on the upward faces of cubes when - // it's noon (since a lot of builders build with the sun forced to noon). - LLVector3 sun_ray = gSky.mVOSkyp->mBumpSunDir; - LLVector3 moon_ray = gSky.getMoonDirection(); - LLVector3& primary_light_ray = (sun_ray.mV[VZ] > 0) ? sun_ray : moon_ray; - - bump_s_primary_light_ray = offset_multiple * s_scale * primary_light_ray; - bump_t_primary_light_ray = offset_multiple * t_scale * primary_light_ray; - } - - U8 texgen = getTextureEntry()->getTexGen(); - if (rebuild_tcoord && texgen != LLTextureEntry::TEX_GEN_DEFAULT) - { //planar texgen needs binormals - mVObjp->getVolume()->genBinormals(f); - } - - LLVector4a scalea; - scalea.load3(scale.mV); - - for (S32 i = 0; i < num_vertices; i++) - { - if (rebuild_tcoord) - { - LLVector2 tc = vf.mVertices[i].mTexCoord; - - if (texgen != LLTextureEntry::TEX_GEN_DEFAULT) - { - LLVector4a vec; - vec.load3(vf.mVertices[i].mPosition.mV); - vec.mul(scalea); - LLVector4a norm; - norm.load3(vf.mVertices[i].mNormal.mV); - - switch (texgen) - { - case LLTextureEntry::TEX_GEN_PLANAR: - planarProjection(tc, norm, *(vf.mCenter), vec); - break; - case LLTextureEntry::TEX_GEN_SPHERICAL: - sphericalProjection(tc, norm, *(vf.mCenter), vec); - break; - case LLTextureEntry::TEX_GEN_CYLINDRICAL: - cylindricalProjection(tc, norm, *(vf.mCenter), vec); - break; - default: - break; - } - } - - if (tex_mode && mTextureMatrix) - { - LLVector3 tmp(tc.mV[0], tc.mV[1], 0.f); - tmp = tmp * *mTextureMatrix; - tc.mV[0] = tmp.mV[0]; - tc.mV[1] = tmp.mV[1]; - } - else - { - xform(tc, cos_ang, sin_ang, os, ot, ms, mt); - } - - *tex_coords++ = tc; - - if (bump_code && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_TEXCOORD1)) - { - LLVector3 tangent = vf.mVertices[i].mBinormal % vf.mVertices[i].mNormal; - - LLMatrix3 tangent_to_object; - tangent_to_object.setRows(tangent, vf.mVertices[i].mBinormal, vf.mVertices[i].mNormal); - LLVector3 binormal = binormal_dir * tangent_to_object; - binormal = binormal * mat_normal; - - if (mDrawablep->isActive()) - { - binormal *= bump_quat; - } - - binormal.normVec(); - tc += LLVector2( bump_s_primary_light_ray * tangent, bump_t_primary_light_ray * binormal ); - - *tex_coords2++ = tc; + do_xform = false; } } + else + { + do_xform = false; + } + + //bump setup + LLVector4a binormal_dir( -sin_ang, cos_ang, 0.f ); + LLVector4a bump_s_primary_light_ray(0.f, 0.f, 0.f); + LLVector4a bump_t_primary_light_ray(0.f, 0.f, 0.f); + + LLQuaternion bump_quat; + if (mDrawablep->isActive()) + { + bump_quat = LLQuaternion(mDrawablep->getRenderMatrix()); + } + + if (bump_code) + { + mVObjp->getVolume()->genBinormals(f); + F32 offset_multiple; + switch( bump_code ) + { + case BE_NO_BUMP: + offset_multiple = 0.f; + break; + case BE_BRIGHTNESS: + case BE_DARKNESS: + if( mTexture.notNull() && mTexture->hasGLTexture()) + { + // Offset by approximately one texel + S32 cur_discard = mTexture->getDiscardLevel(); + S32 max_size = llmax( mTexture->getWidth(), mTexture->getHeight() ); + max_size <<= cur_discard; + const F32 ARTIFICIAL_OFFSET = 2.f; + offset_multiple = ARTIFICIAL_OFFSET / (F32)max_size; + } + else + { + offset_multiple = 1.f/256; + } + break; + + default: // Standard bumpmap textures. Assumed to be 256x256 + offset_multiple = 1.f / 256; + break; + } + + F32 s_scale = 1.f; + F32 t_scale = 1.f; + if( tep ) + { + tep->getScale( &s_scale, &t_scale ); + } + // Use the nudged south when coming from above sun angle, such + // that emboss mapping always shows up on the upward faces of cubes when + // it's noon (since a lot of builders build with the sun forced to noon). + LLVector3 sun_ray = gSky.mVOSkyp->mBumpSunDir; + LLVector3 moon_ray = gSky.getMoonDirection(); + LLVector3& primary_light_ray = (sun_ray.mV[VZ] > 0) ? sun_ray : moon_ray; + + bump_s_primary_light_ray.load3((offset_multiple * s_scale * primary_light_ray).mV); + bump_t_primary_light_ray.load3((offset_multiple * t_scale * primary_light_ray).mV); + } + + U8 texgen = getTextureEntry()->getTexGen(); + if (rebuild_tcoord && texgen != LLTextureEntry::TEX_GEN_DEFAULT) + { //planar texgen needs binormals + mVObjp->getVolume()->genBinormals(f); + } + + U8 tex_mode = 0; + + if (isState(TEXTURE_ANIM)) + { + LLVOVolume* vobj = (LLVOVolume*) (LLViewerObject*) mVObjp; + tex_mode = vobj->mTexAnimMode; + + if (!tex_mode) + { + clearState(TEXTURE_ANIM); + } + else + { + os = ot = 0.f; + r = 0.f; + cos_ang = 1.f; + sin_ang = 0.f; + ms = mt = 1.f; + + do_xform = false; + } + + if (getVirtualSize() >= MIN_TEX_ANIM_SIZE) + { //don't override texture transform during tc bake + tex_mode = 0; + } + } + + LLVector4a scalea; + scalea.load3(scale.mV); + + bool do_bump = bump_code && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_TEXCOORD1); + bool do_tex_mat = tex_mode && mTextureMatrix; + + if (!do_bump) + { //not in atlas or not bump mapped, might be able to do a cheap update + if (texgen != LLTextureEntry::TEX_GEN_PLANAR) + { + if (!do_tex_mat) + { + /*if (!do_xform) + { + LLVector4a::memcpyNonAliased16((F32*) tex_coords.get(), (F32*) vf.mTexCoords, num_vertices*2*sizeof(F32)); + } + else*/ + { + for (S32 i = 0; i < num_vertices; i++) + { + LLVector2 tc(vf.mTexCoords[i]); + if(do_xform) + xform(tc, cos_ang, sin_ang, os, ot, ms, mt); + *tex_coords++ = tc; + } + } + } + else + { //do tex mat, no texgen, no atlas, no bump + for (S32 i = 0; i < num_vertices; i++) + { + LLVector2 tc(vf.mTexCoords[i]); + //LLVector4a& norm = vf.mNormals[i]; + //LLVector4a& center = *(vf.mCenter); + + LLVector3 tmp(tc.mV[0], tc.mV[1], 0.f); + tmp = tmp * *mTextureMatrix; + tc.mV[0] = tmp.mV[0]; + tc.mV[1] = tmp.mV[1]; + *tex_coords++ = tc; + } + } + } + else + { //no bump, no atlas, tex gen planar + if (do_tex_mat) + { + for (S32 i = 0; i < num_vertices; i++) + { + LLVector2 tc(vf.mTexCoords[i]); + LLVector4a& norm = vf.mNormals[i]; + LLVector4a& center = *(vf.mCenter); + LLVector4a vec = vf.mPositions[i]; + vec.mul(scalea); + planarProjection(tc, norm, center, vec); + + LLVector3 tmp(tc.mV[0], tc.mV[1], 0.f); + tmp = tmp * *mTextureMatrix; + tc.mV[0] = tmp.mV[0]; + tc.mV[1] = tmp.mV[1]; + + *tex_coords++ = tc; + } + } + else + { + for (S32 i = 0; i < num_vertices; i++) + { + LLVector2 tc(vf.mTexCoords[i]); + LLVector4a& norm = vf.mNormals[i]; + LLVector4a& center = *(vf.mCenter); + LLVector4a vec = vf.mPositions[i]; + vec.mul(scalea); + planarProjection(tc, norm, center, vec); + + xform(tc, cos_ang, sin_ang, os, ot, ms, mt); + + *tex_coords++ = tc; + } + } + } + } + else + { //either bump mapped or in atlas, just do the whole expensive loop + + std::vector bump_tc; + + for (S32 i = 0; i < num_vertices; i++) + { + LLVector2 tc(vf.mTexCoords[i]); - if (rebuild_pos) - { - *vertices++ = vf.mVertices[i].mPosition * mat_vert; - } + LLVector4a& norm = vf.mNormals[i]; + + LLVector4a& center = *(vf.mCenter); + + if (texgen != LLTextureEntry::TEX_GEN_DEFAULT) + { + LLVector4a vec = vf.mPositions[i]; + + vec.mul(scalea); + + switch (texgen) + { + case LLTextureEntry::TEX_GEN_PLANAR: + planarProjection(tc, norm, center, vec); + break; + case LLTextureEntry::TEX_GEN_SPHERICAL: + sphericalProjection(tc, norm, center, vec); + break; + case LLTextureEntry::TEX_GEN_CYLINDRICAL: + cylindricalProjection(tc, norm, center, vec); + break; + default: + break; + } + } + + if (tex_mode && mTextureMatrix) + { + LLVector3 tmp(tc.mV[0], tc.mV[1], 0.f); + tmp = tmp * *mTextureMatrix; + tc.mV[0] = tmp.mV[0]; + tc.mV[1] = tmp.mV[1]; + } + else + { + xform(tc, cos_ang, sin_ang, os, ot, ms, mt); + } + + + *tex_coords++ = tc; + if (do_bump) + { + bump_tc.push_back(tc); + } + } + + + if (do_bump) + { - if (rebuild_normal) + for (S32 i = 0; i < num_vertices; i++) + { + LLVector4a tangent; + tangent.setCross3(vf.mBinormals[i], vf.mNormals[i]); + + LLMatrix4a tangent_to_object; + tangent_to_object.setRows(tangent, vf.mBinormals[i], vf.mNormals[i]); + LLVector4a t; + tangent_to_object.rotate(binormal_dir, t); + LLVector4a binormal; + mat_normal.rotate(t, binormal); + + //VECTORIZE THIS + if (mDrawablep->isActive()) + { + LLVector3 t; + t.set(binormal.getF32ptr()); + t *= bump_quat; + binormal.load3(t.mV); + } + + binormal.normalize3fast(); + LLVector2 tc = bump_tc[i]; + tc += LLVector2( bump_s_primary_light_ray.dot3(tangent).getF32(), bump_t_primary_light_ray.dot3(binormal).getF32() ); + + *tex_coords2++ = tc; + } + } + } + } + + if (rebuild_pos) + { + LLMatrix4a mat_vert; + mat_vert.loadu(mat_vert_in); + + LLVector4a* src = vf.mPositions; + LLVector4a position; + for (S32 i = 0; i < num_vertices; i++) { - LLVector3 normal = vf.mVertices[i].mNormal * mat_normal; - normal.normVec(); + mat_vert.affineTransform(src[i], position); + vertices[i].set(position.getF32ptr()); + } - *normals++ = normal; - } + + } - if (rebuild_binormal) - { - LLVector3 binormal = vf.mVertices[i].mBinormal * mat_normal; - binormal.normVec(); - *binormals++ = binormal; + if (rebuild_normal) + { + for (S32 i = 0; i < num_vertices; i++) + { + LLVector4a normal; + mat_normal.rotate(vf.mNormals[i], normal); + normal.normalize3fast(); + normals[i].set(normal.getF32ptr()); } + } - if (rebuild_color) + if (rebuild_binormal) + { + for (S32 i = 0; i < num_vertices; i++) + { + LLVector4a binormal; + mat_normal.rotate(vf.mBinormals[i], binormal); + binormal.normalize3fast(); + binormals[i].set(binormal.getF32ptr()); + } + } + + + if (rebuild_color) + { + for (S32 i = 0; i < num_vertices; i++) { - *colors++ = color; + colors[i] = color; } } @@ -1395,7 +1534,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, mTexExtents[0].setVec(0,0); mTexExtents[1].setVec(1,1); xform(mTexExtents[0], cos_ang, sin_ang, os, ot, ms, mt); - xform(mTexExtents[1], cos_ang, sin_ang, os, ot, ms, mt); + xform(mTexExtents[1], cos_ang, sin_ang, os, ot, ms, mt); F32 es = vf.mTexCoordExtents[1].mV[0] - vf.mTexCoordExtents[0].mV[0] ; F32 et = vf.mTexCoordExtents[1].mV[1] - vf.mTexCoordExtents[0].mV[1] ; diff --git a/indra/newview/llfloaterimagepreview.cpp b/indra/newview/llfloaterimagepreview.cpp index 27cd5055d..2fc241cc4 100644 --- a/indra/newview/llfloaterimagepreview.cpp +++ b/indra/newview/llfloaterimagepreview.cpp @@ -348,24 +348,7 @@ bool LLFloaterImagePreview::loadImage(const std::string& src_filename) { std::string exten = gDirUtilp->getExtension(src_filename); - U32 codec = IMG_CODEC_INVALID; - std::string temp_str; - if( exten == "bmp") - { - codec = IMG_CODEC_BMP; - } - else if( exten == "tga") - { - codec = IMG_CODEC_TGA; - } - else if( exten == "jpg" || exten == "jpeg") - { - codec = IMG_CODEC_JPEG; - } - else if( exten == "png" ) - { - codec = IMG_CODEC_PNG; - } + U32 codec = LLImageBase::getCodecFromExtension(exten); LLPointer raw_image = new LLImageRaw; @@ -854,8 +837,8 @@ void LLImagePreviewSculpted::setPreviewTarget(LLImageRaw* imagep, F32 distance) } const LLVolumeFace &vf = mVolume->getVolumeFace(0); - U32 num_indices = vf.mIndices.size(); - U32 num_vertices = vf.mVertices.size(); + U32 num_indices = vf.mNumIndices; + U32 num_vertices = vf.mNumVertices; mVertexBuffer = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL, 0); mVertexBuffer->allocateBuffer(num_vertices, num_indices, TRUE); @@ -869,12 +852,12 @@ void LLImagePreviewSculpted::setPreviewTarget(LLImageRaw* imagep, F32 distance) mVertexBuffer->getIndexStrider(index_strider); // build vertices and normals - for (U32 i = 0; (S32)i < num_vertices; i++) + for (U32 i = 0; (U32)i < num_vertices; i++) { - *(vertex_strider++) = vf.mVertices[i].mPosition; - LLVector3 normal = vf.mVertices[i].mNormal; - normal.normalize(); - *(normal_strider++) = normal; + (vertex_strider++)->set(vf.mPositions[i].getF32ptr()); + LLVector4a normal(vf.mNormals[i]); + normal.normalize3fast(); + (normal_strider++)->set(normal.getF32ptr()); } // build indices @@ -936,7 +919,7 @@ BOOL LLImagePreviewSculpted::render() LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, mFullWidth, mFullHeight, FALSE); const LLVolumeFace &vf = mVolume->getVolumeFace(0); - U32 num_indices = vf.mIndices.size(); + U32 num_indices = (U32)vf.mNumIndices; mVertexBuffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL); @@ -948,8 +931,7 @@ BOOL LLImagePreviewSculpted::render() gGL.color3f(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS); mVertexBuffer->draw(LLRender::TRIANGLES, num_indices, 0); - gGL.popMatrix(); - + gGL.popMatrix(); return TRUE; } diff --git a/indra/newview/llpolymesh.cpp b/indra/newview/llpolymesh.cpp index 3a82e7304..7c433cb43 100644 --- a/indra/newview/llpolymesh.cpp +++ b/indra/newview/llpolymesh.cpp @@ -1426,20 +1426,24 @@ BOOL LLPolyMesh::loadOBJ(LLFILE *fp) S32 f1 = faces[f][1]; S32 f2 = faces[f][2]; - LLVector3& v0 = coords[f0]; - LLVector3& v1 = coords[f1]; - LLVector3& v2 = coords[f2]; - + LLVector4a v0; + LLVector4a v1; + LLVector4a v2; + v0.load3(coords[f0].mV);; + v1.load3(coords[f1].mV);; + v2.load3(coords[f2].mV);; LLVector2& t0 = tex[f0]; LLVector2& t1 = tex[f1]; LLVector2& t2 = tex[f2]; - LLVector3 binorm = calc_binormal_from_triangle(v0, t0, v1, t1, v2, t2); - binorm.normVec(); + LLVector4a binorm; + calc_binormal_from_triangle(binorm, v0, t0, v1, t1, v2, t2); + binorm.normalize3fast(); - binormals[f0] += binorm; - binormals[f1] += binorm; - binormals[f2] += binorm; + LLVector3 binormdelta(binorm.getF32ptr()); + binormals[f0] += binormdelta; + binormals[f1] += binormdelta; + binormals[f2] += binormdelta; } for ( v=0; vgetNumVolumeFaces(); ++i) { const LLVolumeFace& face = volume->getVolumeFace(i); - glVertexPointer(3, GL_FLOAT, 16, face.mVertices[i].mPosition.mV); - //This probably doesn't work. - glDrawElements(GL_TRIANGLES, face.mIndices.size(), GL_UNSIGNED_SHORT, (GLvoid*)face.mIndices[i]); + glVertexPointer(3, GL_FLOAT, 16, face.mPositions); + glDrawElements(GL_TRIANGLES, face.mNumIndices, GL_UNSIGNED_SHORT, face.mIndices); } } diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h index fd2075092..bc11da84d 100644 --- a/indra/newview/llspatialpartition.h +++ b/indra/newview/llspatialpartition.h @@ -71,6 +71,18 @@ protected: ~LLDrawInfo(); public: + + LLDrawInfo(const LLDrawInfo& rhs) + { + *this = rhs; + } + + const LLDrawInfo& operator=(const LLDrawInfo& rhs) + { + llerrs << "Illegal operation!" << llendl; + return *this; + } + LLDrawInfo(U16 start, U16 end, U32 count, U32 offset, LLViewerTexture* image, LLVertexBuffer* buffer, BOOL fullbright = FALSE, U8 bump = 0, BOOL particle = FALSE, F32 part_size = 0); @@ -164,6 +176,18 @@ class LLSpatialGroup : public LLOctreeListener friend class LLSpatialPartition; friend class LLOctreeStateCheck; public: + + LLSpatialGroup(const LLSpatialGroup& rhs) + { + *this = rhs; + } + + const LLSpatialGroup& operator=(const LLSpatialGroup& rhs) + { + llerrs << "Illegal operation!" << llendl; + return *this; + } + static std::set sPendingQueries; //pending occlusion queries static U32 sNodeCount; static BOOL sNoDelete; //deletion of spatial groups and draw info not allowed if TRUE @@ -173,7 +197,7 @@ public: typedef std::vector > drawmap_elem_t; typedef std::map draw_map_t; typedef std::vector > buffer_list_t; - typedef std::map, buffer_list_t> buffer_texture_map_t; + typedef std::map buffer_texture_map_t; typedef std::map buffer_map_t; typedef LLOctreeListener BaseType; diff --git a/indra/newview/llviewercamera.cpp b/indra/newview/llviewercamera.cpp index efdf9e1fe..f08404069 100644 --- a/indra/newview/llviewercamera.cpp +++ b/indra/newview/llviewercamera.cpp @@ -773,10 +773,9 @@ BOOL LLViewerCamera::areVertsVisible(LLViewerObject* volumep, BOOL all_verts) { const LLVolumeFace& face = volume->getVolumeFace(i); - for (U32 v = 0; v < face.mVertices.size(); v++) + for (U32 v = 0; v < (U32)face.mNumVertices; v++) { - LLVector4a src_vec; - src_vec.load3(face.mVertices[v].mPosition.mV); + const LLVector4a& src_vec = face.mPositions[v]; LLVector4a vec; mata.affineTransform(src_vec, vec); diff --git a/indra/newview/llvotextbubble.cpp b/indra/newview/llvotextbubble.cpp index fcbf93367..5b46ec681 100644 --- a/indra/newview/llvotextbubble.cpp +++ b/indra/newview/llvotextbubble.cpp @@ -216,7 +216,7 @@ void LLVOTextBubble::updateFaceSize(S32 idx) else { const LLVolumeFace& vol_face = getVolume()->getVolumeFace(idx); - face->setSize(vol_face.mVertices.size(), vol_face.mIndices.size()); + face->setSize(vol_face.mNumVertices, vol_face.mNumIndices); } } @@ -234,19 +234,27 @@ void LLVOTextBubble::getGeometry(S32 idx, const LLVolumeFace& face = getVolume()->getVolumeFace(idx); - LLVector3 pos = getPositionAgent(); + LLVector4a pos; + pos.load3(getPositionAgent().mV); + + LLVector4a scale; + scale.load3(getScale().mV); + LLColor4U color = LLColor4U(getTE(idx)->getColor()); U32 offset = mDrawable->getFace(idx)->getGeomIndex(); - for (U32 i = 0; i < face.mVertices.size(); i++) + for (U32 i = 0; i < (U32)face.mNumVertices; i++) { - *verticesp++ = face.mVertices[i].mPosition.scaledVec(getScale()) + pos; - *normalsp++ = face.mVertices[i].mNormal; - *texcoordsp++ = face.mVertices[i].mTexCoord; + LLVector4a vertpos; + vertpos.setMul(face.mPositions[i],scale); + vertpos.add(pos); + (verticesp++)->set(vertpos.getF32ptr()); + (normalsp++)->set(face.mNormals[i].getF32ptr()); + *texcoordsp++ = face.mTexCoords[i]; *colorsp++ = color; } - for (U32 i = 0; i < face.mIndices.size(); i++) + for (U32 i = 0; i < (U32)face.mNumIndices; i++) { *indicesp++ = face.mIndices[i] + offset; } diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 55e898888..421be412f 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -1342,7 +1342,9 @@ void LLVOVolume::updateFaceSize(S32 idx) else { const LLVolumeFace& vol_face = getVolume()->getVolumeFace(idx); - facep->setSize(vol_face.mVertices.size(), vol_face.mIndices.size(),true); + facep->setSize(vol_face.mNumVertices, vol_face.mNumIndices, + true); // <--- volume faces should be padded for 16-byte alignment + } } @@ -2033,6 +2035,49 @@ const LLMatrix4 LLVOVolume::getRenderMatrix() const return mDrawable->getWorldMatrix(); } + +U32 LLVOVolume::getTriangleCount() +{ + U32 count = 0; + LLVolume* volume = getVolume(); + if (volume) + { + count = volume->getNumTriangles(); + } + + return count; +} + +U32 LLVOVolume::getHighLODTriangleCount() +{ + U32 ret = 0; + + LLVolume* volume = getVolume(); + + if (!isSculpted()) + { + LLVolume* ref = LLPrimitive::getVolumeManager()->refVolume(volume->getParams(), 3); + ret = ref->getNumTriangles(); + LLPrimitive::getVolumeManager()->unrefVolume(ref); + } + /*else if (isMesh()) + { + LLVolume* ref = LLPrimitive::getVolumeManager()->refVolume(volume->getParams(), 3); + if (ref->isTetrahedron() || ref->getNumVolumeFaces() == 0) + { + gMeshRepo.loadMesh(this, volume->getParams(), LLModel::LOD_HIGH); + } + ret = ref->getNumTriangles(); + LLPrimitive::getVolumeManager()->unrefVolume(ref); + }*/ + else + { //default sculpts have a constant number of triangles + ret = 31*2*31; //31 rows of 31 columns of quads for a 32x32 vertex patch + } + + return ret; +} + //static void LLVOVolume::preUpdateGeom() { @@ -2070,7 +2115,7 @@ void LLVOVolume::setSelected(BOOL sel) } } -void LLVOVolume::updateSpatialExtents(LLVector4a& min, LLVector4a& max) +void LLVOVolume::updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax) { } @@ -2805,6 +2850,32 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group) } } +struct CompareBatchBreakerModified +{ + bool operator()(const LLFace* const& lhs, const LLFace* const& rhs) + { + const LLTextureEntry* lte = lhs->getTextureEntry(); + const LLTextureEntry* rte = rhs->getTextureEntry(); + + if (lte->getBumpmap() != rte->getBumpmap()) + { + return lte->getBumpmap() < rte->getBumpmap(); + } + else if (lte->getFullbright() != rte->getFullbright()) + { + return lte->getFullbright() < rte->getFullbright(); + } + else if (lte->getGlow() != rte->getGlow()) + { + return lte->getGlow() < rte->getGlow(); + } + else + { + return lhs->getTexture() < rhs->getTexture(); + } + + } +}; void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::vector& faces, BOOL distance_sort) { //calculate maximum number of vertices to store in a single buffer @@ -2815,7 +2886,7 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std:: if (!distance_sort) { //sort faces by things that break batches - std::sort(faces.begin(), faces.end(), LLFace::CompareBatchBreaker()); + std::sort(faces.begin(), faces.end(), CompareBatchBreakerModified()); } else { @@ -2859,7 +2930,7 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std:: U32 index_count = facep->getIndicesCount(); U32 geom_count = facep->getGeomCount(); - //sum up vertices needed for this texture + //sum up vertices needed for this render batch std::vector::iterator i = face_iter; ++i; @@ -2880,7 +2951,7 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std:: //create/delete/resize vertex buffer if needed LLVertexBuffer* buffer = NULL; - LLSpatialGroup::buffer_texture_map_t::iterator found_iter = group->mBufferMap[mask].find(tex); + LLSpatialGroup::buffer_texture_map_t::iterator found_iter = group->mBufferMap[mask].find(*face_iter); if (found_iter != group->mBufferMap[mask].end()) { @@ -2911,7 +2982,7 @@ void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std:: } } - buffer_map[mask][tex].push_back(buffer); + buffer_map[mask][*face_iter].push_back(buffer); //add face geometry diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index 420dda150..22bed531f 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -118,6 +118,8 @@ public: /*virtual*/ const LLMatrix4 getRenderMatrix() const; + /*virtual*/ U32 getTriangleCount(); + /*virtual*/ U32 getHighLODTriangleCount(); /*virtual*/ BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end, S32 face = -1, // which face to check, -1 = ALL_SIDES BOOL pick_transparent = FALSE, From 179193d173a863fc6069516fa03535cf188648ac Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sat, 23 Jul 2011 03:24:10 -0500 Subject: [PATCH 06/22] Some minor cleanup. Bringing stuff in-line with current to ensure it all works before mesh is actually plugged in. --- indra/llmath/llvolume.cpp | 5 -- indra/llmath/llvolume.h | 1 - indra/newview/lldrawpool.cpp | 5 +- indra/newview/lldrawpoolalpha.cpp | 6 +- indra/newview/llface.cpp | 22 ++++++- indra/newview/llface.h | 4 +- indra/newview/llselectmgr.cpp | 104 ++++++++++++++---------------- indra/newview/llselectmgr.h | 1 - indra/newview/llviewerwindow.cpp | 29 +++++++++ indra/newview/llvoavatar.cpp | 23 ++++--- 10 files changed, 121 insertions(+), 79 deletions(-) diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 46a22749a..9957aa7c2 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -3980,7 +3980,6 @@ S32 LLVolume::getNumTriangles() const //----------------------------------------------------------------------------- void LLVolume::generateSilhouetteVertices(std::vector &vertices, std::vector &normals, - std::vector &segments, const LLVector3& obj_cam_vec_in, const LLMatrix4& mat_in, const LLMatrix3& norm_mat_in, @@ -3999,7 +3998,6 @@ void LLVolume::generateSilhouetteVertices(std::vector &vertices, vertices.clear(); normals.clear(); - segments.clear(); S32 cur_index = 0; //for each face @@ -4084,13 +4082,11 @@ void LLVolume::generateSilhouetteVertices(std::vector &vertices, 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 } @@ -4196,7 +4192,6 @@ void LLVolume::generateSilhouetteVertices(std::vector &vertices, t.normalize3fast(); normals.push_back(LLVector3(t[0], t[1], t[2])); - segments.push_back(vertices.size()); } } } diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index 2f0dcb616..ba3900ebc 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -992,7 +992,6 @@ public: void generateSilhouetteVertices(std::vector &vertices, std::vector &normals, - std::vector &segments, const LLVector3& view_vec, const LLMatrix4& mat, const LLMatrix3& norm_mat, diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp index 474538a17..89e46013f 100644 --- a/indra/newview/lldrawpool.cpp +++ b/indra/newview/lldrawpool.cpp @@ -471,6 +471,8 @@ void LLRenderPass::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture) { applyModelMatrix(params); + bool tex_setup = false; + if (texture) { if (params.mTexture.notNull()) @@ -479,6 +481,7 @@ void LLRenderPass::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture) gGL.getTexUnit(0)->bind(params.mTexture, TRUE) ; if (params.mTextureMatrix) { + tex_setup = true; glMatrixMode(GL_TEXTURE); glLoadMatrixf((GLfloat*) params.mTextureMatrix->mMatrix); gPipeline.mTextureMatrixOps++; @@ -501,7 +504,7 @@ void LLRenderPass::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture) gPipeline.addTrianglesDrawn(params.mCount/3); } - if (params.mTextureMatrix && texture && params.mTexture.notNull()) + if (tex_setup) { glLoadIdentity(); glMatrixMode(GL_MODELVIEW); diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp index d6ea59ff7..926fa78d7 100644 --- a/indra/newview/lldrawpoolalpha.cpp +++ b/indra/newview/lldrawpoolalpha.cpp @@ -324,7 +324,6 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask) LLRenderPass::applyModelMatrix(params); - { if (params.mFullbright) { // Turn off lighting if it hasn't already been so. @@ -394,6 +393,7 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask) params.mGroup->rebuildMesh(); } + bool tex_setup = false; if (params.mTexture.notNull()) { @@ -404,13 +404,13 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask) } if (params.mTextureMatrix) { + tex_setup = true; gGL.getTexUnit(0)->activate(); glMatrixMode(GL_TEXTURE); glLoadMatrixf((GLfloat*) params.mTextureMatrix->mMatrix); gPipeline.mTextureMatrixOps++; } } - } params.mVertexBuffer->setBuffer(mask); params.mVertexBuffer->drawRange(LLRender::TRIANGLES, params.mStart, params.mEnd, params.mCount, params.mOffset); gPipeline.addTrianglesDrawn(params.mCount/3); @@ -435,7 +435,7 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask) gGL.blendFunc(mColorSFactor, mColorDFactor, mAlphaSFactor, mAlphaDFactor); } - if (params.mTextureMatrix && params.mTexture.notNull()) + if (tex_setup) { gGL.getTexUnit(0)->activate(); glLoadIdentity(); diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index f977b41c4..8742b35ae 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -367,6 +367,24 @@ void LLFace::setSize(S32 num_vertices, const S32 num_indices, bool align) llassert(verify()); } +void LLFace::setGeomIndex(U16 idx) +{ + if (mGeomIndex != idx) + { + mGeomIndex = idx; + mVertexBuffer = NULL; + } +} + +void LLFace::setIndicesIndex(S32 idx) +{ + if (mIndicesIndex != idx) + { + mIndicesIndex = idx; + mVertexBuffer = NULL; + } +} + //============================================================================ U16 LLFace::getGeometryAvatar( @@ -1505,7 +1523,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, LLVector4a normal; mat_normal.rotate(vf.mNormals[i], normal); normal.normalize3fast(); - normals[i].set(normal.getF32ptr()); + normals[i].set(normal.getF32ptr()); } } @@ -1516,7 +1534,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, LLVector4a binormal; mat_normal.rotate(vf.mBinormals[i], binormal); binormal.normalize3fast(); - binormals[i].set(binormal.getF32ptr()); + binormals[i].set(binormal.getF32ptr()); } } diff --git a/indra/newview/llface.h b/indra/newview/llface.h index 7abe0a9d0..c6a2728b4 100644 --- a/indra/newview/llface.h +++ b/indra/newview/llface.h @@ -202,8 +202,8 @@ public: BOOL verify(const U32* indices_array = NULL) const; void printDebugInfo() const; - void setGeomIndex(U16 idx) { mGeomIndex = idx; } - void setIndicesIndex(S32 idx) { mIndicesIndex = idx; } + void setGeomIndex(U16 idx); + void setIndicesIndex(S32 idx); void setDrawInfo(LLDrawInfo* draw_info); F32 getTextureVirtualSize() ; diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 1359fe05a..9bea903ff 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -5224,7 +5224,6 @@ LLSelectNode::LLSelectNode(const LLSelectNode& nodep) mSilhouetteVertices = nodep.mSilhouetteVertices; mSilhouetteNormals = nodep.mSilhouetteNormals; - mSilhouetteSegments = nodep.mSilhouetteSegments; mSilhouetteExists = nodep.mSilhouetteExists; mObject = nodep.mObject; @@ -5536,17 +5535,15 @@ void LLSelectNode::renderOneSilhouette(const LLColor4 &color) gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); gGL.begin(LLRender::LINES); { - S32 i = 0; - for (S32 seg_num = 0; seg_num < (S32)mSilhouetteSegments.size(); seg_num++) + for(S32 i = 0; i < (S32)mSilhouetteVertices.size(); i += 2) { - for(; i < mSilhouetteSegments[seg_num]; i++) - { - u_coord += u_divisor * LLSelectMgr::sHighlightUScale; - - gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f); - gGL.texCoord2f( u_coord, v_coord ); - gGL.vertex3fv( mSilhouetteVertices[i].mV ); - } + u_coord += u_divisor * LLSelectMgr::sHighlightUScale; + gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f); + gGL.texCoord2f( u_coord, v_coord ); + gGL.vertex3fv( mSilhouetteVertices[i].mV); + u_coord += u_divisor * LLSelectMgr::sHighlightUScale; + gGL.texCoord2f( u_coord, v_coord ); + gGL.vertex3fv(mSilhouetteVertices[i+1].mV); } } gGL.end(); @@ -5557,51 +5554,50 @@ void LLSelectNode::renderOneSilhouette(const LLColor4 &color) gGL.setSceneBlendType(LLRender::BT_ALPHA); gGL.begin(LLRender::TRIANGLES); { - S32 i = 0; - for (S32 seg_num = 0; seg_num < (S32)mSilhouetteSegments.size(); seg_num++) + for(S32 i = 0; i < (S32)mSilhouetteVertices.size(); i+=2) { - S32 first_i = i; - LLVector3 v; - LLVector2 t; - - for(; i < mSilhouetteSegments[seg_num]; i++) - { - - if (i == first_i) { - LLVector3 vert = (mSilhouetteNormals[i]) * silhouette_thickness; - vert += mSilhouetteVertices[i]; - - gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.0f); //LLSelectMgr::sHighlightAlpha); - gGL.texCoord2f( u_coord, v_coord + LLSelectMgr::sHighlightVScale ); - gGL.vertex3fv( vert.mV ); - - u_coord += u_divisor * LLSelectMgr::sHighlightUScale; - - gGL.color4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2); - gGL.texCoord2f( u_coord, v_coord ); - gGL.vertex3fv( mSilhouetteVertices[i].mV ); - - v = mSilhouetteVertices[i]; - t = LLVector2(u_coord, v_coord); - } - else { - LLVector3 vert = (mSilhouetteNormals[i]) * silhouette_thickness; - vert += mSilhouetteVertices[i]; - - gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.0f); //LLSelectMgr::sHighlightAlpha); - gGL.texCoord2f( u_coord, v_coord + LLSelectMgr::sHighlightVScale ); - gGL.vertex3fv( vert.mV ); - gGL.vertex3fv( vert.mV ); - - gGL.texCoord2fv(t.mV); - u_coord += u_divisor * LLSelectMgr::sHighlightUScale; - gGL.color4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2); - gGL.vertex3fv(v.mV); - gGL.texCoord2f( u_coord, v_coord ); - gGL.vertex3fv( mSilhouetteVertices[i].mV ); - - } + if (!mSilhouetteNormals[i].isFinite() || + !mSilhouetteNormals[i+1].isFinite()) + { //skip skewed segments + continue; } + + LLVector3 v[4]; + LLVector2 tc[4]; + v[0] = mSilhouetteVertices[i] + (mSilhouetteNormals[i] * silhouette_thickness); + tc[0].set(u_coord, v_coord + LLSelectMgr::sHighlightVScale); + + v[1] = mSilhouetteVertices[i]; + tc[1].set(u_coord, v_coord); + + u_coord += u_divisor * LLSelectMgr::sHighlightUScale; + + v[2] = mSilhouetteVertices[i+1] + (mSilhouetteNormals[i+1] * silhouette_thickness); + tc[2].set(u_coord, v_coord + LLSelectMgr::sHighlightVScale); + + v[3] = mSilhouetteVertices[i+1]; + tc[3].set(u_coord,v_coord); + + gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.0f); //LLSelectMgr::sHighlightAlpha); + gGL.texCoord2fv(tc[0].mV); + gGL.vertex3fv( v[0].mV ); + + gGL.color4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2); + gGL.texCoord2fv( tc[1].mV ); + gGL.vertex3fv( v[1].mV ); + + gGL.color4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.0f); //LLSelectMgr::sHighlightAlpha); + gGL.texCoord2fv( tc[2].mV ); + gGL.vertex3fv( v[2].mV ); + + gGL.vertex3fv( v[2].mV ); + + gGL.color4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2); + gGL.texCoord2fv( tc[1].mV ); + gGL.vertex3fv( v[1].mV ); + + gGL.texCoord2fv( tc[3].mV ); + gGL.vertex3fv( v[3].mV ); } } gGL.end(); diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index be482b725..9a0dc2e70 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -186,7 +186,6 @@ public: std::vector mTextureScaleRatios; std::vector mSilhouetteVertices; // array of vertices to render silhouette of object std::vector mSilhouetteNormals; // array of normals to render silhouette of object - std::vector mSilhouetteSegments; // array of normals to render silhouette of object BOOL mSilhouetteExists; // need to generate silhouette? protected: diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 3aba0cffb..24ffeb17a 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -421,6 +421,29 @@ public: addText(xpos, ypos, "Shaders Disabled"); ypos += y_inc; } + + if (gGLManager.mHasATIMemInfo) + { + S32 meminfo[4]; + glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, meminfo); + + addText(xpos, ypos, llformat("%.2f MB Texture Memory Free", meminfo[0]/1024.f)); + ypos += y_inc; + + if (gGLManager.mHasVertexBufferObject) + { + glGetIntegerv(GL_VBO_FREE_MEMORY_ATI, meminfo); + addText(xpos, ypos, llformat("%.2f MB VBO Memory Free", meminfo[0]/1024.f)); + ypos += y_inc; + } + } + else if (gGLManager.mHasNVXMemInfo) + { + S32 free_memory; + glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &free_memory); + addText(xpos, ypos, llformat("%.2f MB Video Memory Free", free_memory/1024.f)); + ypos += y_inc; + } addText(xpos, ypos, llformat("%d MB Vertex Data", LLVertexBuffer::sAllocatedBytes/(1024*1024))); ypos += y_inc; @@ -469,6 +492,12 @@ public: ypos += y_inc; + if (!LLSpatialGroup::sPendingQueries.empty()) + { + addText(xpos,ypos, llformat("%d Queries pending", LLSpatialGroup::sPendingQueries.size())); + ypos += y_inc; + } + addText(xpos,ypos, llformat("%d Avatars visible", LLVOAvatar::sNumVisibleAvatars)); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 2139a5890..8a1de024b 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -2440,27 +2440,29 @@ void LLVOAvatar::updateMeshData() bool terse_update = false; + facep->setGeomIndex(0); + facep->setIndicesIndex(0); + + LLVertexBuffer* buff = facep->getVertexBuffer(); if(!facep->getVertexBuffer()) { - LLVertexBuffer *buff = new LLVertexBufferAvatar(); + buff = new LLVertexBufferAvatar(); buff->allocateBuffer(num_vertices, num_indices, TRUE); facep->setVertexBuffer(buff); } else { - if (facep->getVertexBuffer()->getRequestedIndices() == num_indices && - facep->getVertexBuffer()->getRequestedVerts() == num_vertices) + if (buff->getRequestedIndices() == num_indices && + buff->getRequestedVerts() == num_vertices) { terse_update = true; } else { - facep->getVertexBuffer()->resizeBuffer(num_vertices, num_indices) ; + buff->resizeBuffer(num_vertices, num_indices); } } - - facep->setGeomIndex(0); - facep->setIndicesIndex(0); + // This is a hack! Avatars have their own pool, so we are detecting // the case of more than one avatar in the pool (thus > 0 instead of >= 0) @@ -2475,7 +2477,7 @@ void LLVOAvatar::updateMeshData() } stop_glerror(); - facep->getVertexBuffer()->setBuffer(0); + buff->setBuffer(0); if(!f_num) { @@ -3237,7 +3239,7 @@ void LLVOAvatar::idleUpdateLoadingEffect() particle_parameters.mPartImageID = cloud->getID(); particle_parameters.mMaxAge = 0.f; particle_parameters.mPattern = LLPartSysData::LL_PART_SRC_PATTERN_ANGLE_CONE; - particle_parameters.mInnerAngle = 3.14159f; + particle_parameters.mInnerAngle = F_PI; particle_parameters.mOuterAngle = 0.f; particle_parameters.mBurstRate = 0.02f; particle_parameters.mBurstRadius = 0.0f; @@ -7140,7 +7142,8 @@ void LLVOAvatar::lazyAttach() void LLVOAvatar::resetHUDAttachments() { for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); - iter != mAttachmentPoints.end(); iter++) + iter != mAttachmentPoints.end(); + ++iter) { LLViewerJointAttachment* attachment = iter->second; if (attachment->getIsHUDAttachment()) From 1e92e734d8ac63af3712bde85c68f7db99e911aa Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sat, 23 Jul 2011 03:26:30 -0500 Subject: [PATCH 07/22] Bulk of mesh. Excluded via #if MESH_ENABLED. LLModel still needs updated for strided vbos, Collada SDK needs to be wrangled, and misc pieces need to be found and brought over. Skipping inventory stuff until meshes are at least displayable. --- indra/llmath/llvolume.cpp | 526 ++- indra/llmath/llvolume.h | 42 +- indra/llmath/xform.h | 3 + indra/llprimitive/CMakeLists.txt | 2 + indra/llprimitive/llmodel.cpp | 2318 +++++++++++ indra/llprimitive/llmodel.h | 257 ++ indra/newview/llagentcamera.cpp | 4 +- indra/newview/llappviewer.cpp | 16 + indra/newview/lldrawable.h | 6 + indra/newview/lldrawpoolavatar.cpp | 1004 ++++- indra/newview/lldrawpoolavatar.h | 108 + indra/newview/llface.cpp | 144 +- indra/newview/llface.h | 14 + indra/newview/llmanipscale.cpp | 34 +- indra/newview/llmanipscale.h | 3 + indra/newview/llmeshrepository.cpp | 3870 +++++++++++++++++++ indra/newview/llmeshrepository.h | 552 +++ indra/newview/llphysicsshapebuilderutil.cpp | 210 + indra/newview/llphysicsshapebuilderutil.h | 138 + indra/newview/llselectmgr.cpp | 114 + indra/newview/llselectmgr.h | 3 + indra/newview/llviewerobject.cpp | 23 +- indra/newview/llviewerregion.cpp | 5 + indra/newview/llviewerwindow.cpp | 21 + indra/newview/llvoavatar.cpp | 68 +- indra/newview/llvoavatar.h | 9 + indra/newview/llvovolume.cpp | 668 +++- indra/newview/llvovolume.h | 57 +- indra/newview/pipeline.cpp | 6 + 29 files changed, 10133 insertions(+), 92 deletions(-) create mode 100644 indra/llprimitive/llmodel.cpp create mode 100644 indra/llprimitive/llmodel.h create mode 100644 indra/newview/llmeshrepository.cpp create mode 100644 indra/newview/llmeshrepository.h create mode 100644 indra/newview/llphysicsshapebuilderutil.cpp create mode 100644 indra/newview/llphysicsshapebuilderutil.h diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 9957aa7c2..a3636a868 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -2079,6 +2079,14 @@ LLVolume::LLVolume(const LLVolumeParams ¶ms, const F32 detail, const BOOL ge mFaceMask = 0x0; mDetail = detail; mSculptLevel = -2; +#if MESH_ENABLED + mIsTetrahedron = FALSE; + mLODScaleBias.setVec(1,1,1); + mHullPoints = NULL; + mHullIndices = NULL; + mNumHullPoints = 0; + mNumHullIndices = 0; +#endif //MESH_ENABLED // set defaults if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE) @@ -2386,9 +2394,461 @@ bool LLVolumeFace::VertexData::compareNormal(const LLVolumeFace::VertexData& rhs return retval; } +#if MESH_ENABLED +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) + { //no faces unpacked, treat as failed decode + llwarns << "found no faces!" << llendl; + return false; + } + + mVolumeFaces.resize(face_count); + + for (U32 i = 0; i < face_count; ++i) + { + LLVolumeFace& face = mVolumeFaces[i]; + + if (mdl[i].has("NoGeometry")) + { //face has no geometry, continue + face.resizeIndices(3); + face.resizeVertices(1); + memset(face.mPositions, 0, sizeof(LLVector4a)); + memset(face.mNormals, 0, sizeof(LLVector4a)); + memset(face.mTexCoords, 0, sizeof(LLVector2)); + memset(face.mIndices, 0, sizeof(U16)*3); + continue; + } + + LLSD::Binary pos = mdl[i]["Position"]; + LLSD::Binary norm = mdl[i]["Normal"]; + LLSD::Binary tc = mdl[i]["TexCoord0"]; + LLSD::Binary idx = mdl[i]["TriangleList"]; + + + + //copy out indices + face.resizeIndices(idx.size()/2); + + if (idx.empty() || face.mNumIndices < 3) + { //why is there an empty index list? + llwarns <<"Empty face present!" << llendl; + continue; + } + + U16* indices = (U16*) &(idx[0]); + U32 count = idx.size()/2; + for (U32 j = 0; j < count; ++j) + { + face.mIndices[j] = indices[j]; + } + + //copy out vertices + U32 num_verts = pos.size()/(3*2); + face.resizeVertices(num_verts); + + 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_range2 = max_tc - min_tc; + LLVector4a tc_range; + tc_range.set(tc_range2[0], tc_range2[1], tc_range2[0], tc_range2[1]); + LLVector4a min_tc4(min_tc[0], min_tc[1], min_tc[0], min_tc[1]); + + LLVector4a* pos_out = face.mPositions; + LLVector4a* norm_out = face.mNormals; + LLVector4a* tc_out = (LLVector4a*) face.mTexCoords; + + { + U16* v = (U16*) &(pos[0]); + for (U32 j = 0; j < num_verts; ++j) + { + 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++; + v += 3; + } + + } + + { + U16* n = (U16*) &(norm[0]); + if(n) + { + for (U32 j = 0; j < num_verts; ++j) + { + 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++; + n += 3; + } + } + } + + { + U16* t = (U16*) &(tc[0]); + if(t) + { + for (U32 j = 0; j < num_verts; j+=2) + { + if (j < num_verts-1) + { + tc_out->set((F32) t[0], (F32) t[1], (F32) t[2], (F32) t[3]); + } + else + { + tc_out->set((F32) t[0], (F32) t[1], 0.f, 0.f); + } + + t += 4; + + tc_out->div(65535.f); + tc_out->mul(tc_range); + tc_out->add(min_tc4); + + tc_out++; + } + } + } + + 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; + } + + } + + // 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]; + + if (face.mNumVertices < 3) + { //empty face, use a dummy 1cm (at 1m scale) bounding box + min.splat(-0.005f); + max.splat(0.005f); + } + else + { + 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(); + } +} +#endif //MESH_ENABLED S32 LLVolume::getNumFaces() const { +#if MESH_ENABLED + U8 sculpt_type = (mParams.getSculptType() & LL_SCULPT_TYPE_MASK); + + if (sculpt_type == LL_SCULPT_TYPE_MESH) + { + return LL_SCULPT_MESH_MAX_FACES; + } +#endif //MESH_ENABLED return (S32)mProfilep->mFaces.size(); } @@ -2854,6 +3314,13 @@ bool LLVolumeParams::isSculpt() const return mSculptID.notNull(); } +#if MESH_ENABLED +bool LLVolumeParams::isMeshSculpt() const +{ + return isSculpt() && ((mSculptType & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH); +} +#endif //MESH_ENABLED + bool LLVolumeParams::operator==(const LLVolumeParams ¶ms) const { return ( (getPathParams() == params.getPathParams()) && @@ -3999,6 +4466,13 @@ void LLVolume::generateSilhouetteVertices(std::vector &vertices, vertices.clear(); normals.clear(); +#if MESH_ENABLED + if ((mParams.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH) + { + return; + } +#endif //MESH_ENABLED + S32 cur_index = 0; //for each face for (face_list_t::iterator iter = mVolumeFaces.begin(); @@ -4191,7 +4665,6 @@ void LLVolume::generateSilhouetteVertices(std::vector &vertices, norm_mat.rotate(n[v2], t); t.normalize3fast(); normals.push_back(LLVector3(t[0], t[1], t[2])); - } } } @@ -5072,6 +5545,9 @@ LLVolumeFace::LLVolumeFace(const LLVolumeFace& src) mBinormals(NULL), mTexCoords(NULL), mIndices(NULL), +#if MESH_ENABLED + mWeights(NULL), +#endif //MESH_ENABLED mOctree(NULL) { mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3); @@ -5128,6 +5604,18 @@ LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src) mBinormals = NULL; } +#if MESH_ENABLED + if (src.mWeights) + { + allocateWeights(src.mNumVertices); + LLVector4a::memcpyNonAliased16((F32*) mWeights, (F32*) src.mWeights, vert_size); + } + else + { + ll_aligned_free_16(mWeights); + mWeights = NULL; + } +#endif //MESH_ENABLED } if (mNumIndices) { @@ -5160,6 +5648,10 @@ void LLVolumeFace::freeData() mIndices = NULL; ll_aligned_free_16(mBinormals); mBinormals = NULL; +#if MESH_ENABLED + ll_aligned_free_16(mWeights); + mWeights = NULL; +#endif //MESH_ENABLED delete mOctree; mOctree = NULL; @@ -5685,6 +6177,13 @@ void LLVolumeFace::cacheOptimize() S32 size = ((num_verts*sizeof(LLVector2)) + 0xF) & ~0xF; LLVector2* tc = (LLVector2*) ll_aligned_malloc_16(size); +#if MESH_ENABLED + LLVector4a* wght = NULL; + if (mWeights) + { + wght = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts); + } +#endif //MESH_ENABLED LLVector4a* binorm = NULL; if (mBinormals) @@ -5708,6 +6207,12 @@ void LLVolumeFace::cacheOptimize() pos[cur_idx] = mPositions[idx]; norm[cur_idx] = mNormals[idx]; tc[cur_idx] = mTexCoords[idx]; +#if MESH_ENABLED + if (mWeights) + { + wght[cur_idx] = mWeights[idx]; + } +#endif //MESH_ENABLED if (mBinormals) { binorm[cur_idx] = mBinormals[idx]; @@ -5725,11 +6230,17 @@ void LLVolumeFace::cacheOptimize() ll_aligned_free_16(mPositions); ll_aligned_free_16(mNormals); ll_aligned_free_16(mTexCoords); +#if MESH_ENABLED + ll_aligned_free_16(mWeights); +#endif //MESH_ENABLED ll_aligned_free_16(mBinormals); mPositions = pos; mNormals = norm; mTexCoords = tc; +#if MESH_ENABLED + mWeights = wght; +#endif //MESH_ENABLED mBinormals = binorm; //std::string result = llformat("ACMR pre/post: %.3f/%.3f -- %d triangles %d breaks", pre_acmr, post_acmr, mNumIndices/3, breaks); @@ -6512,6 +7023,14 @@ void LLVolumeFace::allocateBinormals(S32 num_verts) mBinormals = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts); } +#if MESH_ENABLED +void LLVolumeFace::allocateWeights(S32 num_verts) +{ + ll_aligned_free_16(mWeights); + mWeights = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts); +} +#endif //MESH_ENABLED + void LLVolumeFace::resizeIndices(S32 num_indices) { ll_aligned_free_16(mIndices); @@ -6673,8 +7192,9 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build) { resizeVertices(num_vertices); resizeIndices(num_indices); - - //if ((volume->getParams().getSculptType() & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_MESH) +#if MESH_ENABLED + if ((volume->getParams().getSculptType() & LL_SCULPT_TYPE_MASK) != LL_SCULPT_TYPE_MESH) +#endif //MESH_ENABLED { mEdge.resize(num_indices); } diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index ba3900ebc..a4f9f34b7 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -191,12 +191,21 @@ const U8 LL_SCULPT_TYPE_SPHERE = 1; const U8 LL_SCULPT_TYPE_TORUS = 2; const U8 LL_SCULPT_TYPE_PLANE = 3; const U8 LL_SCULPT_TYPE_CYLINDER = 4; - +#if MESH_ENABLED +const U8 LL_SCULPT_TYPE_MESH = 5; +const U8 LL_SCULPT_TYPE_MASK = LL_SCULPT_TYPE_SPHERE | LL_SCULPT_TYPE_TORUS | LL_SCULPT_TYPE_PLANE | + LL_SCULPT_TYPE_CYLINDER | LL_SCULPT_TYPE_MESH; +#endif //MESH_ENABLED +#if !MESH_ENABLED const U8 LL_SCULPT_TYPE_MASK = LL_SCULPT_TYPE_SPHERE | LL_SCULPT_TYPE_TORUS | LL_SCULPT_TYPE_PLANE | LL_SCULPT_TYPE_CYLINDER; +#endif //!MESH_ENABLED const U8 LL_SCULPT_FLAG_INVERT = 64; const U8 LL_SCULPT_FLAG_MIRROR = 128; +#if MESH_ENABLED +const S32 LL_SCULPT_MESH_MAX_FACES = 8; +#endif //MESH_ENABLED class LLProfileParams { @@ -646,6 +655,9 @@ public: const LLUUID& getSculptID() const { return mSculptID; } const U8& getSculptType() const { return mSculptType; } bool isSculpt() const; + #if MESH_ENABLED + bool isMeshSculpt() const; + #endif //MESH_ENABLED BOOL isConvex() const; // 'begin' and 'end' should be in range [0, 1] (they will be clamped) @@ -853,6 +865,9 @@ public: void resizeVertices(S32 num_verts); void allocateBinormals(S32 num_verts); + #if MESH_ENABLED + void allocateWeights(S32 num_verts); + #endif //MESH_ENABLED void resizeIndices(S32 num_indices); void fillFromLegacyData(std::vector& v, std::vector& idx); @@ -923,6 +938,14 @@ public: U16* mIndices; std::vector mEdge; + + #if MESH_ENABLED + //list of skin weights for rigged volumes + // format is mWeights[vertex_index].mV[influence] = . + // mWeights.size() should be empty or match mVertices.size() + LLVector4a* mWeights; + #endif //MESH_ENABLED + LLOctreeNode* mOctree; private: @@ -1053,11 +1076,20 @@ private: protected: BOOL generate(); void createVolumeFaces(); +#if MESH_ENABLED +public: + virtual bool unpackVolumeFaces(std::istream& is, S32 size); + virtual void makeTetrahedron(); + virtual BOOL isTetrahedron(); +#endif //MESH_ENABLED protected: BOOL mUnique; F32 mDetail; S32 mSculptLevel; +#if MESH_ENABLED + BOOL mIsTetrahedron; +#endif //MESH_ENABLED LLVolumeParams mParams; LLPath *mPathp; @@ -1067,6 +1099,14 @@ protected: BOOL mGenerateSingleFace; typedef std::vector face_list_t; face_list_t mVolumeFaces; + +#if MESH_ENABLED +public: + LLVector4a* mHullPoints; + U16* mHullIndices; + S32 mNumHullPoints; + S32 mNumHullIndices; +#endif //MESH_ENABLED }; std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params); diff --git a/indra/llmath/xform.h b/indra/llmath/xform.h index 05ee8b9ce..70b2a8375 100644 --- a/indra/llmath/xform.h +++ b/indra/llmath/xform.h @@ -39,6 +39,9 @@ const F32 MAX_OBJECT_Z = 4096.f; // should match REGION_HEIGHT_METERS, Pre-havok4: 768.f const F32 MIN_OBJECT_Z = -256.f; const F32 DEFAULT_MAX_PRIM_SCALE = 256.f; +#if MESH_ENABLED +const F32 DEFAULT_MAX_PRIM_SCALE_NO_MESH = DEFAULT_MAX_PRIM_SCALE; +#endif //MESH_ENABLED const F32 MIN_PRIM_SCALE = 0.01f; const F32 MAX_PRIM_SCALE = 65536.f; // something very high but not near FLT_MAX diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt index 572bc4328..88a9ef11d 100644 --- a/indra/llprimitive/CMakeLists.txt +++ b/indra/llprimitive/CMakeLists.txt @@ -17,6 +17,7 @@ include_directories( set(llprimitive_SOURCE_FILES llmaterialtable.cpp + #llmodel.cpp llprimitive.cpp llprimtexturelist.cpp lltextureanim.cpp @@ -32,6 +33,7 @@ set(llprimitive_HEADER_FILES legacy_object_types.h llmaterialtable.h + #llmodel.h llprimitive.h llprimtexturelist.h lltextureanim.h diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp new file mode 100644 index 000000000..0463d5364 --- /dev/null +++ b/indra/llprimitive/llmodel.cpp @@ -0,0 +1,2318 @@ +/** + * @file llmodel.cpp + * @brief Model handling implementation + * + * $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 "linden_common.h" + +#include "llmodel.h" +#include "llconvexdecomposition.h" +#include "llsdserialize.h" +#include "llvector4a.h" + +#include "dae.h" +#include "dae/daeErrorHandler.h" +#include "dom/domConstants.h" +#include "dom/domMesh.h" + +#ifdef LL_STANDALONE +# include +#else +# include "zlib/zlib.h" +#endif + + + +std::string model_names[] = +{ + "lowest_lod", + "low_lod", + "medium_lod", + "high_lod", + "physics_mesh" +}; + +const int MODEL_NAMES_LENGTH = sizeof(model_names) / sizeof(std::string); + +LLModel::LLModel(LLVolumeParams& params, F32 detail) + : LLVolume(params, detail), mNormalizedScale(1,1,1), mNormalizedTranslation(0,0,0) + , mPelvisOffset( 0.0f ), mStatus(NO_ERRORS) +{ + mDecompID = -1; + mLocalID = -1; +} + +LLModel::~LLModel() +{ + if (mDecompID >= 0) + { + LLConvexDecomposition::getInstance()->deleteDecomposition(mDecompID); + } +} + +void load_face_from_dom_inputs(LLVolumeFace& face, const domInputLocalOffset_Array& inputs, U32 min_idx, U32 max_idx) +{ + for (U32 j = 0; j < inputs.getCount(); ++j) + { + if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[j]->getSemantic()) == 0) + { //found vertex array + const domURIFragmentType& uri = inputs[j]->getSource(); + daeElementRef elem = uri.getElement(); + domVertices* vertices = (domVertices*) elem.cast(); + + domInputLocal_Array& v_inp = vertices->getInput_array(); + if (inputs[j]->getOffset() != 0) + { + llerrs << "Vertex array offset MUST be zero." << llendl; + } + + for (U32 k = 0; k < v_inp.getCount(); ++k) + { + if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0) + { + const domURIFragmentType& uri = v_inp[k]->getSource(); + + daeElementRef elem = uri.getElement(); + domSource* src = (domSource*) elem.cast(); + + if (src->getTechnique_common()->getAccessor()->getStride() != 3) + { + llerrs << "Vertex array stride MUST be three." << llendl; + } + + domListOfFloats& v = src->getFloat_array()->getValue(); + + LLVector4a min; + min.set(v[min_idx], v[min_idx+1], v[min_idx+2]); + LLVector4a max = min; + + for (U32 j = min_idx; j <= max_idx; ++j) + { //copy vertex array + face.mPositions[j-min_idx].set(v[j*3+0], v[j*3+1], v[j*3+2]); + update_min_max(min, max, face.mPositions[j-min_idx]); + } + + face.mExtents[0] = min; + face.mExtents[1] = max; + } + } + } + + if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[j]->getSemantic()) == 0) + { + //found normal array for this triangle list + const domURIFragmentType& uri = inputs[j]->getSource(); + daeElementRef elem = uri.getElement(); + domSource* src = (domSource*) elem.cast(); + domListOfFloats& n = src->getFloat_array()->getValue(); + + for (U32 j = min_idx; j <= max_idx; ++j) + { + LLVector4a* norm = (LLVector4a*) face.mNormals + (j-min_idx); + norm->set(n[j*3+0], n[j*3+1], n[j*3+2]); + norm->normalize3(); + } + } + else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[j]->getSemantic()) == 0) + { //found texCoords + const domURIFragmentType& uri = inputs[j]->getSource(); + daeElementRef elem = uri.getElement(); + domSource* src = (domSource*) elem.cast(); + domListOfFloats& u = src->getFloat_array()->getValue(); + + for (U32 j = min_idx; j <= max_idx; ++j) + { + face.mTexCoords[j-min_idx].setVec(u[j*2+0], u[j*2+1]); + } + } + } +} + +bool get_dom_sources(const domInputLocalOffset_Array& inputs, S32& pos_offset, S32& tc_offset, S32& norm_offset, S32 &idx_stride, + domSource* &pos_source, domSource* &tc_source, domSource* &norm_source) +{ + + idx_stride = 0; + + for (U32 j = 0; j < inputs.getCount(); ++j) + { + idx_stride = llmax((S32) inputs[j]->getOffset(), idx_stride); + + if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[j]->getSemantic()) == 0) + { //found vertex array + const domURIFragmentType& uri = inputs[j]->getSource(); + daeElementRef elem = uri.getElement(); + domVertices* vertices = (domVertices*) elem.cast(); + if ( !vertices ) + { + return false; + } + + domInputLocal_Array& v_inp = vertices->getInput_array(); + + + for (U32 k = 0; k < v_inp.getCount(); ++k) + { + if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0) + { + pos_offset = inputs[j]->getOffset(); + + const domURIFragmentType& uri = v_inp[k]->getSource(); + daeElementRef elem = uri.getElement(); + pos_source = (domSource*) elem.cast(); + } + + if (strcmp(COMMON_PROFILE_INPUT_NORMAL, v_inp[k]->getSemantic()) == 0) + { + norm_offset = inputs[j]->getOffset(); + + const domURIFragmentType& uri = v_inp[k]->getSource(); + daeElementRef elem = uri.getElement(); + norm_source = (domSource*) elem.cast(); + } + } + } + + if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[j]->getSemantic()) == 0) + { + //found normal array for this triangle list + norm_offset = inputs[j]->getOffset(); + const domURIFragmentType& uri = inputs[j]->getSource(); + daeElementRef elem = uri.getElement(); + norm_source = (domSource*) elem.cast(); + } + else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[j]->getSemantic()) == 0) + { //found texCoords + tc_offset = inputs[j]->getOffset(); + const domURIFragmentType& uri = inputs[j]->getSource(); + daeElementRef elem = uri.getElement(); + tc_source = (domSource*) elem.cast(); + } + } + + idx_stride += 1; + + return true; +} + +LLModel::EModelStatus load_face_from_dom_triangles(std::vector& face_list, std::vector& materials, domTrianglesRef& tri) +{ + LLVolumeFace face; + std::vector verts; + std::vector indices; + + const domInputLocalOffset_Array& inputs = tri->getInput_array(); + + S32 pos_offset = -1; + S32 tc_offset = -1; + S32 norm_offset = -1; + + domSource* pos_source = NULL; + domSource* tc_source = NULL; + domSource* norm_source = NULL; + + S32 idx_stride = 0; + + if ( !get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source) || !pos_source ) + { + return LLModel::BAD_ELEMENT; + } + + + domPRef p = tri->getP(); + domListOfUInts& idx = p->getValue(); + + domListOfFloats dummy ; + domListOfFloats& v = pos_source ? pos_source->getFloat_array()->getValue() : dummy ; + domListOfFloats& tc = tc_source ? tc_source->getFloat_array()->getValue() : dummy ; + domListOfFloats& n = norm_source ? norm_source->getFloat_array()->getValue() : dummy ; + + if (pos_source) + { + face.mExtents[0].set(v[0], v[1], v[2]); + face.mExtents[1].set(v[0], v[1], v[2]); + } + + LLVolumeFace::VertexMapData::PointMap point_map; + + for (U32 i = 0; i < idx.getCount(); i += idx_stride) + { + LLVolumeFace::VertexData cv; + if (pos_source) + { + cv.setPosition(LLVector4a(v[idx[i+pos_offset]*3+0], + v[idx[i+pos_offset]*3+1], + v[idx[i+pos_offset]*3+2])); + } + + if (tc_source) + { + cv.mTexCoord.setVec(tc[idx[i+tc_offset]*2+0], + tc[idx[i+tc_offset]*2+1]); + } + + if (norm_source) + { + cv.setNormal(LLVector4a(n[idx[i+norm_offset]*3+0], + n[idx[i+norm_offset]*3+1], + n[idx[i+norm_offset]*3+2])); + } + + + BOOL found = FALSE; + + LLVolumeFace::VertexMapData::PointMap::iterator point_iter; + point_iter = point_map.find(LLVector3(cv.getPosition().getF32ptr())); + + if (point_iter != point_map.end()) + { + for (U32 j = 0; j < point_iter->second.size(); ++j) + { + if ((point_iter->second)[j] == cv) + { + found = TRUE; + indices.push_back((point_iter->second)[j].mIndex); + break; + } + } + } + + if (!found) + { + update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition()); + verts.push_back(cv); + if (verts.size() >= 65535) + { + //llerrs << "Attempted to write model exceeding 16-bit index buffer limitation." << llendl; + return LLModel::VERTEX_NUMBER_OVERFLOW ; + } + U16 index = (U16) (verts.size()-1); + indices.push_back(index); + + LLVolumeFace::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); + } + } + + if (indices.size()%3 == 0 && verts.size() >= 65532) + { + face_list.push_back(face); + face_list.rbegin()->fillFromLegacyData(verts, indices); + face = LLVolumeFace(); + point_map.clear(); + } + + } + + if (!verts.empty()) + { + std::string material; + + if (tri->getMaterial()) + { + material = std::string(tri->getMaterial()); + } + + materials.push_back(material); + face_list.push_back(face); + + face_list.rbegin()->fillFromLegacyData(verts, indices); + } + + return LLModel::NO_ERRORS ; +} + +LLModel::EModelStatus load_face_from_dom_polylist(std::vector& face_list, std::vector& materials, domPolylistRef& poly) +{ + domPRef p = poly->getP(); + domListOfUInts& idx = p->getValue(); + + if (idx.getCount() == 0) + { + return LLModel::NO_ERRORS ; + } + + const domInputLocalOffset_Array& inputs = poly->getInput_array(); + + + domListOfUInts& vcount = poly->getVcount()->getValue(); + + S32 pos_offset = -1; + S32 tc_offset = -1; + S32 norm_offset = -1; + + domSource* pos_source = NULL; + domSource* tc_source = NULL; + domSource* norm_source = NULL; + + S32 idx_stride = 0; + + if (!get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source)) + { + return LLModel::BAD_ELEMENT; + } + + LLVolumeFace face; + + std::vector indices; + std::vector verts; + + domListOfFloats v; + domListOfFloats tc; + domListOfFloats n; + + if (pos_source) + { + v = pos_source->getFloat_array()->getValue(); + face.mExtents[0].set(v[0], v[1], v[2]); + face.mExtents[1].set(v[0], v[1], v[2]); + } + + if (tc_source) + { + tc = tc_source->getFloat_array()->getValue(); + } + + if (norm_source) + { + n = norm_source->getFloat_array()->getValue(); + } + + LLVolumeFace::VertexMapData::PointMap point_map; + + U32 cur_idx = 0; + for (U32 i = 0; i < vcount.getCount(); ++i) + { //for each polygon + U32 first_index = 0; + U32 last_index = 0; + for (U32 j = 0; j < vcount[i]; ++j) + { //for each vertex + + LLVolumeFace::VertexData cv; + + if (pos_source) + { + cv.getPosition().set(v[idx[cur_idx+pos_offset]*3+0], + v[idx[cur_idx+pos_offset]*3+1], + v[idx[cur_idx+pos_offset]*3+2]); + } + + if (tc_source) + { + cv.mTexCoord.setVec(tc[idx[cur_idx+tc_offset]*2+0], + tc[idx[cur_idx+tc_offset]*2+1]); + } + + if (norm_source) + { + cv.getNormal().set(n[idx[cur_idx+norm_offset]*3+0], + n[idx[cur_idx+norm_offset]*3+1], + n[idx[cur_idx+norm_offset]*3+2]); + } + + cur_idx += idx_stride; + + BOOL found = FALSE; + + LLVolumeFace::VertexMapData::PointMap::iterator point_iter; + LLVector3 pos3(cv.getPosition().getF32ptr()); + point_iter = point_map.find(pos3); + + if (point_iter != point_map.end()) + { + for (U32 k = 0; k < point_iter->second.size(); ++k) + { + if ((point_iter->second)[k] == cv) + { + found = TRUE; + U32 index = (point_iter->second)[k].mIndex; + if (j == 0) + { + first_index = index; + } + else if (j == 1) + { + last_index = index; + } + else + { + indices.push_back(first_index); + indices.push_back(last_index); + indices.push_back(index); + last_index = index; + } + + break; + } + } + } + + if (!found) + { + update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition()); + verts.push_back(cv); + if (verts.size() >= 65535) + { + //llerrs << "Attempted to write model exceeding 16-bit index buffer limitation." << llendl; + return LLModel::VERTEX_NUMBER_OVERFLOW ; + } + U16 index = (U16) (verts.size()-1); + + if (j == 0) + { + first_index = index; + } + else if (j == 1) + { + last_index = index; + } + else + { + indices.push_back(first_index); + indices.push_back(last_index); + indices.push_back(index); + last_index = index; + } + + LLVolumeFace::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[pos3].push_back(d); + } + } + + if (indices.size()%3 == 0 && indices.size() >= 65532) + { + face_list.push_back(face); + face_list.rbegin()->fillFromLegacyData(verts, indices); + face = LLVolumeFace(); + verts.clear(); + indices.clear(); + point_map.clear(); + } + } + } + + if (!verts.empty()) + { + std::string material; + + if (poly->getMaterial()) + { + material = std::string(poly->getMaterial()); + } + + materials.push_back(material); + face_list.push_back(face); + face_list.rbegin()->fillFromLegacyData(verts, indices); + } + + return LLModel::NO_ERRORS ; +} + +LLModel::EModelStatus load_face_from_dom_polygons(std::vector& face_list, std::vector& materials, domPolygonsRef& poly) +{ + LLVolumeFace face; + std::vector indices; + std::vector verts; + + const domInputLocalOffset_Array& inputs = poly->getInput_array(); + + + S32 v_offset = -1; + S32 n_offset = -1; + S32 t_offset = -1; + + domListOfFloats* v = NULL; + domListOfFloats* n = NULL; + domListOfFloats* t = NULL; + + U32 stride = 0; + for (U32 i = 0; i < inputs.getCount(); ++i) + { + stride = llmax((U32) inputs[i]->getOffset()+1, stride); + + if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[i]->getSemantic()) == 0) + { //found vertex array + v_offset = inputs[i]->getOffset(); + + const domURIFragmentType& uri = inputs[i]->getSource(); + daeElementRef elem = uri.getElement(); + domVertices* vertices = (domVertices*) elem.cast(); + if (!vertices) + { + return LLModel::BAD_ELEMENT; + } + domInputLocal_Array& v_inp = vertices->getInput_array(); + + for (U32 k = 0; k < v_inp.getCount(); ++k) + { + if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0) + { + const domURIFragmentType& uri = v_inp[k]->getSource(); + daeElementRef elem = uri.getElement(); + domSource* src = (domSource*) elem.cast(); + if (!src) + { + return LLModel::BAD_ELEMENT; + } + v = &(src->getFloat_array()->getValue()); + } + } + } + else if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[i]->getSemantic()) == 0) + { + n_offset = inputs[i]->getOffset(); + //found normal array for this triangle list + const domURIFragmentType& uri = inputs[i]->getSource(); + daeElementRef elem = uri.getElement(); + domSource* src = (domSource*) elem.cast(); + if (!src) + { + return LLModel::BAD_ELEMENT; + } + n = &(src->getFloat_array()->getValue()); + } + else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[i]->getSemantic()) == 0 && inputs[i]->getSet() == 0) + { //found texCoords + t_offset = inputs[i]->getOffset(); + const domURIFragmentType& uri = inputs[i]->getSource(); + daeElementRef elem = uri.getElement(); + domSource* src = (domSource*) elem.cast(); + if (!src) + { + return LLModel::BAD_ELEMENT; + } + t = &(src->getFloat_array()->getValue()); + } + } + + domP_Array& ps = poly->getP_array(); + + //make a triangle list in + for (U32 i = 0; i < ps.getCount(); ++i) + { //for each polygon + domListOfUInts& idx = ps[i]->getValue(); + for (U32 j = 0; j < idx.getCount()/stride; ++j) + { //for each vertex + if (j > 2) + { + U32 size = verts.size(); + LLVolumeFace::VertexData v0 = verts[size-3]; + LLVolumeFace::VertexData v1 = verts[size-1]; + + verts.push_back(v0); + verts.push_back(v1); + } + + LLVolumeFace::VertexData vert; + + + if (v) + { + U32 v_idx = idx[j*stride+v_offset]*3; + vert.getPosition().set(v->get(v_idx), + v->get(v_idx+1), + v->get(v_idx+2)); + } + + if (n) + { + U32 n_idx = idx[j*stride+n_offset]*3; + vert.getNormal().set(n->get(n_idx), + n->get(n_idx+1), + n->get(n_idx+2)); + } + + if (t) + { + U32 t_idx = idx[j*stride+t_offset]*2; + vert.mTexCoord.setVec(t->get(t_idx), + t->get(t_idx+1)); + } + + + verts.push_back(vert); + } + } + + if (verts.empty()) + { + return LLModel::NO_ERRORS; + } + + face.mExtents[0] = verts[0].getPosition(); + face.mExtents[1] = verts[0].getPosition(); + + //create a map of unique vertices to indices + std::map vert_idx; + + U32 cur_idx = 0; + for (U32 i = 0; i < verts.size(); ++i) + { + std::map::iterator iter = vert_idx.find(verts[i]); + if (iter == vert_idx.end()) + { + vert_idx[verts[i]] = cur_idx++; + } + } + + //build vertex array from map + std::vector new_verts; + new_verts.resize(vert_idx.size()); + + for (std::map::iterator iter = vert_idx.begin(); iter != vert_idx.end(); ++iter) + { + new_verts[iter->second] = iter->first; + update_min_max(face.mExtents[0], face.mExtents[1], iter->first.getPosition()); + } + + //build index array from map + indices.resize(verts.size()); + + for (U32 i = 0; i < verts.size(); ++i) + { + indices[i] = vert_idx[verts[i]]; + } + + // DEBUG just build an expanded triangle list + /*for (U32 i = 0; i < verts.size(); ++i) + { + indices.push_back((U16) i); + update_min_max(face.mExtents[0], face.mExtents[1], verts[i].getPosition()); + }*/ + + if (!new_verts.empty()) + { + std::string material; + + if (poly->getMaterial()) + { + material = std::string(poly->getMaterial()); + } + + materials.push_back(material); + face_list.push_back(face); + face_list.rbegin()->fillFromLegacyData(new_verts, indices); + } + + return LLModel::NO_ERRORS ; +} + +//static +std::string LLModel::getStatusString(U32 status) +{ + const static std::string status_strings[(S32)INVALID_STATUS] = {"status_no_error", "status_vertex_number_overflow","bad_element"}; + + if(status < INVALID_STATUS) + { + if(status_strings[status] == std::string()) + { + llerrs << "No valid status string for this status: " << (U32)status << llendl ; + } + return status_strings[status] ; + } + + llerrs << "Invalid model status: " << (U32)status << llendl ; + + return std::string() ; +} + +void LLModel::addVolumeFacesFromDomMesh(domMesh* mesh) +{ + domTriangles_Array& tris = mesh->getTriangles_array(); + + for (U32 i = 0; i < tris.getCount(); ++i) + { + domTrianglesRef& tri = tris.get(i); + + mStatus = load_face_from_dom_triangles(mVolumeFaces, mMaterialList, tri); + + if(mStatus != NO_ERRORS) + { + mVolumeFaces.clear() ; + mMaterialList.clear() ; + return ; //abort + } + } + + domPolylist_Array& polys = mesh->getPolylist_array(); + for (U32 i = 0; i < polys.getCount(); ++i) + { + domPolylistRef& poly = polys.get(i); + mStatus = load_face_from_dom_polylist(mVolumeFaces, mMaterialList, poly); + + if(mStatus != NO_ERRORS) + { + mVolumeFaces.clear() ; + mMaterialList.clear() ; + return ; //abort + } + } + + domPolygons_Array& polygons = mesh->getPolygons_array(); + + for (U32 i = 0; i < polygons.getCount(); ++i) + { + domPolygonsRef& poly = polygons.get(i); + mStatus = load_face_from_dom_polygons(mVolumeFaces, mMaterialList, poly); + + if(mStatus != NO_ERRORS) + { + mVolumeFaces.clear() ; + mMaterialList.clear() ; + return ; //abort + } + } + +} + +BOOL LLModel::createVolumeFacesFromDomMesh(domMesh* mesh) +{ + if (mesh) + { + mVolumeFaces.clear(); + mMaterialList.clear(); + + addVolumeFacesFromDomMesh(mesh); + + if (getNumVolumeFaces() > 0) + { + optimizeVolumeFaces(); + normalizeVolumeFaces(); + + if (getNumVolumeFaces() > 0) + { + return TRUE; + } + } + } + else + { + llwarns << "no mesh found" << llendl; + } + + return FALSE; +} + +void LLModel::offsetMesh( const LLVector3& pivotPoint ) +{ + LLVector4a pivot( pivotPoint[VX], pivotPoint[VY], pivotPoint[VZ] ); + + for (std::vector::iterator faceIt = mVolumeFaces.begin(); faceIt != mVolumeFaces.end(); ) + { + std::vector:: iterator currentFaceIt = faceIt++; + LLVolumeFace& face = *currentFaceIt; + LLVector4a *pos = (LLVector4a*) face.mPositions; + + for (U32 i=0; i::iterator iter = mVolumeFaces.begin(); iter != mVolumeFaces.end(); ) + { + std::vector::iterator cur_iter = iter++; + LLVolumeFace& face = *cur_iter; + + for (S32 i = 0; i < (S32) face.mNumIndices; i += 3) + { //remove zero area triangles + U16 i0 = face.mIndices[i+0]; + U16 i1 = face.mIndices[i+1]; + U16 i2 = face.mIndices[i+2]; + + if (i0 == i1 || + i1 == i2 || + i0 == i2) + { //duplicate index in triangle, remove triangle + face.mIndices.erase(face.mIndices.begin()+i, face.mIndices.begin()+i+3); + i -= 3; + } + else + { + LLVolumeFace::VertexData& v0 = face.mVertices[i0]; + LLVolumeFace::VertexData& v1 = face.mVertices[i1]; + LLVolumeFace::VertexData& v2 = face.mVertices[i2]; + + if (v0.mPosition == v1.mPosition || + v1.mPosition == v2.mPosition || + v2.mPosition == v0.mPosition) + { //zero area triangle, delete + face.mIndices.erase(face.mIndices.begin()+i, face.mIndices.begin()+i+3); + i-=3; + } + } + } + + //remove unreference vertices + std::vector ref; + ref.resize(face.mNumVertices); + + for (U32 i = 0; i < ref.size(); ++i) + { + ref[i] = false; + } + + for (U32 i = 0; i < face.mNumIndices; ++i) + { + ref[face.mIndices[i]] = true; + } + + U32 unref_count = 0; + for (U32 i = 0; i < ref.size(); ++i) + { + if (!ref[i]) + { + //vertex is unreferenced + face.mVertices.erase(face.mVertices.begin()+(i-unref_count)); + U16 idx = (U16) (i-unref_count); + + for (U32 j = 0; j < face.mNumIndices; ++j) + { //decrement every index array value greater than idx + if (face.mIndices[j] > idx) + { + --face.mIndices[j]; + } + } + ++unref_count; + } + } + + if (face.mVertices.empty() || face.mIndices.empty()) + { //face is empty, remove it + iter = mVolumeFaces.erase(cur_iter); + } + } +#endif +} + +// Shrink the model to fit +// on a 1x1x1 cube centered at the origin. +// The positions and extents +// multiplied by mNormalizedScale +// and offset by mNormalizedTranslation +// to be the "original" extents and position. +// Also, the positions will fit +// within the unit cube. +void LLModel::normalizeVolumeFaces() +{ + + // ensure we don't have too many faces + if (mVolumeFaces.size() > LL_SCULPT_MESH_MAX_FACES) + mVolumeFaces.resize(LL_SCULPT_MESH_MAX_FACES); + + if (!mVolumeFaces.empty()) + { + LLVector4a min, max; + + // For all of the volume faces + // in the model, loop over + // them and see what the extents + // of the volume along each axis. + min = mVolumeFaces[0].mExtents[0]; + max = mVolumeFaces[0].mExtents[1]; + + for (U32 i = 1; i < mVolumeFaces.size(); ++i) + { + LLVolumeFace& face = mVolumeFaces[i]; + + update_min_max(min, max, face.mExtents[0]); + update_min_max(min, max, face.mExtents[1]); + } + + // Now that we have the extents of the model + // we can compute the offset needed to center + // the model at the origin. + + // Compute center of the model + // and make it negative to get translation + // needed to center at origin. + LLVector4a trans; + trans.setAdd(min, max); + trans.mul(-0.5f); + + // Compute the total size along all + // axes of the model. + LLVector4a size; + size.setSub(max, min); + + // Prevent division by zero. + F32 x = size[0]; + F32 y = size[1]; + F32 z = size[2]; + F32 w = size[3]; + if (fabs(x) pos, + LLStrider norm, + LLStrider tc, + LLStrider ind, + U32 num_verts, + U32 num_indices) +{ + LLVolumeFace& face = mVolumeFaces[f]; + + face.resizeVertices(num_verts); + face.resizeIndices(num_indices); + + LLVector4a::memcpyNonAliased16((F32*) face.mPositions, (F32*) pos.get(), num_verts*4*sizeof(F32)); + LLVector4a::memcpyNonAliased16((F32*) face.mNormals, (F32*) norm.get(), num_verts*4*sizeof(F32)); + LLVector4a::memcpyNonAliased16((F32*) face.mTexCoords, (F32*) tc.get(), num_verts*2*sizeof(F32)); + U32 size = (num_indices*2+0xF)&~0xF; + LLVector4a::memcpyNonAliased16((F32*) face.mIndices, (F32*) ind.get(), size); +} + +void LLModel::appendFaces(LLModel *model, LLMatrix4 &transform, LLMatrix4& norm_mat) +{ + if (mVolumeFaces.empty()) + { + setNumVolumeFaces(1); + } + + LLVolumeFace& face = mVolumeFaces[mVolumeFaces.size()-1]; + + + for (S32 i = 0; i < model->getNumFaces(); ++i) + { + face.appendFace(model->getVolumeFace(i), transform, norm_mat); + } + +} + +void LLModel::appendFace(const LLVolumeFace& src_face, std::string src_material, LLMatrix4& mat, LLMatrix4& norm_mat) +{ + S32 rindex = getNumVolumeFaces()-1; + if (rindex == -1 || + mVolumeFaces[rindex].mNumVertices + src_face.mNumVertices >= 65536) + { //empty or overflow will occur, append new face + LLVolumeFace cur_face; + cur_face.appendFace(src_face, mat, norm_mat); + addFace(cur_face); + mMaterialList.push_back(src_material); + } + else + { //append to existing end face + mVolumeFaces.rbegin()->appendFace(src_face, mat, norm_mat); + } +} + +void LLModel::addFace(const LLVolumeFace& face) +{ + if (face.mNumVertices == 0) + { + llerrs << "Cannot add empty face." << llendl; + } + + mVolumeFaces.push_back(face); + + if (mVolumeFaces.size() > MAX_MODEL_FACES) + { + llerrs << "Model prims cannot have more than " << MAX_MODEL_FACES << " faces!" << llendl; + } +} + + +void LLModel::generateNormals(F32 angle_cutoff) +{ + //generate normals for all faces by: + // 1 - Create faceted copy of face with no texture coordinates + // 2 - Weld vertices in faceted copy that are shared between triangles with less than "angle_cutoff" difference between normals + // 3 - Generate smoothed set of normals based on welding results + // 4 - Create faceted copy of face with texture coordinates + // 5 - Copy smoothed normals to faceted copy, using closest normal to triangle normal where more than one normal exists for a given position + // 6 - Remove redundant vertices from new faceted (now smooth) copy + + angle_cutoff = cosf(angle_cutoff); + for (U32 j = 0; j < mVolumeFaces.size(); ++j) + { + LLVolumeFace& vol_face = mVolumeFaces[j]; + + if (vol_face.mNumIndices > 65535) + { + llwarns << "Too many vertices for normal generation to work." << llendl; + continue; + } + + //create faceted copy of current face with no texture coordinates (step 1) + LLVolumeFace faceted; + + LLVector4a* src_pos = (LLVector4a*) vol_face.mPositions; + //LLVector4a* src_norm = (LLVector4a*) vol_face.mNormals; + + + faceted.resizeVertices(vol_face.mNumIndices); + faceted.resizeIndices(vol_face.mNumIndices); + //bake out triangles into temporary face, clearing texture coordinates + for (U32 i = 0; i < vol_face.mNumIndices; ++i) + { + U32 idx = vol_face.mIndices[i]; + + faceted.mPositions[i] = src_pos[idx]; + faceted.mTexCoords[i] = LLVector2(0,0); + faceted.mIndices[i] = i; + } + + //generate normals for temporary face + for (U32 i = 0; i < faceted.mNumIndices; i += 3) + { //for each triangle + U16 i0 = faceted.mIndices[i+0]; + U16 i1 = faceted.mIndices[i+1]; + U16 i2 = faceted.mIndices[i+2]; + + LLVector4a& p0 = faceted.mPositions[i0]; + LLVector4a& p1 = faceted.mPositions[i1]; + LLVector4a& p2 = faceted.mPositions[i2]; + + LLVector4a& n0 = faceted.mNormals[i0]; + LLVector4a& n1 = faceted.mNormals[i1]; + LLVector4a& n2 = faceted.mNormals[i2]; + + LLVector4a lhs, rhs; + lhs.setSub(p1, p0); + rhs.setSub(p2, p0); + + n0.setCross3(lhs, rhs); + n0.normalize3(); + n1 = n0; + n2 = n0; + } + + //weld vertices in temporary face, respecting angle_cutoff (step 2) + faceted.optimize(angle_cutoff); + + //generate normals for welded face based on new topology (step 3) + + for (U32 i = 0; i < faceted.mNumVertices; i++) + { + faceted.mNormals[i].clear(); + } + + for (U32 i = 0; i < faceted.mNumIndices; i += 3) + { //for each triangle + U16 i0 = faceted.mIndices[i+0]; + U16 i1 = faceted.mIndices[i+1]; + U16 i2 = faceted.mIndices[i+2]; + + LLVector4a& p0 = faceted.mPositions[i0]; + LLVector4a& p1 = faceted.mPositions[i1]; + LLVector4a& p2 = faceted.mPositions[i2]; + + LLVector4a& n0 = faceted.mNormals[i0]; + LLVector4a& n1 = faceted.mNormals[i1]; + LLVector4a& n2 = faceted.mNormals[i2]; + + LLVector4a lhs, rhs; + lhs.setSub(p1, p0); + rhs.setSub(p2, p0); + + LLVector4a n; + n.setCross3(lhs, rhs); + + n0.add(n); + n1.add(n); + n2.add(n); + } + + //normalize normals and build point map + LLVolumeFace::VertexMapData::PointMap point_map; + + for (U32 i = 0; i < faceted.mNumVertices; ++i) + { + faceted.mNormals[i].normalize3(); + + LLVolumeFace::VertexMapData v; + v.setPosition(faceted.mPositions[i]); + v.setNormal(faceted.mNormals[i]); + + point_map[LLVector3(v.getPosition().getF32ptr())].push_back(v); + } + + //create faceted copy of current face with texture coordinates (step 4) + LLVolumeFace new_face; + + //bake out triangles into new face + new_face.resizeIndices(vol_face.mNumIndices); + new_face.resizeVertices(vol_face.mNumIndices); + + for (U32 i = 0; i < vol_face.mNumIndices; ++i) + { + U32 idx = vol_face.mIndices[i]; + LLVolumeFace::VertexData v; + new_face.mPositions[i] = vol_face.mPositions[idx]; + new_face.mNormals[i].clear(); + new_face.mTexCoords[i] = vol_face.mTexCoords[idx]; + new_face.mIndices[i] = i; + } + + //generate normals for new face + for (U32 i = 0; i < new_face.mNumIndices; i += 3) + { //for each triangle + U16 i0 = new_face.mIndices[i+0]; + U16 i1 = new_face.mIndices[i+1]; + U16 i2 = new_face.mIndices[i+2]; + + LLVector4a& p0 = new_face.mPositions[i0]; + LLVector4a& p1 = new_face.mPositions[i1]; + LLVector4a& p2 = new_face.mPositions[i2]; + + LLVector4a& n0 = new_face.mNormals[i0]; + LLVector4a& n1 = new_face.mNormals[i1]; + LLVector4a& n2 = new_face.mNormals[i2]; + + LLVector4a lhs, rhs; + lhs.setSub(p1, p0); + rhs.setSub(p2, p0); + + n0.setCross3(lhs, rhs); + n0.normalize3(); + n1 = n0; + n2 = n0; + } + + //swap out normals in new_face with best match from point map (step 5) + for (U32 i = 0; i < new_face.mNumVertices; ++i) + { + //LLVolumeFace::VertexData v = new_face.mVertices[i]; + + LLVector4a ref_norm = new_face.mNormals[i]; + + LLVolumeFace::VertexMapData::PointMap::iterator iter = point_map.find(LLVector3(new_face.mPositions[i].getF32ptr())); + + if (iter != point_map.end()) + { + F32 best = -2.f; + for (U32 k = 0; k < iter->second.size(); ++k) + { + LLVector4a& n = iter->second[k].getNormal(); + + F32 cur = n.dot3(ref_norm).getF32(); + + if (cur > best) + { + best = cur; + new_face.mNormals[i] = n; + } + } + } + } + + //remove redundant vertices from new face (step 6) + new_face.optimize(); + + mVolumeFaces[j] = new_face; + } +} + +//static +std::string LLModel::getElementLabel(daeElement *element) +{ // try to get a decent label for this element + // if we have a name attribute, use it + std::string name = element->getAttribute("name"); + if (name.length()) + { + return name; + } + + // if we have an ID attribute, use it + if (element->getID()) + { + return std::string(element->getID()); + } + + // if we have a parent, use it + daeElement* parent = element->getParent(); + if (parent) + { + // if parent has a name, use it + std::string name = parent->getAttribute("name"); + if (name.length()) + { + return name; + } + + // if parent has an ID, use it + if (parent->getID()) + { + return std::string(parent->getID()); + } + } + + // try to use our type + daeString element_name = element->getElementName(); + if (element_name) + { + return std::string(element_name); + } + + // if all else fails, use "object" + return std::string("object"); +} + +//static +LLModel* LLModel::loadModelFromDomMesh(domMesh *mesh) +{ + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + LLModel* ret = new LLModel(volume_params, 0.f); + ret->createVolumeFacesFromDomMesh(mesh); + ret->mLabel = getElementLabel(mesh); + return ret; +} + +std::string LLModel::getName() const +{ + if (!mRequestedLabel.empty()) + return mRequestedLabel; + else + return mLabel; +} + +//static +LLSD LLModel::writeModel( + std::ostream& ostr, + LLModel* physics, + LLModel* high, + LLModel* medium, + LLModel* low, + LLModel* impostor, + const LLModel::Decomposition& decomp, + BOOL upload_skin, + BOOL upload_joints, + BOOL nowrite) +{ + LLSD mdl; + + LLModel* model[] = + { + impostor, + low, + medium, + high, + physics + }; + + bool skinning = upload_skin && high && !high->mSkinWeights.empty(); + + if (skinning) + { //write skinning block + mdl["skin"] = high->mSkinInfo.asLLSD(upload_joints); + } + + if (!decomp.mBaseHull.empty() || + !decomp.mHull.empty()) + { + mdl["physics_convex"] = decomp.asLLSD(); + if (!decomp.mHull.empty()) + { //convex decomposition exists, physics mesh will not be used + model[LLModel::LOD_PHYSICS] = NULL; + } + } + + for (U32 idx = 0; idx < MODEL_NAMES_LENGTH; ++idx) + { + if (model[idx] && model[idx]->getNumVolumeFaces() > 0) + { + LLVector3 min_pos = LLVector3(model[idx]->getVolumeFace(0).mPositions[0].getF32ptr()); + LLVector3 max_pos = min_pos; + + //find position domain + for (S32 i = 0; i < model[idx]->getNumVolumeFaces(); ++i) + { //for each face + const LLVolumeFace& face = model[idx]->getVolumeFace(i); + for (U32 j = 0; j < face.mNumVertices; ++j) + { + update_min_max(min_pos, max_pos, face.mPositions[j].getF32ptr()); + } + } + + LLVector3 pos_range = max_pos - min_pos; + + for (S32 i = 0; i < model[idx]->getNumVolumeFaces(); ++i) + { //for each face + const LLVolumeFace& face = model[idx]->getVolumeFace(i); + if (face.mNumVertices < 3) + { //don't export an empty face + mdl[model_names[idx]][i]["NoGeometry"] = true; + continue; + } + LLSD::Binary verts(face.mNumVertices*3*2); + LLSD::Binary tc(face.mNumVertices*2*2); + LLSD::Binary normals(face.mNumVertices*3*2); + LLSD::Binary indices(face.mNumIndices*2); + + U32 vert_idx = 0; + U32 norm_idx = 0; + U32 tc_idx = 0; + + LLVector2* ftc = (LLVector2*) face.mTexCoords; + LLVector2 min_tc = ftc[0]; + LLVector2 max_tc = min_tc; + + //get texture coordinate domain + for (U32 j = 0; j < face.mNumVertices; ++j) + { + update_min_max(min_tc, max_tc, ftc[j]); + } + + LLVector2 tc_range = max_tc - min_tc; + + for (U32 j = 0; j < face.mNumVertices; ++j) + { //for each vert + + F32* pos = face.mPositions[j].getF32ptr(); + F32* norm = face.mNormals[j].getF32ptr(); + + //position + normal + for (U32 k = 0; k < 3; ++k) + { //for each component + //convert to 16-bit normalized across domain + U16 val = (U16) (((pos[k]-min_pos.mV[k])/pos_range.mV[k])*65535); + + U8* buff = (U8*) &val; + //write to binary buffer + verts[vert_idx++] = buff[0]; + verts[vert_idx++] = buff[1]; + + //convert to 16-bit normalized + val = (U16) ((norm[k]+1.f)*0.5f*65535); + + //write to binary buffer + normals[norm_idx++] = buff[0]; + normals[norm_idx++] = buff[1]; + } + + F32* src_tc = (F32*) face.mTexCoords[j].mV; + + //texcoord + for (U32 k = 0; k < 2; ++k) + { //for each component + //convert to 16-bit normalized + U16 val = (U16) ((src_tc[k]-min_tc.mV[k])/tc_range.mV[k]*65535); + + U8* buff = (U8*) &val; + //write to binary buffer + tc[tc_idx++] = buff[0]; + tc[tc_idx++] = buff[1]; + } + + } + + U32 idx_idx = 0; + for (U32 j = 0; j < face.mNumIndices; ++j) + { + U8* buff = (U8*) &(face.mIndices[j]); + indices[idx_idx++] = buff[0]; + indices[idx_idx++] = buff[1]; + } + + //write out face data + mdl[model_names[idx]][i]["PositionDomain"]["Min"] = min_pos.getValue(); + mdl[model_names[idx]][i]["PositionDomain"]["Max"] = max_pos.getValue(); + mdl[model_names[idx]][i]["TexCoord0Domain"]["Min"] = min_tc.getValue(); + mdl[model_names[idx]][i]["TexCoord0Domain"]["Max"] = max_tc.getValue(); + + mdl[model_names[idx]][i]["Position"] = verts; + mdl[model_names[idx]][i]["Normal"] = normals; + mdl[model_names[idx]][i]["TexCoord0"] = tc; + mdl[model_names[idx]][i]["TriangleList"] = indices; + + if (skinning) + { + //write out skin weights + + //each influence list entry is up to 4 24-bit values + // first 8 bits is bone index + // last 16 bits is bone influence weight + // a bone index of 0xFF signifies no more influences for this vertex + + std::stringstream ostr; + + for (U32 j = 0; j < face.mNumVertices; ++j) + { + LLVector3 pos(face.mPositions[j].getF32ptr()); + + weight_list& weights = high->getJointInfluences(pos); + + S32 count = 0; + for (weight_list::iterator iter = weights.begin(); iter != weights.end(); ++iter) + { + if (iter->mJointIdx < 255 && iter->mJointIdx >= 0) + { + U8 idx = (U8) iter->mJointIdx; + ostr.write((const char*) &idx, 1); + + U16 influence = (U16) (iter->mWeight*65535); + ostr.write((const char*) &influence, 2); + + ++count; + } + } + U8 end_list = 0xFF; + if (count < 4) + { + ostr.write((const char*) &end_list, 1); + } + } + + //copy ostr to binary buffer + std::string data = ostr.str(); + const U8* buff = (U8*) data.data(); + U32 bytes = data.size(); + + LLSD::Binary w(bytes); + for (U32 j = 0; j < bytes; ++j) + { + w[j] = buff[j]; + } + + mdl[model_names[idx]][i]["Weights"] = w; + } + } + } + } + + return writeModelToStream(ostr, mdl, nowrite); +} + +LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite) +{ + U32 bytes = 0; + + std::string::size_type cur_offset = 0; + + LLSD header; + + std::string skin; + + if (mdl.has("skin")) + { //write out skin block + skin = zip_llsd(mdl["skin"]); + + U32 size = skin.size(); + if (size > 0) + { + header["skin"]["offset"] = (LLSD::Integer) cur_offset; + header["skin"]["size"] = (LLSD::Integer) size; + cur_offset += size; + bytes += size; + } + } + + std::string decomposition; + + if (mdl.has("physics_convex")) + { //write out convex decomposition + decomposition = zip_llsd(mdl["physics_convex"]); + + U32 size = decomposition.size(); + if (size > 0) + { + header["physics_convex"]["offset"] = (LLSD::Integer) cur_offset; + header["physics_convex"]["size"] = (LLSD::Integer) size; + cur_offset += size; + bytes += size; + } + } + + std::string out[MODEL_NAMES_LENGTH]; + + for (S32 i = 0; i < MODEL_NAMES_LENGTH; i++) + { + if (mdl.has(model_names[i])) + { + out[i] = zip_llsd(mdl[model_names[i]]); + + U32 size = out[i].size(); + + header[model_names[i]]["offset"] = (LLSD::Integer) cur_offset; + header[model_names[i]]["size"] = (LLSD::Integer) size; + cur_offset += size; + bytes += size; + } + } + + if (!nowrite) + { + LLSDSerialize::toBinary(header, ostr); + + if (!skin.empty()) + { //write skin block + ostr.write((const char*) skin.data(), header["skin"]["size"].asInteger()); + } + + if (!decomposition.empty()) + { //write decomposition block + ostr.write((const char*) decomposition.data(), header["physics_convex"]["size"].asInteger()); + } + + for (S32 i = 0; i < MODEL_NAMES_LENGTH; i++) + { + if (!out[i].empty()) + { + ostr.write((const char*) out[i].data(), header[model_names[i]]["size"].asInteger()); + } + } + } + + return header; +} + +LLModel::weight_list& LLModel::getJointInfluences(const LLVector3& pos) +{ + weight_map::iterator iter = mSkinWeights.find(pos); + + if (iter != mSkinWeights.end()) + { + if ((iter->first - pos).magVec() > 0.1f) + { + llerrs << "Couldn't find weight list." << llendl; + } + + return iter->second; + } + else + { //no exact match found, get closest point + const F32 epsilon = 2.f/65536; + weight_map::iterator iter_up = mSkinWeights.lower_bound(pos); + weight_map::iterator iter_down = ++iter_up; + + weight_map::iterator best = iter_up; + + F32 min_dist = (iter->first - pos).magVecSquared(); + + bool done = false; + while (!done) + { //search up and down mSkinWeights from lower bound of pos until a + //match is found within epsilon. If no match is found within epsilon, + //return closest match + done = true; + if (iter_up != mSkinWeights.end() && ++iter_up != mSkinWeights.end()) + { + done = false; + F32 dist = (iter_up->first - pos).magVecSquared(); + + if (dist < epsilon) + { + return iter_up->second; + } + + if (dist < min_dist) + { + best = iter_up; + min_dist = dist; + } + } + + if (iter_down != mSkinWeights.begin() && --iter_down != mSkinWeights.begin()) + { + done = false; + + F32 dist = (iter_down->first - pos).magVecSquared(); + + if (dist < epsilon) + { + return iter_down->second; + } + + if (dist < min_dist) + { + best = iter_down; + min_dist = dist; + } + + } + } + + return best->second; + } +} + +void LLModel::setConvexHullDecomposition( + const LLModel::convex_hull_decomposition& decomp) +{ + mPhysics.mHull = decomp; + mPhysics.mMesh.clear(); + updateHullCenters(); +} + +void LLModel::updateHullCenters() +{ + mHullCenter.resize(mPhysics.mHull.size()); + mHullPoints = 0; + mCenterOfHullCenters.clear(); + + for (U32 i = 0; i < mPhysics.mHull.size(); ++i) + { + LLVector3 cur_center; + + for (U32 j = 0; j < mPhysics.mHull[i].size(); ++j) + { + cur_center += mPhysics.mHull[i][j]; + } + mCenterOfHullCenters += cur_center; + cur_center *= 1.f/mPhysics.mHull[i].size(); + mHullCenter[i] = cur_center; + mHullPoints += mPhysics.mHull[i].size(); + } + + if (mHullPoints > 0) + { + mCenterOfHullCenters *= 1.f / mHullPoints; + llassert(mPhysics.hasHullList()); + } +} + +bool LLModel::loadModel(std::istream& is) +{ + mSculptLevel = -1; // default is an error occured + + LLSD header; + { + if (!LLSDSerialize::fromBinary(header, is, 1024*1024*1024)) + { + llwarns << "Mesh header parse error. Not a valid mesh asset!" << llendl; + return false; + } + } + + std::string nm[] = + { + "lowest_lod", + "low_lod", + "medium_lod", + "high_lod", + "physics_mesh", + }; + + const S32 MODEL_LODS = 5; + + S32 lod = llclamp((S32) mDetail, 0, MODEL_LODS); + + if (header[nm[lod]]["offset"].asInteger() == -1 || + header[nm[lod]]["size"].asInteger() == 0 ) + { //cannot load requested LOD + return false; + } + + bool has_skin = header["skin"]["offset"].asInteger() >=0 && + header["skin"]["size"].asInteger() > 0; + + if (lod == LLModel::LOD_HIGH) + { //try to load skin info and decomp info + std::ios::pos_type cur_pos = is.tellg(); + loadSkinInfo(header, is); + is.seekg(cur_pos); + } + + if (lod == LLModel::LOD_PHYSICS) + { + std::ios::pos_type cur_pos = is.tellg(); + loadDecomposition(header, is); + is.seekg(cur_pos); + } + + is.seekg(header[nm[lod]]["offset"].asInteger(), std::ios_base::cur); + + if (unpackVolumeFaces(is, header[nm[lod]]["size"].asInteger())) + { + if (has_skin) + { + //build out mSkinWeight from face info + for (S32 i = 0; i < getNumVolumeFaces(); ++i) + { + const LLVolumeFace& face = getVolumeFace(i); + + if (face.mWeights) + { + for (S32 j = 0; j < face.mNumVertices; ++j) + { + LLVector4a& w = face.mWeights[j]; + + std::vector wght; + + for (S32 k = 0; k < 4; ++k) + { + S32 idx = (S32) w[k]; + F32 f = w[k] - idx; + if (f > 0.f) + { + wght.push_back(JointWeight(idx, f)); + } + } + + if (!wght.empty()) + { + LLVector3 pos(face.mPositions[j].getF32ptr()); + mSkinWeights[pos] = wght; + } + } + } + } + } + return true; + } + + return false; + +} + + +bool LLModel::loadSkinInfo(LLSD& header, std::istream &is) +{ + S32 offset = header["skin"]["offset"].asInteger(); + S32 size = header["skin"]["size"].asInteger(); + + if (offset >= 0 && size > 0) + { + is.seekg(offset, std::ios_base::cur); + + LLSD skin_data; + + if (unzip_llsd(skin_data, is, size)) + { + mSkinInfo.fromLLSD(skin_data); + return true; + } + } + + return false; +} + +bool LLModel::loadDecomposition(LLSD& header, std::istream& is) +{ + S32 offset = header["physics_convex"]["offset"].asInteger(); + S32 size = header["physics_convex"]["size"].asInteger(); + + if (offset >= 0 && size > 0) + { + is.seekg(offset, std::ios_base::cur); + + LLSD data; + + if (unzip_llsd(data, is, size)) + { + mPhysics.fromLLSD(data); + updateHullCenters(); + } + } + + return true; +} + + +LLMeshSkinInfo::LLMeshSkinInfo(LLSD& skin) +{ + fromLLSD(skin); +} + +void LLMeshSkinInfo::fromLLSD(LLSD& skin) +{ + if (skin.has("joint_names")) + { + for (U32 i = 0; i < skin["joint_names"].size(); ++i) + { + mJointNames.push_back(skin["joint_names"][i]); + } + } + + if (skin.has("inverse_bind_matrix")) + { + for (U32 i = 0; i < skin["inverse_bind_matrix"].size(); ++i) + { + LLMatrix4 mat; + for (U32 j = 0; j < 4; j++) + { + for (U32 k = 0; k < 4; k++) + { + mat.mMatrix[j][k] = skin["inverse_bind_matrix"][i][j*4+k].asReal(); + } + } + + mInvBindMatrix.push_back(mat); + } + } + + if (skin.has("bind_shape_matrix")) + { + for (U32 j = 0; j < 4; j++) + { + for (U32 k = 0; k < 4; k++) + { + mBindShapeMatrix.mMatrix[j][k] = skin["bind_shape_matrix"][j*4+k].asReal(); + } + } + } + + if (skin.has("alt_inverse_bind_matrix")) + { + for (U32 i = 0; i < skin["alt_inverse_bind_matrix"].size(); ++i) + { + LLMatrix4 mat; + for (U32 j = 0; j < 4; j++) + { + for (U32 k = 0; k < 4; k++) + { + mat.mMatrix[j][k] = skin["alt_inverse_bind_matrix"][i][j*4+k].asReal(); + } + } + + mAlternateBindMatrix.push_back(mat); + } + } + + if (skin.has("pelvis_offset")) + { + mPelvisOffset = skin["pelvis_offset"].asReal(); + } +} + +LLSD LLMeshSkinInfo::asLLSD(bool include_joints) const +{ + LLSD ret; + + for (U32 i = 0; i < mJointNames.size(); ++i) + { + ret["joint_names"][i] = mJointNames[i]; + + for (U32 j = 0; j < 4; j++) + { + for (U32 k = 0; k < 4; k++) + { + ret["inverse_bind_matrix"][i][j*4+k] = mInvBindMatrix[i].mMatrix[j][k]; + } + } + } + + for (U32 i = 0; i < 4; i++) + { + for (U32 j = 0; j < 4; j++) + { + ret["bind_shape_matrix"][i*4+j] = mBindShapeMatrix.mMatrix[i][j]; + } + } + + if ( include_joints && mAlternateBindMatrix.size() > 0 ) + { + for (U32 i = 0; i < mJointNames.size(); ++i) + { + for (U32 j = 0; j < 4; j++) + { + for (U32 k = 0; k < 4; k++) + { + ret["alt_inverse_bind_matrix"][i][j*4+k] = mAlternateBindMatrix[i].mMatrix[j][k]; + } + } + } + + ret["pelvis_offset"] = mPelvisOffset; + } + + return ret; +} + +LLModel::Decomposition::Decomposition(LLSD& data) +{ + fromLLSD(data); +} + +void LLModel::Decomposition::fromLLSD(LLSD& decomp) +{ + if (decomp.has("HullList")) + { + // updated for const-correctness. gcc is picky about this type of thing - Nyx + const LLSD::Binary& hulls = decomp["HullList"].asBinary(); + const LLSD::Binary& position = decomp["Positions"].asBinary(); + + U16* p = (U16*) &position[0]; + + mHull.resize(hulls.size()); + + LLVector3 min; + LLVector3 max; + LLVector3 range; + + if (decomp.has("Min")) + { + min.setValue(decomp["Min"]); + max.setValue(decomp["Max"]); + } + else + { + min.set(-0.5f, -0.5f, -0.5f); + max.set(0.5f, 0.5f, 0.5f); + } + + range = max-min; + + for (U32 i = 0; i < hulls.size(); ++i) + { + U16 count = (hulls[i] == 0) ? 256 : hulls[i]; + + std::set valid; + + //must have at least 4 points + //llassert(count > 3); + + for (U32 j = 0; j < count; ++j) + { + U64 test = (U64) p[0] | ((U64) p[1] << 16) | ((U64) p[2] << 32); + //point must be unique + //llassert(valid.find(test) == valid.end()); + valid.insert(test); + + mHull[i].push_back(LLVector3( + (F32) p[0]/65535.f*range.mV[0]+min.mV[0], + (F32) p[1]/65535.f*range.mV[1]+min.mV[1], + (F32) p[2]/65535.f*range.mV[2]+min.mV[2])); + p += 3; + + + } + + //each hull must contain at least 4 unique points + //llassert(valid.size() > 3); + } + } + + if (decomp.has("BoundingVerts")) + { + const LLSD::Binary& position = decomp["BoundingVerts"].asBinary(); + + U16* p = (U16*) &position[0]; + + LLVector3 min; + LLVector3 max; + LLVector3 range; + + if (decomp.has("Min")) + { + min.setValue(decomp["Min"]); + max.setValue(decomp["Max"]); + } + else + { + min.set(-0.5f, -0.5f, -0.5f); + max.set(0.5f, 0.5f, 0.5f); + } + + range = max-min; + + U16 count = position.size()/6; + + for (U32 j = 0; j < count; ++j) + { + mBaseHull.push_back(LLVector3( + (F32) p[0]/65535.f*range.mV[0]+min.mV[0], + (F32) p[1]/65535.f*range.mV[1]+min.mV[1], + (F32) p[2]/65535.f*range.mV[2]+min.mV[2])); + p += 3; + } + } + else + { + //empty base hull mesh to indicate decomposition has been loaded + //but contains no base hull + mBaseHullMesh.clear(); + } +} + +bool LLModel::Decomposition::hasHullList() const +{ + return !mHull.empty() ; +} + +LLSD LLModel::Decomposition::asLLSD() const +{ + LLSD ret; + + if (mBaseHull.empty() && mHull.empty()) + { //nothing to write + return ret; + } + + //write decomposition block + // ["physics_convex"]["HullList"] -- list of 8 bit integers, each entry represents a hull with specified number of points + // ["physics_convex"]["Position"] -- list of 16-bit integers to be decoded to given domain, encoded 3D points + // ["physics_convex"]["BoundingVerts"] -- list of 16-bit integers to be decoded to given domain, encoded 3D points representing a single hull approximation of given shape + + //get minimum and maximum + LLVector3 min; + + if (mHull.empty()) + { + min = mBaseHull[0]; + } + else + { + min = mHull[0][0]; + } + + LLVector3 max = min; + + LLSD::Binary hulls(mHull.size()); + + U32 total = 0; + + for (U32 i = 0; i < mHull.size(); ++i) + { + U32 size = mHull[i].size(); + total += size; + hulls[i] = (U8) (size); + + for (U32 j = 0; j < mHull[i].size(); ++j) + { + update_min_max(min, max, mHull[i][j]); + } + } + + for (U32 i = 0; i < mBaseHull.size(); ++i) + { + update_min_max(min, max, mBaseHull[i]); + } + + ret["Min"] = min.getValue(); + ret["Max"] = max.getValue(); + + if (!hulls.empty()) + { + ret["HullList"] = hulls; + } + + if (total > 0) + { + LLSD::Binary p(total*3*2); + + LLVector3 min(-0.5f, -0.5f, -0.5f); + LLVector3 max(0.5f, 0.5f, 0.5f); + LLVector3 range = max-min; + + U32 vert_idx = 0; + + for (U32 i = 0; i < mHull.size(); ++i) + { + std::set valid; + + llassert(!mHull[i].empty()); + + for (U32 j = 0; j < mHull[i].size(); ++j) + { + U64 test = 0; + for (U32 k = 0; k < 3; k++) + { + F32* src = (F32*) (mHull[i][j].mV); + + llassert(src[k] <= 0.501f && src[k] >= -0.501f); + + //convert to 16-bit normalized across domain + U16 val = (U16) (((src[k]-min.mV[k])/range.mV[k])*65535); + + if(valid.size() < 3) + { + switch (k) + { + case 0: test = test | (U64) val; break; + case 1: test = test | ((U64) val << 16); break; + case 2: test = test | ((U64) val << 32); break; + }; + + valid.insert(test); + } + + U8* buff = (U8*) &val; + //write to binary buffer + p[vert_idx++] = buff[0]; + p[vert_idx++] = buff[1]; + + //makes sure we haven't run off the end of the array + llassert(vert_idx <= p.size()); + } + } + + //must have at least 3 unique points + llassert(valid.size() > 2); + } + + ret["Positions"] = p; + } + + //llassert(!mBaseHull.empty()); + + if (!mBaseHull.empty()) + { + LLSD::Binary p(mBaseHull.size()*3*2); + + LLVector3 min(-0.5f, -0.5f, -0.5f); + LLVector3 max(0.5f, 0.5f, 0.5f); + LLVector3 range = max-min; + + U32 vert_idx = 0; + for (U32 j = 0; j < mBaseHull.size(); ++j) + { + for (U32 k = 0; k < 3; k++) + { + llassert(mBaseHull[j].mV[k] <= 0.51f && mBaseHull[j].mV[k] >= -0.51f); + + //convert to 16-bit normalized across domain + U16 val = (U16) (((mBaseHull[j].mV[k]-min.mV[k])/range.mV[k])*65535); + + U8* buff = (U8*) &val; + //write to binary buffer + p[vert_idx++] = buff[0]; + p[vert_idx++] = buff[1]; + + if (vert_idx > p.size()) + { + llerrs << "Index out of bounds" << llendl; + } + } + } + + ret["BoundingVerts"] = p; + } + + return ret; +} + +void LLModel::Decomposition::merge(const LLModel::Decomposition* rhs) +{ + if (!rhs) + { + return; + } + + if (mMeshID != rhs->mMeshID) + { + llerrs << "Attempted to merge with decomposition of some other mesh." << llendl; + } + + if (mBaseHull.empty()) + { //take base hull and decomposition from rhs + mHull = rhs->mHull; + mBaseHull = rhs->mBaseHull; + mMesh = rhs->mMesh; + mBaseHullMesh = rhs->mBaseHullMesh; + } + + if (mPhysicsShapeMesh.empty()) + { //take physics shape mesh from rhs + mPhysicsShapeMesh = rhs->mPhysicsShapeMesh; + } +} + diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h new file mode 100644 index 000000000..cd9f76fcb --- /dev/null +++ b/indra/llprimitive/llmodel.h @@ -0,0 +1,257 @@ +/** + * @file llmodel.h + * @brief Model handling class definitions + * + * $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_LLMODEL_H +#define LL_LLMODEL_H + +#include "llpointer.h" +#include "llvolume.h" +#include "v4math.h" +#include "m4math.h" + +class daeElement; +class domMesh; + +#define MAX_MODEL_FACES 8 + + +class LLMeshSkinInfo +{ +public: + LLUUID mMeshID; + std::vector mJointNames; + std::vector mInvBindMatrix; + std::vector mAlternateBindMatrix; + std::map mJointMap; + + LLMeshSkinInfo() { } + LLMeshSkinInfo(LLSD& data); + void fromLLSD(LLSD& data); + LLSD asLLSD(bool include_joints) const; + LLMatrix4 mBindShapeMatrix; + float mPelvisOffset; +}; + +class LLModel : public LLVolume +{ +public: + + enum + { + LOD_IMPOSTOR = 0, + LOD_LOW, + LOD_MEDIUM, + LOD_HIGH, + LOD_PHYSICS, + NUM_LODS + }; + + enum EModelStatus + { + NO_ERRORS = 0, + VERTEX_NUMBER_OVERFLOW, //vertex number is >= 65535. + BAD_ELEMENT, + INVALID_STATUS + } ; + + //convex_hull_decomposition is a vector of convex hulls + //each convex hull is a set of points + typedef std::vector > convex_hull_decomposition; + typedef std::vector hull; + + class PhysicsMesh + { + public: + std::vector mPositions; + std::vector mNormals; + + void clear() + { + mPositions.clear(); + mNormals.clear(); + } + + bool empty() const + { + return mPositions.empty(); + } + }; + + class Decomposition + { + public: + Decomposition() { } + Decomposition(LLSD& data); + void fromLLSD(LLSD& data); + LLSD asLLSD() const; + bool hasHullList() const; + + void merge(const Decomposition* rhs); + + LLUUID mMeshID; + LLModel::convex_hull_decomposition mHull; + LLModel::hull mBaseHull; + + std::vector mMesh; + LLModel::PhysicsMesh mBaseHullMesh; + LLModel::PhysicsMesh mPhysicsShapeMesh; + }; + + LLModel(LLVolumeParams& params, F32 detail); + ~LLModel(); + + bool loadModel(std::istream& is); + bool loadSkinInfo(LLSD& header, std::istream& is); + bool loadDecomposition(LLSD& header, std::istream& is); + + static LLSD writeModel( + std::ostream& ostr, + LLModel* physics, + LLModel* high, + LLModel* medium, + LLModel* low, + LLModel* imposotr, + const LLModel::Decomposition& decomp, + BOOL upload_skin, + BOOL upload_joints, + BOOL nowrite = FALSE); + + static LLSD writeModelToStream( + std::ostream& ostr, + LLSD& mdl, + BOOL nowrite = FALSE); + + static LLModel* loadModelFromDomMesh(domMesh* mesh); + static std::string getElementLabel(daeElement* element); + std::string getName() const; + EModelStatus getStatus() const {return mStatus;} + static std::string getStatusString(U32 status) ; + + void appendFaces(LLModel* model, LLMatrix4& transform, LLMatrix4& normal_transform); + void appendFace(const LLVolumeFace& src_face, std::string src_material, LLMatrix4& mat, LLMatrix4& norm_mat); + + void setNumVolumeFaces(S32 count); + void setVolumeFaceData( + S32 f, + LLStrider pos, + LLStrider norm, + LLStrider tc, + LLStrider ind, + U32 num_verts, + U32 num_indices); + + void generateNormals(F32 angle_cutoff); + + void addFace(const LLVolumeFace& face); + + void normalizeVolumeFaces(); + void optimizeVolumeFaces(); + void offsetMesh( const LLVector3& pivotPoint ); + void getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out); + std::vector mMaterialList; + + //data used for skin weights + class JointWeight + { + public: + S32 mJointIdx; + F32 mWeight; + + JointWeight() + { + mJointIdx = 0; + mWeight = 0.f; + } + + JointWeight(S32 idx, F32 weight) + : mJointIdx(idx), mWeight(weight) + { + } + + bool operator<(const JointWeight& rhs) const + { + if (mWeight == rhs.mWeight) + { + return mJointIdx < rhs.mJointIdx; + } + + return mWeight < rhs.mWeight; + } + + }; + + struct CompareWeightGreater + { + bool operator()(const JointWeight& lhs, const JointWeight& rhs) + { + return rhs < lhs; // strongest = first + } + }; + + //copy of position array for this model -- mPosition[idx].mV[X,Y,Z] + std::vector mPosition; + + //map of positions to skin weights --- mSkinWeights[pos].mV[0..4] == . + //joint_index corresponds to mJointList + typedef std::vector weight_list; + typedef std::map weight_map; + weight_map mSkinWeights; + + //get list of weight influences closest to given position + weight_list& getJointInfluences(const LLVector3& pos); + + LLMeshSkinInfo mSkinInfo; + + std::string mRequestedLabel; // name requested in UI, if any. + std::string mLabel; // name computed from dae. + + LLVector3 mNormalizedScale; + LLVector3 mNormalizedTranslation; + + float mPelvisOffset; + // convex hull decomposition + S32 mDecompID; + + void setConvexHullDecomposition( + const convex_hull_decomposition& decomp); + void updateHullCenters(); + + LLVector3 mCenterOfHullCenters; + std::vector mHullCenter; + U32 mHullPoints; + + //ID for storing this model in a .slm file + S32 mLocalID; + + Decomposition mPhysics; + + EModelStatus mStatus ; +protected: + void addVolumeFacesFromDomMesh(domMesh* mesh); + virtual BOOL createVolumeFacesFromDomMesh(domMesh *mesh); +}; + +#endif //LL_LLMODEL_H diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index 9623a3925..2e3f7a829 100644 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -573,7 +573,9 @@ BOOL LLAgentCamera::calcCameraMinDistance(F32 &obj_min_distance) BOOL soft_limit = FALSE; // is the bounding box to be treated literally (volumes) or as an approximation (avatars) if (!mFocusObject || mFocusObject->isDead() || - //mFocusObject->isMesh() || +#if MESH_ENABLED + mFocusObject->isMesh() || +#endif //MESH_ENABLED gSavedSettings.getBOOL("DisableCameraConstraints")) { obj_min_distance = 0.f; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 0d43c8b36..750dc4c29 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -52,6 +52,9 @@ #include "llwindow.h" #include "llviewerstats.h" #include "llmd5.h" +#if MESH_ENABLED +#include "llmeshrepository.h" +#endif //MESH_ENABLED #include "llpumpio.h" #include "llimpanel.h" #include "llmimetypes.h" @@ -1110,6 +1113,9 @@ bool LLAppViewer::mainLoop() break; } } +#if MESH_ENABLED + gMeshRepo.update() ; +#endif //MESH_ENABLED if ((LLStartUp::getStartupState() >= STATE_CLEANUP) && (frameTimer.getElapsedTimeF64() > FRAME_STALL_THRESHOLD)) { @@ -1233,6 +1239,11 @@ bool LLAppViewer::cleanup() llinfos << "Cleaning Up" << llendflush; +#if MESH_ENABLED + // shut down mesh streamer + gMeshRepo.shutdown(); +#endif //MESH_ENABLED + // Must clean up texture references before viewer window is destroyed. if(LLHUDManager::instanceExists()) { @@ -1655,6 +1666,11 @@ bool LLAppViewer::initThreads() LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(), sImageDecodeThread, enable_threads && true); LLImage::initClass(); + +#if MESH_ENABLED + // Mesh streaming and caching + gMeshRepo.init(); +#endif //MESH_ENABLED // *FIX: no error handling here! return true; } diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h index b40982dad..6c044978f 100644 --- a/indra/newview/lldrawable.h +++ b/indra/newview/lldrawable.h @@ -269,6 +269,9 @@ public: REBUILD_GEOMETRY= REBUILD_POSITION|REBUILD_TCOORD|REBUILD_COLOR, REBUILD_MATERIAL= REBUILD_TCOORD|REBUILD_COLOR, REBUILD_ALL = REBUILD_GEOMETRY|REBUILD_VOLUME, +#if MESH_ENABLED + REBUILD_RIGGED = 0x00008000, +#endif //MESH_ENABLED ON_SHIFT_LIST = 0x00010000, BLOCKER = 0x00020000, ACTIVE = 0x00040000, @@ -280,6 +283,9 @@ public: CLEAR_INVISIBLE = 0x01000000, // clear FORCE_INVISIBLE next draw frame REBUILD_SHADOW = 0x02000000, HAS_ALPHA = 0x04000000, +#if MESH_ENABLED + RIGGED = 0x08000000, +#endif //MESH_ENABLED PARTITION_MOVE = 0x10000000, } EDrawableFlags; diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index 92d257734..9e6123c30 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -54,10 +54,11 @@ static U32 sDataMask = LLDrawPoolAvatar::VERTEX_DATA_MASK; static U32 sBufferUsage = GL_STREAM_DRAW_ARB; static U32 sShaderLevel = 0; -static LLGLSLShader* sVertexProgram = NULL; +LLGLSLShader* LLDrawPoolAvatar::sVertexProgram = NULL; BOOL LLDrawPoolAvatar::sSkipOpaque = FALSE; BOOL LLDrawPoolAvatar::sSkipTransparent = FALSE; +S32 LLDrawPoolAvatar::sDiffuseChannel = 0; extern BOOL gUseGLPick; @@ -94,7 +95,7 @@ BOOL gAvatarEmbossBumpMap = FALSE; static BOOL sRenderingSkinned = FALSE; S32 normal_channel = -1; S32 specular_channel = -1; -S32 diffuse_channel = -1; +S32 cube_channel = -1; LLDrawPoolAvatar::LLDrawPoolAvatar() : LLFacePool(POOL_AVATAR) @@ -152,19 +153,14 @@ LLMatrix4& LLDrawPoolAvatar::getModelView() //----------------------------------------------------------------------------- -S32 LLDrawPoolAvatar::getNumDeferredPasses() -{ - return getNumPasses(); -} void LLDrawPoolAvatar::beginDeferredPass(S32 pass) { sSkipTransparent = TRUE; if (LLPipeline::sImpostorRender) - { - beginDeferredSkinned(); - return; + { //impostor pass does not have rigid or impostor rendering + pass += 2; } switch (pass) @@ -178,6 +174,14 @@ void LLDrawPoolAvatar::beginDeferredPass(S32 pass) case 2: beginDeferredSkinned(); break; +#if MESH_ENABLED + case 3: + beginDeferredRiggedSimple(); + break; + case 4: + beginDeferredRiggedBump(); + break; +#endif //MESH_ENABLED } } @@ -187,8 +191,7 @@ void LLDrawPoolAvatar::endDeferredPass(S32 pass) if (LLPipeline::sImpostorRender) { - endDeferredSkinned(); - return; + pass += 2; } switch (pass) @@ -202,6 +205,14 @@ void LLDrawPoolAvatar::endDeferredPass(S32 pass) case 2: endDeferredSkinned(); break; +#if MESH_ENABLED + case 3: + endDeferredRiggedSimple(); + break; + case 4: + endDeferredRiggedBump(); + break; +#endif //MESH_ENABLED } } @@ -212,10 +223,41 @@ void LLDrawPoolAvatar::renderDeferred(S32 pass) S32 LLDrawPoolAvatar::getNumPostDeferredPasses() { +#if MESH_ENABLED + return 6; +#endif //MESH_ENABLED +#if !MESH_ENABLED return 1; +#endif //!MESH_ENABLED } void LLDrawPoolAvatar::beginPostDeferredPass(S32 pass) +{ + switch (pass) + { + case 0: + beginPostDeferredAlpha(); + break; +#if MESH_ENABLED + case 1: + beginRiggedFullbright(); + break; + case 2: + beginRiggedFullbrightShiny(); + break; + case 3: + beginDeferredRiggedAlpha(); + break; + case 4: + beginRiggedFullbrightAlpha(); + break; + case 5: + beginRiggedGlow(); + break; +#endif //MESH_ENABLED + } +} +void LLDrawPoolAvatar::beginPostDeferredAlpha() { sSkipOpaque = TRUE; sShaderLevel = mVertexShaderLevel; @@ -225,10 +267,57 @@ void LLDrawPoolAvatar::beginPostDeferredPass(S32 pass) gPipeline.bindDeferredShader(*sVertexProgram); + sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP); enable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]); } +#if MESH_ENABLED +void LLDrawPoolAvatar::beginDeferredRiggedAlpha() +{ + sVertexProgram = &gDeferredSkinnedAlphaProgram; + gPipeline.bindDeferredShader(*sVertexProgram); + sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP); + LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT); + gPipeline.enableLightsDynamic(); +} + +void LLDrawPoolAvatar::endDeferredRiggedAlpha() +{ + LLVertexBuffer::unbind(); + gPipeline.unbindDeferredShader(*sVertexProgram); + sDiffuseChannel = 0; + LLVertexBuffer::sWeight4Loc = -1; + sVertexProgram = NULL; +} +#endif //MESH_ENABLED void LLDrawPoolAvatar::endPostDeferredPass(S32 pass) +{ + switch (pass) + { + case 0: + endPostDeferredAlpha(); + break; +#if MESH_ENABLED + case 1: + endRiggedFullbright(); + break; + case 2: + endRiggedFullbrightShiny(); + break; + case 3: + endDeferredRiggedAlpha(); + break; + case 4: + endRiggedFullbrightAlpha(); + break; + case 5: + endRiggedGlow(); + break; +#endif //MESH_ENABLED + } +} + +void LLDrawPoolAvatar::endPostDeferredAlpha() { // if we're in software-blending, remember to set the fence _after_ we draw so we wait till this rendering is done sRenderingSkinned = FALSE; @@ -236,55 +325,107 @@ void LLDrawPoolAvatar::endPostDeferredPass(S32 pass) disable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]); gPipeline.unbindDeferredShader(*sVertexProgram); - + sDiffuseChannel = 0; sShaderLevel = mVertexShaderLevel; } void LLDrawPoolAvatar::renderPostDeferred(S32 pass) { - render(2); //pass 2 = skinned +#if MESH_ENABLED + const S32 actual_pass[] = + { //map post deferred pass numbers to what render() expects + 2, //skinned + 4, // rigged fullbright + 6, //rigged fullbright shiny + 7, //rigged alpha + 8, //rigged fullbright alpha + 9, //rigged glow + }; + + pass = actual_pass[pass]; +#endif //MESH_ENABLED +#if !MESH_ENABLED + if(pass > 0) return; + pass = 2; +#endif //!MESH_ENABLED + + if (LLPipeline::sImpostorRender) + { //HACK for impostors so actual pass ends up being proper pass + pass -= 2; + } + + render(pass); + } S32 LLDrawPoolAvatar::getNumShadowPasses() { +#if MESH_ENABLED + return 2; +#endif //MESH_ENABLED +#if !MESH_ENABLED return 1; +#endif //!MESH_ENABLED } void LLDrawPoolAvatar::beginShadowPass(S32 pass) { LLFastTimer t(LLFastTimer::FTM_SHADOW_AVATAR); - - sVertexProgram = &gDeferredAvatarShadowProgram; - if (sShaderLevel > 0) + if (pass == 0) { - gAvatarMatrixParam = sVertexProgram->mUniform[LLViewerShaderMgr::AVATAR_MATRIX]; - } - gGL.setAlphaRejectSettings(LLRender::CF_GREATER_EQUAL, 0.2f); - - glColor4f(1,1,1,1); + sVertexProgram = &gDeferredAvatarShadowProgram; + if (sShaderLevel > 0) + { + gAvatarMatrixParam = sVertexProgram->mUniform[LLViewerShaderMgr::AVATAR_MATRIX]; + } + gGL.setAlphaRejectSettings(LLRender::CF_GREATER_EQUAL, 0.2f); + + glColor4f(1,1,1,1); - if ((sShaderLevel > 0)) // for hardware blending + if ((sShaderLevel > 0)) // for hardware blending + { + sRenderingSkinned = TRUE; + sVertexProgram->bind(); + enable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]); + } + } +#if MESH_ENABLED + else { - sRenderingSkinned = TRUE; + sVertexProgram = &gDeferredAttachmentShadowProgram; + sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP); sVertexProgram->bind(); - enable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]); + LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT); } - +#endif //MESH_ENABLED } void LLDrawPoolAvatar::endShadowPass(S32 pass) { LLFastTimer t(LLFastTimer::FTM_SHADOW_AVATAR); - if (sShaderLevel > 0) + if (pass == 0) { - sRenderingSkinned = FALSE; - sVertexProgram->unbind(); - disable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]); + if (sShaderLevel > 0) + { + sRenderingSkinned = FALSE; + sVertexProgram->unbind(); + disable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]); + } + gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); } - gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); +#if MESH_ENABLED + else + { + LLVertexBuffer::unbind(); + sVertexProgram->unbind(); + LLVertexBuffer::sWeight4Loc = -1; + sVertexProgram = NULL; + } +#endif //MESH_ENABLED + } void LLDrawPoolAvatar::renderShadow(S32 pass) @@ -318,25 +459,54 @@ void LLDrawPoolAvatar::renderShadow(S32 pass) return; } - if (sShaderLevel > 0) + if (pass == 0) { - gAvatarMatrixParam = sVertexProgram->mUniform[LLViewerShaderMgr::AVATAR_MATRIX]; - } - avatarp->renderSkinned(AVATAR_RENDER_PASS_SINGLE); + if (sShaderLevel > 0) + { + gAvatarMatrixParam = sVertexProgram->mUniform[LLViewerShaderMgr::AVATAR_MATRIX]; + } + avatarp->renderSkinned(AVATAR_RENDER_PASS_SINGLE); + } +#if MESH_ENABLED + else + { + renderRigged(avatarp, RIGGED_SIMPLE); + renderRigged(avatarp, RIGGED_ALPHA); + renderRigged(avatarp, RIGGED_FULLBRIGHT); + renderRigged(avatarp, RIGGED_FULLBRIGHT_SHINY); + renderRigged(avatarp, RIGGED_SHINY); + renderRigged(avatarp, RIGGED_FULLBRIGHT_ALPHA); + } +#endif //MESH_ENABLED } S32 LLDrawPoolAvatar::getNumPasses() { +#if MESH_ENABLED + return LLPipeline::sImpostorRender ? 8 : 10; +#endif //MESH_ENABLED +#if !MESH_ENABLED return LLPipeline::sImpostorRender ? 1 : 3; +#endif //!MESH_ENABLED } +S32 LLDrawPoolAvatar::getNumDeferredPasses() +{ +#if MESH_ENABLED + return LLPipeline::sImpostorRender ? 3 : 5; +#endif //MESH_ENABLED +#if !MESH_ENABLED + return getNumPasses(); +#endif //!MESH_ENABLED + +} void LLDrawPoolAvatar::render(S32 pass) { LLFastTimer t(LLFastTimer::FTM_RENDER_CHARACTERS); if (LLPipeline::sImpostorRender) { - renderAvatars(NULL, 2); + renderAvatars(NULL, pass+2); return; } @@ -349,10 +519,14 @@ void LLDrawPoolAvatar::beginRenderPass(S32 pass) //reset vertex buffer mappings LLVertexBuffer::unbind(); + if (pass == 0) + { //make sure no stale colors are left over from a previous render + glColor4f(1,1,1,1); + } + if (LLPipeline::sImpostorRender) - { - beginSkinned(); - return; + { //impostor render does not have impostors or rigid rendering + pass += 2; } switch (pass) @@ -366,6 +540,29 @@ void LLDrawPoolAvatar::beginRenderPass(S32 pass) case 2: beginSkinned(); break; +#if MESH_ENABLED + case 3: + beginRiggedSimple(); + break; + case 4: + beginRiggedFullbright(); + break; + case 5: + beginRiggedShinySimple(); + break; + case 6: + beginRiggedFullbrightShiny(); + break; + case 7: + beginRiggedAlpha(); + break; + case 8: + beginRiggedFullbrightAlpha(); + break; + case 9: + beginRiggedGlow(); + break; +#endif //MESH_ENABLED } } @@ -375,8 +572,7 @@ void LLDrawPoolAvatar::endRenderPass(S32 pass) if (LLPipeline::sImpostorRender) { - endSkinned(); - return; + pass += 2; } switch (pass) @@ -389,6 +585,30 @@ void LLDrawPoolAvatar::endRenderPass(S32 pass) break; case 2: endSkinned(); +#if MESH_ENABLED + break; + case 3: + endRiggedSimple(); + break; + case 4: + endRiggedFullbright(); + break; + case 5: + endRiggedShinySimple(); + break; + case 6: + endRiggedFullbrightShiny(); + break; + case 7: + endRiggedAlpha(); + break; + case 8: + endRiggedFullbrightAlpha(); + break; + case 9: + endRiggedGlow(); + break; +#endif //MESH_ENABLED } } @@ -401,7 +621,7 @@ void LLDrawPoolAvatar::beginImpostor() } gPipeline.enableLightsFullbright(LLColor4(1,1,1,1)); - diffuse_channel = 0; + sDiffuseChannel = 0; } void LLDrawPoolAvatar::endImpostor() @@ -452,9 +672,9 @@ void LLDrawPoolAvatar::beginDeferredImpostor() sVertexProgram = &gDeferredImpostorProgram; - normal_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::DEFERRED_NORMAL); specular_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::SPECULAR_MAP); - diffuse_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP); + normal_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::DEFERRED_NORMAL); + sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP); sVertexProgram->bind(); } @@ -574,6 +794,255 @@ void LLDrawPoolAvatar::endSkinned() gGL.getTexUnit(0)->activate(); } +#if MESH_ENABLED +void LLDrawPoolAvatar::beginRiggedSimple() +{ + if (sShaderLevel > 0) + { + if (LLPipeline::sUnderWaterRender) + { + sVertexProgram = &gSkinnedObjectSimpleWaterProgram; + } + else + { + sVertexProgram = &gSkinnedObjectSimpleProgram; + } + } + else + { + if (LLPipeline::sUnderWaterRender) + { + sVertexProgram = &gObjectSimpleWaterProgram; + } + else + { + sVertexProgram = &gObjectSimpleProgram; + } + } + + if (sShaderLevel > 0 || gPipeline.canUseVertexShaders()) + { + sDiffuseChannel = 0; + sVertexProgram->bind(); + LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT); + } +} + +void LLDrawPoolAvatar::endRiggedSimple() +{ + LLVertexBuffer::unbind(); + if (sShaderLevel > 0 || gPipeline.canUseVertexShaders()) + { + sVertexProgram->unbind(); + sVertexProgram = NULL; + LLVertexBuffer::sWeight4Loc = -1; + } +} + +void LLDrawPoolAvatar::beginRiggedAlpha() +{ + beginRiggedSimple(); +} + +void LLDrawPoolAvatar::endRiggedAlpha() +{ + endRiggedSimple(); +} + + +void LLDrawPoolAvatar::beginRiggedFullbrightAlpha() +{ + beginRiggedFullbright(); +} + +void LLDrawPoolAvatar::endRiggedFullbrightAlpha() +{ + endRiggedFullbright(); +} + +void LLDrawPoolAvatar::beginRiggedGlow() +{ + beginRiggedFullbright(); +} + +void LLDrawPoolAvatar::endRiggedGlow() +{ + endRiggedFullbright(); +} + +void LLDrawPoolAvatar::beginRiggedFullbright() +{ + if (sShaderLevel > 0) + { + if (LLPipeline::sUnderWaterRender) + { + sVertexProgram = &gSkinnedObjectFullbrightWaterProgram; + } + else + { + sVertexProgram = &gSkinnedObjectFullbrightProgram; + } + } + else + { + if (LLPipeline::sUnderWaterRender) + { + sVertexProgram = &gObjectFullbrightWaterProgram; + } + else + { + sVertexProgram = &gObjectFullbrightProgram; + } + } + + if (sShaderLevel > 0 || gPipeline.canUseVertexShaders()) + { + sDiffuseChannel = 0; + sVertexProgram->bind(); + LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT); + } +} + +void LLDrawPoolAvatar::endRiggedFullbright() +{ + LLVertexBuffer::unbind(); + if (sShaderLevel > 0 || gPipeline.canUseVertexShaders()) + { + sVertexProgram->unbind(); + sVertexProgram = NULL; + LLVertexBuffer::sWeight4Loc = -1; + } +} + +void LLDrawPoolAvatar::beginRiggedShinySimple() +{ + if (sShaderLevel > 0) + { + if (LLPipeline::sUnderWaterRender) + { + sVertexProgram = &gSkinnedObjectShinySimpleWaterProgram; + } + else + { + sVertexProgram = &gSkinnedObjectShinySimpleProgram; + } + } + else + { + if (LLPipeline::sUnderWaterRender) + { + sVertexProgram = &gObjectShinyWaterProgram; + } + else + { + sVertexProgram = &gObjectShinyProgram; + } + } + + if (sShaderLevel > 0 || gPipeline.canUseVertexShaders()) + { + sVertexProgram->bind(); + LLDrawPoolBump::bindCubeMap(sVertexProgram, 2, sDiffuseChannel, cube_channel, false); + LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT); + } +} + +void LLDrawPoolAvatar::endRiggedShinySimple() +{ + LLVertexBuffer::unbind(); + if (sShaderLevel > 0 || gPipeline.canUseVertexShaders()) + { + LLDrawPoolBump::unbindCubeMap(sVertexProgram, 2, sDiffuseChannel, cube_channel, false); + sVertexProgram->unbind(); + sVertexProgram = NULL; + LLVertexBuffer::sWeight4Loc = -1; + } +} + +void LLDrawPoolAvatar::beginRiggedFullbrightShiny() +{ + if (sShaderLevel > 0) + { + if (LLPipeline::sUnderWaterRender) + { + sVertexProgram = &gSkinnedObjectFullbrightShinyWaterProgram; + } + else + { + sVertexProgram = &gSkinnedObjectFullbrightShinyProgram; + } + } + else + { + if (LLPipeline::sUnderWaterRender) + { + sVertexProgram = &gObjectFullbrightShinyWaterProgram; + } + else + { + sVertexProgram = &gObjectFullbrightShinyProgram; + } + } + + + if (sShaderLevel > 0 || gPipeline.canUseVertexShaders()) + { + sVertexProgram->bind(); + LLDrawPoolBump::bindCubeMap(sVertexProgram, 2, sDiffuseChannel, cube_channel, false); + LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT); + } +} + +void LLDrawPoolAvatar::endRiggedFullbrightShiny() +{ + LLVertexBuffer::unbind(); + if (sShaderLevel > 0 || gPipeline.canUseVertexShaders()) + { + LLDrawPoolBump::unbindCubeMap(sVertexProgram, 2, sDiffuseChannel, cube_channel, false); + sVertexProgram->unbind(); + sVertexProgram = NULL; + LLVertexBuffer::sWeight4Loc = -1; + } +} + + +void LLDrawPoolAvatar::beginDeferredRiggedSimple() +{ + sVertexProgram = &gDeferredSkinnedDiffuseProgram; + sDiffuseChannel = 0; + sVertexProgram->bind(); + LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT); +} + +void LLDrawPoolAvatar::endDeferredRiggedSimple() +{ + LLVertexBuffer::unbind(); + sVertexProgram->unbind(); + LLVertexBuffer::sWeight4Loc = -1; + sVertexProgram = NULL; +} + +void LLDrawPoolAvatar::beginDeferredRiggedBump() +{ + sVertexProgram = &gDeferredSkinnedBumpProgram; + sVertexProgram->bind(); + normal_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::BUMP_MAP); + sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP); + LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT); +} + +void LLDrawPoolAvatar::endDeferredRiggedBump() +{ + LLVertexBuffer::unbind(); + sVertexProgram->disableTexture(LLViewerShaderMgr::BUMP_MAP); + sVertexProgram->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP); + sVertexProgram->unbind(); + LLVertexBuffer::sWeight4Loc = -1; + normal_channel = -1; + sDiffuseChannel = 0; + sVertexProgram = NULL; +} +#endif //MESH_ENABLED void LLDrawPoolAvatar::beginDeferredSkinned() { sShaderLevel = mVertexShaderLevel; @@ -583,6 +1052,7 @@ void LLDrawPoolAvatar::beginDeferredSkinned() sVertexProgram->bind(); + sDiffuseChannel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP); enable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]); gGL.getTexUnit(0)->activate(); @@ -595,6 +1065,8 @@ void LLDrawPoolAvatar::endDeferredSkinned() disable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]); sVertexProgram->unbind(); + sVertexProgram->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP); + sShaderLevel = mVertexShaderLevel; gGL.getTexUnit(0)->activate(); @@ -654,7 +1126,7 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass) if (pass==1 && (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES) || LLViewerPartSim::getMaxPartCount() <= 0)) { // debug code to draw a sphere in place of avatar - gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sWhiteImagep.get()); + gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sWhiteImagep); gGL.setColorMask(true, true); LLVector3 pos = avatarp->getPositionAgent(); gGL.color4f(1.0f, 1.0f, 1.0f, 0.7f); @@ -697,7 +1169,7 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass) if (impostor) { - if (LLPipeline::sRenderDeferred && avatarp->mImpostor.isComplete()) + if (LLPipeline::sRenderDeferred && !LLPipeline::sReflectionRender && avatarp->mImpostor.isComplete()) { if (normal_channel > -1) { @@ -708,7 +1180,7 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass) avatarp->mImpostor.bindTexture(1, specular_channel); } } - avatarp->renderImpostor(LLColor4U(255,255,255,255), diffuse_channel); + avatarp->renderImpostor(LLColor4U(255,255,255,255), sDiffuseChannel); } else if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FOOT_SHADOWS) && !LLPipeline::sRenderDeferred) { @@ -729,6 +1201,90 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass) return; } +#if MESH_ENABLED + if (pass == 3) + { + if (is_deferred_render) + { + renderDeferredRiggedSimple(avatarp); + } + else + { + renderRiggedSimple(avatarp); + } + return; + } + + if (pass == 4) + { + if (is_deferred_render) + { + renderDeferredRiggedBump(avatarp); + } + else + { + renderRiggedFullbright(avatarp); + } + + return; + } + + if (pass == 5) + { + renderRiggedShinySimple(avatarp); + return; + } + + if (pass == 6) + { + renderRiggedFullbrightShiny(avatarp); + return; + } + + if (pass >= 7 && pass < 9) + { + LLGLEnable blend(GL_BLEND); + + gGL.setColorMask(true, true); + gGL.blendFunc(LLRender::BF_SOURCE_ALPHA, + LLRender::BF_ONE_MINUS_SOURCE_ALPHA, + LLRender::BF_ZERO, + LLRender::BF_ONE_MINUS_SOURCE_ALPHA); + + + if (pass == 7) + { + renderRiggedAlpha(avatarp); + return; + } + + if (pass == 8) + { + renderRiggedFullbrightAlpha(avatarp); + return; + } + } + + if (pass == 9) + { + LLGLEnable blend(GL_BLEND); + LLGLDisable test(GL_ALPHA_TEST); + gGL.flush(); + + LLGLEnable polyOffset(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(-1.0f, -1.0f); + gGL.setSceneBlendType(LLRender::BT_ADD); + + LLGLDepthTest depth(GL_TRUE, GL_FALSE); + gGL.setColorMask(false, true); + + renderRiggedGlow(avatarp); + gGL.setColorMask(true, false); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + return; + } +#endif //MESH_ENABLED + if (sShaderLevel > 0) { gAvatarMatrixParam = sVertexProgram->mUniform[LLViewerShaderMgr::AVATAR_MATRIX]; @@ -765,6 +1321,308 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass) } } +#if MESH_ENABLED +void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace* face, const LLMeshSkinInfo* skin, LLVolume* volume, const LLVolumeFace& vol_face) +{ + LLVector4a* weight = vol_face.mWeights; + if (!weight) + { + return; + } + + LLVertexBuffer* buffer = face->getVertexBuffer(); + + U32 data_mask = face->getRiggedVertexBufferDataMask(); + + if (!buffer || + buffer->getTypeMask() != data_mask || + buffer->getRequestedVerts() != vol_face.mNumVertices) + { + face->setGeomIndex(0); + face->setIndicesIndex(0); + face->setSize(vol_face.mNumVertices, vol_face.mNumIndices, true); + + + if (sShaderLevel > 0) + { + buffer = new LLVertexBuffer(data_mask, GL_DYNAMIC_DRAW_ARB); + } + else + { + buffer = new LLVertexBuffer(data_mask, GL_STREAM_DRAW_ARB); + } + + buffer->allocateBuffer(face->getGeomCount(), face->getIndicesCount(), true); + + face->setVertexBuffer(buffer); + + U16 offset = 0; + + LLMatrix4 mat_vert = skin->mBindShapeMatrix; + glh::matrix4f m((F32*) mat_vert.mMatrix); + m = m.inverse().transpose(); + + F32 mat3[] = + { m.m[0], m.m[1], m.m[2], + m.m[4], m.m[5], m.m[6], + m.m[8], m.m[9], m.m[10] }; + + LLMatrix3 mat_normal(mat3); + + face->getGeometryVolume(*volume, face->getTEOffset(), mat_vert, mat_normal, offset, true); + } + + if (sShaderLevel <= 0 && face->mLastSkinTime < avatar->getLastSkinTime()) + { //perform software vertex skinning for this face + LLStrider position; + LLStrider normal; + + bool has_normal = buffer->hasDataType(LLVertexBuffer::TYPE_NORMAL); + buffer->getVertexStrider(position); + + if (has_normal) + { + buffer->getNormalStrider(normal); + } + + LLVector4a* pos = (LLVector4a*) position.get(); + + LLVector4a* norm = has_normal ? (LLVector4a*) normal.get() : NULL; + + //build matrix palette + LLMatrix4a mp[64]; + LLMatrix4* mat = (LLMatrix4*) mp; + + for (U32 j = 0; j < skin->mJointNames.size(); ++j) + { + LLJoint* joint = avatar->getJoint(skin->mJointNames[j]); + if (joint) + { + mat[j] = skin->mInvBindMatrix[j]; + mat[j] *= joint->getWorldMatrix(); + } + } + + LLMatrix4a bind_shape_matrix; + bind_shape_matrix.loadu(skin->mBindShapeMatrix); + + for (U32 j = 0; j < buffer->getRequestedVerts(); ++j) + { + LLMatrix4a final_mat; + final_mat.clear(); + + S32 idx[4]; + + LLVector4 wght; + + F32 scale = 0.f; + for (U32 k = 0; k < 4; k++) + { + F32 w = weight[j][k]; + + idx[k] = llclamp((S32) floorf(w), 0, 63); + wght[k] = w - floorf(w); + scale += wght[k]; + } + + wght *= 1.f/scale; + + for (U32 k = 0; k < 4; k++) + { + F32 w = wght[k]; + + LLMatrix4a src; + src.setMul(mp[idx[k]], w); + + final_mat.add(src); + } + + + LLVector4a& v = vol_face.mPositions[j]; + LLVector4a t; + LLVector4a dst; + bind_shape_matrix.affineTransform(v, t); + final_mat.affineTransform(t, dst); + pos[j] = dst; + + if (norm) + { + LLVector4a& n = vol_face.mNormals[j]; + bind_shape_matrix.rotate(n, t); + final_mat.rotate(t, dst); + norm[j] = dst; + } + } + } +} + +void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow) +{ + if (avatar->isSelf() && !gAgent.needsRenderAvatar()) + { + return; + } + + stop_glerror(); + + for (U32 i = 0; i < mRiggedFace[type].size(); ++i) + { + LLFace* face = mRiggedFace[type][i]; + LLDrawable* drawable = face->getDrawable(); + if (!drawable) + { + continue; + } + + LLVOVolume* vobj = drawable->getVOVolume(); + + if (!vobj) + { + continue; + } + + LLVolume* volume = vobj->getVolume(); + S32 te = face->getTEOffset(); + + if (!volume || volume->getNumVolumeFaces() <= te) + { + continue; + } + + LLUUID mesh_id = volume->getParams().getSculptID(); + if (mesh_id.isNull()) + { + continue; + } + + const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(mesh_id); + if (!skin) + { + continue; + } + + stop_glerror(); + + const LLVolumeFace& vol_face = volume->getVolumeFace(te); + updateRiggedFaceVertexBuffer(avatar, face, skin, volume, vol_face); + + stop_glerror(); + + U32 data_mask = LLFace::getRiggedDataMask(type); + + LLVertexBuffer* buff = face->getVertexBuffer(); + + if (buff) + { + if (sShaderLevel > 0) + { //upload matrix palette to shader + LLMatrix4 mat[64]; + + for (U32 i = 0; i < skin->mJointNames.size(); ++i) + { + LLJoint* joint = avatar->getJoint(skin->mJointNames[i]); + if (joint) + { + mat[i] = skin->mInvBindMatrix[i]; + mat[i] *= joint->getWorldMatrix(); + } + } + + stop_glerror(); + + LLDrawPoolAvatar::sVertexProgram->uniformMatrix4fv("matrixPalette", + skin->mJointNames.size(), + FALSE, + (GLfloat*) mat[0].mMatrix); + + stop_glerror(); + } + else + { + data_mask &= ~LLVertexBuffer::MAP_WEIGHT4; + } + + buff->setBuffer(data_mask); + + U16 start = face->getGeomStart(); + U16 end = start + face->getGeomCount()-1; + S32 offset = face->getIndicesStart(); + U32 count = face->getIndicesCount(); + + if (glow) + { + glColor4f(0,0,0,face->getTextureEntry()->getGlow()); + } + + gGL.getTexUnit(sDiffuseChannel)->bind(face->getTexture()); + if (normal_channel > -1) + { + LLDrawPoolBump::bindBumpMap(face, normal_channel); + } + + if (face->mTextureMatrix) + { + glMatrixMode(GL_TEXTURE); + glLoadMatrixf((F32*) face->mTextureMatrix->mMatrix); + buff->drawRange(LLRender::TRIANGLES, start, end, count, offset); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + } + else + { + buff->drawRange(LLRender::TRIANGLES, start, end, count, offset); + } + } + } +} + +void LLDrawPoolAvatar::renderDeferredRiggedSimple(LLVOAvatar* avatar) +{ + renderRigged(avatar, RIGGED_DEFERRED_SIMPLE); +} + +void LLDrawPoolAvatar::renderDeferredRiggedBump(LLVOAvatar* avatar) +{ + renderRigged(avatar, RIGGED_DEFERRED_BUMP); +} + +void LLDrawPoolAvatar::renderRiggedSimple(LLVOAvatar* avatar) +{ + renderRigged(avatar, RIGGED_SIMPLE); +} + +void LLDrawPoolAvatar::renderRiggedFullbright(LLVOAvatar* avatar) +{ + renderRigged(avatar, RIGGED_FULLBRIGHT); +} + + +void LLDrawPoolAvatar::renderRiggedShinySimple(LLVOAvatar* avatar) +{ + renderRigged(avatar, RIGGED_SHINY); +} + +void LLDrawPoolAvatar::renderRiggedFullbrightShiny(LLVOAvatar* avatar) +{ + renderRigged(avatar, RIGGED_FULLBRIGHT_SHINY); +} + +void LLDrawPoolAvatar::renderRiggedAlpha(LLVOAvatar* avatar) +{ + renderRigged(avatar, RIGGED_ALPHA); +} + +void LLDrawPoolAvatar::renderRiggedFullbrightAlpha(LLVOAvatar* avatar) +{ + renderRigged(avatar, RIGGED_FULLBRIGHT_ALPHA); +} + +void LLDrawPoolAvatar::renderRiggedGlow(LLVOAvatar* avatar) +{ + renderRigged(avatar, RIGGED_GLOW, true); +} +#endif //MESH_ENABLED + //----------------------------------------------------------------------------- // renderForSelect() //----------------------------------------------------------------------------- @@ -868,6 +1726,52 @@ LLColor3 LLDrawPoolAvatar::getDebugColor() const return LLColor3(0.f, 1.f, 0.f); } +#if MESH_ENABLED +void LLDrawPoolAvatar::addRiggedFace(LLFace* facep, U32 type) +{ + if (type >= NUM_RIGGED_PASSES) + { + llerrs << "Invalid rigged face type." << llendl; + } + + if (facep->getRiggedIndex(type) != -1) + { + llerrs << "Tried to add a rigged face that's referenced elsewhere." << llendl; + } + + facep->setRiggedIndex(type, mRiggedFace[type].size()); + facep->setPool(this); + mRiggedFace[type].push_back(facep); +} + +void LLDrawPoolAvatar::removeRiggedFace(LLFace* facep) +{ + facep->setPool(NULL); + + for (U32 i = 0; i < NUM_RIGGED_PASSES; ++i) + { + S32 index = facep->getRiggedIndex(i); + + if (index > -1) + { + if (mRiggedFace[i].size() > index && mRiggedFace[i][index] == facep) + { + facep->setRiggedIndex(i,-1); + mRiggedFace[i].erase(mRiggedFace[i].begin()+index); + for (U32 j = index; j < mRiggedFace[i].size(); ++j) + { //bump indexes down for faces referenced after erased face + mRiggedFace[i][j]->setRiggedIndex(i, j); + } + } + else + { + llerrs << "Face reference data corrupt for rigged type " << i << llendl; + } + } + } +} +#endif //MESH_ENABLED + LLVertexBufferAvatar::LLVertexBufferAvatar() : LLVertexBuffer(sDataMask, //LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_AVATAR) > 0 ? @@ -882,22 +1786,22 @@ void LLVertexBufferAvatar::setupVertexBuffer(U32 data_mask) const { if (sRenderingSkinned) { - volatile U8* base = useVBOs() ? NULL : mMappedData; + volatile U8* base = useVBOs() ? (U8*) mAlignedOffset : mMappedData; glVertexPointer(3,GL_FLOAT, mStride, (void*)(base + 0)); glNormalPointer(GL_FLOAT, mStride, (void*)(base + mOffsets[TYPE_NORMAL])); glTexCoordPointer(2,GL_FLOAT, mStride, (void*)(base + mOffsets[TYPE_TEXCOORD0])); - set_vertex_weights(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT], mStride, (F32*)(base + mOffsets[TYPE_WEIGHT])); + set_vertex_weights(LLDrawPoolAvatar::sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT], mStride, (F32*)(base + mOffsets[TYPE_WEIGHT])); if (sShaderLevel >= LLDrawPoolAvatar::SHADER_LEVEL_BUMP) { - set_binormals(sVertexProgram->mAttribute[LLViewerShaderMgr::BINORMAL], mStride, (LLVector3*)(base + mOffsets[TYPE_BINORMAL])); + set_binormals(LLDrawPoolAvatar::sVertexProgram->mAttribute[LLViewerShaderMgr::BINORMAL], mStride, (LLVector3*)(base + mOffsets[TYPE_BINORMAL])); } if (sShaderLevel >= LLDrawPoolAvatar::SHADER_LEVEL_CLOTH) { - set_vertex_clothing_weights(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_CLOTHING], mStride, (LLVector4*)(base + mOffsets[TYPE_CLOTHWEIGHT])); + set_vertex_clothing_weights(LLDrawPoolAvatar::sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_CLOTHING], mStride, (LLVector4*)(base + mOffsets[TYPE_CLOTHWEIGHT])); } } else diff --git a/indra/newview/lldrawpoolavatar.h b/indra/newview/lldrawpoolavatar.h index d24726052..8cef71882 100644 --- a/indra/newview/lldrawpoolavatar.h +++ b/indra/newview/lldrawpoolavatar.h @@ -36,6 +36,12 @@ #include "lldrawpool.h" class LLVOAvatar; +class LLGLSLShader; +class LLFace; +class LLMeshSkinInfo; +class LLVolume; +class LLVolumeFace; + class LLDrawPoolAvatar : public LLFacePool { @@ -102,6 +108,105 @@ public: void endDeferredImpostor(); void endDeferredRigid(); void endDeferredSkinned(); + + void beginPostDeferredAlpha(); + void endPostDeferredAlpha(); + +#if MESH_ENABLED + void beginRiggedSimple(); + void beginRiggedFullbright(); + void beginRiggedFullbrightShiny(); + void beginRiggedShinySimple(); + void beginRiggedAlpha(); + void beginRiggedFullbrightAlpha(); + void beginRiggedGlow(); + void beginDeferredRiggedAlpha(); + + void endRiggedSimple(); + void endRiggedFullbright(); + void endRiggedFullbrightShiny(); + void endRiggedShinySimple(); + void endRiggedAlpha(); + void endRiggedFullbrightAlpha(); + void endRiggedGlow(); + void endDeferredRiggedAlpha(); + + void beginDeferredRiggedSimple(); + void beginDeferredRiggedBump(); + + void endDeferredRiggedSimple(); + void endDeferredRiggedBump(); + + void updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, + LLFace* facep, + const LLMeshSkinInfo* skin, + LLVolume* volume, + const LLVolumeFace& vol_face); + + void renderRigged(LLVOAvatar* avatar, U32 type, bool glow = false); + void renderRiggedSimple(LLVOAvatar* avatar); + void renderRiggedAlpha(LLVOAvatar* avatar); + void renderRiggedFullbrightAlpha(LLVOAvatar* avatar); + void renderRiggedFullbright(LLVOAvatar* avatar); + void renderRiggedShinySimple(LLVOAvatar* avatar); + void renderRiggedFullbrightShiny(LLVOAvatar* avatar); + void renderRiggedGlow(LLVOAvatar* avatar); + void renderDeferredRiggedSimple(LLVOAvatar* avatar); + void renderDeferredRiggedBump(LLVOAvatar* avatar); + + + typedef enum + { + RIGGED_SIMPLE = 0, + RIGGED_FULLBRIGHT, + RIGGED_SHINY, + RIGGED_FULLBRIGHT_SHINY, + RIGGED_GLOW, + RIGGED_ALPHA, + RIGGED_FULLBRIGHT_ALPHA, + RIGGED_DEFERRED_BUMP, + RIGGED_DEFERRED_SIMPLE, + NUM_RIGGED_PASSES, + RIGGED_UNKNOWN, + } eRiggedPass; + + typedef enum + { + RIGGED_SIMPLE_MASK = LLVertexBuffer::MAP_VERTEX | + LLVertexBuffer::MAP_NORMAL | + LLVertexBuffer::MAP_TEXCOORD0 | + LLVertexBuffer::MAP_COLOR | + LLVertexBuffer::MAP_WEIGHT4, + RIGGED_FULLBRIGHT_MASK = LLVertexBuffer::MAP_VERTEX | + LLVertexBuffer::MAP_TEXCOORD0 | + LLVertexBuffer::MAP_COLOR | + LLVertexBuffer::MAP_WEIGHT4, + RIGGED_SHINY_MASK = RIGGED_SIMPLE_MASK, + RIGGED_FULLBRIGHT_SHINY_MASK = RIGGED_SIMPLE_MASK, + RIGGED_GLOW_MASK = LLVertexBuffer::MAP_VERTEX | + LLVertexBuffer::MAP_TEXCOORD0 | + LLVertexBuffer::MAP_WEIGHT4, + RIGGED_ALPHA_MASK = RIGGED_SIMPLE_MASK, + RIGGED_FULLBRIGHT_ALPHA_MASK = RIGGED_FULLBRIGHT_MASK, + RIGGED_DEFERRED_BUMP_MASK = LLVertexBuffer::MAP_VERTEX | + LLVertexBuffer::MAP_NORMAL | + LLVertexBuffer::MAP_TEXCOORD0 | + LLVertexBuffer::MAP_BINORMAL | + LLVertexBuffer::MAP_COLOR | + LLVertexBuffer::MAP_WEIGHT4, + RIGGED_DEFERRED_SIMPLE_MASK = LLVertexBuffer::MAP_VERTEX | + LLVertexBuffer::MAP_NORMAL | + LLVertexBuffer::MAP_TEXCOORD0 | + LLVertexBuffer::MAP_COLOR | + LLVertexBuffer::MAP_WEIGHT4, + } eRiggedDataMask; + + void addRiggedFace(LLFace* facep, U32 type); + void removeRiggedFace(LLFace* facep); + + std::vector mRiggedFace[NUM_RIGGED_PASSES]; + +#endif //MESH_ENABLED /*virtual*/ LLViewerTexture *getDebugTexture(); /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display @@ -110,6 +215,9 @@ public: static BOOL sSkipOpaque; static BOOL sSkipTransparent; + static S32 sDiffuseChannel; + + static LLGLSLShader* sVertexProgram; }; class LLVertexBufferAvatar : public LLVertexBuffer diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 8742b35ae..1893621c8 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -205,7 +205,17 @@ void LLFace::destroy() if (mDrawPoolp) { - mDrawPoolp->removeFace(this); +#if MESH_ENABLED + if (this->isState(LLFace::RIGGED) && mDrawPoolp->getType() == LLDrawPool::POOL_AVATAR) + { + ((LLDrawPoolAvatar*) mDrawPoolp)->removeRiggedFace(this); + } + else +#endif //MESH_ENABLED + { + mDrawPoolp->removeFace(this); + } + mDrawPoolp = NULL; } @@ -550,11 +560,40 @@ void LLFace::renderSelected(LLViewerTexture *imagep, const LLColor4& color) } glColor4fv(color.mV); - mVertexBuffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0); -#if !LL_RELEASE_FOR_DOWNLOAD - LLGLState::checkClientArrays("", LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0); -#endif - mVertexBuffer->draw(LLRender::TRIANGLES, mIndicesCount, mIndicesIndex); +#if MESH_ENABLED + if (mDrawablep->isState(LLDrawable::RIGGED)) + { + LLVOVolume* volume = mDrawablep->getVOVolume(); + if (volume) + { + LLRiggedVolume* rigged = volume->getRiggedVolume(); + if (rigged) + { + LLGLEnable offset(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(-1.f, -1.f); + glMultMatrixf((F32*) volume->getRelativeXform().mMatrix); + const LLVolumeFace& vol_face = rigged->getVolumeFace(getTEOffset()); + LLVertexBuffer::unbind(); + glVertexPointer(3, GL_FLOAT, 16, vol_face.mPositions); + if (vol_face.mTexCoords) + { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 8, vol_face.mTexCoords); + } + glDrawElements(GL_TRIANGLES, vol_face.mNumIndices, GL_UNSIGNED_SHORT, vol_face.mIndices); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + } + } + else +#endif //MESH_ENABLED + { + mVertexBuffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0); + #if !LL_RELEASE_FOR_DOWNLOAD + LLGLState::checkClientArrays("", LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0); + #endif + mVertexBuffer->draw(LLRender::TRIANGLES, mIndicesCount, mIndicesIndex); + } gGL.popMatrix(); } @@ -733,7 +772,11 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f, LLMemType mt1(LLMemType::MTYPE_DRAWABLE); //get bounding box - if (mDrawablep->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION)) + if (mDrawablep->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION +#if MESH_ENABLED + | LLDrawable::REBUILD_RIGGED +#endif //MESH_ENABLED + )) { //VECTORIZE THIS LLMatrix4a mat_vert; @@ -1096,6 +1139,9 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, LLStrider colors; LLStrider binormals; LLStrider indicesp; +#if MESH_ENABLED + LLStrider weights; +#endif //MESH_ENABLED BOOL full_rebuild = force_rebuild || mDrawablep->isState(LLDrawable::REBUILD_VOLUME); @@ -1115,7 +1161,9 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, bool rebuild_tcoord = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_TCOORD); bool rebuild_normal = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_NORMAL); bool rebuild_binormal = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_BINORMAL); - +#if MESH_ENABLED + bool rebuild_weights = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_WEIGHT4); +#endif //MESH_ENABLED const LLTextureEntry *tep = mVObjp->getTE(f); if (!tep) rebuild_color = FALSE; // can't get color when tep is NULL @@ -1133,6 +1181,12 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, { mVertexBuffer->getBinormalStrider(binormals, mGeomIndex); } +#if MESH_ENABLED + if (rebuild_weights) + { + mVertexBuffer->getWeight4Strider(weights, mGeomIndex); + } +#endif //MESH_ENABLED if (rebuild_tcoord) { @@ -1538,6 +1592,15 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, } } +#if MESH_ENABLED + if (rebuild_weights && vf.mWeights) + { + for (S32 i = 0; i < num_vertices; i++) + { + weights[i].set(weights.getF32ptr()); + } + } +#endif //MESH_ENABLED if (rebuild_color) { @@ -1959,3 +2022,68 @@ void LLFace::clearVertexBuffer() mLastVertexBuffer = NULL; } +#if MESH_ENABLED +//static +U32 LLFace::getRiggedDataMask(U32 type) +{ + static const U32 rigged_data_mask[] = { + LLDrawPoolAvatar::RIGGED_SIMPLE_MASK, + LLDrawPoolAvatar::RIGGED_FULLBRIGHT_MASK, + LLDrawPoolAvatar::RIGGED_SHINY_MASK, + LLDrawPoolAvatar::RIGGED_FULLBRIGHT_SHINY_MASK, + LLDrawPoolAvatar::RIGGED_GLOW_MASK, + LLDrawPoolAvatar::RIGGED_ALPHA_MASK, + LLDrawPoolAvatar::RIGGED_FULLBRIGHT_ALPHA_MASK, + LLDrawPoolAvatar::RIGGED_DEFERRED_BUMP_MASK, + LLDrawPoolAvatar::RIGGED_DEFERRED_SIMPLE_MASK, + }; + + llassert(type < sizeof(rigged_data_mask)/sizeof(U32)); + + return rigged_data_mask[type]; +} + +U32 LLFace::getRiggedVertexBufferDataMask() const +{ + U32 data_mask = 0; + for (U32 i = 0; i < mRiggedIndex.size(); ++i) + { + if (mRiggedIndex[i] > -1) + { + data_mask |= LLFace::getRiggedDataMask(i); + } + } + + return data_mask; +} + +S32 LLFace::getRiggedIndex(U32 type) const +{ + if (mRiggedIndex.empty()) + { + return -1; + } + + llassert(type < mRiggedIndex.size()); + + return mRiggedIndex[type]; +} + +void LLFace::setRiggedIndex(U32 type, S32 index) +{ + if (mRiggedIndex.empty()) + { + mRiggedIndex.resize(LLDrawPoolAvatar::NUM_RIGGED_PASSES); + for (U32 i = 0; i < mRiggedIndex.size(); ++i) + { + mRiggedIndex[i] = -1; + } + } + + llassert(type < mRiggedIndex.size()); + + mRiggedIndex[type] = index; +} + + +#endif //MESH_ENABLED \ No newline at end of file diff --git a/indra/newview/llface.h b/indra/newview/llface.h index c6a2728b4..7abaaffa3 100644 --- a/indra/newview/llface.h +++ b/indra/newview/llface.h @@ -83,6 +83,9 @@ public: HUD_RENDER = 0x0008, USE_FACE_COLOR = 0x0010, TEXTURE_ANIM = 0x0020, +#if MESH_ENABLED + RIGGED = 0x0040, +#endif //MESH_ENABLED }; static void initClass(); @@ -212,6 +215,13 @@ public: void setVertexBuffer(LLVertexBuffer* buffer); void clearVertexBuffer(); //sets mVertexBuffer and mLastVertexBuffer to NULL LLVertexBuffer* getVertexBuffer() const { return mVertexBuffer; } +#if MESH_ENABLED + U32 getRiggedVertexBufferDataMask() const; + S32 getRiggedIndex(U32 type) const; + void setRiggedIndex(U32 type, S32 index); + + static U32 getRiggedDataMask(U32 type); +#endif //MESH_ENABLED public: //aligned members LLVector4a mExtents[2]; @@ -264,6 +274,10 @@ private: S32 mTEOffset; S32 mReferenceIndex; +#if MESH_ENABLED + std::vector mRiggedIndex; +#endif //MESH_ENABLED + F32 mVSize; F32 mPixelArea; diff --git a/indra/newview/llmanipscale.cpp b/indra/newview/llmanipscale.cpp index bec6a49f4..acb37e897 100644 --- a/indra/newview/llmanipscale.cpp +++ b/indra/newview/llmanipscale.cpp @@ -91,6 +91,28 @@ const LLManip::EManipPart MANIPULATOR_IDS[NUM_MANIPULATORS] = }; +F32 get_default_max_prim_scale(bool is_flora) +{ + // a bit of a hack, but if it's foilage, we don't want to use the + // new larger scale which would result in giant trees and grass +#if MESH_ENABLED + if (gSavedSettings.getBOOL("MeshEnabled") && + gAgent.getRegion() && + !gAgent.getRegion()->getCapability("GetMesh").empty() && + !gAgent.getRegion()->getCapability("ObjectAdd").empty() && + !is_flora) + { + return DEFAULT_MAX_PRIM_SCALE; + } + else + { + return DEFAULT_MAX_PRIM_SCALE_NO_MESH; + } +#endif //MESH_ENABLED +#if !MESH_ENABLED + return DEFAULT_MAX_PRIM_SCALE; +#endif //!MESH_ENABLED +} // static void LLManipScale::setUniform(BOOL b) @@ -953,8 +975,8 @@ void LLManipScale::dragCorner( S32 x, S32 y ) mInSnapRegime = FALSE; } - F32 max_scale_factor = DEFAULT_MAX_PRIM_SCALE / MIN_PRIM_SCALE; - F32 min_scale_factor = MIN_PRIM_SCALE / DEFAULT_MAX_PRIM_SCALE; + F32 max_scale_factor = get_default_max_prim_scale() / MIN_PRIM_SCALE; + F32 min_scale_factor = MIN_PRIM_SCALE / get_default_max_prim_scale(); // find max and min scale factors that will make biggest object hit max absolute scale and smallest object hit min absolute scale for (LLObjectSelection::iterator iter = mObjectSelection->begin(); @@ -966,7 +988,7 @@ void LLManipScale::dragCorner( S32 x, S32 y ) { const LLVector3& scale = selectNode->mSavedScale; - F32 cur_max_scale_factor = llmin( DEFAULT_MAX_PRIM_SCALE / scale.mV[VX], DEFAULT_MAX_PRIM_SCALE / scale.mV[VY], DEFAULT_MAX_PRIM_SCALE / scale.mV[VZ] ); + F32 cur_max_scale_factor = llmin( get_default_max_prim_scale(LLPickInfo::isFlora(cur)) / scale.mV[VX], get_default_max_prim_scale(LLPickInfo::isFlora(cur)) / scale.mV[VY], get_default_max_prim_scale(LLPickInfo::isFlora(cur)) / scale.mV[VZ] ); max_scale_factor = llmin( max_scale_factor, cur_max_scale_factor ); F32 cur_min_scale_factor = llmax( MIN_PRIM_SCALE / scale.mV[VX], MIN_PRIM_SCALE / scale.mV[VY], MIN_PRIM_SCALE / scale.mV[VZ] ); @@ -1263,7 +1285,7 @@ void LLManipScale::stretchFace( const LLVector3& drag_start_agent, const LLVecto F32 denom = axis * dir_local; F32 desired_delta_size = is_approx_zero(denom) ? 0.f : (delta_local_mag / denom); // in meters - F32 desired_scale = llclamp(selectNode->mSavedScale.mV[axis_index] + desired_delta_size, MIN_PRIM_SCALE, DEFAULT_MAX_PRIM_SCALE); + F32 desired_scale = llclamp(selectNode->mSavedScale.mV[axis_index] + desired_delta_size, MIN_PRIM_SCALE, get_default_max_prim_scale(LLPickInfo::isFlora(cur))); // propagate scale constraint back to position offset desired_delta_size = desired_scale - selectNode->mSavedScale.mV[axis_index]; // propagate constraint back to position @@ -1964,7 +1986,7 @@ F32 LLManipScale::partToMaxScale( S32 part, const LLBBox &bbox ) const max_extent = bbox_extents.mV[i]; } } - max_scale_factor = bbox_extents.magVec() * DEFAULT_MAX_PRIM_SCALE / max_extent; + max_scale_factor = bbox_extents.magVec() * get_default_max_prim_scale() / max_extent; if (getUniform()) { @@ -1979,7 +2001,7 @@ F32 LLManipScale::partToMinScale( S32 part, const LLBBox &bbox ) const { LLVector3 bbox_extents = unitVectorToLocalBBoxExtent( partToUnitVector( part ), bbox ); bbox_extents.abs(); - F32 min_extent = DEFAULT_MAX_PRIM_SCALE; + F32 min_extent = get_default_max_prim_scale(); for (U32 i = VX; i <= VZ; i++) { if (bbox_extents.mV[i] > 0.f && bbox_extents.mV[i] < min_extent) diff --git a/indra/newview/llmanipscale.h b/indra/newview/llmanipscale.h index 1de045989..2de75f2ce 100644 --- a/indra/newview/llmanipscale.h +++ b/indra/newview/llmanipscale.h @@ -45,6 +45,9 @@ #include "llviewerobject.h" #include "llbbox.h" + +F32 get_default_max_prim_scale(bool is_flora = false); + class LLToolComposite; class LLColor4; diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp new file mode 100644 index 000000000..55145c6ad --- /dev/null +++ b/indra/newview/llmeshrepository.cpp @@ -0,0 +1,3870 @@ +/** + * @file llmeshrepository.cpp + * @brief Mesh repository implementation. + * + * $LicenseInfo:firstyear=2005&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 "apr_pools.h" +#include "apr_dso.h" +#include "llhttpstatuscodes.h" +#include "llmeshrepository.h" + +#include "llagent.h" +#include "llappviewer.h" +#include "llbufferstream.h" +#include "llcurl.h" +#include "lldatapacker.h" +#include "llfasttimer.h" +#include "llfloatermodelpreview.h" +#include "llfloaterperms.h" +#include "lleconomy.h" +#include "llimagej2c.h" +#include "llhost.h" +#include "llnotificationsutil.h" +#include "llsd.h" +#include "llsdutil_math.h" +#include "llsdserialize.h" +#include "llthread.h" +#include "llvfile.h" +#include "llviewercontrol.h" +#include "llviewermenufile.h" +#include "llviewerobjectlist.h" +#include "llviewerregion.h" +#include "llviewertexturelist.h" +#include "llvolume.h" +#include "llvolumemgr.h" +#include "llvovolume.h" +#include "llworld.h" +#include "material_codes.h" +#include "pipeline.h" +#include "llinventorymodel.h" +#include "llfoldertype.h" + +#ifndef LL_WINDOWS +#include "netdb.h" +#endif + +#include + +LLFastTimer::DeclareTimer FTM_MESH_UPDATE("Mesh Update"); +LLFastTimer::DeclareTimer FTM_LOAD_MESH("Load Mesh"); + +LLMeshRepository gMeshRepo; + +const U32 MAX_MESH_REQUESTS_PER_SECOND = 100; + +U32 LLMeshRepository::sBytesReceived = 0; +U32 LLMeshRepository::sHTTPRequestCount = 0; +U32 LLMeshRepository::sHTTPRetryCount = 0; +U32 LLMeshRepository::sCacheBytesRead = 0; +U32 LLMeshRepository::sCacheBytesWritten = 0; +U32 LLMeshRepository::sPeakKbps = 0; + + +const U32 MAX_TEXTURE_UPLOAD_RETRIES = 5; + +void dumpLLSDToFile(const LLSD& content, std::string filename); + +std::string header_lod[] = +{ + "lowest_lod", + "low_lod", + "medium_lod", + "high_lod" +}; + + +//get the number of bytes resident in memory for given volume +U32 get_volume_memory_size(const LLVolume* volume) +{ + U32 indices = 0; + U32 vertices = 0; + + for (U32 i = 0; i < volume->getNumVolumeFaces(); ++i) + { + const LLVolumeFace& face = volume->getVolumeFace(i); + indices += face.mNumIndices; + vertices += face.mNumVertices; + } + + + return indices*2+vertices*11+sizeof(LLVolume)+sizeof(LLVolumeFace)*volume->getNumVolumeFaces(); +} + +void get_vertex_buffer_from_mesh(LLCDMeshData& mesh, LLModel::PhysicsMesh& res, F32 scale = 1.f) +{ + res.mPositions.clear(); + res.mNormals.clear(); + + const F32* v = mesh.mVertexBase; + + if (mesh.mIndexType == LLCDMeshData::INT_16) + { + U16* idx = (U16*) mesh.mIndexBase; + for (S32 j = 0; j < mesh.mNumTriangles; ++j) + { + F32* mp0 = (F32*) ((U8*)v+idx[0]*mesh.mVertexStrideBytes); + F32* mp1 = (F32*) ((U8*)v+idx[1]*mesh.mVertexStrideBytes); + F32* mp2 = (F32*) ((U8*)v+idx[2]*mesh.mVertexStrideBytes); + + idx = (U16*) (((U8*)idx)+mesh.mIndexStrideBytes); + + LLVector3 v0(mp0); + LLVector3 v1(mp1); + LLVector3 v2(mp2); + + LLVector3 n = (v1-v0)%(v2-v0); + n.normalize(); + + res.mPositions.push_back(v0*scale); + res.mPositions.push_back(v1*scale); + res.mPositions.push_back(v2*scale); + + res.mNormals.push_back(n); + res.mNormals.push_back(n); + res.mNormals.push_back(n); + } + } + else + { + U32* idx = (U32*) mesh.mIndexBase; + for (S32 j = 0; j < mesh.mNumTriangles; ++j) + { + F32* mp0 = (F32*) ((U8*)v+idx[0]*mesh.mVertexStrideBytes); + F32* mp1 = (F32*) ((U8*)v+idx[1]*mesh.mVertexStrideBytes); + F32* mp2 = (F32*) ((U8*)v+idx[2]*mesh.mVertexStrideBytes); + + idx = (U32*) (((U8*)idx)+mesh.mIndexStrideBytes); + + LLVector3 v0(mp0); + LLVector3 v1(mp1); + LLVector3 v2(mp2); + + LLVector3 n = (v1-v0)%(v2-v0); + n.normalize(); + + res.mPositions.push_back(v0*scale); + res.mPositions.push_back(v1*scale); + res.mPositions.push_back(v2*scale); + + res.mNormals.push_back(n); + res.mNormals.push_back(n); + res.mNormals.push_back(n); + } + } +} + +S32 LLMeshRepoThread::sActiveHeaderRequests = 0; +S32 LLMeshRepoThread::sActiveLODRequests = 0; +U32 LLMeshRepoThread::sMaxConcurrentRequests = 1; + + +class LLTextureCostResponder : public LLCurl::Responder +{ +public: + LLTextureUploadData mData; + LLMeshUploadThread* mThread; + + LLTextureCostResponder(LLTextureUploadData data, LLMeshUploadThread* thread) + : mData(data), mThread(thread) + { + + } + + virtual void completed(U32 status, const std::string& reason, const LLSD& content) + { + mThread->mPendingConfirmations--; + if (isGoodStatus(status)) + { + mThread->priceResult(mData, content); + } + else + { + llwarns << status << ": " << reason << llendl; + + if (mData.mRetries < MAX_TEXTURE_UPLOAD_RETRIES) + { + llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl; + + if (status == 499 || status == 500) + { + mThread->uploadTexture(mData); + } + else + { + llerrs << "Unhandled status " << status << llendl; + } + } + else + { + llwarns << "Giving up after " << mData.mRetries << " retries." << llendl; + } + } + } +}; + +class LLTextureUploadResponder : public LLCurl::Responder +{ +public: + LLTextureUploadData mData; + LLMeshUploadThread* mThread; + + LLTextureUploadResponder(LLTextureUploadData data, LLMeshUploadThread* thread) + : mData(data), mThread(thread) + { + } + + virtual void completed(U32 status, const std::string& reason, const LLSD& content) + { + mThread->mPendingUploads--; + if (isGoodStatus(status)) + { + mData.mUUID = content["new_asset"].asUUID(); + gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mData.mPostData, content)); + mThread->onTextureUploaded(mData); + } + else + { + llwarns << status << ": " << reason << llendl; + llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl; + + if (status == 404) + { + mThread->uploadTexture(mData); + } + else if (status == 499) + { + mThread->mConfirmedTextureQ.push(mData); + } + else + { + llerrs << "Unhandled status " << status << llendl; + } + } + } +}; + +class LLMeshCostResponder : public LLCurl::Responder +{ +public: + LLMeshUploadData mData; + LLMeshUploadThread* mThread; + + LLMeshCostResponder(LLMeshUploadData data, LLMeshUploadThread* thread) + : mData(data), mThread(thread) + { + + } + + virtual void completed(U32 status, const std::string& reason, const LLSD& content) + { + mThread->mPendingConfirmations--; + + if (isGoodStatus(status)) + { + mThread->priceResult(mData, content); + } + else + { + llwarns << status << ": " << reason << llendl; + + if (status == HTTP_INTERNAL_ERROR) + { + llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl; + mThread->uploadModel(mData); + } + else if (status == HTTP_BAD_REQUEST) + { + llwarns << "Status 400 received from server, giving up." << llendl; + } + else if (status == HTTP_NOT_FOUND) + { + llwarns <<"Status 404 received, server is disconnected, giving up." << llendl ; + } + else + { + llerrs << "Unhandled status " << status << llendl; + } + } + } +}; + +class LLMeshUploadResponder : public LLCurl::Responder +{ +public: + LLMeshUploadData mData; + LLMeshUploadThread* mThread; + + LLMeshUploadResponder(LLMeshUploadData data, LLMeshUploadThread* thread) + : mData(data), mThread(thread) + { + } + + virtual void completed(U32 status, const std::string& reason, const LLSD& content) + { + mThread->mPendingUploads--; + if (isGoodStatus(status)) + { + mData.mUUID = content["new_asset"].asUUID(); + if (mData.mUUID.isNull()) + { + LLSD args; + std::string message = content["error"]["message"]; + std::string identifier = content["error"]["identifier"]; + std::string invalidity_identifier = content["error"]["invalidity_identifier"]; + + args["MESSAGE"] = message; + args["IDENTIFIER"] = identifier; + args["INVALIDITY_IDENTIFIER"] = invalidity_identifier; + args["LABEL"] = mData.mBaseModel->mLabel; + + gMeshRepo.uploadError(args); + } + else + { + gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mData.mPostData, content)); + mThread->onModelUploaded(mData); + } + } + else + { + llwarns << status << ": " << reason << llendl; + llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl; + + if (status == 404) + { + mThread->uploadModel(mData); + } + else if (status == 499) + { + mThread->mConfirmedQ.push(mData); + } + else if (status != 500) + { //drop internal server errors on the floor, otherwise grab + llerrs << "Unhandled status " << status << llendl; + } + } + } +}; + + +class LLMeshHeaderResponder : public LLCurl::Responder +{ +public: + LLVolumeParams mMeshParams; + + LLMeshHeaderResponder(const LLVolumeParams& mesh_params) + : mMeshParams(mesh_params) + { + } + + virtual void completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer); + +}; + +class LLMeshLODResponder : public LLCurl::Responder +{ +public: + LLVolumeParams mMeshParams; + S32 mLOD; + U32 mRequestedBytes; + U32 mOffset; + + LLMeshLODResponder(const LLVolumeParams& mesh_params, S32 lod, U32 offset, U32 requested_bytes) + : mMeshParams(mesh_params), mLOD(lod), mOffset(offset), mRequestedBytes(requested_bytes) + { + } + + virtual void completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer); + +}; + +class LLMeshSkinInfoResponder : public LLCurl::Responder +{ +public: + LLUUID mMeshID; + U32 mRequestedBytes; + U32 mOffset; + + LLMeshSkinInfoResponder(const LLUUID& id, U32 offset, U32 size) + : mMeshID(id), mRequestedBytes(size), mOffset(offset) + { + } + + virtual void completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer); + +}; + +class LLMeshDecompositionResponder : public LLCurl::Responder +{ +public: + LLUUID mMeshID; + U32 mRequestedBytes; + U32 mOffset; + + LLMeshDecompositionResponder(const LLUUID& id, U32 offset, U32 size) + : mMeshID(id), mRequestedBytes(size), mOffset(offset) + { + } + + virtual void completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer); + +}; + +class LLMeshPhysicsShapeResponder : public LLCurl::Responder +{ +public: + LLUUID mMeshID; + U32 mRequestedBytes; + U32 mOffset; + + LLMeshPhysicsShapeResponder(const LLUUID& id, U32 offset, U32 size) + : mMeshID(id), mRequestedBytes(size), mOffset(offset) + { + } + + virtual void completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer); + +}; + +class LLModelObjectUploadResponder: public LLCurl::Responder +{ + LLSD mObjectAsset; + LLMeshUploadThread* mThread; + +public: + LLModelObjectUploadResponder(LLMeshUploadThread* thread, const LLSD& object_asset): + mThread(thread), + mObjectAsset(object_asset) + { + } + + virtual void completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) + { + assert_main_thread(); + + llinfos << "completed" << llendl; + mThread->mPendingUploads--; + mThread->mFinished = true; + } +}; + +class LLWholeModelFeeResponder: public LLCurl::Responder +{ + LLMeshUploadThread* mThread; +public: + LLWholeModelFeeResponder(LLMeshUploadThread* thread): + mThread(thread) + { + } + virtual void completed(U32 status, + const std::string& reason, + const LLSD& content) + { + //assert_main_thread(); + llinfos << "completed" << llendl; + mThread->mPendingUploads--; + dumpLLSDToFile(content,"whole_model_response.xml"); + + mThread->mWholeModelUploadURL = content["uploader"].asString(); + } +}; + +class LLWholeModelUploadResponder: public LLCurl::Responder +{ + LLMeshUploadThread* mThread; +public: + LLWholeModelUploadResponder(LLMeshUploadThread* thread): + mThread(thread) + { + } + virtual void completed(U32 status, + const std::string& reason, + const LLSD& content) + { + //assert_main_thread(); + llinfos << "upload completed" << llendl; + mThread->mPendingUploads--; + dumpLLSDToFile(content,"whole_model_upload_response.xml"); + } +}; + +LLMeshRepoThread::LLMeshRepoThread() +: LLThread("mesh repo", NULL) +{ + mWaiting = false; + mMutex = new LLMutex(NULL); + mHeaderMutex = new LLMutex(NULL); + mSignal = new LLCondition(NULL); +} + +LLMeshRepoThread::~LLMeshRepoThread() +{ + delete mMutex; + mMutex = NULL; + delete mHeaderMutex; + mHeaderMutex = NULL; + delete mSignal; + mSignal = NULL; +} + +void LLMeshRepoThread::run() +{ + mCurlRequest = new LLCurlRequest(); + LLCDResult res = LLConvexDecomposition::initThread(); + if (res != LLCD_OK) + { + llwarns << "convex decomposition unable to be loaded" << llendl; + } + + while (!LLApp::isQuitting()) + { + mWaiting = true; + mSignal->wait(); + mWaiting = false; + + if (!LLApp::isQuitting()) + { + static U32 count = 0; + + static F32 last_hundred = gFrameTimeSeconds; + + if (gFrameTimeSeconds - last_hundred > 1.f) + { //a second has gone by, clear count + last_hundred = gFrameTimeSeconds; + count = 0; + } + + // NOTE: throttling intentionally favors LOD requests over header requests + + while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveLODRequests < sMaxConcurrentRequests) + { + { + mMutex->lock(); + LODRequest req = mLODReqQ.front(); + mLODReqQ.pop(); + mMutex->unlock(); + if (fetchMeshLOD(req.mMeshParams, req.mLOD)) + { + count++; + } + } + } + + while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveHeaderRequests < sMaxConcurrentRequests) + { + { + mMutex->lock(); + HeaderRequest req = mHeaderReqQ.front(); + mHeaderReqQ.pop(); + mMutex->unlock(); + if (fetchMeshHeader(req.mMeshParams)) + { + count++; + } + } + } + + { //mSkinRequests is protected by mSignal + std::set incomplete; + for (std::set::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter) + { + LLUUID mesh_id = *iter; + if (!fetchMeshSkinInfo(mesh_id)) + { + incomplete.insert(mesh_id); + } + } + mSkinRequests = incomplete; + } + + { //mDecompositionRequests is protected by mSignal + std::set incomplete; + for (std::set::iterator iter = mDecompositionRequests.begin(); iter != mDecompositionRequests.end(); ++iter) + { + LLUUID mesh_id = *iter; + if (!fetchMeshDecomposition(mesh_id)) + { + incomplete.insert(mesh_id); + } + } + mDecompositionRequests = incomplete; + } + + { //mPhysicsShapeRequests is protected by mSignal + std::set incomplete; + for (std::set::iterator iter = mPhysicsShapeRequests.begin(); iter != mPhysicsShapeRequests.end(); ++iter) + { + LLUUID mesh_id = *iter; + if (!fetchMeshPhysicsShape(mesh_id)) + { + incomplete.insert(mesh_id); + } + } + mPhysicsShapeRequests = incomplete; + } + + mCurlRequest->process(); + } + } + + if (mSignal->isLocked()) + { //make sure to let go of the mutex associated with the given signal before shutting down + mSignal->unlock(); + } + + res = LLConvexDecomposition::quitThread(); + if (res != LLCD_OK) + { + llwarns << "convex decomposition unable to be quit" << llendl; + } + + delete mCurlRequest; + mCurlRequest = NULL; +} + +void LLMeshRepoThread::loadMeshSkinInfo(const LLUUID& mesh_id) +{ //protected by mSignal, no locking needed here + mSkinRequests.insert(mesh_id); +} + +void LLMeshRepoThread::loadMeshDecomposition(const LLUUID& mesh_id) +{ //protected by mSignal, no locking needed here + mDecompositionRequests.insert(mesh_id); +} + +void LLMeshRepoThread::loadMeshPhysicsShape(const LLUUID& mesh_id) +{ //protected by mSignal, no locking needed here + mPhysicsShapeRequests.insert(mesh_id); +} + + +void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) +{ //protected by mSignal, no locking needed here + + mesh_header_map::iterator iter = mMeshHeader.find(mesh_params.getSculptID()); + if (iter != mMeshHeader.end()) + { //if we have the header, request LOD byte range + LODRequest req(mesh_params, lod); + { + LLMutexLock lock(mMutex); + mLODReqQ.push(req); + } + } + else + { + HeaderRequest req(mesh_params); + + pending_lod_map::iterator pending = mPendingLOD.find(mesh_params); + + if (pending != mPendingLOD.end()) + { //append this lod request to existing header request + pending->second.push_back(lod); + if (pending->second.size() > 4) + { + llerrs << "WTF?" << llendl; + } + } + else + { //if no header request is pending, fetch header + LLMutexLock lock(mMutex); + mHeaderReqQ.push(req); + mPendingLOD[mesh_params].push_back(lod); + } + } +} + +//static +std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id) +{ + std::string http_url; + + if (gAgent.getRegion()) + { + http_url = gMeshRepo.mGetMeshCapability; + } + + if (!http_url.empty()) + { + http_url += "/?mesh_id="; + http_url += mesh_id.asString().c_str(); + } + else + { + llwarns << "Current region does not have GetMesh capability! Cannot load " << mesh_id << ".mesh" << llendl; + } + + return http_url; +} + +bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) +{ //protected by mMutex + mHeaderMutex->lock(); + + if (mMeshHeader.find(mesh_id) == mMeshHeader.end()) + { //we have no header info for this mesh, do nothing + mHeaderMutex->unlock(); + return false; + } + + U32 header_size = mMeshHeaderSize[mesh_id]; + + if (header_size > 0) + { + S32 offset = header_size + mMeshHeader[mesh_id]["skin"]["offset"].asInteger(); + S32 size = mMeshHeader[mesh_id]["skin"]["size"].asInteger(); + + mHeaderMutex->unlock(); + + if (offset >= 0 && size > 0) + { + //check VFS for mesh skin info + LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); + if (file.getSize() >= offset+size) + { + LLMeshRepository::sCacheBytesRead += size; + file.seek(offset); + U8* buffer = new U8[size]; + file.read(buffer, size); + + //make sure buffer isn't all 0's (reserved block but not written) + bool zero = true; + for (S32 i = 0; i < llmin(size, 1024) && zero; ++i) + { + zero = buffer[i] > 0 ? false : true; + } + + if (!zero) + { //attempt to parse + if (skinInfoReceived(mesh_id, buffer, size)) + { + delete[] buffer; + return true; + } + } + + delete[] buffer; + } + + //reading from VFS failed for whatever reason, fetch from sim + std::vector headers; + headers.push_back("Accept: application/octet-stream"); + + std::string http_url = constructUrl(mesh_id); + if (!http_url.empty()) + { + ++sActiveLODRequests; + LLMeshRepository::sHTTPRequestCount++; + mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size, + new LLMeshSkinInfoResponder(mesh_id, offset, size)); + } + } + } + else + { + mHeaderMutex->unlock(); + } + + //early out was not hit, effectively fetched + return true; +} + +bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) +{ //protected by mMutex + mHeaderMutex->lock(); + + if (mMeshHeader.find(mesh_id) == mMeshHeader.end()) + { //we have no header info for this mesh, do nothing + mHeaderMutex->unlock(); + return false; + } + + U32 header_size = mMeshHeaderSize[mesh_id]; + + if (header_size > 0) + { + S32 offset = header_size + mMeshHeader[mesh_id]["decomposition"]["offset"].asInteger(); + S32 size = mMeshHeader[mesh_id]["decomposition"]["size"].asInteger(); + + mHeaderMutex->unlock(); + + if (offset >= 0 && size > 0) + { + //check VFS for mesh skin info + LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); + if (file.getSize() >= offset+size) + { + LLMeshRepository::sCacheBytesRead += size; + file.seek(offset); + U8* buffer = new U8[size]; + file.read(buffer, size); + + //make sure buffer isn't all 0's (reserved block but not written) + bool zero = true; + for (S32 i = 0; i < llmin(size, 1024) && zero; ++i) + { + zero = buffer[i] > 0 ? false : true; + } + + if (!zero) + { //attempt to parse + if (decompositionReceived(mesh_id, buffer, size)) + { + delete[] buffer; + return true; + } + } + + delete[] buffer; + } + + //reading from VFS failed for whatever reason, fetch from sim + std::vector headers; + headers.push_back("Accept: application/octet-stream"); + + std::string http_url = constructUrl(mesh_id); + if (!http_url.empty()) + { + ++sActiveLODRequests; + LLMeshRepository::sHTTPRequestCount++; + mCurlRequest->getByteRange(http_url, headers, offset, size, + new LLMeshDecompositionResponder(mesh_id, offset, size)); + } + } + } + else + { + mHeaderMutex->unlock(); + } + + //early out was not hit, effectively fetched + return true; +} + +bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) +{ //protected by mMutex + mHeaderMutex->lock(); + + if (mMeshHeader.find(mesh_id) == mMeshHeader.end()) + { //we have no header info for this mesh, do nothing + mHeaderMutex->unlock(); + return false; + } + + U32 header_size = mMeshHeaderSize[mesh_id]; + + if (header_size > 0) + { + S32 offset = header_size + mMeshHeader[mesh_id]["physics_shape"]["offset"].asInteger(); + S32 size = mMeshHeader[mesh_id]["physics_shape"]["size"].asInteger(); + + mHeaderMutex->unlock(); + + if (offset >= 0 && size > 0) + { + //check VFS for mesh physics shape info + LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); + if (file.getSize() >= offset+size) + { + LLMeshRepository::sCacheBytesRead += size; + file.seek(offset); + U8* buffer = new U8[size]; + file.read(buffer, size); + + //make sure buffer isn't all 0's (reserved block but not written) + bool zero = true; + for (S32 i = 0; i < llmin(size, 1024) && zero; ++i) + { + zero = buffer[i] > 0 ? false : true; + } + + if (!zero) + { //attempt to parse + if (physicsShapeReceived(mesh_id, buffer, size)) + { + delete[] buffer; + return true; + } + } + + delete[] buffer; + } + + //reading from VFS failed for whatever reason, fetch from sim + std::vector headers; + headers.push_back("Accept: application/octet-stream"); + + std::string http_url = constructUrl(mesh_id); + if (!http_url.empty()) + { + ++sActiveLODRequests; + LLMeshRepository::sHTTPRequestCount++; + mCurlRequest->getByteRange(http_url, headers, offset, size, + new LLMeshPhysicsShapeResponder(mesh_id, offset, size)); + } + } + else + { //no physics shape whatsoever, report back NULL + physicsShapeReceived(mesh_id, NULL, 0); + } + } + else + { + mHeaderMutex->unlock(); + } + + //early out was not hit, effectively fetched + return true; +} + +bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) +{ + bool retval = false; + + { + //look for mesh in asset in vfs + LLVFile file(gVFS, mesh_params.getSculptID(), LLAssetType::AT_MESH); + + S32 size = file.getSize(); + + if (size > 0) + { + U8 buffer[1024]; + S32 bytes = llmin(size, 1024); + LLMeshRepository::sCacheBytesRead += bytes; + file.read(buffer, bytes); + if (headerReceived(mesh_params, buffer, bytes)) + { //did not do an HTTP request, return false + return false; + } + } + } + + //either cache entry doesn't exist or is corrupt, request header from simulator + + std::vector headers; + headers.push_back("Accept: application/octet-stream"); + + std::string http_url = constructUrl(mesh_params.getSculptID()); + if (!http_url.empty()) + { + ++sActiveHeaderRequests; + retval = true; + //grab first 4KB if we're going to bother with a fetch. Cache will prevent future fetches if a full mesh fits + //within the first 4KB + LLMeshRepository::sHTTPRequestCount++; + mCurlRequest->getByteRange(http_url, headers, 0, 4096, new LLMeshHeaderResponder(mesh_params)); + } + + return retval; +} + +bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) +{ //protected by mMutex + mHeaderMutex->lock(); + + bool retval = false; + + LLUUID mesh_id = mesh_params.getSculptID(); + + U32 header_size = mMeshHeaderSize[mesh_id]; + + if (header_size > 0) + { + S32 offset = header_size + mMeshHeader[mesh_id][header_lod[lod]]["offset"].asInteger(); + S32 size = mMeshHeader[mesh_id][header_lod[lod]]["size"].asInteger(); + mHeaderMutex->unlock(); + if (offset >= 0 && size > 0) + { + + //check VFS for mesh asset + LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); + if (file.getSize() >= offset+size) + { + LLMeshRepository::sCacheBytesRead += size; + file.seek(offset); + U8* buffer = new U8[size]; + file.read(buffer, size); + + //make sure buffer isn't all 0's (reserved block but not written) + bool zero = true; + for (S32 i = 0; i < llmin(size, 1024) && zero; ++i) + { + zero = buffer[i] > 0 ? false : true; + } + + if (!zero) + { //attempt to parse + if (lodReceived(mesh_params, lod, buffer, size)) + { + delete[] buffer; + return false; + } + } + + delete[] buffer; + } + + //reading from VFS failed for whatever reason, fetch from sim + std::vector headers; + headers.push_back("Accept: application/octet-stream"); + + std::string http_url = constructUrl(mesh_id); + if (!http_url.empty()) + { + ++sActiveLODRequests; + retval = true; + LLMeshRepository::sHTTPRequestCount++; + mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size, + new LLMeshLODResponder(mesh_params, lod, offset, size)); + } + else + { + mUnavailableQ.push(LODRequest(mesh_params, lod)); + } + } + else + { + mUnavailableQ.push(LODRequest(mesh_params, lod)); + } + } + else + { + mHeaderMutex->unlock(); + } + + return retval; +} + +bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size) +{ + LLSD header; + + U32 header_size = 0; + if (data_size > 0) + { + std::string res_str((char*) data, data_size); + + std::string deprecated_header(""); + + if (res_str.substr(0, deprecated_header.size()) == deprecated_header) + { + res_str = res_str.substr(deprecated_header.size()+1, data_size); + header_size = deprecated_header.size()+1; + } + data_size = res_str.size(); + + std::istringstream stream(res_str); + + if (!LLSDSerialize::fromBinary(header, stream, data_size)) + { + llwarns << "Mesh header parse error. Not a valid mesh asset!" << llendl; + return false; + } + + header_size += stream.tellg(); + } + else + { + llinfos + << "Marking header as non-existent, will not retry." << llendl; + header["404"] = 1; + } + + { + U32 cost = gMeshRepo.calcResourceCost(header); + + LLUUID mesh_id = mesh_params.getSculptID(); + + mHeaderMutex->lock(); + mMeshHeaderSize[mesh_id] = header_size; + mMeshHeader[mesh_id] = header; + mMeshResourceCost[mesh_id] = cost; + mHeaderMutex->unlock(); + + //check for pending requests + pending_lod_map::iterator iter = mPendingLOD.find(mesh_params); + if (iter != mPendingLOD.end()) + { + LLMutexLock lock(mMutex); + for (U32 i = 0; i < iter->second.size(); ++i) + { + LODRequest req(mesh_params, iter->second[i]); + mLODReqQ.push(req); + } + } + mPendingLOD.erase(iter); + } + + return true; +} + +bool LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size) +{ + LLVolume* volume = new LLVolume(mesh_params, LLVolumeLODGroup::getVolumeScaleFromDetail(lod)); + std::string mesh_string((char*) data, data_size); + std::istringstream stream(mesh_string); + + if (volume->unpackVolumeFaces(stream, data_size)) + { + LoadedMesh mesh(volume, mesh_params, lod); + if (volume->getNumFaces() > 0) + { + LLMutexLock lock(mMutex); + mLoadedQ.push(mesh); + return true; + } + } + + return false; +} + +bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size) +{ + LLSD skin; + + if (data_size > 0) + { + std::string res_str((char*) data, data_size); + + std::istringstream stream(res_str); + + if (!unzip_llsd(skin, stream, data_size)) + { + llwarns << "Mesh skin info parse error. Not a valid mesh asset!" << llendl; + return false; + } + } + + { + LLMeshSkinInfo info(skin); + info.mMeshID = mesh_id; + + //llinfos<<"info pelvis offset"< 0) + { + std::string res_str((char*) data, data_size); + + std::istringstream stream(res_str); + + if (!unzip_llsd(decomp, stream, data_size)) + { + llwarns << "Mesh decomposition parse error. Not a valid mesh asset!" << llendl; + return false; + } + } + + { + LLModel::Decomposition* d = new LLModel::Decomposition(decomp); + d->mMeshID = mesh_id; + mDecompositionQ.push(d); + } + + return true; +} + +bool LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size) +{ + LLSD physics_shape; + + LLModel::Decomposition* d = new LLModel::Decomposition(); + d->mMeshID = mesh_id; + + if (data == NULL) + { //no data, no physics shape exists + d->mPhysicsShapeMesh.clear(); + } + else + { + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + volume_params.setSculptID(mesh_id, LL_SCULPT_TYPE_MESH); + LLPointer volume = new LLVolume(volume_params,0); + std::string mesh_string((char*) data, data_size); + std::istringstream stream(mesh_string); + + if (volume->unpackVolumeFaces(stream, data_size)) + { + //load volume faces into decomposition buffer + S32 vertex_count = 0; + S32 index_count = 0; + + for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i) + { + const LLVolumeFace& face = volume->getVolumeFace(i); + vertex_count += face.mNumVertices; + index_count += face.mNumIndices; + } + + d->mPhysicsShapeMesh.clear(); + + std::vector& pos = d->mPhysicsShapeMesh.mPositions; + std::vector& norm = d->mPhysicsShapeMesh.mNormals; + + for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i) + { + const LLVolumeFace& face = volume->getVolumeFace(i); + + for (S32 i = 0; i < face.mNumIndices; ++i) + { + U16 idx = face.mIndices[i]; + + pos.push_back(LLVector3(face.mPositions[idx].getF32ptr())); + norm.push_back(LLVector3(face.mNormals[idx].getF32ptr())); + } + } + } + } + + mDecompositionQ.push(d); + return true; +} + +LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures, + bool upload_skin, bool upload_joints) +: LLThread("mesh upload"), + mDiscarded(FALSE) +{ + mInstanceList = data; + mUploadTextures = upload_textures; + mUploadSkin = upload_skin; + mUploadJoints = upload_joints; + mMutex = new LLMutex(NULL); + mCurlRequest = NULL; + mPendingConfirmations = 0; + mPendingUploads = 0; + mPendingCost = 0; + mFinished = false; + mOrigin = gAgent.getPositionAgent(); + mHost = gAgent.getRegionHost(); + + mUploadObjectAssetCapability = gAgent.getRegion()->getCapability("UploadObjectAsset"); + mNewInventoryCapability = gAgent.getRegion()->getCapability("NewFileAgentInventoryVariablePrice"); + mWholeModelFeeCapability = gAgent.getRegion()->getCapability("NewFileAgentInventory"); + + mOrigin += gAgent.getAtAxis() * scale.magVec(); +} + +LLMeshUploadThread::~LLMeshUploadThread() +{ + +} + +LLMeshUploadThread::DecompRequest::DecompRequest(LLModel* mdl, LLModel* base_model, LLMeshUploadThread* thread) +{ + mStage = "single_hull"; + mModel = mdl; + mDecompID = &mdl->mDecompID; + mBaseModel = base_model; + mThread = thread; + + //copy out positions and indices + if (mdl) + { + U16 index_offset = 0; + + mPositions.clear(); + mIndices.clear(); + + //queue up vertex positions and indices + for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) + { + const LLVolumeFace& face = mdl->getVolumeFace(i); + if (mPositions.size() + face.mNumVertices > 65535) + { + continue; + } + + for (U32 j = 0; j < face.mNumVertices; ++j) + { + mPositions.push_back(LLVector3(face.mPositions[j].getF32ptr())); + } + + for (U32 j = 0; j < face.mNumIndices; ++j) + { + mIndices.push_back(face.mIndices[j]+index_offset); + } + + index_offset += face.mNumVertices; + } + } + + mThread->mFinalDecomp = this; + mThread->mPhysicsComplete = false; +} + +void LLMeshUploadThread::DecompRequest::completed() +{ + if (mThread->mFinalDecomp == this) + { + mThread->mPhysicsComplete = true; + } + + if (mHull.size() != 1) + { + llerrs << "WTF?" << llendl; + } + + mThread->mHullMap[mBaseModel] = mHull[0]; +} + +//called in the main thread. +void LLMeshUploadThread::preStart() +{ + //build map of LLModel refs to instances for callbacks + for (instance_list::iterator iter = mInstanceList.begin(); iter != mInstanceList.end(); ++iter) + { + mInstance[iter->mModel].push_back(*iter); + } +} + +void LLMeshUploadThread::discard() +{ + LLMutexLock lock(mMutex) ; + mDiscarded = TRUE ; +} + +BOOL LLMeshUploadThread::isDiscarded() +{ + LLMutexLock lock(mMutex) ; + return mDiscarded ; +} + +void LLMeshUploadThread::run() +{ + if (gSavedSettings.getBOOL("MeshUseWholeModelUpload")) + { + doWholeModelUpload(); + } + else + { + doIterativeUpload(); + } +} + +#if 1 +void dumpLLSDToFile(const LLSD& content, std::string filename) +{ + std::ofstream of(filename.c_str()); + LLSDSerialize::toPrettyXML(content,of); +} +#endif + +void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures) +{ + // TODO where do textures go? + + LLSD result; + + LLSD res; + result["folder_id"] = gInventory.findCategoryUUIDForType(LLFolderType::FT_OBJECT); + result["asset_type"] = "mesh"; + result["inventory_type"] = "object"; + result["name"] = "your name here"; + result["description"] = "your description here"; + + // TODO "optional" fields from the spec + + res["mesh_list"] = LLSD::emptyArray(); +// TODO Textures + //res["texture_list"] = LLSD::emptyArray(); + S32 mesh_num = 0; + S32 texture_num = 0; + + std::set textures; + + for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter) + { + LLMeshUploadData data; + data.mBaseModel = iter->first; + + LLModelInstance& instance = *(iter->second.begin()); + + for (S32 i = 0; i < 5; i++) + { + data.mModel[i] = instance.mLOD[i]; + } + + std::stringstream ostr; + + LLModel::Decomposition& decomp = + data.mModel[LLModel::LOD_PHYSICS].notNull() ? + data.mModel[LLModel::LOD_PHYSICS]->mPhysics : + data.mBaseModel->mPhysics; + + decomp.mBaseHull = mHullMap[data.mBaseModel]; + + LLSD mesh_header = LLModel::writeModel( + ostr, + data.mModel[LLModel::LOD_PHYSICS], + data.mModel[LLModel::LOD_HIGH], + data.mModel[LLModel::LOD_MEDIUM], + data.mModel[LLModel::LOD_LOW], + data.mModel[LLModel::LOD_IMPOSTOR], + decomp, + mUploadSkin, + mUploadJoints); + + data.mAssetData = ostr.str(); + + LLSD mesh_entry; + + LLVector3 pos, scale; + LLQuaternion rot; + LLMatrix4 transformation = instance.mTransform; + decomposeMeshMatrix(transformation,pos,rot,scale); + +#if 0 + mesh_entry["childpos"] = ll_sd_from_vector3(pos); + mesh_entry["childrot"] = ll_sd_from_quaternion(rot); + mesh_entry["scale"] = ll_sd_from_vector3(scale); +#endif + mesh_entry["position"] = ll_sd_from_vector3(LLVector3()); + mesh_entry["rotation"] = ll_sd_from_quaternion(rot); + mesh_entry["scale"] = ll_sd_from_vector3(scale); + + // TODO should be binary. + std::string str = ostr.str(); + mesh_entry["mesh_data"] = LLSD::Binary(str.begin(),str.end()); + + res["mesh_list"][mesh_num] = mesh_entry; + + // TODO how do textures in the list map to textures in the meshes? + if (mUploadTextures) + { + for (std::vector::iterator material_iter = instance.mMaterial.begin(); + material_iter != instance.mMaterial.end(); ++material_iter) + { + + if (textures.find(material_iter->mDiffuseMap.get()) == textures.end()) + { + textures.insert(material_iter->mDiffuseMap.get()); + + std::stringstream ostr; + if (include_textures) // otherwise data is blank. + { + LLTextureUploadData data(material_iter->mDiffuseMap.get(), material_iter->mDiffuseMapLabel); + if (!data.mTexture->isRawImageValid()) + { + data.mTexture->reloadRawImage(data.mTexture->getDiscardLevel()); + } + + LLPointer upload_file = + LLViewerTextureList::convertToUploadFile(data.mTexture->getRawImage()); + ostr.write((const char*) upload_file->getData(), upload_file->getDataSize()); + } + LLSD texture_entry; + texture_entry["texture_data"] = ostr.str(); + res["texture_list"][texture_num] = texture_entry; + texture_num++; + } + } + } + + mesh_num++; + } + + result["asset_resources"] = res; +#if 1 + dumpLLSDToFile(result,"whole_model.xml"); +#endif + + dest = result; +} + +void LLMeshUploadThread::doWholeModelUpload() +{ + mCurlRequest = new LLCurlRequest(); + + // Queue up models for hull generation (viewer-side) + for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter) + { + LLMeshUploadData data; + data.mBaseModel = iter->first; + + LLModelInstance& instance = *(iter->second.begin()); + + for (S32 i = 0; i < 5; i++) + { + data.mModel[i] = instance.mLOD[i]; + } + + //queue up models for hull generation + LLModel* physics = NULL; + + if (data.mModel[LLModel::LOD_PHYSICS].notNull()) + { + physics = data.mModel[LLModel::LOD_PHYSICS]; + } + else if (data.mModel[LLModel::LOD_MEDIUM].notNull()) + { + physics = data.mModel[LLModel::LOD_MEDIUM]; + } + else + { + physics = data.mModel[LLModel::LOD_HIGH]; + } + + if (!physics) + { + llerrs << "WTF?" << llendl; + } + + DecompRequest* request = new DecompRequest(physics, data.mBaseModel, this); + gMeshRepo.mDecompThread->submitRequest(request); + } + + while (!mPhysicsComplete) + { + apr_sleep(100); + } + + bool do_include_textures = false; // not needed for initial cost/validation check. + LLSD model_data; + wholeModelToLLSD(model_data, do_include_textures); + + mPendingUploads++; + LLCurlRequest::headers_t headers; + mCurlRequest->post(mWholeModelFeeCapability, headers, model_data, + new LLWholeModelFeeResponder(this)); + + do + { + mCurlRequest->process(); + } while (mCurlRequest->getQueued() > 0); + + mCurlRequest->post(mWholeModelUploadURL, headers, model_data["asset_resources"], new LLWholeModelUploadResponder(this)); + + do + { + mCurlRequest->process(); + } while (mCurlRequest->getQueued() > 0); + + delete mCurlRequest; + mCurlRequest = NULL; + + // Currently a no-op. + mFinished = true; +} + +void LLMeshUploadThread::doIterativeUpload() +{ + if(isDiscarded()) + { + mFinished = true; + return ; + } + + mCurlRequest = new LLCurlRequest(); + + std::set textures; + + //populate upload queue with relevant models + for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter) + { + LLMeshUploadData data; + data.mBaseModel = iter->first; + + LLModelInstance& instance = *(iter->second.begin()); + + for (S32 i = 0; i < 5; i++) + { + data.mModel[i] = instance.mLOD[i]; + } + + uploadModel(data); + + if (mUploadTextures) + { + for (std::vector::iterator material_iter = instance.mMaterial.begin(); + material_iter != instance.mMaterial.end(); ++material_iter) + { + + if (textures.find(material_iter->mDiffuseMap.get()) == textures.end()) + { + textures.insert(material_iter->mDiffuseMap.get()); + + LLTextureUploadData data(material_iter->mDiffuseMap.get(), material_iter->mDiffuseMapLabel); + uploadTexture(data); + } + } + } + + //queue up models for hull generation + DecompRequest* request = new DecompRequest(data.mModel[LLModel::LOD_HIGH], data.mBaseModel, this); + gMeshRepo.mDecompThread->submitRequest(request); + } + + while (!mPhysicsComplete) + { + apr_sleep(100); + } + + //upload textures + bool done = false; + do + { + if (!mTextureQ.empty()) + { + sendCostRequest(mTextureQ.front()); + mTextureQ.pop(); + } + + if (!mConfirmedTextureQ.empty()) + { + doUploadTexture(mConfirmedTextureQ.front()); + mConfirmedTextureQ.pop(); + } + + mCurlRequest->process(); + + done = mTextureQ.empty() && mConfirmedTextureQ.empty(); + } + while (!done || mCurlRequest->getQueued() > 0); + + LLSD object_asset; + object_asset["objects"] = LLSD::emptyArray(); + + done = false; + do + { + static S32 count = 0; + static F32 last_hundred = gFrameTimeSeconds; + if (gFrameTimeSeconds - last_hundred > 1.f) + { + last_hundred = gFrameTimeSeconds; + count = 0; + } + + //how many requests to push before calling process + const S32 PUSH_PER_PROCESS = 32; + + S32 tcount = llmin(count+PUSH_PER_PROCESS, 100); + + while (!mUploadQ.empty() && count < tcount) + { //send any pending upload requests + mMutex->lock(); + LLMeshUploadData data = mUploadQ.front(); + mUploadQ.pop(); + mMutex->unlock(); + sendCostRequest(data); + count++; + } + + tcount = llmin(count+PUSH_PER_PROCESS, 100); + + while (!mConfirmedQ.empty() && count < tcount) + { //process any meshes that have been confirmed for upload + LLMeshUploadData& data = mConfirmedQ.front(); + doUploadModel(data); + mConfirmedQ.pop(); + count++; + } + + tcount = llmin(count+PUSH_PER_PROCESS, 100); + + while (!mInstanceQ.empty() && count < tcount && !isDiscarded()) + { //create any objects waiting for upload + count++; + object_asset["objects"].append(createObject(mInstanceQ.front())); + mInstanceQ.pop(); + } + + mCurlRequest->process(); + + done = isDiscarded() || (mInstanceQ.empty() && mConfirmedQ.empty() && mUploadQ.empty()); + } + while (!done || mCurlRequest->getQueued() > 0); + + delete mCurlRequest; + mCurlRequest = NULL; + + // now upload the object asset + std::string url = mUploadObjectAssetCapability; + + if (object_asset["objects"][0].has("permissions")) + { //copy permissions from first available object to be used for coalesced object + object_asset["permissions"] = object_asset["objects"][0]["permissions"]; + } + + if(!isDiscarded()) + { + mPendingUploads++; + LLHTTPClient::post(url, object_asset, new LLModelObjectUploadResponder(this,object_asset)); + } + else + { + mFinished = true; + } +} + +void LLMeshUploadThread::uploadModel(LLMeshUploadData& data) +{ //called from arbitrary thread + { + LLMutexLock lock(mMutex); + mUploadQ.push(data); + } +} + +void LLMeshUploadThread::uploadTexture(LLTextureUploadData& data) +{ //called from mesh upload thread + mTextureQ.push(data); +} + + +static LLFastTimer::DeclareTimer FTM_NOTIFY_MESH_LOADED("Notify Loaded"); +static LLFastTimer::DeclareTimer FTM_NOTIFY_MESH_UNAVAILABLE("Notify Unavailable"); + +void LLMeshRepoThread::notifyLoadedMeshes() +{ + while (!mLoadedQ.empty()) + { + mMutex->lock(); + LoadedMesh mesh = mLoadedQ.front(); + mLoadedQ.pop(); + mMutex->unlock(); + + if (mesh.mVolume && mesh.mVolume->getNumVolumeFaces() > 0) + { + gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume); + } + else + { + gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams, + LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail())); + } + } + + while (!mUnavailableQ.empty()) + { + mMutex->lock(); + LODRequest req = mUnavailableQ.front(); + mUnavailableQ.pop(); + mMutex->unlock(); + + gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD); + } + + while (!mSkinInfoQ.empty()) + { + gMeshRepo.notifySkinInfoReceived(mSkinInfoQ.front()); + mSkinInfoQ.pop(); + } + + while (!mDecompositionQ.empty()) + { + gMeshRepo.notifyDecompositionReceived(mDecompositionQ.front()); + mDecompositionQ.pop(); + } +} + +S32 LLMeshRepoThread::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod) +{ //only ever called from main thread + LLMutexLock lock(mHeaderMutex); + mesh_header_map::iterator iter = mMeshHeader.find(mesh_params.getSculptID()); + + if (iter != mMeshHeader.end()) + { + LLSD& header = iter->second; + + return LLMeshRepository::getActualMeshLOD(header, lod); + } + + return lod; +} + +//static +S32 LLMeshRepository::getActualMeshLOD(LLSD& header, S32 lod) +{ + lod = llclamp(lod, 0, 3); + + if (header.has("404")) + { + return -1; + } + + if (header[header_lod[lod]]["size"].asInteger() > 0) + { + return lod; + } + + //search down to find the next available lower lod + for (S32 i = lod-1; i >= 0; --i) + { + if (header[header_lod[i]]["size"].asInteger() > 0) + { + return i; + } + } + + //search up to find then ext available higher lod + for (S32 i = lod+1; i < 4; ++i) + { + if (header[header_lod[i]]["size"].asInteger() > 0) + { + return i; + } + } + + //header exists and no good lod found, treat as 404 + header["404"] = 1; + return -1; +} + +U32 LLMeshRepoThread::getResourceCost(const LLUUID& mesh_id) +{ + LLMutexLock lock(mHeaderMutex); + + std::map::iterator iter = mMeshResourceCost.find(mesh_id); + if (iter != mMeshResourceCost.end()) + { + return iter->second; + } + + return 0; +} + +void LLMeshRepository::cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header) +{ + mThread->mMeshHeader[data.mUUID] = header; + + // we cache the mesh for default parameters + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + volume_params.setSculptID(data.mUUID, LL_SCULPT_TYPE_MESH); + + for (U32 i = 0; i < 4; i++) + { + if (data.mModel[i].notNull()) + { + LLPointer volume = new LLVolume(volume_params, LLVolumeLODGroup::getVolumeScaleFromDetail(i)); + volume->copyVolumeFaces(data.mModel[i]); + } + } + +} + +void LLMeshLODResponder::completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) +{ + + LLMeshRepoThread::sActiveLODRequests--; + S32 data_size = buffer->countAfter(channels.in(), NULL); + + if (status < 200 || status > 400) + { + llwarns << status << ": " << reason << llendl; + } + + if (data_size < mRequestedBytes) + { + if (status == 499 || status == 503) + { //timeout or service unavailable, try again + LLMeshRepository::sHTTPRetryCount++; + gMeshRepo.mThread->loadMeshLOD(mMeshParams, mLOD); + } + else + { + llwarns << "Unhandled status " << status << llendl; + } + return; + } + + LLMeshRepository::sBytesReceived += mRequestedBytes; + + U8* data = NULL; + + if (data_size > 0) + { + data = new U8[data_size]; + buffer->readAfter(channels.in(), NULL, data, data_size); + } + + if (gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size)) + { + //good fetch from sim, write to VFS for caching + LLVFile file(gVFS, mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE); + + S32 offset = mOffset; + S32 size = mRequestedBytes; + + if (file.getSize() >= offset+size) + { + file.seek(offset); + file.write(data, size); + LLMeshRepository::sCacheBytesWritten += size; + } + } + + delete [] data; +} + +void LLMeshSkinInfoResponder::completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) +{ + S32 data_size = buffer->countAfter(channels.in(), NULL); + + if (status < 200 || status > 400) + { + llwarns << status << ": " << reason << llendl; + } + + if (data_size < mRequestedBytes) + { + if (status == 499 || status == 503) + { //timeout or service unavailable, try again + LLMeshRepository::sHTTPRetryCount++; + gMeshRepo.mThread->loadMeshSkinInfo(mMeshID); + } + else + { + llwarns << "Unhandled status " << status << llendl; + } + return; + } + + LLMeshRepository::sBytesReceived += mRequestedBytes; + + U8* data = NULL; + + if (data_size > 0) + { + data = new U8[data_size]; + buffer->readAfter(channels.in(), NULL, data, data_size); + } + + if (gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size)) + { + //good fetch from sim, write to VFS for caching + LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); + + S32 offset = mOffset; + S32 size = mRequestedBytes; + + if (file.getSize() >= offset+size) + { + LLMeshRepository::sCacheBytesWritten += size; + file.seek(offset); + file.write(data, size); + } + } + + delete [] data; +} + +void LLMeshDecompositionResponder::completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) +{ + S32 data_size = buffer->countAfter(channels.in(), NULL); + + if (status < 200 || status > 400) + { + llwarns << status << ": " << reason << llendl; + } + + if (data_size < mRequestedBytes) + { + if (status == 499 || status == 503) + { //timeout or service unavailable, try again + LLMeshRepository::sHTTPRetryCount++; + gMeshRepo.mThread->loadMeshDecomposition(mMeshID); + } + else + { + llwarns << "Unhandled status " << status << llendl; + } + return; + } + + LLMeshRepository::sBytesReceived += mRequestedBytes; + + U8* data = NULL; + + if (data_size > 0) + { + data = new U8[data_size]; + buffer->readAfter(channels.in(), NULL, data, data_size); + } + + if (gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size)) + { + //good fetch from sim, write to VFS for caching + LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); + + S32 offset = mOffset; + S32 size = mRequestedBytes; + + if (file.getSize() >= offset+size) + { + LLMeshRepository::sCacheBytesWritten += size; + file.seek(offset); + file.write(data, size); + } + } + + delete [] data; +} + +void LLMeshPhysicsShapeResponder::completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) +{ + S32 data_size = buffer->countAfter(channels.in(), NULL); + + if (status < 200 || status > 400) + { + llwarns << status << ": " << reason << llendl; + } + + if (data_size < mRequestedBytes) + { + if (status == 499 || status == 503) + { //timeout or service unavailable, try again + LLMeshRepository::sHTTPRetryCount++; + gMeshRepo.mThread->loadMeshPhysicsShape(mMeshID); + } + else + { + llwarns << "Unhandled status " << status << llendl; + } + return; + } + + LLMeshRepository::sBytesReceived += mRequestedBytes; + + U8* data = NULL; + + if (data_size > 0) + { + data = new U8[data_size]; + buffer->readAfter(channels.in(), NULL, data, data_size); + } + + if (gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size)) + { + //good fetch from sim, write to VFS for caching + LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); + + S32 offset = mOffset; + S32 size = mRequestedBytes; + + if (file.getSize() >= offset+size) + { + LLMeshRepository::sCacheBytesWritten += size; + file.seek(offset); + file.write(data, size); + } + } + + delete [] data; +} + +void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) +{ + LLMeshRepoThread::sActiveHeaderRequests--; + if (status < 200 || status > 400) + { + //llwarns + // << "Header responder failed with status: " + // << status << ": " << reason << llendl; + + // 503 (service unavailable) or 499 (timeout) + // can be due to server load and can be retried + + // TODO*: Add maximum retry logic, exponential backoff + // and (somewhat more optional than the others) retries + // again after some set period of time + if (status == 503 || status == 499) + { //retry + LLMeshRepository::sHTTPRetryCount++; + LLMeshRepoThread::HeaderRequest req(mMeshParams); + LLMutexLock lock(gMeshRepo.mThread->mMutex); + gMeshRepo.mThread->mHeaderReqQ.push(req); + + return; + } + } + + S32 data_size = buffer->countAfter(channels.in(), NULL); + + U8* data = NULL; + + if (data_size > 0) + { + data = new U8[data_size]; + buffer->readAfter(channels.in(), NULL, data, data_size); + } + + LLMeshRepository::sBytesReceived += llmin(data_size, 4096); + + if (!gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size)) + { + llwarns + << "Unable to parse mesh header: " + << status << ": " << reason << llendl; + } + else if (data && data_size > 0) + { + //header was successfully retrieved from sim, cache in vfs + LLUUID mesh_id = mMeshParams.getSculptID(); + LLSD header = gMeshRepo.mThread->mMeshHeader[mesh_id]; + + std::stringstream str; + + S32 lod_bytes = 0; + + for (U32 i = 0; i < LLModel::LOD_PHYSICS; ++i) + { //figure out how many bytes we'll need to reserve in the file + std::string lod_name = header_lod[i]; + lod_bytes = llmax(lod_bytes, header[lod_name]["offset"].asInteger()+header[lod_name]["size"].asInteger()); + } + + //just in case skin info or decomposition is at the end of the file (which it shouldn't be) + lod_bytes = llmax(lod_bytes, header["skin"]["offset"].asInteger() + header["skin"]["size"].asInteger()); + lod_bytes = llmax(lod_bytes, header["decomposition"]["offset"].asInteger() + header["decomposition"]["size"].asInteger()); + + S32 header_bytes = (S32) gMeshRepo.mThread->mMeshHeaderSize[mesh_id]; + S32 bytes = lod_bytes + header_bytes; + + + //it's possible for the remote asset to have more data than is needed for the local cache + //only allocate as much space in the VFS as is needed for the local cache + data_size = llmin(data_size, bytes); + + LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE); + if (file.getMaxSize() >= bytes || file.setMaxSize(bytes)) + { + LLMeshRepository::sCacheBytesWritten += data_size; + + file.write((const U8*) data, data_size); + + //zero out the rest of the file + U8 block[4096]; + memset(block, 0, 4096); + + while (bytes-file.tell() > 4096) + { + file.write(block, 4096); + } + + S32 remaining = bytes-file.tell(); + + if (remaining < 0 || remaining > 4096) + { + llerrs << "Bad padding of mesh asset cache entry." << llendl; + } + + if (remaining > 0) + { + file.write(block, remaining); + } + } + } + + delete [] data; +} + + +LLMeshRepository::LLMeshRepository() +: mMeshMutex(NULL), + mMeshThreadCount(0), + mThread(NULL) +{ + +} + +void LLMeshRepository::init() +{ + mMeshMutex = new LLMutex(NULL); + + LLConvexDecomposition::getInstance()->initSystem(); + + mDecompThread = new LLPhysicsDecomp(); + mDecompThread->start(); + + while (!mDecompThread->mInited) + { //wait for physics decomp thread to init + apr_sleep(100); + } + + + + mThread = new LLMeshRepoThread(); + mThread->start(); +} + +void LLMeshRepository::shutdown() +{ + llinfos << "Shutting down mesh repository." << llendl; + + for (U32 i = 0; i < mUploads.size(); ++i) + { + llinfos << "Discard the pending mesh uploads " << llendl; + mUploads[i]->discard() ; //discard the uploading requests. + } + + mThread->mSignal->signal(); + + while (!mThread->isStopped()) + { + apr_sleep(10); + } + delete mThread; + mThread = NULL; + + for (U32 i = 0; i < mUploads.size(); ++i) + { + llinfos << "Waiting for pending mesh upload " << i << "/" << mUploads.size() << llendl; + while (!mUploads[i]->isStopped()) + { + apr_sleep(10); + } + delete mUploads[i]; + } + + mUploads.clear(); + + delete mMeshMutex; + mMeshMutex = NULL; + + llinfos << "Shutting down decomposition system." << llendl; + + if (mDecompThread) + { + mDecompThread->shutdown(); + delete mDecompThread; + mDecompThread = NULL; + } + + LLConvexDecomposition::quitSystem(); +} + +//called in the main thread. +S32 LLMeshRepository::update() +{ + if(mUploadWaitList.empty()) + { + return 0 ; + } + + S32 size = mUploadWaitList.size() ; + for (S32 i = 0; i < size; ++i) + { + mUploads.push_back(mUploadWaitList[i]); + mUploadWaitList[i]->preStart() ; + mUploadWaitList[i]->start() ; + } + mUploadWaitList.clear() ; + + return size ; +} + +S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod) +{ + if (detail < 0 || detail > 4) + { + return detail; + } + + LLFastTimer t(FTM_LOAD_MESH); + + { + LLMutexLock lock(mMeshMutex); + //add volume to list of loading meshes + mesh_load_map::iterator iter = mLoadingMeshes[detail].find(mesh_params); + if (iter != mLoadingMeshes[detail].end()) + { //request pending for this mesh, append volume id to list + iter->second.insert(vobj->getID()); + } + else + { + //first request for this mesh + mLoadingMeshes[detail][mesh_params].insert(vobj->getID()); + mPendingRequests.push_back(LLMeshRepoThread::LODRequest(mesh_params, detail)); + } + } + + //do a quick search to see if we can't display something while we wait for this mesh to load + LLVolume* volume = vobj->getVolume(); + + if (volume) + { + if (volume->getNumVolumeFaces() == 0 && !volume->isTetrahedron()) + { + volume->makeTetrahedron(); + } + + LLVolumeParams params = volume->getParams(); + + LLVolumeLODGroup* group = LLPrimitive::getVolumeManager()->getGroup(params); + + if (group) + { + //first, see if last_lod is available (don't transition down to avoid funny popping a la SH-641) + if (last_lod >= 0) + { + LLVolume* lod = group->refLOD(last_lod); + if (lod && !lod->isTetrahedron() && lod->getNumVolumeFaces() > 0) + { + group->derefLOD(lod); + return last_lod; + } + group->derefLOD(lod); + } + + //next, see what the next lowest LOD available might be + for (S32 i = detail-1; i >= 0; --i) + { + LLVolume* lod = group->refLOD(i); + if (lod && !lod->isTetrahedron() && lod->getNumVolumeFaces() > 0) + { + group->derefLOD(lod); + return i; + } + + group->derefLOD(lod); + } + + //no lower LOD is a available, is a higher lod available? + for (S32 i = detail+1; i < 4; ++i) + { + LLVolume* lod = group->refLOD(i); + if (lod && !lod->isTetrahedron() && lod->getNumVolumeFaces() > 0) + { + group->derefLOD(lod); + return i; + } + + group->derefLOD(lod); + } + } + else + { + llerrs << "WTF?" << llendl; + } + } + + return detail; +} + +static LLFastTimer::DeclareTimer FTM_START_MESH_THREAD("Start Thread"); +static LLFastTimer::DeclareTimer FTM_LOAD_MESH_LOD("Load LOD"); +static LLFastTimer::DeclareTimer FTM_MESH_LOCK1("Lock 1"); +static LLFastTimer::DeclareTimer FTM_MESH_LOCK2("Lock 2"); + +void LLMeshRepository::notifyLoadedMeshes() +{ //called from main thread + + LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests"); + + //clean up completed upload threads + for (std::vector::iterator iter = mUploads.begin(); iter != mUploads.end(); ) + { + LLMeshUploadThread* thread = *iter; + + if (thread->isStopped() && thread->finished()) + { + iter = mUploads.erase(iter); + delete thread; + } + else + { + ++iter; + } + } + + //update inventory + if (!mInventoryQ.empty()) + { + LLMutexLock lock(mMeshMutex); + while (!mInventoryQ.empty()) + { + inventory_data& data = mInventoryQ.front(); + + LLAssetType::EType asset_type = LLAssetType::lookup(data.mPostData["asset_type"].asString()); + LLInventoryType::EType inventory_type = LLInventoryType::lookup(data.mPostData["inventory_type"].asString()); + + on_new_single_inventory_upload_complete( + asset_type, + inventory_type, + data.mPostData["asset_type"].asString(), + data.mPostData["folder_id"].asUUID(), + data.mPostData["name"], + data.mPostData["description"], + data.mResponse, + 0); + + mInventoryQ.pop(); + } + } + + //call completed callbacks on finished decompositions + mDecompThread->notifyCompleted(); + + if (!mThread->mWaiting) + { //curl thread is churning, wait for it to go idle + return; + } + + static std::string region_name("never name a region this"); + + if (gAgent.getRegion()) + { //update capability url + if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived()) + { + region_name = gAgent.getRegion()->getName(); + + mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh"); + } + } + + LLFastTimer t(FTM_MESH_UPDATE); + + { + LLFastTimer t(FTM_MESH_LOCK1); + mMeshMutex->lock(); + } + + { + LLFastTimer t(FTM_MESH_LOCK2); + mThread->mMutex->lock(); + } + + //popup queued error messages from background threads + while (!mUploadErrorQ.empty()) + { + LLNotificationsUtil::add("MeshUploadError", mUploadErrorQ.front()); + mUploadErrorQ.pop(); + } + + S32 push_count = LLMeshRepoThread::sMaxConcurrentRequests-(LLMeshRepoThread::sActiveHeaderRequests+LLMeshRepoThread::sActiveLODRequests); + + if (push_count > 0) + { + //calculate "score" for pending requests + + //create score map + std::map score_map; + + for (U32 i = 0; i < 4; ++i) + { + for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin(); iter != mLoadingMeshes[i].end(); ++iter) + { + F32 max_score = 0.f; + for (std::set::iterator obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter) + { + LLViewerObject* object = gObjectList.findObject(*obj_iter); + + if (object) + { + LLDrawable* drawable = object->mDrawable; + if (drawable) + { + F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f); + max_score = llmax(max_score, cur_score); + } + } + } + + score_map[iter->first.getSculptID()] = max_score; + } + } + + //set "score" for pending requests + for (std::vector::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter) + { + iter->mScore = score_map[iter->mMeshParams.getSculptID()]; + } + + //sort by "score" + std::sort(mPendingRequests.begin(), mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater()); + + while (!mPendingRequests.empty() && push_count > 0) + { + LLFastTimer t(FTM_LOAD_MESH_LOD); + LLMeshRepoThread::LODRequest& request = mPendingRequests.front(); + mThread->loadMeshLOD(request.mMeshParams, request.mLOD); + mPendingRequests.erase(mPendingRequests.begin()); + push_count--; + } + } + + //send skin info requests + while (!mPendingSkinRequests.empty()) + { + mThread->loadMeshSkinInfo(mPendingSkinRequests.front()); + mPendingSkinRequests.pop(); + } + + //send decomposition requests + while (!mPendingDecompositionRequests.empty()) + { + mThread->loadMeshDecomposition(mPendingDecompositionRequests.front()); + mPendingDecompositionRequests.pop(); + } + + //send physics shapes decomposition requests + while (!mPendingPhysicsShapeRequests.empty()) + { + mThread->loadMeshPhysicsShape(mPendingPhysicsShapeRequests.front()); + mPendingPhysicsShapeRequests.pop(); + } + + mThread->notifyLoadedMeshes(); + + mThread->mMutex->unlock(); + mMeshMutex->unlock(); + + mThread->mSignal->signal(); +} + +void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo& info) +{ + mSkinMap[info.mMeshID] = info; + mLoadingSkins.erase(info.mMeshID); +} + +void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decomp) +{ + decomposition_map::iterator iter = mDecompositionMap.find(decomp->mMeshID); + if (iter == mDecompositionMap.end()) + { //just insert decomp into map + mDecompositionMap[decomp->mMeshID] = decomp; + } + else + { //merge decomp with existing entry + iter->second->merge(decomp); + delete decomp; + } + + mLoadingDecompositions.erase(decomp->mMeshID); +} + +void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume) +{ //called from main thread + S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail()); + + //get list of objects waiting to be notified this mesh is loaded + mesh_load_map::iterator obj_iter = mLoadingMeshes[detail].find(mesh_params); + + if (volume && obj_iter != mLoadingMeshes[detail].end()) + { + //make sure target volume is still valid + if (volume->getNumVolumeFaces() <= 0) + { + llwarns << "Mesh loading returned empty volume." << llendl; + volume->makeTetrahedron(); + } + + { //update system volume + LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, detail); + if (sys_volume) + { + sys_volume->copyVolumeFaces(volume); + LLPrimitive::getVolumeManager()->unrefVolume(sys_volume); + } + else + { + llwarns << "Couldn't find system volume for given mesh." << llendl; + } + } + + //notify waiting LLVOVolume instances that their requested mesh is available + for (std::set::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter) + { + LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter); + if (vobj) + { + vobj->notifyMeshLoaded(); + } + } + + mLoadingMeshes[detail].erase(mesh_params); + } +} + +void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod) +{ //called from main thread + //get list of objects waiting to be notified this mesh is loaded + mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_params); + + F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod); + + if (obj_iter != mLoadingMeshes[lod].end()) + { + for (std::set::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter) + { + LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter); + if (vobj) + { + LLVolume* obj_volume = vobj->getVolume(); + + if (obj_volume && + obj_volume->getDetail() == detail && + obj_volume->getParams() == mesh_params) + { //should force volume to find most appropriate LOD + vobj->setVolume(obj_volume->getParams(), lod); + } + } + } + + mLoadingMeshes[lod].erase(mesh_params); + } +} + +S32 LLMeshRepository::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod) +{ + return mThread->getActualMeshLOD(mesh_params, lod); +} + +U32 LLMeshRepository::calcResourceCost(LLSD& header) +{ + U32 bytes = 0; + + for (U32 i = 0; i < 4; i++) + { + bytes += header[header_lod[i]]["size"].asInteger(); + } + + bytes += header["skin"]["size"].asInteger(); + + return bytes/4096 + 1; +} + +U32 LLMeshRepository::getResourceCost(const LLUUID& mesh_id) +{ + return mThread->getResourceCost(mesh_id); +} + +const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id) +{ + if (mesh_id.notNull()) + { + skin_map::iterator iter = mSkinMap.find(mesh_id); + if (iter != mSkinMap.end()) + { + return &(iter->second); + } + + //no skin info known about given mesh, try to fetch it + { + LLMutexLock lock(mMeshMutex); + //add volume to list of loading meshes + std::set::iterator iter = mLoadingSkins.find(mesh_id); + if (iter == mLoadingSkins.end()) + { //no request pending for this skin info + mLoadingSkins.insert(mesh_id); + mPendingSkinRequests.push(mesh_id); + } + } + } + + return NULL; +} + +void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id) +{ + if (mesh_id.notNull()) + { + LLModel::Decomposition* decomp = NULL; + decomposition_map::iterator iter = mDecompositionMap.find(mesh_id); + if (iter != mDecompositionMap.end()) + { + decomp = iter->second; + } + + //decomposition block hasn't been fetched yet + if (!decomp || decomp->mPhysicsShapeMesh.empty()) + { + LLMutexLock lock(mMeshMutex); + //add volume to list of loading meshes + std::set::iterator iter = mLoadingPhysicsShapes.find(mesh_id); + if (iter == mLoadingPhysicsShapes.end()) + { //no request pending for this skin info + mLoadingPhysicsShapes.insert(mesh_id); + mPendingPhysicsShapeRequests.push(mesh_id); + } + } + } + +} + +LLModel::Decomposition* LLMeshRepository::getDecomposition(const LLUUID& mesh_id) +{ + LLModel::Decomposition* ret = NULL; + + if (mesh_id.notNull()) + { + decomposition_map::iterator iter = mDecompositionMap.find(mesh_id); + if (iter != mDecompositionMap.end()) + { + ret = iter->second; + } + + //decomposition block hasn't been fetched yet + if (!ret || ret->mBaseHullMesh.empty()) + { + LLMutexLock lock(mMeshMutex); + //add volume to list of loading meshes + std::set::iterator iter = mLoadingDecompositions.find(mesh_id); + if (iter == mLoadingDecompositions.end()) + { //no request pending for this skin info + mLoadingDecompositions.insert(mesh_id); + mPendingDecompositionRequests.push(mesh_id); + } + } + } + + return ret; +} + +void LLMeshRepository::buildHull(const LLVolumeParams& params, S32 detail) +{ + LLVolume* volume = LLPrimitive::sVolumeManager->refVolume(params, detail); + + if (!volume->mHullPoints) + { + //all default params + //execute first stage + //set simplify mode to retain + //set retain percentage to zero + //run second stage + } + + LLPrimitive::sVolumeManager->unrefVolume(volume); +} + +bool LLMeshRepository::hasPhysicsShape(const LLUUID& mesh_id) +{ + LLSD mesh = mThread->getMeshHeader(mesh_id); + return mesh.has("physics_shape") && mesh["physics_shape"].has("size") && (mesh["physics_shape"]["size"].asInteger() > 0); +} + +LLSD& LLMeshRepository::getMeshHeader(const LLUUID& mesh_id) +{ + return mThread->getMeshHeader(mesh_id); +} + +LLSD& LLMeshRepoThread::getMeshHeader(const LLUUID& mesh_id) +{ + static LLSD dummy_ret; + if (mesh_id.notNull()) + { + LLMutexLock lock(mHeaderMutex); + mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); + if (iter != mMeshHeader.end()) + { + return iter->second; + } + } + + return dummy_ret; +} + + +void LLMeshRepository::uploadModel(std::vector& data, LLVector3& scale, bool upload_textures, + bool upload_skin, bool upload_joints) +{ + LLMeshUploadThread* thread = new LLMeshUploadThread(data, scale, upload_textures, upload_skin, upload_joints); + mUploadWaitList.push_back(thread); +} + +S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod) +{ + if (mThread) + { + LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id); + if (iter != mThread->mMeshHeader.end()) + { + LLSD& header = iter->second; + + if (header.has("404")) + { + return -1; + } + + S32 size = header[header_lod[lod]]["size"].asInteger(); + return size; + } + + } + + return -1; + +} + +void LLMeshUploadThread::sendCostRequest(LLMeshUploadData& data) +{ + if(isDiscarded()) + { + return ; + } + + //write model file to memory buffer + std::stringstream ostr; + + LLModel::Decomposition& decomp = + data.mModel[LLModel::LOD_PHYSICS].notNull() ? + data.mModel[LLModel::LOD_PHYSICS]->mPhysics : + data.mBaseModel->mPhysics; + + LLSD header = LLModel::writeModel( + ostr, + data.mModel[LLModel::LOD_PHYSICS], + data.mModel[LLModel::LOD_HIGH], + data.mModel[LLModel::LOD_MEDIUM], + data.mModel[LLModel::LOD_LOW], + data.mModel[LLModel::LOD_IMPOSTOR], + decomp, + mUploadSkin, + mUploadJoints, + true); + + std::string desc = data.mBaseModel->mLabel; + + // Grab the total vertex count of the model + // along with other information for the "asset_resources" map + // to send to the server. + LLSD asset_resources = LLSD::emptyMap(); + + + std::string url = mNewInventoryCapability; + + if (!url.empty()) + { + LLSD body = generate_new_resource_upload_capability_body( + LLAssetType::AT_MESH, + desc, + desc, + LLFolderType::FT_MESH, + LLInventoryType::IT_MESH, + LLFloaterPerms::getNextOwnerPerms(), + LLFloaterPerms::getGroupPerms(), + LLFloaterPerms::getEveryonePerms()); + + body["asset_resources"] = asset_resources; + + mPendingConfirmations++; + LLCurlRequest::headers_t headers; + + data.mPostData = body; + + mCurlRequest->post(url, headers, body, new LLMeshCostResponder(data, this)); + } +} + +void LLMeshUploadThread::sendCostRequest(LLTextureUploadData& data) +{ + if(isDiscarded()) + { + return ; + } + + if (data.mTexture && data.mTexture->getDiscardLevel() >= 0) + { + LLSD asset_resources = LLSD::emptyMap(); + + std::string url = mNewInventoryCapability; + + if (!url.empty()) + { + LLSD body = generate_new_resource_upload_capability_body( + LLAssetType::AT_TEXTURE, + data.mLabel, + data.mLabel, + LLFolderType::FT_TEXTURE, + LLInventoryType::IT_TEXTURE, + LLFloaterPerms::getNextOwnerPerms(), + LLFloaterPerms::getGroupPerms(), + LLFloaterPerms::getEveryonePerms()); + + body["asset_resources"] = asset_resources; + + mPendingConfirmations++; + LLCurlRequest::headers_t headers; + + data.mPostData = body; + mCurlRequest->post(url, headers, body, new LLTextureCostResponder(data, this)); + } + } +} + + +void LLMeshUploadThread::doUploadModel(LLMeshUploadData& data) +{ + if(isDiscarded()) + { + return ; + } + + if (!data.mRSVP.empty()) + { + std::stringstream ostr; + + LLModel::Decomposition& decomp = + data.mModel[LLModel::LOD_PHYSICS].notNull() ? + data.mModel[LLModel::LOD_PHYSICS]->mPhysics : + data.mBaseModel->mPhysics; + + decomp.mBaseHull = mHullMap[data.mBaseModel]; + + LLModel::writeModel( + ostr, + data.mModel[LLModel::LOD_PHYSICS], + data.mModel[LLModel::LOD_HIGH], + data.mModel[LLModel::LOD_MEDIUM], + data.mModel[LLModel::LOD_LOW], + data.mModel[LLModel::LOD_IMPOSTOR], + decomp, + mUploadSkin, + mUploadJoints); + + data.mAssetData = ostr.str(); + + LLCurlRequest::headers_t headers; + mPendingUploads++; + + mCurlRequest->post(data.mRSVP, headers, data.mAssetData, new LLMeshUploadResponder(data, this)); + } +} + +void LLMeshUploadThread::doUploadTexture(LLTextureUploadData& data) +{ + if(isDiscarded()) + { + return ; + } + + if (!data.mRSVP.empty()) + { + std::stringstream ostr; + + if (!data.mTexture->isRawImageValid()) + { + data.mTexture->reloadRawImage(data.mTexture->getDiscardLevel()); + } + + LLPointer upload_file = LLViewerTextureList::convertToUploadFile(data.mTexture->getRawImage()); + + ostr.write((const char*) upload_file->getData(), upload_file->getDataSize()); + + data.mAssetData = ostr.str(); + + LLCurlRequest::headers_t headers; + mPendingUploads++; + + mCurlRequest->post(data.mRSVP, headers, data.mAssetData, new LLTextureUploadResponder(data, this)); + } +} + + +void LLMeshUploadThread::onModelUploaded(LLMeshUploadData& data) +{ + createObjects(data); +} + +void LLMeshUploadThread::onTextureUploaded(LLTextureUploadData& data) +{ + mTextureMap[data.mTexture] = data; +} + + +void LLMeshUploadThread::createObjects(LLMeshUploadData& data) +{ + instance_list& instances = mInstance[data.mBaseModel]; + + for (instance_list::iterator iter = instances.begin(); iter != instances.end(); ++iter) + { //create prims that reference given mesh + LLModelInstance& instance = *iter; + instance.mMeshID = data.mUUID; + mInstanceQ.push(instance); + } +} + +void LLMeshUploadThread::decomposeMeshMatrix(LLMatrix4& transformation, + LLVector3& result_pos, + LLQuaternion& result_rot, + LLVector3& result_scale) +{ + // check for reflection + BOOL reflected = (transformation.determinant() < 0); + + // compute position + LLVector3 position = LLVector3(0, 0, 0) * transformation; + + // compute scale + LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position; + LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position; + LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position; + F32 x_length = x_transformed.normalize(); + F32 y_length = y_transformed.normalize(); + F32 z_length = z_transformed.normalize(); + LLVector3 scale = LLVector3(x_length, y_length, z_length); + + // adjust for "reflected" geometry + LLVector3 x_transformed_reflected = x_transformed; + if (reflected) + { + x_transformed_reflected *= -1.0; + } + + // compute rotation + LLMatrix3 rotation_matrix; + rotation_matrix.setRows(x_transformed_reflected, y_transformed, z_transformed); + LLQuaternion quat_rotation = rotation_matrix.quaternion(); + quat_rotation.normalize(); // the rotation_matrix might not have been orthoginal. make it so here. + LLVector3 euler_rotation; + quat_rotation.getEulerAngles(&euler_rotation.mV[VX], &euler_rotation.mV[VY], &euler_rotation.mV[VZ]); + + result_pos = position + mOrigin; + result_scale = scale; + result_rot = quat_rotation; +} + + +LLSD LLMeshUploadThread::createObject(LLModelInstance& instance) +{ + LLMatrix4 transformation = instance.mTransform; + + if (instance.mMeshID.isNull()) + { + llerrs << "WTF?" << llendl; + } + + // check for reflection + BOOL reflected = (transformation.determinant() < 0); + + // compute position + LLVector3 position = LLVector3(0, 0, 0) * transformation; + + // compute scale + LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position; + LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position; + LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position; + F32 x_length = x_transformed.normalize(); + F32 y_length = y_transformed.normalize(); + F32 z_length = z_transformed.normalize(); + LLVector3 scale = LLVector3(x_length, y_length, z_length); + + // adjust for "reflected" geometry + LLVector3 x_transformed_reflected = x_transformed; + if (reflected) + { + x_transformed_reflected *= -1.0; + } + + // compute rotation + LLMatrix3 rotation_matrix; + rotation_matrix.setRows(x_transformed_reflected, y_transformed, z_transformed); + LLQuaternion quat_rotation = rotation_matrix.quaternion(); + quat_rotation.normalize(); // the rotation_matrix might not have been orthoginal. make it so here. + LLVector3 euler_rotation; + quat_rotation.getEulerAngles(&euler_rotation.mV[VX], &euler_rotation.mV[VY], &euler_rotation.mV[VZ]); + + // + // build parameter block to construct this prim + // + + LLSD object_params; + + // create prim + + // set volume params + U8 sculpt_type = LL_SCULPT_TYPE_MESH; + if (reflected) + { + sculpt_type |= LL_SCULPT_FLAG_MIRROR; + } + LLVolumeParams volume_params; + volume_params.setType( LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE ); + volume_params.setBeginAndEndS( 0.f, 1.f ); + volume_params.setBeginAndEndT( 0.f, 1.f ); + volume_params.setRatio ( 1, 1 ); + volume_params.setShear ( 0, 0 ); + volume_params.setSculptID(instance.mMeshID, sculpt_type); + object_params["shape"] = volume_params.asLLSD(); + + object_params["material"] = LL_MCODE_WOOD; + + object_params["group-id"] = gAgent.getGroupID(); + object_params["pos"] = ll_sd_from_vector3(position + mOrigin); + object_params["rotation"] = ll_sd_from_quaternion(quat_rotation); + object_params["scale"] = ll_sd_from_vector3(scale); + object_params["name"] = instance.mLabel; + + // load material from dae file + object_params["facelist"] = LLSD::emptyArray(); + for (S32 i = 0; i < instance.mMaterial.size(); i++) + { + LLTextureEntry te; + LLImportMaterial& mat = instance.mMaterial[i]; + + te.setColor(mat.mDiffuseColor); + + LLUUID diffuse_id = mTextureMap[mat.mDiffuseMap].mUUID; + + if (diffuse_id.notNull()) + { + te.setID(diffuse_id); + } + else + { + te.setID(LLUUID("5748decc-f629-461c-9a36-a35a221fe21f")); // blank texture + } + + te.setFullbright(mat.mFullbright); + + object_params["facelist"][i] = te.asLLSD(); + } + + // set extra parameters + LLSculptParams sculpt_params; + sculpt_params.setSculptTexture(instance.mMeshID); + sculpt_params.setSculptType(sculpt_type); + U8 buffer[MAX_OBJECT_PARAMS_SIZE+1]; + LLDataPackerBinaryBuffer dp(buffer, MAX_OBJECT_PARAMS_SIZE); + sculpt_params.pack(dp); + std::vector v(dp.getCurrentSize()); + memcpy(&v[0], buffer, dp.getCurrentSize()); + LLSD extra_parameter; + extra_parameter["extra_parameter"] = sculpt_params.mType; + extra_parameter["param_data"] = v; + object_params["extra_parameters"].append(extra_parameter); + + LLPermissions perm; + perm.setOwnerAndGroup(gAgent.getID(), gAgent.getID(), LLUUID::null, false); + perm.setCreator(gAgent.getID()); + + perm.initMasks(PERM_ITEM_UNRESTRICTED | PERM_MOVE, //base + PERM_ITEM_UNRESTRICTED | PERM_MOVE, //owner + LLFloaterPerms::getEveryonePerms(), + LLFloaterPerms::getGroupPerms(), + LLFloaterPerms::getNextOwnerPerms()); + + object_params["permissions"] = ll_create_sd_from_permissions(perm); + + object_params["physics_shape_type"] = (U8)(LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL); + + return object_params; +} + +void LLMeshUploadThread::priceResult(LLMeshUploadData& data, const LLSD& content) +{ + mPendingCost += content["upload_price"].asInteger(); + data.mRSVP = content["rsvp"].asString(); + + mConfirmedQ.push(data); +} + +void LLMeshUploadThread::priceResult(LLTextureUploadData& data, const LLSD& content) +{ + mPendingCost += content["upload_price"].asInteger(); + data.mRSVP = content["rsvp"].asString(); + + mConfirmedTextureQ.push(data); +} + + +bool LLImportMaterial::operator<(const LLImportMaterial &rhs) const +{ + if (mDiffuseMap != rhs.mDiffuseMap) + { + return mDiffuseMap < rhs.mDiffuseMap; + } + + if (mDiffuseMapFilename != rhs.mDiffuseMapFilename) + { + return mDiffuseMapFilename < rhs.mDiffuseMapFilename; + } + + if (mDiffuseMapLabel != rhs.mDiffuseMapLabel) + { + return mDiffuseMapLabel < rhs.mDiffuseMapLabel; + } + + if (mDiffuseColor != rhs.mDiffuseColor) + { + return mDiffuseColor < rhs.mDiffuseColor; + } + + return mFullbright < rhs.mFullbright; +} + + +void LLMeshRepository::updateInventory(inventory_data data) +{ + LLMutexLock lock(mMeshMutex); + mInventoryQ.push(data); +} + +void LLMeshRepository::uploadError(LLSD& args) +{ + LLMutexLock lock(mMeshMutex); + mUploadErrorQ.push(args); +} + +//static +F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32* bytes_visible, S32 lod) +{ + F32 dlowest = llmin(radius/0.03f, 256.f); + F32 dlow = llmin(radius/0.06f, 256.f); + F32 dmid = llmin(radius/0.24f, 256.f); + + F32 bytes_lowest = header["lowest_lod"]["size"].asReal()/1024.f; + F32 bytes_low = header["low_lod"]["size"].asReal()/1024.f; + F32 bytes_mid = header["medium_lod"]["size"].asReal()/1024.f; + F32 bytes_high = header["high_lod"]["size"].asReal()/1024.f; + + if (bytes) + { + *bytes = 0; + *bytes += header["lowest_lod"]["size"].asInteger(); + *bytes += header["low_lod"]["size"].asInteger(); + *bytes += header["medium_lod"]["size"].asInteger(); + *bytes += header["high_lod"]["size"].asInteger(); + } + + + if (bytes_visible) + { + lod = LLMeshRepository::getActualMeshLOD(header, lod); + if (lod >= 0 && lod <= 3) + { + *bytes_visible = header[header_lod[lod]]["size"].asInteger(); + } + } + + if (bytes_high == 0.f) + { + return 0.f; + } + + if (bytes_mid == 0.f) + { + bytes_mid = bytes_high; + } + + if (bytes_low == 0.f) + { + bytes_low = bytes_mid; + } + + if (bytes_lowest == 0.f) + { + bytes_lowest = bytes_low; + } + + F32 max_area = 65536.f; + F32 min_area = 1.f; + + F32 high_area = llmin(F_PI*dmid*dmid, max_area); + F32 mid_area = llmin(F_PI*dlow*dlow, max_area); + F32 low_area = llmin(F_PI*dlowest*dlowest, max_area); + F32 lowest_area = max_area; + + lowest_area -= low_area; + low_area -= mid_area; + mid_area -= high_area; + + high_area = llclamp(high_area, min_area, max_area); + mid_area = llclamp(mid_area, min_area, max_area); + low_area = llclamp(low_area, min_area, max_area); + lowest_area = llclamp(lowest_area, min_area, max_area); + + F32 total_area = high_area + mid_area + low_area + lowest_area; + high_area /= total_area; + mid_area /= total_area; + low_area /= total_area; + lowest_area /= total_area; + + F32 weighted_avg = bytes_high*high_area + + bytes_mid*mid_area + + bytes_low*low_area + + bytes_lowest*lowest_area; + + return weighted_avg * gSavedSettings.getF32("MeshStreamingCostScaler"); +} + + +LLPhysicsDecomp::LLPhysicsDecomp() +: LLThread("Physics Decomp") +{ + mInited = false; + mQuitting = false; + mDone = false; + + mSignal = new LLCondition(NULL); + mMutex = new LLMutex(NULL); +} + +LLPhysicsDecomp::~LLPhysicsDecomp() +{ + shutdown(); + + delete mSignal; + mSignal = NULL; + delete mMutex; + mMutex = NULL; +} + +void LLPhysicsDecomp::shutdown() +{ + if (mSignal) + { + mQuitting = true; + mSignal->signal(); + + while (!isStopped()) + { + apr_sleep(10); + } + } +} + +void LLPhysicsDecomp::submitRequest(LLPhysicsDecomp::Request* request) +{ + LLMutexLock lock(mMutex); + mRequestQ.push(request); + mSignal->signal(); +} + +//static +S32 LLPhysicsDecomp::llcdCallback(const char* status, S32 p1, S32 p2) +{ + if (gMeshRepo.mDecompThread && gMeshRepo.mDecompThread->mCurRequest.notNull()) + { + return gMeshRepo.mDecompThread->mCurRequest->statusCallback(status, p1, p2); + } + + return 1; +} + +void LLPhysicsDecomp::setMeshData(LLCDMeshData& mesh) +{ + mesh.mVertexBase = mCurRequest->mPositions[0].mV; + mesh.mVertexStrideBytes = 12; + mesh.mNumVertices = mCurRequest->mPositions.size(); + + mesh.mIndexType = LLCDMeshData::INT_16; + mesh.mIndexBase = &(mCurRequest->mIndices[0]); + mesh.mIndexStrideBytes = 6; + + mesh.mNumTriangles = mCurRequest->mIndices.size()/3; + + LLCDResult ret = LLCD_OK; + if (LLConvexDecomposition::getInstance() != NULL) + { + ret = LLConvexDecomposition::getInstance()->setMeshData(&mesh); + } + + if (ret) + { + llerrs << "Convex Decomposition thread valid but could not set mesh data" << llendl; + } +} + +void LLPhysicsDecomp::doDecomposition() +{ + LLCDMeshData mesh; + S32 stage = mStageID[mCurRequest->mStage]; + + if (LLConvexDecomposition::getInstance() == NULL) + { + // stub library. do nothing. + return; + } + + //load data intoLLCD + if (stage == 0) + { + setMeshData(mesh); + } + + //build parameter map + std::map param_map; + + static const LLCDParam* params = NULL; + static S32 param_count = 0; + if (!params) + { + param_count = LLConvexDecomposition::getInstance()->getParameters(¶ms); + } + + for (S32 i = 0; i < param_count; ++i) + { + param_map[params[i].mName] = params+i; + } + + //set parameter values + for (decomp_params::iterator iter = mCurRequest->mParams.begin(); iter != mCurRequest->mParams.end(); ++iter) + { + const std::string& name = iter->first; + const LLSD& value = iter->second; + + const LLCDParam* param = param_map[name]; + + if (param == NULL) + { //couldn't find valid parameter + continue; + } + + U32 ret = LLCD_OK; + + if (param->mType == LLCDParam::LLCD_FLOAT) + { + ret = LLConvexDecomposition::getInstance()->setParam(param->mName, (F32) value.asReal()); + } + else if (param->mType == LLCDParam::LLCD_INTEGER || + param->mType == LLCDParam::LLCD_ENUM) + { + ret = LLConvexDecomposition::getInstance()->setParam(param->mName, value.asInteger()); + } + else if (param->mType == LLCDParam::LLCD_BOOLEAN) + { + ret = LLConvexDecomposition::getInstance()->setParam(param->mName, value.asBoolean()); + } + + if (ret) + { + llerrs << "WTF?" << llendl; + } + } + + mCurRequest->setStatusMessage("Executing."); + + LLCDResult ret = LLCD_OK; + + if (LLConvexDecomposition::getInstance() != NULL) + { + ret = LLConvexDecomposition::getInstance()->executeStage(stage); + } + + if (ret) + { + llwarns << "Convex Decomposition thread valid but could not execute stage " << stage << llendl; + LLMutexLock lock(mMutex); + + mCurRequest->mHull.clear(); + mCurRequest->mHullMesh.clear(); + + mCurRequest->setStatusMessage("FAIL"); + + completeCurrent(); + } + else + { + mCurRequest->setStatusMessage("Reading results"); + + S32 num_hulls =0; + if (LLConvexDecomposition::getInstance() != NULL) + { + num_hulls = LLConvexDecomposition::getInstance()->getNumHullsFromStage(stage); + } + + mMutex->lock(); + mCurRequest->mHull.clear(); + mCurRequest->mHull.resize(num_hulls); + + mCurRequest->mHullMesh.clear(); + mCurRequest->mHullMesh.resize(num_hulls); + mMutex->unlock(); + + for (S32 i = 0; i < num_hulls; ++i) + { + std::vector p; + LLCDHull hull; + // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code + LLConvexDecomposition::getInstance()->getHullFromStage(stage, i, &hull); + + const F32* v = hull.mVertexBase; + + for (S32 j = 0; j < hull.mNumVertices; ++j) + { + LLVector3 vert(v[0], v[1], v[2]); + p.push_back(vert); + v = (F32*) (((U8*) v) + hull.mVertexStrideBytes); + } + + LLCDMeshData mesh; + // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code + LLConvexDecomposition::getInstance()->getMeshFromStage(stage, i, &mesh); + + get_vertex_buffer_from_mesh(mesh, mCurRequest->mHullMesh[i]); + + mMutex->lock(); + mCurRequest->mHull[i] = p; + mMutex->unlock(); + } + + { + LLMutexLock lock(mMutex); + + mCurRequest->setStatusMessage("FAIL"); + completeCurrent(); + } + } +} + +void LLPhysicsDecomp::completeCurrent() +{ + LLMutexLock lock(mMutex); + mCompletedQ.push(mCurRequest); + mCurRequest = NULL; +} + +void LLPhysicsDecomp::notifyCompleted() +{ + if (!mCompletedQ.empty()) + { + LLMutexLock lock(mMutex); + while (!mCompletedQ.empty()) + { + Request* req = mCompletedQ.front(); + req->completed(); + mCompletedQ.pop(); + } + } +} + + +void make_box(LLPhysicsDecomp::Request * request) +{ + LLVector3 min,max; + min = request->mPositions[0]; + max = min; + + for (U32 i = 0; i < request->mPositions.size(); ++i) + { + update_min_max(min, max, request->mPositions[i]); + } + + request->mHull.clear(); + + LLModel::hull box; + box.push_back(LLVector3(min[0],min[1],min[2])); + box.push_back(LLVector3(max[0],min[1],min[2])); + box.push_back(LLVector3(min[0],max[1],min[2])); + box.push_back(LLVector3(max[0],max[1],min[2])); + box.push_back(LLVector3(min[0],min[1],max[2])); + box.push_back(LLVector3(max[0],min[1],max[2])); + box.push_back(LLVector3(min[0],max[1],max[2])); + box.push_back(LLVector3(max[0],max[1],max[2])); + + request->mHull.push_back(box); +} + + +void LLPhysicsDecomp::doDecompositionSingleHull() +{ + LLCDMeshData mesh; + + setMeshData(mesh); + + + //set all parameters to default + std::map param_map; + + static const LLCDParam* params = NULL; + static S32 param_count = 0; + + if (!params) + { + param_count = LLConvexDecomposition::getInstance()->getParameters(¶ms); + } + + LLConvexDecomposition* decomp = LLConvexDecomposition::getInstance(); + + if (decomp == NULL) + { + //stub. do nothing. + return; + } + + for (S32 i = 0; i < param_count; ++i) + { + decomp->setParam(params[i].mName, params[i].mDefault.mIntOrEnumValue); + } + + const S32 STAGE_DECOMPOSE = mStageID["Decompose"]; + const S32 STAGE_SIMPLIFY = mStageID["Simplify"]; + const S32 DECOMP_PREVIEW = 0; + const S32 SIMPLIFY_RETAIN = 0; + + decomp->setParam("Decompose Quality", DECOMP_PREVIEW); + decomp->setParam("Simplify Method", SIMPLIFY_RETAIN); + decomp->setParam("Retain%", 0.f); + + LLCDResult ret = LLCD_OK; + ret = decomp->executeStage(STAGE_DECOMPOSE); + + if (ret) + { + llwarns << "Could not execute decomposition stage when attempting to create single hull." << llendl; + make_box(mCurRequest); + } + else + { + ret = decomp->executeStage(STAGE_SIMPLIFY); + + if (ret) + { + llwarns << "Could not execute simiplification stage when attempting to create single hull." << llendl; + make_box(mCurRequest); + } + else + { + S32 num_hulls =0; + if (LLConvexDecomposition::getInstance() != NULL) + { + num_hulls = LLConvexDecomposition::getInstance()->getNumHullsFromStage(STAGE_SIMPLIFY); + } + + mMutex->lock(); + mCurRequest->mHull.clear(); + mCurRequest->mHull.resize(num_hulls); + mCurRequest->mHullMesh.clear(); + mMutex->unlock(); + + for (S32 i = 0; i < num_hulls; ++i) + { + std::vector p; + LLCDHull hull; + // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code + LLConvexDecomposition::getInstance()->getHullFromStage(STAGE_SIMPLIFY, i, &hull); + + const F32* v = hull.mVertexBase; + + for (S32 j = 0; j < hull.mNumVertices; ++j) + { + LLVector3 vert(v[0], v[1], v[2]); + p.push_back(vert); + v = (F32*) (((U8*) v) + hull.mVertexStrideBytes); + } + + mMutex->lock(); + mCurRequest->mHull[i] = p; + mMutex->unlock(); + } + } + } + + + { + completeCurrent(); + + } +} + + +void LLPhysicsDecomp::run() +{ + LLConvexDecomposition* decomp = LLConvexDecomposition::getInstance(); + if (decomp == NULL) + { + // stub library. Set init to true so the main thread + // doesn't wait for this to finish. + mInited = true; + return; + } + + decomp->initThread(); + mInited = true; + + static const LLCDStageData* stages = NULL; + static S32 num_stages = 0; + + if (!stages) + { + num_stages = decomp->getStages(&stages); + } + + for (S32 i = 0; i < num_stages; i++) + { + mStageID[stages[i].mName] = i; + } + + while (!mQuitting) + { + mSignal->wait(); + while (!mQuitting && !mRequestQ.empty()) + { + { + LLMutexLock lock(mMutex); + mCurRequest = mRequestQ.front(); + mRequestQ.pop(); + } + + S32& id = *(mCurRequest->mDecompID); + if (id == -1) + { + decomp->genDecomposition(id); + } + decomp->bindDecomposition(id); + + if (mCurRequest->mStage == "single_hull") + { + doDecompositionSingleHull(); + } + else + { + doDecomposition(); + } + } + } + + decomp->quitThread(); + + if (mSignal->isLocked()) + { //let go of mSignal's associated mutex + mSignal->unlock(); + } + + mDone = true; +} + +void LLPhysicsDecomp::Request::setStatusMessage(const std::string& msg) +{ + mStatusMessage = msg; +} + +LLModelInstance::LLModelInstance(LLSD& data) +{ + mLocalMeshID = data["mesh_id"].asInteger(); + mLabel = data["label"].asString(); + mTransform.setValue(data["transform"]); + + for (U32 i = 0; i < data["material"].size(); ++i) + { + mMaterial.push_back(LLImportMaterial(data["material"][i])); + } +} + + +LLSD LLModelInstance::asLLSD() +{ + LLSD ret; + + ret["mesh_id"] = mModel->mLocalID; + ret["label"] = mLabel; + ret["transform"] = mTransform.getValue(); + + for (U32 i = 0; i < mMaterial.size(); ++i) + { + ret["material"][i] = mMaterial[i].asLLSD(); + } + + return ret; +} + +LLImportMaterial::LLImportMaterial(LLSD& data) +{ + mDiffuseMapFilename = data["diffuse"]["filename"].asString(); + mDiffuseMapLabel = data["diffuse"]["label"].asString(); + mDiffuseColor.setValue(data["diffuse"]["color"]); + mFullbright = data["fullbright"].asBoolean(); +} + + +LLSD LLImportMaterial::asLLSD() +{ + LLSD ret; + + ret["diffuse"]["filename"] = mDiffuseMapFilename; + ret["diffuse"]["label"] = mDiffuseMapLabel; + ret["diffuse"]["color"] = mDiffuseColor.getValue(); + ret["fullbright"] = mFullbright; + + return ret; +} + +void LLMeshRepository::buildPhysicsMesh(LLModel::Decomposition& decomp) +{ + decomp.mMesh.resize(decomp.mHull.size()); + + for (U32 i = 0; i < decomp.mHull.size(); ++i) + { + LLCDHull hull; + hull.mNumVertices = decomp.mHull[i].size(); + hull.mVertexBase = decomp.mHull[i][0].mV; + hull.mVertexStrideBytes = 12; + + LLCDMeshData mesh; + LLCDResult res = LLCD_OK; + if (LLConvexDecomposition::getInstance() != NULL) + { + res = LLConvexDecomposition::getInstance()->getMeshFromHull(&hull, &mesh); + } + if (res == LLCD_OK) + { + get_vertex_buffer_from_mesh(mesh, decomp.mMesh[i]); + } + } + + if (!decomp.mBaseHull.empty() && decomp.mBaseHullMesh.empty()) + { //get mesh for base hull + LLCDHull hull; + hull.mNumVertices = decomp.mBaseHull.size(); + hull.mVertexBase = decomp.mBaseHull[0].mV; + hull.mVertexStrideBytes = 12; + + LLCDMeshData mesh; + LLCDResult res = LLCD_OK; + if (LLConvexDecomposition::getInstance() != NULL) + { + res = LLConvexDecomposition::getInstance()->getMeshFromHull(&hull, &mesh); + } + if (res == LLCD_OK) + { + get_vertex_buffer_from_mesh(mesh, decomp.mBaseHullMesh); + } + } +} diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h new file mode 100644 index 000000000..f859e29c0 --- /dev/null +++ b/indra/newview/llmeshrepository.h @@ -0,0 +1,552 @@ +/** + * @file llmeshrepository.h + * @brief Client-side repository of mesh assets. + * + * $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_MESH_REPOSITORY_H +#define LL_MESH_REPOSITORY_H + +#include "llassettype.h" +#include "llmodel.h" +#include "lluuid.h" +#include "llviewertexture.h" +#include "llvolume.h" + +#define LLCONVEXDECOMPINTER_STATIC 1 + +#include "llconvexdecomposition.h" + +class LLVOVolume; +class LLMeshResponder; +class LLCurlRequest; +class LLMutex; +class LLCondition; +class LLVFS; +class LLMeshRepository; + +class LLMeshUploadData +{ +public: + LLPointer mBaseModel; + LLPointer mModel[5]; + LLUUID mUUID; + U32 mRetries; + std::string mRSVP; + std::string mAssetData; + LLSD mPostData; + + LLMeshUploadData() + { + mRetries = 0; + } +}; + +class LLTextureUploadData +{ +public: + LLViewerFetchedTexture* mTexture; + LLUUID mUUID; + std::string mRSVP; + std::string mLabel; + U32 mRetries; + std::string mAssetData; + LLSD mPostData; + + LLTextureUploadData() + { + mRetries = 0; + } + + LLTextureUploadData(LLViewerFetchedTexture* texture, std::string& label) + : mTexture(texture), mLabel(label) + { + mRetries = 0; + } +}; + +class LLImportMaterial +{ +public: + LLPointer mDiffuseMap; + std::string mDiffuseMapFilename; + std::string mDiffuseMapLabel; + LLColor4 mDiffuseColor; + bool mFullbright; + + bool operator<(const LLImportMaterial ¶ms) const; + + LLImportMaterial() + : mFullbright(false) + { + mDiffuseColor.set(1,1,1,1); + } + + LLImportMaterial(LLSD& data); + + LLSD asLLSD(); +}; + +class LLModelInstance +{ +public: + LLPointer mModel; + LLPointer mLOD[5]; + + std::string mLabel; + + LLUUID mMeshID; + S32 mLocalMeshID; + + LLMatrix4 mTransform; + std::vector mMaterial; + + LLModelInstance(LLModel* model, const std::string& label, LLMatrix4& transform, std::vector& materials) + : mModel(model), mLabel(label), mTransform(transform), mMaterial(materials) + { + mLocalMeshID = -1; + } + + LLModelInstance(LLSD& data); + + LLSD asLLSD(); +}; + +class LLPhysicsDecomp : public LLThread +{ +public: + + typedef std::map decomp_params; + + class Request : public LLRefCount + { + public: + //input params + S32* mDecompID; + std::string mStage; + std::vector mPositions; + std::vector mIndices; + decomp_params mParams; + + //output state + std::string mStatusMessage; + std::vector mHullMesh; + LLModel::convex_hull_decomposition mHull; + + //status message callback, called from decomposition thread + virtual S32 statusCallback(const char* status, S32 p1, S32 p2) = 0; + + //completed callback, called from the main thread + virtual void completed() = 0; + + virtual void setStatusMessage(const std::string& msg); + }; + + LLCondition* mSignal; + LLMutex* mMutex; + + bool mInited; + bool mQuitting; + bool mDone; + + LLPhysicsDecomp(); + ~LLPhysicsDecomp(); + + void shutdown(); + + void submitRequest(Request* request); + static S32 llcdCallback(const char*, S32, S32); + void cancel(); + + void setMeshData(LLCDMeshData& mesh); + void doDecomposition(); + void doDecompositionSingleHull(); + + virtual void run(); + + void completeCurrent(); + void notifyCompleted(); + + std::map mStageID; + + typedef std::queue > request_queue; + request_queue mRequestQ; + + LLPointer mCurRequest; + + std::queue > mCompletedQ; + +}; + +class LLMeshRepoThread : public LLThread +{ +public: + + static S32 sActiveHeaderRequests; + static S32 sActiveLODRequests; + static U32 sMaxConcurrentRequests; + + LLCurlRequest* mCurlRequest; + LLMutex* mMutex; + LLMutex* mHeaderMutex; + LLCondition* mSignal; + + bool mWaiting; + + //map of known mesh headers + typedef std::map mesh_header_map; + mesh_header_map mMeshHeader; + + std::map mMeshHeaderSize; + std::map mMeshResourceCost; + + class HeaderRequest + { + public: + const LLVolumeParams mMeshParams; + + HeaderRequest(const LLVolumeParams& mesh_params) + : mMeshParams(mesh_params) + { + } + + bool operator<(const HeaderRequest& rhs) const + { + return mMeshParams < rhs.mMeshParams; + } + }; + + class LODRequest + { + public: + LLVolumeParams mMeshParams; + S32 mLOD; + F32 mScore; + + LODRequest(const LLVolumeParams& mesh_params, S32 lod) + : mMeshParams(mesh_params), mLOD(lod), mScore(0.f) + { + } + }; + + struct CompareScoreGreater + { + bool operator()(const LODRequest& lhs, const LODRequest& rhs) + { + return lhs.mScore > rhs.mScore; // greatest = first + } + }; + + + class LoadedMesh + { + public: + LLPointer mVolume; + LLVolumeParams mMeshParams; + S32 mLOD; + + LoadedMesh(LLVolume* volume, const LLVolumeParams& mesh_params, S32 lod) + : mVolume(volume), mMeshParams(mesh_params), mLOD(lod) + { + } + + }; + + //set of requested skin info + std::set mSkinRequests; + + //queue of completed skin info requests + std::queue mSkinInfoQ; + + //set of requested decompositions + std::set mDecompositionRequests; + + //set of requested physics shapes + std::set mPhysicsShapeRequests; + + //queue of completed Decomposition info requests + std::queue mDecompositionQ; + + //queue of requested headers + std::queue mHeaderReqQ; + + //queue of requested LODs + std::queue mLODReqQ; + + //queue of unavailable LODs (either asset doesn't exist or asset doesn't have desired LOD) + std::queue mUnavailableQ; + + //queue of successfully loaded meshes + std::queue mLoadedQ; + + //map of pending header requests and currently desired LODs + typedef std::map > pending_lod_map; + pending_lod_map mPendingLOD; + + static std::string constructUrl(LLUUID mesh_id); + + LLMeshRepoThread(); + ~LLMeshRepoThread(); + + virtual void run(); + + void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod); + bool fetchMeshHeader(const LLVolumeParams& mesh_params); + bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod); + bool headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size); + bool lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size); + bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size); + bool decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size); + bool physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size); + LLSD& getMeshHeader(const LLUUID& mesh_id); + + void notifyLoadedMeshes(); + S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod); + U32 getResourceCost(const LLUUID& mesh_params); + + void loadMeshSkinInfo(const LLUUID& mesh_id); + void loadMeshDecomposition(const LLUUID& mesh_id); + void loadMeshPhysicsShape(const LLUUID& mesh_id); + + //send request for skin info, returns true if header info exists + // (should hold onto mesh_id and try again later if header info does not exist) + bool fetchMeshSkinInfo(const LLUUID& mesh_id); + + //send request for decomposition, returns true if header info exists + // (should hold onto mesh_id and try again later if header info does not exist) + bool fetchMeshDecomposition(const LLUUID& mesh_id); + + //send request for PhysicsShape, returns true if header info exists + // (should hold onto mesh_id and try again later if header info does not exist) + bool fetchMeshPhysicsShape(const LLUUID& mesh_id); + + +}; + +class LLMeshUploadThread : public LLThread +{ +public: + class DecompRequest : public LLPhysicsDecomp::Request + { + public: + LLPointer mModel; + LLPointer mBaseModel; + + LLMeshUploadThread* mThread; + + DecompRequest(LLModel* mdl, LLModel* base_model, LLMeshUploadThread* thread); + + S32 statusCallback(const char* status, S32 p1, S32 p2) { return 1; } + void completed(); + }; + + LLPointer mFinalDecomp; + bool mPhysicsComplete; + + typedef std::map, std::vector > hull_map; + hull_map mHullMap; + + typedef std::vector instance_list; + instance_list mInstanceList; + + typedef std::map, instance_list> instance_map; + instance_map mInstance; + + LLMutex* mMutex; + LLCurlRequest* mCurlRequest; + S32 mPendingConfirmations; + S32 mPendingUploads; + S32 mPendingCost; + LLVector3 mOrigin; + bool mFinished; + bool mUploadTextures; + bool mUploadSkin; + bool mUploadJoints; + BOOL mDiscarded ; + + LLHost mHost; + std::string mUploadObjectAssetCapability; + std::string mNewInventoryCapability; + std::string mWholeModelFeeCapability; + std::string mWholeModelUploadURL; + + std::queue mUploadQ; + std::queue mConfirmedQ; + std::queue mInstanceQ; + + std::queue mTextureQ; + std::queue mConfirmedTextureQ; + + std::map mTextureMap; + + LLMeshUploadThread(instance_list& data, LLVector3& scale, bool upload_textures, + bool upload_skin, bool upload_joints); + ~LLMeshUploadThread(); + + void uploadTexture(LLTextureUploadData& data); + void doUploadTexture(LLTextureUploadData& data); + void sendCostRequest(LLTextureUploadData& data); + void priceResult(LLTextureUploadData& data, const LLSD& content); + void onTextureUploaded(LLTextureUploadData& data); + + void uploadModel(LLMeshUploadData& data); + void sendCostRequest(LLMeshUploadData& data); + void doUploadModel(LLMeshUploadData& data); + void onModelUploaded(LLMeshUploadData& data); + void createObjects(LLMeshUploadData& data); + LLSD createObject(LLModelInstance& instance); + void priceResult(LLMeshUploadData& data, const LLSD& content); + + bool finished() { return mFinished; } + virtual void run(); + void preStart(); + void discard() ; + BOOL isDiscarded(); + + void doWholeModelUpload(); + void doIterativeUpload(); + + void wholeModelToLLSD(LLSD& dest, bool include_textures); + + void decomposeMeshMatrix(LLMatrix4& transformation, + LLVector3& result_pos, + LLQuaternion& result_rot, + LLVector3& result_scale); +}; + +class LLMeshRepository +{ +public: + + //metrics + static U32 sBytesReceived; + static U32 sHTTPRequestCount; + static U32 sHTTPRetryCount; + static U32 sCacheBytesRead; + static U32 sCacheBytesWritten; + static U32 sPeakKbps; + + static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1); + + LLMeshRepository(); + + void init(); + void shutdown(); + S32 update() ; + + //mesh management functions + S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 detail = 0, S32 last_lod = -1); + + void notifyLoadedMeshes(); + void notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume); + void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod); + void notifySkinInfoReceived(LLMeshSkinInfo& info); + void notifyDecompositionReceived(LLModel::Decomposition* info); + + S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod); + static S32 getActualMeshLOD(LLSD& header, S32 lod); + U32 calcResourceCost(LLSD& header); + U32 getResourceCost(const LLUUID& mesh_params); + const LLMeshSkinInfo* getSkinInfo(const LLUUID& mesh_id); + LLModel::Decomposition* getDecomposition(const LLUUID& mesh_id); + void fetchPhysicsShape(const LLUUID& mesh_id); + bool hasPhysicsShape(const LLUUID& mesh_id); + + void buildHull(const LLVolumeParams& params, S32 detail); + void buildPhysicsMesh(LLModel::Decomposition& decomp); + + LLSD& getMeshHeader(const LLUUID& mesh_id); + + void uploadModel(std::vector& data, LLVector3& scale, bool upload_textures, + bool upload_skin, bool upload_joints); + + S32 getMeshSize(const LLUUID& mesh_id, S32 lod); + + typedef std::map > mesh_load_map; + mesh_load_map mLoadingMeshes[4]; + + typedef std::map skin_map; + skin_map mSkinMap; + + typedef std::map decomposition_map; + decomposition_map mDecompositionMap; + + LLMutex* mMeshMutex; + + std::vector mPendingRequests; + + //list of mesh ids awaiting skin info + std::set mLoadingSkins; + + //list of mesh ids that need to send skin info fetch requests + std::queue mPendingSkinRequests; + + //list of mesh ids awaiting decompositions + std::set mLoadingDecompositions; + + //list of mesh ids that need to send decomposition fetch requests + std::queue mPendingDecompositionRequests; + + //list of mesh ids awaiting physics shapes + std::set mLoadingPhysicsShapes; + + //list of mesh ids that need to send physics shape fetch requests + std::queue mPendingPhysicsShapeRequests; + + U32 mMeshThreadCount; + + void cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header); + + LLMeshRepoThread* mThread; + std::vector mUploads; + std::vector mUploadWaitList; + + LLPhysicsDecomp* mDecompThread; + + class inventory_data + { + public: + LLSD mPostData; + LLSD mResponse; + + inventory_data(const LLSD& data, const LLSD& content) + : mPostData(data), mResponse(content) + { + } + }; + + std::queue mInventoryQ; + + std::queue mUploadErrorQ; + + void uploadError(LLSD& args); + void updateInventory(inventory_data data); + + std::string mGetMeshCapability; + +}; + +extern LLMeshRepository gMeshRepo; + +#endif + diff --git a/indra/newview/llphysicsshapebuilderutil.cpp b/indra/newview/llphysicsshapebuilderutil.cpp new file mode 100644 index 000000000..5bfe5c994 --- /dev/null +++ b/indra/newview/llphysicsshapebuilderutil.cpp @@ -0,0 +1,210 @@ +/** + * @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" + +#include "llphysicsshapebuilderutil.h" + +/* static */ +void LLPhysicsShapeBuilderUtil::determinePhysicsShape( const LLPhysicsVolumeParams& volume_params, const LLVector3& scale, PhysicsShapeSpecification& specOut ) +{ + const LLProfileParams& profile_params = volume_params.getProfileParams(); + const LLPathParams& path_params = volume_params.getPathParams(); + + specOut.mScale = scale; + + const F32 avgScale = ( scale[VX] + scale[VY] + scale[VZ] )/3.0f; + + // count the scale elements that are small + S32 min_size_counts = 0; + for (S32 i = 0; i < 3; ++i) + { + if (scale[i] < SHAPE_BUILDER_CONVEXIFICATION_SIZE) + { + ++min_size_counts; + } + } + + const bool profile_complete = ( profile_params.getBegin() <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale ) && + ( profile_params.getEnd() >= (1.0f - SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale) ); + + const bool path_complete = ( path_params.getBegin() <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale ) && + ( path_params.getEnd() >= (1.0f - SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale) ); + + const bool simple_params = ( volume_params.getHollow() <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_HOLLOW/avgScale ) + && ( fabs(path_params.getShearX()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_SHEAR/avgScale ) + && ( fabs(path_params.getShearY()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_SHEAR/avgScale ) + && ( !volume_params.isMeshSculpt() && !volume_params.isSculpt() ); + + if (simple_params && profile_complete) + { + // Try to create an implicit shape or convexified + bool no_taper = ( fabs(path_params.getScaleX() - 1.0f) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TAPER/avgScale ) + && ( fabs(path_params.getScaleY() - 1.0f) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TAPER/avgScale ); + + bool no_twist = ( fabs(path_params.getTwistBegin()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TWIST/avgScale ) + && ( fabs(path_params.getTwistEnd()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TWIST/avgScale); + + // Box + if( + ( profile_params.getCurveType() == LL_PCODE_PROFILE_SQUARE ) + && ( path_params.getCurveType() == LL_PCODE_PATH_LINE ) + && no_taper + && no_twist + ) + { + specOut.mType = PhysicsShapeSpecification::BOX; + if ( path_complete ) + { + return; + } + else + { + // Side lengths + specOut.mScale[VX] = llmax( scale[VX], SHAPE_BUILDER_MIN_GEOMETRY_SIZE ); + specOut.mScale[VY] = llmax( scale[VY], SHAPE_BUILDER_MIN_GEOMETRY_SIZE ); + specOut.mScale[VZ] = llmax( scale[VZ] * (path_params.getEnd() - path_params.getBegin()), SHAPE_BUILDER_MIN_GEOMETRY_SIZE ); + + specOut.mCenter.set( 0.f, 0.f, 0.5f * scale[VZ] * ( path_params.getEnd() + path_params.getBegin() - 1.0f ) ); + return; + } + } + + // Sphere + if( path_complete + && ( profile_params.getCurveType() == LL_PCODE_PROFILE_CIRCLE_HALF ) + && ( path_params.getCurveType() == LL_PCODE_PATH_CIRCLE ) + && ( fabs(volume_params.getTaper()) <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_TAPER/avgScale ) + && no_twist + ) + { + if ( ( scale[VX] == scale[VZ] ) + && ( scale[VY] == scale[VZ] ) ) + { + // perfect sphere + specOut.mType = PhysicsShapeSpecification::SPHERE; + specOut.mScale = scale; + return; + } + else if (min_size_counts > 1) + { + // small or narrow sphere -- we can boxify + for (S32 i=0; i<3; ++i) + { + if (specOut.mScale[i] < SHAPE_BUILDER_CONVEXIFICATION_SIZE) + { + // reduce each small dimension size to split the approximation errors + specOut.mScale[i] *= 0.75f; + } + } + specOut.mType = PhysicsShapeSpecification::BOX; + return; + } + } + + // Cylinder + if( (scale[VX] == scale[VY]) + && ( profile_params.getCurveType() == LL_PCODE_PROFILE_CIRCLE ) + && ( path_params.getCurveType() == LL_PCODE_PATH_LINE ) + && ( volume_params.getBeginS() <= SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale ) + && ( volume_params.getEndS() >= (1.0f - SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT/avgScale) ) + && no_taper + ) + { + if (min_size_counts > 1) + { + // small or narrow sphere -- we can boxify + for (S32 i=0; i<3; ++i) + { + if (specOut.mScale[i] < SHAPE_BUILDER_CONVEXIFICATION_SIZE) + { + // reduce each small dimension size to split the approximation errors + specOut.mScale[i] *= 0.75f; + } + } + + specOut.mType = PhysicsShapeSpecification::BOX; + } + else + { + specOut.mType = PhysicsShapeSpecification::CYLINDER; + F32 length = (volume_params.getPathParams().getEnd() - volume_params.getPathParams().getBegin()) * scale[VZ]; + + specOut.mScale[VY] = specOut.mScale[VX]; + specOut.mScale[VZ] = length; + // The minus one below fixes the fact that begin and end range from 0 to 1 not -1 to 1. + specOut.mCenter.set( 0.f, 0.f, 0.5f * (volume_params.getPathParams().getBegin() + volume_params.getPathParams().getEnd() - 1.f) * scale[VZ] ); + } + + return; + } + } + + if ( (min_size_counts == 3 ) + // Possible dead code here--who wants to take it out? + || (path_complete + && profile_complete + && ( path_params.getCurveType() == LL_PCODE_PATH_LINE ) + && (min_size_counts > 1 ) ) + ) + { + // it isn't simple but + // we might be able to convexify this shape if the path and profile are complete + // or the path is linear and both path and profile are complete --> we can boxify it + specOut.mType = PhysicsShapeSpecification::BOX; + specOut.mScale = scale; + return; + } + + // Special case for big, very thin objects - bump the small dimensions up to the COLLISION_TOLERANCE + if (min_size_counts == 1 // One dimension is small + && avgScale > 3.f) // ... but others are fairly large + { + for (S32 i = 0; i < 3; ++i) + { + specOut.mScale[i] = llmax( specOut.mScale[i], COLLISION_TOLERANCE ); + } + } + + if ( volume_params.shouldForceConvex() ) + { + specOut.mType = PhysicsShapeSpecification::USER_CONVEX; + } + // Make a simpler convex shape if we can. + else if (volume_params.isConvex() // is convex + || min_size_counts > 1 ) // two or more small dimensions + { + specOut.mType = PhysicsShapeSpecification::PRIM_CONVEX; + } + else if ( volume_params.isSculpt() ) // Is a sculpt of any kind (mesh or legacy) + { + specOut.mType = volume_params.isMeshSculpt() ? PhysicsShapeSpecification::USER_MESH : PhysicsShapeSpecification::SCULPT; + } + else // Resort to mesh + { + specOut.mType = PhysicsShapeSpecification::PRIM_MESH; + } +} diff --git a/indra/newview/llphysicsshapebuilderutil.h b/indra/newview/llphysicsshapebuilderutil.h new file mode 100644 index 000000000..7dedfb05e --- /dev/null +++ b/indra/newview/llphysicsshapebuilderutil.h @@ -0,0 +1,138 @@ +/** + * @file llphysicsshapebuilder.h + * @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$ + */ + +#ifndef LL_PHYSICS_SHAPE_BUILDER_H +#define LL_PHYSICS_SHAPE_BUILDER_H + +#include "indra_constants.h" +#include "llvolume.h" + +#define USE_SHAPE_QUANTIZATION 0 + +#define SHAPE_BUILDER_DEFAULT_VOLUME_DETAIL 1 + +#define SHAPE_BUILDER_IMPLICIT_THRESHOLD_HOLLOW 0.10f +#define SHAPE_BUILDER_IMPLICIT_THRESHOLD_HOLLOW_SPHERES 0.90f +#define SHAPE_BUILDER_IMPLICIT_THRESHOLD_PATH_CUT 0.05f +#define SHAPE_BUILDER_IMPLICIT_THRESHOLD_TAPER 0.05f +#define SHAPE_BUILDER_IMPLICIT_THRESHOLD_TWIST 0.09f +#define SHAPE_BUILDER_IMPLICIT_THRESHOLD_SHEAR 0.05f + +const F32 SHAPE_BUILDER_ENTRY_SNAP_SCALE_BIN_SIZE = 0.15f; +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 ¶ms) const + { + return ( LLVolumeParams::operator==(params) && (mForceConvex == params.mForceConvex) ); + } + + bool operator!=(const LLPhysicsVolumeParams ¶ms) const + { + return !operator==(params); + } + + bool operator<(const LLPhysicsVolumeParams ¶ms) 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 +{ +public: + + class PhysicsShapeSpecification + { + public: + enum ShapeType + { + // Primitive types + BOX, + SPHERE, + CYLINDER, + + USER_CONVEX, // User specified they wanted the convex hull of the volume + + PRIM_CONVEX, // Either a volume that is inherently convex but not a primitive type, or a shape + // with dimensions such that will convexify it anyway. + + SCULPT, // Special case for traditional sculpts--they are the convex hull of a single particular set of volume params + + USER_MESH, // A user mesh. May or may not contain a convex decomposition. + + PRIM_MESH, // A non-convex volume which we have to represent accurately + + INVALID + }; + + PhysicsShapeSpecification() : + mType( INVALID ), + mScale( 0.f, 0.f, 0.f ), + mCenter( 0.f, 0.f, 0.f ) {} + + bool isConvex() { return (mType != USER_MESH && mType != PRIM_MESH && mType != INVALID); } + bool isMesh() { return (mType == USER_MESH) || (mType == PRIM_MESH); } + + ShapeType getType() { return mType; } + const LLVector3& getScale() { return mScale; } + const LLVector3& getCenter() { return mCenter; } + + private: + friend class LLPhysicsShapeBuilderUtil; + + ShapeType mType; + + // Dimensions of an AABB around the shape + LLVector3 mScale; + + // Offset of shape from origin of primitive's reference frame + LLVector3 mCenter; + }; + + static void determinePhysicsShape( const LLPhysicsVolumeParams& volume_params, const LLVector3& scale, PhysicsShapeSpecification& specOut ); +}; + +#endif //LL_PHYSICS_SHAPE_BUILDER_H diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 9bea903ff..6248f1edf 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -5457,6 +5457,111 @@ BOOL LLSelectNode::allowOperationOnNode(PermissionBit op, U64 group_proxy_power) return (mPermissions->allowOperationBy(op, proxy_agent_id, group_id)); } +#if MESH_ENABLED +void pushWireframe(LLDrawable* drawable) +{ + if (drawable->isState(LLDrawable::RIGGED)) + { //render straight from rigged volume if this is a rigged attachment + LLVOVolume* vobj = drawable->getVOVolume(); + if (vobj) + { + vobj->updateRiggedVolume(); + LLRiggedVolume* rigged_volume = vobj->getRiggedVolume(); + if (rigged_volume) + { + LLVertexBuffer::unbind(); + gGL.pushMatrix(); + glMultMatrixf((F32*) vobj->getRelativeXform().mMatrix); + for (S32 i = 0; i < rigged_volume->getNumVolumeFaces(); ++i) + { + const LLVolumeFace& face = rigged_volume->getVolumeFace(i); + glVertexPointer(3, GL_FLOAT, 16, face.mPositions); + glDrawElements(GL_TRIANGLES, face.mNumIndices, GL_UNSIGNED_SHORT, face.mIndices); + } + gGL.popMatrix(); + } + } + } + else + { + for (S32 i = 0; i < drawable->getNumFaces(); ++i) + { + LLFace* face = drawable->getFace(i); + if (face->verify()) + { + pushVerts(face, LLVertexBuffer::MAP_VERTEX); + } + } + } +} + +void LLSelectNode::renderOneWireframe(const LLColor4& color) +{ + LLViewerObject* objectp = getObject(); + if (!objectp) + { + return; + } + + LLDrawable* drawable = objectp->mDrawable; + if(!drawable) + { + return; + } + + glMatrixMode(GL_MODELVIEW); + gGL.pushMatrix(); + + BOOL is_hud_object = objectp->isHUDAttachment(); + + if (drawable->isActive()) + { + glLoadMatrixd(gGLModelView); + glMultMatrixf((F32*) objectp->getRenderMatrix().mMatrix); + } + else if (!is_hud_object) + { + glLoadIdentity(); + glMultMatrixd(gGLModelView); + LLVector3 trans = objectp->getRegion()->getOriginAgent(); + glTranslatef(trans.mV[0], trans.mV[1], trans.mV[2]); + } + + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + + if (LLSelectMgr::sRenderHiddenSelections) // && gFloaterTools && gFloaterTools->getVisible()) + { + gGL.blendFunc(LLRender::BF_SOURCE_COLOR, LLRender::BF_ONE); + LLGLEnable fog(GL_FOG); + glFogi(GL_FOG_MODE, GL_LINEAR); + float d = (LLViewerCamera::getInstance()->getPointOfInterest()-LLViewerCamera::getInstance()->getOrigin()).magVec(); + LLColor4 fogCol = color * (F32)llclamp((LLSelectMgr::getInstance()->getSelectionCenterGlobal()-gAgentCamera.getCameraPositionGlobal()).magVec()/(LLSelectMgr::getInstance()->getBBoxOfSelection().getExtentLocal().magVec()*4), 0.0, 1.0); + glFogf(GL_FOG_START, d); + glFogf(GL_FOG_END, d*(1 + (LLViewerCamera::getInstance()->getView() / LLViewerCamera::getInstance()->getDefaultFOV()))); + glFogfv(GL_FOG_COLOR, fogCol.mV); + + LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_GEQUAL); + gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); + { + glColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f); + pushWireframe(drawable); + } + } + + gGL.flush(); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + + glColor4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2); + LLGLEnable offset(GL_POLYGON_OFFSET_LINE); + glPolygonOffset(3.f, 3.f); + glLineWidth(3.f); + pushWireframe(drawable); + glLineWidth(1.f); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + gGL.popMatrix(); +} +#endif MESH_ENABLED + //----------------------------------------------------------------------------- // renderOneSilhouette() //----------------------------------------------------------------------------- @@ -5474,6 +5579,15 @@ void LLSelectNode::renderOneSilhouette(const LLColor4 &color) return; } +#if MESH_ENABLED + LLVOVolume* vobj = drawable->getVOVolume(); + if (vobj && vobj->isMesh()) + { + renderOneWireframe(color); + return; + } +#endif //MESH_ENABLED + if (!mSilhouetteExists) { return; diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index 9a0dc2e70..2dee99fda 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -139,6 +139,9 @@ public: BOOL isTESelected(S32 te_index); S32 getLastSelectedTE(); S32 getTESelectMask() { return mTESelectMask; } +#if MESH_ENABLED + void renderOneWireframe(const LLColor4& color); +#endif void renderOneSilhouette(const LLColor4 &color); void setTransient(BOOL transient) { mTransient = transient; } BOOL isTransient() { return mTransient; } diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 3f6c7c474..dac2286bb 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -2999,7 +2999,12 @@ void LLViewerObject::boostTexturePriority(BOOL boost_children /* = TRUE */) getTEImage(i)->setBoostLevel(LLViewerTexture::BOOST_SELECTED); } - if (isSculpted()) + + if (isSculpted() +#if MESH_ENABLED + && !isMesh() +#endif //MESH_ENABLED + ) { LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT); LLUUID sculpt_id = sculpt_params->getSculptTexture(); @@ -3239,6 +3244,16 @@ const LLVector3 LLViewerObject::getPositionEdit() const const LLVector3 LLViewerObject::getRenderPosition() const { +#if MESH_ENABLED + if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED)) + { + LLVOAvatar* avatar = getAvatar(); + if (avatar) + { + return avatar->getPositionAgent(); + } + } +#endif //MESH_ENABLED if (mDrawable.isNull() || mDrawable->getGeneration() < 0) { return getPositionAgent(); @@ -3257,6 +3272,12 @@ const LLVector3 LLViewerObject::getPivotPositionAgent() const const LLQuaternion LLViewerObject::getRenderRotation() const { LLQuaternion ret; +#if MESH_ENABLED + if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED)) + { + return ret; + } +#endif //MESH_ENABLED if (mDrawable.isNull() || mDrawable->isStatic()) { ret = getRotationEdit(); diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index a6cbec632..773dfbea3 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1495,6 +1495,11 @@ void LLViewerRegion::setSeedCapability(const std::string& url) } capabilityNames.append("GetDisplayNames"); capabilityNames.append("GetTexture"); +#if MESH_ENABLED + capabilityNames.append("GetMesh"); + capabilityNames.append("GetObjectCost"); + capabilityNames.append("GetObjectPhysicsData"); +#endif //MESH_ENABLED capabilityNames.append("GroupProposalBallot"); capabilityNames.append("HomeLocation"); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 24ffeb17a..91e715171 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -39,6 +39,9 @@ #include "llagent.h" #include "llagentcamera.h" +#if MESH_ENABLED +#include "llmeshrepository.h" +#endif //MESH_ENABLED #include "llpanellogin.h" #include "llviewerkeyboard.h" #include "llviewerwindow.h" @@ -507,6 +510,24 @@ public: ypos += y_inc; +#if MESH_ENABLED + if (gSavedSettings.getBOOL("MeshEnabled")) + { + addText(xpos, ypos, llformat("%.3f MB Mesh Data Received", LLMeshRepository::sBytesReceived/(1024.f*1024.f))); + + ypos += y_inc; + + addText(xpos, ypos, llformat("%d/%d Mesh HTTP Requests/Retries", LLMeshRepository::sHTTPRequestCount, + LLMeshRepository::sHTTPRetryCount)); + + ypos += y_inc; + + addText(xpos, ypos, llformat("%.3f/%.3f MB Mesh Cache Read/Write ", LLMeshRepository::sCacheBytesRead/(1024.f*1024.f), LLMeshRepository::sCacheBytesWritten/(1024.f*1024.f))); + + ypos += y_inc; + } +#endif //MESH_ENABLED + LLVertexBuffer::sBindCount = LLImageGL::sBindCount = LLVertexBuffer::sSetCount = LLImageGL::sUniqueCount = gPipeline.mNumVisibleNodes = LLPipeline::sVisibleLightCount = 0; diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 8a1de024b..4d88fdc7d 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -63,6 +63,10 @@ #include "llkeyframefallmotion.h" #include "llkeyframestandmotion.h" #include "llkeyframewalkmotion.h" +#if MESH_ENABLED +#include "llmanipscale.h" // for get_default_max_prim_scale() +#include "llmeshrepository.h" +#endif //MESH_ENABLED #include "llmutelist.h" #include "llnotify.h" #include "llquantize.h" @@ -1618,7 +1622,11 @@ void LLVOAvatar::getSpatialExtents(LLVector4a& newMin, LLVector4a& newMax) if (attached_object && !attached_object->isHUDAttachment()) { LLDrawable* drawable = attached_object->mDrawable; - if (drawable) + if (drawable +#if MESH_ENABLED + && !drawable->isState(LLDrawable::RIGGED)) +#endif //MESH_ENABLED + ) { LLSpatialBridge* bridge = drawable->getSpatialBridge(); if (bridge) @@ -4943,6 +4951,7 @@ bool LLVOAvatar::shouldAlphaMask() } + //------------------------------------------------------------------------ // needsRenderBeam() //------------------------------------------------------------------------ @@ -7162,6 +7171,56 @@ void LLVOAvatar::resetHUDAttachments() } } +#if MESH_ENABLED +void LLVOAvatar::rebuildRiggedAttachments( void ) +{ + for ( attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter ) + { + LLViewerJointAttachment* pAttachment = iter->second; + LLViewerJointAttachment::attachedobjs_vec_t::iterator attachmentIterEnd = pAttachment->mAttachedObjects.end(); + + for ( LLViewerJointAttachment::attachedobjs_vec_t::iterator attachmentIter = pAttachment->mAttachedObjects.begin(); + attachmentIter != attachmentIterEnd; ++attachmentIter) + { + const LLViewerObject* pAttachedObject = *attachmentIter; + if ( pAttachment && pAttachedObject->mDrawable.notNull() ) + { + gPipeline.markRebuild(pAttachedObject->mDrawable); + } + } + } +} + +//----------------------------------------------------------------------------- +// cleanupAttachedMesh() +//----------------------------------------------------------------------------- +void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO ) +{ + //If a VO has a skin that we'll reset the joint positions to their default + if ( pVO && pVO->mDrawable ) + { + LLVOVolume* pVObj = pVO->mDrawable->getVOVolume(); + if ( pVObj ) + { + const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( pVObj->getVolume()->getParams().getSculptID() ); + if ( pSkinData ) + { + const int jointCnt = pSkinData->mJointNames.size(); + bool fullRig = ( jointCnt>=20 ) ? true : false; + if ( fullRig ) + { + const int bindCnt = pSkinData->mAlternateBindMatrix.size(); + if ( bindCnt > 0 ) + { + LLVOAvatar::resetJointPositionsToDefault(); + } + } + } + } + } +} +#endif //MESH_ENABLED + //----------------------------------------------------------------------------- // detachObject() //----------------------------------------------------------------------------- @@ -10508,6 +10567,13 @@ BOOL LLVOAvatar::updateLOD() return res; } +#if MESH_ENABLED +void LLVOAvatar::updateLODRiggedAttachments( void ) +{ + updateLOD(); + rebuildRiggedAttachments(); +} +#endif //MESH_ENABLED U32 LLVOAvatar::getPartitionType() const { // Avatars merely exist as drawables in the bridge partition diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 15010e108..59fbb7147 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -124,6 +124,9 @@ public: /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); virtual BOOL updateLOD(); BOOL updateJointLODs(); +#if MESH_ENABLED + void updateLODRiggedAttachments( void ); +#endif //MESH_ENABLED /*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. @@ -722,12 +725,18 @@ public: void clampAttachmentPositions(); BOOL attachObject(LLViewerObject *viewer_object); BOOL detachObject(LLViewerObject *viewer_object); +#if MESH_ENABLED + void cleanupAttachedMesh( LLViewerObject* pVO ); +#endif //MESH_ENABLED static LLVOAvatar* findAvatarFromAttachment( LLViewerObject* obj ); protected: // [RLVa:KB] - Checked: 2009-12-18 (RLVa-1.1.0i) | Added: RLVa-1.1.0i LLViewerJointAttachment* getTargetAttachmentPoint(const LLViewerObject* viewer_object) const; // [/RLVa:KB] void lazyAttach(); +#if MESH_ENABLED + void rebuildRiggedAttachments( void ); +#endif //MESH_ENABLED //-------------------------------------------------------------------- // Map of attachment points, by ID diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 421be412f..3023ef3d4 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -42,12 +42,13 @@ #include "llmaterialtable.h" #include "llprimitive.h" #include "llvolume.h" +#include "llvolumeoctree.h" #include "llvolumemgr.h" #include "llvolumemessage.h" #include "material_codes.h" #include "message.h" #include "object_flags.h" -#include "llagent.h" +#include "llagentconstants.h" #include "lldrawable.h" #include "lldrawpoolbump.h" #include "llface.h" @@ -59,11 +60,21 @@ #include "llvector4a.h" #include "llviewercamera.h" #include "llviewertexturelist.h" +#include "llviewerobjectlist.h" #include "llviewerregion.h" #include "llviewertextureanim.h" #include "llworld.h" #include "llselectmgr.h" #include "pipeline.h" +#include "llsdutil.h" +#include "llmatrix4a.h" +#include "llagent.h" +#if MESH_ENABLED +#include "lldrawpoolavatar.h +#include "llmeshrepository.h" +#include "lldatapacker.h" +#include "llvoavatar.h" +#endif //MESH_ENABLED #include "llvocache.h" // [RLVa:KB] - Checked: 2010-04-04 (RLVa-1.2.0d) @@ -730,8 +741,35 @@ LLDrawable *LLVOVolume::createDrawable(LLPipeline *pipeline) return mDrawable; } -BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume) +BOOL LLVOVolume::setVolume(const LLVolumeParams ¶ms_in, const S32 detail, bool unique_volume) { + LLVolumeParams volume_params = params_in; + S32 lod = mLOD; +#if MESH_ENABLED + S32 last_lod = mVolumep.notNull() ? LLVolumeLODGroup::getVolumeDetailFromScale(mVolumep->getDetail()) : -1; + + BOOL is404 = FALSE; + + if (isSculpted()) + { + // if it's a mesh + if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH) + { //meshes might not have all LODs, get the force detail to best existing LOD + + LLUUID mesh_id = volume_params.getSculptID(); + + //profile and path params don't matter for meshes + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + + lod = gMeshRepo.getActualMeshLOD(volume_params, lod); + if (lod == -1) + { + is404 = TRUE; + lod = 0; + } + } + } +#endif //MESH_ENABLED // Check if we need to change implementations bool is_flexible = (volume_params.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE); if (is_flexible) @@ -759,13 +797,19 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail } } - if ((LLPrimitive::setVolume(volume_params, mLOD, (mVolumeImpl && mVolumeImpl->isVolumeUnique()))) || mSculptChanged) +#if MESH_ENABLED + if (is404) + { + setIcon(LLViewerTextureManager::getFetchedTextureFromFile("icons/Inv_Mesh.png", TRUE, LLViewerTexture::BOOST_UI)); + } +#endif //MESH_ENABLED + if ((LLPrimitive::setVolume(volume_params, lod, (mVolumeImpl && mVolumeImpl->isVolumeUnique()))) || mSculptChanged) { mFaceMappingChanged = TRUE; if (mVolumeImpl) { - mVolumeImpl->onSetVolume(volume_params, detail); + mVolumeImpl->onSetVolume(volume_params, detail); //mLOD ? } updateSculptTexture(); @@ -773,10 +817,31 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail if (isSculpted()) { updateSculptTexture(); - - if (mSculptTexture.notNull()) +#if MESH_ENABLED + // if it's a mesh + if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH) { - sculpt(); + if (getVolume()->getNumVolumeFaces() == 0 || getVolume()->isTetrahedron()) + { + //load request not yet issued, request pipeline load this mesh + LLUUID asset_id = volume_params.getSculptID(); + S32 available_lod = gMeshRepo.loadMesh(this, volume_params, lod, last_lod); + if (available_lod != lod) + { + LLPrimitive::setVolume(volume_params, available_lod); + } + } + + } + else // otherwise is sculptie +#endif //MESH_ENABLED + + + { + if (mSculptTexture.notNull()) + { + sculpt(); + } } } @@ -789,7 +854,11 @@ void LLVOVolume::updateSculptTexture() { LLPointer old_sculpt = mSculptTexture; - if (isSculpted()) + if (isSculpted() +#if MESH_ENABLED + && !isMesh() +#endif //MESH_ENABLED + ) { LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT); LLUUID id = sculpt_params->getSculptTexture(); @@ -814,8 +883,17 @@ void LLVOVolume::updateSculptTexture() mSculptTexture->addVolume(this); } } + } +#if MESH_ENABLED +void LLVOVolume::notifyMeshLoaded() +{ + mSculptChanged = TRUE; + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE); +} +#endif //MESH_ENABLED + // sculpt replaces generate() for sculpted surfaces void LLVOVolume::sculpt() { @@ -907,8 +985,24 @@ BOOL LLVOVolume::calcLOD() S32 cur_detail = 0; - F32 radius = getVolume()->mLODScaleBias.scaledVec(getScale()).length(); - F32 distance = mDrawable->mDistanceWRTCamera; + F32 radius; + F32 distance; + +#if MESH_ENABLED + if (mDrawable->isState(LLDrawable::RIGGED)) + { + LLVOAvatar* avatar = getAvatar(); + distance = avatar->mDrawable->mDistanceWRTCamera; + radius = avatar->getBinRadius(); + } + else +#endif //MESH_ENABLED + { + distance = mDrawable->mDistanceWRTCamera; + radius = getVolume()->mLODScaleBias.scaledVec(getScale()).length(); + } + + distance *= sDistanceFactor; F32 rampDist = LLVOVolume::sLODFactor * 2; @@ -1072,9 +1166,22 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global) min.clear(); max.clear(); - BOOL rebuild = mDrawable->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION); + BOOL rebuild = mDrawable->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION +#if MESH_ENABLED + | LLDrawable::REBUILD_RIGGED +#endif //MESH_ENABLED + ); +#if MESH_ENABLED + LLVolume* volume = mRiggedVolume; + if (!volume) + { + volume = getVolume(); + } +#endif //MESH_ENABLED +#if !MESH_ENABLED LLVolume* volume = getVolume(); +#endif //!MESH_ENABLED for (S32 i = 0; i < getVolume()->getNumFaces(); i++) { LLFace *face = mDrawable->getFace(i); @@ -1132,8 +1239,27 @@ void LLVOVolume::updateRelativeXform() } LLDrawable* drawable = mDrawable; - + +#if MESH_ENABLED + if (drawable->isState(LLDrawable::RIGGED) && mRiggedVolume.notNull()) + { //rigged volume (which is in agent space) is used for generating bounding boxes etc + //inverse of render matrix should go to partition space + mRelativeXform = getRenderMatrix(); + + F32* dst = (F32*) mRelativeXformInvTrans.mMatrix; + F32* src = (F32*) mRelativeXform.mMatrix; + dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; + dst[3] = src[4]; dst[4] = src[5]; dst[5] = src[6]; + dst[6] = src[8]; dst[7] = src[9]; dst[8] = src[10]; + + mRelativeXform.invert(); + mRelativeXformInvTrans.transpose(); + } + else if (drawable->isActive()) +#endif //MESH_ENABLED +#if !MESH_ENABLED if (drawable->isActive()) +#endif //!MESH_ENABLED { // setup relative transforms LLQuaternion delta_rot; @@ -1217,6 +1343,17 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable) { LLFastTimer t(LLFastTimer::FTM_UPDATE_PRIMITIVES); +#if MESH_ENABLED + if (mDrawable->isState(LLDrawable::REBUILD_RIGGED)) + { + { + LLFastTimer t(FTM_UPDATE_RIGGED_VOLUME); + updateRiggedVolume(); + } + genBBoxes(FALSE); + mDrawable->clearState(LLDrawable::REBUILD_RIGGED); + } +#endif //MESH_ENABLED if (mVolumeImpl != NULL) { BOOL res; @@ -1882,6 +2019,24 @@ BOOL LLVOVolume::isSculpted() const return FALSE; } +#if MESH_ENABLED +BOOL LLVOVolume::isMesh() const +{ + if (isSculpted()) + { + LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT); + U8 sculpt_type = sculpt_params->getSculptType(); + + if ((sculpt_type & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH) + // mesh is a mesh + { + return TRUE; + } + } + + return FALSE; +} +#endif //MESH_ENABLED BOOL LLVOVolume::hasLightTexture() const { if (getParameterEntryInUse(LLNetworkData::PARAMS_LIGHT_IMAGE)) @@ -1898,6 +2053,12 @@ BOOL LLVOVolume::isVolumeGlobal() const { return mVolumeImpl->isVolumeGlobal() ? TRUE : FALSE; } +#if MESH_ENABLED + else if (mRiggedVolume.notNull()) + { + return TRUE; + } +#endif //MESH_ENABLED return FALSE; } @@ -1982,7 +2143,7 @@ void LLVOVolume::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_p trans_mat.translate(getRegion()->getOriginAgent()); } - volume->generateSilhouetteVertices(nodep->mSilhouetteVertices, nodep->mSilhouetteNormals, nodep->mSilhouetteSegments, view_vector, trans_mat, mRelativeXformInvTrans, nodep->getTESelectMask()); + volume->generateSilhouetteVertices(nodep->mSilhouetteVertices, nodep->mSilhouetteNormals, view_vector, trans_mat, mRelativeXformInvTrans, nodep->getTESelectMask()); nodep->mSilhouetteExists = TRUE; } @@ -2060,7 +2221,8 @@ U32 LLVOVolume::getHighLODTriangleCount() ret = ref->getNumTriangles(); LLPrimitive::getVolumeManager()->unrefVolume(ref); } - /*else if (isMesh()) +#if MESH_ENABLED + else if (isMesh()) { LLVolume* ref = LLPrimitive::getVolumeManager()->refVolume(volume->getParams(), 3); if (ref->isTetrahedron() || ref->getNumVolumeFaces() == 0) @@ -2069,7 +2231,8 @@ U32 LLVOVolume::getHighLODTriangleCount() } ret = ref->getNumTriangles(); LLPrimitive::getVolumeManager()->unrefVolume(ref); - }*/ + } +#endif //MESH_ENABLED else { //default sculpts have a constant number of triangles ret = 31*2*31; //31 rows of 31 columns of quads for a 32x32 vertex patch @@ -2283,12 +2446,38 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e BOOL ret = FALSE; LLVolume* volume = getVolume(); + + bool transform = true; + +#if MESH_ENABLED + if (mDrawable->isState(LLDrawable::RIGGED)) + { + if (LLFloater::isVisible(gFloaterTools) && getAvatar()->isSelf()) + { + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_RIGGED, TRUE); + volume = mRiggedVolume; + transform = false; + } + else + { //cannot pick rigged attachments on other avatars or when not in build mode + return FALSE; + } + } +#endif //MESH_ENABLED if (volume) { LLVector3 v_start, v_end, v_dir; - v_start = agentPositionToVolume(start); - v_end = agentPositionToVolume(end); + if (transform) + { + v_start = agentPositionToVolume(start); + v_end = agentPositionToVolume(end); + } + else + { + v_start = start; + v_end = end; + } LLVector3 p; LLVector3 n; @@ -2348,18 +2537,40 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e if (intersection != NULL) { - *intersection = volumePositionToAgent(p); // must map back to agent space + if (transform) + { + *intersection = volumePositionToAgent(p); // must map back to agent space + } + else + { + *intersection = p; + } } if (normal != NULL) { - *normal = volumeDirectionToAgent(n); + if (transform) + { + *normal = volumeDirectionToAgent(n); + } + else + { + *normal = n; + } + (*normal).normVec(); } if (bi_normal != NULL) { - *bi_normal = volumeDirectionToAgent(bn); + if (transform) + { + *bi_normal = volumeDirectionToAgent(bn); + } + else + { + *bi_normal = bn; + } (*bi_normal).normVec(); } @@ -2377,6 +2588,203 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e return ret; } +#if MESH_ENABLED +bool LLVOVolume::treatAsRigged() +{ + return LLFloater::isVisible(gFloaterTools) && + isAttachment() && + getAvatar() && + getAvatar()->isSelf() && + mDrawable.notNull() && + mDrawable->isState(LLDrawable::RIGGED); +} + +LLRiggedVolume* LLVOVolume::getRiggedVolume() +{ + return mRiggedVolume; +} + +void LLVOVolume::clearRiggedVolume() +{ + if (mRiggedVolume.notNull()) + { + mRiggedVolume = NULL; + updateRelativeXform(); + } +} + +void LLVOVolume::updateRiggedVolume() +{ + //Update mRiggedVolume to match current animation frame of avatar. + //Also update position/size in octree. + + if (!treatAsRigged()) + { + clearRiggedVolume(); + + return; + } + + LLVolume* volume = getVolume(); + + const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(volume->getParams().getSculptID()); + + if (!skin) + { + clearRiggedVolume(); + return; + } + + LLVOAvatar* avatar = getAvatar(); + + if (!avatar) + { + clearRiggedVolume(); + return; + } + + if (!mRiggedVolume) + { + LLVolumeParams p; + mRiggedVolume = new LLRiggedVolume(p); + updateRelativeXform(); + } + + mRiggedVolume->update(skin, avatar, volume); + +} + + +static LLFastTimer::DeclareTimer FTM_SKIN_RIGGED("Skin"); +static LLFastTimer::DeclareTimer FTM_RIGGED_OCTREE("Octree"); + +void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, const LLVolume* volume) +{ + bool copy = false; + if (volume->getNumVolumeFaces() != getNumVolumeFaces()) + { + copy = true; + } + + for (S32 i = 0; i < volume->getNumVolumeFaces() && !copy; ++i) + { + const LLVolumeFace& src_face = volume->getVolumeFace(i); + const LLVolumeFace& dst_face = getVolumeFace(i); + + if (src_face.mNumIndices != dst_face.mNumIndices || + src_face.mNumVertices != dst_face.mNumVertices) + { + copy = true; + } + } + + if (copy) + { + copyVolumeFaces(volume); + } + + //build matrix palette + LLMatrix4a mp[64]; + LLMatrix4* mat = (LLMatrix4*) mp; + + for (U32 j = 0; j < skin->mJointNames.size(); ++j) + { + LLJoint* joint = avatar->getJoint(skin->mJointNames[j]); + if (joint) + { + mat[j] = skin->mInvBindMatrix[j]; + mat[j] *= joint->getWorldMatrix(); + } + } + + for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i) + { + const LLVolumeFace& vol_face = volume->getVolumeFace(i); + + LLVolumeFace& dst_face = mVolumeFaces[i]; + + LLVector4a* weight = vol_face.mWeights; + + LLMatrix4a bind_shape_matrix; + bind_shape_matrix.loadu(skin->mBindShapeMatrix); + + LLVector4a* pos = dst_face.mPositions; + + { + LLFastTimer t(FTM_SKIN_RIGGED); + + for (U32 j = 0; j < dst_face.mNumVertices; ++j) + { + LLMatrix4a final_mat; + final_mat.clear(); + + S32 idx[4]; + + LLVector4 wght; + + F32 scale = 0.f; + for (U32 k = 0; k < 4; k++) + { + F32 w = weight[j][k]; + + idx[k] = (S32) floorf(w); + wght[k] = w - floorf(w); + scale += wght[k]; + } + + wght *= 1.f/scale; + + for (U32 k = 0; k < 4; k++) + { + F32 w = wght[k]; + + LLMatrix4a src; + src.setMul(mp[idx[k]], w); + + final_mat.add(src); + } + + + LLVector4a& v = vol_face.mPositions[j]; + LLVector4a t; + LLVector4a dst; + bind_shape_matrix.affineTransform(v, t); + final_mat.affineTransform(t, dst); + pos[j] = dst; + } + + //update bounding box + LLVector4a& min = dst_face.mExtents[0]; + LLVector4a& max = dst_face.mExtents[1]; + + min = pos[0]; + max = pos[1]; + + for (U32 j = 1; j < dst_face.mNumVertices; ++j) + { + min.setMin(min, pos[j]); + max.setMax(max, pos[j]); + } + + dst_face.mCenter->setAdd(dst_face.mExtents[0], dst_face.mExtents[1]); + dst_face.mCenter->mul(0.5f); + + } + + { + LLFastTimer t(FTM_RIGGED_OCTREE); + delete dst_face.mOctree; + dst_face.mOctree = NULL; + + LLVector4a size; + size.setSub(dst_face.mExtents[1], dst_face.mExtents[0]); + size.splat(size.getLength3().getF32()*0.5f); + + dst_face.createOctree(1.f); + } + } +} +#endif //MESH_ENABLED U32 LLVOVolume::getPartitionType() const { if (isHUDAttachment()) @@ -2525,6 +2933,35 @@ void LLVolumeGeometryManager::getGeometry(LLSpatialGroup* group) } +#if MESH_ENABLED +static LLDrawPoolAvatar* get_avatar_drawpool(LLViewerObject* vobj) +{ + LLVOAvatar* avatar = vobj->getAvatar(); + + if (avatar) + { + LLDrawable* drawable = avatar->mDrawable; + if (drawable && drawable->getNumFaces() > 0) + { + LLFace* face = drawable->getFace(0); + if (face) + { + LLDrawPool* drawpool = face->getPool(); + if (drawpool) + { + if (drawpool->getType() == LLDrawPool::POOL_AVATAR) + { + return (LLDrawPoolAvatar*) drawpool; + } + } + } + } + } + + return NULL; +} + +#endif //MESH_ENABLED void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) { if (LLPipeline::sSkipUpdate) @@ -2591,12 +3028,28 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) } LLVOVolume* vobj = drawablep->getVOVolume(); + +#if MESH_ENABLED + if (vobj->getVolume() && vobj->getVolume()->isTetrahedron()) + { + continue; + } +#endif //MESH_ENABLED llassert_always(vobj); vobj->updateTextureVirtualSize(); vobj->preRebuild(); drawablep->clearState(LLDrawable::HAS_ALPHA); +#if MESH_ENABLED + bool rigged = vobj->isAttachment() && + vobj->isMesh() && + gMeshRepo.getSkinInfo(vobj->getVolume()->getParams().getSculptID()); + + bool bake_sunlight = LLPipeline::sBakeSunlight && drawablep->isStatic(); + + bool is_rigged = false; +#endif //MESH_ENABLED //for each face for (S32 i = 0; i < drawablep->getNumFaces(); i++) { @@ -2608,6 +3061,170 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) //sum up face verts and indices drawablep->updateFaceSize(i); + +#if MESH_ENABLED + if (rigged) + { + if (!facep->isState(LLFace::RIGGED)) + { //completely reset vertex buffer + facep->clearVertexBuffer(); + } + + facep->setState(LLFace::RIGGED); + is_rigged = true; + + //get drawpool of avatar with rigged face + LLDrawPoolAvatar* pool = get_avatar_drawpool(vobj); + + //Determine if we've received skininfo that contains an + //alternate bind matrix - if it does then apply the translational component + //to the joints of the avatar. + LLVOAvatar* pAvatarVO = vobj->getAvatar(); + bool pelvisGotSet = false; + + if ( pAvatarVO ) + { + LLUUID currentId = vobj->getVolume()->getParams().getSculptID(); + const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( currentId ); + + if ( pSkinData ) + { + const int bindCnt = pSkinData->mAlternateBindMatrix.size(); + if ( bindCnt > 0 ) + { + const int jointCnt = pSkinData->mJointNames.size(); + const F32 pelvisZOffset = pSkinData->mPelvisOffset; + bool fullRig = (jointCnt>=20) ? true : false; + if ( fullRig ) + { + for ( int i=0; imJointNames[i].c_str(); + //llinfos<<"joint name "<getJoint( lookingForJoint ); + if ( pJoint && pJoint->getId() != currentId ) + { + pJoint->setId( currentId ); + const LLVector3& jointPos = pSkinData->mAlternateBindMatrix[i].getTranslation(); + //Set the joint position + pJoint->storeCurrentXform( jointPos ); + //If joint is a pelvis then handle old/new pelvis to foot values + if ( lookingForJoint == "mPelvis" ) + { + pJoint->storeCurrentXform( jointPos ); + if ( !pAvatarVO->hasPelvisOffset() ) + { + pAvatarVO->setPelvisOffset( true, jointPos, pelvisZOffset ); + //Trigger to rebuild viewer AV + pelvisGotSet = true; + } + } + } + } + } + } + } + } + //If we've set the pelvis to a new position we need to also rebuild some information that the + //viewer does at launch (e.g. body size etc.) + if ( pelvisGotSet ) + { + pAvatarVO->postPelvisSetRecalc(); + } + + if (pool) + { + const LLTextureEntry* te = facep->getTextureEntry(); + + //remove face from old pool if it exists + LLDrawPool* old_pool = facep->getPool(); + if (old_pool && old_pool->getType() == LLDrawPool::POOL_AVATAR) + { + ((LLDrawPoolAvatar*) old_pool)->removeRiggedFace(facep); + } + + //add face to new pool + LLViewerTexture* tex = facep->getTexture(); + U32 type = gPipeline.getPoolTypeFromTE(te, tex); + + if (type == LLDrawPool::POOL_ALPHA) + { + if (te->getFullbright()) + { + pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT_ALPHA); + } + else + { + pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_ALPHA); + } + } + else if (te->getShiny()) + { + if (te->getFullbright()) + { + pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT_SHINY); + } + else + { + if (LLPipeline::sRenderDeferred) + { + pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SIMPLE); + } + else + { + pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SHINY); + } + } + } + else + { + if (te->getFullbright()) + { + pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT); + } + else + { + pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SIMPLE); + } + } + + if (te->getGlow()) + { + pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_GLOW); + } + + if (LLPipeline::sRenderDeferred) + { + if (type != LLDrawPool::POOL_ALPHA && !te->getFullbright()) + { + if (te->getBumpmap()) + { + pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_DEFERRED_BUMP); + } + else + { + pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_DEFERRED_SIMPLE); + } + } + } + } + + continue; + } + else + { + if (facep->isState(LLFace::RIGGED)) + { //face is not rigged but used to be, remove from rigged face pool + LLDrawPoolAvatar* pool = (LLDrawPoolAvatar*) facep->getPool(); + if (pool) + { + pool->removeRiggedFace(facep); + } + facep->clearState(LLFace::RIGGED); + } + } +#endif //MESH_ENABLED + if (cur_total > max_total || facep->getIndicesCount() <= 0 || facep->getGeomCount() <= 0) { facep->clearVertexBuffer(); @@ -2718,6 +3335,17 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) facep->clearVertexBuffer(); } } + +#if MESH_ENABLED + if (is_rigged) + { + drawablep->setState(LLDrawable::RIGGED); + } + else + { + drawablep->clearState(LLDrawable::RIGGED); + } +#endif //MESH_ENABLED } group->mBufferUsage = useage; diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index 22bed531f..94e4ff69e 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -44,12 +44,27 @@ class LLViewerTextureAnim; class LLDrawPool; class LLSelectNode; +class LLVOAvatar; +class LLMeshSkinInfo; enum LLVolumeInterfaceType { INTERFACE_FLEXIBLE = 1, }; +#if MESH_ENABLED +class LLRiggedVolume : public LLVolume +{ +public: + LLRiggedVolume(const LLVolumeParams& params) + : LLVolume(params, 0.f) + { + } + + void update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, const LLVolume* src_volume); +}; +#endif //MESH_ENABLED + // Base class for implementations of the volume - Primitive, Flexible Object, etc. class LLVolumeInterface { @@ -181,6 +196,12 @@ public: void updateSculptTexture(); void setIndexInTex(S32 index) { mIndexInTex = index ;} void sculpt(); +#if MESH_ENABLED + static void rebuildMeshAssetCallback(LLVFS *vfs, + const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status); +#endif //MESH_ENABLED void updateRelativeXform(); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); /*virtual*/ void updateFaceSize(S32 idx); @@ -227,6 +248,9 @@ public: U32 getVolumeInterfaceID() const; virtual BOOL isFlexible() const; virtual BOOL isSculpted() const; +#if MESH_ENABLED + virtual BOOL isMesh() const; +#endif //MESH_ENABLED virtual BOOL hasLightTexture() const; BOOL isVolumeGlobal() const; @@ -236,6 +260,35 @@ public: // tag: vaa emerald local_asset_browser void setSculptChanged(BOOL has_changed) { mSculptChanged = has_changed; } +#if MESH_ENABLED + void notifyMeshLoaded(); + + // Returns 'true' iff the media data for this object is in flight + bool isMediaDataBeingFetched() const; + + // Returns the "last fetched" media version, or -1 if not fetched yet + S32 getLastFetchedMediaVersion() const { return mLastFetchedMediaVersion; } + + void addMDCImpl() { ++mMDCImplCount; } + void removeMDCImpl() { --mMDCImplCount; } + S32 getMDCImplCount() { return mMDCImplCount; } + + + //rigged volume update (for raycasting) + void updateRiggedVolume(); + LLRiggedVolume* getRiggedVolume(); + + //returns true if volume should be treated as a rigged volume + // - Build tools are open + // - object is an attachment + // - object is attached to self + // - object is rendered as rigged + bool treatAsRigged(); + + //clear out rigged volume and revert back to non-rigged state for picking/LOD/distance updates + void clearRiggedVolume(); +#endif //MESH_ENABLED + protected: S32 computeLODDetail(F32 distance, F32 radius); BOOL calcLOD(); @@ -262,7 +315,9 @@ private: LLPointer mSculptTexture; LLPointer mLightTexture; S32 mIndexInTex; - +#if MESH_ENABLED + LLPointer mRiggedVolume; +#endif //MESH_ENABLED // statics public: static F32 sLODSlopDistanceFactor;// Changing this to zero, effectively disables the LOD transition slop diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 183bf315f..38cd00028 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -70,6 +70,9 @@ #include "llgldbg.h" #include "llhudmanager.h" #include "lllightconstants.h" +#if MESH_ENABLED +#include "llmeshrepository.h" +#endif //MESH_ENABLED #include "llresmgr.h" #include "llselectmgr.h" #include "llsky.h" @@ -2108,6 +2111,9 @@ void LLPipeline::rebuildPriorityGroups() assertInitialized(); +#if MESH_ENABLED + gMeshRepo.notifyLoadedMeshes(); +#endif //MESH_ENABLED mGroupQ1Locked = true; // Iterate through all drawables on the priority build queue, From a237cb9cad12dc248d60f71a9ce7136408c8dce7 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Fri, 29 Jul 2011 20:40:08 -0500 Subject: [PATCH 08/22] llmemory.h cleanup. pulled llpointer, refcounter, smartpointer, singleton and safe handle out into their own headers. llmemory.h drags all those headers in for legacy support until everything else is cleaned up. getCurrentRSS() moved into LLMemory. getWorkingSetSize() added to LLMemory. --- indra/llcommon/CMakeLists.txt | 7 + indra/llcommon/llmemory.cpp | 69 ++-- indra/llcommon/llmemory.h | 442 +----------------------- indra/llcommon/llpointer.h | 170 +++++++++ indra/llcommon/llrefcount.cpp | 164 +++++++++ indra/llcommon/llrefcount.h | 89 +++++ indra/llcommon/llsafehandle.h | 162 +++++++++ indra/llcommon/llsingleton.cpp | 32 ++ indra/llcommon/llsingleton.h | 201 +++++++++++ indra/newview/app_settings/settings.xml | 13 +- indra/newview/llappviewer.cpp | 2 +- indra/newview/llviewerdisplay.cpp | 2 +- indra/newview/llviewerstats.cpp | 2 +- indra/newview/llviewerwindow.cpp | 7 + 14 files changed, 898 insertions(+), 464 deletions(-) create mode 100644 indra/llcommon/llpointer.h create mode 100644 indra/llcommon/llrefcount.cpp create mode 100644 indra/llcommon/llrefcount.h create mode 100644 indra/llcommon/llsafehandle.h create mode 100644 indra/llcommon/llsingleton.cpp create mode 100644 indra/llcommon/llsingleton.h diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 19fc67588..d6e2c7c45 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -58,12 +58,14 @@ set(llcommon_SOURCE_FILES llprocessor.cpp llqueuedthread.cpp llrand.cpp + llrefcount.cpp llrun.cpp llsd.cpp llsdserialize.cpp llsdserialize_xml.cpp llsdutil.cpp llsecondlifeurls.cpp + llsingleton.cpp llstat.cpp llstacktrace.cpp llstreamtools.cpp @@ -159,6 +161,7 @@ set(llcommon_HEADER_FILES llmortician.h llnametable.h lloptioninterface.h + llpointer.h llpreprocessor.h llpriqueuemap.h llprocesslauncher.h @@ -168,6 +171,8 @@ set(llcommon_HEADER_FILES llqueuedthread.h llrand.h llrun.h + llrefcount.h + llsafehandle.h llscopedvolatileaprpool.h llsd.h llsdserialize.h @@ -175,6 +180,7 @@ set(llcommon_HEADER_FILES llsdutil.h llsecondlifeurls.h llsimplehash.h + llsingleton.h llskiplist.h llskipmap.h llstack.h @@ -205,6 +211,7 @@ set(llcommon_HEADER_FILES stdenums.h stdtypes.h string_table.h + stringize.h timer.h timing.h u64.h diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp index 39abd14ed..87be91c0c 100644 --- a/indra/llcommon/llmemory.cpp +++ b/indra/llcommon/llmemory.cpp @@ -276,37 +276,12 @@ void operator delete[] (void *p) #endif -//---------------------------------------------------------------------------- - -LLRefCount::LLRefCount() : - mRef(0) -{ -} - -LLRefCount::LLRefCount(const LLRefCount& other) -: mRef(0) -{ -} - -LLRefCount::~LLRefCount() -{ - if (mRef != 0) - { - llerrs << "deleting non-zero reference" << llendl; - } -} - -LLRefCount& LLRefCount::operator=(const LLRefCount&) -{ - // do nothing, since ref count is specific to *this* reference - return *this; -} //---------------------------------------------------------------------------- #if defined(LL_WINDOWS) -U64 getCurrentRSS() +U64 LLMemory::getCurrentRSS() { HANDLE self = GetCurrentProcess(); PROCESS_MEMORY_COUNTERS counters; @@ -320,6 +295,20 @@ U64 getCurrentRSS() return counters.WorkingSetSize; } +//static +U32 LLMemory::getWorkingSetSize() +{ + PROCESS_MEMORY_COUNTERS pmc ; + U32 ret = 0 ; + + if (GetProcessMemoryInfo( GetCurrentProcess(), &pmc, sizeof(pmc)) ) + { + ret = pmc.WorkingSetSize ; + } + + return ret ; +} + #elif defined(LL_DARWIN) /* @@ -344,7 +333,7 @@ U64 getCurrentRSS() // } // } -U64 getCurrentRSS() +U64 LLMemory::getCurrentRSS() { U64 residentSize = 0; task_basic_info_data_t basicInfo; @@ -366,9 +355,14 @@ U64 getCurrentRSS() return residentSize; } +U32 LLMemory::getWorkingSetSize() +{ + return 0 ; +} + #elif defined(LL_LINUX) -U64 getCurrentRSS() +U64 LLMemory::getCurrentRSS() { static const char statPath[] = "/proc/self/stat"; LLFILE *fp = LLFile::fopen(statPath, "r"); @@ -400,6 +394,10 @@ bail: return rss; } +U32 LLMemory::getWorkingSetSize() +{ + return 0 ; +} #elif LL_SOLARIS #include #include @@ -407,7 +405,7 @@ bail: #define _STRUCTURED_PROC 1 #include -U64 getCurrentRSS() +U64 LLMemory::getCurrentRSS() { char path [LL_MAX_PATH]; /* Flawfinder: ignore */ @@ -428,11 +426,22 @@ U64 getCurrentRSS() return((U64)proc_psinfo.pr_rssize * 1024); } + +U32 LLMemory::getWorkingSetSize() +{ + return 0 ; +} + #else -U64 getCurrentRSS() +U64 LLMemory::getCurrentRSS() { return 0; } +U32 LLMemory::getWorkingSetSize() +{ + return 0 ; +} + #endif diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index 25cddaf09..46176f4b0 100644 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -29,20 +29,15 @@ * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ -#ifndef LL_MEMORY_H -#define LL_MEMORY_H +#ifndef LLMEMORY_H +#define LLMEMORY_H + #include #include #include "llerror.h" -extern S32 gTotalDAlloc; -extern S32 gTotalDAUse; -extern S32 gDACount; - -const U32 LLREFCOUNT_SENTINEL_VALUE = 0xAAAAAAAA; - //---------------------------------------------------------------------------- #if LL_DEBUG @@ -128,431 +123,18 @@ public: static void initClass(); static void cleanupClass(); static void freeReserve(); + // Return the resident set size of the current process, in bytes. + // Return value is zero if not known. + static U64 getCurrentRSS(); + static U32 getWorkingSetSize(); private: static char* reserveMem; }; -//---------------------------------------------------------------------------- -// RefCount objects should generally only be accessed by way of LLPointer<>'s -// NOTE: LLPointer x = new LLFoo(); MAY NOT BE THREAD SAFE -// if LLFoo::LLFoo() does anything like put itself in an update queue. -// The queue may get accessed before it gets assigned to x. -// The correct implementation is: -// LLPointer x = new LLFoo; // constructor does not do anything interesting -// x->instantiate(); // does stuff like place x into an update queue - -// see llthread.h for LLThreadSafeRefCount - -//---------------------------------------------------------------------------- - -class LL_COMMON_API LLRefCount -{ -protected: - LLRefCount(const LLRefCount&); -private: - LLRefCount&operator=(const LLRefCount&); - -protected: - virtual ~LLRefCount(); // use unref() - -public: - LLRefCount(); - - void ref() - { - mRef++; - } - - S32 unref() - { - llassert(mRef >= 1); - if (0 == --mRef) - { - delete this; - return 0; - } - return mRef; - } - - S32 getNumRefs() const - { - return mRef; - } - -private: - S32 mRef; -}; - -//---------------------------------------------------------------------------- - -// Note: relies on Type having ref() and unref() methods -template class LLPointer -{ -public: - - LLPointer() : - mPointer(NULL) - { - } - - LLPointer(Type* ptr) : - mPointer(ptr) - { - ref(); - } - - LLPointer(const LLPointer& ptr) : - mPointer(ptr.mPointer) - { - ref(); - } - - // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template - LLPointer(const LLPointer& ptr) : - mPointer(ptr.get()) - { - ref(); - } - - ~LLPointer() - { - unref(); - } - - Type* get() const { return mPointer; } - const Type* operator->() const { return mPointer; } - Type* operator->() { return mPointer; } - const Type& operator*() const { return *mPointer; } - Type& operator*() { return *mPointer; } - - operator BOOL() const { return (mPointer != NULL); } - operator bool() const { return (mPointer != NULL); } - bool operator!() const { return (mPointer == NULL); } - bool isNull() const { return (mPointer == NULL); } - bool notNull() const { return (mPointer != NULL); } - - operator Type*() const { return mPointer; } - operator const Type*() const { return mPointer; } - bool operator !=(Type* ptr) const { return (mPointer != ptr); } - bool operator ==(Type* ptr) const { return (mPointer == ptr); } - bool operator ==(const LLPointer& ptr) const { return (mPointer == ptr.mPointer); } - bool operator < (const LLPointer& ptr) const { return (mPointer < ptr.mPointer); } - bool operator > (const LLPointer& ptr) const { return (mPointer > ptr.mPointer); } - - LLPointer& operator =(Type* ptr) - { - if( mPointer != ptr ) - { - unref(); - mPointer = ptr; - ref(); - } - - return *this; - } - - LLPointer& operator =(const LLPointer& ptr) - { - if( mPointer != ptr.mPointer ) - { - unref(); - mPointer = ptr.mPointer; - ref(); - } - return *this; - } - - // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template - LLPointer& operator =(const LLPointer& ptr) - { - if( mPointer != ptr.get() ) - { - unref(); - mPointer = ptr.get(); - ref(); - } - return *this; - } - - // Just exchange the pointers, which will not change the reference counts. - static void swap(LLPointer& a, LLPointer& b) - { - Type* temp = a.mPointer; - a.mPointer = b.mPointer; - b.mPointer = temp; - } - -protected: - void ref() - { - if (mPointer) - { - mPointer->ref(); - } - } - - void unref() - { - if (mPointer) - { - Type *tempp = mPointer; - mPointer = NULL; - tempp->unref(); - if (mPointer != NULL) - { - llwarns << "Unreference did assignment to non-NULL because of destructor" << llendl; - unref(); - } - } - } - -protected: - Type* mPointer; -}; - -//template -//class LLPointerTraits -//{ -// static Type* null(); -//}; -// -// Expands LLPointer to return a pointer to a special instance of class Type instead of NULL. -// This is useful in instances where operations on NULL pointers are semantically safe and/or -// when error checking occurs at a different granularity or in a different part of the code -// than when referencing an object via a LLSafeHandle. -// - -template -class LLSafeHandle -{ -public: - LLSafeHandle() : - mPointer(NULL) - { - } - - LLSafeHandle(Type* ptr) : - mPointer(NULL) - { - assign(ptr); - } - - LLSafeHandle(const LLSafeHandle& ptr) : - mPointer(NULL) - { - assign(ptr.mPointer); - } - - // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template - LLSafeHandle(const LLSafeHandle& ptr) : - mPointer(NULL) - { - assign(ptr.get()); - } - - ~LLSafeHandle() - { - unref(); - } - - const Type* operator->() const { return nonNull(mPointer); } - Type* operator->() { return nonNull(mPointer); } - - Type* get() const { return mPointer; } - // we disallow these operations as they expose our null objects to direct manipulation - // and bypass the reference counting semantics - //const Type& operator*() const { return *nonNull(mPointer); } - //Type& operator*() { return *nonNull(mPointer); } - - operator BOOL() const { return mPointer != NULL; } - operator bool() const { return mPointer != NULL; } - bool operator!() const { return mPointer == NULL; } - bool isNull() const { return mPointer == NULL; } - bool notNull() const { return mPointer != NULL; } - - - operator Type*() const { return mPointer; } - operator const Type*() const { return mPointer; } - bool operator !=(Type* ptr) const { return (mPointer != ptr); } - bool operator ==(Type* ptr) const { return (mPointer == ptr); } - bool operator ==(const LLSafeHandle& ptr) const { return (mPointer == ptr.mPointer); } - bool operator < (const LLSafeHandle& ptr) const { return (mPointer < ptr.mPointer); } - bool operator > (const LLSafeHandle& ptr) const { return (mPointer > ptr.mPointer); } - - LLSafeHandle& operator =(Type* ptr) - { - assign(ptr); - return *this; - } - - LLSafeHandle& operator =(const LLSafeHandle& ptr) - { - assign(ptr.mPointer); - return *this; - } - - // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template - LLSafeHandle& operator =(const LLSafeHandle& ptr) - { - assign(ptr.get()); - return *this; - } - -public: - typedef Type* (*NullFunc)(); - static const NullFunc sNullFunc; - -protected: - void ref() - { - if (mPointer) - { - mPointer->ref(); - } - } - - void unref() - { - if (mPointer) - { - Type *tempp = mPointer; - mPointer = NULL; - tempp->unref(); - if (mPointer != NULL) - { - llwarns << "Unreference did assignment to non-NULL because of destructor" << llendl; - unref(); - } - } - } - - void assign(Type* ptr) - { - if( mPointer != ptr ) - { - unref(); - mPointer = ptr; - ref(); - } - } - - static Type* nonNull(Type* ptr) - { - return ptr == NULL ? sNullFunc() : ptr; - } - -protected: - Type* mPointer; -}; - -// LLInitializedPointer is just a pointer with a default constructor that initializes it to NULL -// NOT a smart pointer like LLPointer<> -// Useful for example in std::map > -// (std::map uses the default constructor for creating new entries) -template class LLInitializedPointer -{ -public: - LLInitializedPointer() : mPointer(NULL) {} - ~LLInitializedPointer() { delete mPointer; } - - const T* operator->() const { return mPointer; } - T* operator->() { return mPointer; } - const T& operator*() const { return *mPointer; } - T& operator*() { return *mPointer; } - operator const T*() const { return mPointer; } - operator T*() { return mPointer; } - T* operator=(T* x) { return (mPointer = x); } - operator bool() const { return mPointer != NULL; } - bool operator!() const { return mPointer == NULL; } - bool operator==(T* rhs) { return mPointer == rhs; } - bool operator==(const LLInitializedPointer* rhs) { return mPointer == rhs.mPointer; } - -protected: - T* mPointer; -}; - -//---------------------------------------------------------------------------- - -// LLSingleton implements the getInstance() method part of the Singleton -// pattern. It can't make the derived class constructors protected, though, so -// you have to do that yourself. -// -// There are two ways to use LLSingleton. The first way is to inherit from it -// while using the typename that you'd like to be static as the template -// parameter, like so: -// -// class Foo: public LLSingleton{}; -// -// Foo& instance = Foo::instance(); -// -// The second way is to use the singleton class directly, without inheritance: -// -// typedef LLSingleton FooSingleton; -// -// Foo& instance = FooSingleton::instance(); -// -// In this case, the class being managed as a singleton needs to provide an -// initSingleton() method since the LLSingleton virtual method won't be -// available -// -// As currently written, it is not thread-safe. -template -class LLSingleton -{ - static bool &needsInit() - { - static bool needs_init = true; - return needs_init; - } -public: - static bool instanceExists() - { - return !needsInit(); - } - virtual ~LLSingleton() {} -#ifdef LL_MSVC7 -// workaround for VC7 compiler bug -// adapted from http://www.codeproject.com/KB/tips/VC2003MeyersSingletonBug.aspx -// our version doesn't introduce a nested struct so that you can still declare LLSingleton -// a friend and hide your constructor - static T* getInstance() - { - LLSingleton singleton; - return singleton.vsHack(); - } - - T* vsHack() -#else - static T* getInstance() -#endif - { - static T instance; - bool &needs_init = needsInit(); - if (needs_init) - { - needs_init = false; - instance.initSingleton(); - } - return &instance; - } - - static T& instance() - { - return *getInstance(); - } - -private: - virtual void initSingleton() {} -}; - -//---------------------------------------------------------------------------- - -// Return the resident set size of the current process, in bytes. -// Return value is zero if not known. -LL_COMMON_API U64 getCurrentRSS(); +//EVENTUALLY REMOVE THESE: +#include "llpointer.h" +#include "llrefcount.h" +#include "llsingleton.h" +#include "llsafehandle.h" #endif diff --git a/indra/llcommon/llpointer.h b/indra/llcommon/llpointer.h new file mode 100644 index 000000000..affa04060 --- /dev/null +++ b/indra/llcommon/llpointer.h @@ -0,0 +1,170 @@ +/** + * @file llpointer.h + * @brief A reference-counted pointer for objects derived from LLRefCount + * + * $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$ + */ +#ifndef LLPOINTER_H +#define LLPOINTER_H + +#include "llerror.h" // *TODO: consider eliminating this + +//---------------------------------------------------------------------------- +// RefCount objects should generally only be accessed by way of LLPointer<>'s +// NOTE: LLPointer x = new LLFoo(); MAY NOT BE THREAD SAFE +// if LLFoo::LLFoo() does anything like put itself in an update queue. +// The queue may get accessed before it gets assigned to x. +// The correct implementation is: +// LLPointer x = new LLFoo; // constructor does not do anything interesting +// x->instantiate(); // does stuff like place x into an update queue + +// see llthread.h for LLThreadSafeRefCount + +//---------------------------------------------------------------------------- + +// Note: relies on Type having ref() and unref() methods +template class LLPointer +{ +public: + + LLPointer() : + mPointer(NULL) + { + } + + LLPointer(Type* ptr) : + mPointer(ptr) + { + ref(); + } + + LLPointer(const LLPointer& ptr) : + mPointer(ptr.mPointer) + { + ref(); + } + + // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. + template + LLPointer(const LLPointer& ptr) : + mPointer(ptr.get()) + { + ref(); + } + + ~LLPointer() + { + unref(); + } + + Type* get() const { return mPointer; } + const Type* operator->() const { return mPointer; } + Type* operator->() { return mPointer; } + const Type& operator*() const { return *mPointer; } + Type& operator*() { return *mPointer; } + + operator BOOL() const { return (mPointer != NULL); } + operator bool() const { return (mPointer != NULL); } + bool operator!() const { return (mPointer == NULL); } + bool isNull() const { return (mPointer == NULL); } + bool notNull() const { return (mPointer != NULL); } + + operator Type*() const { return mPointer; } + bool operator !=(Type* ptr) const { return (mPointer != ptr); } + bool operator ==(Type* ptr) const { return (mPointer == ptr); } + bool operator ==(const LLPointer& ptr) const { return (mPointer == ptr.mPointer); } + bool operator < (const LLPointer& ptr) const { return (mPointer < ptr.mPointer); } + bool operator > (const LLPointer& ptr) const { return (mPointer > ptr.mPointer); } + + LLPointer& operator =(Type* ptr) + { + if( mPointer != ptr ) + { + unref(); + mPointer = ptr; + ref(); + } + + return *this; + } + + LLPointer& operator =(const LLPointer& ptr) + { + if( mPointer != ptr.mPointer ) + { + unref(); + mPointer = ptr.mPointer; + ref(); + } + return *this; + } + + // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. + template + LLPointer& operator =(const LLPointer& ptr) + { + if( mPointer != ptr.get() ) + { + unref(); + mPointer = ptr.get(); + ref(); + } + return *this; + } + + // Just exchange the pointers, which will not change the reference counts. + static void swap(LLPointer& a, LLPointer& b) + { + Type* temp = a.mPointer; + a.mPointer = b.mPointer; + b.mPointer = temp; + } + +protected: + void ref() + { + if (mPointer) + { + mPointer->ref(); + } + } + + void unref() + { + if (mPointer) + { + Type *tempp = mPointer; + mPointer = NULL; + tempp->unref(); + if (mPointer != NULL) + { + llwarns << "Unreference did assignment to non-NULL because of destructor" << llendl; + unref(); + } + } + } + +protected: + Type* mPointer; +}; + +#endif diff --git a/indra/llcommon/llrefcount.cpp b/indra/llcommon/llrefcount.cpp new file mode 100644 index 000000000..e1876599f --- /dev/null +++ b/indra/llcommon/llrefcount.cpp @@ -0,0 +1,164 @@ +/** + * @file llrefcount.cpp + * @brief Base class for reference counted objects for use with LLPointer + * + * $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 "llrefcount.h" + +#include "llerror.h" + +#if LL_REF_COUNT_DEBUG +#include "llthread.h" +#include "llapr.h" +#endif + +LLRefCount::LLRefCount(const LLRefCount& other) +: mRef(0) +{ +#if LL_REF_COUNT_DEBUG + if(gAPRPoolp) + { + mMutexp = new LLMutex(gAPRPoolp) ; + } + else + { + mMutexp = NULL ; + } + mCrashAtUnlock = FALSE ; +#endif +} + +LLRefCount& LLRefCount::operator=(const LLRefCount&) +{ + // do nothing, since ref count is specific to *this* reference + return *this; +} + +LLRefCount::LLRefCount() : + mRef(0) +{ +#if LL_REF_COUNT_DEBUG + if(gAPRPoolp) + { + mMutexp = new LLMutex(gAPRPoolp) ; + } + else + { + mMutexp = NULL ; + } + mCrashAtUnlock = FALSE ; +#endif +} + +LLRefCount::~LLRefCount() +{ + if (mRef != 0) + { + llerrs << "deleting non-zero reference" << llendl; + } + +#if LL_REF_COUNT_DEBUG + if(gAPRPoolp) + { + delete mMutexp ; + } +#endif +} + +#if LL_REF_COUNT_DEBUG +void LLRefCount::ref() const +{ + if(mMutexp) + { + if(mMutexp->isLocked()) + { + mCrashAtUnlock = TRUE ; + llerrs << "the mutex is locked by the thread: " << mLockedThreadID + << " Current thread: " << LLThread::currentID() << llendl ; + } + + mMutexp->lock() ; + mLockedThreadID = LLThread::currentID() ; + + mRef++; + + if(mCrashAtUnlock) + { + while(1); //crash here. + } + mMutexp->unlock() ; + } + else + { + mRef++; + } +} + +S32 LLRefCount::unref() const +{ + if(mMutexp) + { + if(mMutexp->isLocked()) + { + mCrashAtUnlock = TRUE ; + llerrs << "the mutex is locked by the thread: " << mLockedThreadID + << " Current thread: " << LLThread::currentID() << llendl ; + } + + mMutexp->lock() ; + mLockedThreadID = LLThread::currentID() ; + + llassert(mRef >= 1); + if (0 == --mRef) + { + if(mCrashAtUnlock) + { + while(1); //crash here. + } + mMutexp->unlock() ; + + delete this; + return 0; + } + + if(mCrashAtUnlock) + { + while(1); //crash here. + } + mMutexp->unlock() ; + return mRef; + } + else + { + llassert(mRef >= 1); + if (0 == --mRef) + { + delete this; + return 0; + } + return mRef; + } +} +#endif diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h new file mode 100644 index 000000000..8eb5d53f3 --- /dev/null +++ b/indra/llcommon/llrefcount.h @@ -0,0 +1,89 @@ +/** + * @file llrefcount.h + * @brief Base class for reference counted objects for use with LLPointer + * + * $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$ + */ +#ifndef LLREFCOUNT_H +#define LLREFCOUNT_H + +#include + +#define LL_REF_COUNT_DEBUG 0 +#if LL_REF_COUNT_DEBUG +class LLMutex ; +#endif + +//---------------------------------------------------------------------------- +// RefCount objects should generally only be accessed by way of LLPointer<>'s +// see llthread.h for LLThreadSafeRefCount +//---------------------------------------------------------------------------- + +class LL_COMMON_API LLRefCount +{ +protected: + LLRefCount(const LLRefCount& other); + LLRefCount& operator=(const LLRefCount&); + virtual ~LLRefCount(); // use unref() + +public: + LLRefCount(); + +#if LL_REF_COUNT_DEBUG + void ref() const ; + S32 unref() const ; +#else + inline void ref() const + { + mRef++; + } + + inline S32 unref() const + { + llassert(mRef >= 1); + if (0 == --mRef) + { + delete this; + return 0; + } + return mRef; + } +#endif + + //NOTE: when passing around a const LLRefCount object, this can return different results + // at different types, since mRef is mutable + S32 getNumRefs() const + { + return mRef; + } + +private: + mutable S32 mRef; + +#if LL_REF_COUNT_DEBUG + LLMutex* mMutexp ; + mutable U32 mLockedThreadID ; + mutable BOOL mCrashAtUnlock ; +#endif +}; + +#endif diff --git a/indra/llcommon/llsafehandle.h b/indra/llcommon/llsafehandle.h new file mode 100644 index 000000000..8d52d9bb1 --- /dev/null +++ b/indra/llcommon/llsafehandle.h @@ -0,0 +1,162 @@ +/** + * @file llsafehandle.h + * @brief Reference-counted object where Object() is valid, not NULL. + * + * $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$ + */ +#ifndef LLSAFEHANDLE_H +#define LLSAFEHANDLE_H + +#include "llerror.h" // *TODO: consider eliminating this + +// Expands LLPointer to return a pointer to a special instance of class Type instead of NULL. +// This is useful in instances where operations on NULL pointers are semantically safe and/or +// when error checking occurs at a different granularity or in a different part of the code +// than when referencing an object via a LLSafeHandle. + +template +class LLSafeHandle +{ +public: + LLSafeHandle() : + mPointer(NULL) + { + } + + LLSafeHandle(Type* ptr) : + mPointer(NULL) + { + assign(ptr); + } + + LLSafeHandle(const LLSafeHandle& ptr) : + mPointer(NULL) + { + assign(ptr.mPointer); + } + + // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. + template + LLSafeHandle(const LLSafeHandle& ptr) : + mPointer(NULL) + { + assign(ptr.get()); + } + + ~LLSafeHandle() + { + unref(); + } + + const Type* operator->() const { return nonNull(mPointer); } + Type* operator->() { return nonNull(mPointer); } + + Type* get() const { return mPointer; } + void clear() { assign(NULL); } + // we disallow these operations as they expose our null objects to direct manipulation + // and bypass the reference counting semantics + //const Type& operator*() const { return *nonNull(mPointer); } + //Type& operator*() { return *nonNull(mPointer); } + + operator BOOL() const { return mPointer != NULL; } + operator bool() const { return mPointer != NULL; } + bool operator!() const { return mPointer == NULL; } + bool isNull() const { return mPointer == NULL; } + bool notNull() const { return mPointer != NULL; } + + + operator Type*() const { return mPointer; } + operator const Type*() const { return mPointer; } + bool operator !=(Type* ptr) const { return (mPointer != ptr); } + bool operator ==(Type* ptr) const { return (mPointer == ptr); } + bool operator ==(const LLSafeHandle& ptr) const { return (mPointer == ptr.mPointer); } + bool operator < (const LLSafeHandle& ptr) const { return (mPointer < ptr.mPointer); } + bool operator > (const LLSafeHandle& ptr) const { return (mPointer > ptr.mPointer); } + + LLSafeHandle& operator =(Type* ptr) + { + assign(ptr); + return *this; + } + + LLSafeHandle& operator =(const LLSafeHandle& ptr) + { + assign(ptr.mPointer); + return *this; + } + + // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. + template + LLSafeHandle& operator =(const LLSafeHandle& ptr) + { + assign(ptr.get()); + return *this; + } + +public: + typedef Type* (*NullFunc)(); + static const NullFunc sNullFunc; + +protected: + void ref() + { + if (mPointer) + { + mPointer->ref(); + } + } + + void unref() + { + if (mPointer) + { + Type *tempp = mPointer; + mPointer = NULL; + tempp->unref(); + if (mPointer != NULL) + { + llwarns << "Unreference did assignment to non-NULL because of destructor" << llendl; + unref(); + } + } + } + + void assign(Type* ptr) + { + if( mPointer != ptr ) + { + unref(); + mPointer = ptr; + ref(); + } + } + + static Type* nonNull(Type* ptr) + { + return ptr == NULL ? sNullFunc() : ptr; + } + +protected: + Type* mPointer; +}; + +#endif diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp new file mode 100644 index 000000000..eb8e2c945 --- /dev/null +++ b/indra/llcommon/llsingleton.cpp @@ -0,0 +1,32 @@ +/** + * @file llsingleton.cpp + * @author Brad Kittenbrink + * + * $LicenseInfo:firstyear=2009&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 "llsingleton.h" + +std::map * LLSingletonRegistry::sSingletonMap = NULL; + diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h new file mode 100644 index 000000000..7aee1bb85 --- /dev/null +++ b/indra/llcommon/llsingleton.h @@ -0,0 +1,201 @@ +/** + * @file llsingleton.h + * + * $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$ + */ +#ifndef LLSINGLETON_H +#define LLSINGLETON_H + +#include "llerror.h" // *TODO: eliminate this + +#include +#include + +/// @brief A global registry of all singletons to prevent duplicate allocations +/// across shared library boundaries +class LL_COMMON_API LLSingletonRegistry { + private: + typedef std::map TypeMap; + static TypeMap * sSingletonMap; + + static void checkInit() + { + if(sSingletonMap == NULL) + { + sSingletonMap = new TypeMap(); + } + } + + public: + template static void * & get() + { + std::string name(typeid(T).name()); + + checkInit(); + + // the first entry of the pair returned by insert will be either the existing + // iterator matching our key, or the newly inserted NULL initialized entry + // see "Insert element" in http://www.sgi.com/tech/stl/UniqueAssociativeContainer.html + TypeMap::iterator result = + sSingletonMap->insert(std::make_pair(name, (void*)NULL)).first; + + return result->second; + } +}; + +// LLSingleton implements the getInstance() method part of the Singleton +// pattern. It can't make the derived class constructors protected, though, so +// you have to do that yourself. +// +// There are two ways to use LLSingleton. The first way is to inherit from it +// while using the typename that you'd like to be static as the template +// parameter, like so: +// +// class Foo: public LLSingleton{}; +// +// Foo& instance = Foo::instance(); +// +// The second way is to use the singleton class directly, without inheritance: +// +// typedef LLSingleton FooSingleton; +// +// Foo& instance = FooSingleton::instance(); +// +// In this case, the class being managed as a singleton needs to provide an +// initSingleton() method since the LLSingleton virtual method won't be +// available +// +// As currently written, it is not thread-safe. + +template +class LLSingleton : private boost::noncopyable +{ + +private: + typedef enum e_init_state + { + UNINITIALIZED, + CONSTRUCTING, + INITIALIZING, + INITIALIZED, + DELETED + } EInitState; + + static void deleteSingleton() + { + delete getData().mSingletonInstance; + getData().mSingletonInstance = NULL; + } + + // stores pointer to singleton instance + // and tracks initialization state of singleton + struct SingletonInstanceData + { + EInitState mInitState; + DERIVED_TYPE* mSingletonInstance; + + SingletonInstanceData() + : mSingletonInstance(NULL), + mInitState(UNINITIALIZED) + {} + + ~SingletonInstanceData() + { + deleteSingleton(); + } + }; + +public: + virtual ~LLSingleton() + { + SingletonInstanceData& data = getData(); + data.mSingletonInstance = NULL; + data.mInitState = DELETED; + } + + static SingletonInstanceData& getData() + { + // this is static to cache the lookup results + static void * & registry = LLSingletonRegistry::get(); + + // *TODO - look into making this threadsafe + if(NULL == registry) + { + static SingletonInstanceData data; + registry = &data; + } + + return *static_cast(registry); + } + + static DERIVED_TYPE* getInstance() + { + SingletonInstanceData& data = getData(); + + if (data.mInitState == CONSTRUCTING) + { + llerrs << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from singleton constructor!" << llendl; + } + + if (data.mInitState == DELETED) + { + llwarns << "Trying to access deleted singleton " << typeid(DERIVED_TYPE).name() << " creating new instance" << llendl; + } + + if (!data.mSingletonInstance) + { + data.mInitState = CONSTRUCTING; + data.mSingletonInstance = new DERIVED_TYPE(); + data.mInitState = INITIALIZING; + data.mSingletonInstance->initSingleton(); + data.mInitState = INITIALIZED; + } + + return data.mSingletonInstance; + } + + // Reference version of getInstance() + // Preferred over getInstance() as it disallows checking for NULL + static DERIVED_TYPE& instance() + { + return *getInstance(); + } + + // Has this singleton been created uet? + // Use this to avoid accessing singletons before the can safely be constructed + static bool instanceExists() + { + return getData().mInitState == INITIALIZED; + } + + // Has this singleton already been deleted? + // Use this to avoid accessing singletons from a static object's destructor + static bool destroyed() + { + return getData().mInitState == DELETED; + } + +private: + virtual void initSingleton() {} +}; + +#endif diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 43b63e135..31440e213 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -3789,10 +3789,21 @@ Value 0 + DebugShowMemory + + Comment + Show Total Allocated Memory + Persist + 1 + Type + Boolean + Value + 0 + DebugShowRenderInfo Comment - Show depth buffer contents + Show stats about current scene Persist 1 Type diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 750dc4c29..25b38e55d 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2628,7 +2628,7 @@ void LLAppViewer::handleViewerCrash() gDebugInfo["CurrentPath"] = gDirUtilp->getCurPath(); gDebugInfo["SessionLength"] = F32(LLFrameTimer::getElapsedSeconds()); gDebugInfo["StartupState"] = LLStartUp::getStartupStateString(); - gDebugInfo["RAMInfo"]["Allocated"] = (LLSD::Integer) getCurrentRSS() >> 10; + gDebugInfo["RAMInfo"]["Allocated"] = (LLSD::Integer) LLMemory::getCurrentRSS() >> 10; gDebugInfo["FirstLogin"] = (LLSD::Boolean) gAgent.isFirstLogin(); gDebugInfo["FirstRunThisInstall"] = gSavedSettings.getBOOL("FirstRunThisInstall"); diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 8d5b1fef6..8c7839cb5 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -220,7 +220,7 @@ void display_stats() F32 mem_log_freq = gSavedSettings.getF32("MemoryLogFrequency"); if (mem_log_freq > 0.f && gRecentMemoryTime.getElapsedTimeF32() >= mem_log_freq) { - gMemoryAllocated = getCurrentRSS(); + gMemoryAllocated = LLMemory::getCurrentRSS(); U32 memory = (U32)(gMemoryAllocated / (1024*1024)); llinfos << llformat("MEMORY: %d MB", memory) << llendl; gRecentMemoryTime.reset(); diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index c1b87cc34..3f24ba674 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -707,7 +707,7 @@ void send_stats() agent["ping"] = gAvgSimPing; agent["meters_traveled"] = gAgent.getDistanceTraveled(); agent["regions_visited"] = gAgent.getRegionsVisited(); - agent["mem_use"] = getCurrentRSS() / 1024.0; + agent["mem_use"] = LLMemory::getCurrentRSS() / 1024.0; LLSD &system = body["system"]; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 91e715171..8fe85caa4 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -333,6 +333,13 @@ public: addText(xpos, ypos, llformat("Time: %d:%02d:%02d", hours,mins,secs)); ypos += y_inc; } +#if LL_WINDOWS + if (gSavedSettings.getBOOL("DebugShowMemory")) + { + addText(xpos, ypos, llformat("Memory: %d (KB)", LLMemory::getWorkingSetSize() / 1024)); + ypos += y_inc; + } +#endif if (gDisplayCameraPos) { std::string camera_view_text; From c081efa460ddecb8faff92fd77068ff0ea8d19b4 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sat, 30 Jul 2011 20:28:34 -0500 Subject: [PATCH 09/22] LLVoiceClient::mNextAudioSession now initialized. --- indra/newview/llvoiceclient.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 7c3022f06..5fef0f877 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -1120,6 +1120,7 @@ LLVoiceClient::LLVoiceClient() mSpeakerVolume = 0; mMicVolume = 0; + mNextAudioSession = NULL; mAudioSession = NULL; mAudioSessionChanged = false; From b258b71e07e8bf87f5f8def781f6795c0be5e9dd Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sat, 30 Jul 2011 20:30:07 -0500 Subject: [PATCH 10/22] Flipped the MESH_ENABLED switch. Added missing components and updated stuff to be closer to mesh-development head. --- indra/cmake/00-Common.cmake | 1 + indra/llcharacter/lljoint.cpp | 40 + indra/llcharacter/lljoint.h | 27 + indra/llcommon/llassettype.cpp | 3 + indra/llcommon/llassettype.h | 11 +- indra/llcommon/llfasttimer.h | 13 +- indra/llcommon/llqueuedthread.cpp | 46 +- indra/llcommon/llqueuedthread.h | 3 +- indra/llcommon/llsdserialize.cpp | 185 ++ indra/llcommon/llsdserialize.h | 5 + indra/llmath/llvolume.cpp | 172 +- indra/llmath/llvolume.h | 5 + indra/llmath/llvolumemgr.cpp | 2 +- indra/llprimitive/CMakeLists.txt | 4 +- indra/llprimitive/llmodel.cpp | 538 +++-- indra/llprimitive/llmodel.h | 35 +- indra/llprimitive/llprimitive.h | 4 + indra/llrender/llglslshader.cpp | 3 + indra/llrender/llglslshader.h | 3 + indra/llrender/llshadermgr.cpp | 10 + indra/newview/CMakeLists.txt | 2 + indra/newview/app_settings/settings.xml | 78 + .../shaders/class1/avatar/objectSkinV.glsl | 30 + .../class1/deferred/alphaSkinnedV.glsl | 112 + .../class1/deferred/attachmentShadowF.glsl | 18 + .../class1/deferred/attachmentShadowV.glsl | 27 + .../shaders/class1/deferred/bumpSkinnedV.glsl | 37 + .../class1/deferred/diffuseSkinnedV.glsl | 33 + .../objects/fullbrightShinySkinnedV.glsl | 39 + .../class1/objects/fullbrightSkinnedV.glsl | 37 + .../class1/objects/shinySimpleSkinnedV.glsl | 39 + .../class1/objects/simpleSkinnedV.glsl | 39 + .../class2/deferred/alphaSkinnedV.glsl | 115 + .../shaders/class2/deferred/softenLightF.glsl | 346 +++ indra/newview/lldrawpoolavatar.cpp | 28 +- indra/newview/llface.cpp | 8 +- indra/newview/llface.h | 3 + indra/newview/llfasttimerview.cpp | 11 + indra/newview/llmanipscale.cpp | 9 +- indra/newview/llmeshrepository.cpp | 1856 +++++++---------- indra/newview/llmeshrepository.h | 122 +- indra/newview/llviewerobject.cpp | 25 +- indra/newview/llviewerobject.h | 7 +- indra/newview/llviewerregion.cpp | 88 +- indra/newview/llviewerregion.h | 13 + indra/newview/llviewershadermgr.cpp | 217 +- indra/newview/llviewershadermgr.h | 20 + indra/newview/llvoavatar.cpp | 158 +- indra/newview/llvoavatar.h | 23 + indra/newview/llvovolume.cpp | 59 +- indra/newview/llvovolume.h | 6 - 51 files changed, 3264 insertions(+), 1451 deletions(-) create mode 100644 indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl create mode 100644 indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl create mode 100644 indra/newview/app_settings/shaders/class1/deferred/attachmentShadowF.glsl create mode 100644 indra/newview/app_settings/shaders/class1/deferred/attachmentShadowV.glsl create mode 100644 indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl create mode 100644 indra/newview/app_settings/shaders/class1/deferred/diffuseSkinnedV.glsl create mode 100644 indra/newview/app_settings/shaders/class1/objects/fullbrightShinySkinnedV.glsl create mode 100644 indra/newview/app_settings/shaders/class1/objects/fullbrightSkinnedV.glsl create mode 100644 indra/newview/app_settings/shaders/class1/objects/shinySimpleSkinnedV.glsl create mode 100644 indra/newview/app_settings/shaders/class1/objects/simpleSkinnedV.glsl create mode 100644 indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl create mode 100644 indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index ef5b14b80..30fc121ee 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -297,6 +297,7 @@ endif (STANDALONE) if(1 EQUAL 1) add_definitions(-DOPENSIM_RULES=1) + add_definitions(-DMESH_ENABLED=1) endif(1 EQUAL 1) if(SERVER) diff --git a/indra/llcharacter/lljoint.cpp b/indra/llcharacter/lljoint.cpp index 25fbc03fa..16600a227 100644 --- a/indra/llcharacter/lljoint.cpp +++ b/indra/llcharacter/lljoint.cpp @@ -56,6 +56,9 @@ LLJoint::LLJoint() mUpdateXform = TRUE; mJointNum = -1; touch(); +#if MESH_ENABLED + mResetAfterRestoreOldXform = false; +#endif //MESH_ENABLED } @@ -239,6 +242,43 @@ void LLJoint::setPosition( const LLVector3& pos ) } } +#if MESH_ENABLED +//-------------------------------------------------------------------- +// setPosition() +//-------------------------------------------------------------------- +void LLJoint::setDefaultFromCurrentXform( void ) +{ + mDefaultXform = mXform; + touch(MATRIX_DIRTY | POSITION_DIRTY); + +} + +//-------------------------------------------------------------------- +// storeCurrentXform() +//-------------------------------------------------------------------- +void LLJoint::storeCurrentXform( const LLVector3& pos ) +{ + mOldXform = mXform; + mResetAfterRestoreOldXform = true; + setPosition( pos ); +} +//-------------------------------------------------------------------- +// restoreOldXform() +//-------------------------------------------------------------------- +void LLJoint::restoreOldXform( void ) +{ + mResetAfterRestoreOldXform = false; + mXform = mOldXform; +} +//-------------------------------------------------------------------- +// restoreOldXform() +//-------------------------------------------------------------------- +void LLJoint::restoreToDefaultXform( void ) +{ + mXform = mDefaultXform; + setPosition( mXform.getPosition() ); +} +#endif //MESH_ENABLED //-------------------------------------------------------------------- // getWorldPosition() diff --git a/indra/llcharacter/lljoint.h b/indra/llcharacter/lljoint.h index 37ae44998..852f04e05 100644 --- a/indra/llcharacter/lljoint.h +++ b/indra/llcharacter/lljoint.h @@ -86,10 +86,19 @@ protected: // explicit transformation members LLXformMatrix mXform; +#if MESH_ENABLED + LLXformMatrix mOldXform; + LLXformMatrix mDefaultXform; + LLUUID mId; +#endif //MESH_ENABLED public: U32 mDirtyFlags; BOOL mUpdateXform; + +#if MESH_ENABLED + BOOL mResetAfterRestoreOldXform; +#endif //MESH_ENABLED // describes the skin binding pose LLVector3 mSkinOffset; @@ -179,6 +188,24 @@ public: S32 getJointNum() const { return mJointNum; } void setJointNum(S32 joint_num) { mJointNum = joint_num; } +#if MESH_ENABLED + + void restoreOldXform( void ); + void restoreToDefaultXform( void ); + void setDefaultFromCurrentXform( void ); + void storeCurrentXform( const LLVector3& pos ); + + //Accessor for the joint id + LLUUID getId( void ) { return mId; } + //Setter for the joints id + void setId( const LLUUID& id ) { mId = id;} + + //If the old transform flag has been set, then the reset logic in avatar needs to be aware(test) of it + const BOOL doesJointNeedToBeReset( void ) const { return mResetAfterRestoreOldXform; } + //Setter for joint reset flag + void setJointToBeReset( BOOL val ) { mResetAfterRestoreOldXform = val; } +#endif //MESH_ENABLED + // std::string exportString(U32 tabs = 0); // diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp index d88013fc4..c2b3779db 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -105,6 +105,9 @@ LLAssetDictionary::LLAssetDictionary() //addEntry(LLAssetType::AT_CURRENT_OUTFIT, new AssetEntry("FOLDER_LINK", "current", "current outfit", false, false, false)); //addEntry(LLAssetType::AT_OUTFIT, new AssetEntry("OUTFIT", "outfit", "outfit", false, false, false)); //addEntry(LLAssetType::AT_MY_OUTFITS, new AssetEntry("MY_OUTFITS", "my_otfts", "my outfits", false, false, false)); +#if MESH_ENABLED + addEntry(LLAssetType::AT_MESH, new AssetEntry("MESH", "mesh", "mesh", false, false, false)); +#endif //MESH_ENABLED addEntry(LLAssetType::AT_NONE, new AssetEntry("NONE", "-1", NULL, false, false, false)); }; diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index 554fc0c02..4590fc831 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -138,13 +138,19 @@ public: // Inventory folder link AT_LINK_FOLDER = 25, - + //AT_CURRENT_OUTFIT = 46, //AT_OUTFIT = 47, //AT_MY_OUTFITS = 48, +#if MESH_ENABLED + AT_MESH = 49, + // Mesh data in our proprietary SLM format + + AT_COUNT = 50, +#endif //MESH_ENABLED // +*********************************************+ // | TO ADD AN ELEMENT TO THIS ENUM: | // +*********************************************+ @@ -155,8 +161,9 @@ public: // +*********************************************+ //AT_COUNT = 49, - +#if !MESH_ENABLED AT_COUNT = 26, +#endif //!MESH_ENABLED AT_NONE = -1 }; diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index 3a7f30511..7f3dac2cf 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -72,7 +72,12 @@ public: FTM_UPDATE_GRASS, FTM_UPDATE_TREE, FTM_UPDATE_AVATAR, - +#if MESH_ENABLED + FTM_UPDATE_RIGGED_VOLUME, + FTM_SKIN_RIGGED, + FTM_RIGGED_OCTREE, +#endif //MESH_ENABLED + // common render components FTM_SHADOW_GEOMETRY, FTM_SHADOW_RENDER, @@ -131,6 +136,12 @@ public: FTM_STATESORT, FTM_STATESORT_DRAWABLE, FTM_STATESORT_POSTSORT, +#if MESH_ENABLED + FTM_MESH_UPDATE, + FTM_MESH_LOCK1, + FTM_MESH_LOCK2, + FTM_LOAD_MESH_LOD, +#endif //MESH_ENABLED FTM_REBUILD_VBO, FTM_REBUILD_VOLUME_VB, FTM_REBUILD_BRIDGE_VB, diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp index 5be0414f9..c88909eb2 100644 --- a/indra/llcommon/llqueuedthread.cpp +++ b/indra/llcommon/llqueuedthread.cpp @@ -41,7 +41,8 @@ LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded) : LLThread(name), mThreaded(threaded), mIdleThread(TRUE), - mNextHandle(0) + mNextHandle(0), + mStarted(FALSE) { if (mThreaded) { @@ -52,6 +53,10 @@ LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded) : // MAIN THREAD LLQueuedThread::~LLQueuedThread() { + if (!mThreaded) + { + endThread(); + } shutdown(); // ~LLThread() will be called here } @@ -90,6 +95,7 @@ void LLQueuedThread::shutdown() if (req->getStatus() == STATUS_QUEUED || req->getStatus() == STATUS_INPROGRESS) { ++active_count; + req->setStatus(STATUS_ABORTED); // avoid assert in deleteRequest } req->deleteRequest(); } @@ -105,6 +111,14 @@ void LLQueuedThread::shutdown() // virtual S32 LLQueuedThread::update(U32 max_time_ms) { + if (!mStarted) + { + if (!mThreaded) + { + startThread(); + mStarted = TRUE; + } + } return updateQueue(max_time_ms); } @@ -118,8 +132,11 @@ S32 LLQueuedThread::updateQueue(U32 max_time_ms) if (mThreaded) { pending = getPending(); + if(pending > 0) + { unpause(); } + } else { while (pending > 0) @@ -349,9 +366,9 @@ bool LLQueuedThread::completeRequest(handle_t handle) #if _DEBUG // llinfos << llformat("LLQueuedThread::Completed req [%08d]",handle) << llendl; #endif - //re insert to the queue to schedule for a delete later - req->setStatus(STATUS_DELETE); - mRequestQueue.insert(req); + mRequestHash.erase(handle); + req->deleteRequest(); +// check(); res = true; } unlockData(); @@ -395,19 +412,11 @@ S32 LLQueuedThread::processNextRequest() } req = *mRequestQueue.begin(); mRequestQueue.erase(mRequestQueue.begin()); - - if(req->getStatus() == STATUS_DELETE) - { - mRequestHash.erase(req); - req->deleteRequest(); - continue; - } - if ((req->getFlags() & FLAG_ABORT) || (mStatus == QUITTING)) { req->setStatus(STATUS_ABORTED); req->finishRequest(false); - if ((req->getFlags() & FLAG_AUTO_COMPLETE)) + if (req->getFlags() & FLAG_AUTO_COMPLETE) { mRequestHash.erase(req); req->deleteRequest(); @@ -418,9 +427,11 @@ S32 LLQueuedThread::processNextRequest() llassert_always(req->getStatus() == STATUS_QUEUED); break; } + U32 start_priority = 0 ; if (req) { req->setStatus(STATUS_INPROGRESS); + start_priority = req->getPriority(); } unlockData(); @@ -436,13 +447,12 @@ S32 LLQueuedThread::processNextRequest() { lockData(); req->setStatus(STATUS_COMPLETE); - req->finishRequest(true); - - if ((req->getFlags() & FLAG_AUTO_COMPLETE)) + if (req->getFlags() & FLAG_AUTO_COMPLETE) { mRequestHash.erase(req); req->deleteRequest(); +// check(); } unlockData(); } @@ -451,9 +461,8 @@ S32 LLQueuedThread::processNextRequest() lockData(); req->setStatus(STATUS_QUEUED); mRequestQueue.insert(req); - U32 priority = req->getPriority(); unlockData(); - if (priority < PRIORITY_NORMAL) + if (mThreaded && start_priority < PRIORITY_NORMAL) { ms_sleep(1); // sleep the thread a little } @@ -481,6 +490,7 @@ void LLQueuedThread::run() // call checPause() immediately so we don't try to do anything before the class is fully constructed checkPause(); startThread(); + mStarted = TRUE; while (1) { diff --git a/indra/llcommon/llqueuedthread.h b/indra/llcommon/llqueuedthread.h index e0e0f1bfe..f0c36896c 100644 --- a/indra/llcommon/llqueuedthread.h +++ b/indra/llcommon/llqueuedthread.h @@ -184,7 +184,7 @@ public: void waitOnPending(); void printQueueStats(); - S32 getPending(); + virtual S32 getPending(); bool getThreaded() { return mThreaded ? true : false; } // Request accessors @@ -202,6 +202,7 @@ public: protected: BOOL mThreaded; // if false, run on main thread and do updates during update() + BOOL mStarted; // required when mThreaded is false to call startThread() from update() LLAtomic32 mIdleThread; // request queue is empty (or we are quitting) and the thread is idle typedef std::set request_queue_t; diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp index b7f28d365..cc0307a7a 100644 --- a/indra/llcommon/llsdserialize.cpp +++ b/indra/llcommon/llsdserialize.cpp @@ -39,6 +39,13 @@ #include #include "apr_base64.h" +#if MESH_ENABLED +#ifdef LL_STANDALONE +# include +#else +# include "zlib/zlib.h" // for davep's dirty little zip functions +#endif +#endif //MESH_ENABLED #if !LL_WINDOWS #include // htonl & ntohl @@ -1990,3 +1997,181 @@ std::ostream& operator<<(std::ostream& s, const LLSD& llsd) return s; } +#if MESH_ENABLED + +//dirty little zippers -- yell at davep if these are horrid + +//return a string containing gzipped bytes of binary serialized LLSD +// VERY inefficient -- creates several copies of LLSD block in memory +std::string zip_llsd(LLSD& data) +{ + std::stringstream llsd_strm; + + LLSDSerialize::toBinary(data, llsd_strm); + + const U32 CHUNK = 65536; + + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + S32 ret = deflateInit(&strm, Z_BEST_COMPRESSION); + if (ret != Z_OK) + { + llwarns << "Failed to compress LLSD block." << llendl; + return std::string(); + } + + std::string source = llsd_strm.str(); + + U8 out[CHUNK]; + + strm.avail_in = source.size(); + strm.next_in = (U8*) source.data(); + U8* output = NULL; + + U32 cur_size = 0; + + U32 have = 0; + + do + { + strm.avail_out = CHUNK; + strm.next_out = out; + + ret = deflate(&strm, Z_FINISH); + if (ret == Z_OK || ret == Z_STREAM_END) + { //copy result into output + if (strm.avail_out >= CHUNK) + { + free(output); + llwarns << "Failed to compress LLSD block." << llendl; + return std::string(); + } + + have = CHUNK-strm.avail_out; + output = (U8*) realloc(output, cur_size+have); + memcpy(output+cur_size, out, have); + cur_size += have; + } + else + { + free(output); + llwarns << "Failed to compress LLSD block." << llendl; + return std::string(); + } + } + while (ret == Z_OK); + + std::string::size_type size = cur_size; + + std::string result((char*) output, size); + deflateEnd(&strm); + free(output); + +#if 0 //verify results work with unzip_llsd + std::istringstream test(result); + LLSD test_sd; + if (!unzip_llsd(test_sd, test, result.size())) + { + llerrs << "Invalid compression result!" << llendl; + } +#endif + + return result; +} + +//decompress a block of LLSD from provided istream +// not very efficient -- creats a copy of decompressed LLSD block in memory +// and deserializes from that copy using LLSDSerialize +bool unzip_llsd(LLSD& data, std::istream& is, S32 size) +{ + U8* result = NULL; + U32 cur_size = 0; + z_stream strm; + + const U32 CHUNK = 65536; + + U8 *in = new U8[size]; + is.read((char*) in, size); + + U8 out[CHUNK]; + + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = size; + strm.next_in = in; + + S32 ret = inflateInit(&strm); + + do + { + strm.avail_out = CHUNK; + strm.next_out = out; + ret = inflate(&strm, Z_NO_FLUSH); + if (ret == Z_STREAM_ERROR) + { + inflateEnd(&strm); + free(result); + delete [] in; + return false; + } + + switch (ret) + { + case Z_NEED_DICT: + ret = Z_DATA_ERROR; + case Z_DATA_ERROR: + case Z_MEM_ERROR: + inflateEnd(&strm); + free(result); + delete [] in; + return false; + break; + } + + U32 have = CHUNK-strm.avail_out; + + result = (U8*) realloc(result, cur_size + have); + memcpy(result+cur_size, out, have); + cur_size += have; + + } while (ret == Z_OK); + + inflateEnd(&strm); + delete [] in; + + if (ret != Z_STREAM_END) + { + free(result); + return false; + } + + //result now points to the decompressed LLSD block + { + std::string res_str((char*) result, cur_size); + + std::string deprecated_header(""); + + if (res_str.substr(0, deprecated_header.size()) == deprecated_header) + { + res_str = res_str.substr(deprecated_header.size()+1, cur_size); + } + cur_size = res_str.size(); + + std::istringstream istr(res_str); + + if (!LLSDSerialize::fromBinary(data, istr, cur_size)) + { + llwarns << "Failed to unzip LLSD block" << llendl; + free(result); + return false; + } + } + + free(result); + return true; +} +#endif //MESH_ENABLED \ No newline at end of file diff --git a/indra/llcommon/llsdserialize.h b/indra/llcommon/llsdserialize.h index 7dd2603eb..aed844fee 100644 --- a/indra/llcommon/llsdserialize.h +++ b/indra/llcommon/llsdserialize.h @@ -787,4 +787,9 @@ public: } }; +#if MESH_ENABLED +//dirty little zip functions -- yell at davep +LL_COMMON_API std::string zip_llsd(LLSD& data); +LL_COMMON_API bool unzip_llsd(LLSD& data, std::istream& is, S32 size); +#endif //MESH_ENABLED #endif // LL_LLSDSERIALIZE_H diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index a3636a868..d7a0d77a3 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -2138,6 +2138,13 @@ LLVolume::~LLVolume() mPathp = NULL; mProfilep = NULL; mVolumeFaces.clear(); + +#if MESH_ENABLED + ll_aligned_free_16(mHullPoints); + mHullPoints = NULL; + ll_aligned_free_16(mHullIndices); + mHullIndices = NULL; +#endif //MESH_ENABLED } BOOL LLVolume::generate() @@ -2378,11 +2385,16 @@ bool LLVolumeFace::VertexData::operator==(const LLVolumeFace::VertexData& rhs)co 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) + + const F32 epsilon = 0.00001f; + + if (rhs.mData[POSITION].equals3(mData[POSITION], epsilon) && + fabs(rhs.mTexCoord[0]-mTexCoord[0]) < epsilon && + fabs(rhs.mTexCoord[1]-mTexCoord[1]) < epsilon) { if (angle_cutoff > 1.f) { - retval = (mData[NORMAL].equals3(rhs.mData[NORMAL])); + retval = (mData[NORMAL].equals3(rhs.mData[NORMAL], epsilon)); } else { @@ -2499,9 +2511,9 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) } { - U16* n = (U16*) &(norm[0]); - if(n) + if (!norm.empty()) { + U16* n = (U16*) &(norm[0]); for (U32 j = 0; j < num_verts; ++j) { norm_out->set((F32) n[0], (F32) n[1], (F32) n[2]); @@ -2512,12 +2524,16 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) n += 3; } } + else + { + memset(norm_out, 0, sizeof(LLVector4a)*num_verts); + } } { - U16* t = (U16*) &(tc[0]); - if(t) + if (!tc.empty()) { + U16* t = (U16*) &(tc[0]); for (U32 j = 0; j < num_verts; j+=2) { if (j < num_verts-1) @@ -2538,6 +2554,10 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) tc_out++; } } + else + { + memset(tc_out, 0, sizeof(LLVector2)*num_verts); + } } if (mdl[i].has("Weights")) @@ -2635,7 +2655,7 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) if (do_reverse_triangles) { - for (U32 j = 0; j < face.mNumIndices; j += 3) + for (U32 j = 0; j < (U32)face.mNumIndices; j += 3) { // swap the 2nd and 3rd index S32 swap = face.mIndices[j+1]; @@ -2662,6 +2682,25 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) min.setMin(min, face.mPositions[i]); max.setMax(max, face.mPositions[i]); } + + if (face.mTexCoords) + { + LLVector2& min_tc = face.mTexCoordExtents[0]; + LLVector2& max_tc = face.mTexCoordExtents[1]; + + min_tc = face.mTexCoords[0]; + max_tc = face.mTexCoords[0]; + + for (U32 j = 1; j < (U32)face.mNumVertices; ++j) + { + update_min_max(min_tc, max_tc, face.mTexCoords[j]); + } + } + else + { + face.mTexCoordExtents[0].set(0,0); + face.mTexCoordExtents[1].set(1,1); + } } } } @@ -2741,11 +2780,13 @@ void LLVolume::makeTetrahedron() n[2] = cv[2].getNormal(); n += 3; - tc[0] = cv[0].mTexCoord; - tc[1] = cv[1].mTexCoord; - tc[2] = cv[2].mTexCoord; - tc += 3; - + if(tc) + { + tc[0] = cv[0].mTexCoord; + tc[1] = cv[1].mTexCoord; + tc[2] = cv[2].mTexCoord; + tc += 3; + } //side 2 cv[0].setPosition(p[3]); @@ -2764,11 +2805,14 @@ void LLVolume::makeTetrahedron() n[2] = cv[2].getNormal(); n += 3; - tc[0] = cv[0].mTexCoord; - tc[1] = cv[1].mTexCoord; - tc[2] = cv[2].mTexCoord; - tc += 3; - + if(tc) + { + 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]); @@ -2786,10 +2830,13 @@ void LLVolume::makeTetrahedron() n[2] = cv[2].getNormal(); n += 3; - tc[0] = cv[0].mTexCoord; - tc[1] = cv[1].mTexCoord; - tc[2] = cv[2].mTexCoord; - tc += 3; + if(tc) + { + tc[0] = cv[0].mTexCoord; + tc[1] = cv[1].mTexCoord; + tc[2] = cv[2].mTexCoord; + tc += 3; + } //side 4 cv[0].setPosition(p[2]); @@ -2808,10 +2855,13 @@ void LLVolume::makeTetrahedron() n[2] = cv[2].getNormal(); n += 3; - tc[0] = cv[0].mTexCoord; - tc[1] = cv[1].mTexCoord; - tc[2] = cv[2].mTexCoord; - tc += 3; + if(tc) + { + 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++) @@ -2833,7 +2883,7 @@ void LLVolume::copyVolumeFaces(const LLVolume* volume) void LLVolume::cacheOptimize() { - for (S32 i = 0; i < mVolumeFaces.size(); ++i) + for (S32 i = 0; i < (S32)mVolumeFaces.size(); ++i) { mVolumeFaces[i].cacheOptimize(); } @@ -5523,6 +5573,9 @@ LLVolumeFace::LLVolumeFace() : mBinormals(NULL), mTexCoords(NULL), mIndices(NULL), +#if MESH_ENABLED + mWeights(NULL), +#endif //MESH_ENABLED mOctree(NULL) { mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3); @@ -5590,7 +5643,16 @@ LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src) 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.mTexCoords) + { + LLVector4a::memcpyNonAliased16((F32*) mTexCoords, (F32*) src.mTexCoords, tc_size); + } + else + { + ll_aligned_free_16(mTexCoords) ; + mTexCoords = NULL ; + } if (src.mBinormals) @@ -5715,8 +5777,23 @@ BOOL LLVolumeFace::create(LLVolume* volume, BOOL partial_build) void LLVolumeFace::getVertexData(U16 index, LLVolumeFace::VertexData& cv) { cv.setPosition(mPositions[index]); - cv.setNormal(mNormals[index]); - cv.mTexCoord = mTexCoords[index]; + if (mNormals) + { + cv.setNormal(mNormals[index]); + } + else + { + cv.getNormal().clear(); + } + + if (mTexCoords) + { + cv.mTexCoord = mTexCoords[index]; + } + else + { + cv.mTexCoord.clear(); + } } bool LLVolumeFace::VertexMapData::operator==(const LLVolumeFace::VertexData& rhs) const @@ -5746,7 +5823,10 @@ void LLVolumeFace::optimize(F32 angle_cutoff) LLVolumeFace new_face; //map of points to vector of vertices at that point - VertexMapData::PointMap point_map; + std::map > point_map; + + LLVector4a range; + range.setSub(mExtents[1],mExtents[0]); //remove redundant vertices for (U32 i = 0; i < (U32)mNumIndices; ++i) @@ -5757,7 +5837,19 @@ void LLVolumeFace::optimize(F32 angle_cutoff) getVertexData(index, cv); BOOL found = FALSE; - VertexMapData::PointMap::iterator point_iter = point_map.find(LLVector3(cv.getPosition().getF32ptr())); + + LLVector4a pos; + pos.setSub(mPositions[index], mExtents[0]); + pos.div(range); + + U64 pos64 = 0; + + pos64 = (U16) (pos[0]*65535); + pos64 = pos64 | (((U64) (pos[1]*65535)) << 16); + pos64 = pos64 | (((U64) (pos[2]*65535)) << 32); + + std::map >::iterator point_iter = point_map.find(pos64); + if (point_iter != point_map.end()) { //duplicate point might exist for (U32 j = 0; j < point_iter->second.size(); ++j) @@ -5789,11 +5881,26 @@ void LLVolumeFace::optimize(F32 angle_cutoff) } else { - point_map[LLVector3(d.getPosition().getF32ptr())].push_back(d); + point_map[pos64].push_back(d); } } } + llassert(new_face.mNumIndices == mNumIndices); + llassert(new_face.mNumVertices <= mNumVertices); + + if (angle_cutoff > 1.f && !mNormals) + { + ll_aligned_free_16(new_face.mNormals); + new_face.mNormals = NULL; + } + + if (!mTexCoords) + { + ll_aligned_free_16(new_face.mTexCoords); + new_face.mTexCoords = NULL; + } + swapData(new_face); } @@ -7026,6 +7133,7 @@ void LLVolumeFace::allocateBinormals(S32 num_verts) #if MESH_ENABLED void LLVolumeFace::allocateWeights(S32 num_verts) { + ll_aligned_free_16(mWeights); mWeights = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts); } diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index a4f9f34b7..c85e43e1f 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -958,7 +958,12 @@ class LLVolume : public LLRefCount { friend class LLVolumeLODGroup; +#if MESH_ENABLED +protected: +#endif //MESH_ENABLED +#if !MESH_ENABLED private: +#endif //!MESH_ENABLED LLVolume(const LLVolume&); // Don't implement ~LLVolume(); // use unref diff --git a/indra/llmath/llvolumemgr.cpp b/indra/llmath/llvolumemgr.cpp index f140d7910..c6c68e3a6 100644 --- a/indra/llmath/llvolumemgr.cpp +++ b/indra/llmath/llvolumemgr.cpp @@ -154,7 +154,7 @@ void LLVolumeMgr::unrefVolume(LLVolume *volumep) volume_lod_group_map_t::iterator iter = mVolumeLODGroups.find(params); if( iter == mVolumeLODGroups.end() ) { - llerrs << "Warning! Tried to cleanup unknown volume type! " << *params << llendl; + llwarns << "Warning! Tried to cleanup unknown volume type! " << *params << llendl; if (mDataMutex) { mDataMutex->unlock(); diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt index 88a9ef11d..2f385d944 100644 --- a/indra/llprimitive/CMakeLists.txt +++ b/indra/llprimitive/CMakeLists.txt @@ -17,7 +17,7 @@ include_directories( set(llprimitive_SOURCE_FILES llmaterialtable.cpp - #llmodel.cpp + llmodel.cpp llprimitive.cpp llprimtexturelist.cpp lltextureanim.cpp @@ -33,7 +33,7 @@ set(llprimitive_HEADER_FILES legacy_object_types.h llmaterialtable.h - #llmodel.h + llmodel.h llprimitive.h llprimtexturelist.h lltextureanim.h diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index 0463d5364..a7563f17c 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -26,15 +26,20 @@ #include "linden_common.h" +#if MESH_ENABLED #include "llmodel.h" +#if MESH_IMPORT #include "llconvexdecomposition.h" +#endif //MESH_IMPORT #include "llsdserialize.h" #include "llvector4a.h" +#if MESH_IMPORT #include "dae.h" #include "dae/daeErrorHandler.h" #include "dom/domConstants.h" #include "dom/domMesh.h" +#endif //MESH_IMPORT #ifdef LL_STANDALONE # include @@ -67,87 +72,13 @@ LLModel::~LLModel() { if (mDecompID >= 0) { +#if MESH_IMPORT LLConvexDecomposition::getInstance()->deleteDecomposition(mDecompID); +#endif //MESH_IMPORT } } -void load_face_from_dom_inputs(LLVolumeFace& face, const domInputLocalOffset_Array& inputs, U32 min_idx, U32 max_idx) -{ - for (U32 j = 0; j < inputs.getCount(); ++j) - { - if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[j]->getSemantic()) == 0) - { //found vertex array - const domURIFragmentType& uri = inputs[j]->getSource(); - daeElementRef elem = uri.getElement(); - domVertices* vertices = (domVertices*) elem.cast(); - - domInputLocal_Array& v_inp = vertices->getInput_array(); - if (inputs[j]->getOffset() != 0) - { - llerrs << "Vertex array offset MUST be zero." << llendl; - } - - for (U32 k = 0; k < v_inp.getCount(); ++k) - { - if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0) - { - const domURIFragmentType& uri = v_inp[k]->getSource(); - - daeElementRef elem = uri.getElement(); - domSource* src = (domSource*) elem.cast(); - - if (src->getTechnique_common()->getAccessor()->getStride() != 3) - { - llerrs << "Vertex array stride MUST be three." << llendl; - } - - domListOfFloats& v = src->getFloat_array()->getValue(); - - LLVector4a min; - min.set(v[min_idx], v[min_idx+1], v[min_idx+2]); - LLVector4a max = min; - - for (U32 j = min_idx; j <= max_idx; ++j) - { //copy vertex array - face.mPositions[j-min_idx].set(v[j*3+0], v[j*3+1], v[j*3+2]); - update_min_max(min, max, face.mPositions[j-min_idx]); - } - - face.mExtents[0] = min; - face.mExtents[1] = max; - } - } - } - - if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[j]->getSemantic()) == 0) - { - //found normal array for this triangle list - const domURIFragmentType& uri = inputs[j]->getSource(); - daeElementRef elem = uri.getElement(); - domSource* src = (domSource*) elem.cast(); - domListOfFloats& n = src->getFloat_array()->getValue(); - - for (U32 j = min_idx; j <= max_idx; ++j) - { - LLVector4a* norm = (LLVector4a*) face.mNormals + (j-min_idx); - norm->set(n[j*3+0], n[j*3+1], n[j*3+2]); - norm->normalize3(); - } - } - else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[j]->getSemantic()) == 0) - { //found texCoords - const domURIFragmentType& uri = inputs[j]->getSource(); - daeElementRef elem = uri.getElement(); - domSource* src = (domSource*) elem.cast(); - domListOfFloats& u = src->getFloat_array()->getValue(); - - for (U32 j = min_idx; j <= max_idx; ++j) - { - face.mTexCoords[j-min_idx].setVec(u[j*2+0], u[j*2+1]); - } - } - } -} +#if MESH_IMPORT bool get_dom_sources(const domInputLocalOffset_Array& inputs, S32& pos_offset, S32& tc_offset, S32& norm_offset, S32 &idx_stride, domSource* &pos_source, domSource* &tc_source, domSource* &norm_source) @@ -329,6 +260,19 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector& fa { face_list.push_back(face); face_list.rbegin()->fillFromLegacyData(verts, indices); + LLVolumeFace& new_face = *face_list.rbegin(); + if (!norm_source) + { + ll_aligned_free_16(new_face.mNormals); + new_face.mNormals = NULL; + } + + if (!tc_source) + { + ll_aligned_free_16(new_face.mTexCoords); + new_face.mTexCoords = NULL; + } + face = LLVolumeFace(); point_map.clear(); } @@ -348,6 +292,18 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector& fa face_list.push_back(face); face_list.rbegin()->fillFromLegacyData(verts, indices); + LLVolumeFace& new_face = *face_list.rbegin(); + if (!norm_source) + { + ll_aligned_free_16(new_face.mNormals); + new_face.mNormals = NULL; + } + + if (!tc_source) + { + ll_aligned_free_16(new_face.mTexCoords); + new_face.mTexCoords = NULL; + } } return LLModel::NO_ERRORS ; @@ -524,6 +480,19 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector& fac { face_list.push_back(face); face_list.rbegin()->fillFromLegacyData(verts, indices); + LLVolumeFace& new_face = *face_list.rbegin(); + if (!norm_source) + { + ll_aligned_free_16(new_face.mNormals); + new_face.mNormals = NULL; + } + + if (!tc_source) + { + ll_aligned_free_16(new_face.mTexCoords); + new_face.mTexCoords = NULL; + } + face = LLVolumeFace(); verts.clear(); indices.clear(); @@ -544,6 +513,19 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector& fac materials.push_back(material); face_list.push_back(face); face_list.rbegin()->fillFromLegacyData(verts, indices); + + LLVolumeFace& new_face = *face_list.rbegin(); + if (!norm_source) + { + ll_aligned_free_16(new_face.mNormals); + new_face.mNormals = NULL; + } + + if (!tc_source) + { + ll_aligned_free_16(new_face.mTexCoords); + new_face.mTexCoords = NULL; + } } return LLModel::NO_ERRORS ; @@ -733,6 +715,19 @@ LLModel::EModelStatus load_face_from_dom_polygons(std::vector& fac materials.push_back(material); face_list.push_back(face); face_list.rbegin()->fillFromLegacyData(new_verts, indices); + + LLVolumeFace& new_face = *face_list.rbegin(); + if (!n) + { + ll_aligned_free_16(new_face.mNormals); + new_face.mNormals = NULL; + } + + if (!t) + { + ll_aligned_free_16(new_face.mTexCoords); + new_face.mTexCoords = NULL; + } } return LLModel::NO_ERRORS ; @@ -817,9 +812,9 @@ BOOL LLModel::createVolumeFacesFromDomMesh(domMesh* mesh) if (getNumVolumeFaces() > 0) { - optimizeVolumeFaces(); normalizeVolumeFaces(); - + optimizeVolumeFaces(); + if (getNumVolumeFaces() > 0) { return TRUE; @@ -833,6 +828,7 @@ BOOL LLModel::createVolumeFacesFromDomMesh(domMesh* mesh) return FALSE; } +#endif //MESH_IMPORT void LLModel::offsetMesh( const LLVector3& pivotPoint ) { @@ -844,7 +840,7 @@ void LLModel::offsetMesh( const LLVector3& pivotPoint ) LLVolumeFace& face = *currentFaceIt; LLVector4a *pos = (LLVector4a*) face.mPositions; - for (U32 i=0; i::iterator iter = mVolumeFaces.begin(); iter != mVolumeFaces.end(); ) + for (U32 i = 0; i < (U32)getNumVolumeFaces(); ++i) { - std::vector::iterator cur_iter = iter++; - LLVolumeFace& face = *cur_iter; - - for (S32 i = 0; i < (S32) face.mNumIndices; i += 3) - { //remove zero area triangles - U16 i0 = face.mIndices[i+0]; - U16 i1 = face.mIndices[i+1]; - U16 i2 = face.mIndices[i+2]; - - if (i0 == i1 || - i1 == i2 || - i0 == i2) - { //duplicate index in triangle, remove triangle - face.mIndices.erase(face.mIndices.begin()+i, face.mIndices.begin()+i+3); - i -= 3; - } - else - { - LLVolumeFace::VertexData& v0 = face.mVertices[i0]; - LLVolumeFace::VertexData& v1 = face.mVertices[i1]; - LLVolumeFace::VertexData& v2 = face.mVertices[i2]; - - if (v0.mPosition == v1.mPosition || - v1.mPosition == v2.mPosition || - v2.mPosition == v0.mPosition) - { //zero area triangle, delete - face.mIndices.erase(face.mIndices.begin()+i, face.mIndices.begin()+i+3); - i-=3; - } - } - } - - //remove unreference vertices - std::vector ref; - ref.resize(face.mNumVertices); - - for (U32 i = 0; i < ref.size(); ++i) - { - ref[i] = false; - } - - for (U32 i = 0; i < face.mNumIndices; ++i) - { - ref[face.mIndices[i]] = true; - } - - U32 unref_count = 0; - for (U32 i = 0; i < ref.size(); ++i) - { - if (!ref[i]) - { - //vertex is unreferenced - face.mVertices.erase(face.mVertices.begin()+(i-unref_count)); - U16 idx = (U16) (i-unref_count); - - for (U32 j = 0; j < face.mNumIndices; ++j) - { //decrement every index array value greater than idx - if (face.mIndices[j] > idx) - { - --face.mIndices[j]; - } - } - ++unref_count; - } - } - - if (face.mVertices.empty() || face.mIndices.empty()) - { //face is empty, remove it - iter = mVolumeFaces.erase(cur_iter); - } + mVolumeFaces[i].optimize(); } -#endif } // Shrink the model to fit @@ -962,6 +887,25 @@ void LLModel::normalizeVolumeFaces() update_min_max(min, max, face.mExtents[0]); update_min_max(min, max, face.mExtents[1]); + + if (face.mTexCoords) + { + LLVector2& min_tc = face.mTexCoordExtents[0]; + LLVector2& max_tc = face.mTexCoordExtents[1]; + + min_tc = face.mTexCoords[0]; + max_tc = face.mTexCoords[0]; + + for (U32 j = 1; j < (U32)face.mNumVertices; ++j) + { + update_min_max(min_tc, max_tc, face.mTexCoords[j]); + } + } + else + { + face.mTexCoordExtents[0].set(0,0); + face.mTexCoordExtents[1].set(1,1); + } } // Now that we have the extents of the model @@ -1025,12 +969,15 @@ void LLModel::normalizeVolumeFaces() LLVector4a* pos = (LLVector4a*) face.mPositions; LLVector4a* norm = (LLVector4a*) face.mNormals; - for (U32 j = 0; j < face.mNumVertices; ++j) + for (U32 j = 0; j < (U32)face.mNumVertices; ++j) { pos[j].add(trans); pos[j].mul(scale); - norm[j].mul(inv_scale); - norm[j].normalize3(); + if (norm && !norm[j].equals3(LLVector4a::getZero())) + { + norm[j].mul(inv_scale); + norm[j].normalize3(); + } } } @@ -1073,8 +1020,26 @@ void LLModel::setVolumeFaceData( face.resizeIndices(num_indices); LLVector4a::memcpyNonAliased16((F32*) face.mPositions, (F32*) pos.get(), num_verts*4*sizeof(F32)); - LLVector4a::memcpyNonAliased16((F32*) face.mNormals, (F32*) norm.get(), num_verts*4*sizeof(F32)); - LLVector4a::memcpyNonAliased16((F32*) face.mTexCoords, (F32*) tc.get(), num_verts*2*sizeof(F32)); + if (norm.get()) + { + LLVector4a::memcpyNonAliased16((F32*) face.mNormals, (F32*) norm.get(), num_verts*4*sizeof(F32)); + } + else + { + ll_aligned_free_16(face.mNormals); + face.mNormals = NULL; + } + + if (tc.get()) + { + LLVector4a::memcpyNonAliased16((F32*) face.mTexCoords, (F32*) tc.get(), num_verts*2*sizeof(F32)); + } + else + { + ll_aligned_free_16(face.mTexCoords); + face.mTexCoords = NULL; + } + U32 size = (num_indices*2+0xF)&~0xF; LLVector4a::memcpyNonAliased16((F32*) face.mIndices, (F32*) ind.get(), size); } @@ -1160,7 +1125,7 @@ void LLModel::generateNormals(F32 angle_cutoff) faceted.resizeVertices(vol_face.mNumIndices); faceted.resizeIndices(vol_face.mNumIndices); //bake out triangles into temporary face, clearing texture coordinates - for (U32 i = 0; i < vol_face.mNumIndices; ++i) + for (U32 i = 0; i < (U32)vol_face.mNumIndices; ++i) { U32 idx = vol_face.mIndices[i]; @@ -1170,7 +1135,7 @@ void LLModel::generateNormals(F32 angle_cutoff) } //generate normals for temporary face - for (U32 i = 0; i < faceted.mNumIndices; i += 3) + for (U32 i = 0; i < (U32)faceted.mNumIndices; i += 3) { //for each triangle U16 i0 = faceted.mIndices[i+0]; U16 i1 = faceted.mIndices[i+1]; @@ -1199,12 +1164,12 @@ void LLModel::generateNormals(F32 angle_cutoff) //generate normals for welded face based on new topology (step 3) - for (U32 i = 0; i < faceted.mNumVertices; i++) + for (U32 i = 0; i < (U32)faceted.mNumVertices; i++) { faceted.mNormals[i].clear(); } - for (U32 i = 0; i < faceted.mNumIndices; i += 3) + for (U32 i = 0; i < (U32)faceted.mNumIndices; i += 3) { //for each triangle U16 i0 = faceted.mIndices[i+0]; U16 i1 = faceted.mIndices[i+1]; @@ -1233,7 +1198,7 @@ void LLModel::generateNormals(F32 angle_cutoff) //normalize normals and build point map LLVolumeFace::VertexMapData::PointMap point_map; - for (U32 i = 0; i < faceted.mNumVertices; ++i) + for (U32 i = 0; i < (U32)faceted.mNumVertices; ++i) { faceted.mNormals[i].normalize3(); @@ -1251,18 +1216,31 @@ void LLModel::generateNormals(F32 angle_cutoff) new_face.resizeIndices(vol_face.mNumIndices); new_face.resizeVertices(vol_face.mNumIndices); - for (U32 i = 0; i < vol_face.mNumIndices; ++i) + for (U32 i = 0; i < (U32)vol_face.mNumIndices; ++i) { U32 idx = vol_face.mIndices[i]; LLVolumeFace::VertexData v; new_face.mPositions[i] = vol_face.mPositions[idx]; new_face.mNormals[i].clear(); - new_face.mTexCoords[i] = vol_face.mTexCoords[idx]; new_face.mIndices[i] = i; } + if (vol_face.mTexCoords) + { + for (U32 i = 0; i < (U32)vol_face.mNumIndices; i++) + { + U32 idx = vol_face.mIndices[i]; + new_face.mTexCoords[i] = vol_face.mTexCoords[idx]; + } + } + else + { + ll_aligned_free_16(new_face.mTexCoords); + new_face.mTexCoords = NULL; + } + //generate normals for new face - for (U32 i = 0; i < new_face.mNumIndices; i += 3) + for (U32 i = 0; i < (U32)new_face.mNumIndices; i += 3) { //for each triangle U16 i0 = new_face.mIndices[i+0]; U16 i1 = new_face.mIndices[i+1]; @@ -1287,7 +1265,7 @@ void LLModel::generateNormals(F32 angle_cutoff) } //swap out normals in new_face with best match from point map (step 5) - for (U32 i = 0; i < new_face.mNumVertices; ++i) + for (U32 i = 0; i < (U32)new_face.mNumVertices; ++i) { //LLVolumeFace::VertexData v = new_face.mVertices[i]; @@ -1320,6 +1298,8 @@ void LLModel::generateNormals(F32 angle_cutoff) } } +#if MESH_IMPORT + //static std::string LLModel::getElementLabel(daeElement *element) { // try to get a decent label for this element @@ -1375,6 +1355,7 @@ LLModel* LLModel::loadModelFromDomMesh(domMesh *mesh) ret->mLabel = getElementLabel(mesh); return ret; } +#endif MESH_IMPORT std::string LLModel::getName() const { @@ -1395,7 +1376,8 @@ LLSD LLModel::writeModel( const LLModel::Decomposition& decomp, BOOL upload_skin, BOOL upload_joints, - BOOL nowrite) + BOOL nowrite, + BOOL as_slm) { LLSD mdl; @@ -1419,12 +1401,20 @@ LLSD LLModel::writeModel( !decomp.mHull.empty()) { mdl["physics_convex"] = decomp.asLLSD(); - if (!decomp.mHull.empty()) - { //convex decomposition exists, physics mesh will not be used + if (!decomp.mHull.empty() && !as_slm) + { //convex decomposition exists, physics mesh will not be used (unless this is an slm file) model[LLModel::LOD_PHYSICS] = NULL; } } + if (as_slm) + { //save material list names + for (U32 i = 0; i < high->mMaterialList.size(); ++i) + { + mdl["material_list"][i] = high->mMaterialList[i]; + } + } + for (U32 idx = 0; idx < MODEL_NAMES_LENGTH; ++idx) { if (model[idx] && model[idx]->getNumVolumeFaces() > 0) @@ -1436,7 +1426,7 @@ LLSD LLModel::writeModel( for (S32 i = 0; i < model[idx]->getNumVolumeFaces(); ++i) { //for each face const LLVolumeFace& face = model[idx]->getVolumeFace(i); - for (U32 j = 0; j < face.mNumVertices; ++j) + for (U32 j = 0; j < (U32)face.mNumVertices; ++j) { update_min_max(min_pos, max_pos, face.mPositions[j].getF32ptr()); } @@ -1462,24 +1452,30 @@ LLSD LLModel::writeModel( U32 tc_idx = 0; LLVector2* ftc = (LLVector2*) face.mTexCoords; - LLVector2 min_tc = ftc[0]; - LLVector2 max_tc = min_tc; - - //get texture coordinate domain - for (U32 j = 0; j < face.mNumVertices; ++j) + LLVector2 min_tc; + LLVector2 max_tc; + + if (ftc) { - update_min_max(min_tc, max_tc, ftc[j]); + min_tc = ftc[0]; + max_tc = min_tc; + + //get texture coordinate domain + for (U32 j = 0; j < (U32)face.mNumVertices; ++j) + { + update_min_max(min_tc, max_tc, ftc[j]); + } } + LLVector2 tc_range = max_tc - min_tc; - for (U32 j = 0; j < face.mNumVertices; ++j) + for (U32 j = 0; j < (U32)face.mNumVertices; ++j) { //for each vert F32* pos = face.mPositions[j].getF32ptr(); - F32* norm = face.mNormals[j].getF32ptr(); - - //position + normal + + //position for (U32 k = 0; k < 3; ++k) { //for each component //convert to 16-bit normalized across domain @@ -1489,33 +1485,44 @@ LLSD LLModel::writeModel( //write to binary buffer verts[vert_idx++] = buff[0]; verts[vert_idx++] = buff[1]; - - //convert to 16-bit normalized - val = (U16) ((norm[k]+1.f)*0.5f*65535); - - //write to binary buffer - normals[norm_idx++] = buff[0]; - normals[norm_idx++] = buff[1]; } + if (face.mNormals) + { //normals + F32* norm = face.mNormals[j].getF32ptr(); + + for (U32 k = 0; k < 3; ++k) + { //for each component + //convert to 16-bit normalized + U16 val = (U16) ((norm[k]+1.f)*0.5f*65535); + U8* buff = (U8*) &val; + + //write to binary buffer + normals[norm_idx++] = buff[0]; + normals[norm_idx++] = buff[1]; + } + } + F32* src_tc = (F32*) face.mTexCoords[j].mV; //texcoord - for (U32 k = 0; k < 2; ++k) - { //for each component - //convert to 16-bit normalized - U16 val = (U16) ((src_tc[k]-min_tc.mV[k])/tc_range.mV[k]*65535); + if (face.mTexCoords) + { + for (U32 k = 0; k < 2; ++k) + { //for each component + //convert to 16-bit normalized + U16 val = (U16) ((src_tc[k]-min_tc.mV[k])/tc_range.mV[k]*65535); - U8* buff = (U8*) &val; - //write to binary buffer - tc[tc_idx++] = buff[0]; - tc[tc_idx++] = buff[1]; + U8* buff = (U8*) &val; + //write to binary buffer + tc[tc_idx++] = buff[0]; + tc[tc_idx++] = buff[1]; + } } - } U32 idx_idx = 0; - for (U32 j = 0; j < face.mNumIndices; ++j) + for (U32 j = 0; j < (U32)face.mNumIndices; ++j) { U8* buff = (U8*) &(face.mIndices[j]); indices[idx_idx++] = buff[0]; @@ -1525,12 +1532,19 @@ LLSD LLModel::writeModel( //write out face data mdl[model_names[idx]][i]["PositionDomain"]["Min"] = min_pos.getValue(); mdl[model_names[idx]][i]["PositionDomain"]["Max"] = max_pos.getValue(); - mdl[model_names[idx]][i]["TexCoord0Domain"]["Min"] = min_tc.getValue(); - mdl[model_names[idx]][i]["TexCoord0Domain"]["Max"] = max_tc.getValue(); - mdl[model_names[idx]][i]["Position"] = verts; - mdl[model_names[idx]][i]["Normal"] = normals; - mdl[model_names[idx]][i]["TexCoord0"] = tc; + + if (face.mNormals) + { + mdl[model_names[idx]][i]["Normal"] = normals; + } + + if (face.mTexCoords) + { + mdl[model_names[idx]][i]["TexCoord0Domain"]["Min"] = min_tc.getValue(); + mdl[model_names[idx]][i]["TexCoord0Domain"]["Max"] = max_tc.getValue(); + mdl[model_names[idx]][i]["TexCoord0"] = tc; + } mdl[model_names[idx]][i]["TriangleList"] = indices; if (skinning) @@ -1544,7 +1558,7 @@ LLSD LLModel::writeModel( std::stringstream ostr; - for (U32 j = 0; j < face.mNumVertices; ++j) + for (U32 j = 0; j < (U32)face.mNumVertices; ++j) { LLVector3 pos(face.mPositions[j].getF32ptr()); @@ -1588,10 +1602,10 @@ LLSD LLModel::writeModel( } } - return writeModelToStream(ostr, mdl, nowrite); + return writeModelToStream(ostr, mdl, nowrite, as_slm); } -LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite) +LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite, BOOL as_slm) { U32 bytes = 0; @@ -1599,6 +1613,11 @@ LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite) LLSD header; + if (as_slm && mdl.has("material_list")) + { //save material binding names to header + header["material_list"] = mdl["material_list"]; + } + std::string skin; if (mdl.has("skin")) @@ -1689,13 +1708,13 @@ LLModel::weight_list& LLModel::getJointInfluences(const LLVector3& pos) } else { //no exact match found, get closest point - const F32 epsilon = 2.f/65536; + const F32 epsilon = 1e-5f; weight_map::iterator iter_up = mSkinWeights.lower_bound(pos); weight_map::iterator iter_down = ++iter_up; weight_map::iterator best = iter_up; - F32 min_dist = (iter->first - pos).magVecSquared(); + F32 min_dist = (iter->first - pos).magVec(); bool done = false; while (!done) @@ -1706,7 +1725,7 @@ LLModel::weight_list& LLModel::getJointInfluences(const LLVector3& pos) if (iter_up != mSkinWeights.end() && ++iter_up != mSkinWeights.end()) { done = false; - F32 dist = (iter_up->first - pos).magVecSquared(); + F32 dist = (iter_up->first - pos).magVec(); if (dist < epsilon) { @@ -1724,7 +1743,7 @@ LLModel::weight_list& LLModel::getJointInfluences(const LLVector3& pos) { done = false; - F32 dist = (iter_down->first - pos).magVecSquared(); + F32 dist = (iter_down->first - pos).magVec(); if (dist < epsilon) { @@ -1792,6 +1811,15 @@ bool LLModel::loadModel(std::istream& is) } } + if (header.has("material_list")) + { //load material list names + mMaterialList.clear(); + for (U32 i = 0; i < (U32)header["material_list"].size(); ++i) + { + mMaterialList.push_back(header["material_list"][i].asString()); + } + } + std::string nm[] = { "lowest_lod", @@ -1808,6 +1836,7 @@ bool LLModel::loadModel(std::istream& is) if (header[nm[lod]]["offset"].asInteger() == -1 || header[nm[lod]]["size"].asInteger() == 0 ) { //cannot load requested LOD + llwarns << "LoD data is invalid!" << llendl; return false; } @@ -1821,7 +1850,7 @@ bool LLModel::loadModel(std::istream& is) is.seekg(cur_pos); } - if (lod == LLModel::LOD_PHYSICS) + if (lod == LLModel::LOD_HIGH || lod == LLModel::LOD_PHYSICS) { std::ios::pos_type cur_pos = is.tellg(); loadDecomposition(header, is); @@ -1868,11 +1897,66 @@ bool LLModel::loadModel(std::istream& is) } return true; } + else + { + llwarns << "unpackVolumeFaces failed!" << llendl; + } return false; } +void LLModel::matchMaterialOrder(LLModel* ref) +{ + llassert(ref->mMaterialList.size() == mMaterialList.size()); + + std::map index_map; + + //build a map of material slot names to face indexes + bool reorder = false; + std::set base_mat; + std::set cur_mat; + + for (U32 i = 0; i < mMaterialList.size(); i++) + { + index_map[ref->mMaterialList[i]] = i; + if (!reorder) + { //if any material name does not match reference, we need to reorder + reorder = ref->mMaterialList[i] != mMaterialList[i]; + } + base_mat.insert(ref->mMaterialList[i]); + cur_mat.insert(mMaterialList[i]); + } + + + if (reorder && + base_mat == cur_mat) //don't reorder if material name sets don't match + { + std::vector new_face_list; + new_face_list.resize(mVolumeFaces.size()); + + std::vector new_material_list; + new_material_list.resize(mVolumeFaces.size()); + + //rebuild face list so materials have the same order + //as the reference model + for (U32 i = 0; i < mMaterialList.size(); ++i) + { + U32 ref_idx = index_map[mMaterialList[i]]; + new_face_list[ref_idx] = mVolumeFaces[i]; + + new_material_list[ref_idx] = mMaterialList[i]; + } + + llassert(new_material_list == ref->mMaterialList); + + mVolumeFaces = new_face_list; + } + + //override material list with reference model ordering + mMaterialList = ref->mMaterialList; +} + bool LLModel::loadSkinInfo(LLSD& header, std::istream &is) { @@ -1926,7 +2010,7 @@ void LLMeshSkinInfo::fromLLSD(LLSD& skin) { if (skin.has("joint_names")) { - for (U32 i = 0; i < skin["joint_names"].size(); ++i) + for (U32 i = 0; i < (U32)skin["joint_names"].size(); ++i) { mJointNames.push_back(skin["joint_names"][i]); } @@ -1934,7 +2018,7 @@ void LLMeshSkinInfo::fromLLSD(LLSD& skin) if (skin.has("inverse_bind_matrix")) { - for (U32 i = 0; i < skin["inverse_bind_matrix"].size(); ++i) + for (U32 i = 0; i < (U32)skin["inverse_bind_matrix"].size(); ++i) { LLMatrix4 mat; for (U32 j = 0; j < 4; j++) @@ -1962,7 +2046,7 @@ void LLMeshSkinInfo::fromLLSD(LLSD& skin) if (skin.has("alt_inverse_bind_matrix")) { - for (U32 i = 0; i < skin["alt_inverse_bind_matrix"].size(); ++i) + for (U32 i = 0; i < (U32)skin["alt_inverse_bind_matrix"].size(); ++i) { LLMatrix4 mat; for (U32 j = 0; j < 4; j++) @@ -2034,7 +2118,7 @@ LLModel::Decomposition::Decomposition(LLSD& data) void LLModel::Decomposition::fromLLSD(LLSD& decomp) { - if (decomp.has("HullList")) + if (decomp.has("HullList") && decomp.has("Positions")) { // updated for const-correctness. gcc is picky about this type of thing - Nyx const LLSD::Binary& hulls = decomp["HullList"].asBinary(); @@ -2190,6 +2274,8 @@ LLSD LLModel::Decomposition::asLLSD() const ret["Min"] = min.getValue(); ret["Max"] = max.getValue(); + LLVector3 range = max-min; + if (!hulls.empty()) { ret["HullList"] = hulls; @@ -2199,10 +2285,6 @@ LLSD LLModel::Decomposition::asLLSD() const { LLSD::Binary p(total*3*2); - LLVector3 min(-0.5f, -0.5f, -0.5f); - LLVector3 max(0.5f, 0.5f, 0.5f); - LLVector3 range = max-min; - U32 vert_idx = 0; for (U32 i = 0; i < mHull.size(); ++i) @@ -2214,12 +2296,10 @@ LLSD LLModel::Decomposition::asLLSD() const for (U32 j = 0; j < mHull[i].size(); ++j) { U64 test = 0; + const F32* src = mHull[i][j].mV; + for (U32 k = 0; k < 3; k++) { - F32* src = (F32*) (mHull[i][j].mV); - - llassert(src[k] <= 0.501f && src[k] >= -0.501f); - //convert to 16-bit normalized across domain U16 val = (U16) (((src[k]-min.mV[k])/range.mV[k])*65535); @@ -2258,19 +2338,15 @@ LLSD LLModel::Decomposition::asLLSD() const { LLSD::Binary p(mBaseHull.size()*3*2); - LLVector3 min(-0.5f, -0.5f, -0.5f); - LLVector3 max(0.5f, 0.5f, 0.5f); - LLVector3 range = max-min; - U32 vert_idx = 0; for (U32 j = 0; j < mBaseHull.size(); ++j) { + const F32* v = mBaseHull[j].mV; + for (U32 k = 0; k < 3; k++) { - llassert(mBaseHull[j].mV[k] <= 0.51f && mBaseHull[j].mV[k] >= -0.51f); - //convert to 16-bit normalized across domain - U16 val = (U16) (((mBaseHull[j].mV[k]-min.mV[k])/range.mV[k])*65535); + U16 val = (U16) (((v[k]-min.mV[k])/range.mV[k])*65535); U8* buff = (U8*) &val; //write to binary buffer @@ -2315,4 +2391,4 @@ void LLModel::Decomposition::merge(const LLModel::Decomposition* rhs) mPhysicsShapeMesh = rhs->mPhysicsShapeMesh; } } - +#endif //MESH_ENABLED diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h index cd9f76fcb..fe37d3420 100644 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -32,8 +32,10 @@ #include "v4math.h" #include "m4math.h" +#if MESH_IMPORT class daeElement; class domMesh; +#endif //MESH_IMPORT #define MAX_MODEL_FACES 8 @@ -137,16 +139,20 @@ public: const LLModel::Decomposition& decomp, BOOL upload_skin, BOOL upload_joints, - BOOL nowrite = FALSE); + BOOL nowrite = FALSE, + BOOL as_slm = FALSE); static LLSD writeModelToStream( std::ostream& ostr, LLSD& mdl, - BOOL nowrite = FALSE); + BOOL nowrite = FALSE, BOOL as_slm = FALSE); +#if MESH_IMPORT static LLModel* loadModelFromDomMesh(domMesh* mesh); static std::string getElementLabel(daeElement* element); +#endif //MESH_IMPORT std::string getName() const; + std::string getMetric() const {return mMetric;} EModelStatus getStatus() const {return mStatus;} static std::string getStatusString(U32 status) ; @@ -171,6 +177,11 @@ public: void optimizeVolumeFaces(); void offsetMesh( const LLVector3& pivotPoint ); void getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out); + + //reorder face list based on mMaterialList in this and reference so + //order matches that of reference (material ordering touchup) + void matchMaterialOrder(LLModel* reference); + std::vector mMaterialList; //data used for skin weights @@ -211,6 +222,22 @@ public: } }; + + struct JointPositionalCompare + { + //Are the doubles the same w/in epsilon specified tolerance + bool areEqual( double a, double b ) + { + const float epsilon = 1e-5f; + return (abs((int)(a - b)) < epsilon) && (a < b); + } + //Make sure that we return false for any values that are within the tolerance for equivalence + bool operator() ( const LLVector3& a, const LLVector3& b ) + { + return ( areEqual( a[0],b[0]) && areEqual( a[1],b[1] ) && areEqual( a[2],b[2]) ) ? false : true; + } + }; + //copy of position array for this model -- mPosition[idx].mV[X,Y,Z] std::vector mPosition; @@ -228,6 +255,8 @@ public: std::string mRequestedLabel; // name requested in UI, if any. std::string mLabel; // name computed from dae. + std::string mMetric; // user-supplied metric data for upload + LLVector3 mNormalizedScale; LLVector3 mNormalizedTranslation; @@ -250,8 +279,10 @@ public: EModelStatus mStatus ; protected: +#if MESH_IMPORT void addVolumeFacesFromDomMesh(domMesh* mesh); virtual BOOL createVolumeFacesFromDomMesh(domMesh *mesh); +#endif //MESH_IMPORT }; #endif //LL_LLMODEL_H diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h index c9b035db3..c255655de 100644 --- a/indra/llprimitive/llprimitive.h +++ b/indra/llprimitive/llprimitive.h @@ -109,6 +109,10 @@ public: PARAMS_LIGHT = 0x20, PARAMS_SCULPT = 0x30, PARAMS_LIGHT_IMAGE = 0x40, +#if MESH_ENABLED + PARAMS_RESERVED = 0x50, // Used on server-side + PARAMS_MESH = 0x60, +#endif MESH_ENABLED }; public: diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index 5eb5ffd10..ec306ba26 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -63,6 +63,9 @@ LLShaderFeatures::LLShaderFeatures() : calculatesLighting(false), isShiny(false), isFullbright(false), hasWaterFog(false), hasTransport(false), hasSkinning(false), hasAtmospherics(false), isSpecular(false), hasGamma(false), hasLighting(false), calculatesAtmospherics(false) +#if MESH_ENABLED +, hasObjectSkinning(false) +#endif //MESH_ENABLED { } diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index 3c47e7504..3f8a6cb96 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -48,6 +48,9 @@ public: bool hasWaterFog; // implies no gamma bool hasTransport; // implies no lighting (it's possible to have neither though) bool hasSkinning; +#if MESH_ENABLED + bool hasObjectSkinning; +#endif //MESH_ENABLED bool hasAtmospherics; bool hasGamma; diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 2dc3dae91..255643dc8 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -152,6 +152,16 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader) return FALSE; } } + +#if MESH_ENABLED + if (features->hasObjectSkinning) + { + if (!shader->attachObject("avatar/objectSkinV.glsl")) + { + return FALSE; + } + } +#endif //MESH_ENABLED /////////////////////////////////////// // Attach Fragment Shader Features Next diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 21f321452..7a5c71729 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -293,6 +293,7 @@ set(viewer_SOURCE_FILES llmediaremotectrl.cpp llmemoryview.cpp llmenucommands.cpp + llmeshrepository.cpp llmimetypes.cpp llmorphview.cpp llmoveview.cpp @@ -771,6 +772,7 @@ set(viewer_HEADER_FILES llmediaremotectrl.h llmemoryview.h llmenucommands.h + llmeshrepository.h llmimetypes.h llmorphview.h llmoveview.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 31440e213..ef738031e 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7885,6 +7885,51 @@ Value 410 + MeshEnabled + + Comment + Expose UI for mesh functionality (may require restart to take effect). + Persist + 1 + Type + Boolean + Value + 1 + + MeshImportUseSLM + + Comment + Use cached copy of last upload for a dae if available instead of loading dae file from scratch. + Persist + 1 + Type + Boolean + Value + 0 + + MeshUploadLogXML + + Comment + Verbose XML logging on mesh upload + Persist + 1 + Type + Boolean + Value + 0 + + MeshUploadFakeErrors + + Comment + Force upload errors (for testing) + Persist + 1 + Type + S32 + Value + 0 + + MigrateCacheDirectory Comment @@ -11037,6 +11082,39 @@ Value 1.0 + MeshStreamingCostScaler + + Comment + DEBUG + Persist + 1 + Type + F32 + Value + 2.0 + + MeshThreadCount + + Comment + Number of threads to use for loading meshes. + Persist + 1 + Type + U32 + Value + 8 + + MeshMaxConcurrentRequests + + Comment + Number of threads to use for loading meshes. + Persist + 1 + Type + U32 + Value + 32 + RunBtnState Comment diff --git a/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl b/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl new file mode 100644 index 000000000..ef823c28b --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl @@ -0,0 +1,30 @@ +/** + * @file objectSkinV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +attribute vec4 object_weight; + +uniform mat4 matrixPalette[32]; + +mat4 getObjectSkinnedTransform() +{ + int i; + + vec4 w = fract(object_weight); + vec4 index = floor(object_weight); + + float scale = 1.0/(w.x+w.y+w.z+w.w); + w *= scale; + + mat4 mat = matrixPalette[int(index.x)]*w.x; + mat += matrixPalette[int(index.y)]*w.y; + mat += matrixPalette[int(index.z)]*w.z; + mat += matrixPalette[int(index.w)]*w.w; + + return mat; +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl new file mode 100644 index 000000000..65d920998 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl @@ -0,0 +1,112 @@ +/** + * @file alphaSkinnedV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol); +mat4 getObjectSkinnedTransform(); +void calcAtmospherics(vec3 inPositionEye); + +float calcDirectionalLight(vec3 n, vec3 l); + +vec3 atmosAmbient(vec3 light); +vec3 atmosAffectDirectionalLight(float lightIntensity); +vec3 scaleDownLight(vec3 light); +vec3 scaleUpLight(vec3 light); + +varying vec3 vary_position; +varying vec3 vary_ambient; +varying vec3 vary_directional; +varying vec3 vary_normal; +varying vec3 vary_fragcoord; +varying vec3 vary_pointlight_col; + +uniform float near_clip; + +float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float fa, float is_pointlight) +{ + //get light vector + vec3 lv = lp.xyz-v; + + //get distance + float d = length(lv); + + float da = 0.0; + + if (d > 0.0 && la > 0.0 && fa > 0.0) + { + //normalize light vector + lv *= 1.0/d; + + //distance attenuation + float dist2 = d*d/(la*la); + da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0); + + // spotlight coefficient. + float spot = max(dot(-ln, lv), is_pointlight); + da *= spot*spot; // GL_SPOT_EXPONENT=2 + + //angular attenuation + da *= calcDirectionalLight(n, lv); + } + + return da; +} + +void main() +{ + gl_TexCoord[0] = gl_MultiTexCoord0; + + vec4 pos; + vec3 norm; + + mat4 trans = getObjectSkinnedTransform(); + trans = gl_ModelViewMatrix * trans; + + pos = trans * gl_Vertex; + + norm = gl_Vertex.xyz + gl_Normal.xyz; + norm = normalize(( trans*vec4(norm, 1.0) ).xyz-pos.xyz); + + vec4 frag_pos = gl_ProjectionMatrix * pos; + gl_Position = frag_pos; + + vary_position = pos.xyz; + vary_normal = norm; + + calcAtmospherics(pos.xyz); + + vec4 col = vec4(0.0, 0.0, 0.0, gl_Color.a); + + // Collect normal lights + col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].quadraticAttenuation, gl_LightSource[2].specular.a); + col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].quadraticAttenuation ,gl_LightSource[3].specular.a); + col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].quadraticAttenuation, gl_LightSource[4].specular.a); + col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].quadraticAttenuation, gl_LightSource[5].specular.a); + col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].quadraticAttenuation, gl_LightSource[6].specular.a); + col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].quadraticAttenuation, gl_LightSource[7].specular.a); + + vary_pointlight_col = col.rgb*gl_Color.rgb; + + col.rgb = vec3(0,0,0); + + // Add windlight lights + col.rgb = atmosAmbient(vec3(0.)); + + vary_ambient = col.rgb*gl_Color.rgb; + vary_directional = gl_Color.rgb*atmosAffectDirectionalLight(max(calcDirectionalLight(norm, gl_LightSource[0].position.xyz), (1.0-gl_Color.a)*(1.0-gl_Color.a))); + + col.rgb = min(col.rgb*gl_Color.rgb, 1.0); + + gl_FrontColor = col; + + gl_FogFragCoord = pos.z; + + vary_fragcoord.xyz = frag_pos.xyz + vec3(0,0,near_clip); +} + + diff --git a/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowF.glsl b/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowF.glsl new file mode 100644 index 000000000..164322c3a --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowF.glsl @@ -0,0 +1,18 @@ +/** + * @file avatarShadowF.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +uniform sampler2D diffuseMap; + + +void main() +{ + //gl_FragColor = vec4(1,1,1,gl_Color.a * texture2D(diffuseMap, gl_TexCoord[0].xy).a); + gl_FragColor = vec4(1,1,1,1); +} + diff --git a/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowV.glsl b/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowV.glsl new file mode 100644 index 000000000..5ae41cb73 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowV.glsl @@ -0,0 +1,27 @@ +/** + * @file attachmentShadowV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +mat4 getObjectSkinnedTransform(); + +void main() +{ + //transform vertex + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + + mat4 mat = getObjectSkinnedTransform(); + + mat = gl_ModelViewMatrix * mat; + vec3 pos = (mat*gl_Vertex).xyz; + + gl_FrontColor = gl_Color; + + vec4 p = gl_ProjectionMatrix * vec4(pos, 1.0); + p.z = max(p.z, -p.w+0.01); + gl_Position = p; +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl new file mode 100644 index 000000000..d884f2e4a --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl @@ -0,0 +1,37 @@ +/** + * @file bumpV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +varying vec3 vary_mat0; +varying vec3 vary_mat1; +varying vec3 vary_mat2; + +mat4 getObjectSkinnedTransform(); + +void main() +{ + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + + mat4 mat = getObjectSkinnedTransform(); + + mat = gl_ModelViewMatrix * mat; + + vec3 pos = (mat*gl_Vertex).xyz; + + + vec3 n = normalize((mat * vec4(gl_Normal.xyz+gl_Vertex.xyz, 1.0)).xyz-pos.xyz); + vec3 b = normalize((mat * vec4(gl_MultiTexCoord2.xyz+gl_Vertex.xyz, 1.0)).xyz-pos.xyz); + vec3 t = cross(b, n); + + vary_mat0 = vec3(t.x, b.x, n.x); + vary_mat1 = vec3(t.y, b.y, n.y); + vary_mat2 = vec3(t.z, b.z, n.z); + + gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0); + gl_FrontColor = gl_Color; +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseSkinnedV.glsl new file mode 100644 index 000000000..9a45c0323 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseSkinnedV.glsl @@ -0,0 +1,33 @@ +/** + * @file diffuseSkinnedV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +varying vec3 vary_normal; + +mat4 getObjectSkinnedTransform(); + +void main() +{ + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + + mat4 mat = getObjectSkinnedTransform(); + + mat = gl_ModelViewMatrix * mat; + vec3 pos = (mat*gl_Vertex).xyz; + + vec4 norm = gl_Vertex; + norm.xyz += gl_Normal.xyz; + norm.xyz = (mat*norm).xyz; + norm.xyz = normalize(norm.xyz-pos.xyz); + + vary_normal = norm.xyz; + + gl_FrontColor = gl_Color; + + gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0); +} diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightShinySkinnedV.glsl b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinySkinnedV.glsl new file mode 100644 index 000000000..f0baeeeee --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinySkinnedV.glsl @@ -0,0 +1,39 @@ +/** + * @file shinySimpleSkinnedV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +void calcAtmospherics(vec3 inPositionEye); +mat4 getObjectSkinnedTransform(); + +attribute vec4 object_weight; + +void main() +{ + mat4 mat = getObjectSkinnedTransform(); + + mat = gl_ModelViewMatrix * mat; + vec3 pos = (mat*gl_Vertex).xyz; + + vec4 norm = gl_Vertex; + norm.xyz += gl_Normal.xyz; + norm.xyz = (mat*norm).xyz; + norm.xyz = normalize(norm.xyz-pos.xyz); + + vec3 ref = reflect(pos.xyz, -norm.xyz); + + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + gl_TexCoord[1] = gl_TextureMatrix[1]*vec4(ref,1.0); + + calcAtmospherics(pos.xyz); + + gl_FrontColor = gl_Color; + + gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0); + + gl_FogFragCoord = pos.z; +} diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/objects/fullbrightSkinnedV.glsl new file mode 100644 index 000000000..02ff3cc2a --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/objects/fullbrightSkinnedV.glsl @@ -0,0 +1,37 @@ +/** + * @file fullbrightSkinnedV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +void calcAtmospherics(vec3 inPositionEye); +mat4 getObjectSkinnedTransform(); + +attribute vec4 object_weight; + +void main() +{ + //transform vertex + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + + mat4 mat = getObjectSkinnedTransform(); + + mat = gl_ModelViewMatrix * mat; + vec3 pos = (mat*gl_Vertex).xyz; + + vec4 norm = gl_Vertex; + norm.xyz += gl_Normal.xyz; + norm.xyz = (mat*norm).xyz; + norm.xyz = normalize(norm.xyz-pos.xyz); + + calcAtmospherics(pos.xyz); + + gl_FrontColor = gl_Color; + + gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0); + + gl_FogFragCoord = pos.z; +} diff --git a/indra/newview/app_settings/shaders/class1/objects/shinySimpleSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/objects/shinySimpleSkinnedV.glsl new file mode 100644 index 000000000..414664605 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/objects/shinySimpleSkinnedV.glsl @@ -0,0 +1,39 @@ +/** + * @file shinySimpleSkinnedV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol); +void calcAtmospherics(vec3 inPositionEye); +mat4 getObjectSkinnedTransform(); + +attribute vec4 object_weight; + +void main() +{ + mat4 mat = getObjectSkinnedTransform(); + + mat = gl_ModelViewMatrix * mat; + vec3 pos = (mat*gl_Vertex).xyz; + + vec4 norm = gl_Vertex; + norm.xyz += gl_Normal.xyz; + norm.xyz = (mat*norm).xyz; + norm.xyz = normalize(norm.xyz-pos.xyz); + + vec3 ref = reflect(pos.xyz, -norm.xyz); + + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + gl_TexCoord[1] = gl_TextureMatrix[1]*vec4(ref,1.0); + + calcAtmospherics(pos.xyz); + + vec4 color = calcLighting(pos.xyz, norm.xyz, gl_Color, vec4(0.)); + gl_FrontColor = color; + + gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0); +} diff --git a/indra/newview/app_settings/shaders/class1/objects/simpleSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/objects/simpleSkinnedV.glsl new file mode 100644 index 000000000..be38a14d5 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/objects/simpleSkinnedV.glsl @@ -0,0 +1,39 @@ +/** + * @file simpleSkinnedV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol); +void calcAtmospherics(vec3 inPositionEye); +mat4 getObjectSkinnedTransform(); + +attribute vec4 object_weight; + +void main() +{ + //transform vertex + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + + mat4 mat = getObjectSkinnedTransform(); + + mat = gl_ModelViewMatrix * mat; + vec3 pos = (mat*gl_Vertex).xyz; + + vec4 norm = gl_Vertex; + norm.xyz += gl_Normal.xyz; + norm.xyz = (mat*norm).xyz; + norm.xyz = normalize(norm.xyz-pos.xyz); + + calcAtmospherics(pos.xyz); + + vec4 color = calcLighting(pos.xyz, norm.xyz, gl_Color, vec4(0.)); + gl_FrontColor = color; + + gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0); + + gl_FogFragCoord = pos.z; +} diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl new file mode 100644 index 000000000..dfb36980b --- /dev/null +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl @@ -0,0 +1,115 @@ +/** + * @file alphaSkinnedV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol); +void calcAtmospherics(vec3 inPositionEye); + +float calcDirectionalLight(vec3 n, vec3 l); +mat4 getObjectSkinnedTransform(); +vec3 atmosAmbient(vec3 light); +vec3 atmosAffectDirectionalLight(float lightIntensity); +vec3 scaleDownLight(vec3 light); +vec3 scaleUpLight(vec3 light); + +varying vec3 vary_ambient; +varying vec3 vary_directional; +varying vec3 vary_fragcoord; +varying vec3 vary_position; +varying vec3 vary_pointlight_col; + +uniform float near_clip; +uniform float shadow_offset; +uniform float shadow_bias; + +float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float fa, float is_pointlight) +{ + //get light vector + vec3 lv = lp.xyz-v; + + //get distance + float d = length(lv); + + float da = 0.0; + + if (d > 0.0 && la > 0.0 && fa > 0.0) + { + //normalize light vector + lv *= 1.0/d; + + //distance attenuation + float dist2 = d*d/(la*la); + da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0); + + // spotlight coefficient. + float spot = max(dot(-ln, lv), is_pointlight); + da *= spot*spot; // GL_SPOT_EXPONENT=2 + + //angular attenuation + da *= calcDirectionalLight(n, lv); + } + + return da; +} + +void main() +{ + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + + mat4 mat = getObjectSkinnedTransform(); + + mat = gl_ModelViewMatrix * mat; + + vec3 pos = (mat*gl_Vertex).xyz; + + gl_Position = gl_ProjectionMatrix * vec4(pos, 1.0); + + vec4 n = gl_Vertex; + n.xyz += gl_Normal.xyz; + n.xyz = (mat*n).xyz; + n.xyz = normalize(n.xyz-pos.xyz); + + vec3 norm = n.xyz; + + float dp_directional_light = max(0.0, dot(norm, gl_LightSource[0].position.xyz)); + vary_position = pos.xyz + gl_LightSource[0].position.xyz * (1.0-dp_directional_light)*shadow_offset; + + calcAtmospherics(pos.xyz); + + //vec4 color = calcLighting(pos.xyz, norm, gl_Color, vec4(0.)); + vec4 col = vec4(0.0, 0.0, 0.0, gl_Color.a); + + // Collect normal lights + col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].quadraticAttenuation, gl_LightSource[2].specular.a); + col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].quadraticAttenuation ,gl_LightSource[3].specular.a); + col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].quadraticAttenuation, gl_LightSource[4].specular.a); + col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].quadraticAttenuation, gl_LightSource[5].specular.a); + col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].quadraticAttenuation, gl_LightSource[6].specular.a); + col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].quadraticAttenuation, gl_LightSource[7].specular.a); + + vary_pointlight_col = col.rgb*gl_Color.rgb; + + col.rgb = vec3(0,0,0); + + // Add windlight lights + col.rgb = atmosAmbient(vec3(0.)); + + vary_ambient = col.rgb*gl_Color.rgb; + vary_directional.rgb = gl_Color.rgb*atmosAffectDirectionalLight(max(calcDirectionalLight(norm, gl_LightSource[0].position.xyz), (1.0-gl_Color.a)*(1.0-gl_Color.a))); + + col.rgb = min(col.rgb*gl_Color.rgb, 1.0); + + gl_FrontColor = col; + + gl_FogFragCoord = pos.z; + + pos.xyz = (gl_ModelViewProjectionMatrix * gl_Vertex).xyz; + vary_fragcoord.xyz = pos.xyz + vec3(0,0,near_clip); + +} + diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl new file mode 100644 index 000000000..0160e8427 --- /dev/null +++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl @@ -0,0 +1,346 @@ +/** + * @file softenLightF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +#extension GL_ARB_texture_rectangle : enable + +uniform sampler2DRect diffuseRect; +uniform sampler2DRect specularRect; +uniform sampler2DRect normalMap; +uniform sampler2DRect lightMap; +uniform sampler2DRect depthMap; +uniform sampler2D noiseMap; +uniform samplerCube environmentMap; +uniform sampler2D lightFunc; +uniform vec3 gi_quad; + +uniform float blur_size; +uniform float blur_fidelity; + +// Inputs +uniform vec4 morphFactor; +uniform vec3 camPosLocal; +//uniform vec4 camPosWorld; +uniform vec4 gamma; +uniform vec4 lightnorm; +uniform vec4 sunlight_color; +uniform vec4 ambient; +uniform vec4 blue_horizon; +uniform vec4 blue_density; +uniform vec4 haze_horizon; +uniform vec4 haze_density; +uniform vec4 cloud_shadow; +uniform vec4 density_multiplier; +uniform vec4 distance_multiplier; +uniform vec4 max_y; +uniform vec4 glow; +uniform float scene_light_strength; +uniform vec3 env_mat[3]; +uniform vec4 shadow_clip; +uniform mat3 ssao_effect_mat; + +uniform mat4 inv_proj; +uniform vec2 screen_res; + +varying vec4 vary_light; +varying vec2 vary_fragcoord; + +vec3 vary_PositionEye; + +vec3 vary_SunlitColor; +vec3 vary_AmblitColor; +vec3 vary_AdditiveColor; +vec3 vary_AtmosAttenuation; + +vec4 getPosition_d(vec2 pos_screen, float depth) +{ + vec2 sc = pos_screen.xy*2.0; + sc /= screen_res; + sc -= vec2(1.0,1.0); + vec4 ndc = vec4(sc.x, sc.y, 2.0*depth-1.0, 1.0); + vec4 pos = inv_proj * ndc; + pos /= pos.w; + pos.w = 1.0; + return pos; +} + +vec4 getPosition(vec2 pos_screen) +{ //get position in screen space (world units) given window coordinate and depth map + float depth = texture2DRect(depthMap, pos_screen.xy).a; + return getPosition_d(pos_screen, depth); +} + +vec3 getPositionEye() +{ + return vary_PositionEye; +} +vec3 getSunlitColor() +{ + return vary_SunlitColor; +} +vec3 getAmblitColor() +{ + return vary_AmblitColor; +} +vec3 getAdditiveColor() +{ + return vary_AdditiveColor; +} +vec3 getAtmosAttenuation() +{ + return vary_AtmosAttenuation; +} + + +void setPositionEye(vec3 v) +{ + vary_PositionEye = v; +} + +void setSunlitColor(vec3 v) +{ + vary_SunlitColor = v; +} + +void setAmblitColor(vec3 v) +{ + vary_AmblitColor = v; +} + +void setAdditiveColor(vec3 v) +{ + vary_AdditiveColor = v; +} + +void setAtmosAttenuation(vec3 v) +{ + vary_AtmosAttenuation = v; +} + +void calcAtmospherics(vec3 inPositionEye, float ambFactor) { + + vec3 P = inPositionEye; + setPositionEye(P); + + //(TERRAIN) limit altitude + if (P.y > max_y.x) P *= (max_y.x / P.y); + if (P.y < -max_y.x) P *= (-max_y.x / P.y); + + vec3 tmpLightnorm = lightnorm.xyz; + + vec3 Pn = normalize(P); + float Plen = length(P); + + vec4 temp1 = vec4(0); + vec3 temp2 = vec3(0); + vec4 blue_weight; + vec4 haze_weight; + vec4 sunlight = sunlight_color; + vec4 light_atten; + + //sunlight attenuation effect (hue and brightness) due to atmosphere + //this is used later for sunlight modulation at various altitudes + light_atten = (blue_density * 1.0 + vec4(haze_density.r) * 0.25) * (density_multiplier.x * max_y.x); + //I had thought blue_density and haze_density should have equal weighting, + //but attenuation due to haze_density tends to seem too strong + + temp1 = blue_density + vec4(haze_density.r); + blue_weight = blue_density / temp1; + haze_weight = vec4(haze_density.r) / temp1; + + //(TERRAIN) compute sunlight from lightnorm only (for short rays like terrain) + temp2.y = max(0.0, tmpLightnorm.y); + temp2.y = 1. / temp2.y; + sunlight *= exp( - light_atten * temp2.y); + + // main atmospheric scattering line integral + temp2.z = Plen * density_multiplier.x; + + // Transparency (-> temp1) + // ATI Bugfix -- can't store temp1*temp2.z*distance_multiplier.x in a variable because the ati + // compiler gets confused. + temp1 = exp(-temp1 * temp2.z * distance_multiplier.x); + + //final atmosphere attenuation factor + setAtmosAttenuation(temp1.rgb); + + //compute haze glow + //(can use temp2.x as temp because we haven't used it yet) + temp2.x = dot(Pn, tmpLightnorm.xyz); + temp2.x = 1. - temp2.x; + //temp2.x is 0 at the sun and increases away from sun + temp2.x = max(temp2.x, .03); //was glow.y + //set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot) + temp2.x *= glow.x; + //higher glow.x gives dimmer glow (because next step is 1 / "angle") + temp2.x = pow(temp2.x, glow.z); + //glow.z should be negative, so we're doing a sort of (1 / "angle") function + + //add "minimum anti-solar illumination" + temp2.x += .25; + + //increase ambient when there are more clouds + vec4 tmpAmbient = ambient + (vec4(1.) - ambient) * cloud_shadow.x * 0.5; + + /* decrease value and saturation (that in HSV, not HSL) for occluded areas + * // for HSV color/geometry used here, see http://gimp-savvy.com/BOOK/index.html?node52.html + * // The following line of code performs the equivalent of: + * float ambAlpha = tmpAmbient.a; + * float ambValue = dot(vec3(tmpAmbient), vec3(0.577)); // projection onto <1/rt(3), 1/rt(3), 1/rt(3)>, the neutral white-black axis + * vec3 ambHueSat = vec3(tmpAmbient) - vec3(ambValue); + * tmpAmbient = vec4(RenderSSAOEffect.valueFactor * vec3(ambValue) + RenderSSAOEffect.saturationFactor *(1.0 - ambFactor) * ambHueSat, ambAlpha); + */ + tmpAmbient = vec4(mix(ssao_effect_mat * tmpAmbient.rgb, tmpAmbient.rgb, ambFactor), tmpAmbient.a); + + //haze color + setAdditiveColor( + vec3(blue_horizon * blue_weight * (sunlight*(1.-cloud_shadow.x) + tmpAmbient) + + (haze_horizon.r * haze_weight) * (sunlight*(1.-cloud_shadow.x) * temp2.x + + tmpAmbient))); + + //brightness of surface both sunlight and ambient + setSunlitColor(vec3(sunlight * .5)); + setAmblitColor(vec3(tmpAmbient * .25)); + setAdditiveColor(getAdditiveColor() * vec3(1.0 - temp1)); +} + +vec3 atmosLighting(vec3 light) +{ + light *= getAtmosAttenuation().r; + light += getAdditiveColor(); + return (2.0 * light); +} + +vec3 atmosTransport(vec3 light) { + light *= getAtmosAttenuation().r; + light += getAdditiveColor() * 2.0; + return light; +} +vec3 atmosGetDiffuseSunlightColor() +{ + return getSunlitColor(); +} + +vec3 scaleDownLight(vec3 light) +{ + return (light / scene_light_strength ); +} + +vec3 scaleUpLight(vec3 light) +{ + return (light * scene_light_strength); +} + +vec3 atmosAmbient(vec3 light) +{ + return getAmblitColor() + light / 2.0; +} + +vec3 atmosAffectDirectionalLight(float lightIntensity) +{ + return getSunlitColor() * lightIntensity; +} + +vec3 scaleSoftClip(vec3 light) +{ + //soft clip effect: + light = 1. - clamp(light, vec3(0.), vec3(1.)); + light = 1. - pow(light, gamma.xxx); + + return light; +} + +void main() +{ + vec2 tc = vary_fragcoord.xy; + float depth = texture2DRect(depthMap, tc.xy).a; + vec3 pos = getPosition_d(tc, depth).xyz; + vec3 norm = texture2DRect(normalMap, tc).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + //vec3 nz = texture2D(noiseMap, vary_fragcoord.xy/128.0).xyz; + + float da = max(dot(norm.xyz, vary_light.xyz), 0.0); + + vec4 diffuse = texture2DRect(diffuseRect, tc); + vec4 spec = texture2DRect(specularRect, vary_fragcoord.xy); + + vec2 scol_ambocc = texture2DRect(lightMap, vary_fragcoord.xy).rg; + float scol = max(scol_ambocc.r, diffuse.a); + float ambocc = scol_ambocc.g; + + calcAtmospherics(pos.xyz, ambocc); + + vec3 col = atmosAmbient(vec3(0)); + col += atmosAffectDirectionalLight(max(min(da, scol), diffuse.a)); + + col *= diffuse.rgb; + + if (spec.a > 0.0) // specular reflection + { + // the old infinite-sky shiny reflection + // + vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz)); + float sa = dot(refnormpersp, vary_light.xyz); + vec3 dumbshiny = vary_SunlitColor*scol_ambocc.r*texture2D(lightFunc, vec2(sa, spec.a)).a; + + /* + // screen-space cheap fakey reflection map + // + vec3 refnorm = normalize(reflect(vec3(0,0,-1), norm.xyz)); + depth -= 0.5; // unbias depth + // first figure out where we'll make our 2D guess from + vec2 ref2d = (0.25 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth; + // Offset the guess source a little according to a trivial + // checkerboard dither function and spec.a. + // This is meant to be similar to sampling a blurred version + // of the diffuse map. LOD would be better in that regard. + // The goal of the blur is to soften reflections in surfaces + // with low shinyness, and also to disguise our lameness. + float checkerboard = floor(mod(tc.x+tc.y, 2.0)); // 0.0, 1.0 + float checkoffset = (3.0 + (7.0*(1.0-spec.a)))*(checkerboard-0.5); + ref2d += vec2(checkoffset, checkoffset); + ref2d += tc.xy; // use as offset from destination + // Get attributes from the 2D guess point. + // We average two samples of diffuse (not of anything else) per + // pixel to try to reduce aliasing some more. + vec3 refcol = 0.5 * (texture2DRect(diffuseRect, ref2d + vec2(0.0, -checkoffset)).rgb + + texture2DRect(diffuseRect, ref2d + vec2(-checkoffset, 0.0)).rgb); + float refdepth = texture2DRect(depthMap, ref2d).a; + vec3 refpos = getPosition_d(ref2d, refdepth).xyz; + float refshad = texture2DRect(lightMap, ref2d).r; + vec3 refn = texture2DRect(normalMap, ref2d).rgb; + refn = vec3((refn.xy-0.5)*2.0,refn.z); // unpack norm + refn = normalize(refn); + // figure out how appropriate our guess actually was + float refapprop = max(0.0, dot(-refnorm, normalize(pos - refpos))); + // darken reflections from points which face away from the reflected ray - our guess was a back-face + //refapprop *= step(dot(refnorm, refn), 0.0); + refapprop = min(refapprop, max(0.0, -dot(refnorm, refn))); // more conservative variant + // get appropriate light strength for guess-point + // reflect light direction to increase the illusion that + // these are reflections. + vec3 reflight = reflect(lightnorm.xyz, norm.xyz); + float reflit = min(max(dot(refn, reflight.xyz), 0.0), refshad); + // apply sun color to guess-point, dampen according to inappropriateness of guess + float refmod = min(refapprop, reflit); + vec3 refprod = vary_SunlitColor * refcol.rgb * refmod; + vec3 ssshiny = (refprod * spec.a); + ssshiny *= 0.3; // dampen it even more + */ + vec3 ssshiny = vec3(0,0,0); + + // add the two types of shiny together + col += (ssshiny + dumbshiny) * spec.rgb; + } + + col = atmosLighting(col); + col = scaleSoftClip(col); + + gl_FragColor.rgb = col; + gl_FragColor.a = 0.0; +} diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index 9e6123c30..645b99942 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -37,16 +37,23 @@ #include "llvoavatar.h" #include "m3math.h" +#include "llmatrix4a.h" -#include "llagent.h" +#include "llagent.h" //for gAgent.needsRenderAvatar() #include "lldrawable.h" +#include "lldrawpoolbump.h" #include "llface.h" +#if MESH_ENABLED +#include "llmeshrepository.h" +#endif //MESH_ENABLED #include "llsky.h" #include "llviewercamera.h" #include "llviewerregion.h" #include "noise.h" #include "pipeline.h" #include "llviewershadermgr.h" +#include "llvovolume.h" +#include "llvolume.h" #include "llappviewer.h" #include "llrendersphere.h" #include "llviewerpartsim.h" @@ -60,6 +67,10 @@ BOOL LLDrawPoolAvatar::sSkipOpaque = FALSE; BOOL LLDrawPoolAvatar::sSkipTransparent = FALSE; S32 LLDrawPoolAvatar::sDiffuseChannel = 0; +#if MESH_ENABLED +static bool is_deferred_render = false; +#endif //MESH_ENABLED + extern BOOL gUseGLPick; F32 CLOTHING_GRAVITY_EFFECT = 0.7f; @@ -157,6 +168,9 @@ LLMatrix4& LLDrawPoolAvatar::getModelView() void LLDrawPoolAvatar::beginDeferredPass(S32 pass) { sSkipTransparent = TRUE; +#if MESH_ENABLED + is_deferred_render = true; +#endif //MESH_ENABLED if (LLPipeline::sImpostorRender) { //impostor pass does not have rigid or impostor rendering @@ -187,7 +201,11 @@ void LLDrawPoolAvatar::beginDeferredPass(S32 pass) void LLDrawPoolAvatar::endDeferredPass(S32 pass) { + sSkipTransparent = FALSE; +#if MESH_ENABLED + is_deferred_render = false; +#endif //MESH_ENABLED if (LLPipeline::sImpostorRender) { @@ -1406,7 +1424,7 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace* LLMatrix4a bind_shape_matrix; bind_shape_matrix.loadu(skin->mBindShapeMatrix); - for (U32 j = 0; j < buffer->getRequestedVerts(); ++j) + for (U32 j = 0; j < (U32)buffer->getRequestedVerts(); ++j) { LLMatrix4a final_mat; final_mat.clear(); @@ -1458,7 +1476,7 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace* void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow) { - if (avatar->isSelf() && !gAgent.needsRenderAvatar()) + if (avatar->isSelf() && !gAgent.needsRenderAvatar() || !gMeshRepo.meshRezEnabled()) { return; } @@ -1495,7 +1513,7 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow) continue; } - const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(mesh_id); + const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(mesh_id, vobj); if (!skin) { continue; @@ -1754,7 +1772,7 @@ void LLDrawPoolAvatar::removeRiggedFace(LLFace* facep) if (index > -1) { - if (mRiggedFace[i].size() > index && mRiggedFace[i][index] == facep) + if ((S32)mRiggedFace[i].size() > index && mRiggedFace[i][index] == facep) { facep->setRiggedIndex(i,-1); mRiggedFace[i].erase(mRiggedFace[i].begin()+index); diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 1893621c8..ac41fe18c 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -42,6 +42,7 @@ #include "llmatrix4a.h" #include "v3color.h" +#include "lldrawpoolavatar.h" #include "lldrawpoolbump.h" #include "llgl.h" #include "llrender.h" @@ -153,6 +154,9 @@ void LLFace::init(LLDrawable* drawablep, LLViewerObject* objp) { mLastUpdateTime = gFrameTimeSeconds; mLastMoveTime = 0.f; +#if MESH_ENABLED + mLastSkinTime = gFrameTimeSeconds; +#endif //MESH_ENABLED mVSize = 0.f; mPixelArea = 16.f; mState = GLOBAL; @@ -1140,7 +1144,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, LLStrider binormals; LLStrider indicesp; #if MESH_ENABLED - LLStrider weights; + LLStrider weights; #endif //MESH_ENABLED BOOL full_rebuild = force_rebuild || mDrawablep->isState(LLDrawable::REBUILD_VOLUME); @@ -1597,7 +1601,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, { for (S32 i = 0; i < num_vertices; i++) { - weights[i].set(weights.getF32ptr()); + weights[i].set(*(weights.get())); } } #endif //MESH_ENABLED diff --git a/indra/newview/llface.h b/indra/newview/llface.h index 7abaaffa3..15d26d7da 100644 --- a/indra/newview/llface.h +++ b/indra/newview/llface.h @@ -239,6 +239,9 @@ public: LLVector2 mTexExtents[2]; F32 mDistance; F32 mLastUpdateTime; +#if MESH_ENABLED + F32 mLastSkinTime; +#endif //MESH_ENABLED F32 mLastMoveTime; LLMatrix4* mTextureMatrix; LLDrawInfo* mDrawInfo; diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp index ab0868237..210de0faf 100644 --- a/indra/newview/llfasttimerview.cpp +++ b/indra/newview/llfasttimerview.cpp @@ -127,6 +127,11 @@ static struct ft_display_info ft_display_table[] = { LLFastTimer::FTM_UPDATE_TEXTURES, " Textures", &LLColor4::pink2, 0 }, { LLFastTimer::FTM_GEO_UPDATE, " Geo Update", &LLColor4::blue3, 1 }, { LLFastTimer::FTM_UPDATE_PRIMITIVES, " Volumes", &LLColor4::blue4, 0 }, +#if MESH_ENABLED + { LLFastTimer::FTM_UPDATE_RIGGED_VOLUME," Rigged", &LLColor4::red3, 0 }, + { LLFastTimer::FTM_SKIN_RIGGED, " Skin", &LLColor4::green1, 0 }, + { LLFastTimer::FTM_RIGGED_OCTREE, " Octree", &LLColor4::green5, 0 }, +#endif //MESH_ENABLED { LLFastTimer::FTM_GEN_VOLUME, " Gen Volume", &LLColor4::yellow3, 0 }, { LLFastTimer::FTM_GEN_FLEX, " Flexible", &LLColor4::yellow4, 0 }, { LLFastTimer::FTM_GEN_TRIANGLES, " Triangles", &LLColor4::yellow5, 0 }, @@ -155,6 +160,12 @@ static struct ft_display_info ft_display_table[] = { LLFastTimer::FTM_STATESORT, " State Sort", &LLColor4::orange1, 1 }, { LLFastTimer::FTM_STATESORT_DRAWABLE, " Drawable", &LLColor4::orange2, 0 }, { LLFastTimer::FTM_STATESORT_POSTSORT, " Post Sort", &LLColor4::orange3, 0 }, +#if MESH_ENABLED + { LLFastTimer::FTM_MESH_UPDATE, " Mesh Update", &LLColor4::orange4, 0 }, + { LLFastTimer::FTM_MESH_LOCK1, " Lock 1", &LLColor4::orange5, 0 }, + { LLFastTimer::FTM_MESH_LOCK2, " Lock 2", &LLColor4::orange6, 0 }, + { LLFastTimer::FTM_LOAD_MESH_LOD, " Load LOD", &LLColor4::yellow3, 0 }, +#endif //MESH_ENABLED { LLFastTimer::FTM_REBUILD_OCCLUSION_VB," Occlusion", &LLColor4::cyan5, 0 }, { LLFastTimer::FTM_REBUILD_VBO, " VBO Rebuild", &LLColor4::red4, 0 }, { LLFastTimer::FTM_REBUILD_VOLUME_VB, " Volume", &LLColor4::blue1, 0 }, diff --git a/indra/newview/llmanipscale.cpp b/indra/newview/llmanipscale.cpp index acb37e897..54af78285 100644 --- a/indra/newview/llmanipscale.cpp +++ b/indra/newview/llmanipscale.cpp @@ -58,11 +58,15 @@ #include "llui.h" #include "llviewercamera.h" #include "llviewerobject.h" +#include "llviewerregion.h" #include "llviewerwindow.h" #include "llhudrender.h" #include "llworld.h" #include "v2math.h" #include "llvoavatar.h" +#if MESH_ENABLED +#include "llmeshrepository.h" +#endif //MESH_ENABLED const F32 MAX_MANIP_SELECT_DISTANCE_SQUARED = 11.f * 11.f; @@ -96,10 +100,7 @@ F32 get_default_max_prim_scale(bool is_flora) // a bit of a hack, but if it's foilage, we don't want to use the // new larger scale which would result in giant trees and grass #if MESH_ENABLED - if (gSavedSettings.getBOOL("MeshEnabled") && - gAgent.getRegion() && - !gAgent.getRegion()->getCapability("GetMesh").empty() && - !gAgent.getRegion()->getCapability("ObjectAdd").empty() && + if (gMeshRepo.meshRezEnabled() && !is_flora) { return DEFAULT_MAX_PRIM_SCALE; diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 55145c6ad..04011b6ae 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -26,6 +26,7 @@ #include "llviewerprecompiledheaders.h" +#if MESH_ENABLED #include "apr_pools.h" #include "apr_dso.h" #include "llhttpstatuscodes.h" @@ -37,12 +38,14 @@ #include "llcurl.h" #include "lldatapacker.h" #include "llfasttimer.h" +#if MESH_IMPORT #include "llfloatermodelpreview.h" +#endif //MESH_IMPORT #include "llfloaterperms.h" #include "lleconomy.h" #include "llimagej2c.h" #include "llhost.h" -#include "llnotificationsutil.h" +#include "llnotifications.h" #include "llsd.h" #include "llsdutil_math.h" #include "llsdserialize.h" @@ -61,20 +64,30 @@ #include "pipeline.h" #include "llinventorymodel.h" #include "llfoldertype.h" +#include "llviewerparcelmgr.h" +#include "boost/lexical_cast.hpp" #ifndef LL_WINDOWS #include "netdb.h" #endif #include -LLFastTimer::DeclareTimer FTM_MESH_UPDATE("Mesh Update"); -LLFastTimer::DeclareTimer FTM_LOAD_MESH("Load Mesh"); +//LLFastTimer::DeclareTimer FTM_MESH_UPDATE("Mesh Update"); +//LLFastTimer::DeclareTimer FTM_LOAD_MESH("Load Mesh"); LLMeshRepository gMeshRepo; const U32 MAX_MESH_REQUESTS_PER_SECOND = 100; +// Maximum mesh version to support. Three least significant digits are reserved for the minor version, +// with major version changes indicating a format change that is not backwards compatible and should not +// be parsed by viewers that don't specifically support that version. For example, if the integer "1" is +// present, the version is 0.001. A viewer that can parse version 0.001 can also parse versions up to 0.999, +// but not 1.0 (integer 1000). +// See wiki at https://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format +const S32 MAX_MESH_VERSION = 999; + U32 LLMeshRepository::sBytesReceived = 0; U32 LLMeshRepository::sHTTPRequestCount = 0; U32 LLMeshRepository::sHTTPRetryCount = 0; @@ -85,7 +98,14 @@ U32 LLMeshRepository::sPeakKbps = 0; const U32 MAX_TEXTURE_UPLOAD_RETRIES = 5; -void dumpLLSDToFile(const LLSD& content, std::string filename); +static S32 dump_num = 0; +std::string make_dump_name(std::string prefix, S32 num) +{ + return prefix + boost::lexical_cast(num) + std::string(".xml"); + +} +void dump_llsd_to_file(const LLSD& content, std::string filename); +LLSD llsd_from_file(std::string filename); std::string header_lod[] = { @@ -102,7 +122,7 @@ U32 get_volume_memory_size(const LLVolume* volume) U32 indices = 0; U32 vertices = 0; - for (U32 i = 0; i < volume->getNumVolumeFaces(); ++i) + for (U32 i = 0; i < (U32)volume->getNumVolumeFaces(); ++i) { const LLVolumeFace& face = volume->getVolumeFace(i); indices += face.mNumIndices; @@ -180,196 +200,6 @@ S32 LLMeshRepoThread::sActiveHeaderRequests = 0; S32 LLMeshRepoThread::sActiveLODRequests = 0; U32 LLMeshRepoThread::sMaxConcurrentRequests = 1; - -class LLTextureCostResponder : public LLCurl::Responder -{ -public: - LLTextureUploadData mData; - LLMeshUploadThread* mThread; - - LLTextureCostResponder(LLTextureUploadData data, LLMeshUploadThread* thread) - : mData(data), mThread(thread) - { - - } - - virtual void completed(U32 status, const std::string& reason, const LLSD& content) - { - mThread->mPendingConfirmations--; - if (isGoodStatus(status)) - { - mThread->priceResult(mData, content); - } - else - { - llwarns << status << ": " << reason << llendl; - - if (mData.mRetries < MAX_TEXTURE_UPLOAD_RETRIES) - { - llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl; - - if (status == 499 || status == 500) - { - mThread->uploadTexture(mData); - } - else - { - llerrs << "Unhandled status " << status << llendl; - } - } - else - { - llwarns << "Giving up after " << mData.mRetries << " retries." << llendl; - } - } - } -}; - -class LLTextureUploadResponder : public LLCurl::Responder -{ -public: - LLTextureUploadData mData; - LLMeshUploadThread* mThread; - - LLTextureUploadResponder(LLTextureUploadData data, LLMeshUploadThread* thread) - : mData(data), mThread(thread) - { - } - - virtual void completed(U32 status, const std::string& reason, const LLSD& content) - { - mThread->mPendingUploads--; - if (isGoodStatus(status)) - { - mData.mUUID = content["new_asset"].asUUID(); - gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mData.mPostData, content)); - mThread->onTextureUploaded(mData); - } - else - { - llwarns << status << ": " << reason << llendl; - llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl; - - if (status == 404) - { - mThread->uploadTexture(mData); - } - else if (status == 499) - { - mThread->mConfirmedTextureQ.push(mData); - } - else - { - llerrs << "Unhandled status " << status << llendl; - } - } - } -}; - -class LLMeshCostResponder : public LLCurl::Responder -{ -public: - LLMeshUploadData mData; - LLMeshUploadThread* mThread; - - LLMeshCostResponder(LLMeshUploadData data, LLMeshUploadThread* thread) - : mData(data), mThread(thread) - { - - } - - virtual void completed(U32 status, const std::string& reason, const LLSD& content) - { - mThread->mPendingConfirmations--; - - if (isGoodStatus(status)) - { - mThread->priceResult(mData, content); - } - else - { - llwarns << status << ": " << reason << llendl; - - if (status == HTTP_INTERNAL_ERROR) - { - llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl; - mThread->uploadModel(mData); - } - else if (status == HTTP_BAD_REQUEST) - { - llwarns << "Status 400 received from server, giving up." << llendl; - } - else if (status == HTTP_NOT_FOUND) - { - llwarns <<"Status 404 received, server is disconnected, giving up." << llendl ; - } - else - { - llerrs << "Unhandled status " << status << llendl; - } - } - } -}; - -class LLMeshUploadResponder : public LLCurl::Responder -{ -public: - LLMeshUploadData mData; - LLMeshUploadThread* mThread; - - LLMeshUploadResponder(LLMeshUploadData data, LLMeshUploadThread* thread) - : mData(data), mThread(thread) - { - } - - virtual void completed(U32 status, const std::string& reason, const LLSD& content) - { - mThread->mPendingUploads--; - if (isGoodStatus(status)) - { - mData.mUUID = content["new_asset"].asUUID(); - if (mData.mUUID.isNull()) - { - LLSD args; - std::string message = content["error"]["message"]; - std::string identifier = content["error"]["identifier"]; - std::string invalidity_identifier = content["error"]["invalidity_identifier"]; - - args["MESSAGE"] = message; - args["IDENTIFIER"] = identifier; - args["INVALIDITY_IDENTIFIER"] = invalidity_identifier; - args["LABEL"] = mData.mBaseModel->mLabel; - - gMeshRepo.uploadError(args); - } - else - { - gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mData.mPostData, content)); - mThread->onModelUploaded(mData); - } - } - else - { - llwarns << status << ": " << reason << llendl; - llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl; - - if (status == 404) - { - mThread->uploadModel(mData); - } - else if (status == 499) - { - mThread->mConfirmedQ.push(mData); - } - else if (status != 500) - { //drop internal server errors on the floor, otherwise grab - llerrs << "Unhandled status " << status << llendl; - } - } - } -}; - - class LLMeshHeaderResponder : public LLCurl::Responder { public: @@ -459,77 +289,173 @@ public: }; -class LLModelObjectUploadResponder: public LLCurl::Responder +#if MESH_IMPORT +void log_upload_error(S32 status, const LLSD& content, std::string stage, std::string model_name) { - LLSD mObjectAsset; - LLMeshUploadThread* mThread; + // Add notification popup. + LLSD args; + std::string message = content["error"]["message"]; + std::string identifier = content["error"]["identifier"]; + args["MESSAGE"] = message; + args["IDENTIFIER"] = identifier; + args["LABEL"] = model_name; + gMeshRepo.uploadError(args); -public: - LLModelObjectUploadResponder(LLMeshUploadThread* thread, const LLSD& object_asset): - mThread(thread), - mObjectAsset(object_asset) + // Log details. + llwarns << "stage: " << stage << " http status: " << status << llendl; + if (content.has("error")) { + const LLSD& err = content["error"]; + llwarns << "err: " << err << llendl; + llwarns << "mesh upload failed, stage '" << stage + << "' error '" << err["error"].asString() + << "', message '" << err["message"].asString() + << "', id '" << err["identifier"].asString() + << "'" << llendl; + if (err.has("errors")) + { + S32 error_num = 0; + const LLSD& err_list = err["errors"]; + for (LLSD::array_const_iterator it = err_list.beginArray(); + it != err_list.endArray(); + ++it) + { + const LLSD& err_entry = *it; + llwarns << "error[" << error_num << "]:" << llendl; + for (LLSD::map_const_iterator map_it = err_entry.beginMap(); + map_it != err_entry.endMap(); + ++map_it) + { + llwarns << "\t" << map_it->first << ": " + << map_it->second << llendl; + } + error_num++; + } + } } - - virtual void completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) + else { - assert_main_thread(); - - llinfos << "completed" << llendl; - mThread->mPendingUploads--; - mThread->mFinished = true; + llwarns << "bad mesh, no error information available" << llendl; } -}; +} class LLWholeModelFeeResponder: public LLCurl::Responder { LLMeshUploadThread* mThread; + LLSD mModelData; + LLHandle mObserverHandle; public: - LLWholeModelFeeResponder(LLMeshUploadThread* thread): - mThread(thread) + LLWholeModelFeeResponder(LLMeshUploadThread* thread, LLSD& model_data, LLHandle observer_handle): + mThread(thread), + mModelData(model_data), + mObserverHandle(observer_handle) { } virtual void completed(U32 status, const std::string& reason, const LLSD& content) { - //assert_main_thread(); - llinfos << "completed" << llendl; + LLSD cc = content; + if (gSavedSettings.getS32("MeshUploadFakeErrors")&1) + { + cc = llsd_from_file("fake_upload_error.xml"); + } + mThread->mPendingUploads--; - dumpLLSDToFile(content,"whole_model_response.xml"); + dump_llsd_to_file(cc,make_dump_name("whole_model_fee_response_",dump_num)); - mThread->mWholeModelUploadURL = content["uploader"].asString(); + LLWholeModelFeeObserver* observer = mObserverHandle.get(); + + if (isGoodStatus(status) && + cc["state"].asString() == "upload") + { + mThread->mWholeModelUploadURL = cc["uploader"].asString(); + + if (observer) + { + cc["data"]["upload_price"] = cc["upload_price"]; + observer->onModelPhysicsFeeReceived(cc["data"], mThread->mWholeModelUploadURL); + } + } + else + { + llwarns << "fee request failed" << llendl; + log_upload_error(status,cc,"fee",mModelData["name"]); + mThread->mWholeModelUploadURL = ""; + + if (observer) + { + observer->setModelPhysicsFeeErrorStatus(status, reason); + } + } } + }; class LLWholeModelUploadResponder: public LLCurl::Responder { LLMeshUploadThread* mThread; + LLSD mModelData; + LLHandle mObserverHandle; + public: - LLWholeModelUploadResponder(LLMeshUploadThread* thread): - mThread(thread) + LLWholeModelUploadResponder(LLMeshUploadThread* thread, LLSD& model_data, LLHandle observer_handle): + mThread(thread), + mModelData(model_data), + mObserverHandle(observer_handle) { } virtual void completed(U32 status, const std::string& reason, const LLSD& content) { + LLSD cc = content; + if (gSavedSettings.getS32("MeshUploadFakeErrors")&2) + { + cc = llsd_from_file("fake_upload_error.xml"); + } + //assert_main_thread(); - llinfos << "upload completed" << llendl; mThread->mPendingUploads--; - dumpLLSDToFile(content,"whole_model_upload_response.xml"); + dump_llsd_to_file(cc,make_dump_name("whole_model_upload_response_",dump_num)); + + LLWholeModelUploadObserver* observer = mObserverHandle.get(); + + // requested "mesh" asset type isn't actually the type + // of the resultant object, fix it up here. + if (isGoodStatus(status) && + cc["state"].asString() == "complete") + { + mModelData["asset_type"] = "object"; + gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mModelData,cc)); + + if (observer) + { + doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadSuccess, observer)); + } + } + else + { + llwarns << "upload failed" << llendl; + std::string model_name = mModelData["name"].asString(); + log_upload_error(status,cc,"upload",model_name); + + if (observer) + { + doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadFailure, observer)); + } + } } }; +#endif //MESH_IMPORT LLMeshRepoThread::LLMeshRepoThread() -: LLThread("mesh repo", NULL) +: LLThread("mesh repo") { mWaiting = false; - mMutex = new LLMutex(NULL); - mHeaderMutex = new LLMutex(NULL); - mSignal = new LLCondition(NULL); + mMutex = new LLMutex; + mHeaderMutex = new LLMutex; + mSignal = new LLCondition; } LLMeshRepoThread::~LLMeshRepoThread() @@ -545,11 +471,13 @@ LLMeshRepoThread::~LLMeshRepoThread() void LLMeshRepoThread::run() { mCurlRequest = new LLCurlRequest(); +#if MESH_IMPORT LLCDResult res = LLConvexDecomposition::initThread(); if (res != LLCD_OK) { llwarns << "convex decomposition unable to be loaded" << llendl; } +#endif //MESH_IMPORT while (!LLApp::isQuitting()) { @@ -571,7 +499,7 @@ void LLMeshRepoThread::run() // NOTE: throttling intentionally favors LOD requests over header requests - while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveLODRequests < sMaxConcurrentRequests) + while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveLODRequests < (S32)sMaxConcurrentRequests) { { mMutex->lock(); @@ -585,7 +513,7 @@ void LLMeshRepoThread::run() } } - while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveHeaderRequests < sMaxConcurrentRequests) + while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveHeaderRequests < (S32)sMaxConcurrentRequests) { { mMutex->lock(); @@ -647,11 +575,13 @@ void LLMeshRepoThread::run() mSignal->unlock(); } +#if MESH_IMPORT res = LLConvexDecomposition::quitThread(); if (res != LLCD_OK) { llwarns << "convex decomposition unable to be quit" << llendl; } +#endif //MESH_IMPORT delete mCurlRequest; mCurlRequest = NULL; @@ -673,7 +603,7 @@ void LLMeshRepoThread::loadMeshPhysicsShape(const LLUUID& mesh_id) } -void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) +void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool do_lock) { //protected by mSignal, no locking needed here mesh_header_map::iterator iter = mMeshHeader.find(mesh_params.getSculptID()); @@ -681,8 +611,11 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) { //if we have the header, request LOD byte range LODRequest req(mesh_params, lod); { - LLMutexLock lock(mMutex); + if(do_lock) + mMutex->lock(); mLODReqQ.push(req); + if(do_lock) + mMutex->unlock(); } } else @@ -694,16 +627,16 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) if (pending != mPendingLOD.end()) { //append this lod request to existing header request pending->second.push_back(lod); - if (pending->second.size() > 4) - { - llerrs << "WTF?" << llendl; - } + llassert(pending->second.size() <= LLModel::NUM_LODS) } else { //if no header request is pending, fetch header - LLMutexLock lock(mMutex); + if(do_lock) + mMutex->lock(); mHeaderReqQ.push(req); mPendingLOD[mesh_params].push_back(lod); + if(do_lock) + mMutex->unlock(); } } } @@ -745,12 +678,13 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) if (header_size > 0) { + S32 version = mMeshHeader[mesh_id]["version"].asInteger(); S32 offset = header_size + mMeshHeader[mesh_id]["skin"]["offset"].asInteger(); S32 size = mMeshHeader[mesh_id]["skin"]["size"].asInteger(); mHeaderMutex->unlock(); - if (offset >= 0 && size > 0) + if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { //check VFS for mesh skin info LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); @@ -817,12 +751,13 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) if (header_size > 0) { - S32 offset = header_size + mMeshHeader[mesh_id]["decomposition"]["offset"].asInteger(); - S32 size = mMeshHeader[mesh_id]["decomposition"]["size"].asInteger(); + S32 version = mMeshHeader[mesh_id]["version"].asInteger(); + S32 offset = header_size + mMeshHeader[mesh_id]["physics_convex"]["offset"].asInteger(); + S32 size = mMeshHeader[mesh_id]["physics_convex"]["size"].asInteger(); mHeaderMutex->unlock(); - if (offset >= 0 && size > 0) + if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { //check VFS for mesh skin info LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); @@ -889,12 +824,13 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) if (header_size > 0) { - S32 offset = header_size + mMeshHeader[mesh_id]["physics_shape"]["offset"].asInteger(); - S32 size = mMeshHeader[mesh_id]["physics_shape"]["size"].asInteger(); + S32 version = mMeshHeader[mesh_id]["version"].asInteger(); + S32 offset = header_size + mMeshHeader[mesh_id]["physics_mesh"]["offset"].asInteger(); + S32 size = mMeshHeader[mesh_id]["physics_mesh"]["size"].asInteger(); mHeaderMutex->unlock(); - if (offset >= 0 && size > 0) + if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { //check VFS for mesh physics shape info LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); @@ -962,9 +898,9 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) S32 size = file.getSize(); if (size > 0) - { - U8 buffer[1024]; - S32 bytes = llmin(size, 1024); + { //NOTE -- if the header size is ever more than 4KB, this will break + U8 buffer[4096]; + S32 bytes = llmin(size, 4096); LLMeshRepository::sCacheBytesRead += bytes; file.read(buffer, bytes); if (headerReceived(mesh_params, buffer, bytes)) @@ -1005,10 +941,12 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) if (header_size > 0) { + S32 version = mMeshHeader[mesh_id]["version"].asInteger(); S32 offset = header_size + mMeshHeader[mesh_id][header_lod[lod]]["offset"].asInteger(); S32 size = mMeshHeader[mesh_id][header_lod[lod]]["size"].asInteger(); mHeaderMutex->unlock(); - if (offset >= 0 && size > 0) + + if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { //check VFS for mesh asset @@ -1106,14 +1044,11 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat } { - U32 cost = gMeshRepo.calcResourceCost(header); - LLUUID mesh_id = mesh_params.getSculptID(); mHeaderMutex->lock(); mMeshHeaderSize[mesh_id] = header_size; mMeshHeader[mesh_id] = header; - mMeshResourceCost[mesh_id] = cost; mHeaderMutex->unlock(); //check for pending requests @@ -1264,10 +1199,16 @@ bool LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 return true; } +#if MESH_IMPORT LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures, - bool upload_skin, bool upload_joints) + bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload, + LLHandle fee_observer, LLHandle upload_observer) : LLThread("mesh upload"), - mDiscarded(FALSE) + mDiscarded(FALSE), + mDoUpload(do_upload), + mWholeModelUploadURL(upload_url), + mFeeObserverHandle(fee_observer), + mUploadObserverHandle(upload_observer) { mInstanceList = data; mUploadTextures = upload_textures; @@ -1275,18 +1216,16 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, mUploadJoints = upload_joints; mMutex = new LLMutex(NULL); mCurlRequest = NULL; - mPendingConfirmations = 0; mPendingUploads = 0; - mPendingCost = 0; mFinished = false; mOrigin = gAgent.getPositionAgent(); mHost = gAgent.getRegionHost(); - mUploadObjectAssetCapability = gAgent.getRegion()->getCapability("UploadObjectAsset"); - mNewInventoryCapability = gAgent.getRegion()->getCapability("NewFileAgentInventoryVariablePrice"); mWholeModelFeeCapability = gAgent.getRegion()->getCapability("NewFileAgentInventory"); mOrigin += gAgent.getAtAxis() * scale.magVec(); + + mMeshUploadTimeOut = gSavedSettings.getS32("MeshUploadTimeOut") ; } LLMeshUploadThread::~LLMeshUploadThread() @@ -1303,35 +1242,7 @@ LLMeshUploadThread::DecompRequest::DecompRequest(LLModel* mdl, LLModel* base_mod mThread = thread; //copy out positions and indices - if (mdl) - { - U16 index_offset = 0; - - mPositions.clear(); - mIndices.clear(); - - //queue up vertex positions and indices - for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) - { - const LLVolumeFace& face = mdl->getVolumeFace(i); - if (mPositions.size() + face.mNumVertices > 65535) - { - continue; - } - - for (U32 j = 0; j < face.mNumVertices; ++j) - { - mPositions.push_back(LLVector3(face.mPositions[j].getF32ptr())); - } - - for (U32 j = 0; j < face.mNumIndices; ++j) - { - mIndices.push_back(face.mIndices[j]+index_offset); - } - - index_offset += face.mNumVertices; - } - } + assignData(mdl) ; mThread->mFinalDecomp = this; mThread->mPhysicsComplete = false; @@ -1344,11 +1255,8 @@ void LLMeshUploadThread::DecompRequest::completed() mThread->mPhysicsComplete = true; } - if (mHull.size() != 1) - { - llerrs << "WTF?" << llendl; - } - + llassert(mHull.size() == 1); + mThread->mHullMap[mBaseModel] = mHull[0]; } @@ -1376,62 +1284,90 @@ BOOL LLMeshUploadThread::isDiscarded() void LLMeshUploadThread::run() { - if (gSavedSettings.getBOOL("MeshUseWholeModelUpload")) + if (mDoUpload) { doWholeModelUpload(); } else { - doIterativeUpload(); + requestWholeModelFee(); + } +} +#endif //MESH_IMPORT + +void dump_llsd_to_file(const LLSD& content, std::string filename) +{ + if (gSavedSettings.getBOOL("MeshUploadLogXML")) + { + std::ofstream of(filename.c_str()); + LLSDSerialize::toPrettyXML(content,of); } } -#if 1 -void dumpLLSDToFile(const LLSD& content, std::string filename) +LLSD llsd_from_file(std::string filename) { - std::ofstream of(filename.c_str()); - LLSDSerialize::toPrettyXML(content,of); + std::ifstream ifs(filename.c_str()); + LLSD result; + LLSDSerialize::fromXML(result,ifs); + return result; } -#endif +#if MESH_IMPORT void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures) { - // TODO where do textures go? - LLSD result; LLSD res; result["folder_id"] = gInventory.findCategoryUUIDForType(LLFolderType::FT_OBJECT); + result["texture_folder_id"] = gInventory.findCategoryUUIDForType(LLFolderType::FT_TEXTURE); result["asset_type"] = "mesh"; result["inventory_type"] = "object"; - result["name"] = "your name here"; - result["description"] = "your description here"; + result["description"] = "(No Description)"; + result["next_owner_mask"] = LLSD::Integer(LLFloaterPerms::getNextOwnerPerms()); + result["group_mask"] = LLSD::Integer(LLFloaterPerms::getGroupPerms()); + result["everyone_mask"] = LLSD::Integer(LLFloaterPerms::getEveryonePerms()); - // TODO "optional" fields from the spec - res["mesh_list"] = LLSD::emptyArray(); -// TODO Textures - //res["texture_list"] = LLSD::emptyArray(); + res["texture_list"] = LLSD::emptyArray(); + res["instance_list"] = LLSD::emptyArray(); S32 mesh_num = 0; S32 texture_num = 0; std::set textures; + std::map texture_index; + std::map mesh_index; + std::string model_name; + std::string model_metric; + + S32 instance_num = 0; + for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter) { LLMeshUploadData data; data.mBaseModel = iter->first; - - LLModelInstance& instance = *(iter->second.begin()); - + LLModelInstance& first_instance = *(iter->second.begin()); for (S32 i = 0; i < 5; i++) { - data.mModel[i] = instance.mLOD[i]; + data.mModel[i] = first_instance.mLOD[i]; } - std::stringstream ostr; + if (mesh_index.find(data.mBaseModel) == mesh_index.end()) + { + // Have not seen this model before - create a new mesh_list entry for it. + if (model_name.empty()) + { + model_name = data.mBaseModel->getName(); + } - LLModel::Decomposition& decomp = + if (model_metric.empty()) + { + model_metric = data.mBaseModel->getMetric(); + } + + std::stringstream ostr; + + LLModel::Decomposition& decomp = data.mModel[LLModel::LOD_PHYSICS].notNull() ? data.mModel[LLModel::LOD_PHYSICS]->mPhysics : data.mBaseModel->mPhysics; @@ -1449,78 +1385,109 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures) mUploadSkin, mUploadJoints); - data.mAssetData = ostr.str(); + data.mAssetData = ostr.str(); + std::string str = ostr.str(); - LLSD mesh_entry; - - LLVector3 pos, scale; - LLQuaternion rot; - LLMatrix4 transformation = instance.mTransform; - decomposeMeshMatrix(transformation,pos,rot,scale); - -#if 0 - mesh_entry["childpos"] = ll_sd_from_vector3(pos); - mesh_entry["childrot"] = ll_sd_from_quaternion(rot); - mesh_entry["scale"] = ll_sd_from_vector3(scale); -#endif - mesh_entry["position"] = ll_sd_from_vector3(LLVector3()); - mesh_entry["rotation"] = ll_sd_from_quaternion(rot); - mesh_entry["scale"] = ll_sd_from_vector3(scale); - - // TODO should be binary. - std::string str = ostr.str(); - mesh_entry["mesh_data"] = LLSD::Binary(str.begin(),str.end()); - - res["mesh_list"][mesh_num] = mesh_entry; - - // TODO how do textures in the list map to textures in the meshes? - if (mUploadTextures) - { - for (std::vector::iterator material_iter = instance.mMaterial.begin(); - material_iter != instance.mMaterial.end(); ++material_iter) - { - - if (textures.find(material_iter->mDiffuseMap.get()) == textures.end()) - { - textures.insert(material_iter->mDiffuseMap.get()); - - std::stringstream ostr; - if (include_textures) // otherwise data is blank. - { - LLTextureUploadData data(material_iter->mDiffuseMap.get(), material_iter->mDiffuseMapLabel); - if (!data.mTexture->isRawImageValid()) - { - data.mTexture->reloadRawImage(data.mTexture->getDiscardLevel()); - } - - LLPointer upload_file = - LLViewerTextureList::convertToUploadFile(data.mTexture->getRawImage()); - ostr.write((const char*) upload_file->getData(), upload_file->getDataSize()); - } - LLSD texture_entry; - texture_entry["texture_data"] = ostr.str(); - res["texture_list"][texture_num] = texture_entry; - texture_num++; - } - } + res["mesh_list"][mesh_num] = LLSD::Binary(str.begin(),str.end()); + mesh_index[data.mBaseModel] = mesh_num; + mesh_num++; } - mesh_num++; + // For all instances that use this model + for (instance_list::iterator instance_iter = iter->second.begin(); + instance_iter != iter->second.end(); + ++instance_iter) + { + + LLModelInstance& instance = *instance_iter; + + LLSD instance_entry; + + for (S32 i = 0; i < 5; i++) + { + data.mModel[i] = instance.mLOD[i]; + } + + LLVector3 pos, scale; + LLQuaternion rot; + LLMatrix4 transformation = instance.mTransform; + decomposeMeshMatrix(transformation,pos,rot,scale); + instance_entry["position"] = ll_sd_from_vector3(pos); + instance_entry["rotation"] = ll_sd_from_quaternion(rot); + instance_entry["scale"] = ll_sd_from_vector3(scale); + + instance_entry["material"] = LL_MCODE_WOOD; + instance_entry["physics_shape_type"] = (U8)(LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL); + instance_entry["mesh"] = mesh_index[data.mBaseModel]; + + instance_entry["face_list"] = LLSD::emptyArray(); + + S32 end = llmin((S32)data.mBaseModel->mMaterialList.size(), data.mBaseModel->getNumVolumeFaces()) ; + for (S32 face_num = 0; face_num < end; face_num++) + { + LLImportMaterial& material = instance.mMaterial[data.mBaseModel->mMaterialList[face_num]]; + LLSD face_entry = LLSD::emptyMap(); + LLViewerFetchedTexture *texture = material.mDiffuseMap.get(); + + if ((texture != NULL) && + (textures.find(texture) == textures.end())) + { + textures.insert(texture); + } + + std::stringstream texture_str; + if (texture != NULL && include_textures && mUploadTextures) + { + if(texture->hasSavedRawImage()) + { + LLPointer upload_file = + LLViewerTextureList::convertToUploadFile(texture->getSavedRawImage()); + texture_str.write((const char*) upload_file->getData(), upload_file->getDataSize()); + } + } + + if (texture != NULL && + mUploadTextures && + texture_index.find(texture) == texture_index.end()) + { + texture_index[texture] = texture_num; + std::string str = texture_str.str(); + res["texture_list"][texture_num] = LLSD::Binary(str.begin(),str.end()); + texture_num++; + } + + // Subset of TextureEntry fields. + if (texture != NULL && mUploadTextures) + { + face_entry["image"] = texture_index[texture]; + face_entry["scales"] = 1.0; + face_entry["scalet"] = 1.0; + face_entry["offsets"] = 0.0; + face_entry["offsett"] = 0.0; + face_entry["imagerot"] = 0.0; + } + face_entry["diffuse_color"] = ll_sd_from_color4(material.mDiffuseColor); + face_entry["fullbright"] = material.mFullbright; + instance_entry["face_list"][face_num] = face_entry; + } + + res["instance_list"][instance_num] = instance_entry; + instance_num++; + } } + if (model_name.empty()) model_name = "mesh model"; + result["name"] = model_name; + if (model_metric.empty()) model_metric = "MUT_Other"; + result["metric"] = model_metric; result["asset_resources"] = res; -#if 1 - dumpLLSDToFile(result,"whole_model.xml"); -#endif + dump_llsd_to_file(result,make_dump_name("whole_model_",dump_num)); dest = result; } -void LLMeshUploadThread::doWholeModelUpload() +void LLMeshUploadThread::generateHulls() { - mCurlRequest = new LLCurlRequest(); - - // Queue up models for hull generation (viewer-side) for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter) { LLMeshUploadData data; @@ -1549,40 +1516,47 @@ void LLMeshUploadThread::doWholeModelUpload() physics = data.mModel[LLModel::LOD_HIGH]; } - if (!physics) - { - llerrs << "WTF?" << llendl; - } - + llassert(physics != NULL); + DecompRequest* request = new DecompRequest(physics, data.mBaseModel, this); - gMeshRepo.mDecompThread->submitRequest(request); + if(request->isValid()) + { + gMeshRepo.mDecompThread->submitRequest(request); + } } while (!mPhysicsComplete) + { + apr_sleep(100); + } +} + +void LLMeshUploadThread::doWholeModelUpload() +{ + mCurlRequest = new LLCurlRequest(); + + if (mWholeModelUploadURL.empty()) { - apr_sleep(100); + llinfos << "unable to upload, fee request failed" << llendl; } - - bool do_include_textures = false; // not needed for initial cost/validation check. - LLSD model_data; - wholeModelToLLSD(model_data, do_include_textures); - - mPendingUploads++; - LLCurlRequest::headers_t headers; - mCurlRequest->post(mWholeModelFeeCapability, headers, model_data, - new LLWholeModelFeeResponder(this)); - - do + else { - mCurlRequest->process(); - } while (mCurlRequest->getQueued() > 0); + generateHulls(); - mCurlRequest->post(mWholeModelUploadURL, headers, model_data["asset_resources"], new LLWholeModelUploadResponder(this)); - - do - { - mCurlRequest->process(); - } while (mCurlRequest->getQueued() > 0); + LLSD full_model_data; + wholeModelToLLSD(full_model_data, true); + LLSD body = full_model_data["asset_resources"]; + dump_llsd_to_file(body,make_dump_name("whole_model_body_",dump_num)); + LLCurlRequest::headers_t headers; + mCurlRequest->post(mWholeModelUploadURL, headers, body, + new LLWholeModelUploadResponder(this, full_model_data, mUploadObserverHandle), mMeshUploadTimeOut); + do + { + mCurlRequest->process(); + //sleep for 10ms to prevent eating a whole core + apr_sleep(10000); + } while (mCurlRequest->getQueued() > 0); + } delete mCurlRequest; mCurlRequest = NULL; @@ -1591,182 +1565,49 @@ void LLMeshUploadThread::doWholeModelUpload() mFinished = true; } -void LLMeshUploadThread::doIterativeUpload() +void LLMeshUploadThread::requestWholeModelFee() { - if(isDiscarded()) - { - mFinished = true; - return ; - } - - mCurlRequest = new LLCurlRequest(); + dump_num++; - std::set textures; + mCurlRequest = new LLCurlRequest(); - //populate upload queue with relevant models - for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter) - { - LLMeshUploadData data; - data.mBaseModel = iter->first; + generateHulls(); - LLModelInstance& instance = *(iter->second.begin()); + LLSD model_data; + wholeModelToLLSD(model_data,false); + dump_llsd_to_file(model_data,make_dump_name("whole_model_fee_request_",dump_num)); - for (S32 i = 0; i < 5; i++) - { - data.mModel[i] = instance.mLOD[i]; - } + mPendingUploads++; + LLCurlRequest::headers_t headers; + mCurlRequest->post(mWholeModelFeeCapability, headers, model_data, + new LLWholeModelFeeResponder(this,model_data, mFeeObserverHandle), mMeshUploadTimeOut); - uploadModel(data); - - if (mUploadTextures) - { - for (std::vector::iterator material_iter = instance.mMaterial.begin(); - material_iter != instance.mMaterial.end(); ++material_iter) - { - - if (textures.find(material_iter->mDiffuseMap.get()) == textures.end()) - { - textures.insert(material_iter->mDiffuseMap.get()); - - LLTextureUploadData data(material_iter->mDiffuseMap.get(), material_iter->mDiffuseMapLabel); - uploadTexture(data); - } - } - } - - //queue up models for hull generation - DecompRequest* request = new DecompRequest(data.mModel[LLModel::LOD_HIGH], data.mBaseModel, this); - gMeshRepo.mDecompThread->submitRequest(request); - } - - while (!mPhysicsComplete) - { - apr_sleep(100); - } - - //upload textures - bool done = false; do { - if (!mTextureQ.empty()) - { - sendCostRequest(mTextureQ.front()); - mTextureQ.pop(); - } - - if (!mConfirmedTextureQ.empty()) - { - doUploadTexture(mConfirmedTextureQ.front()); - mConfirmedTextureQ.pop(); - } - mCurlRequest->process(); - - done = mTextureQ.empty() && mConfirmedTextureQ.empty(); - } - while (!done || mCurlRequest->getQueued() > 0); - - LLSD object_asset; - object_asset["objects"] = LLSD::emptyArray(); - - done = false; - do - { - static S32 count = 0; - static F32 last_hundred = gFrameTimeSeconds; - if (gFrameTimeSeconds - last_hundred > 1.f) - { - last_hundred = gFrameTimeSeconds; - count = 0; - } - - //how many requests to push before calling process - const S32 PUSH_PER_PROCESS = 32; - - S32 tcount = llmin(count+PUSH_PER_PROCESS, 100); - - while (!mUploadQ.empty() && count < tcount) - { //send any pending upload requests - mMutex->lock(); - LLMeshUploadData data = mUploadQ.front(); - mUploadQ.pop(); - mMutex->unlock(); - sendCostRequest(data); - count++; - } - - tcount = llmin(count+PUSH_PER_PROCESS, 100); - - while (!mConfirmedQ.empty() && count < tcount) - { //process any meshes that have been confirmed for upload - LLMeshUploadData& data = mConfirmedQ.front(); - doUploadModel(data); - mConfirmedQ.pop(); - count++; - } - - tcount = llmin(count+PUSH_PER_PROCESS, 100); - - while (!mInstanceQ.empty() && count < tcount && !isDiscarded()) - { //create any objects waiting for upload - count++; - object_asset["objects"].append(createObject(mInstanceQ.front())); - mInstanceQ.pop(); - } - - mCurlRequest->process(); - - done = isDiscarded() || (mInstanceQ.empty() && mConfirmedQ.empty() && mUploadQ.empty()); - } - while (!done || mCurlRequest->getQueued() > 0); + //sleep for 10ms to prevent eating a whole core + apr_sleep(10000); + } while (mCurlRequest->getQueued() > 0); delete mCurlRequest; mCurlRequest = NULL; - // now upload the object asset - std::string url = mUploadObjectAssetCapability; - - if (object_asset["objects"][0].has("permissions")) - { //copy permissions from first available object to be used for coalesced object - object_asset["permissions"] = object_asset["objects"][0]["permissions"]; - } - - if(!isDiscarded()) - { - mPendingUploads++; - LLHTTPClient::post(url, object_asset, new LLModelObjectUploadResponder(this,object_asset)); - } - else - { - mFinished = true; - } + // Currently a no-op. + mFinished = true; } +#endif //MESH_IMPORT -void LLMeshUploadThread::uploadModel(LLMeshUploadData& data) -{ //called from arbitrary thread - { - LLMutexLock lock(mMutex); - mUploadQ.push(data); - } -} - -void LLMeshUploadThread::uploadTexture(LLTextureUploadData& data) -{ //called from mesh upload thread - mTextureQ.push(data); -} - - -static LLFastTimer::DeclareTimer FTM_NOTIFY_MESH_LOADED("Notify Loaded"); -static LLFastTimer::DeclareTimer FTM_NOTIFY_MESH_UNAVAILABLE("Notify Unavailable"); +//static LLFastTimer::DeclareTimer FTM_NOTIFY_MESH_LOADED("Notify Loaded"); +//static LLFastTimer::DeclareTimer FTM_NOTIFY_MESH_UNAVAILABLE("Notify Unavailable"); void LLMeshRepoThread::notifyLoadedMeshes() -{ +{//called via gMeshRepo.notifyLoadedMeshes(). mMutex already locked while (!mLoadedQ.empty()) { - mMutex->lock(); + //mMutex->lock(); LoadedMesh mesh = mLoadedQ.front(); mLoadedQ.pop(); - mMutex->unlock(); + //mMutex->unlock(); if (mesh.mVolume && mesh.mVolume->getNumVolumeFaces() > 0) { @@ -1781,10 +1622,10 @@ void LLMeshRepoThread::notifyLoadedMeshes() while (!mUnavailableQ.empty()) { - mMutex->lock(); + //mMutex->lock(); LODRequest req = mUnavailableQ.front(); mUnavailableQ.pop(); - mMutex->unlock(); + //mMutex->unlock(); gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD); } @@ -1822,7 +1663,9 @@ S32 LLMeshRepository::getActualMeshLOD(LLSD& header, S32 lod) { lod = llclamp(lod, 0, 3); - if (header.has("404")) + S32 version = header["version"]; + + if (header.has("404") || version > MAX_MESH_VERSION) { return -1; } @@ -1855,19 +1698,7 @@ S32 LLMeshRepository::getActualMeshLOD(LLSD& header, S32 lod) return -1; } -U32 LLMeshRepoThread::getResourceCost(const LLUUID& mesh_id) -{ - LLMutexLock lock(mHeaderMutex); - - std::map::iterator iter = mMeshResourceCost.find(mesh_id); - if (iter != mMeshResourceCost.end()) - { - return iter->second; - } - - return 0; -} - +#if MESH_IMPORT void LLMeshRepository::cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header) { mThread->mMeshHeader[data.mUUID] = header; @@ -1887,6 +1718,7 @@ void LLMeshRepository::cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header) } } +#endif //MESH_IMPORT void LLMeshLODResponder::completedRaw(U32 status, const std::string& reason, const LLChannelDescriptors& channels, @@ -1901,7 +1733,7 @@ void LLMeshLODResponder::completedRaw(U32 status, const std::string& reason, llwarns << status << ": " << reason << llendl; } - if (data_size < mRequestedBytes) + if (data_size < (S32)mRequestedBytes) { if (status == 499 || status == 503) { //timeout or service unavailable, try again @@ -1955,7 +1787,7 @@ void LLMeshSkinInfoResponder::completedRaw(U32 status, const std::string& reason llwarns << status << ": " << reason << llendl; } - if (data_size < mRequestedBytes) + if (data_size < (S32)mRequestedBytes) { if (status == 499 || status == 503) { //timeout or service unavailable, try again @@ -2009,7 +1841,7 @@ void LLMeshDecompositionResponder::completedRaw(U32 status, const std::string& r llwarns << status << ": " << reason << llendl; } - if (data_size < mRequestedBytes) + if (data_size < (S32)mRequestedBytes) { if (status == 499 || status == 503) { //timeout or service unavailable, try again @@ -2063,7 +1895,7 @@ void LLMeshPhysicsShapeResponder::completedRaw(U32 status, const std::string& re llwarns << status << ": " << reason << llendl; } - if (data_size < mRequestedBytes) + if (data_size < (S32)mRequestedBytes) { if (status == 499 || status == 503) { //timeout or service unavailable, try again @@ -2158,54 +1990,54 @@ void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason, LLUUID mesh_id = mMeshParams.getSculptID(); LLSD header = gMeshRepo.mThread->mMeshHeader[mesh_id]; - std::stringstream str; + S32 version = header["version"].asInteger(); - S32 lod_bytes = 0; - - for (U32 i = 0; i < LLModel::LOD_PHYSICS; ++i) - { //figure out how many bytes we'll need to reserve in the file - std::string lod_name = header_lod[i]; - lod_bytes = llmax(lod_bytes, header[lod_name]["offset"].asInteger()+header[lod_name]["size"].asInteger()); - } - - //just in case skin info or decomposition is at the end of the file (which it shouldn't be) - lod_bytes = llmax(lod_bytes, header["skin"]["offset"].asInteger() + header["skin"]["size"].asInteger()); - lod_bytes = llmax(lod_bytes, header["decomposition"]["offset"].asInteger() + header["decomposition"]["size"].asInteger()); - - S32 header_bytes = (S32) gMeshRepo.mThread->mMeshHeaderSize[mesh_id]; - S32 bytes = lod_bytes + header_bytes; - - - //it's possible for the remote asset to have more data than is needed for the local cache - //only allocate as much space in the VFS as is needed for the local cache - data_size = llmin(data_size, bytes); - - LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE); - if (file.getMaxSize() >= bytes || file.setMaxSize(bytes)) + if (version <= MAX_MESH_VERSION) { - LLMeshRepository::sCacheBytesWritten += data_size; + std::stringstream str; - file.write((const U8*) data, data_size); + S32 lod_bytes = 0; + + for (U32 i = 0; i < LLModel::LOD_PHYSICS; ++i) + { //figure out how many bytes we'll need to reserve in the file + std::string lod_name = header_lod[i]; + lod_bytes = llmax(lod_bytes, header[lod_name]["offset"].asInteger()+header[lod_name]["size"].asInteger()); + } + + //just in case skin info or decomposition is at the end of the file (which it shouldn't be) + lod_bytes = llmax(lod_bytes, header["skin"]["offset"].asInteger() + header["skin"]["size"].asInteger()); + lod_bytes = llmax(lod_bytes, header["physics_convex"]["offset"].asInteger() + header["physics_convex"]["size"].asInteger()); + + S32 header_bytes = (S32) gMeshRepo.mThread->mMeshHeaderSize[mesh_id]; + S32 bytes = lod_bytes + header_bytes; + + + //it's possible for the remote asset to have more data than is needed for the local cache + //only allocate as much space in the VFS as is needed for the local cache + data_size = llmin(data_size, bytes); + + LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE); + if (file.getMaxSize() >= bytes || file.setMaxSize(bytes)) + { + LLMeshRepository::sCacheBytesWritten += data_size; + + file.write((const U8*) data, data_size); - //zero out the rest of the file - U8 block[4096]; - memset(block, 0, 4096); + //zero out the rest of the file + U8 block[4096]; + memset(block, 0, 4096); - while (bytes-file.tell() > 4096) - { - file.write(block, 4096); - } + while (bytes-file.tell() > 4096) + { + file.write(block, 4096); + } - S32 remaining = bytes-file.tell(); + S32 remaining = bytes-file.tell(); - if (remaining < 0 || remaining > 4096) - { - llerrs << "Bad padding of mesh asset cache entry." << llendl; - } - - if (remaining > 0) - { - file.write(block, remaining); + if (remaining > 0) + { + file.write(block, remaining); + } } } } @@ -2224,9 +2056,11 @@ LLMeshRepository::LLMeshRepository() void LLMeshRepository::init() { - mMeshMutex = new LLMutex(NULL); + mMeshMutex = new LLMutex; +#if MESH_IMPORT LLConvexDecomposition::getInstance()->initSystem(); +#endif //MESH_IMPORT mDecompThread = new LLPhysicsDecomp(); mDecompThread->start(); @@ -2246,11 +2080,13 @@ void LLMeshRepository::shutdown() { llinfos << "Shutting down mesh repository." << llendl; +#if MESH_IMPORT for (U32 i = 0; i < mUploads.size(); ++i) { llinfos << "Discard the pending mesh uploads " << llendl; mUploads[i]->discard() ; //discard the uploading requests. } +#endif //MESH_IMPORT mThread->mSignal->signal(); @@ -2261,6 +2097,7 @@ void LLMeshRepository::shutdown() delete mThread; mThread = NULL; +#if MESH_IMPORT for (U32 i = 0; i < mUploads.size(); ++i) { llinfos << "Waiting for pending mesh upload " << i << "/" << mUploads.size() << llendl; @@ -2272,6 +2109,7 @@ void LLMeshRepository::shutdown() } mUploads.clear(); +#endif //MESH_IMPORT delete mMeshMutex; mMeshMutex = NULL; @@ -2285,18 +2123,22 @@ void LLMeshRepository::shutdown() mDecompThread = NULL; } +#if MESH_IMPORT LLConvexDecomposition::quitSystem(); +#endif //MESH_IMPORT } //called in the main thread. S32 LLMeshRepository::update() { + S32 size = 0; +#if MESH_IMPORT if(mUploadWaitList.empty()) { return 0 ; } - S32 size = mUploadWaitList.size() ; + size = mUploadWaitList.size() ; for (S32 i = 0; i < size; ++i) { mUploads.push_back(mUploadWaitList[i]); @@ -2304,7 +2146,7 @@ S32 LLMeshRepository::update() mUploadWaitList[i]->start() ; } mUploadWaitList.clear() ; - +#endif //MESH_IMPORT return size ; } @@ -2315,7 +2157,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para return detail; } - LLFastTimer t(FTM_LOAD_MESH); + //LLFastTimer t(LLFastTimer::FTM_LOAD_MESH); { LLMutexLock lock(mMeshMutex); @@ -2387,25 +2229,21 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para group->derefLOD(lod); } } - else - { - llerrs << "WTF?" << llendl; - } } return detail; } -static LLFastTimer::DeclareTimer FTM_START_MESH_THREAD("Start Thread"); +/*static LLFastTimer::DeclareTimer FTM_START_MESH_THREAD("Start Thread"); static LLFastTimer::DeclareTimer FTM_LOAD_MESH_LOD("Load LOD"); static LLFastTimer::DeclareTimer FTM_MESH_LOCK1("Lock 1"); -static LLFastTimer::DeclareTimer FTM_MESH_LOCK2("Lock 2"); +static LLFastTimer::DeclareTimer FTM_MESH_LOCK2("Lock 2");*/ void LLMeshRepository::notifyLoadedMeshes() { //called from main thread - LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests"); +#if MESH_IMPORT //clean up completed upload threads for (std::vector::iterator iter = mUploads.begin(); iter != mUploads.end(); ) { @@ -2433,6 +2271,38 @@ void LLMeshRepository::notifyLoadedMeshes() LLAssetType::EType asset_type = LLAssetType::lookup(data.mPostData["asset_type"].asString()); LLInventoryType::EType inventory_type = LLInventoryType::lookup(data.mPostData["inventory_type"].asString()); + // Handle addition of texture, if any. + if ( data.mResponse.has("new_texture_folder_id") ) + { + const LLUUID& folder_id = data.mResponse["new_texture_folder_id"].asUUID(); + + if ( folder_id.notNull() ) + { + LLUUID parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TEXTURE); + + std::string name; + // Check if the server built a different name for the texture folder + if ( data.mResponse.has("new_texture_folder_name") ) + { + name = data.mResponse["new_texture_folder_name"].asString(); + } + else + { + name = data.mPostData["name"].asString(); + } + + // Add the category to the internal representation + LLPointer cat = + new LLViewerInventoryCategory(folder_id, parent_id, + LLFolderType::FT_NONE, name, gAgent.getID()); + cat->setVersion(LLViewerInventoryCategory::VERSION_UNKNOWN); + + LLInventoryModel::LLCategoryUpdate update(cat->getParentUUID(), 1); + gInventory.accountForUpdate(update); + gInventory.updateCategory(cat); + } + } + on_new_single_inventory_upload_complete( asset_type, inventory_type, @@ -2441,15 +2311,18 @@ void LLMeshRepository::notifyLoadedMeshes() data.mPostData["name"], data.mPostData["description"], data.mResponse, - 0); + data.mResponse["upload_price"]); + //} mInventoryQ.pop(); } } +#endif //MESH_IMPORT + //call completed callbacks on finished decompositions mDecompThread->notifyCompleted(); - + if (!mThread->mWaiting) { //curl thread is churning, wait for it to go idle return; @@ -2467,22 +2340,22 @@ void LLMeshRepository::notifyLoadedMeshes() } } - LLFastTimer t(FTM_MESH_UPDATE); + LLFastTimer t(LLFastTimer::FTM_MESH_UPDATE); { - LLFastTimer t(FTM_MESH_LOCK1); + LLFastTimer t(LLFastTimer::FTM_MESH_LOCK1); mMeshMutex->lock(); } { - LLFastTimer t(FTM_MESH_LOCK2); + LLFastTimer t(LLFastTimer::FTM_MESH_LOCK2); mThread->mMutex->lock(); } //popup queued error messages from background threads while (!mUploadErrorQ.empty()) { - LLNotificationsUtil::add("MeshUploadError", mUploadErrorQ.front()); + LLNotifications::instance().add("MeshUploadError", mUploadErrorQ.front()); mUploadErrorQ.pop(); } @@ -2530,9 +2403,9 @@ void LLMeshRepository::notifyLoadedMeshes() while (!mPendingRequests.empty() && push_count > 0) { - LLFastTimer t(FTM_LOAD_MESH_LOD); + LLFastTimer t(LLFastTimer::FTM_LOAD_MESH_LOD); LLMeshRepoThread::LODRequest& request = mPendingRequests.front(); - mThread->loadMeshLOD(request.mMeshParams, request.mLOD); + mThread->loadMeshLOD(request.mMeshParams, request.mLOD, false); mPendingRequests.erase(mPendingRequests.begin()); push_count--; } @@ -2570,6 +2443,20 @@ void LLMeshRepository::notifyLoadedMeshes() void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo& info) { mSkinMap[info.mMeshID] = info; + + skin_load_map::iterator iter = mLoadingSkins.find(info.mMeshID); + if (iter != mLoadingSkins.end()) + { + for (std::set::iterator obj_id = iter->second.begin(); obj_id != iter->second.end(); ++obj_id) + { + LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*obj_id); + if (vobj) + { + vobj->notifyMeshLoaded(); + } + } + } + mLoadingSkins.erase(info.mMeshID); } @@ -2666,26 +2553,7 @@ S32 LLMeshRepository::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lo return mThread->getActualMeshLOD(mesh_params, lod); } -U32 LLMeshRepository::calcResourceCost(LLSD& header) -{ - U32 bytes = 0; - - for (U32 i = 0; i < 4; i++) - { - bytes += header[header_lod[i]]["size"].asInteger(); - } - - bytes += header["skin"]["size"].asInteger(); - - return bytes/4096 + 1; -} - -U32 LLMeshRepository::getResourceCost(const LLUUID& mesh_id) -{ - return mThread->getResourceCost(mesh_id); -} - -const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id) +const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, LLVOVolume* requesting_obj) { if (mesh_id.notNull()) { @@ -2699,12 +2567,12 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id) { LLMutexLock lock(mMeshMutex); //add volume to list of loading meshes - std::set::iterator iter = mLoadingSkins.find(mesh_id); + skin_load_map::iterator iter = mLoadingSkins.find(mesh_id); if (iter == mLoadingSkins.end()) { //no request pending for this skin info - mLoadingSkins.insert(mesh_id); mPendingSkinRequests.push(mesh_id); } + mLoadingSkins[mesh_id].insert(requesting_obj->getID()); } } @@ -2769,7 +2637,7 @@ LLModel::Decomposition* LLMeshRepository::getDecomposition(const LLUUID& mesh_id void LLMeshRepository::buildHull(const LLVolumeParams& params, S32 detail) { - LLVolume* volume = LLPrimitive::sVolumeManager->refVolume(params, detail); + LLVolume* volume = LLPrimitive::getVolumeManager()->refVolume(params, detail); if (!volume->mHullPoints) { @@ -2780,13 +2648,24 @@ void LLMeshRepository::buildHull(const LLVolumeParams& params, S32 detail) //run second stage } - LLPrimitive::sVolumeManager->unrefVolume(volume); + LLPrimitive::getVolumeManager()->unrefVolume(volume); } bool LLMeshRepository::hasPhysicsShape(const LLUUID& mesh_id) { LLSD mesh = mThread->getMeshHeader(mesh_id); - return mesh.has("physics_shape") && mesh["physics_shape"].has("size") && (mesh["physics_shape"]["size"].asInteger() > 0); + if (mesh.has("physics_mesh") && mesh["physics_mesh"].has("size") && (mesh["physics_mesh"]["size"].asInteger() > 0)) + { + return true; + } + + LLModel::Decomposition* decomp = getDecomposition(mesh_id); + if (decomp && !decomp->mHull.empty()) + { + return true; + } + + return false; } LLSD& LLMeshRepository::getMeshHeader(const LLUUID& mesh_id) @@ -2810,13 +2689,16 @@ LLSD& LLMeshRepoThread::getMeshHeader(const LLUUID& mesh_id) return dummy_ret; } - +#if MESH_IMPORT void LLMeshRepository::uploadModel(std::vector& data, LLVector3& scale, bool upload_textures, - bool upload_skin, bool upload_joints) + bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload, + LLHandle fee_observer, LLHandle upload_observer) { - LLMeshUploadThread* thread = new LLMeshUploadThread(data, scale, upload_textures, upload_skin, upload_joints); + LLMeshUploadThread* thread = new LLMeshUploadThread(data, scale, upload_textures, upload_skin, upload_joints, upload_url, + do_upload, fee_observer, upload_observer); mUploadWaitList.push_back(thread); } +#endif //MESH_IMPORT S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod) { @@ -2842,193 +2724,7 @@ S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod) } -void LLMeshUploadThread::sendCostRequest(LLMeshUploadData& data) -{ - if(isDiscarded()) - { - return ; - } - - //write model file to memory buffer - std::stringstream ostr; - - LLModel::Decomposition& decomp = - data.mModel[LLModel::LOD_PHYSICS].notNull() ? - data.mModel[LLModel::LOD_PHYSICS]->mPhysics : - data.mBaseModel->mPhysics; - - LLSD header = LLModel::writeModel( - ostr, - data.mModel[LLModel::LOD_PHYSICS], - data.mModel[LLModel::LOD_HIGH], - data.mModel[LLModel::LOD_MEDIUM], - data.mModel[LLModel::LOD_LOW], - data.mModel[LLModel::LOD_IMPOSTOR], - decomp, - mUploadSkin, - mUploadJoints, - true); - - std::string desc = data.mBaseModel->mLabel; - - // Grab the total vertex count of the model - // along with other information for the "asset_resources" map - // to send to the server. - LLSD asset_resources = LLSD::emptyMap(); - - - std::string url = mNewInventoryCapability; - - if (!url.empty()) - { - LLSD body = generate_new_resource_upload_capability_body( - LLAssetType::AT_MESH, - desc, - desc, - LLFolderType::FT_MESH, - LLInventoryType::IT_MESH, - LLFloaterPerms::getNextOwnerPerms(), - LLFloaterPerms::getGroupPerms(), - LLFloaterPerms::getEveryonePerms()); - - body["asset_resources"] = asset_resources; - - mPendingConfirmations++; - LLCurlRequest::headers_t headers; - - data.mPostData = body; - - mCurlRequest->post(url, headers, body, new LLMeshCostResponder(data, this)); - } -} - -void LLMeshUploadThread::sendCostRequest(LLTextureUploadData& data) -{ - if(isDiscarded()) - { - return ; - } - - if (data.mTexture && data.mTexture->getDiscardLevel() >= 0) - { - LLSD asset_resources = LLSD::emptyMap(); - - std::string url = mNewInventoryCapability; - - if (!url.empty()) - { - LLSD body = generate_new_resource_upload_capability_body( - LLAssetType::AT_TEXTURE, - data.mLabel, - data.mLabel, - LLFolderType::FT_TEXTURE, - LLInventoryType::IT_TEXTURE, - LLFloaterPerms::getNextOwnerPerms(), - LLFloaterPerms::getGroupPerms(), - LLFloaterPerms::getEveryonePerms()); - - body["asset_resources"] = asset_resources; - - mPendingConfirmations++; - LLCurlRequest::headers_t headers; - - data.mPostData = body; - mCurlRequest->post(url, headers, body, new LLTextureCostResponder(data, this)); - } - } -} - - -void LLMeshUploadThread::doUploadModel(LLMeshUploadData& data) -{ - if(isDiscarded()) - { - return ; - } - - if (!data.mRSVP.empty()) - { - std::stringstream ostr; - - LLModel::Decomposition& decomp = - data.mModel[LLModel::LOD_PHYSICS].notNull() ? - data.mModel[LLModel::LOD_PHYSICS]->mPhysics : - data.mBaseModel->mPhysics; - - decomp.mBaseHull = mHullMap[data.mBaseModel]; - - LLModel::writeModel( - ostr, - data.mModel[LLModel::LOD_PHYSICS], - data.mModel[LLModel::LOD_HIGH], - data.mModel[LLModel::LOD_MEDIUM], - data.mModel[LLModel::LOD_LOW], - data.mModel[LLModel::LOD_IMPOSTOR], - decomp, - mUploadSkin, - mUploadJoints); - - data.mAssetData = ostr.str(); - - LLCurlRequest::headers_t headers; - mPendingUploads++; - - mCurlRequest->post(data.mRSVP, headers, data.mAssetData, new LLMeshUploadResponder(data, this)); - } -} - -void LLMeshUploadThread::doUploadTexture(LLTextureUploadData& data) -{ - if(isDiscarded()) - { - return ; - } - - if (!data.mRSVP.empty()) - { - std::stringstream ostr; - - if (!data.mTexture->isRawImageValid()) - { - data.mTexture->reloadRawImage(data.mTexture->getDiscardLevel()); - } - - LLPointer upload_file = LLViewerTextureList::convertToUploadFile(data.mTexture->getRawImage()); - - ostr.write((const char*) upload_file->getData(), upload_file->getDataSize()); - - data.mAssetData = ostr.str(); - - LLCurlRequest::headers_t headers; - mPendingUploads++; - - mCurlRequest->post(data.mRSVP, headers, data.mAssetData, new LLTextureUploadResponder(data, this)); - } -} - - -void LLMeshUploadThread::onModelUploaded(LLMeshUploadData& data) -{ - createObjects(data); -} - -void LLMeshUploadThread::onTextureUploaded(LLTextureUploadData& data) -{ - mTextureMap[data.mTexture] = data; -} - - -void LLMeshUploadThread::createObjects(LLMeshUploadData& data) -{ - instance_list& instances = mInstance[data.mBaseModel]; - - for (instance_list::iterator iter = instances.begin(); iter != instances.end(); ++iter) - { //create prims that reference given mesh - LLModelInstance& instance = *iter; - instance.mMeshID = data.mUUID; - mInstanceQ.push(instance); - } -} +#if MESH_IMPORT void LLMeshUploadThread::decomposeMeshMatrix(LLMatrix4& transformation, LLVector3& result_pos, @@ -3070,149 +2766,7 @@ void LLMeshUploadThread::decomposeMeshMatrix(LLMatrix4& transformation, result_rot = quat_rotation; } - -LLSD LLMeshUploadThread::createObject(LLModelInstance& instance) -{ - LLMatrix4 transformation = instance.mTransform; - - if (instance.mMeshID.isNull()) - { - llerrs << "WTF?" << llendl; - } - - // check for reflection - BOOL reflected = (transformation.determinant() < 0); - - // compute position - LLVector3 position = LLVector3(0, 0, 0) * transformation; - - // compute scale - LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position; - LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position; - LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position; - F32 x_length = x_transformed.normalize(); - F32 y_length = y_transformed.normalize(); - F32 z_length = z_transformed.normalize(); - LLVector3 scale = LLVector3(x_length, y_length, z_length); - - // adjust for "reflected" geometry - LLVector3 x_transformed_reflected = x_transformed; - if (reflected) - { - x_transformed_reflected *= -1.0; - } - - // compute rotation - LLMatrix3 rotation_matrix; - rotation_matrix.setRows(x_transformed_reflected, y_transformed, z_transformed); - LLQuaternion quat_rotation = rotation_matrix.quaternion(); - quat_rotation.normalize(); // the rotation_matrix might not have been orthoginal. make it so here. - LLVector3 euler_rotation; - quat_rotation.getEulerAngles(&euler_rotation.mV[VX], &euler_rotation.mV[VY], &euler_rotation.mV[VZ]); - - // - // build parameter block to construct this prim - // - - LLSD object_params; - - // create prim - - // set volume params - U8 sculpt_type = LL_SCULPT_TYPE_MESH; - if (reflected) - { - sculpt_type |= LL_SCULPT_FLAG_MIRROR; - } - LLVolumeParams volume_params; - volume_params.setType( LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE ); - volume_params.setBeginAndEndS( 0.f, 1.f ); - volume_params.setBeginAndEndT( 0.f, 1.f ); - volume_params.setRatio ( 1, 1 ); - volume_params.setShear ( 0, 0 ); - volume_params.setSculptID(instance.mMeshID, sculpt_type); - object_params["shape"] = volume_params.asLLSD(); - - object_params["material"] = LL_MCODE_WOOD; - - object_params["group-id"] = gAgent.getGroupID(); - object_params["pos"] = ll_sd_from_vector3(position + mOrigin); - object_params["rotation"] = ll_sd_from_quaternion(quat_rotation); - object_params["scale"] = ll_sd_from_vector3(scale); - object_params["name"] = instance.mLabel; - - // load material from dae file - object_params["facelist"] = LLSD::emptyArray(); - for (S32 i = 0; i < instance.mMaterial.size(); i++) - { - LLTextureEntry te; - LLImportMaterial& mat = instance.mMaterial[i]; - - te.setColor(mat.mDiffuseColor); - - LLUUID diffuse_id = mTextureMap[mat.mDiffuseMap].mUUID; - - if (diffuse_id.notNull()) - { - te.setID(diffuse_id); - } - else - { - te.setID(LLUUID("5748decc-f629-461c-9a36-a35a221fe21f")); // blank texture - } - - te.setFullbright(mat.mFullbright); - - object_params["facelist"][i] = te.asLLSD(); - } - - // set extra parameters - LLSculptParams sculpt_params; - sculpt_params.setSculptTexture(instance.mMeshID); - sculpt_params.setSculptType(sculpt_type); - U8 buffer[MAX_OBJECT_PARAMS_SIZE+1]; - LLDataPackerBinaryBuffer dp(buffer, MAX_OBJECT_PARAMS_SIZE); - sculpt_params.pack(dp); - std::vector v(dp.getCurrentSize()); - memcpy(&v[0], buffer, dp.getCurrentSize()); - LLSD extra_parameter; - extra_parameter["extra_parameter"] = sculpt_params.mType; - extra_parameter["param_data"] = v; - object_params["extra_parameters"].append(extra_parameter); - - LLPermissions perm; - perm.setOwnerAndGroup(gAgent.getID(), gAgent.getID(), LLUUID::null, false); - perm.setCreator(gAgent.getID()); - - perm.initMasks(PERM_ITEM_UNRESTRICTED | PERM_MOVE, //base - PERM_ITEM_UNRESTRICTED | PERM_MOVE, //owner - LLFloaterPerms::getEveryonePerms(), - LLFloaterPerms::getGroupPerms(), - LLFloaterPerms::getNextOwnerPerms()); - - object_params["permissions"] = ll_create_sd_from_permissions(perm); - - object_params["physics_shape_type"] = (U8)(LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL); - - return object_params; -} - -void LLMeshUploadThread::priceResult(LLMeshUploadData& data, const LLSD& content) -{ - mPendingCost += content["upload_price"].asInteger(); - data.mRSVP = content["rsvp"].asString(); - - mConfirmedQ.push(data); -} - -void LLMeshUploadThread::priceResult(LLTextureUploadData& data, const LLSD& content) -{ - mPendingCost += content["upload_price"].asInteger(); - data.mRSVP = content["rsvp"].asString(); - - mConfirmedTextureQ.push(data); -} - +#endif //MESH_IMPORT bool LLImportMaterial::operator<(const LLImportMaterial &rhs) const { @@ -3236,6 +2790,11 @@ bool LLImportMaterial::operator<(const LLImportMaterial &rhs) const return mDiffuseColor < rhs.mDiffuseColor; } + if (mBinding != rhs.mBinding) + { + return mBinding < rhs.mBinding; + } + return mFullbright < rhs.mFullbright; } @@ -3243,6 +2802,8 @@ bool LLImportMaterial::operator<(const LLImportMaterial &rhs) const void LLMeshRepository::updateInventory(inventory_data data) { LLMutexLock lock(mMeshMutex); + dump_llsd_to_file(data.mPostData,make_dump_name("update_inventory_post_data_",dump_num)); + dump_llsd_to_file(data.mResponse,make_dump_name("update_inventory_response_",dump_num)); mInventoryQ.push(data); } @@ -3255,14 +2816,46 @@ void LLMeshRepository::uploadError(LLSD& args) //static F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32* bytes_visible, S32 lod) { - F32 dlowest = llmin(radius/0.03f, 256.f); - F32 dlow = llmin(radius/0.06f, 256.f); - F32 dmid = llmin(radius/0.24f, 256.f); + F32 max_distance = 512.f; + + F32 dlowest = llmin(radius/0.03f, max_distance); + F32 dlow = llmin(radius/0.06f, max_distance); + F32 dmid = llmin(radius/0.24f, max_distance); - F32 bytes_lowest = header["lowest_lod"]["size"].asReal()/1024.f; - F32 bytes_low = header["low_lod"]["size"].asReal()/1024.f; - F32 bytes_mid = header["medium_lod"]["size"].asReal()/1024.f; - F32 bytes_high = header["high_lod"]["size"].asReal()/1024.f; + F32 METADATA_DISCOUNT = (F32) gSavedSettings.getU32("MeshMetaDataDiscount"); //discount 128 bytes to cover the cost of LLSD tags and compression domain overhead + F32 MINIMUM_SIZE = (F32) gSavedSettings.getU32("MeshMinimumByteSize"); //make sure nothing is "free" + + F32 bytes_per_triangle = (F32) gSavedSettings.getU32("MeshBytesPerTriangle"); + + S32 bytes_lowest = header["lowest_lod"]["size"].asInteger(); + S32 bytes_low = header["low_lod"]["size"].asInteger(); + S32 bytes_mid = header["medium_lod"]["size"].asInteger(); + S32 bytes_high = header["high_lod"]["size"].asInteger(); + + if (bytes_high == 0) + { + return 0.f; + } + + if (bytes_mid == 0) + { + bytes_mid = bytes_high; + } + + if (bytes_low == 0) + { + bytes_low = bytes_mid; + } + + if (bytes_lowest == 0) + { + bytes_lowest = bytes_low; + } + + F32 triangles_lowest = llmax((F32) bytes_lowest-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle; + F32 triangles_low = llmax((F32) bytes_low-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle; + F32 triangles_mid = llmax((F32) bytes_mid-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle; + F32 triangles_high = llmax((F32) bytes_high-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle; if (bytes) { @@ -3273,7 +2866,6 @@ F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32 *bytes += header["high_lod"]["size"].asInteger(); } - if (bytes_visible) { lod = LLMeshRepository::getActualMeshLOD(header, lod); @@ -3283,27 +2875,7 @@ F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32 } } - if (bytes_high == 0.f) - { - return 0.f; - } - - if (bytes_mid == 0.f) - { - bytes_mid = bytes_high; - } - - if (bytes_low == 0.f) - { - bytes_low = bytes_mid; - } - - if (bytes_lowest == 0.f) - { - bytes_lowest = bytes_low; - } - - F32 max_area = 65536.f; + F32 max_area = 102932.f; //area of circle that encompasses region F32 min_area = 1.f; F32 high_area = llmin(F_PI*dmid*dmid, max_area); @@ -3326,12 +2898,12 @@ F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32 low_area /= total_area; lowest_area /= total_area; - F32 weighted_avg = bytes_high*high_area + - bytes_mid*mid_area + - bytes_low*low_area + - bytes_lowest*lowest_area; + F32 weighted_avg = triangles_high*high_area + + triangles_mid*mid_area + + triangles_low*low_area + + triangles_lowest*lowest_area; - return weighted_avg * gSavedSettings.getF32("MeshStreamingCostScaler"); + return weighted_avg/gSavedSettings.getU32("MeshTriangleBudget")*15000.f; } @@ -3342,8 +2914,8 @@ LLPhysicsDecomp::LLPhysicsDecomp() mQuitting = false; mDone = false; - mSignal = new LLCondition(NULL); - mMutex = new LLMutex(NULL); + mSignal = new LLCondition; + mMutex = new LLMutex; } LLPhysicsDecomp::~LLPhysicsDecomp() @@ -3388,32 +2960,41 @@ S32 LLPhysicsDecomp::llcdCallback(const char* status, S32 p1, S32 p2) return 1; } -void LLPhysicsDecomp::setMeshData(LLCDMeshData& mesh) +void LLPhysicsDecomp::setMeshData(LLCDMeshData& mesh, bool vertex_based) { mesh.mVertexBase = mCurRequest->mPositions[0].mV; mesh.mVertexStrideBytes = 12; mesh.mNumVertices = mCurRequest->mPositions.size(); - mesh.mIndexType = LLCDMeshData::INT_16; - mesh.mIndexBase = &(mCurRequest->mIndices[0]); - mesh.mIndexStrideBytes = 6; + if(!vertex_based) + { + mesh.mIndexType = LLCDMeshData::INT_16; + mesh.mIndexBase = &(mCurRequest->mIndices[0]); + mesh.mIndexStrideBytes = 6; - mesh.mNumTriangles = mCurRequest->mIndices.size()/3; - - LLCDResult ret = LLCD_OK; - if (LLConvexDecomposition::getInstance() != NULL) - { - ret = LLConvexDecomposition::getInstance()->setMeshData(&mesh); + mesh.mNumTriangles = mCurRequest->mIndices.size()/3; } - if (ret) +#if MESH_IMPORT + if ((vertex_based || mesh.mNumTriangles > 0) && mesh.mNumVertices > 2) { - llerrs << "Convex Decomposition thread valid but could not set mesh data" << llendl; + LLCDResult ret = LLCD_OK; + if (LLConvexDecomposition::getInstance() != NULL) + { + ret = LLConvexDecomposition::getInstance()->setMeshData(&mesh, vertex_based); + } + + if (ret) + { + llerrs << "Convex Decomposition thread valid but could not set mesh data" << llendl; + } } +#endif //MESH_IMPORT } void LLPhysicsDecomp::doDecomposition() { +#if MESH_IMPORT LLCDMeshData mesh; S32 stage = mStageID[mCurRequest->mStage]; @@ -3426,7 +3007,7 @@ void LLPhysicsDecomp::doDecomposition() //load data intoLLCD if (stage == 0) { - setMeshData(mesh); + setMeshData(mesh, false); } //build parameter map @@ -3472,17 +3053,12 @@ void LLPhysicsDecomp::doDecomposition() { ret = LLConvexDecomposition::getInstance()->setParam(param->mName, value.asBoolean()); } - - if (ret) - { - llerrs << "WTF?" << llendl; - } } mCurRequest->setStatusMessage("Executing."); LLCDResult ret = LLCD_OK; - + if (LLConvexDecomposition::getInstance() != NULL) { ret = LLConvexDecomposition::getInstance()->executeStage(stage); @@ -3552,6 +3128,7 @@ void LLPhysicsDecomp::doDecomposition() completeCurrent(); } } +#endif //MESH_IMPORT } void LLPhysicsDecomp::completeCurrent() @@ -3605,11 +3182,57 @@ void make_box(LLPhysicsDecomp::Request * request) void LLPhysicsDecomp::doDecompositionSingleHull() { - LLCDMeshData mesh; +#if !MESH_IMPORT + return; +#endif //!MESH_IMPORT +#if MESH_IMPORT + + if (decomp == NULL) + { + //stub. do nothing. + return; + } - setMeshData(mesh); + LLCDMeshData mesh; + +#if 1 + setMeshData(mesh, true); + + LLCDResult ret = decomp->buildSingleHull() ; + if(ret) + { + llwarns << "Could not execute decomposition stage when attempting to create single hull." << llendl; + make_box(mCurRequest); + } + + mMutex->lock(); + mCurRequest->mHull.clear(); + mCurRequest->mHull.resize(1); + mCurRequest->mHullMesh.clear(); + mMutex->unlock(); + + std::vector p; + LLCDHull hull; + + // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code + decomp->getSingleHull(&hull); + + const F32* v = hull.mVertexBase; + + for (S32 j = 0; j < hull.mNumVertices; ++j) + { + LLVector3 vert(v[0], v[1], v[2]); + p.push_back(vert); + v = (F32*) (((U8*) v) + hull.mVertexStrideBytes); + } + + mMutex->lock(); + mCurRequest->mHull[0] = p; + mMutex->unlock(); - +#else + setMeshData(mesh, false); + //set all parameters to default std::map param_map; @@ -3618,17 +3241,9 @@ void LLPhysicsDecomp::doDecompositionSingleHull() if (!params) { - param_count = LLConvexDecomposition::getInstance()->getParameters(¶ms); + param_count = decomp->getParameters(¶ms); } - - LLConvexDecomposition* decomp = LLConvexDecomposition::getInstance(); - - if (decomp == NULL) - { - //stub. do nothing. - return; - } - + for (S32 i = 0; i < param_count; ++i) { decomp->setParam(params[i].mName, params[i].mDefault.mIntOrEnumValue); @@ -3696,17 +3311,22 @@ void LLPhysicsDecomp::doDecompositionSingleHull() } } } - +#endif { completeCurrent(); } +#endif //MESH_IMPORT } void LLPhysicsDecomp::run() { +#if !MESH_IMPORT + mInited = true; +#endif //!MESH_IMPORT +#if MESH_IMPORT LLConvexDecomposition* decomp = LLConvexDecomposition::getInstance(); if (decomp == NULL) { @@ -3769,6 +3389,83 @@ void LLPhysicsDecomp::run() } mDone = true; +#endif //MESH_IMPORT +} + + +void LLPhysicsDecomp::Request::assignData(LLModel* mdl) +{ + if (!mdl) + { + return ; + } + + U16 index_offset = 0; + U16 tri[3] ; + + mPositions.clear(); + mIndices.clear(); + mBBox[1] = LLVector3(F32_MIN, F32_MIN, F32_MIN) ; + mBBox[0] = LLVector3(F32_MAX, F32_MAX, F32_MAX) ; + + //queue up vertex positions and indices + for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) + { + const LLVolumeFace& face = mdl->getVolumeFace(i); + if (mPositions.size() + face.mNumVertices > 65535) + { + continue; + } + + for (U32 j = 0; j < (U32)face.mNumVertices; ++j) + { + mPositions.push_back(LLVector3(face.mPositions[j].getF32ptr())); + for(U32 k = 0 ; k < 3 ; k++) + { + mBBox[0].mV[k] = llmin(mBBox[0].mV[k], mPositions[j].mV[k]) ; + mBBox[1].mV[k] = llmax(mBBox[1].mV[k], mPositions[j].mV[k]) ; + } + } + + updateTriangleAreaThreshold() ; + + for (U32 j = 0; j+2 < (U32)face.mNumIndices; j += 3) + { + tri[0] = face.mIndices[j] + index_offset ; + tri[1] = face.mIndices[j + 1] + index_offset ; + tri[2] = face.mIndices[j + 2] + index_offset ; + + if(isValidTriangle(tri[0], tri[1], tri[2])) + { + mIndices.push_back(tri[0]); + mIndices.push_back(tri[1]); + mIndices.push_back(tri[2]); + } + } + + index_offset += face.mNumVertices; + } + + return ; +} + +void LLPhysicsDecomp::Request::updateTriangleAreaThreshold() +{ + F32 range = mBBox[1].mV[0] - mBBox[0].mV[0] ; + range = llmin(range, mBBox[1].mV[1] - mBBox[0].mV[1]) ; + range = llmin(range, mBBox[1].mV[2] - mBBox[0].mV[2]) ; + + mTriangleAreaThreshold = llmin(0.0002f, range * 0.000002f) ; +} + +//check if the triangle area is large enough to qualify for a valid triangle +bool LLPhysicsDecomp::Request::isValidTriangle(U16 idx1, U16 idx2, U16 idx3) +{ + LLVector3 a = mPositions[idx2] - mPositions[idx1] ; + LLVector3 b = mPositions[idx3] - mPositions[idx1] ; + F32 c = a * b ; + + return ((a*a) * (b*b) - c * c) > mTriangleAreaThreshold ; } void LLPhysicsDecomp::Request::setStatusMessage(const std::string& msg) @@ -3782,9 +3479,10 @@ LLModelInstance::LLModelInstance(LLSD& data) mLabel = data["label"].asString(); mTransform.setValue(data["transform"]); - for (U32 i = 0; i < data["material"].size(); ++i) + for (U32 i = 0; i < (U32)data["material"].size(); ++i) { - mMaterial.push_back(LLImportMaterial(data["material"][i])); + LLImportMaterial mat(data["material"][i]); + mMaterial[mat.mBinding] = mat; } } @@ -3797,9 +3495,10 @@ LLSD LLModelInstance::asLLSD() ret["label"] = mLabel; ret["transform"] = mTransform.getValue(); - for (U32 i = 0; i < mMaterial.size(); ++i) + U32 i = 0; + for (std::map::iterator iter = mMaterial.begin(); iter != mMaterial.end(); ++iter) { - ret["material"][i] = mMaterial[i].asLLSD(); + ret["material"][i++] = iter->second.asLLSD(); } return ret; @@ -3811,6 +3510,7 @@ LLImportMaterial::LLImportMaterial(LLSD& data) mDiffuseMapLabel = data["diffuse"]["label"].asString(); mDiffuseColor.setValue(data["diffuse"]["color"]); mFullbright = data["fullbright"].asBoolean(); + mBinding = data["binding"].asString(); } @@ -3822,6 +3522,7 @@ LLSD LLImportMaterial::asLLSD() ret["diffuse"]["label"] = mDiffuseMapLabel; ret["diffuse"]["color"] = mDiffuseColor.getValue(); ret["fullbright"] = mFullbright; + ret["binding"] = mBinding; return ret; } @@ -3838,12 +3539,14 @@ void LLMeshRepository::buildPhysicsMesh(LLModel::Decomposition& decomp) hull.mVertexStrideBytes = 12; LLCDMeshData mesh; +#if MESH_IMPORT LLCDResult res = LLCD_OK; if (LLConvexDecomposition::getInstance() != NULL) { res = LLConvexDecomposition::getInstance()->getMeshFromHull(&hull, &mesh); } if (res == LLCD_OK) +#endif //MESH_IMPORT { get_vertex_buffer_from_mesh(mesh, decomp.mMesh[i]); } @@ -3857,14 +3560,41 @@ void LLMeshRepository::buildPhysicsMesh(LLModel::Decomposition& decomp) hull.mVertexStrideBytes = 12; LLCDMeshData mesh; +#if MESH_IMPORT LLCDResult res = LLCD_OK; if (LLConvexDecomposition::getInstance() != NULL) { res = LLConvexDecomposition::getInstance()->getMeshFromHull(&hull, &mesh); } if (res == LLCD_OK) +#endif //MESH_IMPORT { get_vertex_buffer_from_mesh(mesh, decomp.mBaseHullMesh); } } } + + +bool LLMeshRepository::meshUploadEnabled() +{ + LLViewerRegion *region = gAgent.getRegion(); + if(gSavedSettings.getBOOL("MeshEnabled") && + region) + { + return region->meshUploadEnabled(); + } + return false; +} + +bool LLMeshRepository::meshRezEnabled() +{ + LLViewerRegion *region = gAgent.getRegion(); + if(gSavedSettings.getBOOL("MeshEnabled") && + region) + { + return region->meshRezEnabled(); + } + return false; +} +#endif //MESH_ENABLED + diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index f859e29c0..fe905764d 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -33,9 +33,37 @@ #include "llviewertexture.h" #include "llvolume.h" +#if MESH_IMPORT #define LLCONVEXDECOMPINTER_STATIC 1 #include "llconvexdecomposition.h" +#endif //MESH_IMPORT + +#if !MESH_IMPORT +//Placeholder structs from LLConvexDecomposition.h: +struct LLCDMeshData +{ + enum IndexType + { + INT_16, + INT_32 + }; + + const float* mVertexBase; + int mVertexStrideBytes; + int mNumVertices; + const void* mIndexBase; + IndexType mIndexType; + int mIndexStrideBytes; + int mNumTriangles; +}; +struct LLCDHull +{ + const float* mVertexBase; + int mVertexStrideBytes; + int mNumVertices; +}; +#endif //!MESH_IMPORT class LLVOVolume; class LLMeshResponder; @@ -91,6 +119,7 @@ public: LLPointer mDiffuseMap; std::string mDiffuseMapFilename; std::string mDiffuseMapLabel; + std::string mBinding; LLColor4 mDiffuseColor; bool mFullbright; @@ -119,9 +148,9 @@ public: S32 mLocalMeshID; LLMatrix4 mTransform; - std::vector mMaterial; + std::map mMaterial; - LLModelInstance(LLModel* model, const std::string& label, LLMatrix4& transform, std::vector& materials) + LLModelInstance(LLModel* model, const std::string& label, LLMatrix4& transform, std::map& materials) : mModel(model), mLabel(label), mTransform(transform), mMaterial(materials) { mLocalMeshID = -1; @@ -160,6 +189,17 @@ public: virtual void completed() = 0; virtual void setStatusMessage(const std::string& msg); + + bool isValid() const {return mPositions.size() > 2 && mIndices.size() > 2 ;} + + protected: + //internal use + LLVector3 mBBox[2] ; + F32 mTriangleAreaThreshold ; + + void assignData(LLModel* mdl) ; + void updateTriangleAreaThreshold() ; + bool isValidTriangle(U16 idx1, U16 idx2, U16 idx3) ; }; LLCondition* mSignal; @@ -178,7 +218,7 @@ public: static S32 llcdCallback(const char*, S32, S32); void cancel(); - void setMeshData(LLCDMeshData& mesh); + void setMeshData(LLCDMeshData& mesh, bool vertex_based); void doDecomposition(); void doDecompositionSingleHull(); @@ -218,7 +258,6 @@ public: mesh_header_map mMeshHeader; std::map mMeshHeaderSize; - std::map mMeshResourceCost; class HeaderRequest { @@ -310,7 +349,7 @@ public: virtual void run(); - void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod); + void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool do_lock = true); bool fetchMeshHeader(const LLVolumeParams& mesh_params); bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod); bool headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size); @@ -322,8 +361,7 @@ public: void notifyLoadedMeshes(); S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod); - U32 getResourceCost(const LLUUID& mesh_params); - + void loadMeshSkinInfo(const LLUUID& mesh_id); void loadMeshDecomposition(const LLUUID& mesh_id); void loadMeshPhysicsShape(const LLUUID& mesh_id); @@ -343,8 +381,12 @@ public: }; +#if MESH_IMPORT class LLMeshUploadThread : public LLThread { +private: + S32 mMeshUploadTimeOut ; //maximum time in seconds to execute an uploading request. + public: class DecompRequest : public LLPhysicsDecomp::Request { @@ -374,9 +416,7 @@ public: LLMutex* mMutex; LLCurlRequest* mCurlRequest; - S32 mPendingConfirmations; S32 mPendingUploads; - S32 mPendingCost; LLVector3 mOrigin; bool mFinished; bool mUploadTextures; @@ -385,46 +425,24 @@ public: BOOL mDiscarded ; LLHost mHost; - std::string mUploadObjectAssetCapability; - std::string mNewInventoryCapability; std::string mWholeModelFeeCapability; std::string mWholeModelUploadURL; - std::queue mUploadQ; - std::queue mConfirmedQ; - std::queue mInstanceQ; - - std::queue mTextureQ; - std::queue mConfirmedTextureQ; - - std::map mTextureMap; - LLMeshUploadThread(instance_list& data, LLVector3& scale, bool upload_textures, - bool upload_skin, bool upload_joints); + bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload = true, + LLHandle fee_observer= (LLHandle()), LLHandle upload_observer = (LLHandle())); ~LLMeshUploadThread(); - void uploadTexture(LLTextureUploadData& data); - void doUploadTexture(LLTextureUploadData& data); - void sendCostRequest(LLTextureUploadData& data); - void priceResult(LLTextureUploadData& data, const LLSD& content); - void onTextureUploaded(LLTextureUploadData& data); - - void uploadModel(LLMeshUploadData& data); - void sendCostRequest(LLMeshUploadData& data); - void doUploadModel(LLMeshUploadData& data); - void onModelUploaded(LLMeshUploadData& data); - void createObjects(LLMeshUploadData& data); - LLSD createObject(LLModelInstance& instance); - void priceResult(LLMeshUploadData& data, const LLSD& content); - bool finished() { return mFinished; } virtual void run(); void preStart(); void discard() ; BOOL isDiscarded(); + void generateHulls(); + void doWholeModelUpload(); - void doIterativeUpload(); + void requestWholeModelFee(); void wholeModelToLLSD(LLSD& dest, bool include_textures); @@ -432,7 +450,17 @@ public: LLVector3& result_pos, LLQuaternion& result_rot, LLVector3& result_scale); + + void setFeeObserverHandle(LLHandle observer_handle) { mFeeObserverHandle = observer_handle; } + void setUploadObserverHandle(LLHandle observer_handle) { mUploadObserverHandle = observer_handle; } + +private: + LLHandle mFeeObserverHandle; + LLHandle mUploadObserverHandle; + + bool mDoUpload; // if FALSE only model data will be requested, otherwise the model will be uploaded }; +#endif //MESH_IMPORT class LLMeshRepository { @@ -465,20 +493,25 @@ public: S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod); static S32 getActualMeshLOD(LLSD& header, S32 lod); - U32 calcResourceCost(LLSD& header); - U32 getResourceCost(const LLUUID& mesh_params); - const LLMeshSkinInfo* getSkinInfo(const LLUUID& mesh_id); + const LLMeshSkinInfo* getSkinInfo(const LLUUID& mesh_id, LLVOVolume* requesting_obj); LLModel::Decomposition* getDecomposition(const LLUUID& mesh_id); void fetchPhysicsShape(const LLUUID& mesh_id); bool hasPhysicsShape(const LLUUID& mesh_id); void buildHull(const LLVolumeParams& params, S32 detail); void buildPhysicsMesh(LLModel::Decomposition& decomp); + + bool meshUploadEnabled(); + bool meshRezEnabled(); + LLSD& getMeshHeader(const LLUUID& mesh_id); +#if MESH_IMPORT void uploadModel(std::vector& data, LLVector3& scale, bool upload_textures, - bool upload_skin, bool upload_joints); + bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload = true, + LLHandle fee_observer= (LLHandle()), LLHandle upload_observer = (LLHandle())); +#endif //MESH_IMPORT S32 getMeshSize(const LLUUID& mesh_id, S32 lod); @@ -496,7 +529,8 @@ public: std::vector mPendingRequests; //list of mesh ids awaiting skin info - std::set mLoadingSkins; + typedef std::map > skin_load_map; + skin_load_map mLoadingSkins; //list of mesh ids that need to send skin info fetch requests std::queue mPendingSkinRequests; @@ -515,11 +549,15 @@ public: U32 mMeshThreadCount; +#if MESH_IMPORT void cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header); - +#endif //MESH_IMPORT + LLMeshRepoThread* mThread; +#if MESH_IMPORT std::vector mUploads; std::vector mUploadWaitList; +#endif //MESH_IMPORT LLPhysicsDecomp* mDecompThread; diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index dac2286bb..3d091dc24 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -4582,6 +4582,12 @@ void LLViewerObject::adjustAudioGain(const F32 gain) bool LLViewerObject::unpackParameterEntry(U16 param_type, LLDataPacker *dp) { +#if MESH_ENABLED + if (LLNetworkData::PARAMS_MESH == param_type) + { + param_type = LLNetworkData::PARAMS_SCULPT; + } +#endif //MESH_ENABLED ExtraParameter* param = getExtraParameterEntryCreate(param_type); if (param) { @@ -4623,7 +4629,7 @@ LLViewerObject::ExtraParameter* LLViewerObject::createNewParameterEntry(U16 para } default: { - llinfos << "Unknown param type." << llendl; + llinfos << "Unknown param type. (" << llformat("0x%2x",param_type) << ")" << llendl; break; } }; @@ -5363,3 +5369,20 @@ const LLUUID &LLViewerObject::extractAttachmentItemID() return getAttachmentItemID(); } +//virtual +LLVOAvatar* LLViewerObject::getAvatar() const +{ + if (isAttachment()) + { + LLViewerObject* vobj = (LLViewerObject*) getParent(); + + while (vobj && !vobj->isAvatar()) + { + vobj = (LLViewerObject*) vobj->getParent(); + } + + return (LLVOAvatar*) vobj; + } + + return NULL; +} diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index a0041e5b3..3eefe1113 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -63,6 +63,7 @@ class LLWorld; class LLNameValue; class LLNetMap; class LLMessageSystem; +class LLPartSysData; class LLPrimitive; class LLPipeline; class LLTextureEntry; @@ -73,7 +74,7 @@ class LLViewerPartSourceScript; class LLViewerRegion; class LLViewerObjectMedia; class LLVOInventoryListener; -class LLPartSysData; +class LLVOAvatar; typedef enum e_object_update_type { @@ -172,6 +173,7 @@ public: void setOnActiveList(BOOL on_active) { mOnActiveList = on_active; } virtual BOOL isAttachment() const { return FALSE; } + virtual LLVOAvatar* getAvatar() const; //get the avatar this object is attached to, or NULL if object is not an attachment virtual BOOL isHUDAttachment() const { return FALSE; } virtual void updateRadius() {}; virtual F32 getVObjRadius() const; // default implemenation is mDrawable->getRadius() @@ -220,6 +222,9 @@ public: virtual BOOL isFlexible() const { return FALSE; } virtual BOOL isSculpted() const { return FALSE; } +#if MESH_ENABLED + virtual BOOL isMesh() const { return FALSE; } +#endif //MESH_ENABLED // This method returns true if the object is over land owned by // the agent. diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 773dfbea3..0c57c294f 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -69,7 +69,7 @@ #include "llspatialpartition.h" #include "stringize.h" #include "llviewercontrol.h" - +#include "llsdserialize.h" extern BOOL gNoRender; @@ -1142,6 +1142,20 @@ void LLViewerRegion::getInfo(LLSD& info) info["Region"]["Handle"]["y"] = (LLSD::Integer)y; } +void LLViewerRegion::getSimulatorFeatures(LLSD& sim_features) +{ + sim_features = mSimulatorFeatures; +} + +void LLViewerRegion::setSimulatorFeatures(const LLSD& sim_features) +{ + std::stringstream str; + + LLSDSerialize::toPrettyXML(sim_features, str); + llinfos << str.str() << llendl; + mSimulatorFeatures = sim_features; +} + LLViewerRegion::eCacheUpdateResult LLViewerRegion::cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinaryBuffer &dp) { U32 local_id = objectp->getLocalID(); @@ -1520,6 +1534,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url) capabilityNames.append("SendUserReport"); capabilityNames.append("SendUserReportWithScreenshot"); capabilityNames.append("ServerReleaseNotes"); + capabilityNames.append("SimulatorFeatures"); capabilityNames.append("SetDisplayName"); capabilityNames.append("StartGroupProposal"); capabilityNames.append("TextureStats"); @@ -1544,6 +1559,42 @@ void LLViewerRegion::setSeedCapability(const std::string& url) LLHTTPClient::post(url, capabilityNames, mImpl->mHttpResponderPtr); } +class SimulatorFeaturesReceived : public LLHTTPClient::Responder +{ + LOG_CLASS(SimulatorFeaturesReceived); +public: + SimulatorFeaturesReceived(LLViewerRegion* region) + : mRegion(region) + { } + + + void error(U32 statusNum, const std::string& reason) + { + LL_WARNS2("AppInit", "SimulatorFeatures") << statusNum << ": " << reason << LL_ENDL; + } + + void result(const LLSD& content) + { + if(!mRegion) //region is removed or responder is not created. + { + return ; + } + + mRegion->setSimulatorFeatures(content); + } + + static boost::intrusive_ptr build( + LLViewerRegion* region) + { + return boost::intrusive_ptr( + new SimulatorFeaturesReceived(region)); + } + +private: + LLViewerRegion* mRegion; +}; + + void LLViewerRegion::setCapability(const std::string& name, const std::string& url) { if(name == "EventQueueGet") @@ -1556,6 +1607,11 @@ void LLViewerRegion::setCapability(const std::string& name, const std::string& u { LLHTTPSender::setSender(mImpl->mHost, new LLCapHTTPSender(url)); } + else if (name == "SimulatorFeatures") + { + // kick off a request for simulator features + LLHTTPClient::get(url, new SimulatorFeaturesReceived(this)); + } else { mImpl->mCapabilities[name] = url; @@ -1589,6 +1645,21 @@ bool LLViewerRegion::capabilitiesReceived() const void LLViewerRegion::setCapabilitiesReceived(bool received) { mCapabilitiesReceived = received; + + // Tell interested parties that we've received capabilities, + // so that they can safely use getCapability(). + if (received) + { + mCapabilitiesReceivedSignal(getRegionID()); + + // This is a single-shot signal. Forget callbacks to save resources. + mCapabilitiesReceivedSignal.disconnect_all_slots(); + } +} + +boost::signals2::connection LLViewerRegion::setCapabilitiesReceivedCallback(const caps_received_signal_t::slot_type& cb) +{ + return mCapabilitiesReceivedSignal.connect(cb); } void LLViewerRegion::logActiveCapabilities() const @@ -1633,3 +1704,18 @@ std::string LLViewerRegion::getDescription() const { return stringize(*this); } + +#if MESH_ENABLED +bool LLViewerRegion::meshUploadEnabled() const +{ + return (mSimulatorFeatures.has("MeshUploadEnabled") && + mSimulatorFeatures["MeshUploadEnabled"].asBoolean()); +} + +bool LLViewerRegion::meshRezEnabled() const +{ + return (mSimulatorFeatures.has("MeshRezEnabled") && + mSimulatorFeatures["MeshRezEnabled"].asBoolean()); +} +#endif //MESH_ENABLED + diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index 645c240fc..86f657d60 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -96,6 +96,8 @@ public: NUM_PARTITIONS } eObjectPartitions; + typedef boost::signals2::signal caps_received_signal_t; + LLViewerRegion(const U64 &handle, const LLHost &host, const U32 surface_grid_width, @@ -240,6 +242,7 @@ public: // has region received its final (not seed) capability list? bool capabilitiesReceived() const; void setCapabilitiesReceived(bool received); + boost::signals2::connection setCapabilitiesReceivedCallback(const caps_received_signal_t::slot_type& cb); static bool isSpecialCapabilityName(const std::string &name); void logActiveCapabilities() const; @@ -279,6 +282,13 @@ public: void getInfo(LLSD& info); +#if MESH_ENABLED + bool meshRezEnabled() const; + bool meshUploadEnabled() const; +#endif //MESH_ENABLED + + void getSimulatorFeatures(LLSD& info); + void setSimulatorFeatures(const LLSD& info); typedef enum { @@ -406,8 +416,11 @@ private: bool mAlive; // can become false if circuit disconnects bool mCapabilitiesReceived; + caps_received_signal_t mCapabilitiesReceivedSignal; BOOL mReleaseNotesRequested; + + LLSD mSimulatorFeatures; }; inline BOOL LLViewerRegion::getAllowDamage() const diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index 0ab17f6d7..a394e5c21 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -82,7 +82,20 @@ LLGLSLShader gObjectFullbrightShinyProgram(LLViewerShaderMgr::SHADER_OBJECT); LLGLSLShader gObjectFullbrightShinyWaterProgram(LLViewerShaderMgr::SHADER_OBJECT); LLGLSLShader gObjectShinyProgram(LLViewerShaderMgr::SHADER_OBJECT); LLGLSLShader gObjectShinyWaterProgram(LLViewerShaderMgr::SHADER_OBJECT); - + +#if MESH_ENABLED +//object hardware skinning shaders +LLGLSLShader gSkinnedObjectSimpleProgram(LLViewerShaderMgr::SHADER_OBJECT); +LLGLSLShader gSkinnedObjectFullbrightProgram(LLViewerShaderMgr::SHADER_OBJECT); +LLGLSLShader gSkinnedObjectFullbrightShinyProgram(LLViewerShaderMgr::SHADER_OBJECT); +LLGLSLShader gSkinnedObjectShinySimpleProgram(LLViewerShaderMgr::SHADER_OBJECT); + +LLGLSLShader gSkinnedObjectSimpleWaterProgram(LLViewerShaderMgr::SHADER_OBJECT); +LLGLSLShader gSkinnedObjectFullbrightWaterProgram(LLViewerShaderMgr::SHADER_OBJECT); +LLGLSLShader gSkinnedObjectFullbrightShinyWaterProgram(LLViewerShaderMgr::SHADER_OBJECT); +LLGLSLShader gSkinnedObjectShinySimpleWaterProgram(LLViewerShaderMgr::SHADER_OBJECT); +#endif //MESH_ENABLED + //environment shaders LLGLSLShader gTerrainProgram(LLViewerShaderMgr::SHADER_ENVIRONMENT); LLGLSLShader gTerrainWaterProgram(LLViewerShaderMgr::SHADER_WATER); //note, water. @@ -110,6 +123,11 @@ LLGLSLShader gDeferredImpostorProgram(LLViewerShaderMgr::SHADER_DEFERRED); LLGLSLShader gDeferredEdgeProgram(LLViewerShaderMgr::SHADER_DEFERRED); LLGLSLShader gDeferredWaterProgram(LLViewerShaderMgr::SHADER_DEFERRED); //calculatesAtmospherics LLGLSLShader gDeferredDiffuseProgram(LLViewerShaderMgr::SHADER_DEFERRED);//Not in mShaderList +#if MESH_ENABLED +LLGLSLShader gDeferredSkinnedDiffuseProgram(LLViewerShaderMgr::SHADER_DEFERRED); +LLGLSLShader gDeferredSkinnedBumpProgram(LLViewerShaderMgr::SHADER_DEFERRED); +LLGLSLShader gDeferredSkinnedAlphaProgram(LLViewerShaderMgr::SHADER_DEFERRED); +#endif //MESH_ENABLED LLGLSLShader gDeferredBumpProgram(LLViewerShaderMgr::SHADER_DEFERRED); //Not in mShaderList LLGLSLShader gDeferredTerrainProgram(LLViewerShaderMgr::SHADER_DEFERRED);//Not in mShaderList LLGLSLShader gDeferredTreeProgram(LLViewerShaderMgr::SHADER_DEFERRED); //Not in mShaderList @@ -124,6 +142,9 @@ LLGLSLShader gDeferredBlurLightProgram(LLViewerShaderMgr::SHADER_DEFERRED); LLGLSLShader gDeferredSoftenProgram(LLViewerShaderMgr::SHADER_DEFERRED); LLGLSLShader gDeferredShadowProgram(LLViewerShaderMgr::SHADER_DEFERRED); //Not in mShaderList LLGLSLShader gDeferredAvatarShadowProgram(LLViewerShaderMgr::SHADER_DEFERRED);//Not in mShaderList +#if MESH_ENABLED +LLGLSLShader gDeferredAttachmentShadowProgram(LLViewerShaderMgr::SHADER_DEFERRED); +#endif //MESH_ENABLED LLGLSLShader gDeferredAlphaProgram(LLViewerShaderMgr::SHADER_DEFERRED); //calculatesAtmospherics LLGLSLShader gDeferredFullbrightProgram(LLViewerShaderMgr::SHADER_DEFERRED); //calculatesAtmospherics LLGLSLShader gDeferredGIProgram(LLViewerShaderMgr::SHADER_DEFERRED); @@ -414,11 +435,11 @@ void LLViewerShaderMgr::setShaders() loadShadersWindLight(); loadShadersEffects(); loadShadersInterface(); - -#if 0 && LL_DARWIN // force avatar shaders off for mac - mVertexShaderLevel[SHADER_AVATAR] = 0; - sMaxAvatarShaderLevel = 0; -#else + + // Load max avatar shaders to set the max level + mVertexShaderLevel[SHADER_AVATAR] = 3; + mMaxAvatarShaderLevel = 3; + if (gSavedSettings.getBOOL("RenderAvatarVP")) { BOOL avatar_cloth = gSavedSettings.getBOOL("RenderAvatarCloth"); @@ -461,7 +482,6 @@ void LLViewerShaderMgr::setShaders() { gSavedSettings.setBOOL("RenderDeferred", FALSE); } -#endif } else { @@ -554,6 +574,9 @@ BOOL LLViewerShaderMgr::loadBasicShaders() shaders.push_back( make_pair( "lighting/lightSpecularV.glsl", mVertexShaderLevel[SHADER_LIGHTING] ) ); shaders.push_back( make_pair( "windlight/atmosphericsV.glsl", mVertexShaderLevel[SHADER_WINDLIGHT] ) ); shaders.push_back( make_pair( "avatar/avatarSkinV.glsl", 1 ) ); +#if MESH_ENABLED + shaders.push_back( make_pair( "avatar/objectSkinV.glsl", 1 ) ); +#endif //MESH_ENABLED // We no longer have to bind the shaders to global glhandles, they are automatically added to a map now. for (U32 i = 0; i < shaders.size(); i++) @@ -834,7 +857,47 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredDiffuseProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; success = gDeferredDiffuseProgram.createShader(NULL, NULL); } + +#if MESH_ENABLED + if (success) + { + gDeferredSkinnedDiffuseProgram.mName = "Deferred Skinned Diffuse Shader"; + gDeferredSkinnedDiffuseProgram.mFeatures.hasObjectSkinning = true; + gDeferredSkinnedDiffuseProgram.mShaderFiles.clear(); + gDeferredSkinnedDiffuseProgram.mShaderFiles.push_back(make_pair("deferred/diffuseSkinnedV.glsl", GL_VERTEX_SHADER_ARB)); + gDeferredSkinnedDiffuseProgram.mShaderFiles.push_back(make_pair("deferred/diffuseF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredSkinnedDiffuseProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; + success = gDeferredSkinnedDiffuseProgram.createShader(NULL, NULL); + } + if (success) + { + gDeferredSkinnedBumpProgram.mName = "Deferred Skinned Bump Shader"; + gDeferredSkinnedBumpProgram.mFeatures.hasObjectSkinning = true; + gDeferredSkinnedBumpProgram.mShaderFiles.clear(); + gDeferredSkinnedBumpProgram.mShaderFiles.push_back(make_pair("deferred/bumpSkinnedV.glsl", GL_VERTEX_SHADER_ARB)); + gDeferredSkinnedBumpProgram.mShaderFiles.push_back(make_pair("deferred/bumpF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredSkinnedBumpProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; + success = gDeferredSkinnedBumpProgram.createShader(NULL, NULL); + } + + if (success) + { + gDeferredSkinnedAlphaProgram.mName = "Deferred Skinned Alpha Shader"; + gDeferredSkinnedAlphaProgram.mFeatures.hasObjectSkinning = true; + gDeferredSkinnedAlphaProgram.mFeatures.calculatesLighting = true; + gDeferredSkinnedAlphaProgram.mFeatures.calculatesAtmospherics = true; + gDeferredSkinnedAlphaProgram.mFeatures.hasGamma = true; + gDeferredSkinnedAlphaProgram.mFeatures.hasAtmospherics = true; + gDeferredSkinnedAlphaProgram.mFeatures.hasLighting = true; + gDeferredSkinnedAlphaProgram.mShaderFiles.clear(); + gDeferredSkinnedAlphaProgram.mShaderFiles.push_back(make_pair("deferred/alphaSkinnedV.glsl", GL_VERTEX_SHADER_ARB)); + gDeferredSkinnedAlphaProgram.mShaderFiles.push_back(make_pair("deferred/alphaF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredSkinnedAlphaProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; + success = gDeferredSkinnedAlphaProgram.createShader(NULL, NULL); + } +#endif //MESH_ENABLED + if (success) { gDeferredBumpProgram.mName = "Deferred Bump Shader"; @@ -1020,7 +1083,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredAvatarShadowProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; success = gDeferredAvatarShadowProgram.createShader(&mAvatarAttribs, &mAvatarUniforms); } - if (success) { gTerrainProgram.mName = "Deferred Terrain Shader"; @@ -1280,6 +1342,145 @@ BOOL LLViewerShaderMgr::loadShadersObject() success = gObjectFullbrightShinyWaterProgram.createShader(NULL, &mShinyUniforms); } +#if MESH_ENABLED + + if (mVertexShaderLevel[SHADER_AVATAR] > 0) + { //load hardware skinned attachment shaders + if (success) + { + gSkinnedObjectSimpleProgram.mName = "Skinned Simple Shader"; + gSkinnedObjectSimpleProgram.mFeatures.calculatesLighting = true; + gSkinnedObjectSimpleProgram.mFeatures.calculatesAtmospherics = true; + gSkinnedObjectSimpleProgram.mFeatures.hasGamma = true; + gSkinnedObjectSimpleProgram.mFeatures.hasAtmospherics = true; + gSkinnedObjectSimpleProgram.mFeatures.hasLighting = true; + gSkinnedObjectSimpleProgram.mFeatures.hasObjectSkinning = true; + gSkinnedObjectSimpleProgram.mShaderFiles.clear(); + gSkinnedObjectSimpleProgram.mShaderFiles.push_back(make_pair("objects/simpleSkinnedV.glsl", GL_VERTEX_SHADER_ARB)); + gSkinnedObjectSimpleProgram.mShaderFiles.push_back(make_pair("objects/simpleF.glsl", GL_FRAGMENT_SHADER_ARB)); + gSkinnedObjectSimpleProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT]; + success = gSkinnedObjectSimpleProgram.createShader(NULL, NULL); + } + + if (success) + { + gSkinnedObjectFullbrightProgram.mName = "Skinned Fullbright Shader"; + gSkinnedObjectFullbrightProgram.mFeatures.calculatesAtmospherics = true; + gSkinnedObjectFullbrightProgram.mFeatures.hasGamma = true; + gSkinnedObjectFullbrightProgram.mFeatures.hasTransport = true; + gSkinnedObjectFullbrightProgram.mFeatures.isFullbright = true; + gSkinnedObjectFullbrightProgram.mFeatures.hasObjectSkinning = true; + gSkinnedObjectFullbrightProgram.mShaderFiles.clear(); + gSkinnedObjectFullbrightProgram.mShaderFiles.push_back(make_pair("objects/fullbrightSkinnedV.glsl", GL_VERTEX_SHADER_ARB)); + gSkinnedObjectFullbrightProgram.mShaderFiles.push_back(make_pair("objects/fullbrightF.glsl", GL_FRAGMENT_SHADER_ARB)); + gSkinnedObjectFullbrightProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT]; + success = gSkinnedObjectFullbrightProgram.createShader(NULL, NULL); + } + + if (success) + { + gSkinnedObjectFullbrightShinyProgram.mName = "Skinned Fullbright Shiny Shader"; + gSkinnedObjectFullbrightShinyProgram.mFeatures.calculatesAtmospherics = true; + gSkinnedObjectFullbrightShinyProgram.mFeatures.hasGamma = true; + gSkinnedObjectFullbrightShinyProgram.mFeatures.hasTransport = true; + gSkinnedObjectFullbrightShinyProgram.mFeatures.isShiny = true; + gSkinnedObjectFullbrightShinyProgram.mFeatures.isFullbright = true; + gSkinnedObjectFullbrightShinyProgram.mFeatures.hasObjectSkinning = true; + gSkinnedObjectFullbrightShinyProgram.mShaderFiles.clear(); + gSkinnedObjectFullbrightShinyProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinySkinnedV.glsl", GL_VERTEX_SHADER_ARB)); + gSkinnedObjectFullbrightShinyProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinyF.glsl", GL_FRAGMENT_SHADER_ARB)); + gSkinnedObjectFullbrightShinyProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT]; + success = gSkinnedObjectFullbrightShinyProgram.createShader(NULL, &mShinyUniforms); + } + + if (success) + { + gSkinnedObjectShinySimpleProgram.mName = "Skinned Shiny Simple Shader"; + gSkinnedObjectShinySimpleProgram.mFeatures.calculatesLighting = true; + gSkinnedObjectShinySimpleProgram.mFeatures.calculatesAtmospherics = true; + gSkinnedObjectShinySimpleProgram.mFeatures.hasGamma = true; + gSkinnedObjectShinySimpleProgram.mFeatures.hasAtmospherics = true; + gSkinnedObjectShinySimpleProgram.mFeatures.hasObjectSkinning = true; + gSkinnedObjectShinySimpleProgram.mFeatures.isShiny = true; + gSkinnedObjectShinySimpleProgram.mShaderFiles.clear(); + gSkinnedObjectShinySimpleProgram.mShaderFiles.push_back(make_pair("objects/shinySimpleSkinnedV.glsl", GL_VERTEX_SHADER_ARB)); + gSkinnedObjectShinySimpleProgram.mShaderFiles.push_back(make_pair("objects/shinyF.glsl", GL_FRAGMENT_SHADER_ARB)); + gSkinnedObjectShinySimpleProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT]; + success = gSkinnedObjectShinySimpleProgram.createShader(NULL, &mShinyUniforms); + } + + if (success) + { + gSkinnedObjectSimpleWaterProgram.mName = "Skinned Simple Water Shader"; + gSkinnedObjectSimpleWaterProgram.mFeatures.calculatesLighting = true; + gSkinnedObjectSimpleWaterProgram.mFeatures.calculatesAtmospherics = true; + gSkinnedObjectSimpleWaterProgram.mFeatures.hasGamma = true; + gSkinnedObjectSimpleWaterProgram.mFeatures.hasAtmospherics = true; + gSkinnedObjectSimpleWaterProgram.mFeatures.hasLighting = true; + gSkinnedObjectSimpleWaterProgram.mFeatures.hasWaterFog = true; + gSkinnedObjectSimpleWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER; + gSkinnedObjectSimpleWaterProgram.mFeatures.hasObjectSkinning = true; + gSkinnedObjectSimpleWaterProgram.mShaderFiles.clear(); + gSkinnedObjectSimpleWaterProgram.mShaderFiles.push_back(make_pair("objects/simpleSkinnedV.glsl", GL_VERTEX_SHADER_ARB)); + gSkinnedObjectSimpleWaterProgram.mShaderFiles.push_back(make_pair("objects/simpleWaterF.glsl", GL_FRAGMENT_SHADER_ARB)); + gSkinnedObjectSimpleWaterProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT]; + success = gSkinnedObjectSimpleWaterProgram.createShader(NULL, NULL); + } + + if (success) + { + gSkinnedObjectFullbrightWaterProgram.mName = "Skinned Fullbright Water Shader"; + gSkinnedObjectFullbrightWaterProgram.mFeatures.calculatesAtmospherics = true; + gSkinnedObjectFullbrightWaterProgram.mFeatures.hasGamma = true; + gSkinnedObjectFullbrightWaterProgram.mFeatures.hasTransport = true; + gSkinnedObjectFullbrightWaterProgram.mFeatures.isFullbright = true; + gSkinnedObjectFullbrightWaterProgram.mFeatures.hasObjectSkinning = true; + gSkinnedObjectFullbrightWaterProgram.mFeatures.hasWaterFog = true; + gSkinnedObjectFullbrightWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER; + gSkinnedObjectFullbrightWaterProgram.mShaderFiles.clear(); + gSkinnedObjectFullbrightWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightSkinnedV.glsl", GL_VERTEX_SHADER_ARB)); + gSkinnedObjectFullbrightWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightWaterF.glsl", GL_FRAGMENT_SHADER_ARB)); + gSkinnedObjectFullbrightWaterProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT]; + success = gSkinnedObjectFullbrightWaterProgram.createShader(NULL, NULL); + } + + if (success) + { + gSkinnedObjectFullbrightShinyWaterProgram.mName = "Skinned Fullbright Shiny Water Shader"; + gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.calculatesAtmospherics = true; + gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.hasGamma = true; + gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.hasTransport = true; + gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.isShiny = true; + gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.isFullbright = true; + gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.hasObjectSkinning = true; + gSkinnedObjectFullbrightShinyWaterProgram.mFeatures.hasWaterFog = true; + gSkinnedObjectFullbrightShinyWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER; + gSkinnedObjectFullbrightShinyWaterProgram.mShaderFiles.clear(); + gSkinnedObjectFullbrightShinyWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinySkinnedV.glsl", GL_VERTEX_SHADER_ARB)); + gSkinnedObjectFullbrightShinyWaterProgram.mShaderFiles.push_back(make_pair("objects/fullbrightShinyWaterF.glsl", GL_FRAGMENT_SHADER_ARB)); + gSkinnedObjectFullbrightShinyWaterProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT]; + success = gSkinnedObjectFullbrightShinyWaterProgram.createShader(NULL, &mShinyUniforms); + } + + if (success) + { + gSkinnedObjectShinySimpleWaterProgram.mName = "Skinned Shiny Simple Water Shader"; + gSkinnedObjectShinySimpleWaterProgram.mFeatures.calculatesLighting = true; + gSkinnedObjectShinySimpleWaterProgram.mFeatures.calculatesAtmospherics = true; + gSkinnedObjectShinySimpleWaterProgram.mFeatures.hasGamma = true; + gSkinnedObjectShinySimpleWaterProgram.mFeatures.hasAtmospherics = true; + gSkinnedObjectShinySimpleWaterProgram.mFeatures.hasObjectSkinning = true; + gSkinnedObjectShinySimpleWaterProgram.mFeatures.isShiny = true; + gSkinnedObjectShinySimpleWaterProgram.mFeatures.hasWaterFog = true; + gSkinnedObjectShinySimpleWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER; + gSkinnedObjectShinySimpleWaterProgram.mShaderFiles.clear(); + gSkinnedObjectShinySimpleWaterProgram.mShaderFiles.push_back(make_pair("objects/shinySimpleSkinnedV.glsl", GL_VERTEX_SHADER_ARB)); + gSkinnedObjectShinySimpleWaterProgram.mShaderFiles.push_back(make_pair("objects/shinyWaterF.glsl", GL_FRAGMENT_SHADER_ARB)); + gSkinnedObjectShinySimpleWaterProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT]; + success = gSkinnedObjectShinySimpleWaterProgram.createShader(NULL, &mShinyUniforms); + } + } +#endif //MESH_ENABLED if( !success ) { mVertexShaderLevel[SHADER_OBJECT] = 0; diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h index f952b1a6f..fdc0386a2 100644 --- a/indra/newview/llviewershadermgr.h +++ b/indra/newview/llviewershadermgr.h @@ -319,6 +319,18 @@ extern LLGLSLShader gObjectFullbrightShinyWaterProgram; extern LLGLSLShader gObjectShinyProgram; extern LLGLSLShader gObjectShinyWaterProgram; +#if MESH_ENABLED +extern LLGLSLShader gSkinnedObjectSimpleProgram; +extern LLGLSLShader gSkinnedObjectFullbrightProgram; +extern LLGLSLShader gSkinnedObjectFullbrightShinyProgram; +extern LLGLSLShader gSkinnedObjectShinySimpleProgram; + +extern LLGLSLShader gSkinnedObjectSimpleWaterProgram; +extern LLGLSLShader gSkinnedObjectFullbrightWaterProgram; +extern LLGLSLShader gSkinnedObjectFullbrightShinyWaterProgram; +extern LLGLSLShader gSkinnedObjectShinySimpleWaterProgram; +#endif //MESH_ENABLED + //environment shaders extern LLGLSLShader gTerrainProgram; extern LLGLSLShader gTerrainWaterProgram; @@ -350,6 +362,11 @@ extern LLGLSLShader gDeferredImpostorProgram; extern LLGLSLShader gDeferredEdgeProgram; extern LLGLSLShader gDeferredWaterProgram; extern LLGLSLShader gDeferredDiffuseProgram; +#if MESH_ENABLED +extern LLGLSLShader gDeferredSkinnedDiffuseProgram; +extern LLGLSLShader gDeferredSkinnedBumpProgram; +extern LLGLSLShader gDeferredSkinnedAlphaProgram; +#endif //MESH_ENABLED extern LLGLSLShader gDeferredBumpProgram; extern LLGLSLShader gDeferredTerrainProgram; extern LLGLSLShader gDeferredTreeProgram; @@ -368,6 +385,9 @@ extern LLGLSLShader gDeferredPostGIProgram; extern LLGLSLShader gDeferredPostProgram; extern LLGLSLShader gDeferredPostNoDoFProgram; extern LLGLSLShader gDeferredAvatarShadowProgram; +#if MESH_ENABLED +extern LLGLSLShader gDeferredAttachmentShadowProgram; +#endif //MESH_ENABLED extern LLGLSLShader gDeferredAlphaProgram; extern LLGLSLShader gDeferredFullbrightProgram; extern LLGLSLShader gDeferredAvatarAlphaProgram; diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 4d88fdc7d..d5f1bcbdb 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -725,10 +725,16 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mTexHairColor( NULL ), mTexEyeColor( NULL ), mNeedsSkin(FALSE), +#if MESH_ENABLED + mLastSkinTime(0.f), +#endif //MESH_ENABLED mUpdatePeriod(1), mFullyLoadedInitialized(FALSE), mHasBakedHair( FALSE ), mSupportsAlphaLayers(FALSE), +#if MESH_ENABLED + mHasPelvisOffset( FALSE ), +#endif //MESH_ENABLED mFirstSetActualBoobGravRan( false ), mSupportsPhysics( false ) //mFirstSetActualButtGravRan( false ), @@ -838,6 +844,12 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mRuthTimer.reset(); +#if MESH_ENABLED + mPelvisOffset = LLVector3(0.0f,0.0f,0.0f); + mLastPelvisToFoot = 0.0f; + mPelvisFixup = 0.0f; + mLastPelvisFixup = 0.0f; +#endif //MESH_ENABLED //------------------------------------------------------------------------- // initialize joint, mesh and shape members //------------------------------------------------------------------------- @@ -1527,6 +1539,16 @@ const LLVector3 LLVOAvatar::getRenderPosition() const } else if (isRoot() || !mDrawable->getParent()) { +#if MESH_ENABLED + if ( mHasPelvisOffset ) + { + //Apply a pelvis fixup (as defined by the avs skin) + LLVector3 pos = mDrawable->getPositionAgent(); + pos[VZ] += mPelvisFixup; + return pos; + } + else +#endif //MESH_ENABLED return mDrawable->getPositionAgent(); } else @@ -1624,7 +1646,7 @@ void LLVOAvatar::getSpatialExtents(LLVector4a& newMin, LLVector4a& newMax) LLDrawable* drawable = attached_object->mDrawable; if (drawable #if MESH_ENABLED - && !drawable->isState(LLDrawable::RIGGED)) + && !drawable->isState(LLDrawable::RIGGED) #endif //MESH_ENABLED ) { @@ -2881,9 +2903,20 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) // here we get the approximate head position and set as sound source for the voice symbol // (the following version uses a tweak of "mHeadOffset" which handle sitting vs. standing) //-------------------------------------------------------------------------------------------- +#if MESH_ENABLED + if( !mIsSitting ) + { + LLVector3 tagPos = mRoot.getWorldPosition(); + tagPos[VZ] -= mPelvisToFoot; + tagPos[VZ] += ( mBodySize[VZ] + 0.125f ); + mVoiceVisualizer->setVoiceSourceWorldPosition( tagPos ); + } + else +#endif //MESH_ENABLED + { LLVector3 headOffset = LLVector3( 0.0f, 0.0f, mHeadOffset.mV[2] ); mVoiceVisualizer->setVoiceSourceWorldPosition( mRoot.getWorldPosition() + headOffset ); - + } }//if ( voiceEnabled ) } @@ -4801,7 +4834,43 @@ void LLVOAvatar::updateHeadOffset() mHeadOffset = lerp(midEyePt, mHeadOffset, u); } } - +#if MESH_ENABLED +//------------------------------------------------------------------------ +// setPelvisOffset +//------------------------------------------------------------------------ +void LLVOAvatar::setPelvisOffset( bool hasOffset, const LLVector3& offsetAmount, F32 pelvisFixup ) +{ + mHasPelvisOffset = hasOffset; + if ( mHasPelvisOffset ) + { + //Store off last pelvis to foot value + mLastPelvisToFoot = mPelvisToFoot; + mPelvisOffset = offsetAmount; + mLastPelvisFixup = mPelvisFixup; + mPelvisFixup = pelvisFixup; + } +} +//------------------------------------------------------------------------ +// postPelvisSetRecalc +//------------------------------------------------------------------------ +void LLVOAvatar::postPelvisSetRecalc( void ) +{ + computeBodySize(); + mRoot.touch(); + mRoot.updateWorldMatrixChildren(); + dirtyMesh(); + updateHeadOffset(); +} +//------------------------------------------------------------------------ +// pelisPoke +//------------------------------------------------------------------------ +void LLVOAvatar::setPelvisOffset( F32 pelvisFixupAmount ) +{ + mHasPelvisOffset = true; + mLastPelvisFixup = mPelvisFixup; + mPelvisFixup = pelvisFixupAmount; +} +#endif //MESH_ENABLED //------------------------------------------------------------------------ // updateVisibility() //------------------------------------------------------------------------ @@ -5020,6 +5089,9 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass) mMeshLOD[MESH_ID_HAIR]->updateJointGeometry(); } mNeedsSkin = FALSE; +#if MESH_ENABLED + mLastSkinTime = gFrameTimeSeconds; +#endif //MESH_ENABLED LLVertexBuffer* vb = mDrawable->getFace(0)->getVertexBuffer(); if (vb) @@ -6006,13 +6078,89 @@ LLJoint *LLVOAvatar::getJoint( const std::string &name ) } return jointp; } +#if MESH_ENABLED +//----------------------------------------------------------------------------- +// resetJointPositions +//----------------------------------------------------------------------------- +void LLVOAvatar::resetJointPositions( void ) +{ + for(S32 i = 0; i < (S32)mNumJoints; ++i) + { + mSkeleton[i].restoreOldXform(); + mSkeleton[i].setId( LLUUID::null ); + } + mHasPelvisOffset = false; + mPelvisFixup = mLastPelvisFixup; +} +//----------------------------------------------------------------------------- +// resetSpecificJointPosition +//----------------------------------------------------------------------------- +void LLVOAvatar::resetSpecificJointPosition( const std::string& name ) +{ + LLJoint* pJoint = mRoot.findJoint( name ); + + if ( pJoint && pJoint->doesJointNeedToBeReset() ) + { + pJoint->restoreOldXform(); + pJoint->setId( LLUUID::null ); + //If we're reseting the pelvis position make sure not to apply offset + if ( name == "mPelvis" ) + { + mHasPelvisOffset = false; + } + } + else + { + llinfos<<"Did not find "<< name.c_str()<setPosition( avPos + pPelvis->getPosition() ); + } + else + { + llwarns<<"Can't get pelvis joint."<doesJointNeedToBeReset() ) + { + + pJoint->setId( LLUUID::null ); + //restore joints to default positions, however skip over the pelvis + if ( pJoint && pPelvis != pJoint ) + { + pJoint->restoreOldXform(); + } + } + } + //make sure we don't apply the joint offset + mHasPelvisOffset = false; + mPelvisFixup = mLastPelvisFixup; + postPelvisSetRecalc(); +} +#endif //MESH_ENABLED //----------------------------------------------------------------------------- // getCharacterPosition() //----------------------------------------------------------------------------- LLVector3 LLVOAvatar::getCharacterPosition() { - if (mDrawable && mDrawable.notNull()) + if (mDrawable.notNull()) { return mDrawable->getPositionAgent(); } @@ -7202,7 +7350,7 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO ) LLVOVolume* pVObj = pVO->mDrawable->getVOVolume(); if ( pVObj ) { - const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( pVObj->getVolume()->getParams().getSculptID() ); + const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( pVObj->getVolume()->getParams().getSculptID(), pVObj ); if ( pSkinData ) { const int jointCnt = pSkinData->mJointNames.size(); diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 59fbb7147..b216496a9 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -171,6 +171,11 @@ public: virtual LLJoint* getJoint(const std::string &name); virtual LLJoint* getRootJoint() { return &mRoot; } +#if MESH_ENABLED + void resetJointPositions( void ); + void resetJointPositionsToDefault( void ); + void resetSpecificJointPosition( const std::string& name ); +#endif //MESH_ENABLED virtual const char* getAnimationPrefix() { return "avatar"; } virtual const LLUUID& getID(); virtual LLVector3 getVolumePos(S32 joint_index, LLVector3& volume_offset); @@ -289,7 +294,18 @@ private: public: void updateHeadOffset(); F32 getPelvisToFoot() const { return mPelvisToFoot; } +#if MESH_ENABLED + 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; +#endif //MESH_ENABLED LLVector3 mHeadOffset; // current head position LLViewerJoint mRoot; protected: @@ -358,6 +374,10 @@ public: U32 renderImpostor(LLColor4U color = LLColor4U(255,255,255,255), S32 diffuse_channel = 0); U32 renderRigid(); U32 renderSkinned(EAvatarRenderPass pass); +#if MESH_ENABLED + F32 getLastSkinTime() { return mLastSkinTime; } + U32 renderSkinnedAttachments(); +#endif //MESH_ENABLED U32 renderTransparent(BOOL first_pass); void renderCollisionVolumes(); static void deleteCachedImages(bool clearAll=true); @@ -369,6 +389,9 @@ private: bool shouldAlphaMask(); BOOL mNeedsSkin; // avatar has been animated and verts have not been updated +#if MESH_ENABLED + F32 mLastSkinTime; //value of gFrameTimeSeconds at last skin update +#endif //MESH_ENABLED S32 mUpdatePeriod; S32 mNumInitFaces; //number of faces generated when creating the avatar drawable, does not inculde splitted faces due to long vertex buffer. diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 3023ef3d4..af03d5270 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -70,10 +70,11 @@ #include "llmatrix4a.h" #include "llagent.h" #if MESH_ENABLED -#include "lldrawpoolavatar.h +#include "lldrawpoolavatar.h" #include "llmeshrepository.h" #include "lldatapacker.h" #include "llvoavatar.h" +#include "llfloatertools.h" #endif //MESH_ENABLED #include "llvocache.h" @@ -755,7 +756,6 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams ¶ms_in, const S32 detail, bo // if it's a mesh if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH) { //meshes might not have all LODs, get the force detail to best existing LOD - LLUUID mesh_id = volume_params.getSculptID(); //profile and path params don't matter for meshes @@ -801,6 +801,8 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams ¶ms_in, const S32 detail, bo if (is404) { setIcon(LLViewerTextureManager::getFetchedTextureFromFile("icons/Inv_Mesh.png", TRUE, LLViewerTexture::BOOST_UI)); + //render prim proxy when mesh loading attempts give up + volume_params.setSculptID(LLUUID::null, LL_SCULPT_TYPE_NONE); } #endif //MESH_ENABLED if ((LLPrimitive::setVolume(volume_params, lod, (mVolumeImpl && mVolumeImpl->isVolumeUnique()))) || mSculptChanged) @@ -992,8 +994,16 @@ BOOL LLVOVolume::calcLOD() if (mDrawable->isState(LLDrawable::RIGGED)) { LLVOAvatar* avatar = getAvatar(); - distance = avatar->mDrawable->mDistanceWRTCamera; - radius = avatar->getBinRadius(); + if(avatar) + { + distance = avatar->mDrawable->mDistanceWRTCamera; + radius = avatar->getBinRadius(); + } + else + { + distance = mDrawable->mDistanceWRTCamera; + radius = getVolume()->mLODScaleBias.scaledVec(getScale()).length(); + } } else #endif //MESH_ENABLED @@ -1182,7 +1192,7 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global) #if !MESH_ENABLED LLVolume* volume = getVolume(); #endif //!MESH_ENABLED - for (S32 i = 0; i < getVolume()->getNumFaces(); i++) + for (S32 i = 0; i < getVolume()->getNumVolumeFaces(); i++) { LLFace *face = mDrawable->getFace(i); if (!face) @@ -1347,7 +1357,7 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable) if (mDrawable->isState(LLDrawable::REBUILD_RIGGED)) { { - LLFastTimer t(FTM_UPDATE_RIGGED_VOLUME); + LLFastTimer t2(LLFastTimer::FTM_UPDATE_RIGGED_VOLUME); updateRiggedVolume(); } genBBoxes(FALSE); @@ -2022,6 +2032,7 @@ BOOL LLVOVolume::isSculpted() const #if MESH_ENABLED BOOL LLVOVolume::isMesh() const { + if (isSculpted()) { LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT); @@ -2452,9 +2463,10 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e #if MESH_ENABLED if (mDrawable->isState(LLDrawable::RIGGED)) { - if (LLFloater::isVisible(gFloaterTools) && getAvatar()->isSelf()) + if (gFloaterTools->getVisible() && getAvatar()->isSelf()) { - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_RIGGED, TRUE); + updateRiggedVolume(); + genBBoxes(FALSE); volume = mRiggedVolume; transform = false; } @@ -2591,7 +2603,7 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e #if MESH_ENABLED bool LLVOVolume::treatAsRigged() { - return LLFloater::isVisible(gFloaterTools) && + return gFloaterTools->getVisible() && isAttachment() && getAvatar() && getAvatar()->isSelf() && @@ -2627,7 +2639,7 @@ void LLVOVolume::updateRiggedVolume() LLVolume* volume = getVolume(); - const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(volume->getParams().getSculptID()); + const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(volume->getParams().getSculptID(), this); if (!skin) { @@ -2655,8 +2667,8 @@ void LLVOVolume::updateRiggedVolume() } -static LLFastTimer::DeclareTimer FTM_SKIN_RIGGED("Skin"); -static LLFastTimer::DeclareTimer FTM_RIGGED_OCTREE("Octree"); +//static LLFastTimer::DeclareTimer FTM_SKIN_RIGGED("Skin"); +//static LLFastTimer::DeclareTimer FTM_RIGGED_OCTREE("Octree"); void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, const LLVolume* volume) { @@ -2711,9 +2723,9 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons LLVector4a* pos = dst_face.mPositions; { - LLFastTimer t(FTM_SKIN_RIGGED); + LLFastTimer t(LLFastTimer::FTM_SKIN_RIGGED); - for (U32 j = 0; j < dst_face.mNumVertices; ++j) + for (U32 j = 0; j < (U32)dst_face.mNumVertices; ++j) { LLMatrix4a final_mat; final_mat.clear(); @@ -2760,7 +2772,7 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons min = pos[0]; max = pos[1]; - for (U32 j = 1; j < dst_face.mNumVertices; ++j) + for (U32 j = 1; j < (U32)dst_face.mNumVertices; ++j) { min.setMin(min, pos[j]); max.setMax(max, pos[j]); @@ -2772,7 +2784,7 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons } { - LLFastTimer t(FTM_RIGGED_OCTREE); + LLFastTimer t(LLFastTimer::FTM_RIGGED_OCTREE); delete dst_face.mOctree; dst_face.mOctree = NULL; @@ -2864,9 +2876,12 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep, else { model_mat = &(drawable->getRegion()->mRenderMatrix); + if (model_mat->isIdentity()) + { + model_mat = NULL; + } } - U8 bump = (type == LLRenderPass::PASS_BUMP || type == LLRenderPass::PASS_POST_BUMP) ? facep->getTextureEntry()->getBumpmap() : 0; LLViewerTexture* tex = facep->getTexture(); @@ -3030,7 +3045,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) LLVOVolume* vobj = drawablep->getVOVolume(); #if MESH_ENABLED - if (vobj->getVolume() && vobj->getVolume()->isTetrahedron()) + if (vobj->getVolume() && vobj->getVolume()->isTetrahedron() || (vobj->isMesh() && !gMeshRepo.meshRezEnabled())) { continue; } @@ -3044,9 +3059,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) #if MESH_ENABLED bool rigged = vobj->isAttachment() && vobj->isMesh() && - gMeshRepo.getSkinInfo(vobj->getVolume()->getParams().getSculptID()); - - bool bake_sunlight = LLPipeline::sBakeSunlight && drawablep->isStatic(); + gMeshRepo.getSkinInfo(vobj->getVolume()->getParams().getSculptID(), vobj); bool is_rigged = false; #endif //MESH_ENABLED @@ -3085,7 +3098,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) if ( pAvatarVO ) { LLUUID currentId = vobj->getVolume()->getParams().getSculptID(); - const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( currentId ); + const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( currentId, vobj ); if ( pSkinData ) { @@ -3362,8 +3375,8 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) } genDrawInfo(group, simple_mask, simple_faces); - genDrawInfo(group, bump_mask, bump_faces); genDrawInfo(group, fullbright_mask, fullbright_faces); + genDrawInfo(group, bump_mask, bump_faces, FALSE); genDrawInfo(group, alpha_mask, alpha_faces, TRUE); if (!LLPipeline::sDelayVBUpdate) diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index 94e4ff69e..3c2d5fba9 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -266,12 +266,6 @@ public: // Returns 'true' iff the media data for this object is in flight bool isMediaDataBeingFetched() const; - // Returns the "last fetched" media version, or -1 if not fetched yet - S32 getLastFetchedMediaVersion() const { return mLastFetchedMediaVersion; } - - void addMDCImpl() { ++mMDCImplCount; } - void removeMDCImpl() { --mMDCImplCount; } - S32 getMDCImplCount() { return mMDCImplCount; } //rigged volume update (for raycasting) From 62d0be964d03a4790a3a4a637e0248eaebf17119 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sun, 31 Jul 2011 01:51:43 -0500 Subject: [PATCH 11/22] Updated LLThread and LLMutex[Base] to prevent nested mutex locks in same thread from hardlocking. --- indra/llcommon/llthread.cpp | 113 +++++++++++++++++++++++++++-- indra/llcommon/llthread.h | 27 ++++++- indra/newview/llmeshrepository.cpp | 22 ++---- indra/newview/llmeshrepository.h | 2 +- 4 files changed, 141 insertions(+), 23 deletions(-) diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index 8bd341457..b6d35f1db 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -62,6 +62,21 @@ // //---------------------------------------------------------------------------- +#if !LL_DARWIN +U32 ll_thread_local sThreadID = 0; +#endif + +U32 LLThread::sIDIter = 0; + +LL_COMMON_API void assert_main_thread() +{ + static U32 s_thread_id = LLThread::currentID(); + if (LLThread::currentID() != s_thread_id) + { + llerrs << "Illegal execution outside main thread." << llendl; + } +} + // // Handed to the APR thread creation function // @@ -73,8 +88,10 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap LLThread *threadp = (LLThread *)datap; - // Set thread state to running - threadp->mStatus = RUNNING; +#if !LL_DARWIN + sThreadID = threadp->mID; +#endif + // Create a thread local data. AIThreadLocalData::create(threadp); @@ -98,6 +115,7 @@ LLThread::LLThread(std::string const& name) : mStatus(STOPPED), mThreadLocalData(NULL) { + mID = ++sIDIter; mRunCondition = new LLCondition; } @@ -119,7 +137,7 @@ void LLThread::shutdown() // First, set the flag that indicates that we're ready to die setQuitting(); - llinfos << "LLThread::~LLThread() Killing thread " << mName << " Status: " << mStatus << llendl; + llinfos << "LLThread::shutdown() Killing thread " << mName << " Status: " << mStatus << llendl; // Now wait a bit for the thread to exit // It's unclear whether I should even bother doing this - this destructor // should netver get called unless we're already stopped, really... @@ -142,20 +160,38 @@ void LLThread::shutdown() { // This thread just wouldn't stop, even though we gave it time llwarns << "LLThread::shutdown() exiting thread before clean exit!" << llendl; + // Put a stake in its heart. + apr_thread_exit(mAPRThreadp, -1); return; } mAPRThreadp = NULL; } delete mRunCondition; + mRunCondition = 0; } void LLThread::start() { - apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, tldata().mRootPool()); + llassert(isStopped()); + + // Set thread state to running + mStatus = RUNNING; - // We won't bother joining - apr_thread_detach(mAPRThreadp); + apr_status_t status = + apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, tldata().mRootPool()); + + if(status == APR_SUCCESS) + { + // We won't bother joining + apr_thread_detach(mAPRThreadp); + } + else + { + mStatus = STOPPED; + llwarns << "failed to start thread " << mName << llendl; + ll_apr_warn_status(status); + } } //============================================================================ @@ -318,6 +354,55 @@ AIThreadLocalData& AIThreadLocalData::tldata(void) //============================================================================ +void LLMutexBase::lock() +{ +#if LL_DARWIN + if (mLockingThread == LLThread::currentID()) +#else + if (mLockingThread == sThreadID) +#endif + { //redundant lock + mCount++; + return; + } + + apr_thread_mutex_lock(mAPRMutexp); + +#if MUTEX_DEBUG + // Have to have the lock before we can access the debug info + U32 id = LLThread::currentID(); + if (mIsLocked[id] != FALSE) + llerrs << "Already locked in Thread: " << id << llendl; + mIsLocked[id] = TRUE; +#endif + +#if LL_DARWIN + mLockingThread = LLThread::currentID(); +#else + mLockingThread = sThreadID; +#endif +} + +void LLMutexBase::unlock() +{ + if (mCount > 0) + { //not the root unlock + mCount--; + return; + } + +#if MUTEX_DEBUG + // Access the debug info while we have the lock + U32 id = LLThread::currentID(); + if (mIsLocked[id] != TRUE) + llerrs << "Not locked in Thread: " << id << llendl; + mIsLocked[id] = FALSE; +#endif + + mLockingThread = NO_THREAD; + apr_thread_mutex_unlock(mAPRMutexp); +} + bool LLMutexBase::isLocked() { if (!tryLock()) @@ -328,6 +413,11 @@ bool LLMutexBase::isLocked() return false; } +U32 LLMutexBase::lockingThread() const +{ + return mLockingThread; +} + //============================================================================ LLCondition::LLCondition(AIAPRPool& parent) : LLMutex(parent) @@ -335,14 +425,25 @@ LLCondition::LLCondition(AIAPRPool& parent) : LLMutex(parent) apr_thread_cond_create(&mAPRCondp, mPool()); } + LLCondition::~LLCondition() { apr_thread_cond_destroy(mAPRCondp); mAPRCondp = NULL; } + void LLCondition::wait() { + if (!isLocked()) + { //mAPRMutexp MUST be locked before calling apr_thread_cond_wait + apr_thread_mutex_lock(mAPRMutexp); +#if MUTEX_DEBUG + // avoid asserts on destruction in non-release builds + U32 id = LLThread::currentID(); + mIsLocked[id] = TRUE; +#endif + } apr_thread_cond_wait(mAPRCondp, mAPRMutexp); } diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index 97dac455e..7939a0d56 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -48,6 +48,12 @@ class LLThread; class LLMutex; class LLCondition; +#if LL_WINDOWS +#define ll_thread_local __declspec(thread) +#else +#define ll_thread_local __thread +#endif + class LL_COMMON_API AIThreadLocalData { private: @@ -66,6 +72,9 @@ public: class LL_COMMON_API LLThread { +private: + static U32 sIDIter; + public: typedef enum e_thread_status { @@ -106,6 +115,8 @@ public: // Return thread-local data for the current thread. static AIThreadLocalData& tldata(void) { return AIThreadLocalData::tldata(); } + U32 getID() const { return mID; } + private: BOOL mPaused; @@ -118,6 +129,7 @@ protected: apr_thread_t *mAPRThreadp; EThreadStatus mStatus; + U32 mID; friend void AIThreadLocalData::create(LLThread* threadp); AIThreadLocalData* mThreadLocalData; @@ -151,16 +163,26 @@ protected: class LL_COMMON_API LLMutexBase { public: - void lock() { apr_thread_mutex_lock(mAPRMutexp); } - void unlock() { apr_thread_mutex_unlock(mAPRMutexp); } + typedef enum + { + NO_THREAD = 0xFFFFFFFF + } e_locking_thread; + + LLMutexBase() : mLockingThread(NO_THREAD), mCount(0) {} + + void lock(); //blocks + void unlock(); // Returns true if lock was obtained successfully. bool tryLock() { return !APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp)); } bool isLocked(); // non-blocking, but does do a lock/unlock so not free + U32 lockingThread() const; //get ID of locking thread protected: // mAPRMutexp is initialized and uninitialized in the derived class. apr_thread_mutex_t* mAPRMutexp; + mutable U32 mCount; + mutable U32 mLockingThread; }; class LL_COMMON_API LLMutex : public LLMutexBase @@ -350,6 +372,7 @@ void LLThread::unlockData() mRunCondition->unlock(); } + //============================================================================ // see llmemory.h for LLPointer<> definition diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 04011b6ae..c67249ebf 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -603,7 +603,7 @@ void LLMeshRepoThread::loadMeshPhysicsShape(const LLUUID& mesh_id) } -void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool do_lock) +void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) { //protected by mSignal, no locking needed here mesh_header_map::iterator iter = mMeshHeader.find(mesh_params.getSculptID()); @@ -611,11 +611,8 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod, b { //if we have the header, request LOD byte range LODRequest req(mesh_params, lod); { - if(do_lock) - mMutex->lock(); + LLMutexLock lock(mMutex); mLODReqQ.push(req); - if(do_lock) - mMutex->unlock(); } } else @@ -631,12 +628,9 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod, b } else { //if no header request is pending, fetch header - if(do_lock) - mMutex->lock(); + LLMutexLock lock(mMutex); mHeaderReqQ.push(req); mPendingLOD[mesh_params].push_back(lod); - if(do_lock) - mMutex->unlock(); } } } @@ -1604,10 +1598,10 @@ void LLMeshRepoThread::notifyLoadedMeshes() {//called via gMeshRepo.notifyLoadedMeshes(). mMutex already locked while (!mLoadedQ.empty()) { - //mMutex->lock(); + mMutex->lock(); LoadedMesh mesh = mLoadedQ.front(); mLoadedQ.pop(); - //mMutex->unlock(); + mMutex->unlock(); if (mesh.mVolume && mesh.mVolume->getNumVolumeFaces() > 0) { @@ -1622,10 +1616,10 @@ void LLMeshRepoThread::notifyLoadedMeshes() while (!mUnavailableQ.empty()) { - //mMutex->lock(); + mMutex->lock(); LODRequest req = mUnavailableQ.front(); mUnavailableQ.pop(); - //mMutex->unlock(); + mMutex->unlock(); gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD); } @@ -2405,7 +2399,7 @@ void LLMeshRepository::notifyLoadedMeshes() { LLFastTimer t(LLFastTimer::FTM_LOAD_MESH_LOD); LLMeshRepoThread::LODRequest& request = mPendingRequests.front(); - mThread->loadMeshLOD(request.mMeshParams, request.mLOD, false); + mThread->loadMeshLOD(request.mMeshParams, request.mLOD); mPendingRequests.erase(mPendingRequests.begin()); push_count--; } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index fe905764d..80511a1d8 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -349,7 +349,7 @@ public: virtual void run(); - void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool do_lock = true); + void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod); bool fetchMeshHeader(const LLVolumeParams& mesh_params); bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod); bool headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size); From 4034c3464be7abb0fceaf83dda3c42d0b47b05d2 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sun, 31 Jul 2011 02:28:29 -0500 Subject: [PATCH 12/22] Just making sure only one error thread is created. --- indra/llcommon/llapp.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index c35a84255..13a571e69 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -297,10 +297,13 @@ void LLApp::startErrorThread() // Start the error handling thread, which is responsible for taking action // when the app goes into the APP_STATUS_ERROR state // - llinfos << "Starting error thread" << llendl; - mThreadErrorp = new LLErrorThread(); - mThreadErrorp->setUserData((void *) this); - mThreadErrorp->start(); + if(!mThreadErrorp) + { + llinfos << "Starting error thread" << llendl; + mThreadErrorp = new LLErrorThread(); + mThreadErrorp->setUserData((void *) this); + mThreadErrorp->start(); + } } void LLApp::setErrorHandler(LLAppErrorHandler handler) From d39751384025d901b9d93a1297aa2b3a7a1013f4 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sun, 31 Jul 2011 02:52:15 -0500 Subject: [PATCH 13/22] Fixed a mesh-related fasttimer. A freaking \t snuck in. Also, this old fasttimer class is really annoying to maintain. --- indra/newview/llfasttimerview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp index 210de0faf..57a313b9c 100644 --- a/indra/newview/llfasttimerview.cpp +++ b/indra/newview/llfasttimerview.cpp @@ -163,7 +163,7 @@ static struct ft_display_info ft_display_table[] = #if MESH_ENABLED { LLFastTimer::FTM_MESH_UPDATE, " Mesh Update", &LLColor4::orange4, 0 }, { LLFastTimer::FTM_MESH_LOCK1, " Lock 1", &LLColor4::orange5, 0 }, - { LLFastTimer::FTM_MESH_LOCK2, " Lock 2", &LLColor4::orange6, 0 }, + { LLFastTimer::FTM_MESH_LOCK2, " Lock 2", &LLColor4::orange6, 0 }, { LLFastTimer::FTM_LOAD_MESH_LOD, " Load LOD", &LLColor4::yellow3, 0 }, #endif //MESH_ENABLED { LLFastTimer::FTM_REBUILD_OCCLUSION_VB," Occlusion", &LLColor4::cyan5, 0 }, From 28568add4c14bfa07c981e25e8129d427ce8ef35 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sun, 31 Jul 2011 02:53:26 -0500 Subject: [PATCH 14/22] Threaded cURL. Consider experimental (what in this branch isn't?), however it drastically reduces hitching for me... so yup. --- indra/llmessage/llcurl.cpp | 126 +++++++++++++++++++++----- indra/llmessage/llcurl.h | 2 +- indra/newview/llxmlrpctransaction.cpp | 10 +- 3 files changed, 110 insertions(+), 28 deletions(-) diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index 6b8e86b94..6fe9dea2c 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -620,11 +620,18 @@ void LLCurl::Easy::prepRequest(const std::string& url, //////////////////////////////////////////////////////////////////////////// -class LLCurl::Multi +class LLCurl::Multi : public LLThread { LOG_CLASS(Multi); public: - + + typedef enum + { + PERFORM_STATE_READY=0, + PERFORM_STATE_PERFORMING=1, + PERFORM_STATE_COMPLETED=2 + } ePerformState; + Multi(); ~Multi(); @@ -634,13 +641,20 @@ public: void removeEasy(Easy* easy); S32 process(); - S32 perform(); + void perform(); + virtual void run(); + CURLMsg* info_read(S32* msgs_in_queue); S32 mQueued; S32 mErrorCount; + S32 mPerformState; + + LLCondition* mSignal; + bool mQuitting; + private: void easyFree(Easy*); @@ -655,21 +669,32 @@ private: }; LLCurl::Multi::Multi() - : mQueued(0), - mErrorCount(0) + : LLThread("Curl Multi"), + mQueued(0), + mErrorCount(0), + mPerformState(PERFORM_STATE_READY) { + mQuitting = false; + mSignal = new LLCondition; + mCurlMultiHandle = curl_multi_init(); if (!mCurlMultiHandle) { llwarns << "curl_multi_init() returned NULL! Easy handles: " << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << llendl; mCurlMultiHandle = curl_multi_init(); } + llassert_always(mCurlMultiHandle); ++gCurlMultiCount; } LLCurl::Multi::~Multi() { + llassert(isStopped()); + + delete mSignal; + mSignal = NULL; + // Clean up active for(easy_active_list_t::iterator iter = mEasyActiveList.begin(); iter != mEasyActiveList.end(); ++iter) @@ -695,30 +720,50 @@ CURLMsg* LLCurl::Multi::info_read(S32* msgs_in_queue) return curlmsg; } - -S32 LLCurl::Multi::perform() +void LLCurl::Multi::perform() { - S32 q = 0; - for (S32 call_count = 0; - call_count < MULTI_PERFORM_CALL_REPEAT; - call_count += 1) + if (mPerformState == PERFORM_STATE_READY) { - CURLMcode code = curl_multi_perform(mCurlMultiHandle, &q); - if (CURLM_CALL_MULTI_PERFORM != code || q == 0) - { - check_curl_multi_code(code); - break; - } - + mSignal->signal(); + } +} + +void LLCurl::Multi::run() +{ + while (!mQuitting) + { + mSignal->wait(); + mPerformState = PERFORM_STATE_PERFORMING; + if (!mQuitting) + { + S32 q = 0; + for (S32 call_count = 0; + call_count < MULTI_PERFORM_CALL_REPEAT; + call_count += 1) + { + CURLMcode code = curl_multi_perform(mCurlMultiHandle, &q); + if (CURLM_CALL_MULTI_PERFORM != code || q == 0) + { + check_curl_multi_code(code); + break; + } + + } + mQueued = q; + mPerformState = PERFORM_STATE_COMPLETED; + } } - mQueued = q; - return q; } S32 LLCurl::Multi::process() { perform(); - + + if (mPerformState != PERFORM_STATE_COMPLETED) + { + return 0; + } + CURLMsg* msg; int msgs_in_queue; @@ -749,6 +794,8 @@ S32 LLCurl::Multi::process() } } } + + mPerformState = PERFORM_STATE_READY; return processed; } @@ -827,6 +874,18 @@ LLCurlRequest::LLCurlRequest() : LLCurlRequest::~LLCurlRequest() { llassert_always(mThreadID == LLThread::currentID()); + + //stop all Multi handle background threads + for (curlmulti_set_t::iterator iter = mMultiSet.begin(); iter != mMultiSet.end(); ++iter) + { + LLCurl::Multi* multi = *iter; + multi->mQuitting = true; + while (!multi->isStopped()) + { + multi->mSignal->signal(); + apr_sleep(1000); + } + } for_each(mMultiSet.begin(), mMultiSet.end(), DeletePointer()); } @@ -834,6 +893,7 @@ void LLCurlRequest::addMulti() { llassert_always(mThreadID == LLThread::currentID()); LLCurl::Multi* multi = new LLCurl::Multi(); + multi->start(); mMultiSet.insert(multi); mActiveMulti = multi; mActiveRequestCount = 0; @@ -963,6 +1023,13 @@ S32 LLCurlRequest::process() if (multi != mActiveMulti && tres == 0 && multi->mQueued == 0) { mMultiSet.erase(curiter); + multi->mQuitting = true; + while (!multi->isStopped()) + { + multi->mSignal->signal(); + apr_sleep(1000); + } + delete multi; } } @@ -993,6 +1060,7 @@ LLCurlEasyRequest::LLCurlEasyRequest() mResultReturned(false) { mMulti = new LLCurl::Multi(); + mMulti->start(); mEasy = mMulti->allocEasy(); if (mEasy) { @@ -1003,6 +1071,12 @@ LLCurlEasyRequest::LLCurlEasyRequest() LLCurlEasyRequest::~LLCurlEasyRequest() { + mMulti->mQuitting = true; + while (!mMulti->isStopped()) + { + mMulti->mSignal->signal(); + apr_sleep(1000); + } delete mMulti; } @@ -1099,14 +1173,20 @@ void LLCurlEasyRequest::requestComplete() } } -S32 LLCurlEasyRequest::perform() +void LLCurlEasyRequest::perform() { - return mMulti->perform(); + mMulti->perform(); } // Usage: Call getRestult until it returns false (no more messages) bool LLCurlEasyRequest::getResult(CURLcode* result, LLCurl::TransferInfo* info) { + if (mMulti->mPerformState != LLCurl::Multi::PERFORM_STATE_COMPLETED) + { //we're busy, try again later + return false; + } + mMulti->mPerformState = LLCurl::Multi::PERFORM_STATE_READY; + if (!mEasy) { // Special case - we failed to initialize a curl_easy (can happen if too many open files) diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index 084408c70..02abd7400 100644 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -241,7 +241,7 @@ public: void slist_append(const char* str); void sendRequest(const std::string& url); void requestComplete(); - S32 perform(); + void perform(); bool getResult(CURLcode* result, LLCurl::TransferInfo* info = NULL); std::string getErrorString(); diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index c9254d740..7249a8689 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -319,16 +319,18 @@ bool LLXMLRPCTransaction::Impl::process() } } - const F32 MAX_PROCESSING_TIME = 0.05f; - LLTimer timer; + //const F32 MAX_PROCESSING_TIME = 0.05f; + //LLTimer timer; - while (mCurlRequest->perform() > 0) + mCurlRequest->perform(); + + /*while (mCurlRequest->perform() > 0) { if (timer.getElapsedTimeF32() >= MAX_PROCESSING_TIME) { return false; } - } + }*/ while(1) { From fd50e54a7dfdf6ed13f76aee11cc92d068ccf0d0 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sun, 31 Jul 2011 19:00:58 -0500 Subject: [PATCH 15/22] Fixed rigged shaders not loading under certain scenarios. --- indra/newview/llviewershadermgr.cpp | 38 ++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index a394e5c21..7d1b7ca29 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -431,7 +431,6 @@ void LLViewerShaderMgr::setShaders() // Load all shaders to set max levels loadShadersEnvironment(); loadShadersWater(); - loadShadersObject(); loadShadersWindLight(); loadShadersEffects(); loadShadersInterface(); @@ -440,7 +439,7 @@ void LLViewerShaderMgr::setShaders() mVertexShaderLevel[SHADER_AVATAR] = 3; mMaxAvatarShaderLevel = 3; - if (gSavedSettings.getBOOL("RenderAvatarVP")) + if (gSavedSettings.getBOOL("RenderAvatarVP") && loadShadersObject()) { BOOL avatar_cloth = gSavedSettings.getBOOL("RenderAvatarCloth"); S32 avatar_class = 1; @@ -474,13 +473,25 @@ void LLViewerShaderMgr::setShaders() else { mVertexShaderLevel[SHADER_AVATAR] = 0; - gSavedSettings.setBOOL("RenderAvatarCloth", FALSE); + mVertexShaderLevel[SHADER_DEFERRED] = 0; + + if (gSavedSettings.getBOOL("RenderAvatarVP")) + { + gSavedSettings.setBOOL("RenderDeferred", FALSE); + gSavedSettings.setBOOL("RenderAvatarCloth", FALSE); + gSavedSettings.setBOOL("RenderAvatarVP", FALSE); + } + loadShadersAvatar(); // unloads + loadShadersObject(); } if (!loadShadersDeferred()) { gSavedSettings.setBOOL("RenderDeferred", FALSE); + reentrance = false; + setShaders(); + return; } } else @@ -841,7 +852,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() if (mVertexShaderLevel[SHADER_DEFERRED] == 0) { unloadShaderClass(SHADER_DEFERRED); - return FALSE; + return TRUE; } mVertexShaderLevel[SHADER_AVATAR] = 1; @@ -1045,7 +1056,8 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() if (success) { std::string fragment; - + + //Keep this! Fixes shadow softening with ssao off. if (mVertexShaderLevel[SHADER_DEFERRED] < 2 && !gSavedSettings.getBOOL("RenderDeferredSSAO")) { fragment = "deferred/softenLightNoSSAOF.glsl"; @@ -1083,6 +1095,20 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredAvatarShadowProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; success = gDeferredAvatarShadowProgram.createShader(&mAvatarAttribs, &mAvatarUniforms); } + +#if MESH_ENABLED + if (success) + { + gDeferredAttachmentShadowProgram.mName = "Deferred Attachment Shadow Shader"; + gDeferredAttachmentShadowProgram.mFeatures.hasObjectSkinning = true; + gDeferredAttachmentShadowProgram.mShaderFiles.clear(); + gDeferredAttachmentShadowProgram.mShaderFiles.push_back(make_pair("deferred/attachmentShadowV.glsl", GL_VERTEX_SHADER_ARB)); + gDeferredAttachmentShadowProgram.mShaderFiles.push_back(make_pair("deferred/attachmentShadowF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredAttachmentShadowProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; + success = gDeferredAttachmentShadowProgram.createShader(NULL, NULL); + } +#endif //MESH_ENABLED + if (success) { gTerrainProgram.mName = "Deferred Terrain Shader"; @@ -1216,7 +1242,7 @@ BOOL LLViewerShaderMgr::loadShadersObject() if (mVertexShaderLevel[SHADER_OBJECT] == 0) { unloadShaderClass(SHADER_OBJECT); - return FALSE; + return TRUE; } if (success) From e21c9637aecf92f79607334418df7918da951842 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sun, 31 Jul 2011 19:01:59 -0500 Subject: [PATCH 16/22] Added some glFlushes to lessen framestalls --- indra/llrender/llrender.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index 552814c7e..64f92ae73 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -119,6 +119,8 @@ void LLTexUnit::refreshState(void) // We set dirty to true so that the tex unit knows to ignore caching // and we reset the cached tex unit state + gGL.flush(); + glActiveTextureARB(GL_TEXTURE0_ARB + mIndex); // @@ -163,6 +165,7 @@ void LLTexUnit::activate(void) if ((S32)gGL.mCurrTextureUnitIndex != mIndex || gGL.mDirty) { + gGL.flush(); glActiveTextureARB(GL_TEXTURE0_ARB + mIndex); gGL.mCurrTextureUnitIndex = mIndex; } @@ -180,6 +183,7 @@ void LLTexUnit::enable(eTextureType type) disable(); // Force a disable of a previous texture type if it's enabled. } mCurrTexType = type; + gGL.flush(); if (mIndex < gGLManager.mNumTextureUnits) { glEnable(sGLTextureType[type]); @@ -195,6 +199,7 @@ void LLTexUnit::disable(void) { activate(); unbind(mCurrTexType); + gGL.flush(); if (mIndex < gGLManager.mNumTextureUnits) { glDisable(sGLTextureType[mCurrTexType]); @@ -279,6 +284,7 @@ bool LLTexUnit::bind(LLImageGL* texture, bool for_rendering, bool forceBind) } if ((mCurrTexture != texture->getTexName()) || forceBind) { + gGL.flush(); activate(); enable(texture->getTarget()); mCurrTexture = texture->getTexName(); @@ -406,6 +412,8 @@ void LLTexUnit::setTextureAddressMode(eTextureAddressMode mode) { if (mIndex < 0 || mCurrTexture == 0) return; + gGL.flush(); + activate(); glTexParameteri (sGLTextureType[mCurrTexType], GL_TEXTURE_WRAP_S, sGLAddressMode[mode]); @@ -420,6 +428,8 @@ void LLTexUnit::setTextureFilteringOption(LLTexUnit::eTextureFilterOptions optio { if (mIndex < 0 || mCurrTexture == 0) return; + gGL.flush(); + if (option == TFO_POINT) { glTexParameteri(sGLTextureType[mCurrTexType], GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -472,6 +482,8 @@ void LLTexUnit::setTextureBlendType(eTextureBlendType type) return; } + gGL.flush(); + activate(); mCurrBlendType = type; S32 scale_amount = 1; @@ -588,6 +600,7 @@ void LLTexUnit::setTextureCombiner(eTextureBlendOp op, eTextureBlendSrc src1, eT if (mCurrBlendType != TB_COMBINE || gGL.mDirty) { mCurrBlendType = TB_COMBINE; + gGL.flush(); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); } @@ -598,6 +611,8 @@ void LLTexUnit::setTextureCombiner(eTextureBlendOp op, eTextureBlendSrc src1, eT return; } + gGL.flush(); + // Get the gl source enums according to the eTextureBlendSrc sources passed in GLint source1 = getTextureSource(src1); GLint source2 = getTextureSource(src2); @@ -730,6 +745,7 @@ void LLTexUnit::setColorScale(S32 scale) if (mCurrColorScale != scale || gGL.mDirty) { mCurrColorScale = scale; + gGL.flush(); glTexEnvi( GL_TEXTURE_ENV, GL_RGB_SCALE, scale ); } } @@ -739,6 +755,7 @@ void LLTexUnit::setAlphaScale(S32 scale) if (mCurrAlphaScale != scale || gGL.mDirty) { mCurrAlphaScale = scale; + gGL.flush(); glTexEnvi( GL_TEXTURE_ENV, GL_ALPHA_SCALE, scale ); } } From 40d644e432bc507f6dab02381b0970e14d01ddb4 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sun, 31 Jul 2011 19:03:25 -0500 Subject: [PATCH 17/22] Object edit subpanel now recognizes meshes and disables stitching and block-type dropdowns. --- indra/newview/llpanelobject.cpp | 55 +++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index d448368ea..354ea1c0c 100644 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -735,7 +735,7 @@ void LLPanelObject::getState( ) BOOL enabled = FALSE; BOOL hole_enabled = FALSE; F32 scale_x=1.f, scale_y=1.f; - + BOOL isMesh = FALSE; if( !objectp || !objectp->getVolume() || !editable || !single_volume) { // Clear out all geometry fields. @@ -1399,15 +1399,9 @@ void LLPanelObject::getState( ) mCtrlSculptTexture->setVisible(sculpt_texture_visible); mLabelSculptType->setVisible(sculpt_texture_visible); mCtrlSculptType->setVisible(sculpt_texture_visible); - mCtrlSculptMirror->setVisible(sculpt_texture_visible); - mCtrlSculptInvert->setVisible(sculpt_texture_visible); - - - // sculpt texture - if (selected_item == MI_SCULPT) { LLUUID id; @@ -1421,36 +1415,37 @@ void LLPanelObject::getState( ) mSculptTextureRevert = sculpt_params->getSculptTexture(); mSculptTypeRevert = sculpt_params->getSculptType(); } - + U8 sculpt_type = sculpt_params->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; +#if MESH_ENABLED + isMesh = (sculpt_stitching == LL_SCULPT_TYPE_MESH); +#endif //MESH_ENABLED + LLTextureCtrl* mTextureCtrl = getChild("sculpt texture control"); if(mTextureCtrl) { mTextureCtrl->setTentative(FALSE); - mTextureCtrl->setEnabled(editable); + mTextureCtrl->setEnabled(editable && !isMesh); if (editable) mTextureCtrl->setImageAssetID(sculpt_params->getSculptTexture()); else mTextureCtrl->setImageAssetID(LLUUID::null); } - - - - U8 sculpt_type = sculpt_params->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; + mComboBaseType->setEnabled(!isMesh); if (mCtrlSculptType) { mCtrlSculptType->setCurrentByIndex(sculpt_stitching); - mCtrlSculptType->setEnabled(editable); + mCtrlSculptType->setEnabled(editable && !isMesh); } if (mCtrlSculptMirror) { mCtrlSculptMirror->set(sculpt_mirror); - mCtrlSculptMirror->setEnabled(editable); + mCtrlSculptMirror->setEnabled(editable && !isMesh); } if (mCtrlSculptInvert) @@ -1463,18 +1458,16 @@ void LLPanelObject::getState( ) { mLabelSculptType->setEnabled(TRUE); } + } } - - - - - else { - mSculptTextureRevert = LLUUID::null; + mSculptTextureRevert = LLUUID::null; } + mCtrlSculptMirror->setVisible(sculpt_texture_visible && !isMesh); + mCtrlSculptInvert->setVisible(sculpt_texture_visible && !isMesh); //---------------------------------------------------------------------------- @@ -2261,6 +2254,20 @@ void LLPanelObject::sendSculpt() if (mCtrlSculptType) sculpt_type |= mCtrlSculptType->getCurrentIndex(); + bool enabled = true; +#if MESH_ENABLED + enabled = sculpt_type != LL_SCULPT_TYPE_MESH; +#endif //MESH_ENABLED + + if (mCtrlSculptMirror) + { + mCtrlSculptMirror->setEnabled(enabled ? TRUE : FALSE); + } + if (mCtrlSculptInvert) + { + mCtrlSculptInvert->setEnabled(enabled ? TRUE : FALSE); + } + if ((mCtrlSculptMirror) && (mCtrlSculptMirror->get())) sculpt_type |= LL_SCULPT_FLAG_MIRROR; From 17bb0799e0ed4c51297084b957356a09c5b2c941 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sun, 31 Jul 2011 19:05:53 -0500 Subject: [PATCH 18/22] Added LLAgentUI. Includes improved buildLocationString. --- indra/newview/CMakeLists.txt | 2 + indra/newview/llagent.cpp | 56 +-------- indra/newview/llagent.h | 1 - indra/newview/llagentui.cpp | 184 ++++++++++++++++++++++++++++ indra/newview/llagentui.h | 58 +++++++++ indra/newview/llfloaterlandmark.cpp | 12 +- indra/newview/llfloatersnapshot.cpp | 5 +- indra/newview/llstatusbar.cpp | 10 +- indra/newview/llviewermenu.cpp | 3 +- 9 files changed, 262 insertions(+), 69 deletions(-) create mode 100644 indra/newview/llagentui.cpp create mode 100644 indra/newview/llagentui.h diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 7a5c71729..342c19552 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -106,6 +106,7 @@ set(viewer_SOURCE_FILES llagentdata.cpp llagentlanguage.cpp llagentpilot.cpp + llagentui.cpp llagentwearables.cpp llanimstatelabels.cpp llappviewer.cpp @@ -584,6 +585,7 @@ set(viewer_HEADER_FILES llagentdata.h llagentlanguage.h llagentpilot.h + llagentui.h llagentwearables.h llanimstatelabels.h llappearance.h diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 1b8192b36..c41d70ca6 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -39,6 +39,7 @@ #include "llagentaccess.h" #include "llagentcamera.h" #include "llagentwearables.h" +#include "llagentui.h" #include "llanimationstates.h" #include "llcallingcard.h" #include "llconsole.h" @@ -2518,61 +2519,6 @@ BOOL LLAgent::setUserGroupFlags(const LLUUID& group_id, BOOL accept_notices, BOO return FALSE; } -// utility to build a location string -void LLAgent::buildLocationString(std::string& str) -{ -// [RLVa:KB] - Checked: 2009-07-04 (RLVa-1.0.0a) - if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) - { - str = RlvStrings::getString(RLV_STRING_HIDDEN); - return; - } -// [/RLVa:KB] - - const LLVector3& agent_pos_region = getPositionAgent(); - S32 pos_x = S32(agent_pos_region.mV[VX]); - S32 pos_y = S32(agent_pos_region.mV[VY]); - S32 pos_z = S32(agent_pos_region.mV[VZ]); - - // Round the numbers based on the velocity - LLVector3 agent_velocity = getVelocity(); - F32 velocity_mag_sq = agent_velocity.magVecSquared(); - - const F32 FLY_CUTOFF = 6.f; // meters/sec - const F32 FLY_CUTOFF_SQ = FLY_CUTOFF * FLY_CUTOFF; - const F32 WALK_CUTOFF = 1.5f; // meters/sec - const F32 WALK_CUTOFF_SQ = WALK_CUTOFF * WALK_CUTOFF; - - if (velocity_mag_sq > FLY_CUTOFF_SQ) - { - pos_x -= pos_x % 4; - pos_y -= pos_y % 4; - } - else if (velocity_mag_sq > WALK_CUTOFF_SQ) - { - pos_x -= pos_x % 2; - pos_y -= pos_y % 2; - } - - // create a defult name and description for the landmark - std::string buffer; - if( LLViewerParcelMgr::getInstance()->getAgentParcelName().empty() ) - { - // the parcel doesn't have a name - buffer = llformat("%.32s (%d, %d, %d)", - getRegion()->getName().c_str(), - pos_x, pos_y, pos_z); - } - else - { - // the parcel has a name, so include it in the landmark name - buffer = llformat("%.32s, %.32s (%d, %d, %d)", - LLViewerParcelMgr::getInstance()->getAgentParcelName().c_str(), - getRegion()->getName().c_str(), - pos_x, pos_y, pos_z); - } - str = buffer; -} LLQuaternion LLAgent::getHeadRotation() { diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index ff109d2ea..2c593f027 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -253,7 +253,6 @@ public: const LLHost& getRegionHost() const; BOOL inPrelude(); std::string getSLURL() const; //Return uri for current region - void buildLocationString(std::string& str); //Build a description string for current location // struct SHLureRequest diff --git a/indra/newview/llagentui.cpp b/indra/newview/llagentui.cpp new file mode 100644 index 000000000..7f6875385 --- /dev/null +++ b/indra/newview/llagentui.cpp @@ -0,0 +1,184 @@ +/** + * @file llagentui.cpp + * @brief Utility methods to process agent's data as slurl's etc. before displaying + * + * $LicenseInfo:firstyear=2009&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 "llagentui.h" + +// Library includes +#include "llparcel.h" + +// Viewer includes +#include "llagent.h" +#include "llviewercontrol.h" +#include "llviewerregion.h" +#include "llviewerparcelmgr.h" +// [RLVa:KB] - Checked: 2010-09-27 (RLVa-1.1.3b) +#include "rlvhandler.h" +// [/RLVa:KB] + +//static +void LLAgentUI::buildFullname(std::string& name) +{ + if (isAgentAvatarValid()) + name = gAgent.getAvatarObject()->getFullname(); +} + +/* +//static +void LLAgentUI::buildSLURL(LLSLURL& slurl, const bool escaped /*= true*//*) +{ + LLSLURL return_slurl; + LLViewerRegion *regionp = gAgent.getRegion(); + if (regionp) + { + return_slurl = LLSLURL(regionp->getName(), gAgent.getPositionGlobal()); + } + slurl = return_slurl; +}*/ + +//static +BOOL LLAgentUI::checkAgentDistance(const LLVector3& pole, F32 radius) +{ + F32 delta_x = gAgent.getPositionAgent().mV[VX] - pole.mV[VX]; + F32 delta_y = gAgent.getPositionAgent().mV[VY] - pole.mV[VY]; + + return sqrt( delta_x* delta_x + delta_y* delta_y ) < radius; +} +BOOL LLAgentUI::buildLocationString(std::string& str, ELocationFormat fmt,const LLVector3& agent_pos_region) +{ +// [RLVa:KB] - Checked: 2009-07-04 (RLVa-1.0.0a) + if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) + { + str = RlvStrings::getString(RLV_STRING_HIDDEN); + return TRUE; + } +// [/RLVa:KB] + LLViewerRegion* region = gAgent.getRegion(); + LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + + if (!region || !parcel) return FALSE; + + S32 pos_x = S32(agent_pos_region.mV[VX]); + S32 pos_y = S32(agent_pos_region.mV[VY]); + S32 pos_z = S32(agent_pos_region.mV[VZ]); + + // Round the numbers based on the velocity + F32 velocity_mag_sq = gAgent.getVelocity().magVecSquared(); + + const F32 FLY_CUTOFF = 6.f; // meters/sec + const F32 FLY_CUTOFF_SQ = FLY_CUTOFF * FLY_CUTOFF; + const F32 WALK_CUTOFF = 1.5f; // meters/sec + const F32 WALK_CUTOFF_SQ = WALK_CUTOFF * WALK_CUTOFF; + + if (velocity_mag_sq > FLY_CUTOFF_SQ) + { + pos_x -= pos_x % 4; + pos_y -= pos_y % 4; + } + else if (velocity_mag_sq > WALK_CUTOFF_SQ) + { + pos_x -= pos_x % 2; + pos_y -= pos_y % 2; + } + + // create a default name and description for the landmark + std::string parcel_name = LLViewerParcelMgr::getInstance()->getAgentParcelName(); + std::string region_name = region->getName(); + std::string sim_access_string = region->getSimAccessString(); + std::string buffer; + if( parcel_name.empty() ) + { + // the parcel doesn't have a name + switch (fmt) + { + case LOCATION_FORMAT_LANDMARK: + buffer = llformat("%.100s", region_name.c_str()); + break; + case LOCATION_FORMAT_NORMAL: + buffer = llformat("%s", region_name.c_str()); + break; + case LOCATION_FORMAT_NO_COORDS: + buffer = llformat("%s%s%s", + region_name.c_str(), + sim_access_string.empty() ? "" : " - ", + sim_access_string.c_str()); + break; + case LOCATION_FORMAT_NO_MATURITY: + buffer = llformat("%s (%d, %d, %d)", + region_name.c_str(), + pos_x, pos_y, pos_z); + break; + case LOCATION_FORMAT_FULL: + buffer = llformat("%s (%d, %d, %d)%s%s", + region_name.c_str(), + pos_x, pos_y, pos_z, + sim_access_string.empty() ? "" : " - ", + sim_access_string.c_str()); + break; + } + } + else + { + // the parcel has a name, so include it in the landmark name + switch (fmt) + { + case LOCATION_FORMAT_LANDMARK: + buffer = llformat("%.100s", parcel_name.c_str()); + break; + case LOCATION_FORMAT_NORMAL: + buffer = llformat("%s, %s", parcel_name.c_str(), region_name.c_str()); + break; + case LOCATION_FORMAT_NO_MATURITY: + buffer = llformat("%s, %s (%d, %d, %d)", + parcel_name.c_str(), + region_name.c_str(), + pos_x, pos_y, pos_z); + break; + case LOCATION_FORMAT_NO_COORDS: + buffer = llformat("%s, %s%s%s", + parcel_name.c_str(), + region_name.c_str(), + sim_access_string.empty() ? "" : " - ", + sim_access_string.c_str()); + break; + case LOCATION_FORMAT_FULL: + buffer = llformat("%s, %s (%d, %d, %d)%s%s", + parcel_name.c_str(), + region_name.c_str(), + pos_x, pos_y, pos_z, + sim_access_string.empty() ? "" : " - ", + sim_access_string.c_str()); + break; + } + } + str = buffer; + return TRUE; +} +BOOL LLAgentUI::buildLocationString(std::string& str, ELocationFormat fmt) +{ + return buildLocationString(str,fmt, gAgent.getPositionAgent()); +} diff --git a/indra/newview/llagentui.h b/indra/newview/llagentui.h new file mode 100644 index 000000000..dda5dc1fd --- /dev/null +++ b/indra/newview/llagentui.h @@ -0,0 +1,58 @@ +/** + * @file llagentui.h + * @brief Utility methods to process agent's data as slurl's etc. before displaying + * + * $LicenseInfo:firstyear=2009&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 LLAGENTUI_H +#define LLAGENTUI_H + +class LLSLURL; + +class LLAgentUI +{ +public: + enum ELocationFormat + { + LOCATION_FORMAT_NORMAL, // Parcel + LOCATION_FORMAT_LANDMARK, // Parcel, Region + LOCATION_FORMAT_NO_MATURITY, // Parcel, Region (x, y, z) + LOCATION_FORMAT_NO_COORDS, // Parcel, Region - Maturity + LOCATION_FORMAT_FULL, // Parcel, Region (x, y, z) - Maturity + }; + + static void buildFullname(std::string &name); + + static void buildSLURL(LLSLURL& slurl, const bool escaped = true); + //build location string using the current position of gAgent. + static BOOL buildLocationString(std::string& str, ELocationFormat fmt = LOCATION_FORMAT_LANDMARK); + //build location string using a region position of the avatar. + static BOOL buildLocationString(std::string& str, ELocationFormat fmt,const LLVector3& agent_pos_region); + /** + * @brief Check whether the agent is in neighborhood of the pole Within same region + * @return true if the agent is in neighborhood. + */ + static BOOL checkAgentDistance(const LLVector3& local_pole, F32 radius); +}; + +#endif //LLAGENTUI_H diff --git a/indra/newview/llfloaterlandmark.cpp b/indra/newview/llfloaterlandmark.cpp index 25b955526..6f0940c9b 100644 --- a/indra/newview/llfloaterlandmark.cpp +++ b/indra/newview/llfloaterlandmark.cpp @@ -35,6 +35,7 @@ #include "llfloaterlandmark.h" #include "llagent.h" +#include "llagentui.h" #include "llcheckboxctrl.h" #include "llviewerparcelmgr.h" #include "llfolderview.h" @@ -299,14 +300,15 @@ void LLFloaterLandmark::onBtnNew(void* userdata) return; } - LLUUID folder_id; - folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK); - std::string pos_string; - gAgent.buildLocationString(pos_string); + std::string landmark_name, landmark_desc; + + LLAgentUI::buildLocationString(landmark_name, LLAgentUI::LOCATION_FORMAT_LANDMARK); + LLAgentUI::buildLocationString(landmark_desc, LLAgentUI::LOCATION_FORMAT_FULL); + const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK); create_inventory_item(gAgent.getID(), gAgent.getSessionID(), folder_id, LLTransactionID::tnull, - pos_string, pos_string, // name, desc + landmark_name, landmark_desc, // name, desc LLAssetType::AT_LANDMARK, LLInventoryType::IT_LANDMARK, NOT_WEARABLE, PERM_ALL, diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index 9289ff7b0..ed7415132 100644 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -68,6 +68,7 @@ #include "lltoolfocus.h" #include "lltoolmgr.h" #include "llworld.h" +#include "llagentui.h" #include "llgl.h" #include "llglheaders.h" @@ -975,9 +976,9 @@ void LLSnapshotLivePreview::saveTexture() { LLVFile::writeFile(formatted->getData(), formatted->getDataSize(), gVFS, new_asset_id, LLAssetType::AT_TEXTURE); std::string pos_string; - gAgent.buildLocationString(pos_string); + LLAgentUI::buildLocationString(pos_string, LLAgentUI::LOCATION_FORMAT_FULL); std::string who_took_it; - gAgent.buildFullname(who_took_it); + LLAgentUI::buildFullname(who_took_it); LLAssetStorage::LLStoreAssetCallback callback = NULL; S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); void *userdata = NULL; diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index bcfd312ae..036d4f76f 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -69,6 +69,7 @@ #include "llviewerthrottle.h" #include "lluictrlfactory.h" #include "llvoiceclient.h" // for gVoiceClient +#include "llagentui.h" #include "lltoolmgr.h" #include "llfocusmgr.h" @@ -495,11 +496,10 @@ void LLStatusBar::refresh() mRegionDetails.mPing = region->getNetDetailsForLCD(); if (parcel) { - location_name = region->getName() - + llformat(" %d, %d, %d (%s) - %s", - pos_x, pos_y, pos_z, - region->getSimAccessString().c_str(), - parcel->getName().c_str()); + if (!LLAgentUI::buildLocationString(location_name, LLAgentUI::LOCATION_FORMAT_FULL)) + { + location_name = "???"; + } // keep these around for the LCD to use mRegionDetails.mRegionName = region->getName(); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index a0c263cf2..2be3d924c 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -252,6 +252,7 @@ #include "floaterao.h" #include "slfloatermediafilter.h" #include "llviewerobjectbackup.h" +#include "llagentui.h" #include "hippogridmanager.h" @@ -6027,7 +6028,7 @@ class LLWorldCreateLandmark : public view_listener_t LLUUID folder_id; folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK); std::string pos_string; - gAgent.buildLocationString(pos_string); + LLAgentUI::buildLocationString(pos_string); create_inventory_item(gAgent.getID(), gAgent.getSessionID(), folder_id, LLTransactionID::tnull, From 3c244e6df02001d114bc5faf3296954f1e4da755 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sun, 31 Jul 2011 19:08:08 -0500 Subject: [PATCH 19/22] Added new setCommitCallback and setValidateCallback to LLUICtrl, using boost::signals2. Kept legacy variants for now since they are just used too frequently to remove right now. --- indra/llui/lluictrl.cpp | 21 ++++++++++++++++++++- indra/llui/lluictrl.h | 11 +++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 3f4ab5e45..4038f0a3e 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -42,6 +42,8 @@ static LLRegisterWidget r("ui_ctrl"); // NOTE: the LLFocusableElement implementation has been moved to llfocusmgr.cpp, to mirror the header where the class is defined. LLUICtrl::LLUICtrl() : + mCommitSignal(NULL), + mValidateSignal(NULL), mCommitCallback(NULL), mLostTopCallback(NULL), mValidateCallback(NULL), @@ -59,7 +61,9 @@ LLUICtrl::LLUICtrl(const std::string& name, const LLRect& rect, BOOL mouse_opaqu : // can't make this automatically follow top and left, breaks lots // of buttons in the UI. JC 7/20/2002 LLView( name, rect, mouse_opaque, reshape ), - mCommitCallback( on_commit_callback) , + mCommitSignal(NULL), + mValidateSignal(NULL), + mCommitCallback( on_commit_callback), mLostTopCallback( NULL ), mValidateCallback( NULL ), mCallbackUserData( callback_userdata ), @@ -78,6 +82,9 @@ LLUICtrl::~LLUICtrl() llwarns << "UI Control holding top ctrl deleted: " << getName() << ". Top view removed." << llendl; gFocusMgr.removeTopCtrlWithoutCallback( this ); } + + delete mCommitSignal; + delete mValidateSignal; } void LLUICtrl::onCommit() @@ -555,3 +562,15 @@ void LLUICtrl::setMinValue(LLSD min_value) // virtual void LLUICtrl::setMaxValue(LLSD max_value) { } + +boost::signals2::connection LLUICtrl::setCommitCallback( const commit_signal_t::slot_type& cb ) +{ + if (!mCommitSignal) mCommitSignal = new commit_signal_t(); + return mCommitSignal->connect(cb); +} + +boost::signals2::connection LLUICtrl::setValidateCallback( const enable_signal_t::slot_type& cb ) +{ + if (!mValidateSignal) mValidateSignal = new enable_signal_t(); + return mValidateSignal->connect(cb); +} diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index be8e86336..519b45c0b 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -43,6 +43,9 @@ class LLUICtrl : public LLView { public: + typedef boost::signals2::signal commit_signal_t; + typedef boost::signals2::signal enable_signal_t; + typedef void (*LLUICtrlCallback)(LLUICtrl* ctrl, void* userdata); typedef BOOL (*LLUICtrlValidate)(LLUICtrl* ctrl, void* userdata); @@ -111,6 +114,12 @@ public: LLUICtrl* getParentUICtrl() const; + //Start using these! + boost::signals2::connection setCommitCallback( const commit_signal_t::slot_type& cb ); + boost::signals2::connection setValidateCallback( const enable_signal_t::slot_type& cb ); + + // *TODO: Deprecate; for backwards compatability only: + //Keeping userdata around with legacy setCommitCallback because it's used ALL OVER THE PLACE. void* getCallbackUserData() const { return mCallbackUserData; } void setCallbackUserData( void* data ) { mCallbackUserData = data; } @@ -132,6 +141,8 @@ public: protected: + commit_signal_t* mCommitSignal; + enable_signal_t* mValidateSignal; void (*mCommitCallback)( LLUICtrl* ctrl, void* userdata ); void (*mLostTopCallback)( LLUICtrl* ctrl, void* userdata ); BOOL (*mValidateCallback)( LLUICtrl* ctrl, void* userdata ); From 5e9cec2cc5ba26e1802cf66173df8d8b626b301b Mon Sep 17 00:00:00 2001 From: Shyotl Date: Sun, 31 Jul 2011 19:09:47 -0500 Subject: [PATCH 20/22] LLTextParser now a singleton. --- indra/llui/lltexteditor.cpp | 2 +- indra/llui/lltextparser.cpp | 44 ++++++++++++++----------------------- indra/llui/lltextparser.h | 32 +++++++++++++-------------- 3 files changed, 33 insertions(+), 45 deletions(-) diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 3326524cb..0a951e7e9 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -4095,7 +4095,7 @@ void LLTextEditor::appendHighlightedText(const std::string &new_text, if (highlight && stylep) { - LLSD pieces = highlight->parsePartialLineHighlights(new_text, stylep->getColor(), highlight_part); + LLSD pieces = highlight->parsePartialLineHighlights(new_text, stylep->getColor(), (LLTextParser::EHighlightPosition)highlight_part); bool lprepend=prepend_newline; for (S32 i=0;iloadFromDisk(); - } - return sInstance; -} // Moved triggerAlerts() to llfloaterchat.cpp to break llui/llaudio library dependency. @@ -103,8 +88,10 @@ S32 LLTextParser::findPattern(const std::string &text, LLSD highlight) return found; } -LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLColor4 &color, S32 part, S32 index) +LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLColor4 &color, EHighlightPosition part, S32 index) { + loadKeywords(); + //evil recursive string atomizer. LLSD ret_llsd, start_llsd, middle_llsd, end_llsd; @@ -122,7 +109,7 @@ LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLC { S32 end = std::string(mHighlights[i]["pattern"]).length(); S32 len = text.length(); - S32 newpart; + EHighlightPosition newpart; if (start==0) { start_llsd[0]["text"] =text.substr(0,end); @@ -195,6 +182,8 @@ LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLC bool LLTextParser::parseFullLineHighlights(const std::string &text, LLColor4 *color) { + loadKeywords(); + for (S32 i=0;i { public: - enum ConditionType { CONTAINS, MATCHES, STARTS_WITH, ENDS_WITH }; - enum HighlightType { PART, ALL }; - enum HighlightPosition { WHOLE, START, MIDDLE, END }; - enum DialogAction { ACTION_NONE, ACTION_CLOSE, ACTION_ADD, ACTION_COPY, ACTION_UPDATE }; + typedef enum e_condition_type { CONTAINS, MATCHES, STARTS_WITH, ENDS_WITH } EConditionType; + typedef enum e_highlight_type { PART, ALL } EHighlightType; + typedef enum e_highlight_position { WHOLE, START, MIDDLE, END } EHighlightPosition; + typedef enum e_dialog_action { ACTION_NONE, ACTION_CLOSE, ACTION_ADD, ACTION_COPY, ACTION_UPDATE } EDialogAction; - static LLTextParser* getInstance(); - LLTextParser(){}; - ~LLTextParser(); + LLTextParser(); - S32 findPattern(const std::string &text, LLSD highlight); - LLSD parsePartialLineHighlights(const std::string &text,const LLColor4 &color,S32 part=WHOLE, S32 index=0); + LLSD parsePartialLineHighlights(const std::string &text,const LLColor4 &color, EHighlightPosition part=WHOLE, S32 index=0); bool parseFullLineHighlights(const std::string &text, LLColor4 *color); - std::string getFileName(); - LLSD loadFromDisk(); - bool saveToDisk(LLSD highlights); -public: - LLSD mHighlights; + S32 findPattern(const std::string &text, LLSD highlight); private: - static LLTextParser* sInstance; + std::string getFileName(); + void loadKeywords(); + bool saveToDisk(LLSD highlights); + +public: + LLSD mHighlights; + bool mLoaded; }; #endif From 2aa4aa78e2a2e0d10dfd95a5dcb49171f1b02b18 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Mon, 1 Aug 2011 01:33:07 -0500 Subject: [PATCH 21/22] Added physics settings to object features panel. Brought in mesh 'prim cost' fetching and now display cost in edit floater if mesh is enabled. --- indra/llcommon/CMakeLists.txt | 1 + indra/llcommon/llaccountingquota.h | 80 ++++ indra/llinventory/llparcel.cpp | 10 + indra/llinventory/llparcel.h | 13 +- indra/llui/llmultisliderctrl.cpp | 26 +- indra/llui/llsliderctrl.cpp | 26 +- indra/llui/llspinctrl.cpp | 76 ++-- indra/llui/lluictrl.cpp | 2 + indra/newview/CMakeLists.txt | 2 + indra/newview/llaccountingquotamanager.cpp | 281 +++++++++++++ indra/newview/llaccountingquotamanager.h | 55 +++ indra/newview/llfloatertools.cpp | 25 +- indra/newview/llpanelvolume.cpp | 391 +++++++++++++----- indra/newview/llpanelvolume.h | 27 +- indra/newview/llselectmgr.cpp | 288 +++++++++++++ indra/newview/llselectmgr.h | 17 + indra/newview/llviewerobject.cpp | 250 ++++++++++- indra/newview/llviewerobject.h | 72 +++- indra/newview/llviewerobjectlist.cpp | 386 +++++++++++++++++ indra/newview/llviewerobjectlist.h | 30 ++ .../skins/default/xui/en-us/floater_tools.xml | 145 +++++-- scripts/messages/message_template.msg | 9 + 22 files changed, 1977 insertions(+), 235 deletions(-) create mode 100644 indra/llcommon/llaccountingquota.h create mode 100644 indra/newview/llaccountingquotamanager.cpp create mode 100644 indra/newview/llaccountingquotamanager.h diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index d6e2c7c45..3132a03e1 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -96,6 +96,7 @@ set(llcommon_HEADER_FILES indra_constants.h linden_common.h linked_lists.h + llaccountingquota.h llagentconstants.h llavatarname.h llapp.h diff --git a/indra/llcommon/llaccountingquota.h b/indra/llcommon/llaccountingquota.h new file mode 100644 index 000000000..140333de0 --- /dev/null +++ b/indra/llcommon/llaccountingquota.h @@ -0,0 +1,80 @@ +/** + * @file llaccountingquota.h + * @ + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, 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_ACCOUNTINGQUOTA_H +#define LL_ACCOUNTINGQUOTA_H + +struct ParcelQuota +{ + ParcelQuota( F32 ownerRenderCost, F32 ownerPhysicsCost, F32 ownerNetworkCost, F32 ownerSimulationCost, + F32 groupRenderCost, F32 groupPhysicsCost, F32 groupNetworkCost, F32 groupSimulationCost, + F32 otherRenderCost, F32 otherPhysicsCost, F32 otherNetworkCost, F32 otherSimulationCost, + F32 tempRenderCost, F32 tempPhysicsCost, F32 tempNetworkCost, F32 tempSimulationCost, + F32 selectedRenderCost, F32 selectedPhysicsCost, F32 selectedNetworkCost, F32 selectedSimulationCost, + F32 parcelCapacity ) + : mOwnerRenderCost( ownerRenderCost ), mOwnerPhysicsCost( ownerPhysicsCost ) + , mOwnerNetworkCost( ownerNetworkCost ), mOwnerSimulationCost( ownerSimulationCost ) + , mGroupRenderCost( groupRenderCost ), mGroupPhysicsCost( groupPhysicsCost ) + , mGroupNetworkCost( groupNetworkCost ), mGroupSimulationCost( groupSimulationCost ) + , mOtherRenderCost( otherRenderCost ), mOtherPhysicsCost( otherPhysicsCost ) + , mOtherNetworkCost( otherNetworkCost ), mOtherSimulationCost( otherSimulationCost ) + , mTempRenderCost( tempRenderCost ), mTempPhysicsCost( tempPhysicsCost ) + , mTempNetworkCost( tempNetworkCost ), mTempSimulationCost( tempSimulationCost ) + , mSelectedRenderCost( tempRenderCost ), mSelectedPhysicsCost( tempPhysicsCost ) + , mSelectedNetworkCost( tempNetworkCost ), mSelectedSimulationCost( selectedSimulationCost ) + , mParcelCapacity( parcelCapacity ) + { + } + + ParcelQuota(){} + F32 mOwnerRenderCost, mOwnerPhysicsCost, mOwnerNetworkCost, mOwnerSimulationCost; + F32 mGroupRenderCost, mGroupPhysicsCost, mGroupNetworkCost, mGroupSimulationCost; + F32 mOtherRenderCost, mOtherPhysicsCost, mOtherNetworkCost, mOtherSimulationCost; + F32 mTempRenderCost, mTempPhysicsCost, mTempNetworkCost, mTempSimulationCost; + F32 mSelectedRenderCost, mSelectedPhysicsCost, mSelectedNetworkCost, mSelectedSimulationCost; + F32 mParcelCapacity; +}; + +struct SelectionQuota +{ + SelectionQuota( LLUUID localId, F32 renderCost, F32 physicsCost, F32 networkCost, F32 simulationCost ) + : mLocalId( localId) + , mRenderCost( renderCost ) + , mPhysicsCost( physicsCost ) + , mNetworkCost( networkCost ) + , mSimulationCost( simulationCost ) + { + } + SelectionQuota() {} + + F32 mRenderCost, mPhysicsCost, mNetworkCost, mSimulationCost; + LLUUID mLocalId; +}; + +#endif + + + diff --git a/indra/llinventory/llparcel.cpp b/indra/llinventory/llparcel.cpp index c167ef194..432cb3e61 100644 --- a/indra/llinventory/llparcel.cpp +++ b/indra/llinventory/llparcel.cpp @@ -1354,3 +1354,13 @@ LLParcel::ECategory category_ui_string_to_category(const std::string& s) // is a distinct option from "None" and "Other" return LLParcel::C_ANY; } + +#if MESH_ENABLED +void LLParcel::updateQuota( const LLUUID& objectId, const ParcelQuota& quota ) +{ + if ( mID == objectId ) + { + mQuota = quota; + } +} +#endif //MESH_ENABLED diff --git a/indra/llinventory/llparcel.h b/indra/llinventory/llparcel.h index 2d74495b6..7d92d4b37 100644 --- a/indra/llinventory/llparcel.h +++ b/indra/llinventory/llparcel.h @@ -38,8 +38,11 @@ #include "lluuid.h" #include "llparcelflags.h" #include "llpermissions.h" -#include "v3math.h" #include "lltimer.h" +#include "v3math.h" +#if MESH_ENABLED +#include "llaccountingquota.h" +#endif //MESH_ENABLED // Grid out of which parcels taken is stepped every 4 meters. const F32 PARCEL_GRID_STEP_METERS = 4.f; @@ -599,6 +602,10 @@ public: BOOL getPreviouslyGroupOwned() const { return mPreviouslyGroupOwned; } BOOL getSellWithObjects() const { return (mParcelFlags & PF_SELL_PARCEL_OBJECTS) ? TRUE : FALSE; } +#if MESH_ENABLED + void updateQuota( const LLUUID& objectId, const ParcelQuota& quota ); + const ParcelQuota& getQuota( void ) { return mQuota; } +#endif //MESH_ENABLED protected: LLUUID mID; LLUUID mOwnerID; @@ -671,7 +678,9 @@ protected: BOOL mRegionPushOverride; BOOL mRegionDenyAnonymousOverride; BOOL mRegionDenyAgeUnverifiedOverride; - +#if MESH_ENABLED + ParcelQuota mQuota; +#endif //MESH_ENABLED public: // HACK, make private diff --git a/indra/llui/llmultisliderctrl.cpp b/indra/llui/llmultisliderctrl.cpp index f9ec6d5ee..acf928f01 100644 --- a/indra/llui/llmultisliderctrl.cpp +++ b/indra/llui/llmultisliderctrl.cpp @@ -307,17 +307,10 @@ void LLMultiSliderCtrl::onEditorCommit( LLUICtrl* caller, void *userdata ) val = (F32) atof( text.c_str() ); if( self->mMultiSlider->getMinValue() <= val && val <= self->mMultiSlider->getMaxValue() ) { - if( self->mValidateCallback ) + self->setCurSliderValue( val ); + if( (!self->mValidateCallback || self->mValidateCallback( self, self->mCallbackUserData )) && + (!self->mValidateSignal || (*(self->mValidateSignal))(self, val))) { - self->setCurSliderValue( val ); // set the value temporarily so that the callback can retrieve it. - if( self->mValidateCallback( self, self->mCallbackUserData ) ) - { - success = TRUE; - } - } - else - { - self->setCurSliderValue( val ); success = TRUE; } } @@ -348,18 +341,11 @@ void LLMultiSliderCtrl::onSliderCommit( LLUICtrl* caller, void *userdata ) F32 saved_val = self->mCurValue; F32 new_val = self->mMultiSlider->getCurSliderValue(); - if( self->mValidateCallback ) + self->mCurValue = new_val; // set the value temporarily so that the callback can retrieve it. + if( (!self->mValidateCallback || self->mValidateCallback( self, self->mCallbackUserData )) && + (!self->mValidateSignal || (*(self->mValidateSignal))(self, new_val ))) { - self->mCurValue = new_val; // set the value temporarily so that the callback can retrieve it. - if( self->mValidateCallback( self, self->mCallbackUserData ) ) - { success = TRUE; - } - } - else - { - self->mCurValue = new_val; - success = TRUE; } if( success ) diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp index 51d43fbb7..590d3cf2e 100644 --- a/indra/llui/llsliderctrl.cpp +++ b/indra/llui/llsliderctrl.cpp @@ -238,17 +238,10 @@ void LLSliderCtrl::onEditorCommit( LLUICtrl* caller, void *userdata ) val = (F32) atof( text.c_str() ); if( self->mSlider->getMinValue() <= val && val <= self->mSlider->getMaxValue() ) { - if( self->mValidateCallback ) + self->setValue( val ); + if( (!self->mValidateCallback || self->mValidateCallback( self, self->mCallbackUserData )) && + (!self->mValidateSignal || (*(self->mValidateSignal))( self, val ))) { - self->setValue( val ); // set the value temporarily so that the callback can retrieve it. - if( self->mValidateCallback( self, self->mCallbackUserData ) ) - { - success = TRUE; - } - } - else - { - self->setValue( val ); success = TRUE; } } @@ -279,17 +272,10 @@ void LLSliderCtrl::onSliderCommit( LLUICtrl* caller, void *userdata ) F32 saved_val = self->mValue; F32 new_val = self->mSlider->getValueF32(); - if( self->mValidateCallback ) + self->mValue = new_val; // set the value temporarily so that the callback can retrieve it. + if( (!self->mValidateCallback || self->mValidateCallback( self, self->mCallbackUserData )) && + (!self->mValidateSignal || (*(self->mValidateSignal))( self, new_val ))) { - self->mValue = new_val; // set the value temporarily so that the callback can retrieve it. - if( self->mValidateCallback( self, self->mCallbackUserData ) ) - { - success = TRUE; - } - } - else - { - self->mValue = new_val; success = TRUE; } diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index 717604670..f833dac6c 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -168,21 +168,15 @@ void LLSpinCtrl::onUpBtn( void *userdata ) val = clamp_precision(val, self->mPrecision); val = llmin( val, self->mMaxValue ); - if( self->mValidateCallback ) + F32 saved_val = (F32)self->getValue().asReal(); + self->setValue(val); + if( (self->mValidateCallback && !self->mValidateCallback( self, self->mCallbackUserData ) ) || + (self->mValidateSignal && !(*(self->mValidateSignal))( self, val ) )) { - F32 saved_val = (F32)self->getValue().asReal(); - self->setValue(val); - if( !self->mValidateCallback( self, self->mCallbackUserData ) ) - { - self->setValue( saved_val ); - self->reportInvalidData(); - self->updateEditor(); - return; - } - } - else - { - self->setValue(val); + self->setValue( saved_val ); + self->reportInvalidData(); + self->updateEditor(); + return; } self->updateEditor(); @@ -201,21 +195,19 @@ void LLSpinCtrl::onDownBtn( void *userdata ) val = clamp_precision(val, self->mPrecision); val = llmax( val, self->mMinValue ); - if( self->mValidateCallback ) + + if (val < self->mMinValue) val = self->mMinValue; + if (val > self->mMaxValue) val = self->mMaxValue; + + F32 saved_val = (F32)self->getValue().asReal(); + self->setValue(val); + if( (self->mValidateCallback && !self->mValidateCallback( self, self->mCallbackUserData ) ) || + (self->mValidateSignal && !(*(self->mValidateSignal))( self, val ) )) { - F32 saved_val = (F32)self->getValue().asReal(); - self->setValue(val); - if( !self->mValidateCallback( self, self->mCallbackUserData ) ) - { - self->setValue( saved_val ); - self->reportInvalidData(); - self->updateEditor(); - return; - } - } - else - { - self->setValue(val); + self->setValue( saved_val ); + self->reportInvalidData(); + self->updateEditor(); + return; } self->updateEditor(); @@ -303,32 +295,20 @@ void LLSpinCtrl::onEditorCommit( LLUICtrl* caller, void *userdata ) if (val < self->mMinValue) val = self->mMinValue; if (val > self->mMaxValue) val = self->mMaxValue; - if( self->mValidateCallback ) + F32 saved_val = self->mValue; + self->mValue = val; + + if( (!self->mValidateCallback || self->mValidateCallback( self, self->mCallbackUserData )) && + (!self->mValidateSignal || (*(self->mValidateSignal))(self, val))) { - F32 saved_val = self->mValue; - self->mValue = val; - if( self->mValidateCallback( self, self->mCallbackUserData ) ) - { - success = TRUE; - self->onCommit(); - } - else - { - self->mValue = saved_val; - } + success = TRUE; + self->onCommit(); } else { - self->mValue = val; - self->onCommit(); - success = TRUE; + self->mValue = saved_val; } } - else - { - // We want to update the editor in case it fails while blanking -- MC - success = TRUE; - } if( success ) { diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 4038f0a3e..49767a547 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -93,6 +93,8 @@ void LLUICtrl::onCommit() { mCommitCallback( this, mCallbackUserData ); } + if (mCommitSignal) + (*mCommitSignal)(this, getValue()); } //virtual diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 342c19552..b095c90d4 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -100,6 +100,7 @@ set(viewer_SOURCE_FILES jcfloaterareasearch.cpp chatbar_as_cmdline.cpp qtoolalign.cpp + llaccountingquotamanager.cpp llagent.cpp llagentaccess.cpp llagentcamera.cpp @@ -579,6 +580,7 @@ set(viewer_HEADER_FILES lgghunspell_wrapper.h chatbar_as_cmdline.h qtoolalign.h + llaccountingquotamanager.h llagent.h llagentaccess.h llagentcamera.h diff --git a/indra/newview/llaccountingquotamanager.cpp b/indra/newview/llaccountingquotamanager.cpp new file mode 100644 index 000000000..44d5b7e71 --- /dev/null +++ b/indra/newview/llaccountingquotamanager.cpp @@ -0,0 +1,281 @@ +/** + * @file LLAccountingQuotaManager.cpp + * @ Handles the setting and accessing for costs associated with mesh + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, 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" +#if MESH_ENABLED +#include "llaccountingquotamanager.h" +#include "llagent.h" +#include "llviewerregion.h" +#include "llviewerobject.h" +#include "llviewerobjectlist.h" +#include "llviewerparcelmgr.h" +#include "llparcel.h" + +//=============================================================================== +LLAccountingQuotaManager::LLAccountingQuotaManager() +{ +} +//=============================================================================== +class LLAccountingQuotaResponder : public LLCurl::Responder +{ +public: + LLAccountingQuotaResponder( const LLSD& objectIDs ) + : mObjectIDs( objectIDs ) + { + } + + void clearPendingRequests ( void ) + { + for ( LLSD::array_iterator iter = mObjectIDs.beginArray(); iter != mObjectIDs.endArray(); ++iter ) + { + LLAccountingQuotaManager::getInstance()->removePendingObjectQuota( iter->asUUID() ); + } + } + + void error( U32 statusNum, const std::string& reason ) + { + llwarns << "Transport error "<asUUID(); + + LLAccountingQuotaManager::getInstance()->removePendingObjectQuota( objectID ); + + if ( containsParcel ) + { + //Typically should be one + S32 dataCount = content["parcel"].size(); + for(S32 i = 0; i < dataCount; i++) + { + //prep#todo verify that this is safe, otherwise just add a bool + LLUUID parcelId; + //S32 parcelOwner = 0; + if ( content["parcel"][i].has("parcel_id") ) + { + parcelId = content["parcel"][i]["parcel_id"].asUUID(); + } + + //if ( content["parcel"][i].has("parcel_owner") ) + //{ + // parcelOwner = content["parcel"][i]["parcel_owner"].asInteger(); + //} + + F32 ownerRenderCost = 0; + F32 ownerPhysicsCost = 0; + F32 ownerNetworkCost = 0; + F32 ownerSimulationCost = 0; + + F32 groupRenderCost = 0; + F32 groupPhysicsCost = 0; + F32 groupNetworkCost = 0; + F32 groupSimulationCost = 0; + + F32 otherRenderCost = 0; + F32 otherPhysicsCost = 0; + F32 otherNetworkCost = 0; + F32 otherSimulationCost = 0; + + F32 tempRenderCost = 0; + F32 tempPhysicsCost = 0; + F32 tempNetworkCost = 0; + F32 tempSimulationCost = 0; + + F32 selectedRenderCost = 0; + F32 selectedPhysicsCost = 0; + F32 selectedNetworkCost = 0; + F32 selectedSimulationCost = 0; + + F32 parcelCapacity = 0; + + if ( content["parcel"][i].has("capacity") ) + { + parcelCapacity = content["parcel"][i].has("capacity"); + } + + if ( content["parcel"][i].has("owner") ) + { + ownerRenderCost = content["parcel"][i]["owner"]["rendering"].asReal(); + ownerPhysicsCost = content["parcel"][i]["owner"]["physics"].asReal(); + ownerNetworkCost = content["parcel"][i]["owner"]["streaming"].asReal(); + ownerSimulationCost = content["parcel"][i]["owner"]["simulation"].asReal(); + } + + if ( content["parcel"][i].has("group") ) + { + groupRenderCost = content["parcel"][i]["group"]["rendering"].asReal(); + groupPhysicsCost = content["parcel"][i]["group"]["physics"].asReal(); + groupNetworkCost = content["parcel"][i]["group"]["streaming"].asReal(); + groupSimulationCost = content["parcel"][i]["group"]["simulation"].asReal(); + + } + if ( content["parcel"][i].has("other") ) + { + otherRenderCost = content["parcel"][i]["other"]["rendering"].asReal(); + otherPhysicsCost = content["parcel"][i]["other"]["physics"].asReal(); + otherNetworkCost = content["parcel"][i]["other"]["streaming"].asReal(); + otherSimulationCost = content["parcel"][i]["other"]["simulation"].asReal(); + } + + if ( content["parcel"][i].has("temp") ) + { + tempRenderCost = content["parcel"][i]["total"]["rendering"].asReal(); + tempPhysicsCost = content["parcel"][i]["total"]["physics"].asReal(); + tempNetworkCost = content["parcel"][i]["total"]["streaming"].asReal(); + tempSimulationCost = content["parcel"][i]["total"]["simulation"].asReal(); + } + + if ( content["parcel"][i].has("selected") ) + { + selectedRenderCost = content["parcel"][i]["total"]["rendering"].asReal(); + selectedPhysicsCost = content["parcel"][i]["total"]["physics"].asReal(); + selectedNetworkCost = content["parcel"][i]["total"]["streaming"].asReal(); + selectedSimulationCost = content["parcel"][i]["total"]["simulation"].asReal(); + } + + ParcelQuota parcelQuota( ownerRenderCost, ownerPhysicsCost, ownerNetworkCost, ownerSimulationCost, + groupRenderCost, groupPhysicsCost, groupNetworkCost, groupSimulationCost, + otherRenderCost, otherPhysicsCost, otherNetworkCost, otherSimulationCost, + tempRenderCost, tempPhysicsCost, tempNetworkCost, tempSimulationCost, + selectedRenderCost, selectedPhysicsCost, selectedNetworkCost, selectedSimulationCost, + parcelCapacity ); + //Update the Parcel + LLParcel* pParcel = LLViewerParcelMgr::getInstance()->getParcelSelection()->getParcel(); + if ( pParcel ) + { + pParcel->updateQuota( objectID, parcelQuota ); + } + } + } + else + if ( containsSelection ) + { + S32 dataCount = content["selected"].size(); + for(S32 i = 0; i < dataCount; i++) + { + + F32 renderCost = 0; + F32 physicsCost = 0; + F32 networkCost = 0; + F32 simulationCost = 0; + + LLUUID objectId; + + objectId = content["selected"][i]["local_id"].asUUID(); + renderCost = content["selected"][i]["rendering"].asReal(); + physicsCost = content["selected"][i]["physics"].asReal(); + networkCost = content["selected"][i]["streaming"].asReal(); + simulationCost = content["selected"][i]["simulation"].asReal(); + + SelectionQuota selectionQuota( objectId, renderCost, physicsCost, networkCost, simulationCost ); + + //Update the objects + gObjectList.updateQuota( objectId, selectionQuota ); + + } + } + else + { + //Nothing in string + LLAccountingQuotaManager::getInstance()->removePendingObjectQuota( objectID ); + } + } + } + +private: + //List of posted objects + LLSD mObjectIDs; +}; +//=============================================================================== +void LLAccountingQuotaManager::fetchQuotas( const std::string& url ) +{ + // Invoking system must have already determined capability availability + if ( !url.empty() ) + { + LLSD objectList; + U32 objectIndex = 0; + IDIt IDIter = mUpdateObjectQuota.begin(); + IDIt IDIterEnd = mUpdateObjectQuota.end(); + + for ( ; IDIter != IDIterEnd; ++IDIter ) + { + // Check to see if a request for this object has already been made. + if ( mPendingObjectQuota.find( *IDIter ) == mPendingObjectQuota.end() ) + { + mPendingObjectQuota.insert( *IDIter ); + objectList[objectIndex++] = *IDIter; + } + } + + mUpdateObjectQuota.clear(); + + //Post results + if ( objectList.size() > 0 ) + { + LLSD dataToPost = LLSD::emptyMap(); + dataToPost["object_ids"] = objectList; + LLHTTPClient::post( url, dataToPost, new LLAccountingQuotaResponder( objectList )); + } + } + else + { + //url was empty - warn & continue + llwarns<<"Supplied url is empty "< +{ +public: + //Ctor + LLAccountingQuotaManager(); + //Store an object that will be eventually fetched + void updateObjectCost( const LLUUID& objectID ); + //Request quotas for object list + void fetchQuotas( const std::string& url ); + //Delete a specific object from the pending list + void removePendingObjectQuota( const LLUUID& objectID ); + +private: + //Set of objects that need to update their cost + std::set mUpdateObjectQuota; + //During fetchQuota we move object into a the pending set to signify that + //a fetch has been instigated. + std::set mPendingObjectQuota; + typedef std::set::iterator IDIt; +}; +//=============================================================================== + +#endif // LLACCOUNTINGQUOTAMANAGER + diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index d0e41a96a..ac283ff57 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -81,9 +81,14 @@ #include "llviewercontrol.h" #include "llviewerjoystick.h" #include "lluictrlfactory.h" +#if MESH_ENABLED +#include "llaccountingquotamanager.h" +#include "llmeshrepository.h" +#endif #include "qtoolalign.h" //Thank Qarl! + // Globals LLFloaterTools *gFloaterTools = NULL; @@ -511,9 +516,23 @@ void LLFloaterTools::refresh() childSetTextArg("link_num_obj_count", "[DESC]", desc_string); childSetTextArg("link_num_obj_count", "[NUM]", value_string); - std::string prim_count_string; - LLResMgr::getInstance()->getIntegerString(prim_count_string, LLSelectMgr::getInstance()->getSelection()->getObjectCount()); - childSetTextArg("prim_count", "[COUNT]", prim_count_string); + LLStringUtil::format_map_t selection_args; + selection_args["COUNT"] = llformat("%.1d", (S32)prim_count); +#if MESH_ENABLED + if(gMeshRepo.meshRezEnabled()) + { + F32 link_cost = LLSelectMgr::getInstance()->getSelection()->getSelectedObjectCost(); + LLStringUtil::format_map_t prim_equiv_args; + prim_equiv_args["SEL_WEIGHT"] = llformat("%.1d", (S32)link_cost); + selection_args["PE_STRING"] = getString("status_selectprimequiv", prim_equiv_args); + } + else +#endif //MESH_ENABLED + { + selection_args["PE_STRING"] = ""; + } + std::string prim_count_string = getString("status_selectcount",selection_args); + childSetText("prim_count", prim_count_string); // Refresh child tabs mPanelPermissions->refresh(); diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp index 2d4dc3246..19322dfb4 100644 --- a/indra/newview/llpanelvolume.cpp +++ b/indra/newview/llpanelvolume.cpp @@ -78,6 +78,11 @@ #include "lldrawpool.h" #include "lluictrlfactory.h" #include "lltexturectrl.h" +#if MESH_ENABLED +// For mesh physics +#include "llviewercontrol.h" +#include "llmeshrepository.h" +#endif //MESH_ENABLED // "Features" Tab @@ -87,21 +92,21 @@ BOOL LLPanelVolume::postBuild() { childSetCommitCallback("Flexible1D Checkbox Ctrl",onCommitIsFlexible,this); childSetCommitCallback("FlexNumSections",onCommitFlexible,this); - childSetValidate("FlexNumSections",precommitValidate); + getChild("FlexNumSections")->setValidateBeforeCommit(precommitValidate); childSetCommitCallback("FlexGravity",onCommitFlexible,this); - childSetValidate("FlexGravity",precommitValidate); + getChild("FlexGravity")->setValidateBeforeCommit(precommitValidate); childSetCommitCallback("FlexFriction",onCommitFlexible,this); - childSetValidate("FlexFriction",precommitValidate); + getChild("FlexFriction")->setValidateBeforeCommit(precommitValidate); childSetCommitCallback("FlexWind",onCommitFlexible,this); - childSetValidate("FlexWind",precommitValidate); + getChild("FlexWind")->setValidateBeforeCommit(precommitValidate); childSetCommitCallback("FlexTension",onCommitFlexible,this); - childSetValidate("FlexTension",precommitValidate); + getChild("FlexTension")->setValidateBeforeCommit(precommitValidate); childSetCommitCallback("FlexForceX",onCommitFlexible,this); - childSetValidate("FlexForceX",precommitValidate); + getChild("FlexForceX")->setValidateBeforeCommit(precommitValidate); childSetCommitCallback("FlexForceY",onCommitFlexible,this); - childSetValidate("FlexForceY",precommitValidate); + getChild("FlexForceY")->setValidateBeforeCommit(precommitValidate); childSetCommitCallback("FlexForceZ",onCommitFlexible,this); - childSetValidate("FlexForceZ",precommitValidate); + getChild("FlexForceZ")->setValidateBeforeCommit(precommitValidate); } // LIGHT Parameters @@ -123,20 +128,47 @@ BOOL LLPanelVolume::postBuild() } childSetCommitCallback("Light Intensity",onCommitLight,this); - childSetValidate("Light Intensity",precommitValidate); + getChild("Light Intensity")->setValidateBeforeCommit(precommitValidate); childSetCommitCallback("Light Radius",onCommitLight,this); - childSetValidate("Light Radius",precommitValidate); + getChild("Light Radius")->setValidateBeforeCommit(precommitValidate); childSetCommitCallback("Light Falloff",onCommitLight,this); - childSetValidate("Light Falloff",precommitValidate); + getChild("Light Falloff")->setValidateBeforeCommit(precommitValidate); childSetCommitCallback("Light FOV", onCommitLight, this); - childSetValidate("Light FOV", precommitValidate); + getChild("Light FOV")->setValidateBeforeCommit( precommitValidate); childSetCommitCallback("Light Focus", onCommitLight, this); - childSetValidate("Light Focus", precommitValidate); + getChild("Light Focus")->setValidateBeforeCommit( precommitValidate); childSetCommitCallback("Light Ambiance", onCommitLight, this); - childSetValidate("Light Ambiance", precommitValidate); + getChild("Light Ambiance")->setValidateBeforeCommit( precommitValidate); } + +#if MESH_ENABLED + // PHYSICS Parameters + { + // Label + mComboPhysicsShapeLabel = getChild("label physicsshapetype"); + + // PhysicsShapeType combobox + mComboPhysicsShapeType = getChild("Physics Shape Type Combo Ctrl"); + mComboPhysicsShapeType->setCommitCallback(boost::bind(&LLPanelVolume::sendPhysicsShapeType, this, _1, mComboPhysicsShapeType)); + // PhysicsGravity + mSpinPhysicsGravity = getChild("Physics Gravity"); + mSpinPhysicsGravity->setCommitCallback(boost::bind(&LLPanelVolume::sendPhysicsGravity, this, _1, mSpinPhysicsGravity)); + + // PhysicsFriction + mSpinPhysicsFriction = getChild("Physics Friction"); + mSpinPhysicsFriction->setCommitCallback(boost::bind(&LLPanelVolume::sendPhysicsFriction, this, _1, mSpinPhysicsFriction)); + + // PhysicsDensity + mSpinPhysicsDensity = getChild("Physics Density"); + mSpinPhysicsDensity->setCommitCallback(boost::bind(&LLPanelVolume::sendPhysicsDensity, this, _1, mSpinPhysicsDensity)); + + // PhysicsRestitution + mSpinPhysicsRestitution = getChild("Physics Restitution"); + mSpinPhysicsRestitution->setCommitCallback(boost::bind(&LLPanelVolume::sendPhysicsRestitution, this, _1, mSpinPhysicsRestitution)); + } +#endif //MESH_ENABLED // Start with everyone disabled clearCtrls(); @@ -212,25 +244,25 @@ void LLPanelVolume::getState( ) // Select Single Message if (single_volume) { - childSetVisible("edit_object",true); - childSetEnabled("edit_object",true); - childSetVisible("select_single",false); + getChildView("edit_object")->setVisible(true); + getChildView("edit_object")->setEnabled(true); + getChildView("select_single")->setVisible(false); } else { - childSetVisible("edit_object",false); - childSetVisible("select_single",true); - childSetEnabled("select_single",true); + getChildView("edit_object")->setVisible(false); + getChildView("select_single")->setVisible(true); + getChildView("select_single")->setEnabled(true); } // Light properties BOOL is_light = volobjp && volobjp->getIsLight(); - childSetValue("Light Checkbox Ctrl",is_light); - childSetEnabled("Light Checkbox Ctrl",editable && single_volume && volobjp); + getChild("Light Checkbox Ctrl")->setValue(is_light); + getChildView("Light Checkbox Ctrl")->setEnabled(editable && single_volume && volobjp); if (is_light && editable && single_volume) { - childSetEnabled("label color",true); + getChildView("label color")->setEnabled(true); //mLabelColor ->setEnabled( TRUE ); LLColorSwatchCtrl* LightColorSwatch = getChild("colorswatch"); if(LightColorSwatch) @@ -248,22 +280,23 @@ void LLPanelVolume::getState( ) LightTextureCtrl->setValid(TRUE); LightTextureCtrl->setImageAssetID(volobjp->getLightTextureID()); } - childSetEnabled("Light Intensity",true); - childSetEnabled("Light Radius",true); - childSetEnabled("Light Falloff",true); - childSetEnabled("Light FOV",true); - childSetEnabled("Light Focus",true); - childSetEnabled("Light Ambiance",true); + getChildView("Light Intensity")->setEnabled(true); + getChildView("Light Radius")->setEnabled(true); + getChildView("Light Falloff")->setEnabled(true); + + getChildView("Light FOV")->setEnabled(true); + getChildView("Light Focus")->setEnabled(true); + getChildView("Light Ambiance")->setEnabled(true); - childSetValue("Light Intensity",volobjp->getLightIntensity()); - childSetValue("Light Radius",volobjp->getLightRadius()); - childSetValue("Light Falloff",volobjp->getLightFalloff()); + getChild("Light Intensity")->setValue(volobjp->getLightIntensity()); + getChild("Light Radius")->setValue(volobjp->getLightRadius()); + getChild("Light Falloff")->setValue(volobjp->getLightFalloff()); LLVector3 params = volobjp->getSpotLightParams(); - childSetValue("Light FOV",params.mV[0]); - childSetValue("Light Focus",params.mV[1]); - childSetValue("Light Ambiance",params.mV[2]); + getChild("Light FOV")->setValue(params.mV[0]); + getChild("Light Focus")->setValue(params.mV[1]); + getChild("Light Ambiance")->setValue(params.mV[2]); mLightSavedColor = volobjp->getLightColor(); } @@ -273,7 +306,7 @@ void LLPanelVolume::getState( ) getChild("Light Radius", true)->clear(); getChild("Light Falloff", true)->clear(); - childSetEnabled("label color",false); + getChildView("label color")->setEnabled(false); LLColorSwatchCtrl* LightColorSwatch = getChild("colorswatch"); if(LightColorSwatch) { @@ -287,56 +320,61 @@ void LLPanelVolume::getState( ) LightTextureCtrl->setEnabled(FALSE); LightTextureCtrl->setValid(FALSE); } - childSetEnabled("Light Intensity",false); - childSetEnabled("Light Radius",false); - childSetEnabled("Light Falloff",false); - childSetEnabled("Light FOV",false); - childSetEnabled("Light Focus",false); - childSetEnabled("Light Ambiance",false); + getChildView("Light Intensity")->setEnabled(false); + getChildView("Light Radius")->setEnabled(false); + getChildView("Light Falloff")->setEnabled(false); + + getChildView("Light FOV")->setEnabled(false); + getChildView("Light Focus")->setEnabled(false); + getChildView("Light Ambiance")->setEnabled(false); } // Flexible properties BOOL is_flexible = volobjp && volobjp->isFlexible(); - childSetValue("Flexible1D Checkbox Ctrl",is_flexible); + getChild("Flexible1D Checkbox Ctrl")->setValue(is_flexible); if (is_flexible || (volobjp && volobjp->canBeFlexible())) { - childSetEnabled("Flexible1D Checkbox Ctrl", editable && single_volume && volobjp); + getChildView("Flexible1D Checkbox Ctrl")->setEnabled(editable && single_volume && volobjp +#if MESH_ENABLED + && !volobjp->isMesh() +#endif //MESH_ENABLED + ); } else { - childSetEnabled("Flexible1D Checkbox Ctrl", false); + getChildView("Flexible1D Checkbox Ctrl")->setEnabled(false); } if (is_flexible && editable && single_volume) { - childSetVisible("FlexNumSections",true); - childSetVisible("FlexGravity",true); - childSetVisible("FlexTension",true); - childSetVisible("FlexFriction",true); - childSetVisible("FlexWind",true); - childSetVisible("FlexForceX",true); - childSetVisible("FlexForceY",true); - childSetVisible("FlexForceZ",true); + getChildView("FlexNumSections")->setVisible(true); + getChildView("FlexGravity")->setVisible(true); + getChildView("FlexTension")->setVisible(true); + getChildView("FlexFriction")->setVisible(true); + getChildView("FlexWind")->setVisible(true); + getChildView("FlexForceX")->setVisible(true); + getChildView("FlexForceY")->setVisible(true); + getChildView("FlexForceZ")->setVisible(true); - childSetEnabled("FlexNumSections",true); - childSetEnabled("FlexGravity",true); - childSetEnabled("FlexTension",true); - childSetEnabled("FlexFriction",true); - childSetEnabled("FlexWind",true); - childSetEnabled("FlexForceX",true); - childSetEnabled("FlexForceY",true); - childSetEnabled("FlexForceZ",true); + getChildView("FlexNumSections")->setEnabled(true); + getChildView("FlexGravity")->setEnabled(true); + getChildView("FlexTension")->setEnabled(true); + getChildView("FlexFriction")->setEnabled(true); + getChildView("FlexWind")->setEnabled(true); + getChildView("FlexForceX")->setEnabled(true); + getChildView("FlexForceY")->setEnabled(true); + getChildView("FlexForceZ")->setEnabled(true); LLFlexibleObjectData *attributes = (LLFlexibleObjectData *)objectp->getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE); - childSetValue("FlexNumSections",(F32)attributes->getSimulateLOD()); - childSetValue("FlexGravity",attributes->getGravity()); - childSetValue("FlexTension",attributes->getTension()); - childSetValue("FlexFriction",attributes->getAirFriction()); - childSetValue("FlexWind",attributes->getWindSensitivity()); - childSetValue("FlexForceX",attributes->getUserForce().mV[VX]); - childSetValue("FlexForceY",attributes->getUserForce().mV[VY]); - childSetValue("FlexForceZ",attributes->getUserForce().mV[VZ]); + getChild("FlexNumSections")->setValue((F32)attributes->getSimulateLOD()); + getChild("FlexGravity")->setValue(attributes->getGravity()); + getChild("FlexTension")->setValue(attributes->getTension()); + getChild("FlexFriction")->setValue(attributes->getAirFriction()); + getChild("FlexWind")->setValue(attributes->getWindSensitivity()); + getChild("FlexForceX")->setValue(attributes->getUserForce().mV[VX]); + getChild("FlexForceY")->setValue(attributes->getUserForce().mV[VY]); + getChild("FlexForceZ")->setValue(attributes->getUserForce().mV[VZ]); } else { @@ -349,22 +387,73 @@ void LLPanelVolume::getState( ) getChild("FlexForceY", true)->clear(); getChild("FlexForceZ", true)->clear(); - childSetEnabled("FlexNumSections",false); - childSetEnabled("FlexGravity",false); - childSetEnabled("FlexTension",false); - childSetEnabled("FlexFriction",false); - childSetEnabled("FlexWind",false); - childSetEnabled("FlexForceX",false); - childSetEnabled("FlexForceY",false); - childSetEnabled("FlexForceZ",false); + getChildView("FlexNumSections")->setEnabled(false); + getChildView("FlexGravity")->setEnabled(false); + getChildView("FlexTension")->setEnabled(false); + getChildView("FlexFriction")->setEnabled(false); + getChildView("FlexWind")->setEnabled(false); + getChildView("FlexForceX")->setEnabled(false); + getChildView("FlexForceY")->setEnabled(false); + getChildView("FlexForceZ")->setEnabled(false); } +#if MESH_ENABLED + // Physics properties + + mComboPhysicsShapeLabel->setEnabled(editable); + mSpinPhysicsGravity->set(objectp->getPhysicsGravity()); + mSpinPhysicsGravity->setEnabled(editable); + + mSpinPhysicsFriction->set(objectp->getPhysicsFriction()); + mSpinPhysicsFriction->setEnabled(editable); + + mSpinPhysicsDensity->set(objectp->getPhysicsDensity()); + mSpinPhysicsDensity->setEnabled(editable); + + mSpinPhysicsRestitution->set(objectp->getPhysicsRestitution()); + mSpinPhysicsRestitution->setEnabled(editable); + + // update the physics shape combo to include allowed physics shapes + mComboPhysicsShapeType->removeall(); + mComboPhysicsShapeType->add(getString("None"), LLSD(1)); + + BOOL isMesh = FALSE; + LLSculptParams *sculpt_params = (LLSculptParams *)objectp->getParameterEntry(LLNetworkData::PARAMS_SCULPT); + if (sculpt_params) + { + U8 sculpt_type = sculpt_params->getSculptType(); + U8 sculpt_stitching = sculpt_type & LL_SCULPT_TYPE_MASK; + isMesh = (sculpt_stitching == LL_SCULPT_TYPE_MESH); + } + + if(isMesh && objectp) + { + const LLVolumeParams &volume_params = objectp->getVolume()->getParams(); + LLUUID mesh_id = volume_params.getSculptID(); + if(gMeshRepo.hasPhysicsShape(mesh_id)) + { + // if a mesh contains an uploaded or decomposed physics mesh, + // allow 'Prim' + mComboPhysicsShapeType->add(getString("Prim"), LLSD(0)); + } + } + else + { + // simple prims always allow physics shape prim + mComboPhysicsShapeType->add(getString("Prim"), LLSD(0)); + } + + mComboPhysicsShapeType->add(getString("Convex Hull"), LLSD(2)); + mComboPhysicsShapeType->setValue(LLSD(objectp->getPhysicsShapeType())); + mComboPhysicsShapeType->setEnabled(editable); +#endif //MESH_ENABLED + mObject = objectp; mRootObject = root_objectp; } // static -BOOL LLPanelVolume::precommitValidate( LLUICtrl* ctrl, void* userdata ) +BOOL LLPanelVolume::precommitValidate(LLUICtrl* ctrl,void* userdata) { // TODO: Richard will fill this in later. return TRUE; // FALSE means that validation failed and new value should not be commited. @@ -386,11 +475,32 @@ void LLPanelVolume::refresh() BOOL visible = LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED) > 0 ? TRUE : FALSE; - childSetVisible("label texture", visible); - childSetVisible("Light FOV", visible); - childSetVisible("Light Focus", visible); - childSetVisible("Light Ambiance", visible); - childSetVisible("light texture control", visible); + getChildView("label texture")->setVisible( visible); + getChildView("Light FOV")->setVisible( visible); + getChildView("Light Focus")->setVisible( visible); + getChildView("Light Ambiance")->setVisible( visible); + getChildView("light texture control")->setVisible( visible); + +#if MESH_ENABLED + bool enable_mesh = false; + + LLSD sim_features; + LLViewerRegion *region = gAgent.getRegion(); + if(region) + { + LLSD sim_features; + region->getSimulatorFeatures(sim_features); + enable_mesh = sim_features.has("PhysicsShapeTypes"); + } + getChildView("label physicsshapetype")->setVisible(enable_mesh); + getChildView("Physics Shape Type Combo Ctrl")->setVisible(enable_mesh); + getChildView("Physics Gravity")->setVisible(enable_mesh); + getChildView("Physics Friction")->setVisible(enable_mesh); + getChildView("Physics Density")->setVisible(enable_mesh); + getChildView("Physics Restitution")->setVisible(enable_mesh); + + /* TODO: add/remove individual physics shape types as per the PhysicsShapeTypes simulator features */ +#endif //MESH_ENABLED } @@ -404,12 +514,12 @@ void LLPanelVolume::clearCtrls() { LLPanel::clearCtrls(); - childSetEnabled("select_single",false); - childSetVisible("select_single",true); - childSetEnabled("edit_object",false); - childSetVisible("edit_object",false); - childSetEnabled("Light Checkbox Ctrl",false); - childSetEnabled("label color",false); + getChildView("select_single")->setEnabled(false); + getChildView("select_single")->setVisible(true); + getChildView("edit_object")->setEnabled(false); + getChildView("edit_object")->setVisible(false); + getChildView("Light Checkbox Ctrl")->setEnabled(false); + getChildView("label color")->setEnabled(false); LLColorSwatchCtrl* LightColorSwatch = getChild("colorswatch"); if(LightColorSwatch) { @@ -424,19 +534,26 @@ void LLPanelVolume::clearCtrls() LightTextureCtrl->setValid( FALSE ); } - childSetEnabled("Light Intensity",false); - childSetEnabled("Light Radius",false); - childSetEnabled("Light Falloff",false); + getChildView("Light Intensity")->setEnabled(false); + getChildView("Light Radius")->setEnabled(false); + getChildView("Light Falloff")->setEnabled(false); - childSetEnabled("Flexible1D Checkbox Ctrl",false); - childSetEnabled("FlexNumSections",false); - childSetEnabled("FlexGravity",false); - childSetEnabled("FlexTension",false); - childSetEnabled("FlexFriction",false); - childSetEnabled("FlexWind",false); - childSetEnabled("FlexForceX",false); - childSetEnabled("FlexForceY",false); - childSetEnabled("FlexForceZ",false); + getChildView("Flexible1D Checkbox Ctrl")->setEnabled(false); + getChildView("FlexNumSections")->setEnabled(false); + getChildView("FlexGravity")->setEnabled(false); + getChildView("FlexTension")->setEnabled(false); + getChildView("FlexFriction")->setEnabled(false); + getChildView("FlexWind")->setEnabled(false); + getChildView("FlexForceX")->setEnabled(false); + getChildView("FlexForceY")->setEnabled(false); + getChildView("FlexForceZ")->setEnabled(false); + +#if MESH_ENABLED + mSpinPhysicsGravity->setEnabled(FALSE); + mSpinPhysicsFriction->setEnabled(FALSE); + mSpinPhysicsDensity->setEnabled(FALSE); + mSpinPhysicsRestitution->setEnabled(FALSE); +#endif //MESH_ENABLED } // @@ -452,7 +569,7 @@ void LLPanelVolume::sendIsLight() } LLVOVolume *volobjp = (LLVOVolume *)objectp; - BOOL value = childGetValue("Light Checkbox Ctrl"); + BOOL value = getChild("Light Checkbox Ctrl")->getValue(); volobjp->setIsLight(value); llinfos << "update light sent" << llendl; } @@ -466,7 +583,7 @@ void LLPanelVolume::sendIsFlexible() } LLVOVolume *volobjp = (LLVOVolume *)objectp; - BOOL is_flexible = childGetValue("Flexible1D Checkbox Ctrl"); + BOOL is_flexible = getChild("Flexible1D Checkbox Ctrl")->getValue(); //BOOL is_flexible = mCheckFlexible1D->get(); if (is_flexible) @@ -489,6 +606,50 @@ void LLPanelVolume::sendIsFlexible() llinfos << "update flexible sent" << llendl; } +#if MESH_ENABLED +void LLPanelVolume::sendPhysicsShapeType(LLUICtrl* ctrl, void* userdata) +{ + U8 type = ctrl->getValue().asInteger(); + LLSelectMgr::getInstance()->selectionSetPhysicsType(type); + + refreshCost(); +} + +void LLPanelVolume::sendPhysicsGravity(LLUICtrl* ctrl, void* userdata) +{ + F32 val = ctrl->getValue().asReal(); + LLSelectMgr::getInstance()->selectionSetGravity(val); +} + +void LLPanelVolume::sendPhysicsFriction(LLUICtrl* ctrl, void* userdata) +{ + F32 val = ctrl->getValue().asReal(); + LLSelectMgr::getInstance()->selectionSetFriction(val); +} + +void LLPanelVolume::sendPhysicsRestitution(LLUICtrl* ctrl, void* userdata) +{ + F32 val = ctrl->getValue().asReal(); + LLSelectMgr::getInstance()->selectionSetRestitution(val); +} + +void LLPanelVolume::sendPhysicsDensity(LLUICtrl* ctrl, void* userdata) +{ + F32 val = ctrl->getValue().asReal(); + LLSelectMgr::getInstance()->selectionSetDensity(val); +} + +void LLPanelVolume::refreshCost() +{ + LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); + + if (obj) + { + obj->getObjectCost(); + } +} +#endif //MESH_ENABLED + void LLPanelVolume::onLightCancelColor(LLUICtrl* ctrl, void* userdata) { LLPanelVolume* self = (LLPanelVolume*) userdata; @@ -531,6 +692,7 @@ void LLPanelVolume::onLightSelectColor(LLUICtrl* ctrl, void* userdata) } } + void LLPanelVolume::onLightSelectTexture(LLUICtrl* ctrl, void* userdata) { LLPanelVolume* self = (LLPanelVolume*) userdata; @@ -550,6 +712,7 @@ void LLPanelVolume::onLightSelectTexture(LLUICtrl* ctrl, void* userdata) } } + // static void LLPanelVolume::onCommitLight( LLUICtrl* ctrl, void* userdata ) { @@ -562,9 +725,9 @@ void LLPanelVolume::onCommitLight( LLUICtrl* ctrl, void* userdata ) LLVOVolume *volobjp = (LLVOVolume *)objectp; - volobjp->setLightIntensity((F32)self->childGetValue("Light Intensity").asReal()); - volobjp->setLightRadius((F32)self->childGetValue("Light Radius").asReal()); - volobjp->setLightFalloff((F32)self->childGetValue("Light Falloff").asReal()); + volobjp->setLightIntensity((F32)self->getChild("Light Intensity")->getValue().asReal()); + volobjp->setLightRadius((F32)self->getChild("Light Radius")->getValue().asReal()); + volobjp->setLightFalloff((F32)self->getChild("Light Falloff")->getValue().asReal()); LLColorSwatchCtrl* LightColorSwatch = self->getChild("colorswatch"); if(LightColorSwatch) @@ -632,14 +795,14 @@ void LLPanelVolume::onCommitFlexible( LLUICtrl* ctrl, void* userdata ) new_attributes = *attributes; - new_attributes.setSimulateLOD(self->childGetValue("FlexNumSections").asInteger());//(S32)self->mSpinSections->get()); - new_attributes.setGravity((F32)self->childGetValue("FlexGravity").asReal()); - new_attributes.setTension((F32)self->childGetValue("FlexTension").asReal()); - new_attributes.setAirFriction((F32)self->childGetValue("FlexFriction").asReal()); - new_attributes.setWindSensitivity((F32)self->childGetValue("FlexWind").asReal()); - F32 fx = (F32)self->childGetValue("FlexForceX").asReal(); - F32 fy = (F32)self->childGetValue("FlexForceY").asReal(); - F32 fz = (F32)self->childGetValue("FlexForceZ").asReal(); + new_attributes.setSimulateLOD(self->getChild("FlexNumSections")->getValue().asInteger());//(S32)self->mSpinSections->get()); + new_attributes.setGravity((F32)self->getChild("FlexGravity")->getValue().asReal()); + new_attributes.setTension((F32)self->getChild("FlexTension")->getValue().asReal()); + new_attributes.setAirFriction((F32)self->getChild("FlexFriction")->getValue().asReal()); + new_attributes.setWindSensitivity((F32)self->getChild("FlexWind")->getValue().asReal()); + F32 fx = (F32)self->getChild("FlexForceX")->getValue().asReal(); + F32 fy = (F32)self->getChild("FlexForceY")->getValue().asReal(); + F32 fz = (F32)self->getChild("FlexForceZ")->getValue().asReal(); LLVector3 force(fx,fy,fz); new_attributes.setUserForce(force); diff --git a/indra/newview/llpanelvolume.h b/indra/newview/llpanelvolume.h index bd9803b90..9cb744db6 100644 --- a/indra/newview/llpanelvolume.h +++ b/indra/newview/llpanelvolume.h @@ -70,16 +70,31 @@ public: static void onCommitLight( LLUICtrl* ctrl, void* userdata); static void onCommitIsFlexible( LLUICtrl* ctrl, void* userdata); static void onCommitFlexible( LLUICtrl* ctrl, void* userdata); +#if MESH_ENABLED + static void onCommitPhysicsParam( LLUICtrl* ctrl, void* userdata); +#endif //MESH_ENABLED static void onLightCancelColor(LLUICtrl* ctrl, void* userdata); static void onLightSelectColor(LLUICtrl* ctrl, void* userdata); static void onLightCancelTexture(LLUICtrl* ctrl, void* userdata); static void onLightSelectTexture(LLUICtrl* ctrl, void* userdata); -protected: - void getState(); + protected: + void getState(); +#if MESH_ENABLED + void refreshCost(); +#endif //MESH_ENABLED + +protected: +#if MESH_ENABLED + void sendPhysicsShapeType(LLUICtrl* ctrl, void* userdata); + void sendPhysicsGravity(LLUICtrl* ctrl, void* userdata); + void sendPhysicsFriction(LLUICtrl* ctrl, void* userdata); + void sendPhysicsRestitution(LLUICtrl* ctrl, void* userdata); + void sendPhysicsDensity(LLUICtrl* ctrl, void* userdata); +#endif //MESH_ENABLED /* LLTextBox* mLabelSelectSingleMessage; // Light @@ -104,6 +119,14 @@ protected: LLUUID mLightSavedTexture; LLPointer mObject; LLPointer mRootObject; +#if MESH_ENABLED + LLTextBox* mComboPhysicsShapeLabel; + LLComboBox* mComboPhysicsShapeType; + LLSpinCtrl* mSpinPhysicsGravity; + LLSpinCtrl* mSpinPhysicsFriction; + LLSpinCtrl* mSpinPhysicsDensity; + LLSpinCtrl* mSpinPhysicsRestitution; +#endif //MESH_ENABLED }; #endif diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 6248f1edf..dadd29dbd 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -1992,6 +1992,103 @@ BOOL LLSelectMgr::selectionGetGlow(F32 *glow) return identical; } +#if MESH_ENABLED +void LLSelectMgr::selectionSetPhysicsType(U8 type) +{ + struct f : public LLSelectedObjectFunctor + { + U8 mType; + f(const U8& t) : mType(t) {} + virtual bool apply(LLViewerObject* object) + { + if (object->permModify()) + { + object->setPhysicsShapeType(mType); + object->updateFlags(TRUE); + } + return true; + } + } sendfunc(type); + getSelection()->applyToObjects(&sendfunc); +} + +void LLSelectMgr::selectionSetFriction(F32 friction) +{ + struct f : public LLSelectedObjectFunctor + { + F32 mFriction; + f(const F32& friction) : mFriction(friction) {} + virtual bool apply(LLViewerObject* object) + { + if (object->permModify()) + { + object->setPhysicsFriction(mFriction); + object->updateFlags(TRUE); + } + return true; + } + } sendfunc(friction); + getSelection()->applyToObjects(&sendfunc); +} + +void LLSelectMgr::selectionSetGravity(F32 gravity ) +{ + struct f : public LLSelectedObjectFunctor + { + F32 mGravity; + f(const F32& gravity) : mGravity(gravity) {} + virtual bool apply(LLViewerObject* object) + { + if (object->permModify()) + { + object->setPhysicsGravity(mGravity); + object->updateFlags(TRUE); + } + return true; + } + } sendfunc(gravity); + getSelection()->applyToObjects(&sendfunc); +} + +void LLSelectMgr::selectionSetDensity(F32 density ) +{ + struct f : public LLSelectedObjectFunctor + { + F32 mDensity; + f(const F32& density ) : mDensity(density) {} + virtual bool apply(LLViewerObject* object) + { + if (object->permModify()) + { + object->setPhysicsDensity(mDensity); + object->updateFlags(TRUE); + } + return true; + } + } sendfunc(density); + getSelection()->applyToObjects(&sendfunc); +} + +void LLSelectMgr::selectionSetRestitution(F32 restitution) +{ + struct f : public LLSelectedObjectFunctor + { + F32 mRestitution; + f(const F32& restitution ) : mRestitution(restitution) {} + virtual bool apply(LLViewerObject* object) + { + if (object->permModify()) + { + object->setPhysicsRestitution(mRestitution); + object->updateFlags(TRUE); + } + return true; + } + } sendfunc(restitution); + getSelection()->applyToObjects(&sendfunc); +} +#endif //MESH_ENABLED + //----------------------------------------------------------------------------- // selectionSetMaterial() //----------------------------------------------------------------------------- @@ -3832,6 +3929,27 @@ void LLSelectMgr::sendDelink() return; } +#if MESH_ENABLED + struct f : public LLSelectedObjectFunctor + { //on delink, any modifyable object should + f() {} + + virtual bool apply(LLViewerObject* object) + { + if (object->permModify()) + { + if (object->getPhysicsShapeType() == LLViewerObject::PHYSICS_SHAPE_NONE) + { + object->setPhysicsShapeType(LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL); + object->updateFlags(); + } + } + return true; + } + } sendfunc; + getSelection()->applyToObjects(&sendfunc); +#endif //MESH_ENABLED + // Delink needs to send individuals so you can unlink a single object from // a linked set. sendListToRegions( @@ -6277,6 +6395,176 @@ S32 LLObjectSelection::getObjectCount() return count; } +#if MESH_ENABLED +F32 LLObjectSelection::getSelectedObjectCost() +{ + cleanupNodes(); + F32 cost = 0.f; + + for (list_t::iterator iter = mList.begin(); iter != mList.end(); ++iter) + { + LLSelectNode* node = *iter; + LLViewerObject* object = node->getObject(); + + if (object) + { + cost += object->getObjectCost(); + } + } + + return cost; +} + +F32 LLObjectSelection::getSelectedLinksetCost() +{ + cleanupNodes(); + F32 cost = 0.f; + + std::set me_roots; + + for (list_t::iterator iter = mList.begin(); iter != mList.end(); ++iter) + { + LLSelectNode* node = *iter; + LLViewerObject* object = node->getObject(); + + if (object) + { + LLViewerObject* root = static_cast(object->getRoot()); + if (root) + { + if (me_roots.find(root) == me_roots.end()) + { + me_roots.insert(root); + cost += root->getLinksetCost(); + } + } + } + } + + return cost; +} + +F32 LLObjectSelection::getSelectedPhysicsCost() +{ + cleanupNodes(); + F32 cost = 0.f; + + for (list_t::iterator iter = mList.begin(); iter != mList.end(); ++iter) + { + LLSelectNode* node = *iter; + LLViewerObject* object = node->getObject(); + + if (object) + { + cost += object->getPhysicsCost(); + } + } + + return cost; +} + +F32 LLObjectSelection::getSelectedLinksetPhysicsCost() +{ + cleanupNodes(); + F32 cost = 0.f; + + std::set me_roots; + + for (list_t::iterator iter = mList.begin(); iter != mList.end(); ++iter) + { + LLSelectNode* node = *iter; + LLViewerObject* object = node->getObject(); + + if (object) + { + LLViewerObject* root = static_cast(object->getRoot()); + if (root) + { + if (me_roots.find(root) == me_roots.end()) + { + me_roots.insert(root); + cost += root->getLinksetPhysicsCost(); + } + } + } + } + + return cost; +} + +F32 LLObjectSelection::getSelectedObjectStreamingCost(S32* total_bytes, S32* visible_bytes) +{ + F32 cost = 0.f; + for (list_t::iterator iter = mList.begin(); iter != mList.end(); ++iter) + { + LLSelectNode* node = *iter; + LLViewerObject* object = node->getObject(); + + if (object) + { + S32 bytes = 0; + S32 visible = 0; + cost += object->getStreamingCost(&bytes, &visible); + + if (total_bytes) + { + *total_bytes += bytes; + } + + if (visible_bytes) + { + *visible_bytes += visible; + } + } + } + + return cost; +} + +U32 LLObjectSelection::getSelectedObjectTriangleCount() +{ + U32 count = 0; + for (list_t::iterator iter = mList.begin(); iter != mList.end(); ++iter) + { + LLSelectNode* node = *iter; + LLViewerObject* object = node->getObject(); + + if (object) + { + count += object->getTriangleCount(); + } + } + + return count; +} + +/*S32 LLObjectSelection::getSelectedObjectRenderCost() +{ + S32 cost = 0; + LLVOVolume::texture_cost_t textures; + for (list_t::iterator iter = mList.begin(); iter != mList.end(); ++iter) + { + LLSelectNode* node = *iter; + LLVOVolume* object = (LLVOVolume*)node->getObject(); + + if (object) + { + cost += object->getRenderCost(textures); + } + + for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter) + { + // add the cost of each individual texture in the linkset + cost += iter->second; + } + textures.clear(); + } + + + return cost; +}*/ +#endif //MESH_ENABLED + //----------------------------------------------------------------------------- // getTECount() diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index 2dee99fda..2ea1f46b7 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -284,6 +284,16 @@ public: // count members S32 getObjectCount(); +#if MESH_ENABLED + F32 getSelectedObjectCost(); + F32 getSelectedLinksetCost(); + F32 getSelectedPhysicsCost(); + F32 getSelectedLinksetPhysicsCost(); + S32 getSelectedObjectRenderCost(); + + F32 getSelectedObjectStreamingCost(S32* total_bytes = NULL, S32* visible_bytes = NULL); + U32 getSelectedObjectTriangleCount(); +#endif //MESH_ENABLED S32 getTECount(); S32 getRootObjectCount(); @@ -505,6 +515,13 @@ public: bool selectionGetIncludeInSearch(bool* include_in_search_out); // true if all selected objects have same BOOL selectionGetGlow(F32 *glow); +#if MESH_ENABLED + void selectionSetPhysicsType(U8 type); + void selectionSetGravity(F32 gravity); + void selectionSetFriction(F32 friction); + void selectionSetDensity(F32 density); + void selectionSetRestitution(F32 restitution); +#endif //MESH_ENABLED void selectionSetMaterial(U8 material); void selectionSetImage(const LLUUID& imageid); // could be item or asset id void selectionSetColor(const LLColor4 &color); diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 3d091dc24..57e8b5d18 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -66,6 +66,7 @@ #include "lldrawable.h" #include "llface.h" #include "llfloaterproperties.h" +#include "llfloatertools.h" #include "llfollowcam.h" #include "llselectmgr.h" #include "llrendersphere.h" @@ -178,6 +179,13 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe mGLName(0), mbCanSelect(TRUE), mFlags(0), +#if MESH_ENABLED + mPhysicsShapeType(0), + mPhysicsGravity(0), + mPhysicsFriction(0), + mPhysicsDensity(0), + mPhysicsRestitution(0), +#endif //MESH_ENABLED mDrawable(), mCreateSelected(FALSE), mRenderMedia(FALSE), @@ -210,6 +218,14 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe mState(0), mMedia(NULL), mClickAction(0), +#if MESH_ENABLED + mObjectCost(0), + mLinksetCost(0), + mPhysicsCost(0), + mLinksetPhysicsCost(0.f), + mCostStale(true), + mPhysicsShapeUnknown(true), +#endif //MESH_ENABLED mAttachmentItemID(LLUUID::null), mLastUpdateType(OUT_UNKNOWN), mLastUpdateCached(FALSE) @@ -791,6 +807,14 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, #ifdef DEBUG_UPDATE_TYPE llinfos << "Full:" << getID() << llendl; #endif +#if MESH_ENABLED + //clear cost and linkset cost + mCostStale = true; + if (isSelected()) + { + gFloaterTools->dirty(); + } +#endif //MESH_ENABLED LLUUID audio_uuid; LLUUID owner_id; // only valid if audio_uuid or particle system is not null F32 gain; @@ -1389,6 +1413,15 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, #ifdef DEBUG_UPDATE_TYPE llinfos << "CompFull:" << getID() << llendl; #endif + +#if MESH_ENABLED + mCostStale = true; + + if (isSelected()) + { + gFloaterTools->dirty(); + } +#endif //MESH_ENABLED dp->unpackU32(crc, "CRC"); mTotalCRC = crc; dp->unpackU8(material, "Material"); @@ -2912,6 +2945,108 @@ void LLViewerObject::setScale(const LLVector3 &scale, BOOL damped) } } +#if MESH_ENABLED +void LLViewerObject::setObjectCost(F32 cost) +{ + mObjectCost = cost; + mCostStale = false; + + if (isSelected()) + { + gFloaterTools->dirty(); + } +} + +void LLViewerObject::setLinksetCost(F32 cost) +{ + mLinksetCost = cost; + mCostStale = false; + + if (isSelected()) + { + gFloaterTools->dirty(); + } +} + +void LLViewerObject::setPhysicsCost(F32 cost) +{ + mPhysicsCost = cost; + mCostStale = false; + + if (isSelected()) + { + gFloaterTools->dirty(); + } +} + +void LLViewerObject::setLinksetPhysicsCost(F32 cost) +{ + mLinksetPhysicsCost = cost; + mCostStale = false; + + if (isSelected()) + { + gFloaterTools->dirty(); + } +} + + +F32 LLViewerObject::getObjectCost() +{ + if (mCostStale) + { + gObjectList.updateObjectCost(this); + } + + return mObjectCost; +} + +F32 LLViewerObject::getLinksetCost() +{ + if (mCostStale) + { + gObjectList.updateObjectCost(this); + } + + return mLinksetCost; +} + +F32 LLViewerObject::getPhysicsCost() +{ + if (mCostStale) + { + gObjectList.updateObjectCost(this); + } + + return mPhysicsCost; +} + +F32 LLViewerObject::getLinksetPhysicsCost() +{ + if (mCostStale) + { + gObjectList.updateObjectCost(this); + } + + return mLinksetPhysicsCost; +} + +F32 LLViewerObject::getStreamingCost(S32* bytes, S32* visible_bytes) +{ + return 0.f; +} + +U32 LLViewerObject::getTriangleCount() +{ + return 0; +} + +U32 LLViewerObject::getHighLODTriangleCount() +{ + return 0; +} +#endif //MESH_ENABLED + void LLViewerObject::updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax) { if(mDrawable.isNull()) @@ -5060,7 +5195,7 @@ bool LLViewerObject::specialHoverCursor() const || (mClickAction != 0); } -void LLViewerObject::updateFlags() +void LLViewerObject::updateFlags(BOOL physics_changed) { LLViewerRegion* regionp = getRegion(); if(!regionp) return; @@ -5073,6 +5208,17 @@ void LLViewerObject::updateFlags() gMessageSystem->addBOOL("IsTemporary", flagTemporaryOnRez() ); gMessageSystem->addBOOL("IsPhantom", flagPhantom() ); gMessageSystem->addBOOL("CastsShadows", flagCastShadows() ); +#if MESH_ENABLED + if (physics_changed) + { + gMessageSystem->nextBlock("ExtraPhysics"); + gMessageSystem->addU8("PhysicsShapeType", getPhysicsShapeType() ); + gMessageSystem->addF32("Density", getPhysicsDensity() ); + gMessageSystem->addF32("Friction", getPhysicsFriction() ); + gMessageSystem->addF32("Restitution", getPhysicsRestitution() ); + gMessageSystem->addF32("GravityMultiplier", getPhysicsGravity() ); + } +#endif //MESH_ENABLED gMessageSystem->sendReliable( regionp->getHost() ); } @@ -5105,6 +5251,46 @@ BOOL LLViewerObject::setFlags(U32 flags, BOOL state) return setit; } +#if MESH_ENABLED +void LLViewerObject::setPhysicsShapeType(U8 type) +{ + mPhysicsShapeUnknown = false; + mPhysicsShapeType = type; + mCostStale = true; +} + +void LLViewerObject::setPhysicsGravity(F32 gravity) +{ + mPhysicsGravity = gravity; +} + +void LLViewerObject::setPhysicsFriction(F32 friction) +{ + mPhysicsFriction = friction; +} + +void LLViewerObject::setPhysicsDensity(F32 density) +{ + mPhysicsDensity = density; +} + +void LLViewerObject::setPhysicsRestitution(F32 restitution) +{ + mPhysicsRestitution = restitution; +} + +U8 LLViewerObject::getPhysicsShapeType() const +{ + if (mPhysicsShapeUnknown) + { + mPhysicsShapeUnknown = false; + gObjectList.updatePhysicsFlags(this); + } + + return mPhysicsShapeType; +} +#endif //MESH_ENABLED + void LLViewerObject::applyAngularVelocity(F32 dt) { //do target omega here @@ -5386,3 +5572,65 @@ LLVOAvatar* LLViewerObject::getAvatar() const return NULL; } + +#if MESH_ENABLED +class ObjectPhysicsProperties : public LLHTTPNode +{ +public: + virtual void post( + ResponsePtr responder, + const LLSD& context, + const LLSD& input) const + { + LLSD object_data = input["body"]["ObjectData"]; + S32 num_entries = object_data.size(); + + for ( S32 i = 0; i < num_entries; i++ ) + { + LLSD& curr_object_data = object_data[i]; + U32 local_id = curr_object_data["LocalID"].asInteger(); + + // Iterate through nodes at end, since it can be on both the regular AND hover list + struct f : public LLSelectedNodeFunctor + { + U32 mID; + f(const U32& id) : mID(id) {} + virtual bool apply(LLSelectNode* node) + { + return (node->getObject() && node->getObject()->mLocalID == mID ); + } + } func(local_id); + + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(&func); + + if (node) + { + // The LLSD message builder doesn't know how to handle U8, so we need to send as S8 and cast + U8 type = (U8)curr_object_data["PhysicsShapeType"].asInteger(); + F32 density = (F32)curr_object_data["Density"].asReal(); + F32 friction = (F32)curr_object_data["Friction"].asReal(); + F32 restitution = (F32)curr_object_data["Restitution"].asReal(); + F32 gravity = (F32)curr_object_data["GravityMultiplier"].asReal(); + + node->getObject()->setPhysicsShapeType(type); + node->getObject()->setPhysicsGravity(gravity); + node->getObject()->setPhysicsFriction(friction); + node->getObject()->setPhysicsDensity(density); + node->getObject()->setPhysicsRestitution(restitution); + } + } + + dialog_refresh_all(); + }; +}; + +LLHTTPRegistration + gHTTPRegistrationObjectPhysicsProperties("/message/ObjectPhysicsProperties"); + + +void LLViewerObject::updateQuota( const SelectionQuota& quota ) +{ + //update quotas + mSelectionQuota = quota; +} +#endif //MESH_ENABLED diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 3eefe1113..fadecd742 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -49,6 +49,9 @@ #include "v3dmath.h" #include "v3math.h" #include "llvertexbuffer.h" +#if MESH_ENABLED +#include "llaccountingquota.h" +#endif //MESH_ENABLED class LLAgent; // TODO: Get rid of this. class LLAudioSource; @@ -327,6 +330,23 @@ public: virtual void setScale(const LLVector3 &scale, BOOL damped = FALSE); +#if MESH_ENABLED + virtual F32 getStreamingCost(S32* bytes = NULL, S32* visible_bytes = NULL); + virtual U32 getTriangleCount(); + virtual U32 getHighLODTriangleCount(); + + void setObjectCost(F32 cost); + F32 getObjectCost(); + + void setLinksetCost(F32 cost); + F32 getLinksetCost(); + + void setPhysicsCost(F32 cost); + F32 getPhysicsCost(); + + void setLinksetPhysicsCost(F32 cost); + F32 getLinksetPhysicsCost(); +#endif //MESH_ENABLED void sendShapeUpdate(); // U8 getState() { return mState; } @@ -460,6 +480,14 @@ public: inline BOOL flagCameraDecoupled() const { return ((mFlags & FLAGS_CAMERA_DECOUPLED) != 0); } inline BOOL flagObjectMove() const { return ((mFlags & FLAGS_OBJECT_MOVE) != 0); } +#if MESH_ENABLED + U8 getPhysicsShapeType() const; + inline F32 getPhysicsGravity() const { return mPhysicsGravity; } + inline F32 getPhysicsFriction() const { return mPhysicsFriction; } + inline F32 getPhysicsDensity() const { return mPhysicsDensity; } + inline F32 getPhysicsRestitution() const { return mPhysicsRestitution; } +#endif //MESH_ENABLED + bool getIncludeInSearch() const; void setIncludeInSearch(bool include_in_search); @@ -473,8 +501,15 @@ public: void setRegion(LLViewerRegion *regionp); virtual void updateRegion(LLViewerRegion *regionp) {} - void updateFlags(); + void updateFlags(BOOL physics_changed = FALSE); BOOL setFlags(U32 flag, BOOL state); +#if MESH_ENABLED + void setPhysicsShapeType(U8 type); + void setPhysicsGravity(F32 gravity); + void setPhysicsFriction(F32 friction); + void setPhysicsDensity(F32 density); + void setPhysicsRestitution(F32 restitution); +#endif //MESH_ENABLED virtual void dump() const; static U32 getNumZombieObjects() { return sNumZombieObjects; } @@ -532,6 +567,15 @@ public: LL_VO_HUD_PART_GROUP = LL_PCODE_APP | 0xc0, } EVOType; +#if MESH_ENABLED + typedef enum e_physics_shape_types + { + PHYSICS_SHAPE_PRIM = 0, + PHYSICS_SHAPE_NONE, + PHYSICS_SHAPE_CONVEX_HULL, + } EPhysicsShapeType; +#endif //MESH_ENABLED + LLUUID mID; // unique within region, not unique across regions @@ -550,6 +594,15 @@ public: // Grabbed from UPDATE_FLAGS U32 mFlags; +#if MESH_ENABLED + // Sent to sim in UPDATE_FLAGS, received in ObjectPhysicsProperties + U8 mPhysicsShapeType; + F32 mPhysicsGravity; + F32 mPhysicsFriction; + F32 mPhysicsDensity; + F32 mPhysicsRestitution; +#endif //MESH_ENABLED + // Pipeline classes LLPointer mDrawable; @@ -604,6 +657,12 @@ protected: void deleteParticleSource(); void setParticleSource(const LLPartSysData& particle_parameters, const LLUUID& owner_id); +#if MESH_ENABLED +public: + void updateQuota( const SelectionQuota& quota ); + const SelectionQuota& getQuota( void ) { return mSelectionQuota; } +#endif //MESH_ENABLED + private: void setNameValueList(const std::string& list); // clears nv pairs and then individually adds \n separated NV pairs from \0 terminated string void deleteTEImages(); // correctly deletes list of images @@ -659,6 +718,17 @@ protected: U8 mState; // legacy LLViewerObjectMedia* mMedia; // NULL if no media associated U8 mClickAction; +#if MESH_ENABLED + F32 mObjectCost; //resource cost of this object or -1 if unknown + F32 mLinksetCost; + F32 mPhysicsCost; + F32 mLinksetPhysicsCost; + + SelectionQuota mSelectionQuota; + + bool mCostStale; + mutable bool mPhysicsShapeUnknown; +#endif //MESH_ENABLED static U32 sNumZombieObjects; // Objects which are dead, but not deleted diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 9be2aacc3..94f532306 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -671,6 +671,192 @@ void LLViewerObjectList::updateApparentAngles(LLAgent &agent) LLVOAvatar::cullAvatarsByPixelArea(); } +#if MESH_ENABLED + +class LLObjectCostResponder : public LLCurl::Responder +{ +public: + LLObjectCostResponder(const LLSD& object_ids) + : mObjectIDs(object_ids) + { + } + + // Clear's the global object list's pending + // request list for all objects requested + void clear_object_list_pending_requests() + { + // TODO*: No more hard coding + for ( + LLSD::array_iterator iter = mObjectIDs.beginArray(); + iter != mObjectIDs.endArray(); + ++iter) + { + gObjectList.onObjectCostFetchFailure(iter->asUUID()); + } + } + + void error(U32 statusNum, const std::string& reason) + { + llwarns + << "Transport error requesting object cost " + << "HTTP status: " << statusNum << ", reason: " + << reason << "." << llendl; + + // TODO*: Error message to user + // For now just clear the request from the pending list + clear_object_list_pending_requests(); + } + + void result(const LLSD& content) + { + if ( !content.isMap() || content.has("error") ) + { + // Improper response or the request had an error, + // show an error to the user? + llwarns + << "Application level error when fetching object " + << "cost. Message: " << content["error"]["message"].asString() + << ", identifier: " << content["error"]["identifier"].asString() + << llendl; + + // TODO*: Adaptively adjust request size if the + // service says we've requested too many and retry + + // TODO*: Error message if not retrying + clear_object_list_pending_requests(); + return; + } + + // Success, grab the resource cost and linked set costs + // for an object if one was returned + for ( + LLSD::array_iterator iter = mObjectIDs.beginArray(); + iter != mObjectIDs.endArray(); + ++iter) + { + LLUUID object_id = iter->asUUID(); + + // Check to see if the request contains data for the object + if ( content.has(iter->asString()) ) + { + F32 link_cost = + content[iter->asString()]["linked_set_resource_cost"].asReal(); + F32 object_cost = + content[iter->asString()]["resource_cost"].asReal(); + + F32 physics_cost = content[iter->asString()]["physics_cost"].asReal(); + F32 link_physics_cost = content[iter->asString()]["linked_set_physics_cost"].asReal(); + + gObjectList.updateObjectCost(object_id, object_cost, link_cost, physics_cost, link_physics_cost); + } + else + { + // TODO*: Give user feedback about the missing data? + gObjectList.onObjectCostFetchFailure(object_id); + } + } + } + +private: + LLSD mObjectIDs; +}; + + +class LLPhysicsFlagsResponder : public LLCurl::Responder +{ +public: + LLPhysicsFlagsResponder(const LLSD& object_ids) + : mObjectIDs(object_ids) + { + } + + // Clear's the global object list's pending + // request list for all objects requested + void clear_object_list_pending_requests() + { + // TODO*: No more hard coding + for ( + LLSD::array_iterator iter = mObjectIDs.beginArray(); + iter != mObjectIDs.endArray(); + ++iter) + { + gObjectList.onPhysicsFlagsFetchFailure(iter->asUUID()); + } + } + + void error(U32 statusNum, const std::string& reason) + { + llwarns + << "Transport error requesting object physics flags " + << "HTTP status: " << statusNum << ", reason: " + << reason << "." << llendl; + + // TODO*: Error message to user + // For now just clear the request from the pending list + clear_object_list_pending_requests(); + } + + void result(const LLSD& content) + { + if ( !content.isMap() || content.has("error") ) + { + // Improper response or the request had an error, + // show an error to the user? + llwarns + << "Application level error when fetching object " + << "physics flags. Message: " << content["error"]["message"].asString() + << ", identifier: " << content["error"]["identifier"].asString() + << llendl; + + // TODO*: Adaptively adjust request size if the + // service says we've requested too many and retry + + // TODO*: Error message if not retrying + clear_object_list_pending_requests(); + return; + } + + // Success, grab the resource cost and linked set costs + // for an object if one was returned + for ( + LLSD::array_iterator iter = mObjectIDs.beginArray(); + iter != mObjectIDs.endArray(); + ++iter) + { + LLUUID object_id = iter->asUUID(); + + // Check to see if the request contains data for the object + if ( content.has(iter->asString()) ) + { + const LLSD& data = content[iter->asString()]; + + S32 shape_type = data["PhysicsShapeType"].asInteger(); + + gObjectList.updatePhysicsShapeType(object_id, shape_type); + + if (data.has("Density")) + { + F32 density = data["Density"].asReal(); + F32 friction = data["Friction"].asReal(); + F32 restitution = data["Restitution"].asReal(); + F32 gravity_multiplier = data["GravityMultiplier"].asReal(); + + gObjectList.updatePhysicsProperties(object_id, + density, friction, restitution, gravity_multiplier); + } + } + else + { + // TODO*: Give user feedback about the missing data? + gObjectList.onPhysicsFlagsFetchFailure(object_id); + } + } + } + +private: + LLSD mObjectIDs; +}; +#endif //MESH_ENABLED void LLViewerObjectList::update(LLAgent &agent, LLWorld &world) { @@ -763,6 +949,10 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world) } } +#if MESH_ENABLED + fetchObjectCosts(); + fetchPhysicsFlags(); +#endif //MESH_ENABLED mNumSizeCulled = 0; mNumVisCulled = 0; @@ -828,6 +1018,124 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world) mNumVisCulledStat.addValue(mNumVisCulled); } +#if MESH_ENABLED +void LLViewerObjectList::fetchObjectCosts() +{ + // issue http request for stale object physics costs + if (!mStaleObjectCost.empty()) + { + LLViewerRegion* regionp = gAgent.getRegion(); + + if (regionp) + { + std::string url = regionp->getCapability("GetObjectCost"); + + if (!url.empty()) + { + LLSD id_list; + U32 object_index = 0; + + U32 count = 0; + + for ( + std::set::iterator iter = mStaleObjectCost.begin(); + iter != mStaleObjectCost.end(); + ) + { + // Check to see if a request for this object + // has already been made. + if ( mPendingObjectCost.find(*iter) == + mPendingObjectCost.end() ) + { + mPendingObjectCost.insert(*iter); + id_list[object_index++] = *iter; + } + + mStaleObjectCost.erase(iter++); + + if (count++ >= 450) + { + break; + } + } + + if ( id_list.size() > 0 ) + { + LLSD post_data = LLSD::emptyMap(); + + post_data["object_ids"] = id_list; + LLHTTPClient::post( + url, + post_data, + new LLObjectCostResponder(id_list)); + } + } + else + { + mStaleObjectCost.clear(); + mPendingObjectCost.clear(); + } + } + } +} + +void LLViewerObjectList::fetchPhysicsFlags() +{ + // issue http request for stale object physics flags + if (!mStalePhysicsFlags.empty()) + { + LLViewerRegion* regionp = gAgent.getRegion(); + + if (regionp) + { + std::string url = regionp->getCapability("GetObjectPhysicsData"); + + if (!url.empty()) + { + LLSD id_list; + U32 object_index = 0; + + for ( + std::set::iterator iter = mStalePhysicsFlags.begin(); + iter != mStalePhysicsFlags.end(); + ++iter) + { + // Check to see if a request for this object + // has already been made. + if ( mPendingPhysicsFlags.find(*iter) == + mPendingPhysicsFlags.end() ) + { + mPendingPhysicsFlags.insert(*iter); + id_list[object_index++] = *iter; + } + } + + // id_list should now contain all + // requests in mStalePhysicsFlags before, so clear + // it now + mStalePhysicsFlags.clear(); + + if ( id_list.size() > 0 ) + { + LLSD post_data = LLSD::emptyMap(); + + post_data["object_ids"] = id_list; + LLHTTPClient::post( + url, + post_data, + new LLPhysicsFlagsResponder(id_list)); + } + } + else + { + mStalePhysicsFlags.clear(); + mPendingPhysicsFlags.clear(); + } + } + } +} +#endif //MESH_ENABLED + void LLViewerObjectList::clearDebugText() { for (vobj_list_t::iterator iter = mObjects.begin(); iter != mObjects.end(); ++iter) @@ -1084,6 +1392,84 @@ void LLViewerObjectList::updateActive(LLViewerObject *objectp) } } +#if MESH_ENABLED +void LLViewerObjectList::updateObjectCost(LLViewerObject* object) +{ + if (!object->isRoot()) + { //always fetch cost for the parent when fetching cost for children + mStaleObjectCost.insert(((LLViewerObject*)object->getParent())->getID()); + } + mStaleObjectCost.insert(object->getID()); +} + +void LLViewerObjectList::updateObjectCost(const LLUUID& object_id, F32 object_cost, F32 link_cost, F32 physics_cost, F32 link_physics_cost) +{ + mPendingObjectCost.erase(object_id); + + LLViewerObject* object = findObject(object_id); + if (object) + { + object->setObjectCost(object_cost); + object->setLinksetCost(link_cost); + object->setPhysicsCost(physics_cost); + object->setLinksetPhysicsCost(link_physics_cost); + } +} + +void LLViewerObjectList::onObjectCostFetchFailure(const LLUUID& object_id) +{ + //llwarns << "Failed to fetch object cost for object: " << object_id << llendl; + mPendingObjectCost.erase(object_id); +} + +void LLViewerObjectList::updateQuota( const LLUUID& objectId, const SelectionQuota& quota ) +{ + LLViewerObject* pVO = findObject( objectId ); + if ( pVO ) + { + pVO->updateQuota( quota ); + } +} + +void LLViewerObjectList::updatePhysicsFlags(const LLViewerObject* object) +{ + mStalePhysicsFlags.insert(object->getID()); +} + +void LLViewerObjectList::updatePhysicsShapeType(const LLUUID& object_id, S32 type) +{ + mPendingPhysicsFlags.erase(object_id); + LLViewerObject* object = findObject(object_id); + if (object) + { + object->setPhysicsShapeType(type); + } +} + +void LLViewerObjectList::updatePhysicsProperties(const LLUUID& object_id, + F32 density, + F32 friction, + F32 restitution, + F32 gravity_multiplier) +{ + mPendingPhysicsFlags.erase(object_id); + + LLViewerObject* object = findObject(object_id); + if (object) + { + object->setPhysicsDensity(density); + object->setPhysicsFriction(friction); + object->setPhysicsGravity(gravity_multiplier); + object->setPhysicsRestitution(restitution); + } +} + +void LLViewerObjectList::onPhysicsFlagsFetchFailure(const LLUUID& object_id) +{ + //llwarns << "Failed to fetch physics flags for object: " << object_id << llendl; + mPendingPhysicsFlags.erase(object_id); +} +#endif //MESH_ENABLED void LLViewerObjectList::shiftObjects(const LLVector3 &offset) diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h index 90d100044..c47ace52e 100644 --- a/indra/newview/llviewerobjectlist.h +++ b/indra/newview/llviewerobjectlist.h @@ -44,6 +44,7 @@ #include "llviewerobject.h" #include "llvoavatar.h" +class LLCamera; class LLNetMap; class LLDebugBeacon; @@ -94,6 +95,25 @@ public: void updateApparentAngles(LLAgent &agent); void update(LLAgent &agent, LLWorld &world); +#if MESH_ENABLED + void fetchObjectCosts(); + void fetchPhysicsFlags(); + + void updateObjectCost(LLViewerObject* object); + void updateObjectCost(const LLUUID& object_id, F32 object_cost, F32 link_cost, F32 physics_cost, F32 link_physics_cost); + void onObjectCostFetchFailure(const LLUUID& object_id); + + void updatePhysicsFlags(const LLViewerObject* object); + void onPhysicsFlagsFetchFailure(const LLUUID& object_id); + void updatePhysicsShapeType(const LLUUID& object_id, S32 type); + void updatePhysicsProperties(const LLUUID& object_id, + F32 density, + F32 friction, + F32 restitution, + F32 gravity_multiplier); + + void updateQuota( const LLUUID& objectId, const SelectionQuota& costs ); +#endif //MESH_ENABLED void shiftObjects(const LLVector3 &offset); void repartitionObjects(); @@ -212,6 +232,16 @@ protected: std::map > mUUIDObjectMap; std::map > mUUIDAvatarMap; +#if MESH_ENABLED + //set of objects that need to update their cost + std::set mStaleObjectCost; + std::set mPendingObjectCost; + + //set of objects that need to update their physics flags + std::set mStalePhysicsFlags; + std::set mPendingPhysicsFlags; +#endif //MESH_ENABLED + std::vector mDebugBeacons; S32 mCurLazyUpdateIndex; diff --git a/indra/newview/skins/default/xui/en-us/floater_tools.xml b/indra/newview/skins/default/xui/en-us/floater_tools.xml index 295ac58c7..b303cb50b 100644 --- a/indra/newview/skins/default/xui/en-us/floater_tools.xml +++ b/indra/newview/skins/default/xui/en-us/floater_tools.xml @@ -301,9 +301,7 @@ - primitives: [COUNT] - + mouse_opaque="true" name="prim_count" v_pad="0" width="143"/> @@ -1011,6 +1009,9 @@ + None + Prim + Convex Hull + width="118" /> + width="118" /> + width="118" /> + increment="0.5" initial_val="0" label="Wind" label_width="55" left="10" + max_val="10" min_val="0" mouse_opaque="true" name="FlexWind" width="118" /> + increment="0.5" initial_val="1" label="Tension" label_width="55" left="10" + max_val="10" min_val="0" mouse_opaque="true" name="FlexTension" width="118" /> + width="118" /> + width="118" /> + width="118" /> - + ncrement="0.05" + initial_val="1" + label="Ambiance" + label_width="55" + max_val="1" + min_val="0" + mouse_opaque="true" + name="Light Ambiance" + width="118" /> + + Physics Shape Type: + + + + + + + + + @@ -1469,4 +1560,10 @@ Attachment + + , Cost: [SEL_WEIGHT] + + + Primitives: [COUNT][PE_STRING] + diff --git a/scripts/messages/message_template.msg b/scripts/messages/message_template.msg index d56bda3f6..d3acb611e 100644 --- a/scripts/messages/message_template.msg +++ b/scripts/messages/message_template.msg @@ -2061,6 +2061,15 @@ version 2.0 { IsPhantom BOOL } { CastsShadows BOOL } } + { + ExtraPhysics Variable + { PhysicsShapeType U8 } + { Density F32 } + { Friction F32 } + { Restitution F32 } + { GravityMultiplier F32 } + + } } From 0bd444de5f5cabb7eb567edeffd0ef1be5a7bbbc Mon Sep 17 00:00:00 2001 From: Shyotl Date: Mon, 1 Aug 2011 03:53:25 -0500 Subject: [PATCH 22/22] A few more rigging related things added in. Also fixed vertex weights --- indra/llcharacter/llcharacter.cpp | 23 ++++++-- indra/llcharacter/llcharacter.h | 1 + indra/newview/llface.cpp | 60 +++++++++---------- indra/newview/llspatialpartition.cpp | 31 +++++++++- indra/newview/llvoavatar.cpp | 87 +++++++++++++++++++++++++++- indra/newview/llvoavatar.h | 10 ++++ indra/newview/llvovolume.cpp | 2 +- 7 files changed, 172 insertions(+), 42 deletions(-) diff --git a/indra/llcharacter/llcharacter.cpp b/indra/llcharacter/llcharacter.cpp index 89a55658f..288bf1ff5 100644 --- a/indra/llcharacter/llcharacter.cpp +++ b/indra/llcharacter/llcharacter.cpp @@ -45,7 +45,7 @@ LLStringTable LLCharacter::sVisualParamNames(1024); std::vector< LLCharacter* > LLCharacter::sInstances; - +BOOL LLCharacter::sAllowInstancesChange = TRUE ; //----------------------------------------------------------------------------- // LLCharacter() @@ -59,8 +59,10 @@ LLCharacter::LLCharacter() mSkeletonSerialNum( 0 ), mInAppearance( false ) { - mMotionController.setCharacter( this ); + llassert_always(sAllowInstancesChange) ; sInstances.push_back(this); + + mMotionController.setCharacter( this ); mPauseRequest = new LLPauseRequestHandle(); } @@ -77,11 +79,22 @@ LLCharacter::~LLCharacter() { delete param; } - std::vector::iterator iter = std::find(sInstances.begin(), sInstances.end(), this); - if (iter != sInstances.end()) + + U32 i ; + U32 size = sInstances.size() ; + for(i = 0 ; i < size ; i++) { - sInstances.erase(iter); + if(sInstances[i] == this) + { + break ; + } } + + llassert_always(i < size) ; + + llassert_always(sAllowInstancesChange) ; + sInstances[i] = sInstances[size - 1] ; + sInstances.pop_back() ; } diff --git a/indra/llcharacter/llcharacter.h b/indra/llcharacter/llcharacter.h index f7206f750..7d30b006d 100644 --- a/indra/llcharacter/llcharacter.h +++ b/indra/llcharacter/llcharacter.h @@ -279,6 +279,7 @@ public: void setSkeletonSerialNum( U32 num ) { mSkeletonSerialNum = num; } static std::vector< LLCharacter* > sInstances; + static BOOL sAllowInstancesChange ; //debug use protected: LLMotionController mMotionController; diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index ac41fe18c..39eaed4b2 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -1173,37 +1173,6 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, if (!tep) rebuild_color = FALSE; // can't get color when tep is NULL U8 bump_code = tep ? tep->getBumpmap() : 0; - if (rebuild_pos) - { - mVertexBuffer->getVertexStrider(vertices, mGeomIndex); - } - if (rebuild_normal) - { - mVertexBuffer->getNormalStrider(normals, mGeomIndex); - } - if (rebuild_binormal) - { - mVertexBuffer->getBinormalStrider(binormals, mGeomIndex); - } -#if MESH_ENABLED - if (rebuild_weights) - { - mVertexBuffer->getWeight4Strider(weights, mGeomIndex); - } -#endif //MESH_ENABLED - - if (rebuild_tcoord) - { - mVertexBuffer->getTexCoord0Strider(tex_coords, mGeomIndex); - if (bump_code && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_TEXCOORD1)) - { - mVertexBuffer->getTexCoord1Strider(tex_coords2, mGeomIndex); - } - } - if (rebuild_color) - { - mVertexBuffer->getColorStrider(colors, mGeomIndex); - } BOOL is_static = mDrawablep->isStatic(); @@ -1249,6 +1218,8 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, { indicesp[i] = vf.mIndices[i] + index_offset; } + + //mVertexBuffer->setBuffer(0); } LLMatrix4a mat_normal; @@ -1394,6 +1365,8 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, if (!do_bump) { //not in atlas or not bump mapped, might be able to do a cheap update + mVertexBuffer->getTexCoord0Strider(tex_coords, mGeomIndex); + if (texgen != LLTextureEntry::TEX_GEN_PLANAR) { if (!do_tex_mat) @@ -1467,9 +1440,12 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, } } } + + //mVertexBuffer->setBuffer(0); } else { //either bump mapped or in atlas, just do the whole expensive loop + mVertexBuffer->getTexCoord0Strider(tex_coords, mGeomIndex); std::vector bump_tc; @@ -1523,9 +1499,12 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, } } + //mVertexBuffer->setBuffer(0); + if (do_bump) { + mVertexBuffer->getTexCoord1Strider(tex_coords2, mGeomIndex); for (S32 i = 0; i < num_vertices; i++) { @@ -1553,13 +1532,16 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, tc += LLVector2( bump_s_primary_light_ray.dot3(tangent).getF32(), bump_t_primary_light_ray.dot3(binormal).getF32() ); *tex_coords2++ = tc; - } + } + + //mVertexBuffer->setBuffer(0); } } } if (rebuild_pos) { + mVertexBuffer->getVertexStrider(vertices, mGeomIndex); LLMatrix4a mat_vert; mat_vert.loadu(mat_vert_in); @@ -1572,10 +1554,12 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, } + //mVertexBuffer->setBuffer(0); } if (rebuild_normal) { + mVertexBuffer->getNormalStrider(normals, mGeomIndex); for (S32 i = 0; i < num_vertices; i++) { LLVector4a normal; @@ -1583,10 +1567,13 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, normal.normalize3fast(); normals[i].set(normal.getF32ptr()); } + + //mVertexBuffer->setBuffer(0); } if (rebuild_binormal) { + mVertexBuffer->getBinormalStrider(binormals, mGeomIndex); for (S32 i = 0; i < num_vertices; i++) { LLVector4a binormal; @@ -1594,24 +1581,31 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, binormal.normalize3fast(); binormals[i].set(binormal.getF32ptr()); } + + //mVertexBuffer->setBuffer(0); } #if MESH_ENABLED if (rebuild_weights && vf.mWeights) { + mVertexBuffer->getWeight4Strider(weights, mGeomIndex); for (S32 i = 0; i < num_vertices; i++) { - weights[i].set(*(weights.get())); + weights[i].set(vf.mWeights[i].getF32ptr()); } + //mVertexBuffer->setBuffer(0); } #endif //MESH_ENABLED if (rebuild_color) { + mVertexBuffer->getColorStrider(colors, mGeomIndex); for (S32 i = 0; i < num_vertices; i++) { colors[i] = color; } + + //mVertexBuffer->setBuffer(0); } if (rebuild_tcoord) diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index 2b49710d4..abc281e0e 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -38,13 +38,18 @@ #include "llviewerobjectlist.h" #include "llvovolume.h" #include "llvolume.h" +#include "llvolumeoctree.h" #include "llviewercamera.h" #include "llface.h" +#include "llfloatertools.h" #include "llviewercontrol.h" #include "llagent.h" #include "llviewerregion.h" #include "llcamera.h" #include "pipeline.h" +#if MESH_ENABLED +#include "llmeshrepository.h" +#endif //MESH_ENABLED #include "llrender.h" #include "lloctree.h" #include "llvoavatar.h" @@ -3467,7 +3472,31 @@ public: if (vobj) { LLVector3 intersection; - if (vobj->lineSegmentIntersect(mStart, mEnd, -1, mPickTransparent, mFaceHit, &intersection, mTexCoord, mNormal, mBinormal)) + bool skip_check = false; +#if MESH_ENABLED + if (vobj->isAvatar()) + { + LLVOAvatar* avatar = (LLVOAvatar*) vobj; + if (avatar->isSelf() && gFloaterTools->getVisible()) + { + LLViewerObject* hit = avatar->lineSegmentIntersectRiggedAttachments(mStart, mEnd, -1, mPickTransparent, mFaceHit, &intersection, mTexCoord, mNormal, mBinormal); + if (hit) + { + mEnd = intersection; + if (mIntersection) + { + *mIntersection = intersection; + } + + mHit = hit->mDrawable; + skip_check = true; + } + + } + } +#endif //MESH_ENABLED + + if (!skip_check && vobj->lineSegmentIntersect(mStart, mEnd, -1, mPickTransparent, mFaceHit, &intersection, mTexCoord, mNormal, mBinormal)) { mEnd = intersection; // shorten ray so we only find CLOSER hits if (mIntersection) diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index d5f1bcbdb..ccf294730 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -1753,6 +1753,33 @@ BOOL LLVOAvatar::lineSegmentIntersect(const LLVector3& start, const LLVector3& e return TRUE; } } +#if MESH_ENABLED + if (isSelf()) + { + for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); + iter != mAttachmentPoints.end(); + ++iter) + { + LLViewerJointAttachment* attachment = iter->second; + + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) + { + LLViewerObject* attached_object = (*attachment_iter); + + if (attached_object && !attached_object->isDead() && attachment->getValid()) + { + LLDrawable* drawable = attached_object->mDrawable; + if (drawable->isState(LLDrawable::RIGGED)) + { //regenerate octree for rigged attachment + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_RIGGED, TRUE); + } + } + } + } + } +#endif //MESH_ENABLED } LLVector3 position; @@ -1769,6 +1796,58 @@ BOOL LLVOAvatar::lineSegmentIntersect(const LLVector3& start, const LLVector3& e return FALSE; } +#if MESH_ENABLED +LLViewerObject* LLVOAvatar::lineSegmentIntersectRiggedAttachments(const LLVector3& start, const LLVector3& end, + S32 face, + BOOL pick_transparent, + S32* face_hit, + LLVector3* intersection, + LLVector2* tex_coord, + LLVector3* normal, + LLVector3* bi_normal) +{ + if (isSelf() && !gAgent.needsRenderAvatar()) + { + return NULL; + } + + LLViewerObject* hit = NULL; + + if (lineSegmentBoundingBox(start, end)) + { + LLVector3 local_end = end; + LLVector3 local_intersection; + + for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); + iter != mAttachmentPoints.end(); + ++iter) + { + LLViewerJointAttachment* attachment = iter->second; + + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) + { + LLViewerObject* attached_object = (*attachment_iter); + + if (attached_object->lineSegmentIntersect(start, local_end, face, pick_transparent, face_hit, &local_intersection, tex_coord, normal, bi_normal)) + { + local_end = local_intersection; + if (intersection) + { + *intersection = local_intersection; + } + + hit = attached_object; + } + } + } + } + + return hit; +} +#endif //MESH_ENABLED + //----------------------------------------------------------------------------- // parseSkeletonFile() //----------------------------------------------------------------------------- @@ -7247,6 +7326,7 @@ BOOL LLVOAvatar::attachObject(LLViewerObject *viewer_object) if (attachment->isObjectAttached(viewer_object)) { LLCOFMgr::instance().addAttachment(viewer_object->getAttachmentItemID()); + updateLODRiggedAttachments(); } } @@ -10731,16 +10811,19 @@ U32 LLVOAvatar::getPartitionType() const //static void LLVOAvatar::updateImpostors() { + LLCharacter::sAllowInstancesChange = FALSE ; + for (std::vector::iterator iter = LLCharacter::sInstances.begin(); - iter != LLCharacter::sInstances.end(); ++iter) + iter != LLCharacter::sInstances.end(); ++iter) { LLVOAvatar* avatar = (LLVOAvatar*) *iter; - if (!avatar->isDead() && avatar->needsImpostorUpdate() && avatar->isVisible() && avatar->isImpostor()) { gPipeline.generateImpostor(avatar); } } + + LLCharacter::sAllowInstancesChange = TRUE ; } BOOL LLVOAvatar::isImpostor() const diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index b216496a9..92e240ac8 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -148,7 +148,17 @@ public: 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 +#if MESH_ENABLED + LLViewerObject* lineSegmentIntersectRiggedAttachments(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 +#endif //MESH_ENABLED //-------------------------------------------------------------------- // LLCharacter interface and related //-------------------------------------------------------------------- diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index af03d5270..b5ca840ca 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -811,7 +811,7 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams ¶ms_in, const S32 detail, bo if (mVolumeImpl) { - mVolumeImpl->onSetVolume(volume_params, detail); //mLOD ? + mVolumeImpl->onSetVolume(volume_params, mLOD); //detail ? } updateSculptTexture();