diff options
Diffstat (limited to 'indra')
114 files changed, 9906 insertions, 5720 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 80df91a5c1..9910281b64 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -115,6 +115,7 @@ set(llcommon_HEADER_FILES indra_constants.h linden_common.h linked_lists.h + llaccountingquota.h llallocator.h llallocator_heap_profile.h llagentconstants.h diff --git a/indra/llcommon/llaccountingquota.h b/indra/llcommon/llaccountingquota.h new file mode 100644 index 0000000000..567d02dac1 --- /dev/null +++ b/indra/llcommon/llaccountingquota.h @@ -0,0 +1,86 @@ +/** + * @file llaccountingquota.h + * @ + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#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/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp index 5be5ecc492..bf62600514 100644 --- a/indra/llcommon/llsdserialize.cpp +++ b/indra/llcommon/llsdserialize.cpp @@ -2036,7 +2036,9 @@ std::string zip_llsd(LLSD& data) { //copy result into output if (strm.avail_out >= CHUNK) { - llerrs << "WTF?" << llendl; + free(output); + llwarns << "Failed to compress LLSD block." << llendl; + return std::string(); } have = CHUNK-strm.avail_out; diff --git a/indra/llcommon/llstat.cpp b/indra/llcommon/llstat.cpp index 8ba97d7730..b2c495d093 100644 --- a/indra/llcommon/llstat.cpp +++ b/indra/llcommon/llstat.cpp @@ -737,7 +737,7 @@ void LLPerfBlock::addStatsToLLSDandReset( LLSD & stats, } } else - { // WTF? Shouldn't have a NULL pointer in the map. + { // Shouldn't have a NULL pointer in the map. llwarns << "Unexpected NULL dynamic stat at '" << stats_full_path << "'" << llendl; } } diff --git a/indra/llinventory/llparcel.cpp b/indra/llinventory/llparcel.cpp index 0a4cd51ea0..e8cd871157 100644 --- a/indra/llinventory/llparcel.cpp +++ b/indra/llinventory/llparcel.cpp @@ -1348,3 +1348,12 @@ LLParcel::ECategory category_ui_string_to_category(const std::string& s) // is a distinct option from "None" and "Other" return LLParcel::C_ANY; } + +void LLParcel::updateQuota( const LLUUID& objectId, const ParcelQuota& quota ) +{ + if ( mID == objectId ) + { + mQuota = quota; + } +} + diff --git a/indra/llinventory/llparcel.h b/indra/llinventory/llparcel.h index 71b65d99ce..4893337967 100644 --- a/indra/llinventory/llparcel.h +++ b/indra/llinventory/llparcel.h @@ -34,7 +34,7 @@ #include "llpermissions.h" #include "lltimer.h" #include "v3math.h" - +#include "llaccountingquota.h" // Grid out of which parcels taken is stepped every 4 meters. const F32 PARCEL_GRID_STEP_METERS = 4.f; @@ -586,7 +586,11 @@ public: LLUUID getPreviousOwnerID() const { return mPreviousOwnerID; } BOOL getPreviouslyGroupOwned() const { return mPreviouslyGroupOwned; } BOOL getSellWithObjects() const { return (mParcelFlags & PF_SELL_PARCEL_OBJECTS) ? TRUE : FALSE; } - + + + void updateQuota( const LLUUID& objectId, const ParcelQuota& quota ); + const ParcelQuota& getQuota( void ) { return mQuota; } + protected: LLUUID mID; LLUUID mOwnerID; @@ -657,8 +661,9 @@ protected: BOOL mRegionPushOverride; BOOL mRegionDenyAnonymousOverride; BOOL mRegionDenyAgeUnverifiedOverride; - - + + ParcelQuota mQuota; + public: // HACK, make private S32 mLocalID; diff --git a/indra/llmath/lloctree.h b/indra/llmath/lloctree.h index fdfc24f8b7..e5ca47da69 100644 --- a/indra/llmath/lloctree.h +++ b/indra/llmath/lloctree.h @@ -35,12 +35,14 @@ #define OCT_ERRS LL_WARNS("OctreeErrors") -#define LL_OCTREE_PARANOIA_CHECK 0 + +extern U32 gOctreeMaxCapacity; +/*#define LL_OCTREE_PARANOIA_CHECK 0 #if LL_DARWIN #define LL_OCTREE_MAX_CAPACITY 32 #else #define LL_OCTREE_MAX_CAPACITY 128 -#endif +#endif*/ template <class T> class LLOctreeNode; @@ -74,6 +76,7 @@ template <class T> class LLOctreeNode : public LLTreeNode<T> { public: + typedef LLOctreeTraveler<T> oct_traveler; typedef LLTreeTraveler<T> tree_traveler; typedef typename std::set<LLPointer<T> > element_list; @@ -294,8 +297,8 @@ public: //is it here? if (isInside(data->getPositionGroup())) { - if ((getElementCount() < LL_OCTREE_MAX_CAPACITY && contains(data->getBinRadius()) || - (data->getBinRadius() > getSize()[0] && parent && parent->getElementCount() >= LL_OCTREE_MAX_CAPACITY))) + if ((getElementCount() < gOctreeMaxCapacity && contains(data->getBinRadius()) || + (data->getBinRadius() > getSize()[0] && parent && parent->getElementCount() >= gOctreeMaxCapacity))) { //it belongs here #if LL_OCTREE_PARANOIA_CHECK //if this is a redundant insertion, error out (should never happen) diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index c504215ee5..fbda824179 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -100,7 +100,7 @@ void assert_aligned(void* ptr, uintptr_t alignment) uintptr_t t = (uintptr_t) ptr; if (t%alignment != 0) { - llerrs << "WTF?" << llendl; + llerrs << "Alignment check failed." << llendl; } #endif } @@ -361,7 +361,7 @@ public: } else { - llerrs << "WTF? Empty leaf" << llendl; + llerrs << "Empty leaf" << llendl; } for (S32 i = 0; i < branch->getChildCount(); ++i) @@ -2159,8 +2159,9 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) U32 face_count = mdl.size(); if (face_count == 0) - { - llerrs << "WTF?" << llendl; + { //no faces unpacked, treat as failed decode + llwarns << "found no faces!" << llendl; + return false; } mVolumeFaces.resize(face_count); @@ -2179,7 +2180,7 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) if (idx.empty() || face.mNumIndices < 3) { //why is there an empty index list? - llerrs <<"WTF?" << llendl; + llwarns <<"Empty face present!" << llendl; continue; } diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index 794cdb83d5..3439cf3310 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -84,7 +84,7 @@ void load_face_from_dom_inputs(LLVolumeFace& face, const domInputLocalOffset_Arr domInputLocal_Array& v_inp = vertices->getInput_array(); if (inputs[j]->getOffset() != 0) { - llerrs << "WTF?" << llendl; + llerrs << "Vertex array offset MUST be zero." << llendl; } for (U32 k = 0; k < v_inp.getCount(); ++k) @@ -98,7 +98,7 @@ void load_face_from_dom_inputs(LLVolumeFace& face, const domInputLocalOffset_Arr if (src->getTechnique_common()->getAccessor()->getStride() != 3) { - llerrs << "WTF?" << llendl; + llerrs << "Vertex array stride MUST be three." << llendl; } domListOfFloats& v = src->getFloat_array()->getValue(); @@ -667,11 +667,6 @@ LLModel::EModelStatus load_face_from_dom_polygons(std::vector<LLVolumeFace>& fac } } - if (cur_idx != vert_idx.size()) - { - llerrs << "WTF?" << llendl; - } - //build vertex array from map std::vector<LLVolumeFace::VertexData> new_verts; new_verts.resize(vert_idx.size()); @@ -926,11 +921,6 @@ void LLModel::normalizeVolumeFaces() { LLVector4a min, max; - if (mVolumeFaces[0].mNumVertices <= 0) - { - llerrs << "WTF?" << llendl; - } - // For all of the volume faces // in the model, loop over // them and see what the extents @@ -942,11 +932,6 @@ void LLModel::normalizeVolumeFaces() { LLVolumeFace& face = mVolumeFaces[i]; - if (face.mNumVertices <= 0) - { - llerrs << "WTF?" << llendl; - } - update_min_max(min, max, face.mExtents[0]); update_min_max(min, max, face.mExtents[1]); } @@ -991,6 +976,9 @@ void LLModel::normalizeVolumeFaces() scale.splat(1.f); scale.div(size); + LLVector4a inv_scale(1.f); + inv_scale.div(scale); + for (U32 i = 0; i < mVolumeFaces.size(); ++i) { LLVolumeFace& face = mVolumeFaces[i]; @@ -1007,10 +995,14 @@ void LLModel::normalizeVolumeFaces() // For all the positions, we scale // the positions to fit within the unit cube. LLVector4a* pos = (LLVector4a*) face.mPositions; + LLVector4a* norm = (LLVector4a*) face.mNormals; + for (U32 j = 0; j < face.mNumVertices; ++j) { pos[j].add(trans); pos[j].mul(scale); + norm[j].mul(inv_scale); + norm[j].normalize3(); } } @@ -1282,11 +1274,6 @@ void LLModel::generateNormals(F32 angle_cutoff) { LLVector4a& n = iter->second[k].getNormal(); - if (!iter->second[k].getPosition().equals3(new_face.mPositions[i])) - { - llerrs << "WTF?" << llendl; - } - F32 cur = n.dot3(ref_norm).getF32(); if (cur > best) @@ -1532,11 +1519,6 @@ LLSD LLModel::writeModel( weight_list& weights = high->getJointInfluences(pos); - if (weights.size() > 4) - { - llerrs << "WTF?" << llendl; - } - S32 count = 0; for (weight_list::iterator iter = weights.begin(); iter != weights.end(); ++iter) { @@ -1600,10 +1582,6 @@ LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite) cur_offset += size; bytes += size; } - else - { - llerrs << "WTF?" << llendl; - } } std::string decomposition; @@ -1678,7 +1656,7 @@ LLModel::weight_list& LLModel::getJointInfluences(const LLVector3& pos) { if ((iter->first - pos).magVec() > 0.1f) { - llerrs << "WTF?" << llendl; + llerrs << "Couldn't find weight list." << llendl; } return iter->second; @@ -1771,7 +1749,7 @@ void LLModel::updateHullCenters() if (mHullPoints > 0) { mCenterOfHullCenters *= 1.f / mHullPoints; - llassert(mPhysics.asLLSD().has("HullList")); + llassert(mPhysics.hasHullList()); } } @@ -2120,6 +2098,11 @@ void LLModel::Decomposition::fromLLSD(LLSD& decomp) } } +bool LLModel::Decomposition::hasHullList() const +{ + return !mHull.empty() ; +} + LLSD LLModel::Decomposition::asLLSD() const { LLSD ret; @@ -2201,14 +2184,17 @@ LLSD LLModel::Decomposition::asLLSD() const //convert to 16-bit normalized across domain U16 val = (U16) (((mHull[i][j].mV[k]-min.mV[k])/range.mV[k])*65535); - switch (k) + if(valid.size() < 3) { - case 0: test = test | (U64) val; break; - case 1: test = test | ((U64) val << 16); break; - case 2: test = test | ((U64) val << 32); break; - }; + 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); + valid.insert(test); + } U8* buff = (U8*) &val; //write to binary buffer @@ -2220,8 +2206,8 @@ LLSD LLModel::Decomposition::asLLSD() const } } - //must have at least 4 unique points - llassert(valid.size() > 3); + //must have at least 3 unique points + llassert(valid.size() > 2); } ret["Position"] = p; @@ -2248,7 +2234,7 @@ LLSD LLModel::Decomposition::asLLSD() const if (vert_idx > p.size()) { - llerrs << "WTF?" << llendl; + llerrs << "Index out of bounds" << llendl; } } } @@ -2283,10 +2269,5 @@ void LLModel::Decomposition::merge(const LLModel::Decomposition* rhs) { //take physics shape mesh from rhs mPhysicsShapeMesh = rhs->mPhysicsShapeMesh; } - - if (!mHull.empty()) - { //verify - llassert(asLLSD().has("HullList")); - } } diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h index 23f4b5cb42..ebca1f9d9d 100644 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -106,6 +106,7 @@ public: Decomposition(LLSD& data); void fromLLSD(LLSD& data); LLSD asLLSD() const; + bool hasHullList() const; void merge(const Decomposition* rhs); diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index f29ee0e57e..96b1838158 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -178,6 +178,12 @@ PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer = NULL; PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample = NULL; PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer = NULL; +//GL_ARB_texture_multisample +PFNGLTEXIMAGE2DMULTISAMPLEPROC glTexImage2DMultisample; +PFNGLTEXIMAGE3DMULTISAMPLEPROC glTexImage3DMultisample; +PFNGLGETMULTISAMPLEFVPROC glGetMultisamplefv; +PFNGLSAMPLEMASKIPROC glSampleMaski; + // GL_EXT_blend_func_separate PFNGLBLENDFUNCSEPARATEEXTPROC glBlendFuncSeparateEXT = NULL; @@ -321,6 +327,7 @@ LLGLManager::LLGLManager() : mHasMipMapGeneration(FALSE), mHasCompressedTextures(FALSE), mHasFramebufferObject(FALSE), + mMaxSamples(0), mHasBlendFuncSeparate(FALSE), mHasVertexBufferObject(FALSE), @@ -328,11 +335,17 @@ LLGLManager::LLGLManager() : mHasShaderObjects(FALSE), mHasVertexShader(FALSE), mHasFragmentShader(FALSE), + mNumTextureImageUnits(0), mHasOcclusionQuery(FALSE), mHasOcclusionQuery2(FALSE), mHasPointParameters(FALSE), mHasDrawBuffers(FALSE), mHasTextureRectangle(FALSE), + mHasTextureMultisample(FALSE), + mMaxSampleMaskWords(0), + mMaxColorTextureSamples(0), + mMaxDepthTextureSamples(0), + mMaxIntegerSamples(0), mHasAnisotropic(FALSE), mHasARBEnvCombine(FALSE), @@ -534,6 +547,26 @@ bool LLGLManager::initGL() return false; } + if (mHasFragmentShader) + { + GLint num_tex_image_units; + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &num_tex_image_units); + mNumTextureImageUnits = num_tex_image_units; + } + + if (mHasTextureMultisample) + { + glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &mMaxColorTextureSamples); + glGetIntegerv(GL_MAX_DEPTH_TEXTURE_SAMPLES, &mMaxDepthTextureSamples); + glGetIntegerv(GL_MAX_INTEGER_SAMPLES, &mMaxIntegerSamples); + glGetIntegerv(GL_MAX_SAMPLE_MASK_WORDS, &mMaxSampleMaskWords); + } + + if (mHasFramebufferObject) + { + glGetIntegerv(GL_MAX_SAMPLES, &mMaxSamples); + } + setToDebugGPU(); initGLStates(); @@ -640,6 +673,14 @@ std::string LLGLManager::getRawGLString() return gl_string; } +U32 LLGLManager::getNumFBOFSAASamples(U32 samples) +{ + samples = llmin(samples, (U32) mMaxColorTextureSamples); + samples = llmin(samples, (U32) mMaxDepthTextureSamples); + samples = llmin(samples, (U32) 4); + return samples; +} + void LLGLManager::shutdownGL() { if (mInited) @@ -734,6 +775,7 @@ void LLGLManager::initExtensions() mHasDrawBuffers = ExtensionExists("GL_ARB_draw_buffers", gGLHExts.mSysExts); mHasBlendFuncSeparate = ExtensionExists("GL_EXT_blend_func_separate", gGLHExts.mSysExts); mHasTextureRectangle = ExtensionExists("GL_ARB_texture_rectangle", gGLHExts.mSysExts); + mHasTextureMultisample = ExtensionExists("GL_ARB_texture_multisample", gGLHExts.mSysExts); #if !LL_DARWIN mHasPointParameters = !mIsATI && ExtensionExists("GL_ARB_point_parameters", gGLHExts.mSysExts); #endif @@ -878,11 +920,13 @@ void LLGLManager::initExtensions() LL_INFOS("RenderInit") << "Disabling mip-map generation for Intel GPUs" << LL_ENDL; mHasMipMapGeneration = FALSE; } +#if !LL_DARWIN if (mIsATI && mHasMipMapGeneration) { LL_INFOS("RenderInit") << "Disabling mip-map generation for ATI GPUs (performance opt)" << LL_ENDL; mHasMipMapGeneration = FALSE; } +#endif // Misc glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, (GLint*) &mGLMaxVertexRange); @@ -943,6 +987,13 @@ void LLGLManager::initExtensions() { glBlendFuncSeparateEXT = (PFNGLBLENDFUNCSEPARATEEXTPROC) GLH_EXT_GET_PROC_ADDRESS("glBlendFuncSeparateEXT"); } + if (mHasTextureMultisample) + { + glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC) GLH_EXT_GET_PROC_ADDRESS("glTexImage2DMultisample"); + glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC) GLH_EXT_GET_PROC_ADDRESS("glTexImage3DMultisample"); + glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC) GLH_EXT_GET_PROC_ADDRESS("glGetMultisamplefv"); + glSampleMaski = (PFNGLSAMPLEMASKIPROC) GLH_EXT_GET_PROC_ADDRESS("glSampleMaski"); + } #if (!LL_LINUX && !LL_SOLARIS) || LL_LINUX_NV_GL_HEADERS // This is expected to be a static symbol on Linux GL implementations, except if we use the nvidia headers - bah glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawRangeElements"); @@ -1374,7 +1425,8 @@ void LLGLState::checkTextureChannels(const std::string& msg) "GL_TEXTURE_GEN_T", "GL_TEXTURE_GEN_Q", "GL_TEXTURE_GEN_R", - "GL_TEXTURE_RECTANGLE_ARB" + "GL_TEXTURE_RECTANGLE_ARB", + "GL_TEXTURE_2D_MULTISAMPLE" }; static GLint value[] = @@ -1387,7 +1439,8 @@ void LLGLState::checkTextureChannels(const std::string& msg) GL_TEXTURE_GEN_T, GL_TEXTURE_GEN_Q, GL_TEXTURE_GEN_R, - GL_TEXTURE_RECTANGLE_ARB + GL_TEXTURE_RECTANGLE_ARB, + GL_TEXTURE_2D_MULTISAMPLE }; GLint stackDepth = 0; @@ -1427,11 +1480,16 @@ void LLGLState::checkTextureChannels(const std::string& msg) gFailLog << "Texture matrix in channel " << i << " corrupt." << std::endl; } } - - + for (S32 j = (i == 0 ? 1 : 0); - j < (gGLManager.mHasTextureRectangle ? 9 : 8); j++) + j < 9; j++) { + if (j == 8 && !gGLManager.mHasTextureRectangle || + j == 9 && !gGLManager.mHasTextureMultisample) + { + continue; + } + if (glIsEnabled(value[j])) { error = TRUE; diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h index 3d002fd8c4..420922cf06 100644 --- a/indra/llrender/llgl.h +++ b/indra/llrender/llgl.h @@ -83,20 +83,27 @@ public: BOOL mHasMipMapGeneration; BOOL mHasCompressedTextures; BOOL mHasFramebufferObject; + S32 mMaxSamples; BOOL mHasBlendFuncSeparate; - + // ARB Extensions BOOL mHasVertexBufferObject; BOOL mHasPBuffer; BOOL mHasShaderObjects; BOOL mHasVertexShader; BOOL mHasFragmentShader; + S32 mNumTextureImageUnits; BOOL mHasOcclusionQuery; BOOL mHasOcclusionQuery2; BOOL mHasPointParameters; BOOL mHasDrawBuffers; BOOL mHasDepthClamp; BOOL mHasTextureRectangle; + BOOL mHasTextureMultisample; + S32 mMaxSampleMaskWords; + S32 mMaxColorTextureSamples; + S32 mMaxDepthTextureSamples; + S32 mMaxIntegerSamples; // Other extensions. BOOL mHasAnisotropic; @@ -138,6 +145,7 @@ public: void printGLInfoString(); void getGLInfo(LLSD& info); + U32 getNumFBOFSAASamples(U32 desired_samples = 32); // In ALL CAPS std::string mGLVendor; std::string mGLVendorShort; diff --git a/indra/llrender/llglheaders.h b/indra/llrender/llglheaders.h index d8140a124d..825d304d35 100644 --- a/indra/llrender/llglheaders.h +++ b/indra/llrender/llglheaders.h @@ -474,6 +474,11 @@ extern PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer; //GL_ARB_draw_buffers extern PFNGLDRAWBUFFERSARBPROC glDrawBuffersARB; +//GL_ARB_texture_multisample +extern PFNGLTEXIMAGE2DMULTISAMPLEPROC glTexImage2DMultisample; +extern PFNGLTEXIMAGE3DMULTISAMPLEPROC glTexImage3DMultisample; +extern PFNGLGETMULTISAMPLEFVPROC glGetMultisamplefv; +extern PFNGLSAMPLEMASKIPROC glSampleMaski; #elif LL_WINDOWS //---------------------------------------------------------------------------- @@ -673,6 +678,11 @@ extern PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer; //GL_ARB_draw_buffers extern PFNGLDRAWBUFFERSARBPROC glDrawBuffersARB; +//GL_ARB_texture_multisample +extern PFNGLTEXIMAGE2DMULTISAMPLEPROC glTexImage2DMultisample; +extern PFNGLTEXIMAGE3DMULTISAMPLEPROC glTexImage3DMultisample; +extern PFNGLGETMULTISAMPLEFVPROC glGetMultisamplefv; +extern PFNGLSAMPLEMASKIPROC glSampleMaski; #elif LL_DARWIN //---------------------------------------------------------------------------- diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index 257bcd9380..2e7147a3b4 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -293,7 +293,8 @@ void LLGLSLShader::mapUniform(GLint index, const vector<string> * uniforms) GLint LLGLSLShader::mapUniformTextureChannel(GLint location, GLenum type) { - if (type >= GL_SAMPLER_1D_ARB && type <= GL_SAMPLER_2D_RECT_SHADOW_ARB) + if (type >= GL_SAMPLER_1D_ARB && type <= GL_SAMPLER_2D_RECT_SHADOW_ARB || + type == GL_SAMPLER_2D_MULTISAMPLE) { //this here is a texture glUniform1iARB(location, mActiveTextureChannels); LL_DEBUGS("ShaderLoading") << "Assigned to texture channel " << mActiveTextureChannels << LL_ENDL; diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index d408077c68..3a4139bace 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -1083,12 +1083,17 @@ void LLImageGL::generateTextures(S32 numTextures, U32 *textures) } // static -void LLImageGL::deleteTextures(S32 numTextures, U32 *textures) +void LLImageGL::deleteTextures(S32 numTextures, U32 *textures, bool immediate) { for (S32 i = 0; i < numTextures; i++) { sDeadTextureList.push_back(textures[i]); } + + if (immediate) + { + LLImageGL::deleteDeadTextures(); + } } // static @@ -1417,7 +1422,7 @@ void LLImageGL::deleteDeadTextures() { if (sCurrentBoundTextures[i] == tex) { - gGL.getTexUnit(i)->unbind(LLTexUnit::TT_TEXTURE); + gGL.getTexUnit(i)->unbind(gGL.getTexUnit(i)->getCurrType()); stop_glerror(); } } diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index 6c980984c0..2cfb15b0d9 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -98,7 +98,7 @@ public: // These 3 functions currently wrap glGenTextures(), glDeleteTextures(), and glTexImage2D() // for tracking purposes and will be deprecated in the future static void generateTextures(S32 numTextures, U32 *textures); - static void deleteTextures(S32 numTextures, U32 *textures); + static void deleteTextures(S32 numTextures, U32 *textures, bool immediate = false); static void setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels); BOOL createGLTexture() ; diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index 49e10c4790..049dd4346b 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -53,7 +53,8 @@ static GLenum sGLTextureType[] = { GL_TEXTURE_2D, GL_TEXTURE_RECTANGLE_ARB, - GL_TEXTURE_CUBE_MAP_ARB + GL_TEXTURE_CUBE_MAP_ARB, + GL_TEXTURE_2D_MULTISAMPLE }; static GLint sGLAddressMode[] = @@ -119,14 +120,29 @@ void LLTexUnit::refreshState(void) gGL.flush(); glActiveTextureARB(GL_TEXTURE0_ARB + mIndex); + + // + // Per apple spec, don't call glEnable/glDisable when index exceeds max texture units + // http://www.mailinglistarchive.com/html/mac-opengl@lists.apple.com/2008-07/msg00653.html + // + bool enableDisable = (mIndex < gGLManager.mNumTextureUnits) && mCurrTexType != LLTexUnit::TT_MULTISAMPLE_TEXTURE; + if (mCurrTexType != TT_NONE) { - glEnable(sGLTextureType[mCurrTexType]); + if (enableDisable) + { + glEnable(sGLTextureType[mCurrTexType]); + } + glBindTexture(sGLTextureType[mCurrTexType], mCurrTexture); } else { - glDisable(GL_TEXTURE_2D); + if (enableDisable) + { + glDisable(GL_TEXTURE_2D); + } + glBindTexture(GL_TEXTURE_2D, 0); } @@ -167,7 +183,11 @@ void LLTexUnit::enable(eTextureType type) mCurrTexType = type; gGL.flush(); - glEnable(sGLTextureType[type]); + if (type != LLTexUnit::TT_MULTISAMPLE_TEXTURE && + mIndex < gGLManager.mNumTextureUnits) + { + glEnable(sGLTextureType[type]); + } } } @@ -180,7 +200,12 @@ void LLTexUnit::disable(void) activate(); unbind(mCurrTexType); gGL.flush(); - glDisable(sGLTextureType[mCurrTexType]); + if (mCurrTexType != LLTexUnit::TT_MULTISAMPLE_TEXTURE && + mIndex < gGLManager.mNumTextureUnits) + { + glDisable(sGLTextureType[mCurrTexType]); + } + mCurrTexType = TT_NONE; } } @@ -399,7 +424,7 @@ void LLTexUnit::setTextureAddressMode(eTextureAddressMode mode) void LLTexUnit::setTextureFilteringOption(LLTexUnit::eTextureFilterOptions option) { - if (mIndex < 0 || mCurrTexture == 0) return; + if (mIndex < 0 || mCurrTexture == 0 || mCurrTexType == LLTexUnit::TT_MULTISAMPLE_TEXTURE) return; gGL.flush(); diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h index 7ba14f7b40..41e7b35341 100644 --- a/indra/llrender/llrender.h +++ b/indra/llrender/llrender.h @@ -57,6 +57,7 @@ public: TT_TEXTURE = 0, // Standard 2D Texture TT_RECT_TEXTURE, // Non power of 2 texture TT_CUBE_MAP, // 6-sided cube map texture + TT_MULTISAMPLE_TEXTURE, // see GL_ARB_texture_multisample TT_NONE // No texture type is currently enabled } eTextureType; diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index cd2556d435..715f466314 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -44,6 +44,7 @@ void check_framebuffer_status() case GL_FRAMEBUFFER_COMPLETE: break; default: + llwarns << "check_framebuffer_status failed -- " << std::hex << status << llendl; ll_fail("check_framebuffer_status failed"); break; } @@ -62,8 +63,7 @@ LLRenderTarget::LLRenderTarget() : mUseDepth(false), mRenderDepth(false), mUsage(LLTexUnit::TT_TEXTURE), - mSamples(0), - mSampleBuffer(NULL) + mSamples(0) { } @@ -72,13 +72,7 @@ LLRenderTarget::~LLRenderTarget() release(); } - -void LLRenderTarget::setSampleBuffer(LLMultisampleBuffer* buffer) -{ - mSampleBuffer = buffer; -} - -void LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo) +void LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo, S32 samples) { stop_glerror(); mResX = resx; @@ -87,6 +81,20 @@ void LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, boo mStencil = stencil; mUsage = usage; mUseDepth = depth; + mSamples = samples; + + mSamples = gGLManager.getNumFBOFSAASamples(mSamples); + + if (mSamples > 1 && gGLManager.mHasTextureMultisample) + { + mUsage = LLTexUnit::TT_MULTISAMPLE_TEXTURE; + //no support for multisampled stencil targets yet + mStencil = false; + } + else + { + mSamples = 0; + } release(); @@ -145,29 +153,47 @@ void LLRenderTarget::addColorAttachment(U32 color_fmt) stop_glerror(); - LLImageGL::setManualImage(LLTexUnit::getInternalType(mUsage), 0, color_fmt, mResX, mResY, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - stop_glerror(); - - if (offset == 0) + if (mSamples > 1) { - gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); + glTexImage2DMultisample(LLTexUnit::getInternalType(mUsage), mSamples, color_fmt, mResX, mResY, GL_TRUE); } else - { //don't filter data attachments - gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT); - } - if (mUsage != LLTexUnit::TT_RECT_TEXTURE) { - gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_MIRROR); + LLImageGL::setManualImage(LLTexUnit::getInternalType(mUsage), 0, color_fmt, mResX, mResY, GL_RGBA, GL_UNSIGNED_BYTE, NULL); } - else - { - // ATI doesn't support mirrored repeat for rectangular textures. - gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); + + stop_glerror(); + + if (mSamples == 0) + { + if (offset == 0) + { //use bilinear filtering on single texture render targets that aren't multisampled + gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); + stop_glerror(); + } + else + { //don't filter data attachments + gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + stop_glerror(); + } + + if (mUsage != LLTexUnit::TT_RECT_TEXTURE) + { + gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_MIRROR); + stop_glerror(); + } + else + { + // ATI doesn't support mirrored repeat for rectangular textures. + gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); + stop_glerror(); + } } + if (mFBO) { + stop_glerror(); glBindFramebuffer(GL_FRAMEBUFFER, mFBO); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+offset, LLTexUnit::getInternalType(mUsage), tex, 0); @@ -180,6 +206,12 @@ void LLRenderTarget::addColorAttachment(U32 color_fmt) mTex.push_back(tex); + if (gDebugGL) + { //bind and unbind to validate target + bindTarget(); + flush(); + } + } void LLRenderTarget::allocateDepth() @@ -196,9 +228,16 @@ void LLRenderTarget::allocateDepth() { LLImageGL::generateTextures(1, &mDepth); gGL.getTexUnit(0)->bindManual(mUsage, mDepth); - U32 internal_type = LLTexUnit::getInternalType(mUsage); - gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT); - LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT32, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + if (mSamples == 0) + { + U32 internal_type = LLTexUnit::getInternalType(mUsage); + gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + LLImageGL::setManualImage(internal_type, 0, GL_DEPTH_COMPONENT32, mResX, mResY, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + } + else + { + glTexImage2DMultisample(LLTexUnit::getInternalType(mUsage), mSamples, GL_DEPTH_COMPONENT32, mResX, mResY, GL_TRUE); + } } } @@ -238,6 +277,9 @@ void LLRenderTarget::shareDepthBuffer(LLRenderTarget& target) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, LLTexUnit::getInternalType(mUsage), mDepth, 0); stop_glerror(); } + + check_framebuffer_status(); + glBindFramebuffer(GL_FRAMEBUFFER, 0); target.mUseDepth = true; @@ -255,7 +297,7 @@ void LLRenderTarget::release() } else { - LLImageGL::deleteTextures(1, &mDepth); + LLImageGL::deleteTextures(1, &mDepth, true); stop_glerror(); } mDepth = 0; @@ -284,11 +326,10 @@ void LLRenderTarget::release() if (mTex.size() > 0) { - LLImageGL::deleteTextures(mTex.size(), &mTex[0]); + LLImageGL::deleteTextures(mTex.size(), &mTex[0], true); mTex.clear(); } - mSampleBuffer = NULL; sBoundTarget = NULL; } @@ -297,34 +338,27 @@ void LLRenderTarget::bindTarget() if (mFBO) { stop_glerror(); - if (mSampleBuffer) - { - mSampleBuffer->bindTarget(this); - stop_glerror(); + + glBindFramebuffer(GL_FRAMEBUFFER, mFBO); + stop_glerror(); + if (gGLManager.mHasDrawBuffers) + { //setup multiple render targets + GLenum drawbuffers[] = {GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3}; + glDrawBuffersARB(mTex.size(), drawbuffers); } - else - { - glBindFramebuffer(GL_FRAMEBUFFER, mFBO); - stop_glerror(); - if (gGLManager.mHasDrawBuffers) - { //setup multiple render targets - GLenum drawbuffers[] = {GL_COLOR_ATTACHMENT0, - GL_COLOR_ATTACHMENT1, - GL_COLOR_ATTACHMENT2, - GL_COLOR_ATTACHMENT3}; - glDrawBuffersARB(mTex.size(), drawbuffers); - } - if (mTex.empty()) - { //no color buffer to draw to - glDrawBuffer(GL_NONE); - glReadBuffer(GL_NONE); - } + if (mTex.empty()) + { //no color buffer to draw to + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + } - check_framebuffer_status(); + check_framebuffer_status(); - stop_glerror(); - } + stop_glerror(); } glViewport(0, 0, mResX, mResY); @@ -406,50 +440,8 @@ void LLRenderTarget::flush(bool fetch_depth) else { stop_glerror(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - stop_glerror(); - - if (mSampleBuffer) - { - LLGLEnable multisample(GL_MULTISAMPLE); - stop_glerror(); - glBindFramebuffer(GL_FRAMEBUFFER, mFBO); - stop_glerror(); - check_framebuffer_status(); - glBindFramebuffer(GL_READ_FRAMEBUFFER, mSampleBuffer->mFBO); - check_framebuffer_status(); - - stop_glerror(); - glBlitFramebuffer(0, 0, mResX, mResY, 0, 0, mResX, mResY, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); - stop_glerror(); - - if (mTex.size() > 1) - { - for (U32 i = 1; i < mTex.size(); ++i) - { - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - LLTexUnit::getInternalType(mUsage), mTex[i], 0); - stop_glerror(); - glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mSampleBuffer->mTex[i]); - stop_glerror(); - glBlitFramebuffer(0, 0, mResX, mResY, 0, 0, mResX, mResY, GL_COLOR_BUFFER_BIT, GL_NEAREST); - stop_glerror(); - } - - for (U32 i = 0; i < mTex.size(); ++i) - { - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+i, - LLTexUnit::getInternalType(mUsage), mTex[i], 0); - stop_glerror(); - glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+i, GL_RENDERBUFFER, mSampleBuffer->mTex[i]); - stop_glerror(); - } - } - } - - glBindFramebuffer(GL_FRAMEBUFFER, 0); } } @@ -466,37 +458,36 @@ void LLRenderTarget::copyContents(LLRenderTarget& source, S32 srcX0, S32 srcY0, llerrs << "Cannot copy framebuffer contents for non FBO render targets." << llendl; } - if (mSampleBuffer) + + if (mask == GL_DEPTH_BUFFER_BIT && source.mStencil != mStencil) { - mSampleBuffer->copyContents(source, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + stop_glerror(); + + glBindFramebuffer(GL_FRAMEBUFFER, source.mFBO); + check_framebuffer_status(); + gGL.getTexUnit(0)->bind(this, true); + stop_glerror(); + glCopyTexSubImage2D(LLTexUnit::getInternalType(mUsage), 0, srcX0, srcY0, dstX0, dstY0, dstX1, dstY1); + stop_glerror(); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + stop_glerror(); } else { - if (mask == GL_DEPTH_BUFFER_BIT && source.mStencil != mStencil) - { - stop_glerror(); - - glBindFramebuffer(GL_FRAMEBUFFER, source.mFBO); - gGL.getTexUnit(0)->bind(this, true); - stop_glerror(); - glCopyTexSubImage2D(LLTexUnit::getInternalType(mUsage), 0, srcX0, srcY0, dstX0, dstY0, dstX1, dstY1); - stop_glerror(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - stop_glerror(); - } - else - { - glBindFramebuffer(GL_READ_FRAMEBUFFER, source.mFBO); - stop_glerror(); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFBO); - stop_glerror(); - check_framebuffer_status(); - stop_glerror(); - glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); - stop_glerror(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - stop_glerror(); - } + glBindFramebuffer(GL_READ_FRAMEBUFFER, source.mFBO); + stop_glerror(); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFBO); + stop_glerror(); + check_framebuffer_status(); + stop_glerror(); + glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + stop_glerror(); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + stop_glerror(); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + stop_glerror(); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + stop_glerror(); } } @@ -539,179 +530,3 @@ void LLRenderTarget::getViewport(S32* viewport) viewport[3] = mResY; } -//================================================== -// LLMultisampleBuffer implementation -//================================================== -LLMultisampleBuffer::LLMultisampleBuffer() -{ - -} - -LLMultisampleBuffer::~LLMultisampleBuffer() -{ - release(); -} - -void LLMultisampleBuffer::release() -{ - if (mFBO) - { - glDeleteFramebuffers(1, (GLuint *) &mFBO); - mFBO = 0; - } - - if (mTex.size() > 0) - { - glDeleteRenderbuffers(mTex.size(), (GLuint *) &mTex[0]); - mTex.clear(); - } - - if (mDepth) - { - glDeleteRenderbuffers(1, (GLuint *) &mDepth); - mDepth = 0; - } -} - -void LLMultisampleBuffer::bindTarget() -{ - bindTarget(this); -} - -void LLMultisampleBuffer::bindTarget(LLRenderTarget* ref) -{ - if (!ref) - { - ref = this; - } - - glBindFramebuffer(GL_FRAMEBUFFER, mFBO); - if (gGLManager.mHasDrawBuffers) - { //setup multiple render targets - GLenum drawbuffers[] = {GL_COLOR_ATTACHMENT0, - GL_COLOR_ATTACHMENT1, - GL_COLOR_ATTACHMENT2, - GL_COLOR_ATTACHMENT3}; - glDrawBuffersARB(ref->mTex.size(), drawbuffers); - } - - check_framebuffer_status(); - - glViewport(0, 0, mResX, mResY); - - sBoundTarget = this; -} - -void LLMultisampleBuffer::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo ) -{ - allocate(resx,resy,color_fmt,depth,stencil,usage,use_fbo,2); -} - -void LLMultisampleBuffer::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo, U32 samples ) -{ - stop_glerror(); - mResX = resx; - mResY = resy; - - mUsage = usage; - mUseDepth = depth; - mStencil = stencil; - - release(); - - mSamples = samples; - - if (mSamples <= 1) - { - llerrs << "Cannot create a multisample buffer with less than 2 samples." << llendl; - } - - stop_glerror(); - - if ((sUseFBO || use_fbo) && gGLManager.mHasFramebufferObject) - { - - if (depth) - { - stop_glerror(); - allocateDepth(); - stop_glerror(); - } - - glGenFramebuffers(1, (GLuint *) &mFBO); - - glBindFramebuffer(GL_FRAMEBUFFER, mFBO); - - if (mDepth) - { - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mDepth); - if (mStencil) - { - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mDepth); - } - } - - stop_glerror(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - stop_glerror(); - } - - addColorAttachment(color_fmt); -} - -void LLMultisampleBuffer::addColorAttachment(U32 color_fmt) -{ - if (color_fmt == 0) - { - return; - } - - U32 offset = mTex.size(); - if (offset >= 4 || - (offset > 0 && (mFBO == 0 || !gGLManager.mHasDrawBuffers))) - { - llerrs << "Too many color attachments!" << llendl; - } - - U32 tex; - glGenRenderbuffers(1, &tex); - - glBindRenderbuffer(GL_RENDERBUFFER, tex); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, mSamples, color_fmt, mResX, mResY); - stop_glerror(); - - if (mFBO) - { - glBindFramebuffer(GL_FRAMEBUFFER, mFBO); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+offset, GL_RENDERBUFFER, tex); - stop_glerror(); - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - switch (status) - { - case GL_FRAMEBUFFER_COMPLETE: - break; - default: - llerrs << "WTF? " << std::hex << status << llendl; - break; - } - - glBindFramebuffer(GL_FRAMEBUFFER, 0); - } - - mTex.push_back(tex); -} - -void LLMultisampleBuffer::allocateDepth() -{ - glGenRenderbuffers(1, (GLuint* ) &mDepth); - glBindRenderbuffer(GL_RENDERBUFFER, mDepth); - if (mStencil) - { - glRenderbufferStorageMultisample(GL_RENDERBUFFER, mSamples, GL_DEPTH24_STENCIL8, mResX, mResY); - } - else - { - glRenderbufferStorageMultisample(GL_RENDERBUFFER, mSamples, GL_DEPTH_COMPONENT16, mResX, mResY); - } -} - diff --git a/indra/llrender/llrendertarget.h b/indra/llrender/llrendertarget.h index 12dd1c8b90..094b58b562 100644 --- a/indra/llrender/llrendertarget.h +++ b/indra/llrender/llrendertarget.h @@ -71,10 +71,7 @@ public: //allocate resources for rendering //must be called before use //multiple calls will release previously allocated resources - void allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage = LLTexUnit::TT_TEXTURE, bool use_fbo = FALSE); - - //provide this render target with a multisample resource. - void setSampleBuffer(LLMultisampleBuffer* buffer); + void allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage = LLTexUnit::TT_TEXTURE, bool use_fbo = false, S32 samples = 0); //add color buffer attachment //limit of 4 color attachments per render target @@ -141,7 +138,6 @@ public: static LLRenderTarget* getCurrentBoundTarget() { return sBoundTarget; } protected: - friend class LLMultisampleBuffer; U32 mResX; U32 mResY; std::vector<U32> mTex; @@ -152,26 +148,8 @@ protected: bool mRenderDepth; LLTexUnit::eTextureType mUsage; U32 mSamples; - LLMultisampleBuffer* mSampleBuffer; - - static LLRenderTarget* sBoundTarget; -}; - -class LLMultisampleBuffer : public LLRenderTarget -{ -public: - LLMultisampleBuffer(); - virtual ~LLMultisampleBuffer(); - - virtual void release(); - - virtual void bindTarget(); - void bindTarget(LLRenderTarget* ref); - virtual void allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo); - void allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, bool stencil, LLTexUnit::eTextureType usage, bool use_fbo, U32 samples); - virtual void addColorAttachment(U32 color_fmt); - virtual void allocateDepth(); + static LLRenderTarget* sBoundTarget; }; #endif //!LL_MESA_HEADLESS diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 98a0a93084..b8f9c60ca9 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -374,6 +374,12 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade GLcharARB* text[1024]; GLuint count = 0; + //copy preprocessor definitions into buffer + for (std::map<std::string,std::string>::iterator iter = mDefinitions.begin(); iter != mDefinitions.end(); ++iter) + { + std::string define = "#define " + iter->first + " " + iter->second + "\n"; + text[count++] = (GLcharARB *) strdup(define.c_str()); + } //copy file into memory while( fgets((char *)buff, 1024, file) != NULL && count < LL_ARRAY_SIZE(buff) ) diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index c54c4608d7..9ee11b98c8 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -60,6 +60,9 @@ public: std::vector<std::string> mReservedUniforms; + //preprocessor definitions (name/value) + std::map<std::string, std::string> mDefinitions; + protected: // our parameter manager singleton instance diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index cbf22b75e8..769dcf8457 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -77,6 +77,7 @@ include_directories( set(viewer_SOURCE_FILES groupchatlistener.cpp + llaccountingquotamanager.cpp llagent.cpp llagentaccess.cpp llagentcamera.cpp @@ -626,6 +627,7 @@ set(viewer_HEADER_FILES CMakeLists.txt ViewerInstall.cmake groupchatlistener.h + llaccountingquotamanager.h llagent.h llagentaccess.h llagentcamera.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 78db307d64..2c6d75e3c9 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7091,7 +7091,76 @@ <key>Value</key> <integer>1</integer> </map> - <key>RenderAnisotropic</key> + + <key>OctreeMaxNodeCapacity</key> + <map> + <key>Comment</key> + <string>Maximum number of elements to store in a single octree node</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>128</integer> + </map> + + <key>OctreeStaticObjectSizeFactor</key> + <map> + <key>Comment</key> + <string>Multiplier on static object size for determining octree node size </string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>1</integer> + </map> + + <key>OctreeAlphaDistanceFactor</key> + <map> + <key>Comment</key> + <string>Multiplier on alpha object distance for determining octree node size </string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Vector3</string> + <key>Value</key> + <array> + <real>0.05</real> + <real>0.0</real> + <real>0.0</real> + </array> + </map> + + <key>OctreeAttachmentSizeFactor</key> + <map> + <key>Comment</key> + <string>Multiplier on attachment size for determining octree node size </string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>4</integer> + </map> + + <key>OctreeDistanceFactor</key> + <map> + <key>Comment</key> + <string>Multiplier on distance for determining octree node size </string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Vector3</string> + <key>Value</key> + <array> + <real>0.05</real> + <real>0.0</real> + <real>0.0</real> + </array> + </map> + + <key>RenderAnisotropic</key> <map> <key>Comment</key> <string>Render textures using anisotropic filtering</string> @@ -7188,7 +7257,7 @@ <key>Type</key> <string>F32</string> <key>Value</key> - <integer>1.0</integer> + <real>1.0</real> </map> <key>RenderAvatarVP</key> <map> @@ -8589,7 +8658,7 @@ <key>Type</key> <string>S32</string> <key>Value</key> - <integer>8192</integer> + <integer>65536</integer> </map> <key>RenderMaxVBOSize</key> <map> diff --git a/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl index 5addbbb176..65d9209983 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl @@ -35,19 +35,24 @@ float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, floa //get distance float d = length(lv); - //normalize light vector - lv *= 1.0/d; + 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); - float da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0); + //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 + // spotlight coefficient. + float spot = max(dot(-ln, lv), is_pointlight); + da *= spot*spot; // GL_SPOT_EXPONENT=2 - //angular attenuation - da *= calcDirectionalLight(n, lv); + //angular attenuation + da *= calcDirectionalLight(n, lv); + } return da; } diff --git a/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl b/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl index 525b68c437..2691fc8ded 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl @@ -36,19 +36,24 @@ float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, floa //get distance float d = length(lv); - //normalize light vector - lv *= 1.0/d; + 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); - float da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0); + //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 + // spotlight coefficient. + float spot = max(dot(-ln, lv), is_pointlight); + da *= spot*spot; // GL_SPOT_EXPONENT=2 - //angular attenuation - da *= calcDirectionalLight(n, lv); + //angular attenuation + da *= calcDirectionalLight(n, lv); + } return da; } diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl index a2a7dea20d..a012cb5030 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl @@ -35,19 +35,24 @@ float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, floa //get distance float d = length(lv); - //normalize light vector - lv *= 1.0/d; + 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); - float da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0); + //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 + // spotlight coefficient. + float spot = max(dot(-ln, lv), is_pointlight); + da *= spot*spot; // GL_SPOT_EXPONENT=2 - //angular attenuation - da *= calcDirectionalLight(n, lv); + //angular attenuation + da *= calcDirectionalLight(n, lv); + } return da; } diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl index 1b7ae06888..3d0884bbe8 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl @@ -9,10 +9,13 @@ uniform sampler2D diffuseMap; +varying vec4 post_pos; 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); + + gl_FragDepth = max(post_pos.z/post_pos.w*0.5+0.5, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarShadowV.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarShadowV.glsl index cf6579a40d..3460f40657 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/avatarShadowV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/avatarShadowV.glsl @@ -11,6 +11,8 @@ mat4 getSkinnedTransform(); attribute vec4 weight; +varying vec4 post_pos; + void main() { gl_TexCoord[0] = gl_MultiTexCoord0; @@ -30,8 +32,9 @@ void main() norm = normalize(norm); pos = gl_ProjectionMatrix * pos; - pos.z = max(pos.z, -pos.w+0.01); - gl_Position = pos; + post_pos = pos; + + gl_Position = vec4(pos.x, pos.y, pos.w*0.5, pos.w); gl_FrontColor = gl_Color; } diff --git a/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl index d9f021b114..98d034c2b5 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl @@ -26,7 +26,7 @@ uniform vec2 screen_res; vec4 getPosition(vec2 pos_screen) { - float depth = texture2DRect(depthMap, pos_screen.xy).a; + float depth = texture2DRect(depthMap, pos_screen.xy).r; vec2 sc = pos_screen.xy*2.0; sc /= screen_res; sc -= vec2(1.0,1.0); @@ -39,7 +39,7 @@ vec4 getPosition(vec2 pos_screen) void main() { - vec2 tc = vary_fragcoord.xy; + vec2 tc = vary_fragcoord.xy; vec3 norm = texture2DRect(normalMap, tc).xyz; norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm vec3 pos = getPosition(tc).xyz; diff --git a/indra/newview/app_settings/shaders/class1/deferred/blurLightMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/blurLightMSF.glsl new file mode 100644 index 0000000000..edec83ea07 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/blurLightMSF.glsl @@ -0,0 +1,113 @@ +/** + * @file blurLightF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +#extension GL_ARB_texture_rectangle : enable +#extension GL_ARB_texture_multisample : enable + +uniform sampler2DMS depthMap; +uniform sampler2DMS normalMap; +uniform sampler2DRect lightMap; + +uniform float dist_factor; +uniform float blur_size; +uniform vec2 delta; +uniform vec3 kern[4]; +uniform float kern_scale; + +varying vec2 vary_fragcoord; + +uniform mat4 inv_proj; +uniform vec2 screen_res; + +vec3 texture2DMS3(sampler2DMS tex, ivec2 tc) +{ + vec3 ret = vec3(0,0,0); + for (int i = 0; i < samples; i++) + { + ret += texelFetch(tex, tc, i).rgb; + } + + return ret/samples; +} + +float texture2DMS1(sampler2DMS tex, ivec2 tc) +{ + float ret = 0; + for (int i = 0; i < samples; i++) + { + ret += texelFetch(tex, tc, i).r; + } + + return ret/samples; +} + +vec4 getPosition(ivec2 pos_screen) +{ + float depth = texture2DMS1(depthMap, pos_screen.xy); + 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; +} + +void main() +{ + vec2 tc = vary_fragcoord.xy; + ivec2 itc = ivec2(tc); + + vec3 norm = texture2DMS3(normalMap, itc).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + vec3 pos = getPosition(itc).xyz; + vec4 ccol = texture2DRect(lightMap, tc).rgba; + + vec2 dlt = kern_scale * delta / (1.0+norm.xy*norm.xy); + dlt /= max(-pos.z*dist_factor, 1.0); + + vec2 defined_weight = kern[0].xy; // special case the first (centre) sample's weight in the blur; we have to sample it anyway so we get it for 'free' + vec4 col = defined_weight.xyxx * ccol; + + // relax tolerance according to distance to avoid speckling artifacts, as angles and distances are a lot more abrupt within a small screen area at larger distances + float pointplanedist_tolerance_pow2 = pos.z*pos.z*0.00005; + + // perturb sampling origin slightly in screen-space to hide edge-ghosting artifacts where smoothing radius is quite large + tc += ( (mod(tc.x+tc.y,2) - 0.5) * kern[1].z * dlt * 0.5 ); + + for (int i = 1; i < 4; i++) + { + vec2 samptc = tc + kern[i].z*dlt; + vec3 samppos = getPosition(ivec2(samptc)).xyz; + float d = dot(norm.xyz, samppos.xyz-pos.xyz);// dist from plane + if (d*d <= pointplanedist_tolerance_pow2) + { + col += texture2DRect(lightMap, samptc)*kern[i].xyxx; + defined_weight += kern[i].xy; + } + } + for (int i = 1; i < 4; i++) + { + vec2 samptc = vec2(tc - kern[i].z*dlt); + vec3 samppos = getPosition(ivec2(samptc)).xyz; + float d = dot(norm.xyz, samppos.xyz-pos.xyz);// dist from plane + if (d*d <= pointplanedist_tolerance_pow2) + { + col += texture2DRect(lightMap, samptc)*kern[i].xyxx; + defined_weight += kern[i].xy; + } + } + + col /= defined_weight.xyxx; + col.y *= col.y; + + gl_FragColor = col; +} + diff --git a/indra/newview/app_settings/shaders/class1/deferred/cloudsF.glsl b/indra/newview/app_settings/shaders/class1/deferred/cloudsF.glsl new file mode 100644 index 0000000000..9e551fa976 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/cloudsF.glsl @@ -0,0 +1,79 @@ +/** + * @file WLCloudsF.glsl + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +///////////////////////////////////////////////////////////////////////// +// The fragment shader for the sky +///////////////////////////////////////////////////////////////////////// + +varying vec4 vary_CloudColorSun; +varying vec4 vary_CloudColorAmbient; +varying float vary_CloudDensity; + +uniform sampler2D cloud_noise_texture; +uniform vec4 cloud_pos_density1; +uniform vec4 cloud_pos_density2; +uniform vec4 gamma; + +/// Soft clips the light with a gamma correction +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() +{ + // Set variables + vec2 uv1 = gl_TexCoord[0].xy; + vec2 uv2 = gl_TexCoord[1].xy; + + vec4 cloudColorSun = vary_CloudColorSun; + vec4 cloudColorAmbient = vary_CloudColorAmbient; + float cloudDensity = vary_CloudDensity; + vec2 uv3 = gl_TexCoord[2].xy; + vec2 uv4 = gl_TexCoord[3].xy; + + // Offset texture coords + uv1 += cloud_pos_density1.xy; //large texture, visible density + uv2 += cloud_pos_density1.xy; //large texture, self shadow + uv3 += cloud_pos_density2.xy; //small texture, visible density + uv4 += cloud_pos_density2.xy; //small texture, self shadow + + + // Compute alpha1, the main cloud opacity + float alpha1 = (texture2D(cloud_noise_texture, uv1).x - 0.5) + (texture2D(cloud_noise_texture, uv3).x - 0.5) * cloud_pos_density2.z; + alpha1 = min(max(alpha1 + cloudDensity, 0.) * 10. * cloud_pos_density1.z, 1.); + + // And smooth + alpha1 = 1. - alpha1 * alpha1; + alpha1 = 1. - alpha1 * alpha1; + + + // Compute alpha2, for self shadowing effect + // (1 - alpha2) will later be used as percentage of incoming sunlight + float alpha2 = (texture2D(cloud_noise_texture, uv2).x - 0.5); + alpha2 = min(max(alpha2 + cloudDensity, 0.) * 2.5 * cloud_pos_density1.z, 1.); + + // And smooth + alpha2 = 1. - alpha2; + alpha2 = 1. - alpha2 * alpha2; + + // Combine + vec4 color; + color = (cloudColorSun*(1.-alpha2) + cloudColorAmbient); + color *= 2.; + + /// Gamma correct for WL (soft clip effect). + gl_FragData[0] = vec4(scaleSoftClip(color.rgb), alpha1); + gl_FragData[1] = vec4(0.0,0.0,0.0,0.0); + gl_FragData[2] = vec4(0,0,1,0); +} + diff --git a/indra/newview/app_settings/shaders/class1/deferred/cloudsV.glsl b/indra/newview/app_settings/shaders/class1/deferred/cloudsV.glsl new file mode 100644 index 0000000000..267ef36d4d --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/cloudsV.glsl @@ -0,0 +1,165 @@ +/** + * @file WLCloudsV.glsl + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +////////////////////////////////////////////////////////////////////////// +// The vertex shader for creating the atmospheric sky +/////////////////////////////////////////////////////////////////////////////// + +// Output parameters +varying vec4 vary_CloudColorSun; +varying vec4 vary_CloudColorAmbient; +varying float vary_CloudDensity; + +// Inputs +uniform vec3 camPosLocal; + +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 max_y; + +uniform vec4 glow; + +uniform vec4 cloud_color; + +uniform vec4 cloud_scale; + +void main() +{ + + // World / view / projection + gl_Position = ftransform(); + + gl_TexCoord[0] = gl_MultiTexCoord0; + + // Get relative position + vec3 P = gl_Vertex.xyz - camPosLocal.xyz + vec3(0,50,0); + + // Set altitude + if (P.y > 0.) + { + P *= (max_y.x / P.y); + } + else + { + P *= (-32000. / P.y); + } + + // Can normalize then + vec3 Pn = normalize(P); + float Plen = length(P); + + // Initialize temp variables + vec4 temp1 = vec4(0.); + vec4 temp2 = vec4(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 + haze_density.x * 0.25) * (density_multiplier.x * max_y.x); + + // Calculate relative weights + temp1 = blue_density + haze_density.x; + blue_weight = blue_density / temp1; + haze_weight = haze_density.x / temp1; + + // Compute sunlight from P & lightnorm (for long rays like sky) + temp2.y = max(0., max(0., Pn.y) * 1.0 + lightnorm.y ); + temp2.y = 1. / temp2.y; + sunlight *= exp( - light_atten * temp2.y); + + // Distance + temp2.z = Plen * density_multiplier.x; + + // Transparency (-> temp1) + // ATI Bugfix -- can't store temp1*temp2.z in a variable because the ati + // compiler gets confused. + temp1 = exp(-temp1 * temp2.z); + + + // Compute haze glow + temp2.x = dot(Pn, lightnorm.xyz); + temp2.x = 1. - temp2.x; + // temp2.x is 0 at the sun and increases away from sun + temp2.x = max(temp2.x, .001); + // 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; + tmpAmbient += (1. - tmpAmbient) * cloud_shadow.x * 0.5; + + // Dim sunlight by cloud shadow percentage + sunlight *= (1. - cloud_shadow.x); + + // Haze color below cloud + vec4 additiveColorBelowCloud = ( blue_horizon * blue_weight * (sunlight + tmpAmbient) + + (haze_horizon.r * haze_weight) * (sunlight * temp2.x + tmpAmbient) + ); + + // CLOUDS + + sunlight = sunlight_color; + temp2.y = max(0., lightnorm.y * 2.); + temp2.y = 1. / temp2.y; + sunlight *= exp( - light_atten * temp2.y); + + // Cloud color out + vary_CloudColorSun = (sunlight * temp2.x) * cloud_color; + vary_CloudColorAmbient = tmpAmbient * cloud_color; + + // Attenuate cloud color by atmosphere + temp1 = sqrt(temp1); //less atmos opacity (more transparency) below clouds + vary_CloudColorSun *= temp1; + vary_CloudColorAmbient *= temp1; + vec4 oHazeColorBelowCloud = additiveColorBelowCloud * (1. - temp1); + + // Make a nice cloud density based on the cloud_shadow value that was passed in. + vary_CloudDensity = 2. * (cloud_shadow.x - 0.25); + + + // Texture coords + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[0].xy -= 0.5; + gl_TexCoord[0].xy /= cloud_scale.x; + gl_TexCoord[0].xy += 0.5; + + gl_TexCoord[1] = gl_TexCoord[0]; + gl_TexCoord[1].x += lightnorm.x * 0.0125; + gl_TexCoord[1].y += lightnorm.z * 0.0125; + + gl_TexCoord[2] = gl_TexCoord[0] * 16.; + gl_TexCoord[3] = gl_TexCoord[1] * 16.; + + // Combine these to minimize register use + vary_CloudColorAmbient += oHazeColorBelowCloud; + + // needs this to compile on mac + //vary_AtmosAttenuation = vec3(0.0,0.0,0.0); + + // END CLOUDS +} + diff --git a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl index c5ddf31ac0..e10fb105e8 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl @@ -23,8 +23,9 @@ uniform float sun_wash; uniform int light_count; -uniform vec4 light[16]; -uniform vec4 light_col[16]; +#define MAX_LIGHT_COUNT 16 +uniform vec4 light[MAX_LIGHT_COUNT]; +uniform vec4 light_col[MAX_LIGHT_COUNT]; varying vec4 vary_fragcoord; uniform vec2 screen_res; @@ -35,7 +36,7 @@ uniform mat4 inv_proj; vec4 getPosition(vec2 pos_screen) { - float depth = texture2DRect(depthMap, pos_screen.xy).a; + float depth = texture2DRect(depthMap, pos_screen.xy).r; vec2 sc = pos_screen.xy*2.0; sc /= screen_res; sc -= vec2(1.0,1.0); @@ -63,50 +64,56 @@ void main() float noise = texture2D(noiseMap, frag.xy/128.0).b; vec3 out_col = vec3(0,0,0); vec3 npos = normalize(-pos); - - for (int i = 0; i < light_count; ++i) + + // As of OSX 10.6.7 ATI Apple's crash when using a variable size loop + for (int i = 0; i < MAX_LIGHT_COUNT; ++i) { + bool light_contrib = (i < light_count); + vec3 lv = light[i].xyz-pos; float dist2 = dot(lv,lv); dist2 /= light[i].w; if (dist2 > 1.0) { - continue; + light_contrib = false; } float da = dot(norm, lv); if (da < 0.0) { - continue; + light_contrib = false; } - - lv = normalize(lv); - da = dot(norm, lv); - - float fa = light_col[i].a+1.0; - float dist_atten = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0); - dist_atten *= noise; - - float lit = da * dist_atten; - vec3 col = light_col[i].rgb*lit*diff; - //vec3 col = vec3(dist2, light_col[i].a, lit); - - if (spec.a > 0.0) + if (light_contrib) { - //vec3 ref = dot(pos+lv, norm); + lv = normalize(lv); + da = dot(norm, lv); + + float fa = light_col[i].a+1.0; + float dist_atten = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0); + dist_atten *= noise; + + float lit = da * dist_atten; - float sa = dot(normalize(lv+npos),norm); + vec3 col = light_col[i].rgb*lit*diff; + //vec3 col = vec3(dist2, light_col[i].a, lit); - if (sa > 0.0) + if (spec.a > 0.0) { - sa = texture2D(lightFunc,vec2(sa, spec.a)).a * min(dist_atten*4.0, 1.0); - sa *= noise; - col += da*sa*light_col[i].rgb*spec.rgb; + //vec3 ref = dot(pos+lv, norm); + + float sa = dot(normalize(lv+npos),norm); + + if (sa > 0.0) + { + sa = texture2D(lightFunc,vec2(sa, spec.a)).a * min(dist_atten*4.0, 1.0); + sa *= noise; + col += da*sa*light_col[i].rgb*spec.rgb; + } } + + out_col += col; } - - out_col += col; } if (dot(out_col, out_col) <= 0.0) diff --git a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightMSF.glsl new file mode 100644 index 0000000000..66c7a5cb4a --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightMSF.glsl @@ -0,0 +1,137 @@ +/** + * @file multiPointLightF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +#extension GL_ARB_texture_rectangle : enable +#extension GL_ARB_texture_multisample : enable + +uniform sampler2DMS depthMap; +uniform sampler2DMS diffuseRect; +uniform sampler2DMS specularRect; +uniform sampler2DMS normalMap; +uniform sampler2D noiseMap; +uniform sampler2D lightFunc; + + +uniform vec3 env_mat[3]; +uniform float sun_wash; + +uniform int light_count; + +#define MAX_LIGHT_COUNT 16 +uniform vec4 light[MAX_LIGHT_COUNT]; +uniform vec4 light_col[MAX_LIGHT_COUNT]; + +varying vec4 vary_fragcoord; +uniform vec2 screen_res; + +uniform float far_z; + +uniform mat4 inv_proj; + +vec4 getPosition(ivec2 pos_screen, int sample) +{ + float depth = texelFetch(depthMap, pos_screen, sample).r; + vec2 sc = vec2(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; +} + +void main() +{ + vec2 frag = (vary_fragcoord.xy*0.5+0.5)*screen_res; + ivec2 itc = ivec2(frag); + + int wght = 0; + vec3 fcol = vec3(0,0,0); + + for (int s = 0; s < samples; ++s) + { + vec3 pos = getPosition(itc, s).xyz; + if (pos.z >= far_z) + { + vec3 norm = texelFetch(normalMap, itc, s).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + norm = normalize(norm); + vec4 spec = texelFetch(specularRect, itc, s); + vec3 diff = texelFetch(diffuseRect, itc, s).rgb; + float noise = texture2D(noiseMap, frag.xy/128.0).b; + vec3 out_col = vec3(0,0,0); + vec3 npos = normalize(-pos); + + // As of OSX 10.6.7 ATI Apple's crash when using a variable size loop + for (int i = 0; i < MAX_LIGHT_COUNT; ++i) + { + bool light_contrib = (i < light_count); + + vec3 lv = light[i].xyz-pos; + float dist2 = dot(lv,lv); + dist2 /= light[i].w; + if (dist2 > 1.0) + { + light_contrib = false; + } + + float da = dot(norm, lv); + if (da < 0.0) + { + light_contrib = false; + } + + if (light_contrib) + { + lv = normalize(lv); + da = dot(norm, lv); + + float fa = light_col[i].a+1.0; + float dist_atten = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0); + dist_atten *= noise; + + float lit = da * dist_atten; + + vec3 col = light_col[i].rgb*lit*diff; + //vec3 col = vec3(dist2, light_col[i].a, lit); + + if (spec.a > 0.0) + { + //vec3 ref = dot(pos+lv, norm); + + float sa = dot(normalize(lv+npos),norm); + + if (sa > 0.0) + { + sa = texture2D(lightFunc,vec2(sa, spec.a)).a * min(dist_atten*4.0, 1.0); + sa *= noise; + col += da*sa*light_col[i].rgb*spec.rgb; + } + } + + out_col += col; + } + } + + fcol += out_col; + ++wght; + } + } + + if (wght <= 0) + { + discard; + } + + gl_FragColor.rgb = fcol/samples; + gl_FragColor.a = 0.0; + + +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/multiSpotLightMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/multiSpotLightMSF.glsl new file mode 100644 index 0000000000..b450ff1ca2 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/multiSpotLightMSF.glsl @@ -0,0 +1,232 @@ +/** + * @file multiSpotLightF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +//class 1 -- no shadows + +#extension GL_ARB_texture_rectangle : enable +#extension GL_ARB_texture_multisample : enable + +uniform sampler2DMS diffuseRect; +uniform sampler2DMS specularRect; +uniform sampler2DMS depthMap; +uniform sampler2DMS normalMap; +uniform sampler2D noiseMap; +uniform sampler2D lightFunc; +uniform sampler2D projectionMap; + +uniform mat4 proj_mat; //screen space to light space +uniform float proj_near; //near clip for projection +uniform vec3 proj_p; //plane projection is emitting from (in screen space) +uniform vec3 proj_n; +uniform float proj_focus; //distance from plane to begin blurring +uniform float proj_lod; //(number of mips in proj map) +uniform float proj_range; //range between near clip and far clip plane of projection +uniform float proj_ambient_lod; +uniform float proj_ambiance; +uniform float near_clip; +uniform float far_clip; + +uniform vec3 proj_origin; //origin of projection to be used for angular attenuation +uniform float sun_wash; +uniform float shadow_fade; + +varying vec4 vary_light; + +varying vec4 vary_fragcoord; +uniform vec2 screen_res; + +uniform mat4 inv_proj; + +vec4 texture2DLodSpecular(sampler2D projectionMap, vec2 tc, float lod) +{ + vec4 ret = texture2DLod(projectionMap, tc, lod); + + vec2 dist = tc-vec2(0.5); + + float det = max(1.0-lod/(proj_lod*0.5), 0.0); + + float d = dot(dist,dist); + + ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0)+det, 1.0); + + return ret; +} + +vec4 texture2DLodDiffuse(sampler2D projectionMap, vec2 tc, float lod) +{ + vec4 ret = texture2DLod(projectionMap, tc, lod); + + vec2 dist = vec2(0.5) - abs(tc-vec2(0.5)); + + float det = min(lod/(proj_lod*0.5), 1.0); + + float d = min(dist.x, dist.y); + + float edge = 0.25*det; + + ret *= clamp(d/edge, 0.0, 1.0); + + return ret; +} + +vec4 texture2DLodAmbient(sampler2D projectionMap, vec2 tc, float lod) +{ + vec4 ret = texture2DLod(projectionMap, tc, lod); + + vec2 dist = tc-vec2(0.5); + + float d = dot(dist,dist); + + ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0), 1.0); + + return ret; +} + + +vec4 getPosition(ivec2 pos_screen, int sample) +{ + float depth = texelFetch(depthMap, pos_screen, sample).r; + vec2 sc = vec2(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; +} + +void main() +{ + int wght = 0; + + vec3 fcol = vec3(0,0,0); + + vec2 frag = (vary_fragcoord.xy*0.5+0.5)*screen_res; + + ivec2 itc = ivec2(frag.xy); + + for (int i = 0; i < samples; ++i) + { + vec3 pos = getPosition(itc, i).xyz; + vec3 lv = vary_light.xyz-pos.xyz; + float dist2 = dot(lv,lv); + dist2 /= vary_light.w; + if (dist2 <= 1.0) + { + vec3 norm = texelFetch(normalMap, itc, i).xyz*2.0-1.0; + + norm = normalize(norm); + float l_dist = -dot(lv, proj_n); + + vec4 proj_tc = (proj_mat * vec4(pos.xyz, 1.0)); + if (proj_tc.z >= 0.0) + { + proj_tc.xyz /= proj_tc.w; + + float fa = gl_Color.a+1.0; + float dist_atten = min(1.0-(dist2-1.0*(1.0-fa))/fa, 1.0); + if (dist_atten > 0.0) + { + lv = proj_origin-pos.xyz; + lv = normalize(lv); + float da = dot(norm, lv); + + vec3 col = vec3(0,0,0); + + vec3 diff_tex = texelFetch(diffuseRect, itc, i).rgb; + + float noise = texture2D(noiseMap, frag.xy/128.0).b; + if (proj_tc.z > 0.0 && + proj_tc.x < 1.0 && + proj_tc.y < 1.0 && + proj_tc.x > 0.0 && + proj_tc.y > 0.0) + { + float lit = 0.0; + float amb_da = proj_ambiance; + + if (da > 0.0) + { + float diff = clamp((l_dist-proj_focus)/proj_range, 0.0, 1.0); + float lod = diff * proj_lod; + + vec4 plcol = texture2DLodDiffuse(projectionMap, proj_tc.xy, lod); + + vec3 lcol = gl_Color.rgb * plcol.rgb * plcol.a; + + lit = da * dist_atten * noise; + + col = lcol*lit*diff_tex; + amb_da += (da*0.5)*proj_ambiance; + } + + //float diff = clamp((proj_range-proj_focus)/proj_range, 0.0, 1.0); + vec4 amb_plcol = texture2DLodAmbient(projectionMap, proj_tc.xy, proj_lod); + + amb_da += (da*da*0.5+0.5)*proj_ambiance; + + amb_da *= dist_atten * noise; + + amb_da = min(amb_da, 1.0-lit); + + col += amb_da*gl_Color.rgb*diff_tex.rgb*amb_plcol.rgb*amb_plcol.a; + } + + + vec4 spec = texelFetch(specularRect, itc, i); + if (spec.a > 0.0) + { + vec3 ref = reflect(normalize(pos), norm); + + //project from point pos in direction ref to plane proj_p, proj_n + vec3 pdelta = proj_p-pos; + float ds = dot(ref, proj_n); + + if (ds < 0.0) + { + vec3 pfinal = pos + ref * dot(pdelta, proj_n)/ds; + + vec4 stc = (proj_mat * vec4(pfinal.xyz, 1.0)); + + if (stc.z > 0.0) + { + stc.xy /= stc.w; + + float fatten = clamp(spec.a*spec.a+spec.a*0.5, 0.25, 1.0); + + stc.xy = (stc.xy - vec2(0.5)) * fatten + vec2(0.5); + + if (stc.x < 1.0 && + stc.y < 1.0 && + stc.x > 0.0 && + stc.y > 0.0) + { + vec4 scol = texture2DLodSpecular(projectionMap, stc.xy, proj_lod-spec.a*proj_lod); + col += dist_atten*scol.rgb*gl_Color.rgb*scol.a*spec.rgb; + } + } + } + } + + fcol += col; + ++wght; + } + } + } + } + + if (wght <= 0) + { + discard; + } + + gl_FragColor.rgb = fcol/samples; + gl_FragColor.a = 0.0; +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl index 22ed9dcd40..0d771109fb 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl @@ -30,7 +30,7 @@ uniform vec4 viewport; vec4 getPosition(vec2 pos_screen) { - float depth = texture2DRect(depthMap, pos_screen.xy).a; + float depth = texture2DRect(depthMap, pos_screen.xy).r; vec2 sc = (pos_screen.xy-viewport.xy)*2.0; sc /= viewport.zw; sc -= vec2(1.0,1.0); diff --git a/indra/newview/app_settings/shaders/class1/deferred/pointLightMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pointLightMSF.glsl new file mode 100644 index 0000000000..7521c3310c --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/pointLightMSF.glsl @@ -0,0 +1,108 @@ +/** + * @file pointLightF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $/LicenseInfo$ + */ + + #version 120 + +#extension GL_ARB_texture_rectangle : enable +#extension GL_ARB_texture_multisample : enable + +uniform sampler2DMS depthMap; +uniform sampler2DMS diffuseRect; +uniform sampler2DMS specularRect; +uniform sampler2DMS normalMap; +uniform sampler2D noiseMap; +uniform sampler2D lightFunc; + + +uniform vec3 env_mat[3]; +uniform float sun_wash; + +varying vec4 vary_light; + +varying vec4 vary_fragcoord; +uniform vec2 screen_res; + +uniform mat4 inv_proj; +uniform vec4 viewport; + +vec4 getPosition(ivec2 pos_screen, int sample) +{ + float depth = texelFetch(depthMap, pos_screen, sample).r; + vec2 sc = (vec2(pos_screen.xy)-viewport.xy)*2.0; + sc /= viewport.zw; + 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; +} + +void main() +{ + vec4 frag = vary_fragcoord; + frag.xyz /= frag.w; + frag.xyz = frag.xyz*0.5+0.5; + frag.xy *= screen_res; + + ivec2 itc = ivec2(frag.xy); + + int wght = 0; + vec3 fcol = vec3(0,0,0); + + for (int s = 0; s < samples; ++s) + { + vec3 pos = getPosition(itc, s).xyz; + vec3 lv = vary_light.xyz-pos; + float dist2 = dot(lv,lv); + dist2 /= vary_light.w; + if (dist2 <= 1.0) + { + vec3 norm = texelFetch(normalMap, itc, s).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + float da = dot(norm, lv); + if (da >= 0.0) + { + norm = normalize(norm); + lv = normalize(lv); + da = dot(norm, lv); + + float noise = texture2D(noiseMap, frag.xy/128.0).b; + + vec3 col = texelFetch(diffuseRect, itc, s).rgb; + float fa = gl_Color.a+1.0; + float dist_atten = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0); + float lit = da * dist_atten * noise; + + col = gl_Color.rgb*lit*col; + + vec4 spec = texelFetch(specularRect, itc, s); + if (spec.a > 0.0) + { + float sa = dot(normalize(lv-normalize(pos)),norm); + if (sa > 0.0) + { + sa = texture2D(lightFunc, vec2(sa, spec.a)).a * min(dist_atten*4.0, 1.0); + sa *= noise; + col += da*sa*gl_Color.rgb*spec.rgb; + } + } + + fcol += col; + ++wght; + } + } + } + + if (wght <= 0) + { + discard; + } + + gl_FragColor.rgb = fcol/samples; + gl_FragColor.a = 0.0; +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/pointLightV.glsl b/indra/newview/app_settings/shaders/class1/deferred/pointLightV.glsl index 8e74feb615..2377947e7f 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/pointLightV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/pointLightV.glsl @@ -10,14 +10,9 @@ varying vec4 vary_light; varying vec4 vary_fragcoord; -uniform vec2 screen_res; -uniform float near_clip; - void main() { //transform vertex - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; - vec4 pos = gl_ModelViewProjectionMatrix * gl_Vertex; vary_fragcoord = pos; @@ -25,6 +20,8 @@ void main() tex.w = 1.0; vary_light = gl_MultiTexCoord0; + + gl_Position = pos; gl_FrontColor = gl_Color; } diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl index 77f1b2224c..d389add03b 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredF.glsl @@ -29,7 +29,7 @@ varying vec2 vary_fragcoord; float getDepth(vec2 pos_screen) { - float z = texture2DRect(depthMap, pos_screen.xy).a; + float z = texture2DRect(depthMap, pos_screen.xy).r; z = z*2.0-1.0; vec4 ndc = vec4(0.0, 0.0, z, 1.0); vec4 p = inv_proj*ndc; diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredMSF.glsl new file mode 100644 index 0000000000..711c6dc96d --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredMSF.glsl @@ -0,0 +1,133 @@ +/** + * @file postDeferredF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +#extension GL_ARB_texture_rectangle : enable +#extension GL_ARB_texture_multisample : enable + +uniform sampler2DMS diffuseRect; +uniform sampler2DMS edgeMap; +uniform sampler2DMS depthMap; +uniform sampler2DMS normalMap; +uniform sampler2D bloomMap; + +uniform float depth_cutoff; +uniform float norm_cutoff; +uniform float focal_distance; +uniform float blur_constant; +uniform float tan_pixel_angle; +uniform float magnification; + +uniform mat4 inv_proj; +uniform vec2 screen_res; + +varying vec2 vary_fragcoord; + +vec4 texture2DMS(sampler2DMS tex, ivec2 tc) +{ + vec4 ret = vec4(0,0,0,0); + for (int i = 0; i < samples; ++i) + { + ret += texelFetch(tex, tc, i); + } + + return ret/samples; +} + +float getDepth(ivec2 pos_screen) +{ + float z = texture2DMS(depthMap, pos_screen.xy).r; + z = z*2.0-1.0; + vec4 ndc = vec4(0.0, 0.0, z, 1.0); + vec4 p = inv_proj*ndc; + return p.z/p.w; +} + +float calc_cof(float depth) +{ + float sc = abs(depth-focal_distance)/-depth*blur_constant; + + sc /= magnification; + + // tan_pixel_angle = pixel_length/-depth; + float pixel_length = tan_pixel_angle*-focal_distance; + + sc = sc/pixel_length; + sc *= 1.414; + + return sc; +} + +void dofSample(inout vec4 diff, inout float w, float min_sc, float cur_depth, ivec2 tc) +{ + float d = getDepth(tc); + + float sc = calc_cof(d); + + if (sc > min_sc //sampled pixel is more "out of focus" than current sample radius + || d < cur_depth) //sampled pixel is further away than current pixel + { + float wg = 0.25; + + vec4 s = texture2DMS(diffuseRect, tc); + // de-weight dull areas to make highlights 'pop' + wg += s.r+s.g+s.b; + + diff += wg*s; + + w += wg; + } +} + + +void main() +{ + ivec2 itc = ivec2(vary_fragcoord.xy); + + vec3 norm = texture2DMS(normalMap, itc).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + + float depth = getDepth(itc); + + vec4 diff = texture2DMS(diffuseRect, itc); + + { + float w = 1.0; + + float sc = calc_cof(depth); + sc = min(abs(sc), 10.0); + + float fd = depth*0.5f; + + float PI = 3.14159265358979323846264; + + int isc = int(sc); + + // sample quite uniformly spaced points within a circle, for a circular 'bokeh' + //if (depth < focal_distance) + { + for (int x = -isc; x <= isc; x+=2) + { + for (int y = -isc; y <= isc; y+=2) + { + ivec2 cur_samp = ivec2(x,y); + float cur_sc = length(vec2(cur_samp)); + if (cur_sc < sc) + { + dofSample(diff, w, cur_sc, depth, itc+cur_samp); + } + } + } + } + + diff /= w; + } + + vec4 bloom = texture2D(bloomMap, vary_fragcoord.xy/screen_res); + gl_FragColor = diff + bloom; +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredNoDoFMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredNoDoFMSF.glsl new file mode 100644 index 0000000000..359b26b7d4 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredNoDoFMSF.glsl @@ -0,0 +1,37 @@ +/** + * @file postDeferredF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +#extension GL_ARB_texture_rectangle : enable +#extension GL_ARB_texture_multisample : enable + +uniform sampler2DMS diffuseRect; +uniform sampler2D bloomMap; + +uniform vec2 screen_res; +varying vec2 vary_fragcoord; + +vec4 texture2DMS(sampler2DMS tex, ivec2 tc) +{ + vec4 ret = vec4(0,0,0,0); + + for (int i = 0; i < samples; ++i) + { + ret += texelFetch(tex,tc,i); + } + + return ret/samples; +} + +void main() +{ + vec4 diff = texture2DMS(diffuseRect, ivec2(vary_fragcoord.xy)); + + vec4 bloom = texture2D(bloomMap, vary_fragcoord.xy/screen_res); + gl_FragColor = diff + bloom; +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl b/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl new file mode 100644 index 0000000000..91143943b6 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl @@ -0,0 +1,44 @@ +/** + * @file WLSkyF.glsl + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +///////////////////////////////////////////////////////////////////////// +// The fragment shader for the sky +///////////////////////////////////////////////////////////////////////// + +varying vec4 vary_HazeColor; + +uniform sampler2D cloud_noise_texture; +uniform vec4 gamma; + +/// Soft clips the light with a gamma correction +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() +{ + // Potential Fill-rate optimization. Add cloud calculation + // back in and output alpha of 0 (so that alpha culling kills + // the fragment) if the sky wouldn't show up because the clouds + // are fully opaque. + + vec4 color; + color = vary_HazeColor; + color *= 2.; + + /// Gamma correct for WL (soft clip effect). + gl_FragData[0] = vec4(scaleSoftClip(color.rgb), 1.0); + gl_FragData[1] = vec4(0.0,0.0,0.0,0.0); + gl_FragData[2] = vec4(0,0,1,0); +} + diff --git a/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl b/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl new file mode 100644 index 0000000000..03bca8f27e --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl @@ -0,0 +1,140 @@ +/** + * @file WLSkyV.glsl + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +// SKY //////////////////////////////////////////////////////////////////////// +// The vertex shader for creating the atmospheric sky +/////////////////////////////////////////////////////////////////////////////// + +// Output parameters +varying vec4 vary_HazeColor; + +// Inputs +uniform vec3 camPosLocal; + +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 max_y; + +uniform vec4 glow; + +uniform vec4 cloud_color; + +uniform vec4 cloud_scale; + +void main() +{ + + // World / view / projection + gl_Position = ftransform(); + gl_TexCoord[0] = gl_MultiTexCoord0; + + // Get relative position + vec3 P = gl_Vertex.xyz - camPosLocal.xyz + vec3(0,50,0); + //vec3 P = gl_Vertex.xyz + vec3(0,50,0); + + // Set altitude + if (P.y > 0.) + { + P *= (max_y.x / P.y); + } + else + { + P *= (-32000. / P.y); + } + + // Can normalize then + vec3 Pn = normalize(P); + float Plen = length(P); + + // Initialize temp variables + vec4 temp1 = vec4(0.); + vec4 temp2 = vec4(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 + haze_density.x * 0.25) * (density_multiplier.x * max_y.x); + + // Calculate relative weights + temp1 = blue_density + haze_density.x; + blue_weight = blue_density / temp1; + haze_weight = haze_density.x / temp1; + + // Compute sunlight from P & lightnorm (for long rays like sky) + temp2.y = max(0., max(0., Pn.y) * 1.0 + lightnorm.y ); + temp2.y = 1. / temp2.y; + sunlight *= exp( - light_atten * temp2.y); + + // Distance + temp2.z = Plen * density_multiplier.x; + + // Transparency (-> temp1) + // ATI Bugfix -- can't store temp1*temp2.z in a variable because the ati + // compiler gets confused. + temp1 = exp(-temp1 * temp2.z); + + + // Compute haze glow + temp2.x = dot(Pn, lightnorm.xyz); + temp2.x = 1. - temp2.x; + // temp2.x is 0 at the sun and increases away from sun + temp2.x = max(temp2.x, .001); + // 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; + + + // Haze color above cloud + vary_HazeColor = ( blue_horizon * blue_weight * (sunlight + ambient) + + (haze_horizon.r * haze_weight) * (sunlight * temp2.x + ambient) + ); + + + // Increase ambient when there are more clouds + vec4 tmpAmbient = ambient; + tmpAmbient += (1. - tmpAmbient) * cloud_shadow.x * 0.5; + + // Dim sunlight by cloud shadow percentage + sunlight *= (1. - cloud_shadow.x); + + // Haze color below cloud + vec4 additiveColorBelowCloud = ( blue_horizon * blue_weight * (sunlight + tmpAmbient) + + (haze_horizon.r * haze_weight) * (sunlight * temp2.x + tmpAmbient) + ); + + // Final atmosphere additive + vary_HazeColor *= (1. - temp1); + + // Attenuate cloud color by atmosphere + temp1 = sqrt(temp1); //less atmos opacity (more transparency) below clouds + + // At horizon, blend high altitude sky color towards the darker color below the clouds + vary_HazeColor += (additiveColorBelowCloud - vary_HazeColor) * (1. - sqrt(temp1)); + + // won't compile on mac without this being set + //vary_AtmosAttenuation = vec3(0.0,0.0,0.0); +} + diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index 29340c7e9f..c1147feff6 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -259,7 +259,7 @@ vec3 scaleSoftClip(vec3 light) void main() { vec2 tc = vary_fragcoord.xy; - float depth = texture2DRect(depthMap, tc.xy).a; + float depth = texture2DRect(depthMap, tc.xy).r; 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 diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightMSF.glsl new file mode 100644 index 0000000000..fcba4f57e6 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightMSF.glsl @@ -0,0 +1,318 @@ +/** + * @file softenLightF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +#extension GL_ARB_texture_rectangle : enable +#extension GL_ARB_texture_multisample : enable + +uniform sampler2DMS diffuseRect; +uniform sampler2DMS specularRect; +uniform sampler2DMS normalMap; +uniform sampler2DMS depthMap; +uniform sampler2D noiseMap; +uniform samplerCube environmentMap; +uniform sampler2D lightFunc; + +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 mat4 shadow_matrix[3]; +//uniform vec4 shadow_clip; +uniform mat3 ssao_effect_mat; + +varying vec4 vary_light; +varying vec2 vary_fragcoord; + +vec3 vary_PositionEye; + +vec3 vary_SunlitColor; +vec3 vary_AmblitColor; +vec3 vary_AdditiveColor; +vec3 vary_AtmosAttenuation; + +uniform mat4 inv_proj; +uniform vec2 screen_res; + +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; +} + +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; +} + +vec4 texture2DMS(sampler2DMS tex, ivec2 tc) +{ + vec4 ret = vec4(0,0,0,0); + + for (int i = 0; i < samples; ++i) + { + ret += texelFetch(tex,tc,i); + } + + return ret/samples; +} + +void main() +{ + vec2 tc = vary_fragcoord.xy; + ivec2 itc = ivec2(tc); + + vec3 fcol = vec3(0,0,0); + + for (int i = 0; i < samples; ++i) + { + float depth = texelFetch(depthMap, itc, i).r; + vec3 pos = getPosition_d(tc, depth).xyz; + vec3 norm = texelFetch(normalMap, itc, i).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 = texelFetch(diffuseRect, itc, i); + if (diffuse.a >= 1.0) + { + fcol += diffuse.rgb; + } + else + { + vec4 spec = texelFetch(specularRect, itc, i); + + calcAtmospherics(pos.xyz, 1.0); + + vec3 col = atmosAmbient(vec3(0)); + col += atmosAffectDirectionalLight(max(min(da, 1.0), 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*texture2D(lightFunc, vec2(sa, spec.a)).a; + + // add the two types of shiny together + col += dumbshiny * spec.rgb; + } + + col = atmosLighting(col); + col = scaleSoftClip(col); + fcol += col; + } + } + + gl_FragColor.rgb = fcol.rgb/samples; + gl_FragColor.a = 0.0; +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/spotLightMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/spotLightMSF.glsl new file mode 100644 index 0000000000..6702bd5014 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/spotLightMSF.glsl @@ -0,0 +1,234 @@ +/** + * @file multiSpotLightF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +//class 1 -- no shadows + +#extension GL_ARB_texture_rectangle : enable +#extension GL_ARB_texture_multisample : enable + +uniform sampler2DMS diffuseRect; +uniform sampler2DMS specularRect; +uniform sampler2DMS depthMap; +uniform sampler2DMS normalMap; +uniform sampler2D noiseMap; +uniform sampler2D lightFunc; +uniform sampler2D projectionMap; + +uniform mat4 proj_mat; //screen space to light space +uniform float proj_near; //near clip for projection +uniform vec3 proj_p; //plane projection is emitting from (in screen space) +uniform vec3 proj_n; +uniform float proj_focus; //distance from plane to begin blurring +uniform float proj_lod; //(number of mips in proj map) +uniform float proj_range; //range between near clip and far clip plane of projection +uniform float proj_ambient_lod; +uniform float proj_ambiance; +uniform float near_clip; +uniform float far_clip; + +uniform vec3 proj_origin; //origin of projection to be used for angular attenuation +uniform float sun_wash; +uniform int proj_shadow_idx; +uniform float shadow_fade; + +varying vec4 vary_light; + +varying vec4 vary_fragcoord; +uniform vec2 screen_res; + +uniform mat4 inv_proj; + +vec4 texture2DLodSpecular(sampler2D projectionMap, vec2 tc, float lod) +{ + vec4 ret = texture2DLod(projectionMap, tc, lod); + + vec2 dist = tc-vec2(0.5); + + float det = max(1.0-lod/(proj_lod*0.5), 0.0); + + float d = dot(dist,dist); + + ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0)+det, 1.0); + + return ret; +} + +vec4 texture2DLodDiffuse(sampler2D projectionMap, vec2 tc, float lod) +{ + vec4 ret = texture2DLod(projectionMap, tc, lod); + + vec2 dist = vec2(0.5) - abs(tc-vec2(0.5)); + + float det = min(lod/(proj_lod*0.5), 1.0); + + float d = min(dist.x, dist.y); + + float edge = 0.25*det; + + ret *= clamp(d/edge, 0.0, 1.0); + + return ret; +} + +vec4 texture2DLodAmbient(sampler2D projectionMap, vec2 tc, float lod) +{ + vec4 ret = texture2DLod(projectionMap, tc, lod); + + vec2 dist = tc-vec2(0.5); + + float d = dot(dist,dist); + + ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0), 1.0); + + return ret; +} + + +vec4 getPosition(ivec2 pos_screen, int sample) +{ + float depth = texelFetch(depthMap, pos_screen, sample).r; + vec2 sc = vec2(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; +} + +void main() +{ + vec4 frag = vary_fragcoord; + frag.xyz /= frag.w; + frag.xyz = frag.xyz*0.5+0.5; + frag.xy *= screen_res; + ivec2 itc = ivec2(frag.xy); + + vec3 fcol = vec3(0,0,0); + int wght = 0; + + for (int i = 0; i < samples; ++i) + { + vec3 pos = getPosition(itc, i).xyz; + vec3 lv = vary_light.xyz-pos.xyz; + float dist2 = dot(lv,lv); + dist2 /= vary_light.w; + if (dist2 <= 1.0) + { + vec3 norm = texelFetch(normalMap, itc, i).xyz*2.0-1.0; + + norm = normalize(norm); + float l_dist = -dot(lv, proj_n); + + vec4 proj_tc = (proj_mat * vec4(pos.xyz, 1.0)); + if (proj_tc.z >= 0.0) + { + proj_tc.xyz /= proj_tc.w; + + float fa = gl_Color.a+1.0; + float dist_atten = min(1.0-(dist2-1.0*(1.0-fa))/fa, 1.0); + if (dist_atten > 0.0) + { + lv = proj_origin-pos.xyz; + lv = normalize(lv); + float da = dot(norm, lv); + + vec3 col = vec3(0,0,0); + + vec3 diff_tex = texelFetch(diffuseRect, itc, i).rgb; + + float noise = texture2D(noiseMap, frag.xy/128.0).b; + if (proj_tc.z > 0.0 && + proj_tc.x < 1.0 && + proj_tc.y < 1.0 && + proj_tc.x > 0.0 && + proj_tc.y > 0.0) + { + float lit = 0.0; + float amb_da = proj_ambiance; + + if (da > 0.0) + { + float diff = clamp((l_dist-proj_focus)/proj_range, 0.0, 1.0); + float lod = diff * proj_lod; + + vec4 plcol = texture2DLodDiffuse(projectionMap, proj_tc.xy, lod); + + vec3 lcol = gl_Color.rgb * plcol.rgb * plcol.a; + + lit = da * dist_atten * noise; + + col = lcol*lit*diff_tex; + amb_da += (da*0.5)*proj_ambiance; + } + + //float diff = clamp((proj_range-proj_focus)/proj_range, 0.0, 1.0); + vec4 amb_plcol = texture2DLodAmbient(projectionMap, proj_tc.xy, proj_lod); + + amb_da += (da*da*0.5+0.5)*proj_ambiance; + + amb_da *= dist_atten * noise; + + amb_da = min(amb_da, 1.0-lit); + + col += amb_da*gl_Color.rgb*diff_tex.rgb*amb_plcol.rgb*amb_plcol.a; + } + + + vec4 spec = texelFetch(specularRect, itc, i); + if (spec.a > 0.0) + { + vec3 ref = reflect(normalize(pos), norm); + + //project from point pos in direction ref to plane proj_p, proj_n + vec3 pdelta = proj_p-pos; + float ds = dot(ref, proj_n); + + if (ds < 0.0) + { + vec3 pfinal = pos + ref * dot(pdelta, proj_n)/ds; + + vec4 stc = (proj_mat * vec4(pfinal.xyz, 1.0)); + + if (stc.z > 0.0) + { + stc.xy /= stc.w; + + float fatten = clamp(spec.a*spec.a+spec.a*0.5, 0.25, 1.0); + + stc.xy = (stc.xy - vec2(0.5)) * fatten + vec2(0.5); + + if (stc.x < 1.0 && + stc.y < 1.0 && + stc.x > 0.0 && + stc.y > 0.0) + { + vec4 scol = texture2DLodSpecular(projectionMap, stc.xy, proj_lod-spec.a*proj_lod); + col += dist_atten*scol.rgb*gl_Color.rgb*scol.a*spec.rgb; + } + } + } + } + + fcol += col; + ++wght; + } + } + } + } + + if (wght <= 0) + { + discard; + } + + gl_FragColor.rgb = fcol/samples; + gl_FragColor.a = 0.0; +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/starsF.glsl b/indra/newview/app_settings/shaders/class1/deferred/starsF.glsl new file mode 100644 index 0000000000..b1c25b7fd3 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/starsF.glsl @@ -0,0 +1,19 @@ +/** + * @file starsF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +uniform sampler2D diffuseMap; + +void main() +{ + vec4 col = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].xy); + + gl_FragData[0] = col; + gl_FragData[1] = vec4(0,0,0,0); + gl_FragData[2] = vec4(0,0,1,0); +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/starsV.glsl b/indra/newview/app_settings/shaders/class1/deferred/starsV.glsl new file mode 100644 index 0000000000..1f0376ce6b --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/starsV.glsl @@ -0,0 +1,17 @@ +/** + * @file starsV.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + + +void main() +{ + //transform vertex + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + gl_FrontColor = gl_Color; +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/sunLightMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/sunLightMSF.glsl new file mode 100644 index 0000000000..00093836a2 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/sunLightMSF.glsl @@ -0,0 +1,17 @@ +/** + * @file sunLightF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +//class 1, no shadow, no SSAO, should never be called + +#extension GL_ARB_texture_rectangle : enable + +void main() +{ + gl_FragColor = vec4(0,0,0,0); +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl b/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl index cd91351ad4..d1e8359742 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl @@ -35,7 +35,7 @@ uniform float shadow_offset; vec4 getPosition(vec2 pos_screen) { - float depth = texture2DRect(depthMap, pos_screen.xy).a; + float depth = texture2DRect(depthMap, pos_screen.xy).r; vec2 sc = pos_screen.xy*2.0; sc /= screen_res; sc -= vec2(1.0,1.0); diff --git a/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOMSF.glsl b/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOMSF.glsl new file mode 100644 index 0000000000..ce0ebc54f8 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOMSF.glsl @@ -0,0 +1,123 @@ +/** + * @file sunLightSSAOF.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +#extension GL_ARB_texture_rectangle : enable +#extension GL_ARB_texture_multisample : enable + +//class 1 -- no shadow, SSAO only + +uniform sampler2DMS depthMap; +uniform sampler2DMS normalMap; +uniform sampler2D noiseMap; + + +// Inputs +uniform mat4 shadow_matrix[6]; +uniform vec4 shadow_clip; +uniform float ssao_radius; +uniform float ssao_max_radius; +uniform float ssao_factor; +uniform float ssao_factor_inv; + +varying vec2 vary_fragcoord; +varying vec4 vary_light; + +uniform mat4 inv_proj; +uniform vec2 screen_res; + +uniform float shadow_bias; +uniform float shadow_offset; + +vec4 getPosition(ivec2 pos_screen, int sample) +{ + float depth = texelFetch(depthMap, pos_screen, sample).r; + 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; +} + +//calculate decreases in ambient lighting when crowded out (SSAO) +float calcAmbientOcclusion(vec4 pos, vec3 norm, int sample) +{ + float ret = 1.0; + + vec2 kern[8]; + // exponentially (^2) distant occlusion samples spread around origin + kern[0] = vec2(-1.0, 0.0) * 0.125*0.125; + kern[1] = vec2(1.0, 0.0) * 0.250*0.250; + kern[2] = vec2(0.0, 1.0) * 0.375*0.375; + kern[3] = vec2(0.0, -1.0) * 0.500*0.500; + kern[4] = vec2(0.7071, 0.7071) * 0.625*0.625; + kern[5] = vec2(-0.7071, -0.7071) * 0.750*0.750; + kern[6] = vec2(-0.7071, 0.7071) * 0.875*0.875; + kern[7] = vec2(0.7071, -0.7071) * 1.000*1.000; + + vec2 pos_screen = vary_fragcoord.xy; + vec3 pos_world = pos.xyz; + vec2 noise_reflect = texture2D(noiseMap, vary_fragcoord.xy/128.0).xy; + + float angle_hidden = 0.0; + int points = 0; + + float scale = min(ssao_radius / -pos_world.z, ssao_max_radius); + + // it was found that keeping # of samples a constant was the fastest, probably due to compiler optimizations unrolling?) + for (int i = 0; i < 8; i++) + { + ivec2 samppos_screen = ivec2(pos_screen + scale * reflect(kern[i], noise_reflect)); + vec3 samppos_world = getPosition(samppos_screen, sample).xyz; + + vec3 diff = pos_world - samppos_world; + float dist2 = dot(diff, diff); + + // assume each sample corresponds to an occluding sphere with constant radius, constant x-sectional area + // --> solid angle shrinking by the square of distance + //radius is somewhat arbitrary, can approx with just some constant k * 1 / dist^2 + //(k should vary inversely with # of samples, but this is taken care of later) + + angle_hidden = angle_hidden + float(dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) * min(1.0/dist2, ssao_factor_inv); + + // 'blocked' samples (significantly closer to camera relative to pos_world) are "no data", not "no occlusion" + points = points + int(diff.z > -1.0); + } + + angle_hidden = min(ssao_factor*angle_hidden/float(points), 1.0); + + ret = (1.0 - (float(points != 0) * angle_hidden)); + + return min(ret, 1.0); +} + +void main() +{ + vec2 pos_screen = vary_fragcoord.xy; + ivec2 itc = ivec2(pos_screen); + + float col = 0; + + for (int i = 0; i < samples; i++) + { + vec4 pos = getPosition(itc, i); + vec3 norm = texelFetch(normalMap, itc, i).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + col += calcAmbientOcclusion(pos,norm,i); + } + + col /= samples; + + gl_FragColor[0] = 1.0; + gl_FragColor[1] = col; + gl_FragColor[2] = 1.0; + gl_FragColor[3] = 1.0; +} diff --git a/indra/newview/app_settings/shaders/class1/effects/glowExtractMSF.glsl b/indra/newview/app_settings/shaders/class1/effects/glowExtractMSF.glsl new file mode 100644 index 0000000000..c03a0c61bd --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/effects/glowExtractMSF.glsl @@ -0,0 +1,38 @@ +/** + * @file glowExtractF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +#extension GL_ARB_texture_rectangle : enable +#extension GL_ARB_texture_multisample : enable + +uniform sampler2DMS diffuseMap; +uniform float minLuminance; +uniform float maxExtractAlpha; +uniform vec3 lumWeights; +uniform vec3 warmthWeights; +uniform float warmthAmount; + +void main() +{ + ivec2 itc = ivec2(gl_TexCoord[0].xy); + vec4 fcol = vec4(0,0,0,0); + + for (int i = 0; i < samples; i++) + { + vec4 col = texelFetch(diffuseMap, itc, i); + + /// CALCULATING LUMINANCE (Using NTSC lum weights) + /// http://en.wikipedia.org/wiki/Luma_%28video%29 + float lum = smoothstep(minLuminance, minLuminance+1.0, dot(col.rgb, lumWeights ) ); + float warmth = smoothstep(minLuminance, minLuminance+1.0, max(col.r * warmthWeights.r, max(col.g * warmthWeights.g, col.b * warmthWeights.b)) ); + + fcol += vec4(col.rgb, max(col.a, mix(lum, warmth, warmthAmount) * maxExtractAlpha)); + } + + gl_FragColor = fcol/samples; +} diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl index d227346163..dfb36980b0 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl @@ -35,19 +35,24 @@ float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, floa //get distance float d = length(lv); - //normalize light vector - lv *= 1.0/d; + 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); - float da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0); + //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 + // spotlight coefficient. + float spot = max(dot(-ln, lv), is_pointlight); + da *= spot*spot; // GL_SPOT_EXPONENT=2 - //angular attenuation - da *= calcDirectionalLight(n, lv); + //angular attenuation + da *= calcDirectionalLight(n, lv); + } return da; } diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl index 86f014df35..f6160815eb 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl @@ -35,19 +35,24 @@ float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, floa //get distance float d = length(lv); - //normalize light vector - lv *= 1.0/d; + 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); - float da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0); + //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 + // spotlight coefficient. + float spot = max(dot(-ln, lv), is_pointlight); + da *= spot*spot; // GL_SPOT_EXPONENT=2 - //angular attenuation - da *= calcDirectionalLight(n, lv); + //angular attenuation + da *= calcDirectionalLight(n, lv); + } return da; } diff --git a/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl b/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl index 495e86c8db..0ae09df0c6 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl @@ -37,19 +37,24 @@ float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, floa //get distance float d = length(lv); - //normalize light vector - lv *= 1.0/d; + 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); - float da = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0); + //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 + // spotlight coefficient. + float spot = max(dot(-ln, lv), is_pointlight); + da *= spot*spot; // GL_SPOT_EXPONENT=2 - //angular attenuation - da *= calcDirectionalLight(n, lv); + //angular attenuation + da *= calcDirectionalLight(n, lv); + } return da; } diff --git a/indra/newview/app_settings/shaders/class2/deferred/edgeF.glsl b/indra/newview/app_settings/shaders/class2/deferred/edgeF.glsl index 3155f3f929..30e1702e9f 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/edgeF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/edgeF.glsl @@ -22,7 +22,7 @@ uniform vec2 screen_res; float getDepth(vec2 pos_screen) { - float z = texture2DRect(depthMap, pos_screen.xy).a; + float z = texture2DRect(depthMap, pos_screen.xy).r; z = z*2.0-1.0; vec4 ndc = vec4(0.0, 0.0, z, 1.0); vec4 p = inv_proj*ndc; diff --git a/indra/newview/app_settings/shaders/class2/deferred/edgeMSF.glsl b/indra/newview/app_settings/shaders/class2/deferred/edgeMSF.glsl new file mode 100644 index 0000000000..38100d1523 --- /dev/null +++ b/indra/newview/app_settings/shaders/class2/deferred/edgeMSF.glsl @@ -0,0 +1,74 @@ +/** + * @file edgeF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +#extension GL_ARB_texture_rectangle : enable +#extension GL_ARB_texture_multisample : enable + +uniform sampler2DMS depthMap; +uniform sampler2DMS normalMap; + +varying vec2 vary_fragcoord; + +uniform float depth_cutoff; +uniform float norm_cutoff; + +uniform mat4 inv_proj; +uniform vec2 screen_res; + +float getDepth(ivec2 pos_screen, int sample) +{ + float z = texelFetch(depthMap, pos_screen, sample).r; + z = z*2.0-1.0; + vec4 ndc = vec4(0.0, 0.0, z, 1.0); + vec4 p = inv_proj*ndc; + return p.z/p.w; +} + +void main() +{ + float e = 0; + + ivec2 itc = ivec2(vary_fragcoord.xy); + + for (int i = 0; i < samples; i++) + { + vec3 norm = texelFetch(normalMap, itc, i).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + float depth = getDepth(itc, i); + + vec2 tc = vary_fragcoord.xy; + + int sc = 1; + + vec2 de; + de.x = (depth-getDepth(itc+ivec2(sc, sc),i)) + (depth-getDepth(itc+ivec2(-sc, -sc), i)); + de.y = (depth-getDepth(itc+ivec2(-sc, sc),i)) + (depth-getDepth(itc+ivec2(sc, -sc), i)); + de /= depth; + de *= de; + de = step(depth_cutoff, de); + + vec2 ne; + vec3 nexnorm = texelFetch(normalMap, itc+ivec2(-sc,-sc), i).rgb; + nexnorm = vec3((nexnorm.xy-0.5)*2.0,nexnorm.z); // unpack norm + ne.x = dot(nexnorm, norm); + vec3 neynorm = texelFetch(normalMap, itc+ivec2(sc,sc), i).rgb; + neynorm = vec3((neynorm.xy-0.5)*2.0,neynorm.z); // unpack norm + ne.y = dot(neynorm, norm); + + ne = 1.0-ne; + + ne = step(norm_cutoff, ne); + + e += dot(de,de)+dot(ne,ne); + } + + e /= samples; + + gl_FragColor.a = e; +} diff --git a/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl index d6cd984ebe..de987b1233 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl @@ -91,7 +91,7 @@ vec4 texture2DLodAmbient(sampler2D projectionMap, vec2 tc, float lod) vec4 getPosition(vec2 pos_screen) { - float depth = texture2DRect(depthMap, pos_screen.xy).a; + float depth = texture2DRect(depthMap, pos_screen.xy).r; vec2 sc = pos_screen.xy*2.0; sc /= screen_res; sc -= vec2(1.0,1.0); diff --git a/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightMSF.glsl b/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightMSF.glsl new file mode 100644 index 0000000000..d0f9101774 --- /dev/null +++ b/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightMSF.glsl @@ -0,0 +1,244 @@ +/** + * @file multiSpotLightF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +#extension GL_ARB_texture_rectangle : enable +#extension GL_ARB_texture_multisample : enable + +uniform sampler2DMS diffuseRect; +uniform sampler2DMS specularRect; +uniform sampler2DMS depthMap; +uniform sampler2DMS normalMap; +uniform sampler2DRect lightMap; +uniform sampler2D noiseMap; +uniform sampler2D lightFunc; +uniform sampler2D projectionMap; + +uniform mat4 proj_mat; //screen space to light space +uniform float proj_near; //near clip for projection +uniform vec3 proj_p; //plane projection is emitting from (in screen space) +uniform vec3 proj_n; +uniform float proj_focus; //distance from plane to begin blurring +uniform float proj_lod; //(number of mips in proj map) +uniform float proj_range; //range between near clip and far clip plane of projection +uniform float proj_ambient_lod; +uniform float proj_ambiance; +uniform float near_clip; +uniform float far_clip; + +uniform vec3 proj_origin; //origin of projection to be used for angular attenuation +uniform float sun_wash; +uniform int proj_shadow_idx; +uniform float shadow_fade; + +varying vec4 vary_light; + +varying vec4 vary_fragcoord; +uniform vec2 screen_res; + +uniform mat4 inv_proj; + +vec4 texture2DLodSpecular(sampler2D projectionMap, vec2 tc, float lod) +{ + vec4 ret = texture2DLod(projectionMap, tc, lod); + + vec2 dist = tc-vec2(0.5); + + float det = max(1.0-lod/(proj_lod*0.5), 0.0); + + float d = dot(dist,dist); + + ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0)+det, 1.0); + + return ret; +} + +vec4 texture2DLodDiffuse(sampler2D projectionMap, vec2 tc, float lod) +{ + vec4 ret = texture2DLod(projectionMap, tc, lod); + + vec2 dist = vec2(0.5) - abs(tc-vec2(0.5)); + + float det = min(lod/(proj_lod*0.5), 1.0); + + float d = min(dist.x, dist.y); + + float edge = 0.25*det; + + ret *= clamp(d/edge, 0.0, 1.0); + + return ret; +} + +vec4 texture2DLodAmbient(sampler2D projectionMap, vec2 tc, float lod) +{ + vec4 ret = texture2DLod(projectionMap, tc, lod); + + vec2 dist = tc-vec2(0.5); + + float d = dot(dist,dist); + + ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0), 1.0); + + return ret; +} + + +vec4 getPosition(ivec2 pos_screen, int sample) +{ + float depth = texelFetch(depthMap, pos_screen, sample).r; + vec2 sc = vec2(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; +} + +void main() +{ + int wght = 0; + + vec3 fcol = vec3(0,0,0); + + vec2 frag = (vary_fragcoord.xy*0.5+0.5)*screen_res; + + ivec2 itc = ivec2(frag.xy); + + float shadow = 1.0; + + if (proj_shadow_idx >= 0) + { + vec4 shd = texture2DRect(lightMap, frag); + float sh[2]; + sh[0] = shd.b; + sh[1] = shd.a; + shadow = min(sh[proj_shadow_idx]+shadow_fade, 1.0); + } + + for (int i = 0; i < samples; i++) + { + vec3 pos = getPosition(itc, i).xyz; + vec3 lv = vary_light.xyz-pos.xyz; + float dist2 = dot(lv,lv); + dist2 /= vary_light.w; + if (dist2 <= 1.0) + { + vec3 norm = texelFetch(normalMap, itc, i).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + + norm = normalize(norm); + float l_dist = -dot(lv, proj_n); + + vec4 proj_tc = (proj_mat * vec4(pos.xyz, 1.0)); + if (proj_tc.z >= 0.0) + { + proj_tc.xyz /= proj_tc.w; + + float fa = gl_Color.a+1.0; + float dist_atten = min(1.0-(dist2-1.0*(1.0-fa))/fa, 1.0); + if (dist_atten > 0.0) + { + lv = proj_origin-pos.xyz; + lv = normalize(lv); + float da = dot(norm, lv); + + vec3 col = vec3(0,0,0); + + vec3 diff_tex = texelFetch(diffuseRect, itc, i).rgb; + + float noise = texture2D(noiseMap, frag.xy/128.0).b; + if (proj_tc.z > 0.0 && + proj_tc.x < 1.0 && + proj_tc.y < 1.0 && + proj_tc.x > 0.0 && + proj_tc.y > 0.0) + { + float lit = 0.0; + float amb_da = proj_ambiance; + + if (da > 0.0) + { + float diff = clamp((l_dist-proj_focus)/proj_range, 0.0, 1.0); + float lod = diff * proj_lod; + + vec4 plcol = texture2DLodDiffuse(projectionMap, proj_tc.xy, lod); + + vec3 lcol = gl_Color.rgb * plcol.rgb * plcol.a; + + lit = da * dist_atten * noise; + + col = lcol*lit*diff_tex*shadow; + amb_da += (da*0.5)*(1.0-shadow)*proj_ambiance; + } + + //float diff = clamp((proj_range-proj_focus)/proj_range, 0.0, 1.0); + vec4 amb_plcol = texture2DLodAmbient(projectionMap, proj_tc.xy, proj_lod); + + amb_da += (da*da*0.5+0.5)*proj_ambiance; + + amb_da *= dist_atten * noise; + + amb_da = min(amb_da, 1.0-lit); + + col += amb_da*gl_Color.rgb*diff_tex.rgb*amb_plcol.rgb*amb_plcol.a; + } + + + vec4 spec = texelFetch(specularRect, itc, i); + if (spec.a > 0.0) + { + vec3 ref = reflect(normalize(pos), norm); + + //project from point pos in direction ref to plane proj_p, proj_n + vec3 pdelta = proj_p-pos; + float ds = dot(ref, proj_n); + + if (ds < 0.0) + { + vec3 pfinal = pos + ref * dot(pdelta, proj_n)/ds; + + vec4 stc = (proj_mat * vec4(pfinal.xyz, 1.0)); + + if (stc.z > 0.0) + { + stc.xy /= stc.w; + + float fatten = clamp(spec.a*spec.a+spec.a*0.5, 0.25, 1.0); + + stc.xy = (stc.xy - vec2(0.5)) * fatten + vec2(0.5); + + if (stc.x < 1.0 && + stc.y < 1.0 && + stc.x > 0.0 && + stc.y > 0.0) + { + vec4 scol = texture2DLodSpecular(projectionMap, stc.xy, proj_lod-spec.a*proj_lod); + col += dist_atten*scol.rgb*gl_Color.rgb*scol.a*spec.rgb*shadow; + } + } + } + } + + fcol += col; + wght++; + } + } + } + } + + if (wght <= 0) + { + discard; + } + + gl_FragColor.rgb = fcol/samples; + gl_FragColor.a = 0.0; +} diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl index 0160e84278..dfa1964142 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl @@ -71,7 +71,7 @@ vec4 getPosition_d(vec2 pos_screen, float depth) 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; + float depth = texture2DRect(depthMap, pos_screen.xy).r; return getPosition_d(pos_screen, depth); } @@ -258,7 +258,7 @@ vec3 scaleSoftClip(vec3 light) void main() { vec2 tc = vary_fragcoord.xy; - float depth = texture2DRect(depthMap, tc.xy).a; + float depth = texture2DRect(depthMap, tc.xy).r; 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 @@ -288,54 +288,8 @@ void main() 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 += dumbshiny * spec.rgb; } col = atmosLighting(col); diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightMSF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightMSF.glsl new file mode 100644 index 0000000000..cb09fe9895 --- /dev/null +++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightMSF.glsl @@ -0,0 +1,307 @@ +/** + * @file softenLightMSF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +#extension GL_ARB_texture_rectangle : enable +#extension GL_ARB_texture_multisample : enable + +uniform sampler2DMS diffuseRect; +uniform sampler2DMS specularRect; +uniform sampler2DMS normalMap; +uniform sampler2DRect lightMap; +uniform sampler2DMS 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; +} + +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; + ivec2 itc = ivec2(tc); + + vec3 fcol = vec3(0,0,0); + + vec2 scol_ambocc = texture2DRect(lightMap, tc).rg; + float ambocc = scol_ambocc.g; + + for (int i = 0; i < samples; ++i) + { + float depth = texelFetch(depthMap, itc.xy, i).r; + vec3 pos = getPosition_d(tc, depth).xyz; + vec3 norm = texelFetch(normalMap, itc, i).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + + float da = max(dot(norm.xyz, vary_light.xyz), 0.0); + + vec4 diffuse = texelFetch(diffuseRect, itc, i); + vec4 spec = texelFetch(specularRect, itc, i); + + float amb = 0; + + float scol = max(scol_ambocc.r, diffuse.a); + amb += ambocc; + + 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; + + // add the two types of shiny together + col += dumbshiny * spec.rgb; + } + + col = atmosLighting(col); + col = scaleSoftClip(col); + + fcol += col; + } + + gl_FragColor.rgb = fcol/samples; + gl_FragColor.a = 0.0; +} diff --git a/indra/newview/app_settings/shaders/class2/deferred/spotLightMSF.glsl b/indra/newview/app_settings/shaders/class2/deferred/spotLightMSF.glsl new file mode 100644 index 0000000000..8d6ffd79c5 --- /dev/null +++ b/indra/newview/app_settings/shaders/class2/deferred/spotLightMSF.glsl @@ -0,0 +1,245 @@ +/** + * @file multiSpotLightF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +#extension GL_ARB_texture_rectangle : enable +#extension GL_ARB_texture_multisample : enable + +uniform sampler2DMS diffuseRect; +uniform sampler2DMS specularRect; +uniform sampler2DMS depthMap; +uniform sampler2DMS normalMap; +uniform sampler2DRect lightMap; +uniform sampler2D noiseMap; +uniform sampler2D lightFunc; +uniform sampler2D projectionMap; + +uniform mat4 proj_mat; //screen space to light space +uniform float proj_near; //near clip for projection +uniform vec3 proj_p; //plane projection is emitting from (in screen space) +uniform vec3 proj_n; +uniform float proj_focus; //distance from plane to begin blurring +uniform float proj_lod; //(number of mips in proj map) +uniform float proj_range; //range between near clip and far clip plane of projection +uniform float proj_ambient_lod; +uniform float proj_ambiance; +uniform float near_clip; +uniform float far_clip; + +uniform vec3 proj_origin; //origin of projection to be used for angular attenuation +uniform float sun_wash; +uniform int proj_shadow_idx; +uniform float shadow_fade; + +varying vec4 vary_light; + +varying vec4 vary_fragcoord; +uniform vec2 screen_res; + +uniform mat4 inv_proj; + +vec4 texture2DLodSpecular(sampler2D projectionMap, vec2 tc, float lod) +{ + vec4 ret = texture2DLod(projectionMap, tc, lod); + + vec2 dist = tc-vec2(0.5); + + float det = max(1.0-lod/(proj_lod*0.5), 0.0); + + float d = dot(dist,dist); + + ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0)+det, 1.0); + + return ret; +} + +vec4 texture2DLodDiffuse(sampler2D projectionMap, vec2 tc, float lod) +{ + vec4 ret = texture2DLod(projectionMap, tc, lod); + + vec2 dist = vec2(0.5) - abs(tc-vec2(0.5)); + + float det = min(lod/(proj_lod*0.5), 1.0); + + float d = min(dist.x, dist.y); + + float edge = 0.25*det; + + ret *= clamp(d/edge, 0.0, 1.0); + + return ret; +} + +vec4 texture2DLodAmbient(sampler2D projectionMap, vec2 tc, float lod) +{ + vec4 ret = texture2DLod(projectionMap, tc, lod); + + vec2 dist = tc-vec2(0.5); + + float d = dot(dist,dist); + + ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0), 1.0); + + return ret; +} + + +vec4 getPosition(ivec2 pos_screen, int sample) +{ + float depth = texelFetch(depthMap, pos_screen, sample).r; + vec2 sc = vec2(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; +} + +void main() +{ + vec4 frag = vary_fragcoord; + frag.xyz /= frag.w; + frag.xyz = frag.xyz*0.5+0.5; + frag.xy *= screen_res; + ivec2 itc = ivec2(frag.xy); + + vec3 fcol = vec3(0,0,0); + int wght = 0; + + float shadow = 1.0; + + if (proj_shadow_idx >= 0) + { + vec4 shd = texture2DRect(lightMap, frag.xy); + float sh[2]; + sh[0] = shd.b; + sh[1] = shd.a; + shadow = min(sh[proj_shadow_idx]+shadow_fade, 1.0); + } + + for (int i = 0; i < samples; i++) + { + vec3 pos = getPosition(itc, i).xyz; + vec3 lv = vary_light.xyz-pos.xyz; + float dist2 = dot(lv,lv); + dist2 /= vary_light.w; + if (dist2 <= 1.0) + { + vec3 norm = texelFetch(normalMap, itc, i).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + + norm = normalize(norm); + float l_dist = -dot(lv, proj_n); + + vec4 proj_tc = (proj_mat * vec4(pos.xyz, 1.0)); + if (proj_tc.z >= 0.0) + { + proj_tc.xyz /= proj_tc.w; + + float fa = gl_Color.a+1.0; + float dist_atten = min(1.0-(dist2-1.0*(1.0-fa))/fa, 1.0); + if (dist_atten > 0.0) + { + lv = proj_origin-pos.xyz; + lv = normalize(lv); + float da = dot(norm, lv); + + vec3 col = vec3(0,0,0); + + vec3 diff_tex = texelFetch(diffuseRect, itc, i).rgb; + + float noise = texture2D(noiseMap, frag.xy/128.0).b; + if (proj_tc.z > 0.0 && + proj_tc.x < 1.0 && + proj_tc.y < 1.0 && + proj_tc.x > 0.0 && + proj_tc.y > 0.0) + { + float lit = 0.0; + float amb_da = proj_ambiance; + + if (da > 0.0) + { + float diff = clamp((l_dist-proj_focus)/proj_range, 0.0, 1.0); + float lod = diff * proj_lod; + + vec4 plcol = texture2DLodDiffuse(projectionMap, proj_tc.xy, lod); + + vec3 lcol = gl_Color.rgb * plcol.rgb * plcol.a; + + lit = da * dist_atten * noise; + + col = lcol*lit*diff_tex*shadow; + amb_da += (da*0.5)*(1.0-shadow)*proj_ambiance; + } + + //float diff = clamp((proj_range-proj_focus)/proj_range, 0.0, 1.0); + vec4 amb_plcol = texture2DLodAmbient(projectionMap, proj_tc.xy, proj_lod); + + amb_da += (da*da*0.5+0.5)*proj_ambiance; + + amb_da *= dist_atten * noise; + + amb_da = min(amb_da, 1.0-lit); + + col += amb_da*gl_Color.rgb*diff_tex.rgb*amb_plcol.rgb*amb_plcol.a; + } + + + vec4 spec = texelFetch(specularRect, itc, i); + if (spec.a > 0.0) + { + vec3 ref = reflect(normalize(pos), norm); + + //project from point pos in direction ref to plane proj_p, proj_n + vec3 pdelta = proj_p-pos; + float ds = dot(ref, proj_n); + + if (ds < 0.0) + { + vec3 pfinal = pos + ref * dot(pdelta, proj_n)/ds; + + vec4 stc = (proj_mat * vec4(pfinal.xyz, 1.0)); + + if (stc.z > 0.0) + { + stc.xy /= stc.w; + + float fatten = clamp(spec.a*spec.a+spec.a*0.5, 0.25, 1.0); + + stc.xy = (stc.xy - vec2(0.5)) * fatten + vec2(0.5); + + if (stc.x < 1.0 && + stc.y < 1.0 && + stc.x > 0.0 && + stc.y > 0.0) + { + vec4 scol = texture2DLodSpecular(projectionMap, stc.xy, proj_lod-spec.a*proj_lod); + col += dist_atten*scol.rgb*gl_Color.rgb*scol.a*spec.rgb*shadow; + } + } + } + } + + fcol += col; + wght++; + } + } + } + } + + if (wght <= 0) + { + discard; + } + + gl_FragColor.rgb = fcol/wght; + gl_FragColor.a = 0.0; +} diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl index 4369b3b34f..b724c134b9 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl @@ -45,7 +45,7 @@ uniform float spot_shadow_offset; vec4 getPosition(vec2 pos_screen) { - float depth = texture2DRect(depthMap, pos_screen.xy).a; + float depth = texture2DRect(depthMap, pos_screen.xy).r; vec2 sc = pos_screen.xy*2.0; sc /= screen_res; sc -= vec2(1.0,1.0); diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightMSF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightMSF.glsl new file mode 100644 index 0000000000..dd6fa958c9 --- /dev/null +++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightMSF.glsl @@ -0,0 +1,202 @@ +/** + * @file sunLightMSF.glsl + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * $/LicenseInfo$ + */ + +#version 120 + +#extension GL_ARB_texture_rectangle : enable +#extension GL_ARB_texture_multisample : enable + +//class 2, shadows, no SSAO + +uniform sampler2DMS depthMap; +uniform sampler2DMS normalMap; +uniform sampler2DRectShadow shadowMap0; +uniform sampler2DRectShadow shadowMap1; +uniform sampler2DRectShadow shadowMap2; +uniform sampler2DRectShadow shadowMap3; +uniform sampler2DShadow shadowMap4; +uniform sampler2DShadow shadowMap5; + + +// Inputs +uniform mat4 shadow_matrix[6]; +uniform vec4 shadow_clip; +uniform float ssao_radius; +uniform float ssao_max_radius; +uniform float ssao_factor; +uniform float ssao_factor_inv; + +varying vec2 vary_fragcoord; +varying vec4 vary_light; + +uniform mat4 inv_proj; +uniform vec2 screen_res; +uniform vec2 shadow_res; +uniform vec2 proj_shadow_res; + +uniform float shadow_bias; +uniform float shadow_offset; + +uniform float spot_shadow_bias; +uniform float spot_shadow_offset; + +vec4 getPosition(ivec2 pos_screen, int sample) +{ + float depth = texelFetch(depthMap, pos_screen.xy, sample).r; + vec2 sc = vec2(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; +} + +float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl) +{ + stc.xyz /= stc.w; + stc.z += shadow_bias*scl; + + float cs = shadow2DRect(shadowMap, stc.xyz).x; + float shadow = cs; + + shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, 1.5, 0.0)).x, cs); + shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, -1.5, 0.0)).x, cs); + shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, 1.5, 0.0)).x, cs); + shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, -1.5, 0.0)).x, cs); + + return shadow/5.0; + + //return shadow; +} + +float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl) +{ + stc.xyz /= stc.w; + stc.z += spot_shadow_bias*scl; + + float cs = shadow2D(shadowMap, stc.xyz).x; + float shadow = cs; + + vec2 off = 1.5/proj_shadow_res; + + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, -off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, -off.y, 0.0)).x, cs); + + return shadow/5.0; + + //return shadow; +} + +void main() +{ + vec2 pos_screen = vary_fragcoord.xy; + ivec2 itc = ivec2(pos_screen); + + //try doing an unproject here + + vec4 fcol = vec4(0,0,0,0); + + for (int i = 0; i < samples; i++) + { + vec4 pos = getPosition(itc, i); + + vec4 nmap4 = texelFetch(normalMap, itc, i); + nmap4 = vec4((nmap4.xy-0.5)*2.0,nmap4.z,nmap4.w); // unpack norm + float displace = nmap4.w; + vec3 norm = nmap4.xyz; + + /*if (pos.z == 0.0) // do nothing for sky *FIX: REMOVE THIS IF/WHEN THE POSITION MAP IS BEING USED AS A STENCIL + { + gl_FragColor = vec4(0.0); // doesn't matter + return; + }*/ + + float shadow = 1.0; + float dp_directional_light = max(0.0, dot(norm, vary_light.xyz)); + + vec3 shadow_pos = pos.xyz + displace*norm; + vec3 offset = vary_light.xyz * (1.0-dp_directional_light); + + vec4 spos = vec4(shadow_pos+offset*shadow_offset, 1.0); + + if (spos.z > -shadow_clip.w) + { + if (dp_directional_light == 0.0) + { + // if we know this point is facing away from the sun then we know it's in shadow without having to do a squirrelly shadow-map lookup + shadow = 0.0; + } + else + { + vec4 lpos; + + if (spos.z < -shadow_clip.z) + { + lpos = shadow_matrix[3]*spos; + lpos.xy *= shadow_res; + shadow = pcfShadow(shadowMap3, lpos, 0.25); + shadow += max((pos.z+shadow_clip.z)/(shadow_clip.z-shadow_clip.w)*2.0-1.0, 0.0); + } + else if (spos.z < -shadow_clip.y) + { + lpos = shadow_matrix[2]*spos; + lpos.xy *= shadow_res; + shadow = pcfShadow(shadowMap2, lpos, 0.5); + } + else if (spos.z < -shadow_clip.x) + { + lpos = shadow_matrix[1]*spos; + lpos.xy *= shadow_res; + shadow = pcfShadow(shadowMap1, lpos, 0.75); + } + else + { + lpos = shadow_matrix[0]*spos; + lpos.xy *= shadow_res; + shadow = pcfShadow(shadowMap0, lpos, 1.0); + } + + // take the most-shadowed value out of these two: + // * the blurred sun shadow in the light (shadow) map + // * an unblurred dot product between the sun and this norm + // the goal is to err on the side of most-shadow to fill-in shadow holes and reduce artifacting + shadow = min(shadow, dp_directional_light); + + //lpos.xy /= lpos.w*32.0; + //if (fract(lpos.x) < 0.1 || fract(lpos.y) < 0.1) + //{ + // shadow = 0.0; + //} + + } + } + else + { + // more distant than the shadow map covers + shadow = 1.0; + } + + fcol[0] += shadow; + fcol[1] += 1.0; + + spos = vec4(shadow_pos+norm*spot_shadow_offset, 1.0); + + //spotlight shadow 1 + vec4 lpos = shadow_matrix[4]*spos; + fcol[2] += pcfShadow(shadowMap4, lpos, 0.8); + + //spotlight shadow 2 + lpos = shadow_matrix[5]*spos; + fcol[3] += pcfShadow(shadowMap5, lpos, 0.8); + } + + gl_FragColor = fcol/samples; +} diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl index 847b36b1ac..68e18162f6 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl @@ -45,7 +45,7 @@ uniform float spot_shadow_offset; vec4 getPosition(vec2 pos_screen) { - float depth = texture2DRect(depthMap, pos_screen.xy).a; + float depth = texture2DRect(depthMap, pos_screen.xy).r; vec2 sc = pos_screen.xy*2.0; sc /= screen_res; sc -= vec2(1.0,1.0); diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOMSF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOMSF.glsl new file mode 100644 index 0000000000..d28741f945 --- /dev/null +++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOMSF.glsl @@ -0,0 +1,241 @@ +/** + * @file sunLightSSAOF.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +#extension GL_ARB_texture_rectangle : enable +#extension GL_ARB_texture_multisample : enable + +//class 2 -- shadows and SSAO + +uniform sampler2DMS depthMap; +uniform sampler2DMS normalMap; +uniform sampler2DRectShadow shadowMap0; +uniform sampler2DRectShadow shadowMap1; +uniform sampler2DRectShadow shadowMap2; +uniform sampler2DRectShadow shadowMap3; +uniform sampler2DShadow shadowMap4; +uniform sampler2DShadow shadowMap5; +uniform sampler2D noiseMap; + +// Inputs +uniform mat4 shadow_matrix[6]; +uniform vec4 shadow_clip; +uniform float ssao_radius; +uniform float ssao_max_radius; +uniform float ssao_factor; +uniform float ssao_factor_inv; + +varying vec2 vary_fragcoord; +varying vec4 vary_light; + +uniform mat4 inv_proj; +uniform vec2 screen_res; +uniform vec2 shadow_res; +uniform vec2 proj_shadow_res; + +uniform float shadow_bias; +uniform float shadow_offset; + +uniform float spot_shadow_bias; +uniform float spot_shadow_offset; + +vec4 getPosition(ivec2 pos_screen, int sample) +{ + float depth = texelFetch(depthMap, pos_screen, sample).r; + vec2 sc = vec2(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; +} + +//calculate decreases in ambient lighting when crowded out (SSAO) +float calcAmbientOcclusion(vec4 pos, vec3 norm, int sample) +{ + float ret = 1.0; + + vec2 kern[8]; + // exponentially (^2) distant occlusion samples spread around origin + kern[0] = vec2(-1.0, 0.0) * 0.125*0.125; + kern[1] = vec2(1.0, 0.0) * 0.250*0.250; + kern[2] = vec2(0.0, 1.0) * 0.375*0.375; + kern[3] = vec2(0.0, -1.0) * 0.500*0.500; + kern[4] = vec2(0.7071, 0.7071) * 0.625*0.625; + kern[5] = vec2(-0.7071, -0.7071) * 0.750*0.750; + kern[6] = vec2(-0.7071, 0.7071) * 0.875*0.875; + kern[7] = vec2(0.7071, -0.7071) * 1.000*1.000; + + vec2 pos_screen = vary_fragcoord.xy; + vec3 pos_world = pos.xyz; + vec2 noise_reflect = texture2D(noiseMap, vary_fragcoord.xy/128.0).xy; + + float angle_hidden = 0.0; + int points = 0; + + float scale = min(ssao_radius / -pos_world.z, ssao_max_radius); + + // it was found that keeping # of samples a constant was the fastest, probably due to compiler optimizations (unrolling?) + for (int i = 0; i < 8; i++) + { + ivec2 samppos_screen = ivec2(pos_screen + scale * reflect(kern[i], noise_reflect)); + vec3 samppos_world = getPosition(samppos_screen, sample).xyz; + + vec3 diff = pos_world - samppos_world; + float dist2 = dot(diff, diff); + + // assume each sample corresponds to an occluding sphere with constant radius, constant x-sectional area + // --> solid angle shrinking by the square of distance + //radius is somewhat arbitrary, can approx with just some constant k * 1 / dist^2 + //(k should vary inversely with # of samples, but this is taken care of later) + + angle_hidden = angle_hidden + float(dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) * min(1.0/dist2, ssao_factor_inv); + + // 'blocked' samples (significantly closer to camera relative to pos_world) are "no data", not "no occlusion" + points = points + int(diff.z > -1.0); + } + + angle_hidden = min(ssao_factor*angle_hidden/float(points), 1.0); + + ret = (1.0 - (float(points != 0) * angle_hidden)); + + return min(ret, 1.0); +} + +float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl) +{ + stc.xyz /= stc.w; + stc.z += shadow_bias*scl; + + float cs = shadow2DRect(shadowMap, stc.xyz).x; + float shadow = cs; + + shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, 1.5, 0.0)).x, cs); + shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, -1.5, 0.0)).x, cs); + shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, 1.5, 0.0)).x, cs); + shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, -1.5, 0.0)).x, cs); + + return shadow/5.0; + + //return shadow; +} + +float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl) +{ + stc.xyz /= stc.w; + stc.z += spot_shadow_bias*scl; + + float cs = shadow2D(shadowMap, stc.xyz).x; + float shadow = cs; + + vec2 off = 1.5/proj_shadow_res; + + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, -off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, -off.y, 0.0)).x, cs); + + + return shadow/5.0; + + //return shadow; +} + +void main() +{ + vec2 pos_screen = vary_fragcoord.xy; + ivec2 itc = ivec2(pos_screen); + vec4 fcol = vec4(0,0,0,0); + + for (int i = 0; i < samples; i++) + { + vec4 pos = getPosition(itc, i); + + vec4 nmap4 = texelFetch(normalMap, itc, i); + nmap4 = vec4((nmap4.xy-0.5)*2.0,nmap4.z,nmap4.w); // unpack norm + float displace = nmap4.w; + vec3 norm = nmap4.xyz; + + float shadow = 1.0; + float dp_directional_light = max(0.0, dot(norm, vary_light.xyz)); + + vec3 shadow_pos = pos.xyz + displace*norm; + vec3 offset = vary_light.xyz * (1.0-dp_directional_light); + + vec4 spos = vec4(shadow_pos+offset*shadow_offset, 1.0); + + if (spos.z > -shadow_clip.w) + { + if (dp_directional_light == 0.0) + { + // if we know this point is facing away from the sun then we know it's in shadow without having to do a squirrelly shadow-map lookup + shadow = 0.0; + } + else + { + vec4 lpos; + + if (spos.z < -shadow_clip.z) + { + lpos = shadow_matrix[3]*spos; + lpos.xy *= shadow_res; + shadow = pcfShadow(shadowMap3, lpos, 0.25); + shadow += max((pos.z+shadow_clip.z)/(shadow_clip.z-shadow_clip.w)*2.0-1.0, 0.0); + } + else if (spos.z < -shadow_clip.y) + { + lpos = shadow_matrix[2]*spos; + lpos.xy *= shadow_res; + shadow = pcfShadow(shadowMap2, lpos, 0.5); + } + else if (spos.z < -shadow_clip.x) + { + lpos = shadow_matrix[1]*spos; + lpos.xy *= shadow_res; + shadow = pcfShadow(shadowMap1, lpos, 0.75); + } + else + { + lpos = shadow_matrix[0]*spos; + lpos.xy *= shadow_res; + shadow = pcfShadow(shadowMap0, lpos, 1.0); + } + + // take the most-shadowed value out of these two: + // * the blurred sun shadow in the light (shadow) map + // * an unblurred dot product between the sun and this norm + // the goal is to err on the side of most-shadow to fill-in shadow holes and reduce artifacting + shadow = min(shadow, dp_directional_light); + + } + } + else + { + // more distant than the shadow map covers + shadow = 1.0; + } + + + fcol[0] += shadow; + fcol[1] += calcAmbientOcclusion(pos, norm, i); + + spos.xyz = shadow_pos+offset*spot_shadow_offset; + + //spotlight shadow 1 + vec4 lpos = shadow_matrix[4]*spos; + fcol[2] += pcfShadow(shadowMap4, lpos, 0.8); + + //spotlight shadow 2 + lpos = shadow_matrix[5]*spos; + fcol[3] += pcfShadow(shadowMap5, lpos, 0.8); + } + + gl_FragColor = fcol / samples; +} diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt index dd8a88e558..25dd2ffe78 100644 --- a/indra/newview/featuretable.txt +++ b/indra/newview/featuretable.txt @@ -467,7 +467,6 @@ RenderAvatarCloth 0 0 list ATI RenderUseStreamVBO 1 0 -RenderAvatarVP 1 0 // Disable vertex buffer objects by default for ATI cards with little video memory list ATIVramLT256 diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt index c075c660f3..8b8340bd71 100644 --- a/indra/newview/featuretable_mac.txt +++ b/indra/newview/featuretable_mac.txt @@ -1,4 +1,4 @@ -version 23 +version 24 // NOTE: This is mostly identical to featuretable_mac.txt with a few differences // Should be combined into one table @@ -281,6 +281,9 @@ RenderVBOEnable 1 0 list TexUnit8orLess RenderDeferredSSAO 0 0 +list ATI +RenderDeferredSSAO 0 0 + list Intel RenderAnisotropic 1 0 RenderLocalLights 1 0 @@ -409,31 +412,6 @@ Disregard128DefaultDrawDistance 1 0 list ATI_Mobility_Radeon_X1xxx Disregard128DefaultDrawDistance 1 0 - - - -// Avatar hardware skinning causes -// invisible avatars on HD 2600... so I masked -// out other possible bad ones till it's fixed - -list ATI_Radeon_HD_2300 -RenderAvatarVP 0 0 -RenderAvatarCloth 0 0 -Disregard128DefaultDrawDistance 1 0 -list ATI_Radeon_HD_2400 -RenderAvatarVP 0 0 -RenderAvatarCloth 0 0 -Disregard128DefaultDrawDistance 1 0 -list ATI_Radeon_HD_2600 -RenderAvatarVP 0 0 -RenderAvatarCloth 0 0 -list ATI_Radeon_HD_2900 -RenderAvatarVP 0 0 -RenderAvatarCloth 0 0 -list ATI_Radeon_HD_3800 -RenderAvatarVP 0 0 -RenderAvatarCloth 0 0 - /// Tweaked NVIDIA list NVIDIA_GeForce_FX_5100 diff --git a/indra/newview/featuretable_xp.txt b/indra/newview/featuretable_xp.txt index 3339172a1a..65a6f59bbe 100644 --- a/indra/newview/featuretable_xp.txt +++ b/indra/newview/featuretable_xp.txt @@ -466,7 +466,6 @@ RenderAvatarCloth 0 0 list ATI RenderUseStreamVBO 1 0 -RenderAvatarVP 1 0 // Disable vertex buffer objects by default for ATI cards with little video memory list ATIVramLT256 diff --git a/indra/newview/llaccountingquotamanager.cpp b/indra/newview/llaccountingquotamanager.cpp new file mode 100644 index 0000000000..9b77ada8b7 --- /dev/null +++ b/indra/newview/llaccountingquotamanager.cpp @@ -0,0 +1,284 @@ +/**
+ * @file LLAccountingQuotaManager.cpp
+ * @ Handles the setting and accessing for costs associated with mesh
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2010, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#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 "<<reason<<llendl;
+ //prep#do we really want to remove all because of one failure - verify
+ clearPendingRequests();
+ }
+
+ void result( const LLSD& content )
+ {
+ if ( !content.isMap() || content.has("error") )
+ {
+ llwarns << "Error on fetched data"<< llendl;
+ //prep#do we really want to remove all because of one failure - verify
+ clearPendingRequests();
+ return;
+ }
+
+ //Differentiate what the incoming caps could be from the data
+ bool containsParcel = content.has("parcel");
+ bool containsSelection = content.has("selected");
+
+ //Loop over the stored object ids checking against the incoming data
+ for ( LLSD::array_iterator iter = mObjectIDs.beginArray(); iter != mObjectIDs.endArray(); ++iter )
+ {
+ LLUUID objectID = iter->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 "<<llendl;
+ mUpdateObjectQuota.clear();
+ mPendingObjectQuota.clear();
+ }
+}
+//===============================================================================
+void LLAccountingQuotaManager::updateObjectCost( const LLUUID& objectID )
+{
+ mUpdateObjectQuota.insert( objectID );
+}
+//===============================================================================
+void LLAccountingQuotaManager::removePendingObjectQuota( const LLUUID& objectID )
+{
+ mPendingObjectQuota.erase( objectID );
+}
+//===============================================================================
diff --git a/indra/newview/llaccountingquotamanager.h b/indra/newview/llaccountingquotamanager.h new file mode 100755 index 0000000000..f605d1e6b2 --- /dev/null +++ b/indra/newview/llaccountingquotamanager.h @@ -0,0 +1,60 @@ +/** + * @file lllAccountingQuotaManager.h + * @ + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_ACCOUNTINGQUOTAMANAGER_H +#define LL_ACCOUNTINGQUOTAMANAGER_H +//=============================================================================== +#include "llaccountingquota.h" +//=============================================================================== +class LLAccountingQuotaManager : public LLSingleton<LLAccountingQuotaManager> +{ +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<LLUUID> mUpdateObjectQuota; + //During fetchQuota we move object into a the pending set to signify that + //a fetch has been instigated. + std::set<LLUUID> mPendingObjectQuota; + typedef std::set<LLUUID>::iterator IDIt; +}; +//=============================================================================== + +#endif diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index c6b5a0113f..80085dad9d 100644 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -394,7 +394,9 @@ LLVector3 LLAgentCamera::calcFocusOffset(LLViewerObject *object, LLVector3 origi LLQuaternion inv_obj_rot = ~obj_rot; // get inverse of rotation LLVector3 object_extents; const LLVector4a* oe4 = object->mDrawable->getSpatialExtents(); - object_extents.set( oe4[1][0], oe4[1][1], oe4[1][2] ); + LLVector4a size; + size.setSub(oe4[1], oe4[0]); + object_extents.set( size[0], size[1], size[2] ); // make sure they object extents are non-zero object_extents.clamp(0.001f, F32_MAX); diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index bdc12ec0e3..ad3710843c 100644 --- a/indra/newview/lldrawable.cpp +++ b/indra/newview/lldrawable.cpp @@ -94,7 +94,9 @@ void LLDrawable::init() mRenderType = 0; mCurrentScale = LLVector3(1,1,1); mDistanceWRTCamera = 0.0f; - + mPositionGroup.clear(); + mExtents[0].clear(); + mExtents[1].clear(); mQuietCount = 0; mState = 0; @@ -587,7 +589,10 @@ void LLDrawable::setRadius(F32 radius) void LLDrawable::moveUpdatePipeline(BOOL moved) { - makeActive(); + if (moved) + { + makeActive(); + } // Update the face centers. for (S32 i = 0; i < getNumFaces(); i++) @@ -695,7 +700,8 @@ void LLDrawable::updateDistance(LLCamera& camera, bool force_update) { if (LLViewerCamera::sCurCameraID != LLViewerCamera::CAMERA_WORLD) { - llerrs << "WTF?" << llendl; + llwarns << "Attempted to update distance for non-world camera." << llendl; + return; } //switch LOD with the spatial group to avoid artifacts diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h index 9ebe1a45b4..e268640a21 100644 --- a/indra/newview/lldrawable.h +++ b/indra/newview/lldrawable.h @@ -276,6 +276,7 @@ public: REBUILD_SHADOW = 0x02000000, HAS_ALPHA = 0x04000000, RIGGED = 0x08000000, + PARTITION_MOVE = 0x10000000, } EDrawableFlags; private: //aligned members diff --git a/indra/newview/lldrawpoolwlsky.cpp b/indra/newview/lldrawpoolwlsky.cpp index 696c2d1abd..409b18d522 100644 --- a/indra/newview/lldrawpoolwlsky.cpp +++ b/indra/newview/lldrawpoolwlsky.cpp @@ -44,6 +44,8 @@ LLPointer<LLViewerTexture> LLDrawPoolWLSky::sCloudNoiseTexture = NULL; LLPointer<LLImageRaw> LLDrawPoolWLSky::sCloudNoiseRawImage = NULL; +static LLGLSLShader* cloud_shader = NULL; +static LLGLSLShader* sky_shader = NULL; LLDrawPoolWLSky::LLDrawPoolWLSky(void) : @@ -83,12 +85,32 @@ LLViewerTexture *LLDrawPoolWLSky::getDebugTexture() void LLDrawPoolWLSky::beginRenderPass( S32 pass ) { + sky_shader = + LLPipeline::sUnderWaterRender ? + &gObjectSimpleWaterProgram : + &gWLSkyProgram; + + cloud_shader = + LLPipeline::sUnderWaterRender ? + &gObjectSimpleWaterProgram : + &gWLCloudProgram; } void LLDrawPoolWLSky::endRenderPass( S32 pass ) { } +void LLDrawPoolWLSky::beginDeferredPass(S32 pass) +{ + sky_shader = &gDeferredWLSkyProgram; + cloud_shader = &gDeferredWLCloudProgram; +} + +void LLDrawPoolWLSky::endDeferredPass(S32 pass) +{ + +} + void LLDrawPoolWLSky::renderDome(F32 camHeightLocal, LLGLSLShader * shader) const { LLVector3 const & origin = LLViewerCamera::getInstance()->getOrigin(); @@ -128,19 +150,14 @@ void LLDrawPoolWLSky::renderSkyHaze(F32 camHeightLocal) const { if (gPipeline.canUseWindLightShaders() && gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SKY)) { - LLGLSLShader* shader = - LLPipeline::sUnderWaterRender ? - &gObjectSimpleWaterProgram : - &gWLSkyProgram; - LLGLDisable blend(GL_BLEND); - shader->bind(); + sky_shader->bind(); /// Render the skydome - renderDome(camHeightLocal, shader); + renderDome(camHeightLocal, sky_shader); - shader->unbind(); + sky_shader->unbind(); } } @@ -186,23 +203,18 @@ void LLDrawPoolWLSky::renderSkyClouds(F32 camHeightLocal) const { if (gPipeline.canUseWindLightShaders() && gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_CLOUDS)) { - LLGLSLShader* shader = - LLPipeline::sUnderWaterRender ? - &gObjectSimpleWaterProgram : - &gWLCloudProgram; - LLGLEnable blend(GL_BLEND); gGL.setSceneBlendType(LLRender::BT_ALPHA); gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); gGL.getTexUnit(0)->bind(sCloudNoiseTexture); - shader->bind(); + cloud_shader->bind(); /// Render the skydome - renderDome(camHeightLocal, shader); + renderDome(camHeightLocal, cloud_shader); - shader->unbind(); + cloud_shader->unbind(); } } @@ -246,6 +258,53 @@ void LLDrawPoolWLSky::renderHeavenlyBodies() } } +void LLDrawPoolWLSky::renderDeferred(S32 pass) +{ + if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SKY)) + { + return; + } + LLFastTimer ftm(FTM_RENDER_WL_SKY); + + const F32 camHeightLocal = LLWLParamManager::instance()->getDomeOffset() * LLWLParamManager::instance()->getDomeRadius(); + + LLGLSNoFog disableFog; + LLGLDepthTest depth(GL_TRUE, GL_FALSE); + LLGLDisable clip(GL_CLIP_PLANE0); + + gGL.setColorMask(true, false); + + LLGLSquashToFarClip far_clip(glh_get_current_projection()); + + renderSkyHaze(camHeightLocal); + + LLVector3 const & origin = LLViewerCamera::getInstance()->getOrigin(); + glPushMatrix(); + + + glTranslatef(origin.mV[0], origin.mV[1], origin.mV[2]); + + gDeferredStarProgram.bind(); + // *NOTE: have to bind a texture here since register combiners blending in + // renderStars() requires something to be bound and we might as well only + // bind the moon's texture once. + gGL.getTexUnit(0)->bind(gSky.mVOSkyp->mFace[LLVOSky::FACE_MOON]->getTexture()); + + renderHeavenlyBodies(); + + renderStars(); + + gDeferredStarProgram.unbind(); + + glPopMatrix(); + + renderSkyClouds(camHeightLocal); + + gGL.setColorMask(true, true); + //gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + +} + void LLDrawPoolWLSky::render(S32 pass) { if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SKY)) diff --git a/indra/newview/lldrawpoolwlsky.h b/indra/newview/lldrawpoolwlsky.h index 8ca1ebb942..cd15c991ee 100644 --- a/indra/newview/lldrawpoolwlsky.h +++ b/indra/newview/lldrawpoolwlsky.h @@ -44,10 +44,10 @@ public: /*virtual*/ BOOL isDead() { return FALSE; } - /*virtual*/ S32 getNumPostDeferredPasses() { return getNumPasses(); } - /*virtual*/ void beginPostDeferredPass(S32 pass) { beginRenderPass(pass); } - /*virtual*/ void endPostDeferredPass(S32 pass) { endRenderPass(pass); } - /*virtual*/ void renderPostDeferred(S32 pass) { render(pass); } + /*virtual*/ S32 getNumDeferredPasses() { return 1; } + /*virtual*/ void beginDeferredPass(S32 pass); + /*virtual*/ void endDeferredPass(S32 pass); + /*virtual*/ void renderDeferred(S32 pass); /*virtual*/ LLViewerTexture *getDebugTexture(); /*virtual*/ void beginRenderPass( S32 pass ); diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 5398c13c44..9f9e50ad0a 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -364,14 +364,7 @@ void LLFace::setSize(S32 num_vertices, S32 num_indices, bool align) //allocate vertices in blocks of 4 for alignment num_vertices = (num_vertices + 0x3) & ~0x3; } - else - { - if (mDrawablep->getVOVolume()) - { - llerrs << "WTF?" << llendl; - } - } - + if (mGeomCount != num_vertices || mIndicesCount != num_indices) { diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp index 3bdab75acf..d1bff9f423 100644 --- a/indra/newview/llfeaturemanager.cpp +++ b/indra/newview/llfeaturemanager.cpp @@ -1,807 +1,807 @@ -/** - * @file llfeaturemanager.cpp - * @brief LLFeatureManager class implementation - * - * $LicenseInfo:firstyear=2003&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include <iostream> -#include <fstream> - -#include <boost/regex.hpp> - -#include "llfeaturemanager.h" -#include "lldir.h" - -#include "llsys.h" -#include "llgl.h" -#include "llsecondlifeurls.h" - -#include "llappviewer.h" -#include "llhttpclient.h" -#include "llnotificationsutil.h" -#include "llviewercontrol.h" -#include "llworld.h" -#include "lldrawpoolterrain.h" -#include "llviewertexturelist.h" -#include "llversioninfo.h" -#include "llwindow.h" -#include "llui.h" -#include "llcontrol.h" -#include "llboost.h" -#include "llweb.h" - -#if LL_WINDOWS -#include "lldxhardware.h" -#endif - - -#if LL_DARWIN -const char FEATURE_TABLE_FILENAME[] = "featuretable_mac.txt"; -const char FEATURE_TABLE_VER_FILENAME[] = "featuretable_mac.%s.txt"; -#elif LL_LINUX -const char FEATURE_TABLE_FILENAME[] = "featuretable_linux.txt"; -const char FEATURE_TABLE_VER_FILENAME[] = "featuretable_linux.%s.txt"; -#elif LL_SOLARIS -const char FEATURE_TABLE_FILENAME[] = "featuretable_solaris.txt"; -const char FEATURE_TABLE_VER_FILENAME[] = "featuretable_solaris.%s.txt"; -#else -const char FEATURE_TABLE_FILENAME[] = "featuretable%s.txt"; -const char FEATURE_TABLE_VER_FILENAME[] = "featuretable%s.%s.txt"; -#endif - -const char GPU_TABLE_FILENAME[] = "gpu_table.txt"; -const char GPU_TABLE_VER_FILENAME[] = "gpu_table.%s.txt"; - -LLFeatureInfo::LLFeatureInfo(const std::string& name, const BOOL available, const F32 level) - : mValid(TRUE), mName(name), mAvailable(available), mRecommendedLevel(level) -{ -} - -LLFeatureList::LLFeatureList(const std::string& name) - : mName(name) -{ -} - -LLFeatureList::~LLFeatureList() -{ -} - -void LLFeatureList::addFeature(const std::string& name, const BOOL available, const F32 level) -{ - if (mFeatures.count(name)) - { - LL_WARNS("RenderInit") << "LLFeatureList::Attempting to add preexisting feature " << name << LL_ENDL; - } - - LLFeatureInfo fi(name, available, level); - mFeatures[name] = fi; -} - -BOOL LLFeatureList::isFeatureAvailable(const std::string& name) -{ - if (mFeatures.count(name)) - { - return mFeatures[name].mAvailable; - } - - LL_WARNS("RenderInit") << "Feature " << name << " not on feature list!" << LL_ENDL; - - // changing this to TRUE so you have to explicitly disable - // something for it to be disabled - return TRUE; -} - -F32 LLFeatureList::getRecommendedValue(const std::string& name) -{ - if (mFeatures.count(name) && isFeatureAvailable(name)) - { - return mFeatures[name].mRecommendedLevel; - } - - LL_WARNS("RenderInit") << "Feature " << name << " not on feature list or not available!" << LL_ENDL; - return 0; -} - -BOOL LLFeatureList::maskList(LLFeatureList &mask) -{ - //llinfos << "Masking with " << mask.mName << llendl; - // - // Lookup the specified feature mask, and overlay it on top of the - // current feature mask. - // - - LLFeatureInfo mask_fi; - - feature_map_t::iterator feature_it; - for (feature_it = mask.mFeatures.begin(); feature_it != mask.mFeatures.end(); ++feature_it) - { - mask_fi = feature_it->second; - // - // Look for the corresponding feature - // - if (!mFeatures.count(mask_fi.mName)) - { - LL_WARNS("RenderInit") << "Feature " << mask_fi.mName << " in mask not in top level!" << LL_ENDL; - continue; - } - - LLFeatureInfo &cur_fi = mFeatures[mask_fi.mName]; - if (mask_fi.mAvailable && !cur_fi.mAvailable) - { - LL_WARNS("RenderInit") << "Mask attempting to reenabling disabled feature, ignoring " << cur_fi.mName << LL_ENDL; - continue; - } - cur_fi.mAvailable = mask_fi.mAvailable; - cur_fi.mRecommendedLevel = llmin(cur_fi.mRecommendedLevel, mask_fi.mRecommendedLevel); - LL_DEBUGS("RenderInit") << "Feature mask " << mask.mName - << " Feature " << mask_fi.mName - << " Mask: " << mask_fi.mRecommendedLevel - << " Now: " << cur_fi.mRecommendedLevel << LL_ENDL; - } - - LL_DEBUGS("RenderInit") << "After applying mask " << mask.mName << std::endl; - // Will conditionally call dump only if the above message will be logged, thanks - // to it being wrapped by the LL_DEBUGS and LL_ENDL macros. - dump(); - LL_CONT << LL_ENDL; - - return TRUE; -} - -void LLFeatureList::dump() -{ - LL_DEBUGS("RenderInit") << "Feature list: " << mName << LL_ENDL; - LL_DEBUGS("RenderInit") << "--------------" << LL_ENDL; - - LLFeatureInfo fi; - feature_map_t::iterator feature_it; - for (feature_it = mFeatures.begin(); feature_it != mFeatures.end(); ++feature_it) - { - fi = feature_it->second; - LL_DEBUGS("RenderInit") << fi.mName << "\t\t" << fi.mAvailable << ":" << fi.mRecommendedLevel << LL_ENDL; - } - LL_DEBUGS("RenderInit") << LL_ENDL; -} - -LLFeatureList *LLFeatureManager::findMask(const std::string& name) -{ - if (mMaskList.count(name)) - { - return mMaskList[name]; - } - - return NULL; -} - -BOOL LLFeatureManager::maskFeatures(const std::string& name) -{ - LLFeatureList *maskp = findMask(name); - if (!maskp) - { - LL_DEBUGS("RenderInit") << "Unknown feature mask " << name << LL_ENDL; - return FALSE; - } - LL_INFOS("RenderInit") << "Applying GPU Feature list: " << name << LL_ENDL; - return maskList(*maskp); -} - -BOOL LLFeatureManager::loadFeatureTables() -{ - // *TODO - if I or anyone else adds something else to the skipped list - // make this data driven. Put it in the feature table and parse it - // correctly - mSkippedFeatures.insert("RenderAnisotropic"); - mSkippedFeatures.insert("RenderGamma"); - mSkippedFeatures.insert("RenderVBOEnable"); - mSkippedFeatures.insert("RenderFogRatio"); - - // first table is install with app - std::string app_path = gDirUtilp->getAppRODataDir(); - app_path += gDirUtilp->getDirDelimiter(); - - std::string filename; - std::string http_filename; -#if LL_WINDOWS - std::string os_string = LLAppViewer::instance()->getOSInfo().getOSStringSimple(); - if (os_string.find("Microsoft Windows XP") == 0) - { - filename = llformat(FEATURE_TABLE_FILENAME, "_xp"); - http_filename = llformat(FEATURE_TABLE_VER_FILENAME, "_xp", LLVersionInfo::getVersion().c_str()); - } - else - { - filename = llformat(FEATURE_TABLE_FILENAME, ""); - http_filename = llformat(FEATURE_TABLE_VER_FILENAME, "", LLVersionInfo::getVersion().c_str()); - } -#else - filename = FEATURE_TABLE_FILENAME; - http_filename = llformat(FEATURE_TABLE_VER_FILENAME, LLVersionInfo::getVersion().c_str()); -#endif - - app_path += filename; - - - // second table is downloaded with HTTP - std::string http_path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, http_filename); - - // use HTTP table if it exists - std::string path; - if (gDirUtilp->fileExists(http_path)) - { - path = http_path; - } - else - { - path = app_path; - } - - - return parseFeatureTable(path); -} - - -BOOL LLFeatureManager::parseFeatureTable(std::string filename) -{ - llinfos << "Looking for feature table in " << filename << llendl; - - llifstream file; - std::string name; - U32 version; - - file.open(filename); /*Flawfinder: ignore*/ - - if (!file) - { - LL_WARNS("RenderInit") << "Unable to open feature table " << filename << LL_ENDL; - return FALSE; - } - - // Check file version - file >> name; - file >> version; - if (name != "version") - { - LL_WARNS("RenderInit") << filename << " does not appear to be a valid feature table!" << LL_ENDL; - return FALSE; - } - - mTableVersion = version; - - LLFeatureList *flp = NULL; - while (file >> name) - { - char buffer[MAX_STRING]; /*Flawfinder: ignore*/ - - if (name.substr(0,2) == "//") - { - // This is a comment. - file.getline(buffer, MAX_STRING); - continue; - } - - if (name == "list") - { - if (flp) - { - //flp->dump(); - } - // It's a new mask, create it. - file >> name; - if (mMaskList.count(name)) - { - LL_ERRS("RenderInit") << "Overriding mask " << name << ", this is invalid!" << LL_ENDL; - } - - flp = new LLFeatureList(name); - mMaskList[name] = flp; - } - else - { - if (!flp) - { - LL_ERRS("RenderInit") << "Specified parameter before <list> keyword!" << LL_ENDL; - return FALSE; - } - S32 available; - F32 recommended; - file >> available >> recommended; - flp->addFeature(name, available, recommended); - } - } - file.close(); - - return TRUE; -} - -void LLFeatureManager::loadGPUClass() -{ - // defaults - mGPUClass = GPU_CLASS_UNKNOWN; - mGPUString = gGLManager.getRawGLString(); - mGPUSupported = FALSE; - - // first table is in the app dir - std::string app_path = gDirUtilp->getAppRODataDir(); - app_path += gDirUtilp->getDirDelimiter(); - app_path += GPU_TABLE_FILENAME; - - // second table is downloaded with HTTP - std::string http_filename = llformat(GPU_TABLE_VER_FILENAME, LLVersionInfo::getVersion().c_str()); - std::string http_path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, http_filename); - - // use HTTP table if it exists - std::string path; - if (gDirUtilp->fileExists(http_path)) - { - path = http_path; - } - else - { - path = app_path; - } - - parseGPUTable(path); -} - - -void LLFeatureManager::parseGPUTable(std::string filename) -{ - llifstream file; - - file.open(filename); - - if (!file) - { - LL_WARNS("RenderInit") << "Unable to open GPU table: " << filename << "!" << LL_ENDL; - return; - } - - std::string rawRenderer = gGLManager.getRawGLString(); - std::string renderer = rawRenderer; - for (std::string::iterator i = renderer.begin(); i != renderer.end(); ++i) - { - *i = tolower(*i); - } - - bool gpuFound; - U32 lineNumber; - for (gpuFound = false, lineNumber = 0; !gpuFound && !file.eof(); lineNumber++) - { - char buffer[MAX_STRING]; /*Flawfinder: ignore*/ - buffer[0] = 0; - - file.getline(buffer, MAX_STRING); - - if (strlen(buffer) >= 2 && /*Flawfinder: ignore*/ - buffer[0] == '/' && - buffer[1] == '/') - { - // This is a comment. - continue; - } - - if (strlen(buffer) == 0) /*Flawfinder: ignore*/ - { - // This is a blank line - continue; - } - - // setup the tokenizer - std::string buf(buffer); - std::string cls, label, expr, supported; - boost_tokenizer tokens(buf, boost::char_separator<char>("\t\n")); - boost_tokenizer::iterator token_iter = tokens.begin(); - - // grab the label, pseudo regular expression, and class - if(token_iter != tokens.end()) - { - label = *token_iter++; - } - if(token_iter != tokens.end()) - { - expr = *token_iter++; - } - if(token_iter != tokens.end()) - { - cls = *token_iter++; - } - if(token_iter != tokens.end()) - { - supported = *token_iter++; - } - - if (label.empty() || expr.empty() || cls.empty() || supported.empty()) - { - LL_WARNS("RenderInit") << "invald gpu_table.txt:" << lineNumber << ": '" << buffer << "'" << LL_ENDL; - continue; - } - - for (U32 i = 0; i < expr.length(); i++) /*Flawfinder: ignore*/ - { - expr[i] = tolower(expr[i]); - } - - // run the regular expression against the renderer - boost::regex re(expr.c_str()); - if(boost::regex_search(renderer, re)) - { - // if we found it, stop! - gpuFound = true; - mGPUString = label; - mGPUClass = (EGPUClass) strtol(cls.c_str(), NULL, 10); - mGPUSupported = (BOOL) strtol(supported.c_str(), NULL, 10); - } - } - file.close(); - - if ( gpuFound ) - { - LL_INFOS("RenderInit") << "GPU '" << rawRenderer << "' recognized as '" << mGPUString << "'" << LL_ENDL; - if (!mGPUSupported) - { - LL_INFOS("RenderInit") << "GPU '" << mGPUString << "' is not supported." << LL_ENDL; - } - } - else - { - LL_WARNS("RenderInit") << "GPU '" << rawRenderer << "' not recognized" << LL_ENDL; - } -} - -// responder saves table into file -class LLHTTPFeatureTableResponder : public LLHTTPClient::Responder -{ -public: - - LLHTTPFeatureTableResponder(std::string filename) : - mFilename(filename) - { - } - - - virtual void completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - if (isGoodStatus(status)) - { - // write to file - - llinfos << "writing feature table to " << mFilename << llendl; - - S32 file_size = buffer->countAfter(channels.in(), NULL); - if (file_size > 0) - { - // read from buffer - U8* copy_buffer = new U8[file_size]; - buffer->readAfter(channels.in(), NULL, copy_buffer, file_size); - - // write to file - LLAPRFile out(mFilename, LL_APR_WB); - out.write(copy_buffer, file_size); - out.close(); - } - } - - } - -private: - std::string mFilename; -}; - -void fetch_feature_table(std::string table) -{ - const std::string base = gSavedSettings.getString("FeatureManagerHTTPTable"); - -#if LL_WINDOWS - std::string os_string = LLAppViewer::instance()->getOSInfo().getOSStringSimple(); - std::string filename; - if (os_string.find("Microsoft Windows XP") == 0) - { - filename = llformat(table.c_str(), "_xp", LLVersionInfo::getVersion().c_str()); - } - else - { - filename = llformat(table.c_str(), "", LLVersionInfo::getVersion().c_str()); - } -#else - const std::string filename = llformat(table.c_str(), LLVersionInfo::getVersion().c_str()); -#endif - - const std::string url = base + "/" + filename; - - const std::string path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); - - llinfos << "LLFeatureManager fetching " << url << " into " << path << llendl; - - LLHTTPClient::get(url, new LLHTTPFeatureTableResponder(path)); -} - -void fetch_gpu_table(std::string table) -{ - const std::string base = gSavedSettings.getString("FeatureManagerHTTPTable"); - - const std::string filename = llformat(table.c_str(), LLVersionInfo::getVersion().c_str()); - - const std::string url = base + "/" + filename; - - const std::string path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); - - llinfos << "LLFeatureManager fetching " << url << " into " << path << llendl; - - LLHTTPClient::get(url, new LLHTTPFeatureTableResponder(path)); -} - -// fetch table(s) from a website (S3) -void LLFeatureManager::fetchHTTPTables() -{ - fetch_feature_table(FEATURE_TABLE_VER_FILENAME); - fetch_gpu_table(GPU_TABLE_VER_FILENAME); -} - - -void LLFeatureManager::cleanupFeatureTables() -{ - std::for_each(mMaskList.begin(), mMaskList.end(), DeletePairedPointer()); - mMaskList.clear(); -} - -void LLFeatureManager::init() -{ - // load the tables - loadFeatureTables(); - - // get the gpu class - loadGPUClass(); - - // apply the base masks, so we know if anything is disabled - applyBaseMasks(); -} - -void LLFeatureManager::applyRecommendedSettings() -{ - // apply saved settings - // cap the level at 2 (high) - S32 level = llmax(GPU_CLASS_0, llmin(mGPUClass, GPU_CLASS_2)); - - llinfos << "Applying Recommended Features" << llendl; - - setGraphicsLevel(level, false); - gSavedSettings.setU32("RenderQualityPerformance", level); - - // now apply the tweaks to draw distance - // these are double negatives, because feature masks only work by - // downgrading values, so i needed to make a true value go to false - // for certain cards, thus the awkward name, "Disregard..." - if(!gSavedSettings.getBOOL("Disregard96DefaultDrawDistance")) - { - gSavedSettings.setF32("RenderFarClip", 96.0f); - } - else if(!gSavedSettings.getBOOL("Disregard128DefaultDrawDistance")) - { - gSavedSettings.setF32("RenderFarClip", 128.0f); - } -} - -void LLFeatureManager::applyFeatures(bool skipFeatures) -{ - // see featuretable.txt / featuretable_linux.txt / featuretable_mac.txt - -#ifndef LL_RELEASE_FOR_DOWNLOAD - dump(); -#endif - - // scroll through all of these and set their corresponding control value - for(feature_map_t::iterator mIt = mFeatures.begin(); - mIt != mFeatures.end(); - ++mIt) - { - // skip features you want to skip - // do this for when you don't want to change certain settings - if(skipFeatures) - { - if(mSkippedFeatures.find(mIt->first) != mSkippedFeatures.end()) - { - continue; - } - } - - // get the control setting - LLControlVariable* ctrl = gSavedSettings.getControl(mIt->first); - if(ctrl == NULL) - { - llwarns << "AHHH! Control setting " << mIt->first << " does not exist!" << llendl; - continue; - } - - // handle all the different types - if(ctrl->isType(TYPE_BOOLEAN)) - { - gSavedSettings.setBOOL(mIt->first, (BOOL)getRecommendedValue(mIt->first)); - } - else if (ctrl->isType(TYPE_S32)) - { - gSavedSettings.setS32(mIt->first, (S32)getRecommendedValue(mIt->first)); - } - else if (ctrl->isType(TYPE_U32)) - { - gSavedSettings.setU32(mIt->first, (U32)getRecommendedValue(mIt->first)); - } - else if (ctrl->isType(TYPE_F32)) - { - gSavedSettings.setF32(mIt->first, (F32)getRecommendedValue(mIt->first)); - } - else - { - llwarns << "AHHH! Control variable is not a numeric type!" << llendl; - } - } -} - -void LLFeatureManager::setGraphicsLevel(S32 level, bool skipFeatures) -{ - applyBaseMasks(); - - switch (level) - { - case 0: - maskFeatures("Low"); - break; - case 1: - maskFeatures("Mid"); - break; - case 2: - maskFeatures("High"); - break; - case 3: - maskFeatures("Ultra"); - break; - default: - maskFeatures("Low"); - break; - } - - applyFeatures(skipFeatures); -} - -void LLFeatureManager::applyBaseMasks() -{ - // reapply masks - mFeatures.clear(); - - LLFeatureList* maskp = findMask("all"); - if(maskp == NULL) - { - LL_WARNS("RenderInit") << "AHH! No \"all\" in feature table!" << LL_ENDL; - return; - } - - mFeatures = maskp->getFeatures(); - - // mask class - if (mGPUClass >= 0 && mGPUClass < 4) - { - const char* class_table[] = - { - "Class0", - "Class1", - "Class2", - "Class3" - }; - - LL_INFOS("RenderInit") << "Setting GPU Class to " << class_table[mGPUClass] << LL_ENDL; - maskFeatures(class_table[mGPUClass]); - } - else - { - LL_INFOS("RenderInit") << "Setting GPU Class to Unknown" << LL_ENDL; - maskFeatures("Unknown"); - } - - // now all those wacky ones - if (!gGLManager.mHasFragmentShader) - { - maskFeatures("NoPixelShaders"); - } - if (!gGLManager.mHasVertexShader) - { - maskFeatures("NoVertexShaders"); - } - if (gGLManager.mIsNVIDIA) - { - maskFeatures("NVIDIA"); - } - if (gGLManager.mIsGF2or4MX) - { - maskFeatures("GeForce2"); - } - if (gGLManager.mIsATI) - { - maskFeatures("ATI"); - } - if (gGLManager.mHasATIMemInfo && gGLManager.mVRAM < 256) - { - maskFeatures("ATIVramLT256"); - } - if (gGLManager.mATIOldDriver) - { - maskFeatures("ATIOldDriver"); - } - if (gGLManager.mIsGFFX) - { - maskFeatures("GeForceFX"); - } - if (gGLManager.mIsIntel) - { - maskFeatures("Intel"); - } - if (gGLManager.mGLVersion < 1.5f) - { - maskFeatures("OpenGLPre15"); - } - if (gGLManager.mGLVersion < 3.f) - { - maskFeatures("OpenGLPre30"); - } - if (gGLManager.mNumTextureUnits <= 8) - { - maskFeatures("TexUnit8orLess"); - } - - // now mask by gpu string - // Replaces ' ' with '_' in mGPUString to deal with inability for parser to handle spaces - std::string gpustr = mGPUString; - for (std::string::iterator iter = gpustr.begin(); iter != gpustr.end(); ++iter) - { - if (*iter == ' ') - { - *iter = '_'; - } - } - - //llinfos << "Masking features from gpu table match: " << gpustr << llendl; - maskFeatures(gpustr); - - // now mask cpu type ones - if (gSysMemory.getPhysicalMemoryClamped() <= 256*1024*1024) - { - maskFeatures("RAM256MB"); - } - -#if LL_SOLARIS && defined(__sparc) // even low MHz SPARCs are fast -#error The 800 is hinky. Would something like a LL_MIN_MHZ make more sense here? - if (gSysCPU.getMHz() < 800) -#else - if (gSysCPU.getMHz() < 1100) -#endif - { - maskFeatures("CPUSlow"); - } - - if (isSafe()) - { - maskFeatures("safe"); - } -} +/**
+ * @file llfeaturemanager.cpp
+ * @brief LLFeatureManager class implementation
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <iostream>
+#include <fstream>
+
+#include <boost/regex.hpp>
+
+#include "llfeaturemanager.h"
+#include "lldir.h"
+
+#include "llsys.h"
+#include "llgl.h"
+#include "llsecondlifeurls.h"
+
+#include "llappviewer.h"
+#include "llhttpclient.h"
+#include "llnotificationsutil.h"
+#include "llviewercontrol.h"
+#include "llworld.h"
+#include "lldrawpoolterrain.h"
+#include "llviewertexturelist.h"
+#include "llversioninfo.h"
+#include "llwindow.h"
+#include "llui.h"
+#include "llcontrol.h"
+#include "llboost.h"
+#include "llweb.h"
+
+#if LL_WINDOWS
+#include "lldxhardware.h"
+#endif
+
+
+#if LL_DARWIN
+const char FEATURE_TABLE_FILENAME[] = "featuretable_mac.txt";
+const char FEATURE_TABLE_VER_FILENAME[] = "featuretable_mac.%s.txt";
+#elif LL_LINUX
+const char FEATURE_TABLE_FILENAME[] = "featuretable_linux.txt";
+const char FEATURE_TABLE_VER_FILENAME[] = "featuretable_linux.%s.txt";
+#elif LL_SOLARIS
+const char FEATURE_TABLE_FILENAME[] = "featuretable_solaris.txt";
+const char FEATURE_TABLE_VER_FILENAME[] = "featuretable_solaris.%s.txt";
+#else
+const char FEATURE_TABLE_FILENAME[] = "featuretable%s.txt";
+const char FEATURE_TABLE_VER_FILENAME[] = "featuretable%s.%s.txt";
+#endif
+
+const char GPU_TABLE_FILENAME[] = "gpu_table.txt";
+const char GPU_TABLE_VER_FILENAME[] = "gpu_table.%s.txt";
+
+LLFeatureInfo::LLFeatureInfo(const std::string& name, const BOOL available, const F32 level)
+ : mValid(TRUE), mName(name), mAvailable(available), mRecommendedLevel(level)
+{
+}
+
+LLFeatureList::LLFeatureList(const std::string& name)
+ : mName(name)
+{
+}
+
+LLFeatureList::~LLFeatureList()
+{
+}
+
+void LLFeatureList::addFeature(const std::string& name, const BOOL available, const F32 level)
+{
+ if (mFeatures.count(name))
+ {
+ LL_WARNS("RenderInit") << "LLFeatureList::Attempting to add preexisting feature " << name << LL_ENDL;
+ }
+
+ LLFeatureInfo fi(name, available, level);
+ mFeatures[name] = fi;
+}
+
+BOOL LLFeatureList::isFeatureAvailable(const std::string& name)
+{
+ if (mFeatures.count(name))
+ {
+ return mFeatures[name].mAvailable;
+ }
+
+ LL_WARNS("RenderInit") << "Feature " << name << " not on feature list!" << LL_ENDL;
+
+ // changing this to TRUE so you have to explicitly disable
+ // something for it to be disabled
+ return TRUE;
+}
+
+F32 LLFeatureList::getRecommendedValue(const std::string& name)
+{
+ if (mFeatures.count(name) && isFeatureAvailable(name))
+ {
+ return mFeatures[name].mRecommendedLevel;
+ }
+
+ LL_WARNS("RenderInit") << "Feature " << name << " not on feature list or not available!" << LL_ENDL;
+ return 0;
+}
+
+BOOL LLFeatureList::maskList(LLFeatureList &mask)
+{
+ //llinfos << "Masking with " << mask.mName << llendl;
+ //
+ // Lookup the specified feature mask, and overlay it on top of the
+ // current feature mask.
+ //
+
+ LLFeatureInfo mask_fi;
+
+ feature_map_t::iterator feature_it;
+ for (feature_it = mask.mFeatures.begin(); feature_it != mask.mFeatures.end(); ++feature_it)
+ {
+ mask_fi = feature_it->second;
+ //
+ // Look for the corresponding feature
+ //
+ if (!mFeatures.count(mask_fi.mName))
+ {
+ LL_WARNS("RenderInit") << "Feature " << mask_fi.mName << " in mask not in top level!" << LL_ENDL;
+ continue;
+ }
+
+ LLFeatureInfo &cur_fi = mFeatures[mask_fi.mName];
+ if (mask_fi.mAvailable && !cur_fi.mAvailable)
+ {
+ LL_WARNS("RenderInit") << "Mask attempting to reenabling disabled feature, ignoring " << cur_fi.mName << LL_ENDL;
+ continue;
+ }
+ cur_fi.mAvailable = mask_fi.mAvailable;
+ cur_fi.mRecommendedLevel = llmin(cur_fi.mRecommendedLevel, mask_fi.mRecommendedLevel);
+ LL_DEBUGS("RenderInit") << "Feature mask " << mask.mName
+ << " Feature " << mask_fi.mName
+ << " Mask: " << mask_fi.mRecommendedLevel
+ << " Now: " << cur_fi.mRecommendedLevel << LL_ENDL;
+ }
+
+ LL_DEBUGS("RenderInit") << "After applying mask " << mask.mName << std::endl;
+ // Will conditionally call dump only if the above message will be logged, thanks
+ // to it being wrapped by the LL_DEBUGS and LL_ENDL macros.
+ dump();
+ LL_CONT << LL_ENDL;
+
+ return TRUE;
+}
+
+void LLFeatureList::dump()
+{
+ LL_DEBUGS("RenderInit") << "Feature list: " << mName << LL_ENDL;
+ LL_DEBUGS("RenderInit") << "--------------" << LL_ENDL;
+
+ LLFeatureInfo fi;
+ feature_map_t::iterator feature_it;
+ for (feature_it = mFeatures.begin(); feature_it != mFeatures.end(); ++feature_it)
+ {
+ fi = feature_it->second;
+ LL_DEBUGS("RenderInit") << fi.mName << "\t\t" << fi.mAvailable << ":" << fi.mRecommendedLevel << LL_ENDL;
+ }
+ LL_DEBUGS("RenderInit") << LL_ENDL;
+}
+
+LLFeatureList *LLFeatureManager::findMask(const std::string& name)
+{
+ if (mMaskList.count(name))
+ {
+ return mMaskList[name];
+ }
+
+ return NULL;
+}
+
+BOOL LLFeatureManager::maskFeatures(const std::string& name)
+{
+ LLFeatureList *maskp = findMask(name);
+ if (!maskp)
+ {
+ LL_DEBUGS("RenderInit") << "Unknown feature mask " << name << LL_ENDL;
+ return FALSE;
+ }
+ LL_INFOS("RenderInit") << "Applying GPU Feature list: " << name << LL_ENDL;
+ return maskList(*maskp);
+}
+
+BOOL LLFeatureManager::loadFeatureTables()
+{
+ // *TODO - if I or anyone else adds something else to the skipped list
+ // make this data driven. Put it in the feature table and parse it
+ // correctly
+ mSkippedFeatures.insert("RenderAnisotropic");
+ mSkippedFeatures.insert("RenderGamma");
+ mSkippedFeatures.insert("RenderVBOEnable");
+ mSkippedFeatures.insert("RenderFogRatio");
+
+ // first table is install with app
+ std::string app_path = gDirUtilp->getAppRODataDir();
+ app_path += gDirUtilp->getDirDelimiter();
+
+ std::string filename;
+ std::string http_filename;
+#if LL_WINDOWS
+ std::string os_string = LLAppViewer::instance()->getOSInfo().getOSStringSimple();
+ if (os_string.find("Microsoft Windows XP") == 0)
+ {
+ filename = llformat(FEATURE_TABLE_FILENAME, "_xp");
+ http_filename = llformat(FEATURE_TABLE_VER_FILENAME, "_xp", LLVersionInfo::getVersion().c_str());
+ }
+ else
+ {
+ filename = llformat(FEATURE_TABLE_FILENAME, "");
+ http_filename = llformat(FEATURE_TABLE_VER_FILENAME, "", LLVersionInfo::getVersion().c_str());
+ }
+#else
+ filename = FEATURE_TABLE_FILENAME;
+ http_filename = llformat(FEATURE_TABLE_VER_FILENAME, LLVersionInfo::getVersion().c_str());
+#endif
+
+ app_path += filename;
+
+
+ // second table is downloaded with HTTP
+ std::string http_path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, http_filename);
+
+ // use HTTP table if it exists
+ std::string path;
+ if (gDirUtilp->fileExists(http_path))
+ {
+ path = http_path;
+ }
+ else
+ {
+ path = app_path;
+ }
+
+
+ return parseFeatureTable(path);
+}
+
+
+BOOL LLFeatureManager::parseFeatureTable(std::string filename)
+{
+ llinfos << "Looking for feature table in " << filename << llendl;
+
+ llifstream file;
+ std::string name;
+ U32 version;
+
+ file.open(filename); /*Flawfinder: ignore*/
+
+ if (!file)
+ {
+ LL_WARNS("RenderInit") << "Unable to open feature table " << filename << LL_ENDL;
+ return FALSE;
+ }
+
+ // Check file version
+ file >> name;
+ file >> version;
+ if (name != "version")
+ {
+ LL_WARNS("RenderInit") << filename << " does not appear to be a valid feature table!" << LL_ENDL;
+ return FALSE;
+ }
+
+ mTableVersion = version;
+
+ LLFeatureList *flp = NULL;
+ while (file >> name)
+ {
+ char buffer[MAX_STRING]; /*Flawfinder: ignore*/
+
+ if (name.substr(0,2) == "//")
+ {
+ // This is a comment.
+ file.getline(buffer, MAX_STRING);
+ continue;
+ }
+
+ if (name == "list")
+ {
+ if (flp)
+ {
+ //flp->dump();
+ }
+ // It's a new mask, create it.
+ file >> name;
+ if (mMaskList.count(name))
+ {
+ LL_ERRS("RenderInit") << "Overriding mask " << name << ", this is invalid!" << LL_ENDL;
+ }
+
+ flp = new LLFeatureList(name);
+ mMaskList[name] = flp;
+ }
+ else
+ {
+ if (!flp)
+ {
+ LL_ERRS("RenderInit") << "Specified parameter before <list> keyword!" << LL_ENDL;
+ return FALSE;
+ }
+ S32 available;
+ F32 recommended;
+ file >> available >> recommended;
+ flp->addFeature(name, available, recommended);
+ }
+ }
+ file.close();
+
+ return TRUE;
+}
+
+void LLFeatureManager::loadGPUClass()
+{
+ // defaults
+ mGPUClass = GPU_CLASS_UNKNOWN;
+ mGPUString = gGLManager.getRawGLString();
+ mGPUSupported = FALSE;
+
+ // first table is in the app dir
+ std::string app_path = gDirUtilp->getAppRODataDir();
+ app_path += gDirUtilp->getDirDelimiter();
+ app_path += GPU_TABLE_FILENAME;
+
+ // second table is downloaded with HTTP
+ std::string http_filename = llformat(GPU_TABLE_VER_FILENAME, LLVersionInfo::getVersion().c_str());
+ std::string http_path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, http_filename);
+
+ // use HTTP table if it exists
+ std::string path;
+ if (gDirUtilp->fileExists(http_path))
+ {
+ path = http_path;
+ }
+ else
+ {
+ path = app_path;
+ }
+
+ parseGPUTable(path);
+}
+
+
+void LLFeatureManager::parseGPUTable(std::string filename)
+{
+ llifstream file;
+
+ file.open(filename);
+
+ if (!file)
+ {
+ LL_WARNS("RenderInit") << "Unable to open GPU table: " << filename << "!" << LL_ENDL;
+ return;
+ }
+
+ std::string rawRenderer = gGLManager.getRawGLString();
+ std::string renderer = rawRenderer;
+ for (std::string::iterator i = renderer.begin(); i != renderer.end(); ++i)
+ {
+ *i = tolower(*i);
+ }
+
+ bool gpuFound;
+ U32 lineNumber;
+ for (gpuFound = false, lineNumber = 0; !gpuFound && !file.eof(); lineNumber++)
+ {
+ char buffer[MAX_STRING]; /*Flawfinder: ignore*/
+ buffer[0] = 0;
+
+ file.getline(buffer, MAX_STRING);
+
+ if (strlen(buffer) >= 2 && /*Flawfinder: ignore*/
+ buffer[0] == '/' &&
+ buffer[1] == '/')
+ {
+ // This is a comment.
+ continue;
+ }
+
+ if (strlen(buffer) == 0) /*Flawfinder: ignore*/
+ {
+ // This is a blank line
+ continue;
+ }
+
+ // setup the tokenizer
+ std::string buf(buffer);
+ std::string cls, label, expr, supported;
+ boost_tokenizer tokens(buf, boost::char_separator<char>("\t\n"));
+ boost_tokenizer::iterator token_iter = tokens.begin();
+
+ // grab the label, pseudo regular expression, and class
+ if(token_iter != tokens.end())
+ {
+ label = *token_iter++;
+ }
+ if(token_iter != tokens.end())
+ {
+ expr = *token_iter++;
+ }
+ if(token_iter != tokens.end())
+ {
+ cls = *token_iter++;
+ }
+ if(token_iter != tokens.end())
+ {
+ supported = *token_iter++;
+ }
+
+ if (label.empty() || expr.empty() || cls.empty() || supported.empty())
+ {
+ LL_WARNS("RenderInit") << "invald gpu_table.txt:" << lineNumber << ": '" << buffer << "'" << LL_ENDL;
+ continue;
+ }
+
+ for (U32 i = 0; i < expr.length(); i++) /*Flawfinder: ignore*/
+ {
+ expr[i] = tolower(expr[i]);
+ }
+
+ // run the regular expression against the renderer
+ boost::regex re(expr.c_str());
+ if(boost::regex_search(renderer, re))
+ {
+ // if we found it, stop!
+ gpuFound = true;
+ mGPUString = label;
+ mGPUClass = (EGPUClass) strtol(cls.c_str(), NULL, 10);
+ mGPUSupported = (BOOL) strtol(supported.c_str(), NULL, 10);
+ }
+ }
+ file.close();
+
+ if ( gpuFound )
+ {
+ LL_INFOS("RenderInit") << "GPU '" << rawRenderer << "' recognized as '" << mGPUString << "'" << LL_ENDL;
+ if (!mGPUSupported)
+ {
+ LL_INFOS("RenderInit") << "GPU '" << mGPUString << "' is not supported." << LL_ENDL;
+ }
+ }
+ else
+ {
+ LL_WARNS("RenderInit") << "GPU '" << rawRenderer << "' not recognized" << LL_ENDL;
+ }
+}
+
+// responder saves table into file
+class LLHTTPFeatureTableResponder : public LLHTTPClient::Responder
+{
+public:
+
+ LLHTTPFeatureTableResponder(std::string filename) :
+ mFilename(filename)
+ {
+ }
+
+
+ virtual void completedRaw(U32 status, const std::string& reason,
+ const LLChannelDescriptors& channels,
+ const LLIOPipe::buffer_ptr_t& buffer)
+ {
+ if (isGoodStatus(status))
+ {
+ // write to file
+
+ llinfos << "writing feature table to " << mFilename << llendl;
+
+ S32 file_size = buffer->countAfter(channels.in(), NULL);
+ if (file_size > 0)
+ {
+ // read from buffer
+ U8* copy_buffer = new U8[file_size];
+ buffer->readAfter(channels.in(), NULL, copy_buffer, file_size);
+
+ // write to file
+ LLAPRFile out(mFilename, LL_APR_WB);
+ out.write(copy_buffer, file_size);
+ out.close();
+ }
+ }
+
+ }
+
+private:
+ std::string mFilename;
+};
+
+void fetch_feature_table(std::string table)
+{
+ const std::string base = gSavedSettings.getString("FeatureManagerHTTPTable");
+
+#if LL_WINDOWS
+ std::string os_string = LLAppViewer::instance()->getOSInfo().getOSStringSimple();
+ std::string filename;
+ if (os_string.find("Microsoft Windows XP") == 0)
+ {
+ filename = llformat(table.c_str(), "_xp", LLVersionInfo::getVersion().c_str());
+ }
+ else
+ {
+ filename = llformat(table.c_str(), "", LLVersionInfo::getVersion().c_str());
+ }
+#else
+ const std::string filename = llformat(table.c_str(), LLVersionInfo::getVersion().c_str());
+#endif
+
+ const std::string url = base + "/" + filename;
+
+ const std::string path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename);
+
+ llinfos << "LLFeatureManager fetching " << url << " into " << path << llendl;
+
+ LLHTTPClient::get(url, new LLHTTPFeatureTableResponder(path));
+}
+
+void fetch_gpu_table(std::string table)
+{
+ const std::string base = gSavedSettings.getString("FeatureManagerHTTPTable");
+
+ const std::string filename = llformat(table.c_str(), LLVersionInfo::getVersion().c_str());
+
+ const std::string url = base + "/" + filename;
+
+ const std::string path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename);
+
+ llinfos << "LLFeatureManager fetching " << url << " into " << path << llendl;
+
+ LLHTTPClient::get(url, new LLHTTPFeatureTableResponder(path));
+}
+
+// fetch table(s) from a website (S3)
+void LLFeatureManager::fetchHTTPTables()
+{
+ fetch_feature_table(FEATURE_TABLE_VER_FILENAME);
+ fetch_gpu_table(GPU_TABLE_VER_FILENAME);
+}
+
+
+void LLFeatureManager::cleanupFeatureTables()
+{
+ std::for_each(mMaskList.begin(), mMaskList.end(), DeletePairedPointer());
+ mMaskList.clear();
+}
+
+void LLFeatureManager::init()
+{
+ // load the tables
+ loadFeatureTables();
+
+ // get the gpu class
+ loadGPUClass();
+
+ // apply the base masks, so we know if anything is disabled
+ applyBaseMasks();
+}
+
+void LLFeatureManager::applyRecommendedSettings()
+{
+ // apply saved settings
+ // cap the level at 2 (high)
+ S32 level = llmax(GPU_CLASS_0, llmin(mGPUClass, GPU_CLASS_2));
+
+ llinfos << "Applying Recommended Features" << llendl;
+
+ setGraphicsLevel(level, false);
+ gSavedSettings.setU32("RenderQualityPerformance", level);
+
+ // now apply the tweaks to draw distance
+ // these are double negatives, because feature masks only work by
+ // downgrading values, so i needed to make a true value go to false
+ // for certain cards, thus the awkward name, "Disregard..."
+ if(!gSavedSettings.getBOOL("Disregard96DefaultDrawDistance"))
+ {
+ gSavedSettings.setF32("RenderFarClip", 96.0f);
+ }
+ else if(!gSavedSettings.getBOOL("Disregard128DefaultDrawDistance"))
+ {
+ gSavedSettings.setF32("RenderFarClip", 128.0f);
+ }
+}
+
+void LLFeatureManager::applyFeatures(bool skipFeatures)
+{
+ // see featuretable.txt / featuretable_linux.txt / featuretable_mac.txt
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ dump();
+#endif
+
+ // scroll through all of these and set their corresponding control value
+ for(feature_map_t::iterator mIt = mFeatures.begin();
+ mIt != mFeatures.end();
+ ++mIt)
+ {
+ // skip features you want to skip
+ // do this for when you don't want to change certain settings
+ if(skipFeatures)
+ {
+ if(mSkippedFeatures.find(mIt->first) != mSkippedFeatures.end())
+ {
+ continue;
+ }
+ }
+
+ // get the control setting
+ LLControlVariable* ctrl = gSavedSettings.getControl(mIt->first);
+ if(ctrl == NULL)
+ {
+ llwarns << "AHHH! Control setting " << mIt->first << " does not exist!" << llendl;
+ continue;
+ }
+
+ // handle all the different types
+ if(ctrl->isType(TYPE_BOOLEAN))
+ {
+ gSavedSettings.setBOOL(mIt->first, (BOOL)getRecommendedValue(mIt->first));
+ }
+ else if (ctrl->isType(TYPE_S32))
+ {
+ gSavedSettings.setS32(mIt->first, (S32)getRecommendedValue(mIt->first));
+ }
+ else if (ctrl->isType(TYPE_U32))
+ {
+ gSavedSettings.setU32(mIt->first, (U32)getRecommendedValue(mIt->first));
+ }
+ else if (ctrl->isType(TYPE_F32))
+ {
+ gSavedSettings.setF32(mIt->first, (F32)getRecommendedValue(mIt->first));
+ }
+ else
+ {
+ llwarns << "AHHH! Control variable is not a numeric type!" << llendl;
+ }
+ }
+}
+
+void LLFeatureManager::setGraphicsLevel(S32 level, bool skipFeatures)
+{
+ applyBaseMasks();
+
+ switch (level)
+ {
+ case 0:
+ maskFeatures("Low");
+ break;
+ case 1:
+ maskFeatures("Mid");
+ break;
+ case 2:
+ maskFeatures("High");
+ break;
+ case 3:
+ maskFeatures("Ultra");
+ break;
+ default:
+ maskFeatures("Low");
+ break;
+ }
+
+ applyFeatures(skipFeatures);
+}
+
+void LLFeatureManager::applyBaseMasks()
+{
+ // reapply masks
+ mFeatures.clear();
+
+ LLFeatureList* maskp = findMask("all");
+ if(maskp == NULL)
+ {
+ LL_WARNS("RenderInit") << "AHH! No \"all\" in feature table!" << LL_ENDL;
+ return;
+ }
+
+ mFeatures = maskp->getFeatures();
+
+ // mask class
+ if (mGPUClass >= 0 && mGPUClass < 4)
+ {
+ const char* class_table[] =
+ {
+ "Class0",
+ "Class1",
+ "Class2",
+ "Class3"
+ };
+
+ LL_INFOS("RenderInit") << "Setting GPU Class to " << class_table[mGPUClass] << LL_ENDL;
+ maskFeatures(class_table[mGPUClass]);
+ }
+ else
+ {
+ LL_INFOS("RenderInit") << "Setting GPU Class to Unknown" << LL_ENDL;
+ maskFeatures("Unknown");
+ }
+
+ // now all those wacky ones
+ if (!gGLManager.mHasFragmentShader)
+ {
+ maskFeatures("NoPixelShaders");
+ }
+ if (!gGLManager.mHasVertexShader)
+ {
+ maskFeatures("NoVertexShaders");
+ }
+ if (gGLManager.mIsNVIDIA)
+ {
+ maskFeatures("NVIDIA");
+ }
+ if (gGLManager.mIsGF2or4MX)
+ {
+ maskFeatures("GeForce2");
+ }
+ if (gGLManager.mIsATI)
+ {
+ maskFeatures("ATI");
+ }
+ if (gGLManager.mHasATIMemInfo && gGLManager.mVRAM < 256)
+ {
+ maskFeatures("ATIVramLT256");
+ }
+ if (gGLManager.mATIOldDriver)
+ {
+ maskFeatures("ATIOldDriver");
+ }
+ if (gGLManager.mIsGFFX)
+ {
+ maskFeatures("GeForceFX");
+ }
+ if (gGLManager.mIsIntel)
+ {
+ maskFeatures("Intel");
+ }
+ if (gGLManager.mGLVersion < 1.5f)
+ {
+ maskFeatures("OpenGLPre15");
+ }
+ if (gGLManager.mGLVersion < 3.f)
+ {
+ maskFeatures("OpenGLPre30");
+ }
+ if (gGLManager.mNumTextureImageUnits <= 8)
+ {
+ maskFeatures("TexUnit8orLess");
+ }
+
+ // now mask by gpu string
+ // Replaces ' ' with '_' in mGPUString to deal with inability for parser to handle spaces
+ std::string gpustr = mGPUString;
+ for (std::string::iterator iter = gpustr.begin(); iter != gpustr.end(); ++iter)
+ {
+ if (*iter == ' ')
+ {
+ *iter = '_';
+ }
+ }
+
+ //llinfos << "Masking features from gpu table match: " << gpustr << llendl;
+ maskFeatures(gpustr);
+
+ // now mask cpu type ones
+ if (gSysMemory.getPhysicalMemoryClamped() <= 256*1024*1024)
+ {
+ maskFeatures("RAM256MB");
+ }
+
+#if LL_SOLARIS && defined(__sparc) // even low MHz SPARCs are fast
+#error The 800 is hinky. Would something like a LL_MIN_MHZ make more sense here?
+ if (gSysCPU.getMHz() < 800)
+#else
+ if (gSysCPU.getMHz() < 1100)
+#endif
+ {
+ maskFeatures("CPUSlow");
+ }
+
+ if (isSafe())
+ {
+ maskFeatures("safe");
+ }
+}
diff --git a/indra/newview/llfirstuse.cpp b/indra/newview/llfirstuse.cpp index 2c4153688a..a9f52282a5 100644 --- a/indra/newview/llfirstuse.cpp +++ b/indra/newview/llfirstuse.cpp @@ -131,7 +131,7 @@ void LLFirstUse::notMoving(bool enable) // static void LLFirstUse::viewPopup(bool enable) { - firstUseNotification("FirstViewPopup", enable, "HintView", LLSD(), LLSD().with("target", "view_popup").with("direction", "right")); +// firstUseNotification("FirstViewPopup", enable, "HintView", LLSD(), LLSD().with("target", "view_popup").with("direction", "right")); } // static diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index e8da1aa42c..3bddd020e1 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -98,7 +98,8 @@ #include "llvfile.h" #include "llvfs.h" #include "llcallbacklist.h" - +#include "llviewerobjectlist.h" +#include "llanimationstates.h" #include "glod/glod.h" //static @@ -313,7 +314,7 @@ BOOL LLFloaterModelPreview::postBuild() childSetCommitCallback("pelvis_offset", onPelvisOffsetCommit, this); childSetCommitCallback("lod_file_or_limit", refresh, this); - childSetCommitCallback("physics_load_radio", refresh, this); + childSetCommitCallback("physics_load_radio", onPhysicsLoadRadioCommit, this); //childSetCommitCallback("physics_optimize", refresh, this); //childSetCommitCallback("physics_use_hull", refresh, this); @@ -381,12 +382,6 @@ LLFloaterModelPreview::~LLFloaterModelPreview() { sInstance = NULL; - if ( mModelPreview && mModelPreview->getResetJointFlag() ) - { - gAgentAvatarp->resetJointPositions(); - } - - if ( mModelPreview ) { delete mModelPreview; @@ -475,6 +470,29 @@ void LLFloaterModelPreview::onPelvisOffsetCommit( LLUICtrl*, void* userdata ) } //static +void LLFloaterModelPreview::onPhysicsLoadRadioCommit( LLUICtrl*, void *userdata) +{ + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + if (fmp) + { + if (fmp->childGetValue("physics_use_lod").asBoolean()) + { + onPhysicsUseLOD(NULL,NULL); + } + if (fmp->childGetValue("physics_load_from_file").asBoolean()) + { + + } + LLModelPreview *model_preview = fmp->mModelPreview; + if (model_preview) + { + model_preview->refresh(); + model_preview->updateStatusMessages(); + } + } +} + +//static void LLFloaterModelPreview::onUploadJointsCommit(LLUICtrl*,void* userdata) { LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata; @@ -1577,7 +1595,7 @@ bool LLModelLoader::doLoadModel() { //llinfos<<"joint "<<lookingForJoint.c_str()<<llendl; LLMatrix4 jointTransform = mJointList[lookingForJoint]; - LLJoint* pJoint = gAgentAvatarp->getJoint( lookingForJoint ); + LLJoint* pJoint = mPreview->getPreviewAvatar()->getJoint( lookingForJoint ); if ( pJoint ) { pJoint->storeCurrentXform( jointTransform.getTranslation() ); @@ -1639,7 +1657,7 @@ bool LLModelLoader::doLoadModel() { if (pos.getCount() <= j+2) { - llerrs << "WTF?" << llendl; + llerrs << "Invalid position array size." << llendl; } LLVector3 v(pos[j], pos[j+1], pos[j+2]); @@ -2301,8 +2319,11 @@ void LLModelLoader::processElement(daeElement* element) { domFloat3 dom_value = scale->getValue(); + + LLVector3 scale_vector = LLVector3(dom_value[0], dom_value[1], dom_value[2]); + scale_vector.abs(); // Set all values positive, since we don't currently support mirrored meshes LLMatrix4 scaling; - scaling.initScale(LLVector3(dom_value[0], dom_value[1], dom_value[2])); + scaling.initScale(scale_vector); scaling *= mTransform; mTransform = scaling; @@ -2597,6 +2618,7 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) mLoading = false; mLoadState = LLModelLoader::STARTING; mGroup = 0; + mLODFrozen = false; mBuildShareTolerance = 0.f; mBuildQueueMode = GLOD_QUEUE_GREEDY; mBuildBorderMode = GLOD_BORDER_UNLOCK; @@ -2605,6 +2627,13 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) for (U32 i = 0; i < LLModel::NUM_LODS; ++i) { mRequestedTriangleCount[i] = 0; + mRequestedCreaseAngle[i] = -1.f; + mRequestedLoDMode[i] = 0; + mRequestedErrorThreshold[i] = 0.f; + mRequestedBuildOperator[i] = 0; + mRequestedQueueMode[i] = 0; + mRequestedBorderMode[i] = 0; + mRequestedShareTolerance[i] = 0.f; } mViewOption["show_textures"] = false; @@ -2649,6 +2678,8 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) mMasterLegacyJointList.push_front("mHipLeft"); mMasterLegacyJointList.push_front("mKneeLeft"); mMasterLegacyJointList.push_front("mFootLeft"); + + createPreviewAvatar(); } LLModelPreview::~LLModelPreview() @@ -2702,7 +2733,7 @@ U32 LLModelPreview::calcResourceCost() if ( mFMP && mFMP->childGetValue("upload_joints").asBoolean() ) { - gAgentAvatarp->setPelvisOffset( mPelvisZOffset ); + getPreviewAvatar()->setPelvisOffset( mPelvisZOffset ); } F32 streaming_cost = 0.f; @@ -3247,6 +3278,8 @@ void LLModelPreview::generateNormals() F32 angle_cutoff = mFMP->childGetValue("crease_angle").asReal(); + mRequestedCreaseAngle[which_lod] = angle_cutoff; + angle_cutoff *= DEG_TO_RAD; if (which_lod == 3 && !mBaseModel.empty()) @@ -3266,7 +3299,7 @@ void LLModelPreview::generateNormals() mVertexBuffer[which_lod].clear(); refresh(); - + updateStatusMessages(); } void LLModelPreview::clearMaterials() @@ -3342,6 +3375,7 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim { lod_mode = iface->getFirstSelectedIndex(); } + mRequestedLoDMode[mPreviewLOD] = lod_mode; F32 lod_error_threshold = mFMP->childGetValue("lod_error_threshold").asReal(); @@ -3365,6 +3399,7 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim { build_operator = iface->getFirstSelectedIndex(); } + mRequestedBuildOperator[mPreviewLOD] = build_operator; if (build_operator == 0) { @@ -3381,6 +3416,7 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim { queue_mode = iface->getFirstSelectedIndex(); } + mRequestedQueueMode[mPreviewLOD] = queue_mode; if (queue_mode == 0) { @@ -3402,6 +3438,7 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim { border_mode = iface->getFirstSelectedIndex(); } + mRequestedBorderMode[mPreviewLOD] = border_mode; if (border_mode == 0) { @@ -3437,6 +3474,7 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim mBuildShareTolerance = share_tolerance; object_dirty = true; } + mRequestedShareTolerance[mPreviewLOD] = share_tolerance; if (mGroup == 0) { @@ -3545,6 +3583,7 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim U32 submeshes = 0; mRequestedTriangleCount[lod] = triangle_count; + mRequestedErrorThreshold[lod] = lod_error_threshold; glodGroupParameteri(mGroup, GLOD_ADAPT_MODE, lod_mode); stop_gloderror(); @@ -3832,6 +3871,18 @@ void LLModelPreview::updateStatusMessages() } } + + //make sure no hulls have more than 256 points in them + for (U32 i = 0; upload_ok && i < mModel[LLModel::LOD_PHYSICS].size(); ++i) + { + LLModel* mdl = mModel[LLModel::LOD_PHYSICS][i]; + + for (U32 j = 0; upload_ok && j < mdl->mPhysics.mHull.size(); ++j) + { + upload_ok = upload_ok && mdl->mPhysics.mHull[i].size() <= 256; + } + } + bool errorStateFromLoader = getLoadState() >= LLModelLoader::ERROR_PARSING ? true : false; bool skinAndRigOk = true; @@ -3855,6 +3906,10 @@ void LLModelPreview::updateStatusMessages() { mFMP->childEnable("ok_btn"); } + else + { + mFMP->childDisable("ok_btn"); + } //add up physics triangles etc S32 start = 0; @@ -4021,6 +4076,9 @@ void LLModelPreview::updateStatusMessages() { // auto generate, also the default case for wizard which has no radio selection fmp->mLODMode[mPreviewLOD] = 1; + //don't actually regenerate lod when refreshing UI + mLODFrozen = true; + for (U32 i = 0; i < num_file_controls; ++i) { mFMP->childDisable(file_controls[i]); @@ -4033,20 +4091,21 @@ void LLModelPreview::updateStatusMessages() //if (threshold) { - U32 lod_mode = 0; - LLCtrlSelectionInterface* iface = mFMP->childGetSelectionInterface("lod_mode"); - if (iface) - { - lod_mode = iface->getFirstSelectedIndex(); - } - LLSpinCtrl* threshold = mFMP->getChild<LLSpinCtrl>("lod_error_threshold"); LLSpinCtrl* limit = mFMP->getChild<LLSpinCtrl>("lod_triangle_limit"); limit->setMaxValue(mMaxTriangleLimit); - limit->setValue(mRequestedTriangleCount[mPreviewLOD]); + limit->forceSetValue(mRequestedTriangleCount[mPreviewLOD]); + + threshold->forceSetValue(mRequestedErrorThreshold[mPreviewLOD]); - if (lod_mode == 0) + mFMP->getChild<LLComboBox>("lod_mode")->selectNthItem(mRequestedLoDMode[mPreviewLOD]); + mFMP->getChild<LLComboBox>("build_operator")->selectNthItem(mRequestedBuildOperator[mPreviewLOD]); + mFMP->getChild<LLComboBox>("queue_mode")->selectNthItem(mRequestedQueueMode[mPreviewLOD]); + mFMP->getChild<LLComboBox>("border_mode")->selectNthItem(mRequestedBorderMode[mPreviewLOD]); + mFMP->getChild<LLSpinCtrl>("share_tolerance")->setValue(mRequestedShareTolerance[mPreviewLOD]); + + if (mRequestedLoDMode[mPreviewLOD] == 0) { limit->setVisible(true); threshold->setVisible(false); @@ -4060,6 +4119,8 @@ void LLModelPreview::updateStatusMessages() threshold->setVisible(true); } } + + mLODFrozen = false; } } @@ -4075,6 +4136,20 @@ void LLModelPreview::updateStatusMessages() mFMP->childDisable("physics_file"); mFMP->childDisable("physics_browse"); } + + LLSpinCtrl* crease = mFMP->getChild<LLSpinCtrl>("crease_angle"); + + if (mRequestedCreaseAngle[mPreviewLOD] == -1.f) + { + mFMP->childSetColor("crease_label", LLColor4::grey); + crease->forceSetValue(75.f); + } + else + { + mFMP->childSetColor("crease_label", LLColor4::white); + crease->forceSetValue(mRequestedCreaseAngle[mPreviewLOD]); + } + } void LLModelPreview::setPreviewTarget(F32 distance) @@ -4189,11 +4264,7 @@ void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights) const LLModel::weight_list& weight_list = base_mdl->getJointInfluences(pos); LLVector4 w(0,0,0,0); - if (weight_list.size() > 4) - { - llerrs << "WTF?" << llendl; - } - + for (U32 i = 0; i < weight_list.size(); ++i) { F32 wght = llmin(weight_list[i].mWeight, 0.999999f); @@ -4241,42 +4312,6 @@ void LLModelPreview::update() } //----------------------------------------------------------------------------- -// changeAvatarsJointPositions() -//----------------------------------------------------------------------------- -void LLModelPreview::changeAvatarsJointPositions( LLModel* pModel ) -{ - if ( mMasterJointList.empty() ) - { - return; - } - - std::vector<std::string> :: const_iterator jointListItBegin = pModel->mSkinInfo.mJointNames.begin(); - std::vector<std::string> :: const_iterator jointListItEnd = pModel->mSkinInfo.mJointNames.end(); - - S32 index = 0; - for ( ; jointListItBegin!=jointListItEnd; ++jointListItBegin, ++index ) - { - std::string elem = *jointListItBegin; - //llinfos<<"joint "<<elem<<llendl; - - S32 matrixCnt = pModel->mSkinInfo.mAlternateBindMatrix.size(); - if ( matrixCnt < 1 ) - { - llinfos<<"Total WTF moment :"<<matrixCnt<<llendl; - } - else - { - LLMatrix4 jointTransform = pModel->mSkinInfo.mAlternateBindMatrix[index]; - - LLJoint* pJoint = gAgentAvatarp->getJoint( elem ); - if ( pJoint ) - { - pJoint->storeCurrentXform( jointTransform.getTranslation() ); - } - } - } -} -//----------------------------------------------------------------------------- // getTranslationForJointOffset() //----------------------------------------------------------------------------- LLVector3 LLModelPreview::getTranslationForJointOffset( std::string joint ) @@ -4290,6 +4325,30 @@ LLVector3 LLModelPreview::getTranslationForJointOffset( std::string joint ) return LLVector3(0.0f,0.0f,0.0f); } //----------------------------------------------------------------------------- +// createPreviewAvatar +//----------------------------------------------------------------------------- +void LLModelPreview::createPreviewAvatar( void ) +{ + mPreviewAvatar = (LLVOAvatar*)gObjectList.createObjectViewer( LL_PCODE_LEGACY_AVATAR, gAgent.getRegion() ); + if ( mPreviewAvatar ) + { + mPreviewAvatar->createDrawable( &gPipeline ); + mPreviewAvatar->mIsDummy = TRUE; + mPreviewAvatar->mSpecialRenderMode = 1; + mPreviewAvatar->setPositionAgent( LLVector3::zero ); + mPreviewAvatar->slamPosition(); + mPreviewAvatar->updateJointLODs(); + mPreviewAvatar->updateGeometry( mPreviewAvatar->mDrawable ); + mPreviewAvatar->startMotion( ANIM_AGENT_STAND ); + mPreviewAvatar->hideSkirt(); + } + else + { + llinfos<<"Failed to create preview avatar for upload model window"<<llendl; + } +} + +//----------------------------------------------------------------------------- // render() //----------------------------------------------------------------------------- BOOL LLModelPreview::render() @@ -4403,25 +4462,6 @@ BOOL LLModelPreview::render() mFMP->childSetEnabled("upload_joints", upload_skin); - //poke at avatar when we upload custom joints - /* - if ( upload_joints ) - { - for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) - { - for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) - { - LLModelInstance& instance = *model_iter; - LLModel* model = instance.mModel; - if ( !model->mSkinWeights.empty() ) - { - changeAvatarsJointPositions( model ); - } - } - } - } - */ - F32 explode = mFMP->childGetValue("physics_explode").asReal(); glClear(GL_DEPTH_BUFFER_BIT); @@ -4441,7 +4481,7 @@ BOOL LLModelPreview::render() if (skin_weight) { - target_pos = gAgentAvatarp->getPositionAgent(); + target_pos = getPreviewAvatar()->getPositionAgent(); z_near = 0.01f; z_far = 1024.f; mCameraDistance = 16.f; @@ -4661,8 +4701,7 @@ BOOL LLModelPreview::render() } else { - LLVOAvatarSelf* avatar = gAgentAvatarp; - target_pos = avatar->getPositionAgent(); + target_pos = getPreviewAvatar()->getPositionAgent(); LLViewerCamera::getInstance()->setOriginAndLookAt( target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + offset) * av_rot), // camera @@ -4671,7 +4710,7 @@ BOOL LLModelPreview::render() if (joint_positions) { - avatar->renderCollisionVolumes(); + getPreviewAvatar()->renderCollisionVolumes(); } for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) @@ -4702,7 +4741,7 @@ BOOL LLModelPreview::render() LLMatrix4 mat[64]; for (U32 j = 0; j < model->mSkinInfo.mJointNames.size(); ++j) { - LLJoint* joint = avatar->getJoint(model->mSkinInfo.mJointNames[j]); + LLJoint* joint = getPreviewAvatar()->getJoint(model->mSkinInfo.mJointNames[j]); if (joint) { mat[j] = model->mSkinInfo.mInvBindMatrix[j]; @@ -4925,9 +4964,12 @@ void LLModelPreview::textureLoadedCallback( BOOL success, LLViewerFetchedTexture void LLModelPreview::onLODParamCommit(bool enforce_tri_limit) { - genLODs(mPreviewLOD, 3, enforce_tri_limit); - updateStatusMessages(); - refresh(); + if (!mLODFrozen) + { + genLODs(mPreviewLOD, 3, enforce_tri_limit); + updateStatusMessages(); + refresh(); + } } LLFloaterModelPreview::DecompRequest::DecompRequest(const std::string& stage, LLModel* mdl) @@ -4942,9 +4984,12 @@ LLFloaterModelPreview::DecompRequest::DecompRequest(const std::string& stage, LL if (mdl) { 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) @@ -4958,12 +5003,28 @@ LLFloaterModelPreview::DecompRequest::DecompRequest(const std::string& stage, LL for (U32 j = 0; j < 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]) ; + } } - for (U32 j = 0; j < face.mNumIndices; ++j) + updateTriangleAreaThreshold() ; + + for (U32 j = 0; j+2 < face.mNumIndices; j += 3) { - mIndices.push_back(face.mIndices[j]+index_offset); - } + 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; } diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h index 4d8b46807f..ed7cbe6d1e 100644 --- a/indra/newview/llfloatermodelpreview.h +++ b/indra/newview/llfloatermodelpreview.h @@ -199,6 +199,8 @@ protected: static void onUploadJointsCommit(LLUICtrl*,void*); static void onUploadSkinCommit(LLUICtrl*,void*); + static void onPhysicsLoadRadioCommit(LLUICtrl*,void *data); + static void onPreviewLODCommit(LLUICtrl*,void*); static void onGenerateNormalsCommit(LLUICtrl*,void*); @@ -341,6 +343,9 @@ public: LLVector3 getTranslationForJointOffset( std::string joint ); + void createPreviewAvatar( void ); + LLVOAvatar* getPreviewAvatar( void ) { return mPreviewAvatar; } + protected: friend class LLModelLoader; friend class LLFloaterModelPreview; @@ -373,13 +378,20 @@ public: std::map<std::string, bool> mViewOption; //GLOD object parameters (must rebuild object if these change) + bool mLODFrozen; F32 mBuildShareTolerance; U32 mBuildQueueMode; U32 mBuildOperator; U32 mBuildBorderMode; + U32 mRequestedLoDMode[LLModel::NUM_LODS]; S32 mRequestedTriangleCount[LLModel::NUM_LODS]; + F32 mRequestedErrorThreshold[LLModel::NUM_LODS]; + U32 mRequestedBuildOperator[LLModel::NUM_LODS]; + U32 mRequestedQueueMode[LLModel::NUM_LODS]; + U32 mRequestedBorderMode[LLModel::NUM_LODS]; + F32 mRequestedShareTolerance[LLModel::NUM_LODS]; + F32 mRequestedCreaseAngle[LLModel::NUM_LODS]; - LLModelLoader* mModelLoader; LLModelLoader::scene mScene[LLModel::NUM_LODS]; @@ -415,6 +427,7 @@ public: std::deque<std::string> mMasterLegacyJointList; std::deque<std::string> mJointsFromNode; JointTransformMap mJointTransformMap; + LLPointer<LLVOAvatar> mPreviewAvatar; }; #endif // LL_LLFLOATERMODELPREVIEW_H diff --git a/indra/newview/llfloatermodelwizard.cpp b/indra/newview/llfloatermodelwizard.cpp index faf81dbc5c..e44737f39e 100644 --- a/indra/newview/llfloatermodelwizard.cpp +++ b/indra/newview/llfloatermodelwizard.cpp @@ -441,10 +441,13 @@ LLFloaterModelWizard::DecompRequest::DecompRequest(const std::string& stage, LLM if (mdl) { 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) { @@ -457,11 +460,27 @@ LLFloaterModelWizard::DecompRequest::DecompRequest(const std::string& stage, LLM for (U32 j = 0; j < 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]) ; + } } - for (U32 j = 0; j < face.mNumIndices; ++j) + updateTriangleAreaThreshold() ; + + for (U32 j = 0; j+2 < face.mNumIndices; j += 3) { - mIndices.push_back(face.mIndices[j]+index_offset); + 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; diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 34fda49375..fc6976755f 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -590,6 +590,11 @@ bool LLPanelRegionGeneralInfo::refreshFromRegion(LLViewerRegion* region) getChildView("im_btn")->setEnabled(allow_modify); getChildView("manage_telehub_btn")->setEnabled(allow_modify); + const bool enable_mesh = gSavedSettings.getBOOL("MeshEnabled") && + gAgent.getRegion() && + !gAgent.getRegion()->getCapability("GetMesh").empty(); + getChildView("mesh_rez_enabled_check")->setVisible(enable_mesh); + getChildView("mesh_rez_enabled_check")->setEnabled(getChildView("mesh_rez_enabled_check")->getEnabled() && enable_mesh); // Data gets filled in by processRegionInfo return LLPanelRegionInfo::refreshFromRegion(region); diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index 73c1f99fa0..061a42ab57 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -85,6 +85,7 @@ #include "llviewerwindow.h" #include "llvovolume.h" #include "lluictrlfactory.h" +#include "llaccountingquotamanager.h" // Globals LLFloaterTools *gFloaterTools = NULL; diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index b5180854ef..318beafe65 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -632,10 +632,12 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) } // We're hiding mesh types +#if 0 if (item->getType() == LLAssetType::AT_MESH) { return mask; } +#endif LLViewerInventoryItem* old_item = getItem(item->getUUID()); LLPointer<LLViewerInventoryItem> new_item; diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 0b96a3b34f..bef2a09e6c 100644..100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1,3827 +1,3943 @@ -/** - * @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 <queue> - -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; - -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 completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - assert_main_thread(); - llinfos << "completed" << llendl; - mThread->mPendingUploads--; - } - -}; - -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<LLUUID> incomplete; - for (std::set<LLUUID>::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<LLUUID> incomplete; - for (std::set<LLUUID>::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<LLUUID> incomplete; - for (std::set<LLUUID>::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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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("<? LLSD/Binary ?>"); - - 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"<<info.mPelvisOffset<<llendl; - mSkinInfoQ.push(info); - } - - return true; -} - -bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size) -{ - LLSD decomp; - - if (data_size > 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<LLVolume> 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<LLVector3>& pos = d->mPhysicsShapeMesh.mPositions; - std::vector<LLVector3>& 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"); - mWholeModelUploadCapability = 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 0 -void dumpLLSDToFile(LLSD& content, std::string& filename) -{ - std::ofstream of(filename); - LLSDSerialize::toPrettyXML(content,of); -} -#endif - -void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures) -{ - // TODO where do textures go? - - LLSD result; - - 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 - - LLSD res; - res["mesh_list"] = LLSD::emptyArray(); - res["texture_list"] = LLSD::emptyArray(); - S32 mesh_num = 0; - S32 texture_num = 0; - - std::set<LLViewerTexture* > 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); - - mesh_entry["childpos"] = ll_sd_from_vector3(pos); - mesh_entry["childrot"] = 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<LLImportMaterial>::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<LLImageJ2C> 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 0 - std::string name("whole_model.xml"); - dumpLLSDToFile(result,name); -#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(mWholeModelUploadCapability, headers, model_data.asString(), - new LLWholeModelFeeResponder(this)); - - // Currently a no-op. - mFinished = true; -} - -void LLMeshUploadThread::doIterativeUpload() -{ - if(isDiscarded()) - { - mFinished = true; - return ; - } - - mCurlRequest = new LLCurlRequest(); - - std::set<LLViewerTexture* > 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<LLImportMaterial>::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<LLUUID, U32>::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<LLVolume> 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<LLMeshUploadThread*>::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<LLUUID, F32> 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<LLUUID>::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<LLMeshRepoThread::LODRequest>::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<LLUUID>::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<LLUUID>::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<LLUUID>::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<LLUUID>::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<LLUUID>::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<LLModelInstance>& 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<LLImageJ2C> 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<U8> 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<std::string, const LLCDParam*> 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<LLVector3> 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<std::string, const LLCDParam*> 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<LLVector3> 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); - } - } -} +/**
+ * @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 <queue>
+
+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_fee_response.xml");
+ if (isGoodStatus(status))
+ {
+ mThread->mWholeModelUploadURL = content["uploader"].asString();
+ }
+ else
+ {
+ llinfos << "upload failed" << llendl;
+ mThread->mWholeModelUploadURL = "";
+ }
+
+ }
+};
+
+class LLWholeModelUploadResponder: public LLCurl::Responder
+{
+ LLMeshUploadThread* mThread;
+ LLSD mPostData;
+
+public:
+ LLWholeModelUploadResponder(LLMeshUploadThread* thread, LLSD& post_data):
+ mThread(thread),
+ mPostData(post_data)
+ {
+ }
+ 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");
+ // requested "mesh" asset type isn't actually the type
+ // of the resultant object, fix it up here.
+ mPostData["asset_type"] = "object";
+ gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mPostData,content));
+ }
+};
+
+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<LLUUID> incomplete;
+ for (std::set<LLUUID>::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<LLUUID> incomplete;
+ for (std::set<LLUUID>::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<LLUUID> incomplete;
+ for (std::set<LLUUID>::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);
+ llassert(pending->second.size() <= LLModel::NUM_LODS)
+ }
+ 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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("<? LLSD/Binary ?>");
+
+ 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"<<info.mPelvisOffset<<llendl;
+ mSkinInfoQ.push(info);
+ }
+
+ return true;
+}
+
+bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size)
+{
+ LLSD decomp;
+
+ if (data_size > 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<LLVolume> 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<LLVector3>& pos = d->mPhysicsShapeMesh.mPositions;
+ std::vector<LLVector3>& 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;
+ }
+
+ llassert(mHull.size() == 1);
+
+ 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();
+ }
+}
+
+void dumpLLSDToFile(const LLSD& content, std::string filename)
+{
+#if 1
+ std::ofstream of(filename.c_str());
+ LLSDSerialize::toPrettyXML(content,of);
+#endif
+}
+
+void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
+{
+ LLSD result;
+
+ LLSD res;
+ result["folder_id"] = gInventory.findCategoryUUIDForType(LLFolderType::FT_OBJECT);
+ result["asset_type"] = "mesh";
+ result["inventory_type"] = "object";
+ result["name"] = "mesh model";
+ result["description"] = "your description here";
+
+ res["mesh_list"] = LLSD::emptyArray();
+ res["texture_list"] = LLSD::emptyArray();
+ res["instance_list"] = LLSD::emptyArray();
+ S32 mesh_num = 0;
+ S32 texture_num = 0;
+
+ std::set<LLViewerTexture* > textures;
+ std::map<LLViewerTexture*,S32> texture_index;
+
+ std::map<LLModel*,S32> mesh_index;
+
+ 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());
+ LLModel* model = instance.mModel;
+ if (mesh_index.find(model) == mesh_index.end())
+ {
+ // Have not seen this model before - create a new mesh_list entry for it.
+ std::string model_name = data.mBaseModel->getName();
+ if (!model_name.empty())
+ {
+ result["name"] = model_name;
+ }
+
+ 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();
+ std::string str = ostr.str();
+
+ res["mesh_list"][mesh_num] = LLSD::Binary(str.begin(),str.end());
+ mesh_index[model] = mesh_num;
+ mesh_num++;
+ }
+
+ 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;
+ 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());
+ instance_entry["permissions"] = ll_create_sd_from_permissions(perm);
+ instance_entry["physics_shape_type"] = (U8)(LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL);
+ instance_entry["mesh"] = mesh_index[model];
+
+ if (mUploadTextures)
+ {
+ instance_entry["face_list"] = LLSD::emptyArray();
+
+ for (S32 face_num = 0; face_num < model->getNumVolumeFaces(); face_num++)
+ {
+ LLImportMaterial& material = instance.mMaterial[face_num];
+ LLSD face_entry = LLSD::emptyMap();
+ LLViewerFetchedTexture *texture = material.mDiffuseMap.get();
+
+ if (texture != NULL)
+ {
+ if (textures.find(texture) == textures.end())
+ {
+ textures.insert(texture);
+ }
+
+ std::stringstream ostr;
+ if (include_textures) // otherwise data is blank.
+ {
+ LLTextureUploadData data(texture, material.mDiffuseMapLabel);
+ if (!data.mTexture->isRawImageValid())
+ {
+ data.mTexture->reloadRawImage(data.mTexture->getDiscardLevel());
+ }
+
+ LLPointer<LLImageJ2C> upload_file =
+ LLViewerTextureList::convertToUploadFile(data.mTexture->getRawImage());
+ ostr.write((const char*) upload_file->getData(), upload_file->getDataSize());
+ }
+
+ if (texture_index.find(texture) == texture_index.end())
+ {
+ texture_index[texture] = texture_num;
+ std::string str = ostr.str();
+ res["texture_list"][texture_num] = LLSD::Binary(str.begin(),str.end());
+ texture_num++;
+ }
+ }
+
+ // Subset of TextureEntry fields.
+ if (texture)
+ {
+ 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["colors"] = 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++;
+ }
+
+ result["asset_resources"] = res;
+ dumpLLSDToFile(result,"whole_model.xml");
+
+ 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];
+ }
+
+ llassert(physics != NULL);
+
+ DecompRequest* request = new DecompRequest(physics, data.mBaseModel, this);
+ gMeshRepo.mDecompThread->submitRequest(request);
+ }
+
+ while (!mPhysicsComplete)
+ {
+ apr_sleep(100);
+ }
+
+ LLSD model_data;
+ wholeModelToLLSD(model_data,false);
+ dumpLLSDToFile(model_data,"whole_model_fee_request.xml");
+
+ mPendingUploads++;
+ LLCurlRequest::headers_t headers;
+ mCurlRequest->post(mWholeModelFeeCapability, headers, model_data,
+ new LLWholeModelFeeResponder(this));
+
+ do
+ {
+ mCurlRequest->process();
+ } while (mCurlRequest->getQueued() > 0);
+
+
+ if (mWholeModelUploadURL.empty())
+ {
+ llinfos << "unable to upload, fee request failed" << llendl;
+ }
+ else
+ {
+ LLSD full_model_data;
+ wholeModelToLLSD(full_model_data, true);
+ LLSD body = full_model_data["asset_resources"];
+ dumpLLSDToFile(body,"whole_model_body.xml");
+ mCurlRequest->post(mWholeModelUploadURL, headers, body,
+ new LLWholeModelUploadResponder(this, model_data));
+ 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<LLViewerTexture* > 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<LLImportMaterial>::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
+ LLModel* physics = data.mModel[LLModel::LOD_PHYSICS];
+ if (physics == NULL)
+ { //no physics model available, use high lod
+ physics = data.mModel[LLModel::LOD_HIGH];
+ }
+
+ DecompRequest* request = new DecompRequest(physics, 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<LLUUID, U32>::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<LLVolume> 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);
+ }
+ }
+ }
+
+ 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<LLMeshUploadThread*>::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<LLUUID, F32> 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<LLUUID>::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<LLMeshRepoThread::LODRequest>::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<LLUUID>::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<LLUUID>::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<LLUUID>::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<LLUUID>::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<LLUUID>::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<LLModelInstance>& 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<LLImageJ2C> 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;
+
+ llassert(instance.mMeshID.notNull());
+
+ // 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<U8> 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);
+ dumpLLSDToFile(data.mPostData,"update_inventory_post_data.xml");
+ dumpLLSDToFile(data.mResponse,"update_inventory_response.xml");
+ 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<std::string, const LLCDParam*> 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());
+ }
+ }
+
+ 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<LLVector3> 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<std::string, const LLCDParam*> 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<LLVector3> 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::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)
+{
+ 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 index 802e3e1aba..0a6954bade 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -152,7 +152,7 @@ public: std::string mStatusMessage; std::vector<LLModel::PhysicsMesh> mHullMesh; LLModel::convex_hull_decomposition mHull; - + //status message callback, called from decomposition thread virtual S32 statusCallback(const char* status, S32 p1, S32 p2) = 0; @@ -160,6 +160,14 @@ public: virtual void completed() = 0; virtual void setStatusMessage(const std::string& msg); + + protected: + //internal use + LLVector3 mBBox[2] ; + F32 mTriangleAreaThreshold ; + + void updateTriangleAreaThreshold() ; + bool isValidTriangle(U16 idx1, U16 idx2, U16 idx3) ; }; LLCondition* mSignal; @@ -387,7 +395,8 @@ public: LLHost mHost; std::string mUploadObjectAssetCapability; std::string mNewInventoryCapability; - std::string mWholeModelUploadCapability; + std::string mWholeModelFeeCapability; + std::string mWholeModelUploadURL; std::queue<LLMeshUploadData> mUploadQ; std::queue<LLMeshUploadData> mConfirmedQ; diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index 64af6c2157..34a92cd0ac 100644 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -33,11 +33,9 @@ #include "lleconomy.h" #include "llerror.h" #include "llfontgl.h" -#include "llmaterialtable.h" #include "llpermissionsflags.h" #include "llstring.h" #include "llvolume.h" -#include "material_codes.h" #include "m3math.h" // project includes @@ -57,7 +55,6 @@ #include "lltool.h" #include "lltoolcomp.h" #include "lltoolmgr.h" -#include "lltrans.h" #include "llui.h" #include "llviewerobject.h" #include "llviewerregion.h" @@ -101,17 +98,6 @@ BOOL LLPanelObject::postBuild() { setMouseOpaque(FALSE); - std::map<std::string, std::string> material_name_map; - material_name_map["Stone"]= LLTrans::getString("Stone"); - material_name_map["Metal"]= LLTrans::getString("Metal"); - material_name_map["Glass"]= LLTrans::getString("Glass"); - material_name_map["Wood"]= LLTrans::getString("Wood"); - material_name_map["Flesh"]= LLTrans::getString("Flesh"); - material_name_map["Plastic"]= LLTrans::getString("Plastic"); - material_name_map["Rubber"]= LLTrans::getString("Rubber"); - material_name_map["Light"]= LLTrans::getString("Light"); - - LLMaterialTable::basic.initTableTransNames(material_name_map); //-------------------------------------------------------- // Top //-------------------------------------------------------- @@ -166,22 +152,6 @@ BOOL LLPanelObject::postBuild() //-------------------------------------------------------- - // material type popup - mComboMaterial = getChild<LLComboBox>("material"); - childSetCommitCallback("material",onCommitMaterial,this); - mComboMaterial->removeall(); - - for (LLMaterialTable::info_list_t::iterator iter = LLMaterialTable::basic.mMaterialInfoList.begin(); - iter != LLMaterialTable::basic.mMaterialInfoList.end(); ++iter) - { - LLMaterialInfo* minfop = *iter; - if (minfop->mMCode != LL_MCODE_LIGHT) - { - mComboMaterial->add(minfop->mName); - } - } - mComboMaterialItemCount = mComboMaterial->getItemCount(); - // Base Type mComboBaseType = getChild<LLComboBox>("comboBaseType"); childSetCommitCallback("comboBaseType",onCommitParametric,this); @@ -309,7 +279,6 @@ BOOL LLPanelObject::postBuild() LLPanelObject::LLPanelObject() : LLPanel(), - mComboMaterialItemCount(0), mIsPhysical(FALSE), mIsTemporary(FALSE), mIsPhantom(FALSE), @@ -527,43 +496,6 @@ void LLPanelObject::getState( ) mCheckCastShadows->setEnabled( roots_selected==1 && editable ); #endif - // Update material part - // slightly inefficient - materials are unique per object, not per TE - U8 material_code = 0; - struct f : public LLSelectedTEGetFunctor<U8> - { - U8 get(LLViewerObject* object, S32 te) - { - return object->getMaterial(); - } - } func; - bool material_same = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, material_code ); - std::string LEGACY_FULLBRIGHT_DESC = LLTrans::getString("Fullbright"); - if (editable && single_volume && material_same) - { - mComboMaterial->setEnabled( TRUE ); - if (material_code == LL_MCODE_LIGHT) - { - if (mComboMaterial->getItemCount() == mComboMaterialItemCount) - { - mComboMaterial->add(LEGACY_FULLBRIGHT_DESC); - } - mComboMaterial->setSimple(LEGACY_FULLBRIGHT_DESC); - } - else - { - if (mComboMaterial->getItemCount() != mComboMaterialItemCount) - { - mComboMaterial->remove(LEGACY_FULLBRIGHT_DESC); - } - - mComboMaterial->setSimple(std::string(LLMaterialTable::basic.getName(material_code))); - } - } - else - { - mComboMaterial->setEnabled( FALSE ); - } //---------------------------------------------------------------------------- S32 selected_item = MI_BOX; @@ -1095,12 +1027,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) { @@ -1145,7 +1074,7 @@ void LLPanelObject::getState( ) if (mCtrlSculptMirror) { mCtrlSculptMirror->set(sculpt_mirror); - mCtrlSculptMirror->setEnabled(editable); + mCtrlSculptMirror->setEnabled(editable && !isMesh); } if (mCtrlSculptInvert) @@ -1166,6 +1095,9 @@ void LLPanelObject::getState( ) mSculptTextureRevert = LLUUID::null; } + mCtrlSculptMirror->setVisible(sculpt_texture_visible && !isMesh); + mCtrlSculptInvert->setVisible(sculpt_texture_visible && !isMesh); + //---------------------------------------------------------------------------- mObject = objectp; @@ -1245,25 +1177,6 @@ void LLPanelObject::sendCastShadows() } // static -void LLPanelObject::onCommitMaterial( LLUICtrl* ctrl, void* userdata ) -{ - //LLPanelObject* self = (LLPanelObject*) userdata; - LLComboBox* box = (LLComboBox*) ctrl; - - if (box) - { - // apply the currently selected material to the object - const std::string& material_name = box->getSimple(); - std::string LEGACY_FULLBRIGHT_DESC = LLTrans::getString("Fullbright"); - if (material_name != LEGACY_FULLBRIGHT_DESC) - { - U8 material_code = LLMaterialTable::basic.getMCode(material_name); - LLSelectMgr::getInstance()->selectionSetMaterial(material_code); - } - } -} - -// static void LLPanelObject::onCommitParametric( LLUICtrl* ctrl, void* userdata ) { LLPanelObject* self = (LLPanelObject*) userdata; @@ -1937,7 +1850,6 @@ void LLPanelObject::clearCtrls() mCheckCastShadows->set(FALSE); mCheckCastShadows->setEnabled( FALSE ); #endif - mComboMaterial ->setEnabled( FALSE ); // Disable text labels mLabelPosition ->setEnabled( FALSE ); mLabelSize ->setEnabled( FALSE ); diff --git a/indra/newview/llpanelobject.h b/indra/newview/llpanelobject.h index e2f2a4400d..475dfdaedb 100644 --- a/indra/newview/llpanelobject.h +++ b/indra/newview/llpanelobject.h @@ -66,7 +66,6 @@ public: static void onCommitPhantom( LLUICtrl* ctrl, void* userdata); static void onCommitCastShadows( LLUICtrl* ctrl, void* userdata); static void onCommitPhysics( LLUICtrl* ctrl, void* userdata); - static void onCommitMaterial( LLUICtrl* ctrl, void* userdata); static void onCommitParametric(LLUICtrl* ctrl, void* userdata); @@ -94,10 +93,6 @@ protected: void getVolumeParams(LLVolumeParams& volume_params); protected: - S32 mComboMaterialItemCount; - - LLComboBox* mComboMaterial; - // Per-object options LLComboBox* mComboBaseType; diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp index c443814c89..ebddaa90bc 100644 --- a/indra/newview/llpanelvolume.cpp +++ b/indra/newview/llpanelvolume.cpp @@ -59,6 +59,7 @@ #include "lltool.h" #include "lltoolcomp.h" #include "lltoolmgr.h" +#include "lltrans.h" #include "llui.h" #include "llviewerobject.h" #include "llviewerregion.h" @@ -156,6 +157,34 @@ BOOL LLPanelVolume::postBuild() mSpinPhysicsRestitution = getChild<LLSpinCtrl>("Physics Restitution"); mSpinPhysicsRestitution->setCommitCallback(boost::bind(&LLPanelVolume::sendPhysicsRestitution, this, _1, mSpinPhysicsRestitution)); } + + std::map<std::string, std::string> material_name_map; + material_name_map["Stone"]= LLTrans::getString("Stone"); + material_name_map["Metal"]= LLTrans::getString("Metal"); + material_name_map["Glass"]= LLTrans::getString("Glass"); + material_name_map["Wood"]= LLTrans::getString("Wood"); + material_name_map["Flesh"]= LLTrans::getString("Flesh"); + material_name_map["Plastic"]= LLTrans::getString("Plastic"); + material_name_map["Rubber"]= LLTrans::getString("Rubber"); + material_name_map["Light"]= LLTrans::getString("Light"); + + LLMaterialTable::basic.initTableTransNames(material_name_map); + + // material type popup + mComboMaterial = getChild<LLComboBox>("material"); + childSetCommitCallback("material",onCommitMaterial,this); + mComboMaterial->removeall(); + + for (LLMaterialTable::info_list_t::iterator iter = LLMaterialTable::basic.mMaterialInfoList.begin(); + iter != LLMaterialTable::basic.mMaterialInfoList.end(); ++iter) + { + LLMaterialInfo* minfop = *iter; + if (minfop->mMCode != LL_MCODE_LIGHT) + { + mComboMaterial->add(minfop->mName); + } + } + mComboMaterialItemCount = mComboMaterial->getItemCount(); // Start with everyone disabled clearCtrls(); @@ -164,7 +193,8 @@ BOOL LLPanelVolume::postBuild() } LLPanelVolume::LLPanelVolume() - : LLPanel() + : LLPanel(), + mComboMaterialItemCount(0) { setMouseOpaque(FALSE); @@ -379,6 +409,46 @@ void LLPanelVolume::getState( ) getChildView("FlexForceZ")->setEnabled(false); } + // Material properties + + // Update material part + // slightly inefficient - materials are unique per object, not per TE + U8 material_code = 0; + struct f : public LLSelectedTEGetFunctor<U8> + { + U8 get(LLViewerObject* object, S32 te) + { + return object->getMaterial(); + } + } func; + bool material_same = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, material_code ); + std::string LEGACY_FULLBRIGHT_DESC = LLTrans::getString("Fullbright"); + if (editable && single_volume && material_same) + { + mComboMaterial->setEnabled( TRUE ); + if (material_code == LL_MCODE_LIGHT) + { + if (mComboMaterial->getItemCount() == mComboMaterialItemCount) + { + mComboMaterial->add(LEGACY_FULLBRIGHT_DESC); + } + mComboMaterial->setSimple(LEGACY_FULLBRIGHT_DESC); + } + else + { + if (mComboMaterial->getItemCount() != mComboMaterialItemCount) + { + mComboMaterial->remove(LEGACY_FULLBRIGHT_DESC); + } + + mComboMaterial->setSimple(std::string(LLMaterialTable::basic.getName(material_code))); + } + } + else + { + mComboMaterial->setEnabled( FALSE ); + } + // Physics properties mSpinPhysicsGravity->set(objectp->getPhysicsGravity()); @@ -467,7 +537,6 @@ void LLPanelVolume::refresh() getChildView("label physicsshapetype")->setVisible(enable_mesh); getChildView("Physics Shape Type Combo Ctrl")->setVisible(enable_mesh); getChildView("Physics Gravity")->setVisible(enable_mesh); - getChildView("Physics Material Override")->setVisible(enable_mesh); getChildView("Physics Friction")->setVisible(enable_mesh); getChildView("Physics Density")->setVisible(enable_mesh); getChildView("Physics Restitution")->setVisible(enable_mesh); @@ -522,6 +591,8 @@ void LLPanelVolume::clearCtrls() mSpinPhysicsFriction->setEnabled(FALSE); mSpinPhysicsDensity->setEnabled(FALSE); mSpinPhysicsRestitution->setEnabled(FALSE); + + mComboMaterial->setEnabled( FALSE ); } // @@ -674,6 +745,25 @@ void LLPanelVolume::onLightSelectTexture(const LLSD& data) } // static +void LLPanelVolume::onCommitMaterial( LLUICtrl* ctrl, void* userdata ) +{ + //LLPanelObject* self = (LLPanelObject*) userdata; + LLComboBox* box = (LLComboBox*) ctrl; + + if (box) + { + // apply the currently selected material to the object + const std::string& material_name = box->getSimple(); + std::string LEGACY_FULLBRIGHT_DESC = LLTrans::getString("Fullbright"); + if (material_name != LEGACY_FULLBRIGHT_DESC) + { + U8 material_code = LLMaterialTable::basic.getMCode(material_name); + LLSelectMgr::getInstance()->selectionSetMaterial(material_code); + } + } +} + +// static void LLPanelVolume::onCommitLight( LLUICtrl* ctrl, void* userdata ) { LLPanelVolume* self = (LLPanelVolume*) userdata; diff --git a/indra/newview/llpanelvolume.h b/indra/newview/llpanelvolume.h index 776a2c1f4a..0ef47db0d9 100644 --- a/indra/newview/llpanelvolume.h +++ b/indra/newview/llpanelvolume.h @@ -63,8 +63,8 @@ public: static void onCommitLight( LLUICtrl* ctrl, void* userdata); static void onCommitIsFlexible( LLUICtrl* ctrl, void* userdata); static void onCommitFlexible( LLUICtrl* ctrl, void* userdata); - static void onCommitPhysicsParam( LLUICtrl* ctrl, void* userdata); + static void onCommitMaterial( LLUICtrl* ctrl, void* userdata); void onLightCancelColor(const LLSD& data); void onLightSelectColor(const LLSD& data); @@ -104,6 +104,10 @@ protected: LLSpinCtrl* mSpinForce[3]; */ + S32 mComboMaterialItemCount; + LLComboBox* mComboMaterial; + + LLColor4 mLightSavedColor; LLUUID mLightSavedTexture; LLPointer<LLViewerObject> mObject; diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 9b264b81c7..8fa4065fa6 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -1997,7 +1997,7 @@ void LLSelectMgr::selectionSetPhysicsType(U8 type) if (object->permModify()) { object->setPhysicsShapeType(mType); - object->updateFlags(); + object->updateFlags(TRUE); } return true; } @@ -2016,7 +2016,7 @@ void LLSelectMgr::selectionSetFriction(F32 friction) if (object->permModify()) { object->setPhysicsFriction(mFriction); - object->updateFlags(); + object->updateFlags(TRUE); } return true; } @@ -2035,7 +2035,7 @@ void LLSelectMgr::selectionSetGravity(F32 gravity ) if (object->permModify()) { object->setPhysicsGravity(mGravity); - object->updateFlags(); + object->updateFlags(TRUE); } return true; } @@ -2054,7 +2054,7 @@ void LLSelectMgr::selectionSetDensity(F32 density ) if (object->permModify()) { object->setPhysicsDensity(mDensity); - object->updateFlags(); + object->updateFlags(TRUE); } return true; } @@ -2073,7 +2073,7 @@ void LLSelectMgr::selectionSetRestitution(F32 restitution) if (object->permModify()) { object->setPhysicsRestitution(mRestitution); - object->updateFlags(); + object->updateFlags(TRUE); } return true; } diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index fa329eb0ae..8143d6a41f 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -69,6 +69,7 @@ U32 LLSpatialGroup::sNodeCount = 0; std::set<GLuint> LLSpatialGroup::sPendingQueries; +U32 gOctreeMaxCapacity; BOOL LLSpatialGroup::sNoDelete = FALSE; @@ -630,7 +631,7 @@ BOOL LLSpatialGroup::updateInGroup(LLDrawable *drawablep, BOOL immediate) if (mOctreeNode->isInside(drawablep->getPositionGroup()) && (mOctreeNode->contains(drawablep) || (drawablep->getBinRadius() > mOctreeNode->getSize()[0] && - parent && parent->getElementCount() >= LL_OCTREE_MAX_CAPACITY))) + parent && parent->getElementCount() >= gOctreeMaxCapacity))) { unbound(); setState(OBJECT_DIRTY); @@ -689,17 +690,8 @@ static LLFastTimer::DeclareTimer FTM_REBUILD_VBO("VBO Rebuilt"); void LLSpatialPartition::rebuildGeom(LLSpatialGroup* group) { - /*if (!gPipeline.hasRenderType(mDrawableType)) - { - return; - }*/ - if (group->isDead() || !group->isState(LLSpatialGroup::GEOM_DIRTY)) { - /*if (!group->isState(LLSpatialGroup::GEOM_DIRTY) && mRenderByGroup) - { - llerrs << "WTF?" << llendl; - }*/ return; } @@ -961,21 +953,15 @@ void LLSpatialGroup::setState(U32 state) { mState |= state; - if (state > LLSpatialGroup::STATE_MASK) - { - llerrs << "WTF?" << llendl; - } + llassert(state <= LLSpatialGroup::STATE_MASK); } void LLSpatialGroup::setState(U32 state, S32 mode) { LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION); - if (state > LLSpatialGroup::STATE_MASK) - { - llerrs << "WTF?" << llendl; - } - + llassert(state <= LLSpatialGroup::STATE_MASK); + if (mode > STATE_MODE_SINGLE) { if (mode == STATE_MODE_DIFF) @@ -1021,20 +1007,14 @@ public: void LLSpatialGroup::clearState(U32 state) { - if (state > LLSpatialGroup::STATE_MASK) - { - llerrs << "WTF?" << llendl; - } + llassert(state <= LLSpatialGroup::STATE_MASK); mState &= ~state; } void LLSpatialGroup::clearState(U32 state, S32 mode) { - if (state > LLSpatialGroup::STATE_MASK) - { - llerrs << "WTF?" << llendl; - } + llassert(state <= LLSpatialGroup::STATE_MASK); LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION); @@ -1059,10 +1039,7 @@ void LLSpatialGroup::clearState(U32 state, S32 mode) BOOL LLSpatialGroup::isState(U32 state) const { - if (state > LLSpatialGroup::STATE_MASK) - { - llerrs << "WTF?" << llendl; - } + llassert(state <= LLSpatialGroup::STATE_MASK); return mState & state ? TRUE : FALSE; } @@ -1250,7 +1227,8 @@ void LLSpatialGroup::updateDistance(LLCamera &camera) { if (LLViewerCamera::sCurCameraID != LLViewerCamera::CAMERA_WORLD) { - llerrs << "WTF?" << llendl; + llwarns << "Attempted to update distance for camera other than world camera!" << llendl; + return; } #if !LL_RELEASE_FOR_DOWNLOAD @@ -2064,11 +2042,8 @@ public: virtual void processGroup(LLSpatialGroup* group) { - if (group->isState(LLSpatialGroup::DIRTY) || group->getData().empty()) - { - llerrs << "WTF?" << llendl; - } - + llassert(!group->isState(LLSpatialGroup::DIRTY) && !group->getData().empty()) + if (mRes < 2) { if (mCamera->AABBInFrustum(group->mObjectBounds[0], group->mObjectBounds[1]) > 0) @@ -2541,7 +2516,7 @@ void renderOctree(LLSpatialGroup* group) //coded by buffer usage and activity gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA); LLVector4 col; - if (group->mBuilt > 0.f) + /*if (group->mBuilt > 0.f) { group->mBuilt -= 2.f * gFrameIntervalSeconds; if (group->mBufferUsage == GL_STATIC_DRAW_ARB) @@ -2610,7 +2585,7 @@ void renderOctree(LLSpatialGroup* group) gGL.color4f(1,1,1,1); } } - else + else*/ { if (group->mBufferUsage == GL_STATIC_DRAW_ARB && !group->getData().empty() && group->mSpatialPartition->mRenderByGroup) @@ -2630,33 +2605,24 @@ void renderOctree(LLSpatialGroup* group) size.mul(1.01f); size.add(fudge); - { - LLGLDepthTest depth(GL_TRUE, GL_FALSE); - drawBox(group->mObjectBounds[0], fudge); - } + //{ + // LLGLDepthTest depth(GL_TRUE, GL_FALSE); + // drawBox(group->mObjectBounds[0], fudge); + //} gGL.setSceneBlendType(LLRender::BT_ALPHA); - if (group->mBuilt <= 0.f) + //if (group->mBuilt <= 0.f) { //draw opaque outline - gGL.color4f(col.mV[0], col.mV[1], col.mV[2], 1.f); - drawBoxOutline(group->mObjectBounds[0], group->mObjectBounds[1]); + //gGL.color4f(col.mV[0], col.mV[1], col.mV[2], 1.f); + //drawBoxOutline(group->mObjectBounds[0], group->mObjectBounds[1]); - if (group->mOctreeNode->isLeaf()) - { - gGL.color4f(1,1,1,1); - } - else - { - gGL.color4f(0,1,1,1); - } - + gGL.color4f(0,1,1,1); drawBoxOutline(group->mBounds[0],group->mBounds[1]); - - + //draw bounding box for draw info - if (group->mSpatialPartition->mRenderByGroup) + /*if (group->mSpatialPartition->mRenderByGroup) { gGL.color4f(1.0f, 0.75f, 0.25f, 0.6f); for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i) @@ -2673,7 +2639,7 @@ void renderOctree(LLSpatialGroup* group) drawBoxOutline(center, size); } } - } + }*/ } // LLSpatialGroup::OctreeNode* node = group->mOctreeNode; @@ -2716,7 +2682,7 @@ void renderVisibility(LLSpatialGroup* group, LLCamera* camera) gGL.color4f(0.f, 0.75f, 0.f, 0.5f); pushBufferVerts(group, LLVertexBuffer::MAP_VERTEX); } - else if (camera && group->mOcclusionVerts.notNull()) + /*else if (camera && group->mOcclusionVerts.notNull()) { LLVertexBuffer::unbind(); group->mOcclusionVerts->setBuffer(LLVertexBuffer::MAP_VERTEX); @@ -2728,7 +2694,7 @@ void renderVisibility(LLSpatialGroup* group, LLCamera* camera) glColor4f(1.0f, 1.f, 1.f, 1.0f); group->mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, group->mBounds[0])); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - } + }*/ } } diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 379bbe614d..61200db2b9 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -57,6 +57,7 @@ #include "llworld.h" #include "pipeline.h" #include "llviewerjoystick.h" +#include "llviewerobjectlist.h" #include "llviewerparcelmgr.h" #include "llparcel.h" #include "llkeyboard.h" @@ -183,6 +184,21 @@ static bool handleReleaseGLBufferChanged(const LLSD& newvalue) return true; } +static bool handleFSAASamplesChanged(const LLSD& newvalue) +{ + if (gPipeline.isInit()) + { + gPipeline.releaseGLBuffers(); + gPipeline.createGLBuffers(); + + if (LLPipeline::sRenderDeferred) + { + LLViewerShaderMgr::instance()->setShaders(); + } + } + return true; +} + static bool handleAnisotropicChanged(const LLSD& newvalue) { LLImageGL::sGlobalUseAnisotropic = newvalue.asBoolean(); @@ -357,6 +373,16 @@ static bool handleResetVertexBuffersChanged(const LLSD&) return true; } +static bool handleRepartition(const LLSD&) +{ + if (gPipeline.isInit()) + { + gOctreeMaxCapacity = gSavedSettings.getU32("OctreeMaxNodeCapacity"); + gObjectList.repartitionObjects(); + } + return true; +} + static bool handleRenderDynamicLODChanged(const LLSD& newvalue) { LLPipeline::sDynamicLOD = newvalue.asBoolean(); @@ -560,6 +586,11 @@ void settings_setup_listeners() gSavedSettings.getControl("FirstPersonAvatarVisible")->getSignal()->connect(boost::bind(&handleRenderAvatarMouselookChanged, _2)); gSavedSettings.getControl("RenderFarClip")->getSignal()->connect(boost::bind(&handleRenderFarClipChanged, _2)); gSavedSettings.getControl("RenderTerrainDetail")->getSignal()->connect(boost::bind(&handleTerrainDetailChanged, _2)); + gSavedSettings.getControl("OctreeStaticObjectSizeFactor")->getSignal()->connect(boost::bind(&handleRepartition, _2)); + gSavedSettings.getControl("OctreeDistanceFactor")->getSignal()->connect(boost::bind(&handleRepartition, _2)); + gSavedSettings.getControl("OctreeMaxNodeCapacity")->getSignal()->connect(boost::bind(&handleRepartition, _2)); + gSavedSettings.getControl("OctreeAlphaDistanceFactor")->getSignal()->connect(boost::bind(&handleRepartition, _2)); + gSavedSettings.getControl("OctreeAttachmentSizeFactor")->getSignal()->connect(boost::bind(&handleRepartition, _2)); gSavedSettings.getControl("RenderUseTriStrips")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2)); gSavedSettings.getControl("RenderAnimateTrees")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2)); gSavedSettings.getControl("RenderAvatarVP")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2)); @@ -568,7 +599,7 @@ void settings_setup_listeners() gSavedSettings.getControl("RenderSpecularResX")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2)); gSavedSettings.getControl("RenderSpecularResY")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2)); gSavedSettings.getControl("RenderSpecularExponent")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2)); - gSavedSettings.getControl("RenderFSAASamples")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2)); + gSavedSettings.getControl("RenderFSAASamples")->getSignal()->connect(boost::bind(&handleFSAASamplesChanged, _2)); gSavedSettings.getControl("RenderAnisotropic")->getSignal()->connect(boost::bind(&handleAnisotropicChanged, _2)); gSavedSettings.getControl("RenderShadowResolutionScale")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2)); gSavedSettings.getControl("RenderGlow")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2)); diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index e41773d273..5dadd2c33b 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -582,6 +582,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) LLMemType mt_ug(LLMemType::MTYPE_DISPLAY_UPDATE_GEOM); const F32 max_geom_update_time = 0.005f*10.f*gFrameIntervalSeconds; // 50 ms/second update time gPipeline.createObjects(max_geom_update_time); + gPipeline.processPartitionQ(); gPipeline.updateGeom(max_geom_update_time); stop_glerror(); } @@ -836,7 +837,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) if (LLPipeline::sRenderDeferred && !LLPipeline::sUnderWaterRender) { gPipeline.mDeferredScreen.bindTarget(); - glClearColor(0,0,0,0); + glClearColor(1,0,1,1); gPipeline.mDeferredScreen.clear(); } else diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 6d493bfcd5..72cdb4b355 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -102,6 +102,7 @@ #include "lltrans.h" #include "llsdutil.h" #include "llmediaentry.h" +#include "llaccountingquota.h" //#define DEBUG_UPDATE_TYPE @@ -1893,7 +1894,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, // // - // WTF? If we're going to skip this message, why are we + // If we're going to skip this message, why are we // doing all the parenting, etc above? U32 packet_id = mesgsys->getCurrentRecvPacketID(); if (packet_id < mLatestRecvPacketID && @@ -5282,7 +5283,7 @@ bool LLViewerObject::specialHoverCursor() const || (mClickAction != 0); } -void LLViewerObject::updateFlags() +void LLViewerObject::updateFlags(BOOL physics_changed) { LLViewerRegion* regionp = getRegion(); if(!regionp) return; @@ -5295,12 +5296,15 @@ void LLViewerObject::updateFlags() gMessageSystem->addBOOL("IsTemporary", flagTemporaryOnRez() ); gMessageSystem->addBOOL("IsPhantom", flagPhantom() ); gMessageSystem->addBOOL("CastsShadows", flagCastShadows() ); - gMessageSystem->nextBlock("ExtraPhysics"); - gMessageSystem->addU8("PhysicsShapeType", getPhysicsShapeType() ); - gMessageSystem->addF32("Density", getPhysicsDensity() ); - gMessageSystem->addF32("Friction", getPhysicsFriction() ); - gMessageSystem->addF32("Restitution", getPhysicsRestitution() ); - gMessageSystem->addF32("GravityMultiplier", getPhysicsGravity() ); + 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() ); + } gMessageSystem->sendReliable( regionp->getHost() ); } @@ -5699,3 +5703,10 @@ public: LLHTTPRegistration<ObjectPhysicsProperties> gHTTPRegistrationObjectPhysicsProperties("/message/ObjectPhysicsProperties"); + + +void LLViewerObject::updateQuota( const SelectionQuota& quota ) +{ + //update quotas + mSelectionQuota = quota; +} diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index e417343bec..a0ad52df6b 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -43,6 +43,7 @@ #include "v3dmath.h" #include "v3math.h" #include "llvertexbuffer.h" +#include "llaccountingquota.h" class LLAgent; // TODO: Get rid of this. class LLAudioSource; @@ -488,7 +489,7 @@ public: void setRegion(LLViewerRegion *regionp); virtual void updateRegion(LLViewerRegion *regionp); - void updateFlags(); + void updateFlags(BOOL physics_changed = FALSE); BOOL setFlags(U32 flag, BOOL state); void setPhysicsShapeType(U8 type); void setPhysicsGravity(F32 gravity); @@ -643,7 +644,11 @@ protected: void unpackParticleSource(LLDataPacker &dp, const LLUUID& owner_id); void deleteParticleSource(); void setParticleSource(const LLPartSysData& particle_parameters, const LLUUID& owner_id); - + +public: + void updateQuota( const SelectionQuota& quota ); + const SelectionQuota& getQuota( void ) { return mSelectionQuota; } + 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 @@ -705,6 +710,8 @@ protected: F32 mPhysicsCost; F32 mLinksetPhysicsCost; + SelectionQuota mSelectionQuota; + bool mCostStale; mutable bool mPhysicsShapeUnknown; diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index ab2e07e4df..45c6777ae8 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -1418,6 +1418,15 @@ void LLViewerObjectList::onObjectCostFetchFailure(const LLUUID& object_id) 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()); @@ -1488,6 +1497,24 @@ void LLViewerObjectList::shiftObjects(const LLVector3 &offset) LLWorld::getInstance()->shiftRegions(offset); } +void LLViewerObjectList::repartitionObjects() +{ + for (vobj_list_t::iterator iter = mObjects.begin(); iter != mObjects.end(); ++iter) + { + LLViewerObject* objectp = *iter; + if (!objectp->isDead()) + { + LLDrawable* drawable = objectp->mDrawable; + if (drawable && !drawable->isDead()) + { + drawable->updateBinRadius(); + drawable->updateSpatialExtents(); + drawable->movePartition(); + } + } + } +} + //debug code bool LLViewerObjectList::hasMapObjectInRegion(LLViewerRegion* regionp) { diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h index 65374bca70..9d1b5cb56f 100644 --- a/indra/newview/llviewerobjectlist.h +++ b/indra/newview/llviewerobjectlist.h @@ -36,6 +36,7 @@ // project includes #include "llviewerobject.h" +#include "llaccountingquota.h" class LLCamera; class LLNetMap; @@ -101,7 +102,10 @@ public: F32 restitution, F32 gravity_multiplier); + void updateQuota( const LLUUID& objectId, const SelectionQuota& costs ); + void shiftObjects(const LLVector3 &offset); + void repartitionObjects(); bool hasMapObjectInRegion(LLViewerRegion* regionp) ; void clearAllMapObjectsInRegion(LLViewerRegion* regionp) ; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index f835351c04..c4ca5d37ce 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1480,6 +1480,8 @@ void LLViewerRegion::setSeedCapability(const std::string& url) LLSD capabilityNames = LLSD::emptyArray(); + capabilityNames.append("AccountingParcel"); + capabilityNames.append("AccountingSelection"); capabilityNames.append("AttachmentResources"); capabilityNames.append("AvatarPickerSearch"); capabilityNames.append("ChatSessionRequest"); @@ -1508,6 +1510,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url) capabilityNames.append("LandResources"); capabilityNames.append("MapLayer"); capabilityNames.append("MapLayerGod"); + capabilityNames.append("NewAccountingEnabled"); capabilityNames.append("NewFileAgentInventory"); capabilityNames.append("NewFileAgentInventoryVariablePrice"); capabilityNames.append("ObjectAdd"); @@ -1545,6 +1548,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url) capabilityNames.append("ViewerMetrics"); capabilityNames.append("ViewerStartAuction"); capabilityNames.append("ViewerStats"); + // Please add new capabilities alphabetically to reduce // merge conflicts. @@ -1658,3 +1662,4 @@ std::string LLViewerRegion::getDescription() const { return stringize(*this); } + diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index 9c5b85b77f..a6e5c47b86 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -275,6 +275,7 @@ public: F32 getLandHeightRegion(const LLVector3& region_pos); void getInfo(LLSD& info); + typedef enum { diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index 3e85802ba6..b818da205e 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -138,7 +138,9 @@ LLGLSLShader gDeferredGIFinalProgram; LLGLSLShader gDeferredPostGIProgram; LLGLSLShader gDeferredPostProgram; LLGLSLShader gDeferredPostNoDoFProgram; - +LLGLSLShader gDeferredWLSkyProgram; +LLGLSLShader gDeferredWLCloudProgram; +LLGLSLShader gDeferredStarProgram; LLGLSLShader gLuminanceGatherProgram; @@ -190,6 +192,9 @@ LLViewerShaderMgr::LLViewerShaderMgr() : mShaderList.push_back(&gDeferredGIFinalProgram); mShaderList.push_back(&gDeferredWaterProgram); mShaderList.push_back(&gDeferredAvatarAlphaProgram); + mShaderList.push_back(&gDeferredWLSkyProgram); + mShaderList.push_back(&gDeferredWLCloudProgram); + mShaderList.push_back(&gDeferredStarProgram); } LLViewerShaderMgr::~LLViewerShaderMgr() @@ -347,6 +352,9 @@ void LLViewerShaderMgr::setShaders() return; } + //setup preprocessor definitions + LLShaderMgr::instance()->mDefinitions["samples"] = llformat("%d", gGLManager.getNumFBOFSAASamples(gSavedSettings.getU32("RenderFSAASamples"))); + reentrance = true; // Make sure the compiled shader map is cleared before we recompile shaders. @@ -833,6 +841,9 @@ BOOL LLViewerShaderMgr::loadShadersEffects() { BOOL success = TRUE; + U32 samples = gGLManager.getNumFBOFSAASamples(gSavedSettings.getU32("RenderFSAASamples")); + bool multisample = samples > 1 && LLPipeline::sRenderDeferred && gGLManager.mHasTextureMultisample; + if (mVertexShaderLevel[SHADER_EFFECT] == 0) { gGlowProgram.unload(); @@ -858,10 +869,21 @@ BOOL LLViewerShaderMgr::loadShadersEffects() if (success) { + std::string fragment; + + if (multisample) + { + fragment = "effects/glowExtractMSF.glsl"; + } + else + { + fragment = "effects/glowExtractF.glsl"; + } + gGlowExtractProgram.mName = "Glow Extract Shader (Post)"; gGlowExtractProgram.mShaderFiles.clear(); gGlowExtractProgram.mShaderFiles.push_back(make_pair("effects/glowExtractV.glsl", GL_VERTEX_SHADER_ARB)); - gGlowExtractProgram.mShaderFiles.push_back(make_pair("effects/glowExtractF.glsl", GL_FRAGMENT_SHADER_ARB)); + gGlowExtractProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB)); gGlowExtractProgram.mShaderLevel = mVertexShaderLevel[SHADER_EFFECT]; success = gGlowExtractProgram.createShader(NULL, &mGlowExtractUniforms); if (!success) @@ -952,6 +974,9 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredGIProgram.unload(); gDeferredGIFinalProgram.unload(); gDeferredWaterProgram.unload(); + gDeferredWLSkyProgram.unload(); + gDeferredWLCloudProgram.unload(); + gDeferredStarProgram.unload(); return TRUE; } @@ -959,6 +984,9 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() BOOL success = TRUE; + U32 samples = gGLManager.getNumFBOFSAASamples(gSavedSettings.getU32("RenderFSAASamples")); + bool multisample = samples > 1 && gGLManager.mHasTextureMultisample; + if (success) { gDeferredDiffuseProgram.mName = "Deferred Diffuse Shader"; @@ -1039,40 +1067,83 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() if (success) { + std::string fragment; + + if (multisample) + { + fragment = "deferred/pointLightMSF.glsl"; + } + else + { + fragment = "deferred/pointLightF.glsl"; + } + gDeferredLightProgram.mName = "Deferred Light Shader"; gDeferredLightProgram.mShaderFiles.clear(); gDeferredLightProgram.mShaderFiles.push_back(make_pair("deferred/pointLightV.glsl", GL_VERTEX_SHADER_ARB)); - gDeferredLightProgram.mShaderFiles.push_back(make_pair("deferred/pointLightF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredLightProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB)); gDeferredLightProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; success = gDeferredLightProgram.createShader(NULL, NULL); } if (success) { + std::string fragment; + if (multisample) + { + fragment = "deferred/multiPointLightMSF.glsl"; + } + else + { + fragment = "deferred/multiPointLightF.glsl"; + } + gDeferredMultiLightProgram.mName = "Deferred MultiLight Shader"; gDeferredMultiLightProgram.mShaderFiles.clear(); gDeferredMultiLightProgram.mShaderFiles.push_back(make_pair("deferred/multiPointLightV.glsl", GL_VERTEX_SHADER_ARB)); - gDeferredMultiLightProgram.mShaderFiles.push_back(make_pair("deferred/multiPointLightF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredMultiLightProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB)); gDeferredMultiLightProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; success = gDeferredMultiLightProgram.createShader(NULL, NULL); } if (success) { + std::string fragment; + + if (multisample) + { + fragment = "deferred/spotLightMSF.glsl"; + } + else + { + fragment = "deferred/multiSpotLightF.glsl"; + } + gDeferredSpotLightProgram.mName = "Deferred SpotLight Shader"; gDeferredSpotLightProgram.mShaderFiles.clear(); gDeferredSpotLightProgram.mShaderFiles.push_back(make_pair("deferred/pointLightV.glsl", GL_VERTEX_SHADER_ARB)); - gDeferredSpotLightProgram.mShaderFiles.push_back(make_pair("deferred/multiSpotLightF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredSpotLightProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB)); gDeferredSpotLightProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; success = gDeferredSpotLightProgram.createShader(NULL, NULL); } if (success) { + std::string fragment; + + if (multisample) + { + fragment = "deferred/multiSpotLightMSF.glsl"; + } + else + { + fragment = "deferred/multiSpotLightF.glsl"; + } + gDeferredMultiSpotLightProgram.mName = "Deferred MultiSpotLight Shader"; gDeferredMultiSpotLightProgram.mShaderFiles.clear(); gDeferredMultiSpotLightProgram.mShaderFiles.push_back(make_pair("deferred/pointLightV.glsl", GL_VERTEX_SHADER_ARB)); - gDeferredMultiSpotLightProgram.mShaderFiles.push_back(make_pair("deferred/multiSpotLightF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredMultiSpotLightProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB)); gDeferredMultiSpotLightProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; success = gDeferredMultiSpotLightProgram.createShader(NULL, NULL); } @@ -1083,11 +1154,25 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() if (gSavedSettings.getBOOL("RenderDeferredSSAO")) { - fragment = "deferred/sunLightSSAOF.glsl"; + if (multisample) + { + fragment = "deferred/sunlightSSAOMSF.glsl"; + } + else + { + fragment = "deferred/sunLightSSAOF.glsl"; + } } else { - fragment = "deferred/sunLightF.glsl"; + if (multisample) + { + fragment = "deferred/sunlightMSF.glsl"; + } + else + { + fragment = "deferred/sunLightF.glsl"; + } } gDeferredSunProgram.mName = "Deferred Sun Shader"; @@ -1100,10 +1185,21 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() if (success) { + std::string fragment; + + if (multisample) + { + fragment = "deferred/blurLightMSF.glsl"; + } + else + { + fragment = "deferred/blurLightF.glsl"; + } + gDeferredBlurLightProgram.mName = "Deferred Blur Light Shader"; gDeferredBlurLightProgram.mShaderFiles.clear(); gDeferredBlurLightProgram.mShaderFiles.push_back(make_pair("deferred/blurLightV.glsl", GL_VERTEX_SHADER_ARB)); - gDeferredBlurLightProgram.mShaderFiles.push_back(make_pair("deferred/blurLightF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredBlurLightProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB)); gDeferredBlurLightProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; success = gDeferredBlurLightProgram.createShader(NULL, NULL); } @@ -1153,10 +1249,21 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() if (success) { + std::string fragment; + + if (multisample) + { + fragment = "deferred/softenLightMSF.glsl"; + } + else + { + fragment = "deferred/softenLightF.glsl"; + } + gDeferredSoftenProgram.mName = "Deferred Soften Shader"; gDeferredSoftenProgram.mShaderFiles.clear(); gDeferredSoftenProgram.mShaderFiles.push_back(make_pair("deferred/softenLightV.glsl", GL_VERTEX_SHADER_ARB)); - gDeferredSoftenProgram.mShaderFiles.push_back(make_pair("deferred/softenLightF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredSoftenProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB)); gDeferredSoftenProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; @@ -1239,32 +1346,96 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() if (success) { + std::string fragment; + if (multisample) + { + fragment = "deferred/postDeferredMSF.glsl"; + } + else + { + fragment = "deferred/postDeferredF.glsl"; + } + gDeferredPostProgram.mName = "Deferred Post Shader"; gDeferredPostProgram.mShaderFiles.clear(); gDeferredPostProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredV.glsl", GL_VERTEX_SHADER_ARB)); - gDeferredPostProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredPostProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB)); gDeferredPostProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; success = gDeferredPostProgram.createShader(NULL, NULL); } if (success) { + std::string fragment; + if (multisample) + { + fragment = "deferred/postDeferredNoDoFMSF.glsl"; + } + else + { + fragment = "deferred/postDeferredNoDoFF.glsl"; + } + gDeferredPostNoDoFProgram.mName = "Deferred Post Shader"; gDeferredPostNoDoFProgram.mShaderFiles.clear(); gDeferredPostNoDoFProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredV.glsl", GL_VERTEX_SHADER_ARB)); - gDeferredPostNoDoFProgram.mShaderFiles.push_back(make_pair("deferred/postDeferredNoDoFF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredPostNoDoFProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB)); gDeferredPostNoDoFProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; success = gDeferredPostNoDoFProgram.createShader(NULL, NULL); } + if (success) + { + gDeferredWLSkyProgram.mName = "Deferred Windlight Sky Shader"; + //gWLSkyProgram.mFeatures.hasGamma = true; + gDeferredWLSkyProgram.mShaderFiles.clear(); + gDeferredWLSkyProgram.mShaderFiles.push_back(make_pair("deferred/skyV.glsl", GL_VERTEX_SHADER_ARB)); + gDeferredWLSkyProgram.mShaderFiles.push_back(make_pair("deferred/skyF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredWLSkyProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; + gDeferredWLSkyProgram.mShaderGroup = LLGLSLShader::SG_SKY; + success = gDeferredWLSkyProgram.createShader(NULL, &mWLUniforms); + } + + if (success) + { + gDeferredWLCloudProgram.mName = "Deferred Windlight Cloud Program"; + gDeferredWLCloudProgram.mShaderFiles.clear(); + gDeferredWLCloudProgram.mShaderFiles.push_back(make_pair("deferred/cloudsV.glsl", GL_VERTEX_SHADER_ARB)); + gDeferredWLCloudProgram.mShaderFiles.push_back(make_pair("deferred/cloudsF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredWLCloudProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; + gDeferredWLCloudProgram.mShaderGroup = LLGLSLShader::SG_SKY; + success = gDeferredWLCloudProgram.createShader(NULL, &mWLUniforms); + } + + if (success) + { + gDeferredStarProgram.mName = "Deferred Star Program"; + gDeferredStarProgram.mShaderFiles.clear(); + gDeferredStarProgram.mShaderFiles.push_back(make_pair("deferred/starsV.glsl", GL_VERTEX_SHADER_ARB)); + gDeferredStarProgram.mShaderFiles.push_back(make_pair("deferred/starsF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredStarProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; + gDeferredStarProgram.mShaderGroup = LLGLSLShader::SG_SKY; + success = gDeferredStarProgram.createShader(NULL, &mWLUniforms); + } + if (mVertexShaderLevel[SHADER_DEFERRED] > 1) { if (success) { + std::string fragment; + if (multisample) + { + fragment = "deferred/edgeMSF.glsl"; + } + else + { + fragment = "deferred/edgeF.glsl"; + } + gDeferredEdgeProgram.mName = "Deferred Edge Shader"; gDeferredEdgeProgram.mShaderFiles.clear(); gDeferredEdgeProgram.mShaderFiles.push_back(make_pair("deferred/edgeV.glsl", GL_VERTEX_SHADER_ARB)); - gDeferredEdgeProgram.mShaderFiles.push_back(make_pair("deferred/edgeF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredEdgeProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB)); gDeferredEdgeProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; success = gDeferredEdgeProgram.createShader(NULL, NULL); } @@ -1272,8 +1443,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() if (mVertexShaderLevel[SHADER_DEFERRED] > 2) { - - if (success) { gDeferredPostGIProgram.mName = "Deferred Post GI Shader"; diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h index 72ac5e02ee..f5371f0619 100644 --- a/indra/newview/llviewershadermgr.h +++ b/indra/newview/llviewershadermgr.h @@ -374,7 +374,9 @@ extern LLGLSLShader gDeferredAttachmentShadowProgram; extern LLGLSLShader gDeferredAlphaProgram; extern LLGLSLShader gDeferredFullbrightProgram; extern LLGLSLShader gDeferredAvatarAlphaProgram; - +extern LLGLSLShader gDeferredWLSkyProgram; +extern LLGLSLShader gDeferredWLCloudProgram; +extern LLGLSLShader gDeferredStarProgram; extern LLGLSLShader gLuminanceGatherProgram; //current avatar shader parameter pointer diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index ec2b5a4c98..dc355362ce 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -4968,19 +4968,6 @@ void LLVOAvatar::resetSpecificJointPosition( const std::string& name ) //----------------------------------------------------------------------------- void LLVOAvatar::resetJointPositionsToDefault( void ) { - const LLVector3& avPos = getCharacterPosition(); - - //Reposition the pelvis - LLJoint* pPelvis = mRoot.findJoint("mPelvis"); - if ( pPelvis ) - { - pPelvis->setPosition( avPos + pPelvis->getPosition() ); - } - else - { - llwarns<<"Can't get pelvis joint."<<llendl; - return; - } //Subsequent joints are relative to pelvis for( S32 i = 0; i < (S32)mNumJoints; ++i ) @@ -4991,7 +4978,7 @@ void LLVOAvatar::resetJointPositionsToDefault( void ) pJoint->setId( LLUUID::null ); //restore joints to default positions, however skip over the pelvis - if ( pJoint && pPelvis != pJoint ) + if ( pJoint ) { pJoint->restoreOldXform(); } @@ -6028,6 +6015,14 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO ) if ( bindCnt > 0 ) { LLVOAvatar::resetJointPositionsToDefault(); + //Need to handle the repositioning of the cam, updating rig data etc during outfit editing + //This handles the case where we detach a replacement rig. + if ( gAgentCamera.cameraCustomizeAvatar() ) + { + gAgent.unpauseAnimation(); + //Still want to refocus on head bone + gAgentCamera.changeCameraToCustomizeAvatar(); + } } } } diff --git a/indra/newview/llvotree.cpp b/indra/newview/llvotree.cpp index 8946d4e0b6..3c7fe708e6 100644 --- a/indra/newview/llvotree.cpp +++ b/indra/newview/llvotree.cpp @@ -980,11 +980,6 @@ void LLVOTree::appendMesh(LLStrider<LLVector3>& vertices, for (S32 i = 0; i < index_count; i++) { U16 index = index_offset + i; - if (idx[index] >= vert_start + vert_count || - idx[index] < vert_start) - { - llerrs << "WTF?" << llendl; - } *indices++ = idx[index]-vert_start+cur_idx; } diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index e9a8c9b80a..cc443d32fb 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -1245,7 +1245,19 @@ BOOL LLVOVolume::calcLOD() if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_LOD_INFO)) { - setDebugText(llformat("%.2f:%.2f, %d", debug_distance, radius, cur_detail)); + //setDebugText(llformat("%.2f:%.2f, %d", debug_distance, radius, cur_detail)); + + F32 bin_radius = getBinRadius(); + F32 node_size = 0.f; + + LLSpatialGroup* group = mDrawable->getSpatialGroup(); + if (group) + { + LLSpatialGroup::OctreeNode* node = group->mOctreeNode; + node_size = node->getSize()[0]; + } + + setDebugText(llformat("%.2f:%.2f", bin_radius, node_size)); } if (cur_detail != mLOD) @@ -1274,6 +1286,15 @@ BOOL LLVOVolume::updateLOD() gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, FALSE); mLODChanged = TRUE; } + else + { + F32 new_radius = getBinRadius(); + F32 old_radius = mDrawable->getBinRadius(); + if (new_radius < old_radius * 0.9f || new_radius > old_radius*1.1f) + { + gPipeline.markPartitionMove(mDrawable); + } + } lod_changed = lod_changed || LLViewerObject::updateLOD(); @@ -3187,6 +3208,10 @@ F32 LLVOVolume::getBinRadius() F32 scale = 1.f; + S32 size_factor = llmax(gSavedSettings.getS32("OctreeStaticObjectSizeFactor"), 1); + S32 attachment_size_factor = llmax(gSavedSettings.getS32("OctreeAttachmentSizeFactor"), 1); + LLVector3 distance_factor = gSavedSettings.getVector3("OctreeDistanceFactor"); + LLVector3 alpha_distance_factor = gSavedSettings.getVector3("OctreeAlphaDistanceFactor"); const LLVector4a* ext = mDrawable->getSpatialExtents(); BOOL shrink_wrap = mDrawable->isAnimating(); @@ -3216,6 +3241,8 @@ F32 LLVOVolume::getBinRadius() radius = llmin(bounds.mV[1], bounds.mV[2]); radius = llmin(radius, bounds.mV[0]); radius *= 0.5f; + radius *= 1.f+mDrawable->mDistanceWRTCamera*alpha_distance_factor[1]; + radius += mDrawable->mDistanceWRTCamera*alpha_distance_factor[0]; } else if (shrink_wrap) { @@ -3226,24 +3253,19 @@ F32 LLVOVolume::getBinRadius() } else if (mDrawable->isStatic()) { - /*if (mDrawable->getRadius() < 2.0f) - { - radius = 16.f; - } - else - { - radius = llmax(mDrawable->getRadius(), 32.f); - }*/ - - radius = (((S32) mDrawable->getRadius())/2+1)*8; + radius = llmax((S32) mDrawable->getRadius(), 1)*size_factor; + radius *= 1.f + mDrawable->mDistanceWRTCamera * distance_factor[1]; + radius += mDrawable->mDistanceWRTCamera * distance_factor[0]; } else if (mDrawable->getVObj()->isAttachment()) { - radius = (((S32) (mDrawable->getRadius()*4)+1))*2; + radius = llmax((S32) mDrawable->getRadius(),1)*attachment_size_factor; } else { - radius = 8.f; + radius = mDrawable->getRadius(); + radius *= 1.f + mDrawable->mDistanceWRTCamera * distance_factor[1]; + radius += mDrawable->mDistanceWRTCamera * distance_factor[0]; } return llclamp(radius*scale, 0.5f, 256.f); @@ -4297,11 +4319,6 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group) face->getGeometryVolume(*volume, face->getTEOffset(), vobj->getRelativeXform(), vobj->getRelativeXformInvTrans(), face->getGeomIndex()); } - - if (!face) - { - llerrs << "WTF?" << llendl; - } } drawablep->clearState(LLDrawable::REBUILD_ALL); @@ -4356,10 +4373,7 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group) group->clearState(LLSpatialGroup::MESH_DIRTY | LLSpatialGroup::NEW_DRAWINFO); } - if (group && group->isState(LLSpatialGroup::NEW_DRAWINFO)) - { - llerrs << "WTF?" << llendl; - } + llassert(!group || !group->isState(LLSpatialGroup::NEW_DRAWINFO)); } void LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, std::vector<LLFace*>& faces, BOOL distance_sort) diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index f64eb89866..fadae7c71e 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -392,6 +392,7 @@ void LLPipeline::init() { LLMemType mt(LLMemType::MTYPE_PIPELINE_INIT); + gOctreeMaxCapacity = gSavedSettings.getU32("OctreeMaxNodeCapacity"); sDynamicLOD = gSavedSettings.getBOOL("RenderDynamicLOD"); sRenderBump = gSavedSettings.getBOOL("RenderObjectBump"); sUseTriStrips = gSavedSettings.getBOOL("RenderUseTriStrips"); @@ -580,11 +581,6 @@ void LLPipeline::allocatePhysicsBuffer() if (mPhysicsDisplay.getWidth() != resX || mPhysicsDisplay.getHeight() != resY) { mPhysicsDisplay.allocate(resX, resY, GL_RGBA, TRUE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE); - if (mSampleBuffer.getWidth() == mPhysicsDisplay.getWidth() && - mSampleBuffer.getHeight() == mPhysicsDisplay.getHeight()) - { - mPhysicsDisplay.setSampleBuffer(&mSampleBuffer); - } } } @@ -594,8 +590,9 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY) mScreenWidth = resX; mScreenHeight = resY; - //never use more than 4 samples for render targets - U32 samples = llmin(gSavedSettings.getU32("RenderFSAASamples"), (U32) 4); + //cap samples at 4 for render targets to avoid out of memory errors + U32 samples = gGLManager.getNumFBOFSAASamples(gSavedSettings.getU32("RenderFSAASamples")); + if (gGLManager.mIsATI) { //disable multisampling of render targets where ATI is involved samples = 0; @@ -621,16 +618,22 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY) bool gi = LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED); //allocate deferred rendering color buffers - mDeferredScreen.allocate(resX, resY, GL_RGBA, TRUE, TRUE, LLTexUnit::TT_RECT_TEXTURE, FALSE); - mDeferredDepth.allocate(resX, resY, 0, TRUE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE); + mDeferredScreen.allocate(resX, resY, GL_RGBA, TRUE, TRUE, LLTexUnit::TT_RECT_TEXTURE, FALSE, samples); + mDeferredDepth.allocate(resX, resY, 0, TRUE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE, samples); addDeferredAttachments(mDeferredScreen); - mScreen.allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE); + mScreen.allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE, samples); + +#if LL_DARWIN + // As of OS X 10.6.7, Apple doesn't support multiple color formats in a single FBO + mEdgeMap.allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE); +#else mEdgeMap.allocate(resX, resY, GL_ALPHA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE); +#endif if (shadow_detail > 0 || ssao) { //only need mDeferredLight[0] for shadows OR ssao - mDeferredLight[0].allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE); + mDeferredLight[0].allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE); } else { @@ -639,7 +642,7 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY) if (ssao) { //only need mDeferredLight[1] for ssao - mDeferredLight[1].allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE); + mDeferredLight[1].allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, false); } else { @@ -648,10 +651,15 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY) if (gi) { //only need mDeferredLight[2] and mGIMapPost for gi - mDeferredLight[2].allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE); + mDeferredLight[2].allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, false); for (U32 i = 0; i < 2; i++) { +#if LL_DARWIN + // As of OS X 10.6.7, Apple doesn't support multiple color formats in a single FBO + mGIMapPost[i].allocate(resX,resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE); +#else mGIMapPost[i].allocate(resX,resY, GL_RGB, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE); +#endif } } else @@ -666,8 +674,12 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY) F32 scale = gSavedSettings.getF32("RenderShadowResolutionScale"); +#if LL_DARWIN + U32 shadow_fmt = 0; +#else //HACK: make alpha masking work on ATI depth shadows (work around for ATI driver bug) U32 shadow_fmt = gGLManager.mIsATI ? GL_ALPHA : 0; +#endif if (shadow_detail > 0) { //allocate 4 sun shadow maps @@ -729,35 +741,9 @@ void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY) mScreen.allocate(resX, resY, GL_RGBA, TRUE, TRUE, LLTexUnit::TT_RECT_TEXTURE, FALSE); } - if (LLRenderTarget::sUseFBO && samples > 1) - { - mSampleBuffer.allocate(resX,resY,GL_RGBA,TRUE,TRUE,LLTexUnit::TT_RECT_TEXTURE,FALSE,samples); - if (LLPipeline::sRenderDeferred) - { - addDeferredAttachments(mSampleBuffer); - mDeferredScreen.setSampleBuffer(&mSampleBuffer); - mEdgeMap.setSampleBuffer(&mSampleBuffer); - } - - mScreen.setSampleBuffer(&mSampleBuffer); - - stop_glerror(); - } - else - { - mSampleBuffer.release(); - } - if (LLPipeline::sRenderDeferred) { //share depth buffer between deferred targets mDeferredScreen.shareDepthBuffer(mScreen); - for (U32 i = 0; i < 3; i++) - { //share stencil buffer with screen space lightmap to stencil out sky - if (mDeferredLight[i].getTexture(0)) - { - mDeferredScreen.shareDepthBuffer(mDeferredLight[i]); - } - } } gGL.getTexUnit(0)->disable(); @@ -826,7 +812,6 @@ void LLPipeline::releaseGLBuffers() mScreen.release(); mPhysicsDisplay.release(); mUIScreen.release(); - mSampleBuffer.release(); mDeferredScreen.release(); mDeferredDepth.release(); for (U32 i = 0; i < 3; i++) @@ -2535,6 +2520,32 @@ void LLPipeline::markGLRebuild(LLGLUpdate* glu) } } +void LLPipeline::markPartitionMove(LLDrawable* drawable) +{ + if (!drawable->isState(LLDrawable::PARTITION_MOVE) && + !drawable->getPositionGroup().equals3(LLVector4a::getZero())) + { + drawable->setState(LLDrawable::PARTITION_MOVE); + mPartitionQ.push_back(drawable); + } +} + +void LLPipeline::processPartitionQ() +{ + for (LLDrawable::drawable_list_t::iterator iter = mPartitionQ.begin(); iter != mPartitionQ.end(); ++iter) + { + LLDrawable* drawable = *iter; + if (!drawable->isDead()) + { + drawable->updateBinRadius(); + drawable->movePartition(); + } + drawable->clearState(LLDrawable::PARTITION_MOVE); + } + + mPartitionQ.clear(); +} + void LLPipeline::markRebuild(LLSpatialGroup* group, BOOL priority) { LLMemType mt(LLMemType::MTYPE_PIPELINE); @@ -5904,7 +5915,6 @@ LLSpatialPartition* LLPipeline::getSpatialPartition(LLViewerObject* vobj) return NULL; } - void LLPipeline::resetVertexBuffers(LLDrawable* drawable) { if (!drawable || drawable->isDead()) @@ -6180,11 +6190,8 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield) gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.getTexUnit(0)->disable(); - gGL.getTexUnit(0)->enable(LLTexUnit::TT_RECT_TEXTURE); - gGL.getTexUnit(0)->bind(&mScreen); - + mScreen.bindTexture(0, 0); + gGL.color4f(1,1,1,1); gPipeline.enableLightsFullbright(LLColor4(1,1,1,1)); gGL.begin(LLRender::TRIANGLE_STRIP); @@ -6199,7 +6206,7 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield) gGL.end(); - gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); + gGL.getTexUnit(0)->unbind(mScreen.getUsage()); mGlow[2].flush(); } @@ -6227,7 +6234,6 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield) for (S32 i = 0; i < kernel; i++) { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); { LLFastTimer ftm(FTM_RENDER_BLOOM_FBO); mGlow[i%2].bindTarget(); @@ -6420,11 +6426,10 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield) shader->uniform1f("magnification", magnification); } - S32 channel = shader->enableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_RECT_TEXTURE); + S32 channel = shader->enableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, mScreen.getUsage()); if (channel > -1) { mScreen.bindTexture(0, channel); - gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); } //channel = shader->enableTexture(LLViewerShaderMgr::DEFERRED_DEPTH, LLTexUnit::TT_RECT_TEXTURE); //if (channel > -1) @@ -6567,21 +6572,21 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, LLRen shader.bind(); S32 channel = 0; - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_RECT_TEXTURE); + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, mDeferredScreen.getUsage()); if (channel > -1) { mDeferredScreen.bindTexture(0,channel); gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); } - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_SPECULAR, LLTexUnit::TT_RECT_TEXTURE); + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_SPECULAR, mDeferredScreen.getUsage()); if (channel > -1) { mDeferredScreen.bindTexture(1, channel); gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); } - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_NORMAL, LLTexUnit::TT_RECT_TEXTURE); + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_NORMAL, mDeferredScreen.getUsage()); if (channel > -1) { mDeferredScreen.bindTexture(2, channel); @@ -6704,22 +6709,16 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, LLRen shader.uniformMatrix4fv("gi_norm_mat", 1, FALSE, mGINormalMatrix.m); } } - - /*channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_POSITION, LLTexUnit::TT_RECT_TEXTURE); - if (channel > -1) - { - mDeferredScreen.bindTexture(3, channel); - }*/ + stop_glerror(); - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_DEPTH, LLTexUnit::TT_RECT_TEXTURE); + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_DEPTH, mDeferredDepth.getUsage()); if (channel > -1) { gGL.getTexUnit(channel)->bind(&mDeferredDepth, TRUE); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); stop_glerror(); - glTexParameteri(LLTexUnit::getInternalType(mDeferredDepth.getUsage()), GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); - glTexParameteri(LLTexUnit::getInternalType(mDeferredDepth.getUsage()), GL_DEPTH_TEXTURE_MODE_ARB, GL_ALPHA); + //glTexParameteri(LLTexUnit::getInternalType(mDeferredDepth.getUsage()), GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); + //glTexParameteri(LLTexUnit::getInternalType(mDeferredDepth.getUsage()), GL_DEPTH_TEXTURE_MODE_ARB, GL_ALPHA); stop_glerror(); @@ -6748,7 +6747,7 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, LLRen stop_glerror(); - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_LIGHT, LLTexUnit::TT_RECT_TEXTURE); + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_LIGHT, mDeferredLight[light_index].getUsage()); if (channel > -1) { mDeferredLight[light_index].bindTexture(0, channel); @@ -6968,9 +6967,9 @@ void LLPipeline::renderDeferredLighting() } //ati doesn't seem to love actually using the stencil buffer on FBO's - LLGLEnable stencil(GL_STENCIL_TEST); - glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + LLGLDisable stencil(GL_STENCIL_TEST); + //glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF); + //glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); gGL.setColorMask(true, true); @@ -7772,33 +7771,41 @@ void LLPipeline::setupSpotLight(LLGLSLShader& shader, LLDrawable* drawablep) LLViewerTexture* img = volume->getLightTexture(); + if (img == NULL) + { + img = LLViewerFetchedTexture::sWhiteImagep; + } + S32 channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_PROJECTION); - if (channel > -1 && img) + if (channel > -1) { - gGL.getTexUnit(channel)->bind(img); + if (img) + { + gGL.getTexUnit(channel)->bind(img); - F32 lod_range = logf(img->getWidth())/logf(2.f); + F32 lod_range = logf(img->getWidth())/logf(2.f); - shader.uniform1f("proj_focus", focus); - shader.uniform1f("proj_lod", lod_range); - shader.uniform1f("proj_ambient_lod", llclamp((proj_range-focus)/proj_range*lod_range, 0.f, 1.f)); + shader.uniform1f("proj_focus", focus); + shader.uniform1f("proj_lod", lod_range); + shader.uniform1f("proj_ambient_lod", llclamp((proj_range-focus)/proj_range*lod_range, 0.f, 1.f)); + } } + } void LLPipeline::unbindDeferredShader(LLGLSLShader &shader) { stop_glerror(); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_POSITION, LLTexUnit::TT_RECT_TEXTURE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_NORMAL, LLTexUnit::TT_RECT_TEXTURE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_RECT_TEXTURE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_SPECULAR, LLTexUnit::TT_RECT_TEXTURE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_DEPTH, LLTexUnit::TT_RECT_TEXTURE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_LIGHT, LLTexUnit::TT_RECT_TEXTURE); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_NORMAL, mDeferredScreen.getUsage()); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, mDeferredScreen.getUsage()); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_SPECULAR, mDeferredScreen.getUsage()); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_DEPTH, mDeferredScreen.getUsage()); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_LIGHT, mDeferredLight[0].getUsage()); shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_LIGHT, LLTexUnit::TT_RECT_TEXTURE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_EDGE, LLTexUnit::TT_RECT_TEXTURE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_SUN_LIGHT, LLTexUnit::TT_RECT_TEXTURE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_LOCAL_LIGHT, LLTexUnit::TT_RECT_TEXTURE); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_EDGE, mEdgeMap.getUsage()); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_SUN_LIGHT, mDeferredLight[1].getUsage()); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_LOCAL_LIGHT, mDeferredLight[2].getUsage()); shader.disableTexture(LLViewerShaderMgr::DEFERRED_LUMINANCE); shader.disableTexture(LLViewerShaderMgr::DIFFUSE_MAP); shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_MIP); @@ -9394,6 +9401,11 @@ void LLPipeline::generateSunShadow(LLCamera& camera) mShadow[i+4].flush(); } } + else + { //no spotlight shadows + mShadowSpotLight[0] = mShadowSpotLight[1] = NULL; + } + if (!gSavedSettings.getBOOL("CameraOffset")) { diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index e9a250cd6d..e967b8c31e 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -157,7 +157,8 @@ public: void markGLRebuild(LLGLUpdate* glu); void markRebuild(LLSpatialGroup* group, BOOL priority = FALSE); void markRebuild(LLDrawable *drawablep, LLDrawable::EDrawableFlags flag = LLDrawable::REBUILD_ALL, BOOL priority = FALSE); - + void markPartitionMove(LLDrawable* drawablep); + //get the object between start and end that's closest to start. LLViewerObject* lineSegmentIntersectInWorld(const LLVector3& start, const LLVector3& end, BOOL pick_transparent, @@ -211,6 +212,7 @@ public: void updateCull(LLCamera& camera, LLCullResult& result, S32 water_clip = 0, LLPlane* plane = NULL); //if water_clip is 0, ignore water plane, 1, cull to above plane, -1, cull to below plane void createObjects(F32 max_dtime); void createObject(LLViewerObject* vobj); + void processPartitionQ(); void updateGeom(F32 max_dtime); void updateGL(); void rebuildPriorityGroups(); @@ -524,7 +526,6 @@ public: LLRenderTarget mEdgeMap; LLRenderTarget mDeferredDepth; LLRenderTarget mDeferredLight[3]; - LLMultisampleBuffer mSampleBuffer; LLRenderTarget mGIMap; LLRenderTarget mGIMapPost[2]; LLRenderTarget mLuminanceMap; @@ -637,6 +638,9 @@ protected: LLDrawable::drawable_list_t mBuildQ2; // non-priority LLSpatialGroup::sg_vector_t mGroupQ1; //priority LLSpatialGroup::sg_vector_t mGroupQ2; // non-priority + + LLDrawable::drawable_list_t mPartitionQ; //drawables that need to update their spatial partition radius + bool mGroupQ2Locked; bool mGroupQ1Locked; diff --git a/indra/newview/skins/default/xui/en/floater_model_preview.xml b/indra/newview/skins/default/xui/en/floater_model_preview.xml index d08c3e7078..dce55dae12 100644 --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml @@ -26,7 +26,7 @@ <text left="15" bottom="25" follows="top|left" height="15" name="name_label"> Name: </text> - <line_editor bottom_delta="20" follows="top|left|right" height="19" + <line_editor bottom_delta="20" follows="top|left|right" height="19" max_length_bytes="64" name="description_form" prevalidate_callback="ascii" width="290" /> <text bottom_delta="20" left="15" follows="left|top" height="15" name="lod_label"> @@ -219,7 +219,7 @@ <text left="10" top_pad="35" follows="top|left" width="240" height="15"> Generate Normals </text> - <text left="35" top_pad="5" follows="top|left" width="100" height="15"> + <text left="35" top_pad="5" follows="top|left" width="100" height="15" name="crease_label"> Crease Angle: </text> <spinner follows="top|left" left_pad="5" min_val="0" max_val="180" value="75" width="60" height="20" name="crease_angle"/> diff --git a/indra/newview/skins/default/xui/en/floater_model_wizard.xml b/indra/newview/skins/default/xui/en/floater_model_wizard.xml index 92d57b20be..47b2e5fd79 100644 --- a/indra/newview/skins/default/xui/en/floater_model_wizard.xml +++ b/indra/newview/skins/default/xui/en/floater_model_wizard.xml @@ -572,9 +572,9 @@ Advanced users familiar with 3d content creation tools may prefer to use the [se bg_opaque_color="DkGray2" background_visible="true" background_opaque="true"> - <text top="25" left="30" text_color="White" font="SansSerifSmallBold" width="300" height="4">Performance</text> + <text top="15" left="20" text_color="White" font="SansSerifSmallBold" width="110" height="30" wrap="true" halign="center">Higher Performance</text> <text top="45" left="10" halign="center" width="130" word_wrap="true" font="SansSerifSmall" height="80">Faster rendering but less detailed; lowers Resource (prim) cost.</text> - <text top="25" left="390" text_color="White" font="SansSerifSmallBold" width="300" height="4">Accuracy</text> + <text top="15" left="372" text_color="White" font="SansSerifSmallBold" width="90" height="30" wrap="true" halign="center">Higher Accuracy</text> <text top="45" left="360" halign="center" width="130" word_wrap="true" font="SansSerifSmall" height="80">More detailed model but slower; increases Resource (prim) cost.</text> <slider diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml index 05d47506db..40bf7bfed7 100644 --- a/indra/newview/skins/default/xui/en/floater_tools.xml +++ b/indra/newview/skins/default/xui/en/floater_tools.xml @@ -1608,41 +1608,6 @@ even though the user gets a free copy. name="Sculpted" value="Sculpted" /> </combo_box> - <combo_box - height="19" - layout="topleft" - name="material" - top_pad="5" - width="150"> - <combo_box.item - label="Stone" - name="Stone" - value="Stone" /> - <combo_box.item - label="Metal" - name="Metal" - value="Metal" /> - <combo_box.item - label="Glass" - name="Glass" - value="Glass" /> - <combo_box.item - label="Wood" - name="Wood" - value="Wood" /> - <combo_box.item - label="Flesh" - name="Flesh" - value="Flesh" /> - <combo_box.item - label="Plastic" - name="Plastic" - value="Plastic" /> - <combo_box.item - label="Rubber" - name="Rubber" - value="Rubber" /> - </combo_box> <text type="string" length="1" @@ -2426,6 +2391,41 @@ even though the user gets a free copy. name="Physics Shape Type Combo Ctrl" tool_tip="Choose the physics shape type" width="108"/> + <combo_box + height="19" + layout="topleft" + name="material" + top_pad="5" + width="150"> + <combo_box.item + label="Stone" + name="Stone" + value="Stone" /> + <combo_box.item + label="Metal" + name="Metal" + value="Metal" /> + <combo_box.item + label="Glass" + name="Glass" + value="Glass" /> + <combo_box.item + label="Wood" + name="Wood" + value="Wood" /> + <combo_box.item + label="Flesh" + name="Flesh" + value="Flesh" /> + <combo_box.item + label="Plastic" + name="Plastic" + value="Plastic" /> + <combo_box.item + label="Rubber" + name="Rubber" + value="Rubber" /> + </combo_box> <spinner follows="left|top" @@ -2441,16 +2441,6 @@ even though the user gets a free copy. top_pad="10" width="132" /> - <check_box - height="19" - label="Override material" - layout="topleft" - left_delta="0" - name="Physics Material Override" - tool_tip="Override Material" - top_pad="10" - width="132" /> - <spinner follows="left|top" height="19" diff --git a/indra/newview/skins/default/xui/en/menu_inventory_add.xml b/indra/newview/skins/default/xui/en/menu_inventory_add.xml index 484af63097..b36b82ebd8 100644 --- a/indra/newview/skins/default/xui/en/menu_inventory_add.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory_add.xml @@ -42,7 +42,7 @@ <menu_item_call.on_enable function="File.EnableUpload" /> </menu_item_call> - <menu_item_call + <menu_item_call label="Model..." layout="topleft" name="Upload Model"> @@ -54,6 +54,18 @@ <menu_item_call.on_visible function="File.VisibleUploadModel"/> </menu_item_call> + <menu_item_call + label="Model Wizard..." + layout="topleft" + name="Upload Model Wizard"> + <menu_item_call.on_click + function="Floater.Show" + parameter="upload_model_wizard" /> + <menu_item_call.on_enable + function="File.EnableUploadModel" /> + <menu_item_call.on_visible + function="File.VisibleUploadModel"/> + </menu_item_call> <menu_item_call label="Bulk (L$[COST] per file)..." layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_region_general.xml b/indra/newview/skins/default/xui/en/panel_region_general.xml index e0d9f3f714..3f9195d092 100644 --- a/indra/newview/skins/default/xui/en/panel_region_general.xml +++ b/indra/newview/skins/default/xui/en/panel_region_general.xml @@ -134,6 +134,7 @@ top="190" width="80" /> <check_box + visible="FALSE" height="20" label="Allow Mesh Objects" layout="topleft" |