diff options
225 files changed, 9588 insertions, 4823 deletions
@@ -507,3 +507,6 @@ d07f76c5b9860fb87924d00ca729f7d4532534d6 3.7.29-release 3f61ed662347dc7c6941b8266e72746a66d90e2a 3.8.1-release 3a62616f3dd8bd512fcdfd29ef033b2505b11213 3.8.2-release 60572f718879f786f6bc8b5c9373ebebf4693078 3.8.3-release +27e3cf444c4cc645884960a61325a9ee0e9a2d0f 3.8.4-release +e821ef17c6edea4a59997719d8ba416d8c16e143 3.8.5-release +5a5bd148943bfb46cf2ff2ccf376c42dee93d19b 3.8.6-release diff --git a/doc/contributions.txt b/doc/contributions.txt index 211d4fcf08..7493e96c39 100755 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -185,6 +185,7 @@ Ansariel Hiller BUG-3764 STORM-1984 STORM-1979 + MAINT-5533 Aralara Rajal Arare Chantilly CHUIBUG-191 diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 7a320ffc59..1a3b6c5117 100755 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -191,7 +191,7 @@ if (DARWIN) add_definitions(-DLL_DARWIN=1) set(CMAKE_CXX_LINK_FLAGS "-Wl,-no_compact_unwind -Wl,-headerpad_max_install_names,-search_paths_first") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_CXX_LINK_FLAGS}") - set(DARWIN_extra_cstar_flags "-g") + set(DARWIN_extra_cstar_flags "-g -Wno-unused-local-typedef -Wno-deprecated-declarations") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${DARWIN_extra_cstar_flags}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${DARWIN_extra_cstar_flags}") # NOTE: it's critical that the optimization flag is put in front. diff --git a/indra/cmake/Variables.cmake b/indra/cmake/Variables.cmake index 76d92d7a48..2fb47c58a7 100755 --- a/indra/cmake/Variables.cmake +++ b/indra/cmake/Variables.cmake @@ -130,10 +130,10 @@ endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(DARWIN 1) - # now we only support Xcode 6.0 using 10.9 (Mavericks), minimum OS 10.7 (Lion) - set(XCODE_VERSION 6.0) + # now we only support Xcode 7.0 using 10.11 (El Capitan), minimum OS 10.7 (Lion) + set(XCODE_VERSION 7.0) set(CMAKE_OSX_DEPLOYMENT_TARGET 10.7) - set(CMAKE_OSX_SYSROOT macosx10.9) + set(CMAKE_OSX_SYSROOT macosx10.11) set(CMAKE_XCODE_ATTRIBUTE_GCC_VERSION "com.apple.compilers.llvm.clang.1_0") set(CMAKE_XCODE_ATTRIBUTE_GCC_OPTIMIZATION_LEVEL 3) diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 1459b9ada2..5863310162 100755 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -37,6 +37,7 @@ set(llcommon_SOURCE_FILES llbase32.cpp llbase64.cpp llbitpack.cpp + llcallbacklist.cpp llcommon.cpp llcommonutils.cpp llcoros.cpp @@ -129,6 +130,7 @@ set(llcommon_HEADER_FILES llbase64.h llbitpack.h llboost.h + llcallbacklist.h llcommon.h llcommonutils.h llcoros.h diff --git a/indra/llcommon/llcallbacklist.cpp b/indra/llcommon/llcallbacklist.cpp new file mode 100644 index 0000000000..541ff75ee4 --- /dev/null +++ b/indra/llcommon/llcallbacklist.cpp @@ -0,0 +1,230 @@ +/** + * @file llcallbacklist.cpp + * @brief A simple list of callback functions to call. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llcallbacklist.h" +#include "lleventtimer.h" +#include "llerrorlegacy.h" + +// Globals +// +LLCallbackList gIdleCallbacks; + +// +// Member functions +// + +LLCallbackList::LLCallbackList() +{ + // nothing +} + +LLCallbackList::~LLCallbackList() +{ +} + + +void LLCallbackList::addFunction( callback_t func, void *data) +{ + if (!func) + { + return; + } + + // only add one callback per func/data pair + // + if (containsFunction(func)) + { + return; + } + + callback_pair_t t(func, data); + mCallbackList.push_back(t); +} + +bool LLCallbackList::containsFunction( callback_t func, void *data) +{ + callback_pair_t t(func, data); + callback_list_t::iterator iter = find(func,data); + if (iter != mCallbackList.end()) + { + return TRUE; + } + else + { + return FALSE; + } +} + + +bool LLCallbackList::deleteFunction( callback_t func, void *data) +{ + callback_list_t::iterator iter = find(func,data); + if (iter != mCallbackList.end()) + { + mCallbackList.erase(iter); + return TRUE; + } + else + { + return FALSE; + } +} + +inline +LLCallbackList::callback_list_t::iterator +LLCallbackList::find(callback_t func, void *data) +{ + callback_pair_t t(func, data); + return std::find(mCallbackList.begin(), mCallbackList.end(), t); +} + +void LLCallbackList::deleteAllFunctions() +{ + mCallbackList.clear(); +} + + +void LLCallbackList::callFunctions() +{ + for (callback_list_t::iterator iter = mCallbackList.begin(); iter != mCallbackList.end(); ) + { + callback_list_t::iterator curiter = iter++; + curiter->first(curiter->second); + } +} + +// Shim class to allow arbitrary boost::bind +// expressions to be run as one-time idle callbacks. +class OnIdleCallbackOneTime +{ +public: + OnIdleCallbackOneTime(nullary_func_t callable): + mCallable(callable) + { + } + static void onIdle(void *data) + { + gIdleCallbacks.deleteFunction(onIdle, data); + OnIdleCallbackOneTime* self = reinterpret_cast<OnIdleCallbackOneTime*>(data); + self->call(); + delete self; + } + void call() + { + mCallable(); + } +private: + nullary_func_t mCallable; +}; + +void doOnIdleOneTime(nullary_func_t callable) +{ + OnIdleCallbackOneTime* cb_functor = new OnIdleCallbackOneTime(callable); + gIdleCallbacks.addFunction(&OnIdleCallbackOneTime::onIdle,cb_functor); +} + +// Shim class to allow generic boost functions to be run as +// recurring idle callbacks. Callable should return true when done, +// false to continue getting called. +class OnIdleCallbackRepeating +{ +public: + OnIdleCallbackRepeating(bool_func_t callable): + mCallable(callable) + { + } + // Will keep getting called until the callable returns true. + static void onIdle(void *data) + { + OnIdleCallbackRepeating* self = reinterpret_cast<OnIdleCallbackRepeating*>(data); + bool done = self->call(); + if (done) + { + gIdleCallbacks.deleteFunction(onIdle, data); + delete self; + } + } + bool call() + { + return mCallable(); + } +private: + bool_func_t mCallable; +}; + +void doOnIdleRepeating(bool_func_t callable) +{ + OnIdleCallbackRepeating* cb_functor = new OnIdleCallbackRepeating(callable); + gIdleCallbacks.addFunction(&OnIdleCallbackRepeating::onIdle,cb_functor); +} + +class NullaryFuncEventTimer: public LLEventTimer +{ +public: + NullaryFuncEventTimer(nullary_func_t callable, F32 seconds): + LLEventTimer(seconds), + mCallable(callable) + { + } + +private: + BOOL tick() + { + mCallable(); + return TRUE; + } + + nullary_func_t mCallable; +}; + +// Call a given callable once after specified interval. +void doAfterInterval(nullary_func_t callable, F32 seconds) +{ + new NullaryFuncEventTimer(callable, seconds); +} + +class BoolFuncEventTimer: public LLEventTimer +{ +public: + BoolFuncEventTimer(bool_func_t callable, F32 seconds): + LLEventTimer(seconds), + mCallable(callable) + { + } +private: + BOOL tick() + { + return mCallable(); + } + + bool_func_t mCallable; +}; + +// Call a given callable every specified number of seconds, until it returns true. +void doPeriodically(bool_func_t callable, F32 seconds) +{ + new BoolFuncEventTimer(callable, seconds); +} diff --git a/indra/newview/llcallbacklist.h b/indra/llcommon/llcallbacklist.h index 0516c9cdb4..89716cd74c 100755..100644 --- a/indra/newview/llcallbacklist.h +++ b/indra/llcommon/llcallbacklist.h @@ -28,27 +28,34 @@ #define LL_LLCALLBACKLIST_H #include "llstl.h" +#include <boost/function.hpp> +#include <list> class LLCallbackList { public: typedef void (*callback_t)(void*); + + typedef std::pair< callback_t,void* > callback_pair_t; + // NOTE: It is confirmed that we DEPEND on the order provided by using a list :( + // + typedef std::list< callback_pair_t > callback_list_t; LLCallbackList(); ~LLCallbackList(); - void addFunction( callback_t func, void *data = NULL ); // register a callback, which will be called as func(data) - BOOL containsFunction( callback_t func, void *data = NULL ); // true if list already contains the function/data pair - BOOL deleteFunction( callback_t func, void *data = NULL ); // removes the first instance of this function/data pair from the list, false if not found - void callFunctions(); // calls all functions + void addFunction( callback_t func, void *data = NULL ); // register a callback, which will be called as func(data) + bool containsFunction( callback_t func, void *data = NULL ); // true if list already contains the function/data pair + bool deleteFunction( callback_t func, void *data = NULL ); // removes the first instance of this function/data pair from the list, false if not found + void callFunctions(); // calls all functions void deleteAllFunctions(); static void test(); protected: - // Use a list so that the callbacks are ordered in case that matters - typedef std::pair<callback_t,void*> callback_pair_t; - typedef std::list<callback_pair_t > callback_list_t; + + inline callback_list_t::iterator find(callback_t func, void *data); + callback_list_t mCallbackList; }; diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp index ae11988df8..3a8eabac09 100755 --- a/indra/llcommon/llmemory.cpp +++ b/indra/llcommon/llmemory.cpp @@ -2176,8 +2176,7 @@ void* ll_aligned_malloc_fallback( size_t size, int align ) SYSTEM_INFO sysinfo; GetSystemInfo(&sysinfo); - unsigned int for_alloc = sysinfo.dwPageSize; - while(for_alloc < size) for_alloc += sysinfo.dwPageSize; + unsigned int for_alloc = (size/sysinfo.dwPageSize + !!(size%sysinfo.dwPageSize)) * sysinfo.dwPageSize; void *p = VirtualAlloc(NULL, for_alloc+sysinfo.dwPageSize, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); if(NULL == p) { diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index c4c9cc0566..0fb257aab1 100755 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -28,6 +28,7 @@ #include "linden_common.h" #include "llunits.h" +#include "stdtypes.h" #if !LL_WINDOWS #include <stdint.h> #endif @@ -59,7 +60,7 @@ class LLMutex ; LL_COMMON_API void ll_assert_aligned_func(uintptr_t ptr,U32 alignment); #ifdef SHOW_ASSERT -#define ll_assert_aligned(ptr,alignment) ll_assert_aligned_func(reinterpret_cast<uintptr_t>(ptr),((U32)alignment)) +#define ll_assert_aligned(ptr,alignment) ll_assert_aligned_func(uintptr_t(ptr),((U32)alignment)) #else #define ll_assert_aligned(ptr,alignment) #endif @@ -69,13 +70,13 @@ LL_COMMON_API void ll_assert_aligned_func(uintptr_t ptr,U32 alignment); template <typename T> T* LL_NEXT_ALIGNED_ADDRESS(T* address) { return reinterpret_cast<T*>( - (reinterpret_cast<uintptr_t>(address) + 0xF) & ~0xF); + (uintptr_t(address) + 0xF) & ~0xF); } template <typename T> T* LL_NEXT_ALIGNED_ADDRESS_64(T* address) { return reinterpret_cast<T*>( - (reinterpret_cast<uintptr_t>(address) + 0x3F) & ~0x3F); + (uintptr_t(address) + 0x3F) & ~0x3F); } #if LL_LINUX || LL_DARWIN @@ -97,7 +98,7 @@ template <typename T> T* LL_NEXT_ALIGNED_ADDRESS_64(T* address) //------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------ // for enable buffer overrun detection predefine LL_DEBUG_BUFFER_OVERRUN in current library - // change preprocessro code to: #if 1 && defined(LL_WINDOWS) + // change preprocessor code to: #if 1 && defined(LL_WINDOWS) #if 0 && defined(LL_WINDOWS) void* ll_aligned_malloc_fallback( size_t size, int align ); diff --git a/indra/llcommon/llmutex.h b/indra/llcommon/llmutex.h index 3659a319a2..ea535cee86 100644 --- a/indra/llcommon/llmutex.h +++ b/indra/llcommon/llmutex.h @@ -33,6 +33,10 @@ #define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO) +#if MUTEX_DEBUG +#include <map> +#endif + struct apr_thread_mutex_t; struct apr_pool_t; struct apr_thread_cond_t; diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp index 715df36f39..e0aa30cc1a 100755 --- a/indra/llcommon/llprocess.cpp +++ b/indra/llcommon/llprocess.cpp @@ -738,8 +738,11 @@ LLProcess::LLProcess(const LLSDOrParams& params): { mPipes.replace(i, new ReadPipeImpl(desc, pipe, FILESLOT(i))); } - LL_DEBUGS("LLProcess") << "Instantiating " << typeid(mPipes[i]).name() - << "('" << desc << "')" << LL_ENDL; + // Removed temporaily for Xcode 7 build tests: error was: + // "error: expression with side effects will be evaluated despite + // being used as an operand to 'typeid' [-Werror,-Wpotentially-evaluated-expression]"" + //LL_DEBUGS("LLProcess") << "Instantiating " << typeid(mPipes[i]).name() + // << "('" << desc << "')" << LL_ENDL; } } diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h index 02f10fa2ba..0435cb8a08 100755 --- a/indra/llcommon/llstl.h +++ b/indra/llcommon/llstl.h @@ -27,6 +27,7 @@ #ifndef LL_LLSTL_H #define LL_LLSTL_H +#include "stdtypes.h" #include <functional> #include <algorithm> #include <map> @@ -272,6 +273,7 @@ inline T get_if_there(const std::map<K,T>& inmap, const K& key, T default_value) } }; +// Useful for replacing the removeObj() functionality of LLDynamicArray // Example: // for (std::vector<T>::iterator iter = mList.begin(); iter != mList.end(); ) // { @@ -530,7 +532,7 @@ bool before(const std::type_info* lhs, const std::type_info* rhs) return strcmp(lhs->name(), rhs->name()) < 0; #else // not Linux, or gcc 4.4+ // Just use before(), as we normally would - return lhs->before(*rhs); + return lhs->before(*rhs) ? true : false; #endif } diff --git a/indra/llcommon/lluriparser.cpp b/indra/llcommon/lluriparser.cpp index d98bc297e5..82d0dc8b4b 100644 --- a/indra/llcommon/lluriparser.cpp +++ b/indra/llcommon/lluriparser.cpp @@ -238,12 +238,12 @@ void LLUriParser::glueSecond(std::string& uri) const { uri += '?'; uri += mQuery; + } - if (mFragment.size()) - { - uri += '#'; - uri += mFragment; - } + if (mFragment.size()) + { + uri += '#'; + uri += mFragment; } } diff --git a/indra/llimage/llimagebmp.cpp b/indra/llimage/llimagebmp.cpp index 8573fe0d91..a2ce2fee86 100755 --- a/indra/llimage/llimagebmp.cpp +++ b/indra/llimage/llimagebmp.cpp @@ -443,6 +443,10 @@ BOOL LLImageBMP::decodeColorMask32( U8* dst, U8* src ) mBitfieldMask[2] = 0x000000FF; } + if (getWidth() * getHeight() * 4 > getDataSize() - mBitmapOffset) + { //here we have situation when data size in src less than actually needed + return FALSE; + } S32 src_row_span = getWidth() * 4; S32 alignment_bytes = (3 * src_row_span) % 4; // round up to nearest multiple of 4 @@ -476,6 +480,11 @@ BOOL LLImageBMP::decodeColorTable8( U8* dst, U8* src ) S32 src_row_span = getWidth() * 1; S32 alignment_bytes = (3 * src_row_span) % 4; // round up to nearest multiple of 4 + if ((getWidth() * getHeight()) + getHeight() * alignment_bytes > getDataSize() - mBitmapOffset) + { //here we have situation when data size in src less than actually needed + return FALSE; + } + for( S32 row = 0; row < getHeight(); row++ ) { for( S32 col = 0; col < getWidth(); col++ ) @@ -501,6 +510,11 @@ BOOL LLImageBMP::decodeTruecolor24( U8* dst, U8* src ) S32 src_row_span = getWidth() * 3; S32 alignment_bytes = (3 * src_row_span) % 4; // round up to nearest multiple of 4 + if ((getWidth() * getHeight() * 3) + getHeight() * alignment_bytes > getDataSize() - mBitmapOffset) + { //here we have situation when data size in src less than actually needed + return FALSE; + } + for( S32 row = 0; row < getHeight(); row++ ) { for( S32 col = 0; col < getWidth(); col++ ) diff --git a/indra/llimage/llimagetga.cpp b/indra/llimage/llimagetga.cpp index 4eb8dc7440..d0ae105ba7 100755 --- a/indra/llimage/llimagetga.cpp +++ b/indra/llimage/llimagetga.cpp @@ -437,7 +437,13 @@ BOOL LLImageTGA::decodeTruecolorNonRle( LLImageRaw* raw_image, BOOL &alpha_opaqu // Origin is the bottom left U8* dst = raw_image->getData(); U8* src = getData() + mDataOffset; + S32 pixels = getWidth() * getHeight(); + + if (pixels * (mIs15Bit ? 2 : getComponents()) > getDataSize() - mDataOffset) + { //here we have situation when data size in src less than actually needed + return FALSE; + } if (getComponents() == 4) { diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index c2198b91a7..8608e45a91 100755 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -2536,6 +2536,8 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) U32 cur_influence = 0; LLVector4 wght(0,0,0,0); + U32 joints[4] = {0,0,0,0}; + LLVector4 joints_with_weights(0,0,0,0); while (joint != END_INFLUENCES && idx < weights.size()) { @@ -2543,7 +2545,9 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) influence |= ((U16) weights[idx++] << 8); F32 w = llclamp((F32) influence / 65535.f, 0.f, 0.99999f); - wght.mV[cur_influence++] = (F32) joint + w; + wght.mV[cur_influence] = w; + joints[cur_influence] = joint; + cur_influence++; if (cur_influence >= 4) { @@ -2554,8 +2558,16 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) joint = weights[idx++]; } } - - face.mWeights[cur_vertex].loadua(wght.mV); + F32 wsum = wght.mV[VX] + wght.mV[VY] + wght.mV[VZ] + wght.mV[VW]; + if (wsum <= 0.f) + { + wght = LLVector4(0.99999f,0.f,0.f,0.f); + } + for (U32 k=0; k<4; k++) + { + joints_with_weights[k] = (F32) joints[k] + wght[k]; + } + face.mWeights[cur_vertex].loadua(joints_with_weights.mV); cur_vertex++; } @@ -5584,7 +5596,7 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build) { resizeVertices(num_vertices+1); - if (!partial_build) + //if (!partial_build) { resizeIndices(num_indices+3); } @@ -5592,7 +5604,7 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build) else { resizeVertices(num_vertices); - if (!partial_build) + //if (!partial_build) { resizeIndices(num_indices); } @@ -5714,10 +5726,10 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build) LL_CHECK_MEMORY - if (partial_build) - { - return TRUE; - } + //if (partial_build) + //{ + // return TRUE; + //} if (mTypeMask & HOLLOW_MASK) { @@ -6072,7 +6084,7 @@ void LLVolumeFace::pushVertex(const LLVector4a& pos, const LLVector4a& norm, con if (new_verts > mNumAllocatedVertices) { - //double buffer size on expansion + // double buffer size on expansion new_verts *= 2; S32 new_tc_size = ((new_verts*8)+0xF) & ~0xF; @@ -6088,18 +6100,21 @@ void LLVolumeFace::pushVertex(const LLVector4a& pos, const LLVector4a& norm, con mNormals = mPositions+new_verts; mTexCoords = (LLVector2*) (mNormals+new_verts); - //positions - LLVector4a::memcpyNonAliased16((F32*) mPositions, (F32*) old_buf, old_vsize); - - //normals - LLVector4a::memcpyNonAliased16((F32*) mNormals, (F32*) (old_buf+mNumVertices), old_vsize); + if (old_buf != NULL) + { + // copy old positions into new buffer + LLVector4a::memcpyNonAliased16((F32*)mPositions, (F32*)old_buf, old_vsize); - //tex coords - LLVector4a::memcpyNonAliased16((F32*) mTexCoords, (F32*) (old_buf+mNumVertices*2), old_tc_size); + // normals + LLVector4a::memcpyNonAliased16((F32*)mNormals, (F32*)(old_buf + mNumVertices), old_vsize); - //just clear tangents - ll_aligned_free_16(mTangents); - mTangents = NULL; + // tex coords + LLVector4a::memcpyNonAliased16((F32*)mTexCoords, (F32*)(old_buf + mNumVertices * 2), old_tc_size); + } + + // just clear tangents + ll_aligned_free_16(mTangents); + mTangents = NULL; ll_aligned_free<64>(old_buf); mNumAllocatedVertices = new_verts; diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index c8476f6897..1da2d0c6b1 100755 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -190,8 +190,12 @@ const U8 LL_SCULPT_TYPE_MESH = 5; const U8 LL_SCULPT_TYPE_MASK = LL_SCULPT_TYPE_SPHERE | LL_SCULPT_TYPE_TORUS | LL_SCULPT_TYPE_PLANE | LL_SCULPT_TYPE_CYLINDER | LL_SCULPT_TYPE_MESH; +// for value checks, assign new value after adding new types +const U8 LL_SCULPT_TYPE_MAX = LL_SCULPT_TYPE_MESH; + const U8 LL_SCULPT_FLAG_INVERT = 64; const U8 LL_SCULPT_FLAG_MIRROR = 128; +const U8 LL_SCULPT_FLAG_MASK = LL_SCULPT_FLAG_INVERT | LL_SCULPT_FLAG_MIRROR; const S32 LL_SCULPT_MESH_MAX_FACES = 8; @@ -968,6 +972,7 @@ protected: ~LLVolume(); // use unref public: + typedef std::vector<LLVolumeFace> face_list_t; struct FaceParams { @@ -1041,6 +1046,10 @@ public: // conversion if *(LLVolume*) to LLVolume& const LLVolumeFace &getVolumeFace(const S32 f) const {return mVolumeFaces[f];} // DO NOT DELETE VOLUME WHILE USING THIS REFERENCE, OR HOLD A POINTER TO THIS VOLUMEFACE + LLVolumeFace &getVolumeFace(const S32 f) {return mVolumeFaces[f];} // DO NOT DELETE VOLUME WHILE USING THIS REFERENCE, OR HOLD A POINTER TO THIS VOLUMEFACE + + face_list_t& getVolumeFaces() { return mVolumeFaces; } + U32 mFaceMask; // bit array of which faces exist in this volume LLVector3 mLODScaleBias; // vector for biasing LOD based on scale @@ -1080,7 +1089,6 @@ public: BOOL mGenerateSingleFace; - typedef std::vector<LLVolumeFace> face_list_t; face_list_t mVolumeFaces; public: diff --git a/indra/llmath/m4math.cpp b/indra/llmath/m4math.cpp index 6a1b4143cf..d89c482804 100755 --- a/indra/llmath/m4math.cpp +++ b/indra/llmath/m4math.cpp @@ -274,6 +274,19 @@ const LLMatrix4& LLMatrix4::invert(void) return *this; } +// Convenience func for simplifying comparison-heavy code by +// intentionally stomping values in [-FLT_EPS,FLT_EPS] to 0.0f +// +void LLMatrix4::condition(void) +{ + U32 i; + U32 j; + for (i = 0; i < 3;i++) + for (j = 0; j < 3;j++) + mMatrix[i][j] = ((mMatrix[i][j] > -FLT_EPSILON) + && (mMatrix[i][j] < FLT_EPSILON)) ? 0.0f : mMatrix[i][j]; +} + LLVector4 LLMatrix4::getFwdRow4() const { return LLVector4(mMatrix[VX][VX], mMatrix[VX][VY], mMatrix[VX][VZ], mMatrix[VX][VW]); diff --git a/indra/llmath/m4math.h b/indra/llmath/m4math.h index a7dce10397..a77c5bc76d 100755 --- a/indra/llmath/m4math.h +++ b/indra/llmath/m4math.h @@ -180,6 +180,11 @@ public: const LLMatrix4& setTranslation(const LLVector4 &translation); const LLMatrix4& setTranslation(const LLVector3 &translation); + // Convenience func for simplifying comparison-heavy code by + // intentionally stomping values [-FLT_EPS,FLT_EPS] to 0.0 + // + void condition(void); + /////////////////////////// // // Get properties of a matrix diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index 52626b0302..4bfd0de81e 100755 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -146,6 +146,8 @@ void LLPluginClassMedia::reset() mClickTarget.clear(); mClickUUID.clear(); mStatusCode = 0; + + mClickEnforceTarget = false; // media_time class mCurrentTime = 0.0f; @@ -1358,6 +1360,12 @@ void LLPluginClassMedia::addCertificateFilePath(const std::string& path) sendMessage(message); } +void LLPluginClassMedia::setOverrideClickTarget(const std::string &target) +{ + mClickEnforceTarget = true; + mOverrideClickTarget = target; +} + void LLPluginClassMedia::crashPlugin() { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "crash"); diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h index 5fe8254331..96d577f43c 100755 --- a/indra/llplugin/llpluginclassmedia.h +++ b/indra/llplugin/llpluginclassmedia.h @@ -249,6 +249,13 @@ public: // This is valid during MEDIA_EVENT_CLICK_LINK_HREF and MEDIA_EVENT_GEOMETRY_CHANGE std::string getClickUUID() const { return mClickUUID; }; + // mClickTarget is received from message and governs how link will be opened + // use this to enforce your own way of opening links inside plugins + void setOverrideClickTarget(const std::string &target); + void resetOverrideClickTarget() { mClickEnforceTarget = false; }; + bool isOverrideClickTarget() const { return mClickEnforceTarget; } + std::string getOverrideClickTarget() const { return mOverrideClickTarget; }; + // These are valid during MEDIA_EVENT_DEBUG_MESSAGE std::string getDebugMessageText() const { return mDebugMessageText; }; std::string getDebugMessageLevel() const { return mDebugMessageLevel; }; @@ -404,6 +411,8 @@ protected: std::string mClickNavType; std::string mClickTarget; std::string mClickUUID; + bool mClickEnforceTarget; + std::string mOverrideClickTarget; std::string mDebugMessageText; std::string mDebugMessageLevel; S32 mGeometryX; diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt index d1475cf734..75ce624a58 100755 --- a/indra/llprimitive/CMakeLists.txt +++ b/indra/llprimitive/CMakeLists.txt @@ -8,6 +8,7 @@ include(LLMath) include(LLMessage) include(LLXML) include(LLPhysicsExtensions) +include(LLCharacter) include_directories( ${LLCOMMON_INCLUDE_DIRS} @@ -16,6 +17,7 @@ include_directories( ${LLXML_INCLUDE_DIRS} ${LIBS_PREBUILT_DIR}/include/collada ${LIBS_PREBUILT_DIR}/include/collada/1.4 + ${LLCHARACTER_INCLUDE_DIRS} ) include_directories(SYSTEM ${LLCOMMON_SYSTEM_INCLUDE_DIRS} @@ -24,11 +26,13 @@ include_directories(SYSTEM ) set(llprimitive_SOURCE_FILES + lldaeloader.cpp llmaterialid.cpp llmaterial.cpp llmaterialtable.cpp llmediaentry.cpp llmodel.cpp + llmodelloader.cpp llprimitive.cpp llprimtexturelist.cpp lltextureanim.cpp @@ -40,16 +44,17 @@ set(llprimitive_SOURCE_FILES set(llprimitive_HEADER_FILES CMakeLists.txt - + lldaeloader.h legacy_object_types.h - lllslconstants.h llmaterial.h llmaterialid.h llmaterialtable.h llmediaentry.h llmodel.h + llmodelloader.h llprimitive.h llprimtexturelist.h + lllslconstants.h lltextureanim.h lltextureentry.h lltreeparams.h @@ -72,6 +77,7 @@ target_link_libraries(llprimitive ${LLMESSAGE_LIBRARIES} ${LLXML_LIBRARIES} ${LLPHYSICSEXTENSIONS_LIBRARIES} + ${LLCHARACTER_LIBRARIES} ) diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp new file mode 100644 index 0000000000..c1b74b1fd7 --- /dev/null +++ b/indra/llprimitive/lldaeloader.cpp @@ -0,0 +1,2436 @@ +/** + * @file lldaeloader.cpp + * @brief LLDAELoader class implementation + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2013, 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$ + */ + +#if LL_MSVC +#pragma warning (disable : 4263) +#pragma warning (disable : 4264) +#endif +#include "dae.h" +#include "dom/domAsset.h" +#include "dom/domBind_material.h" +#include "dom/domCOLLADA.h" +#include "dom/domConstants.h" +#include "dom/domController.h" +#include "dom/domEffect.h" +#include "dom/domGeometry.h" +#include "dom/domInstance_geometry.h" +#include "dom/domInstance_material.h" +#include "dom/domInstance_node.h" +#include "dom/domInstance_effect.h" +#include "dom/domMaterial.h" +#include "dom/domMatrix.h" +#include "dom/domNode.h" +#include "dom/domProfile_COMMON.h" +#include "dom/domRotate.h" +#include "dom/domScale.h" +#include "dom/domTranslate.h" +#include "dom/domVisual_scene.h" +#if LL_MSVC +#pragma warning (default : 4263) +#pragma warning (default : 4264) +#endif + +#include <boost/lexical_cast.hpp> + +#include "lldaeloader.h" +#include "llsdserialize.h" +#include "lljoint.h" + +#include "glh/glh_linear.h" +#include "llmatrix4a.h" + +std::string colladaVersion[VERSIONTYPE_COUNT+1] = +{ + "1.4.0", + "1.4.1", + "Unsupported" +}; + +static const std::string lod_suffix[LLModel::NUM_LODS] = +{ + "_LOD0", + "_LOD1", + "_LOD2", + "", + "_PHYS", +}; + +const U32 LIMIT_MATERIALS_OUTPUT = 12; + +bool get_dom_sources(const domInputLocalOffset_Array& inputs, S32& pos_offset, S32& tc_offset, S32& norm_offset, S32 &idx_stride, + domSource* &pos_source, domSource* &tc_source, domSource* &norm_source) +{ + idx_stride = 0; + + for (U32 j = 0; j < inputs.getCount(); ++j) + { + idx_stride = llmax((S32) inputs[j]->getOffset(), idx_stride); + + if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[j]->getSemantic()) == 0) + { //found vertex array + const domURIFragmentType& uri = inputs[j]->getSource(); + daeElementRef elem = uri.getElement(); + domVertices* vertices = (domVertices*) elem.cast(); + if ( !vertices ) + { + return false; + } + + domInputLocal_Array& v_inp = vertices->getInput_array(); + + + for (U32 k = 0; k < v_inp.getCount(); ++k) + { + if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0) + { + pos_offset = inputs[j]->getOffset(); + + const domURIFragmentType& uri = v_inp[k]->getSource(); + daeElementRef elem = uri.getElement(); + pos_source = (domSource*) elem.cast(); + } + + if (strcmp(COMMON_PROFILE_INPUT_NORMAL, v_inp[k]->getSemantic()) == 0) + { + norm_offset = inputs[j]->getOffset(); + + const domURIFragmentType& uri = v_inp[k]->getSource(); + daeElementRef elem = uri.getElement(); + norm_source = (domSource*) elem.cast(); + } + } + } + + if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[j]->getSemantic()) == 0) + { + //found normal array for this triangle list + norm_offset = inputs[j]->getOffset(); + const domURIFragmentType& uri = inputs[j]->getSource(); + daeElementRef elem = uri.getElement(); + norm_source = (domSource*) elem.cast(); + } + else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[j]->getSemantic()) == 0) + { //found texCoords + tc_offset = inputs[j]->getOffset(); + const domURIFragmentType& uri = inputs[j]->getSource(); + daeElementRef elem = uri.getElement(); + tc_source = (domSource*) elem.cast(); + } + } + + idx_stride += 1; + + return true; +} + +LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domTrianglesRef& tri) +{ + LLVolumeFace face; + std::vector<LLVolumeFace::VertexData> verts; + std::vector<U16> indices; + + const domInputLocalOffset_Array& inputs = tri->getInput_array(); + + S32 pos_offset = -1; + S32 tc_offset = -1; + S32 norm_offset = -1; + + domSource* pos_source = NULL; + domSource* tc_source = NULL; + domSource* norm_source = NULL; + + S32 idx_stride = 0; + + if ( !get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source)) + { + return LLModel::BAD_ELEMENT; + } + + if (!pos_source || !pos_source->getFloat_array()) + { + LL_WARNS() << "Unable to process mesh without position data; invalid model; invalid model." << LL_ENDL; + return LLModel::BAD_ELEMENT; + } + + domPRef p = tri->getP(); + domListOfUInts& idx = p->getValue(); + + domListOfFloats dummy ; + domListOfFloats& v = pos_source ? pos_source->getFloat_array()->getValue() : dummy ; + domListOfFloats& tc = tc_source ? tc_source->getFloat_array()->getValue() : dummy ; + domListOfFloats& n = norm_source ? norm_source->getFloat_array()->getValue() : dummy ; + + if (pos_source) + { + if(v.getCount() == 0) + { + return LLModel::BAD_ELEMENT; + } + + face.mExtents[0].set(v[0], v[1], v[2]); + face.mExtents[1].set(v[0], v[1], v[2]); + } + + LLVolumeFace::VertexMapData::PointMap point_map; + + for (U32 i = 0; i < idx.getCount(); i += idx_stride) + { + LLVolumeFace::VertexData cv; + if (pos_source) + { + cv.setPosition(LLVector4a(v[idx[i+pos_offset]*3+0], + v[idx[i+pos_offset]*3+1], + v[idx[i+pos_offset]*3+2])); + } + + if (tc_source) + { + cv.mTexCoord.setVec(tc[idx[i+tc_offset]*2+0], + tc[idx[i+tc_offset]*2+1]); + } + + if (norm_source) + { + cv.setNormal(LLVector4a(n[idx[i+norm_offset]*3+0], + n[idx[i+norm_offset]*3+1], + n[idx[i+norm_offset]*3+2])); + } + + BOOL found = FALSE; + + LLVolumeFace::VertexMapData::PointMap::iterator point_iter; + point_iter = point_map.find(LLVector3(cv.getPosition().getF32ptr())); + + if (point_iter != point_map.end()) + { + for (U32 j = 0; j < point_iter->second.size(); ++j) + { + // We have a matching loc + // + if ((point_iter->second)[j] == cv) + { + U16 shared_index = (point_iter->second)[j].mIndex; + + // Don't share verts within the same tri, degenerate + // + if ((indices.size() % 3) && (indices[indices.size()-1] != shared_index)) + { + found = true; + indices.push_back(shared_index); + } + break; + } + } + } + + if (!found) + { + update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition()); + verts.push_back(cv); + if (verts.size() >= 65535) + { + //llerrs << "Attempted to write model exceeding 16-bit index buffer limitation." << LL_ENDL; + return LLModel::VERTEX_NUMBER_OVERFLOW ; + } + U16 index = (U16) (verts.size()-1); + indices.push_back(index); + + LLVolumeFace::VertexMapData d; + d.setPosition(cv.getPosition()); + d.mTexCoord = cv.mTexCoord; + d.setNormal(cv.getNormal()); + d.mIndex = index; + if (point_iter != point_map.end()) + { + point_iter->second.push_back(d); + } + else + { + point_map[LLVector3(d.getPosition().getF32ptr())].push_back(d); + } + } + + if (indices.size()%3 == 0 && verts.size() >= 65532) + { + std::string material; + + if (tri->getMaterial()) + { + material = std::string(tri->getMaterial()); + } + + materials.push_back(material); + face_list.push_back(face); + face_list.rbegin()->fillFromLegacyData(verts, indices); + LLVolumeFace& new_face = *face_list.rbegin(); + if (!norm_source) + { + //ll_aligned_free_16(new_face.mNormals); + new_face.mNormals = NULL; + } + + if (!tc_source) + { + //ll_aligned_free_16(new_face.mTexCoords); + new_face.mTexCoords = NULL; + } + + face = LLVolumeFace(); + point_map.clear(); + } + } + + if (!verts.empty()) + { + std::string material; + + if (tri->getMaterial()) + { + material = std::string(tri->getMaterial()); + } + + materials.push_back(material); + face_list.push_back(face); + + face_list.rbegin()->fillFromLegacyData(verts, indices); + LLVolumeFace& new_face = *face_list.rbegin(); + if (!norm_source) + { + //ll_aligned_free_16(new_face.mNormals); + new_face.mNormals = NULL; + } + + if (!tc_source) + { + //ll_aligned_free_16(new_face.mTexCoords); + new_face.mTexCoords = NULL; + } + } + + return LLModel::NO_ERRORS ; +} + +LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolylistRef& poly) +{ + domPRef p = poly->getP(); + domListOfUInts& idx = p->getValue(); + + if (idx.getCount() == 0) + { + return LLModel::NO_ERRORS ; + } + + const domInputLocalOffset_Array& inputs = poly->getInput_array(); + + + domListOfUInts& vcount = poly->getVcount()->getValue(); + + S32 pos_offset = -1; + S32 tc_offset = -1; + S32 norm_offset = -1; + + domSource* pos_source = NULL; + domSource* tc_source = NULL; + domSource* norm_source = NULL; + + S32 idx_stride = 0; + + if (!get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source)) + { + return LLModel::BAD_ELEMENT; + } + + LLVolumeFace face; + + std::vector<U16> indices; + std::vector<LLVolumeFace::VertexData> verts; + + domListOfFloats v; + domListOfFloats tc; + domListOfFloats n; + + if (pos_source) + { + v = pos_source->getFloat_array()->getValue(); + face.mExtents[0].set(v[0], v[1], v[2]); + face.mExtents[1].set(v[0], v[1], v[2]); + } + + if (tc_source) + { + tc = tc_source->getFloat_array()->getValue(); + } + + if (norm_source) + { + n = norm_source->getFloat_array()->getValue(); + } + + LLVolumeFace::VertexMapData::PointMap point_map; + + U32 cur_idx = 0; + for (U32 i = 0; i < vcount.getCount(); ++i) + { //for each polygon + U32 first_index = 0; + U32 last_index = 0; + for (U32 j = 0; j < vcount[i]; ++j) + { //for each vertex + + LLVolumeFace::VertexData cv; + + if (pos_source) + { + cv.getPosition().set(v[idx[cur_idx+pos_offset]*3+0], + v[idx[cur_idx+pos_offset]*3+1], + v[idx[cur_idx+pos_offset]*3+2]); + if (!cv.getPosition().isFinite3()) + { + LL_WARNS() << "Found NaN while loading position data from DAE-Model, invalid model." << LL_ENDL; + return LLModel::BAD_ELEMENT; + } + } + + if (tc_source) + { + cv.mTexCoord.setVec(tc[idx[cur_idx+tc_offset]*2+0], + tc[idx[cur_idx+tc_offset]*2+1]); + } + + if (norm_source) + { + cv.getNormal().set(n[idx[cur_idx+norm_offset]*3+0], + n[idx[cur_idx+norm_offset]*3+1], + n[idx[cur_idx+norm_offset]*3+2]); + + if (!cv.getNormal().isFinite3()) + { + LL_WARNS() << "Found NaN while loading normals from DAE-Model, invalid model." << LL_ENDL; + return LLModel::BAD_ELEMENT; + } + } + + cur_idx += idx_stride; + + BOOL found = FALSE; + + LLVolumeFace::VertexMapData::PointMap::iterator point_iter; + LLVector3 pos3(cv.getPosition().getF32ptr()); + point_iter = point_map.find(pos3); + + if (point_iter != point_map.end()) + { + for (U32 k = 0; k < point_iter->second.size(); ++k) + { + if ((point_iter->second)[k] == cv) + { + found = TRUE; + U32 index = (point_iter->second)[k].mIndex; + if (j == 0) + { + first_index = index; + } + else if (j == 1) + { + last_index = index; + } + else + { + // if these are the same, we have a very, very skinny triangle (coincident verts on one or more edges) + // + llassert((first_index != last_index) && (last_index != index) && (first_index != index)); + indices.push_back(first_index); + indices.push_back(last_index); + indices.push_back(index); + last_index = index; + } + + break; + } + } + } + + if (!found) + { + update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition()); + verts.push_back(cv); + if (verts.size() >= 65535) + { + //llerrs << "Attempted to write model exceeding 16-bit index buffer limitation." << LL_ENDL; + return LLModel::VERTEX_NUMBER_OVERFLOW ; + } + U16 index = (U16) (verts.size()-1); + + if (j == 0) + { + first_index = index; + } + else if (j == 1) + { + last_index = index; + } + else + { + // detect very skinny degenerate triangles with collapsed edges + // + llassert((first_index != last_index) && (last_index != index) && (first_index != index)); + indices.push_back(first_index); + indices.push_back(last_index); + indices.push_back(index); + last_index = index; + } + + LLVolumeFace::VertexMapData d; + d.setPosition(cv.getPosition()); + d.mTexCoord = cv.mTexCoord; + d.setNormal(cv.getNormal()); + d.mIndex = index; + if (point_iter != point_map.end()) + { + point_iter->second.push_back(d); + } + else + { + point_map[pos3].push_back(d); + } + } + + if (indices.size()%3 == 0 && indices.size() >= 65532) + { + std::string material; + + if (poly->getMaterial()) + { + material = std::string(poly->getMaterial()); + } + + materials.push_back(material); + face_list.push_back(face); + face_list.rbegin()->fillFromLegacyData(verts, indices); + LLVolumeFace& new_face = *face_list.rbegin(); + if (!norm_source) + { + //ll_aligned_free_16(new_face.mNormals); + new_face.mNormals = NULL; + } + + if (!tc_source) + { + //ll_aligned_free_16(new_face.mTexCoords); + new_face.mTexCoords = NULL; + } + + face = LLVolumeFace(); + verts.clear(); + indices.clear(); + point_map.clear(); + } + } + } + + if (!verts.empty()) + { + std::string material; + + if (poly->getMaterial()) + { + material = std::string(poly->getMaterial()); + } + + materials.push_back(material); + face_list.push_back(face); + face_list.rbegin()->fillFromLegacyData(verts, indices); + + LLVolumeFace& new_face = *face_list.rbegin(); + if (!norm_source) + { + //ll_aligned_free_16(new_face.mNormals); + new_face.mNormals = NULL; + } + + if (!tc_source) + { + //ll_aligned_free_16(new_face.mTexCoords); + new_face.mTexCoords = NULL; + } + } + + return LLModel::NO_ERRORS ; +} + +LLModel::EModelStatus load_face_from_dom_polygons(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolygonsRef& poly) +{ + LLVolumeFace face; + std::vector<U16> indices; + std::vector<LLVolumeFace::VertexData> verts; + + const domInputLocalOffset_Array& inputs = poly->getInput_array(); + + S32 v_offset = -1; + S32 n_offset = -1; + S32 t_offset = -1; + + domListOfFloats* v = NULL; + domListOfFloats* n = NULL; + domListOfFloats* t = NULL; + + U32 stride = 0; + for (U32 i = 0; i < inputs.getCount(); ++i) + { + stride = llmax((U32) inputs[i]->getOffset()+1, stride); + + if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[i]->getSemantic()) == 0) + { //found vertex array + v_offset = inputs[i]->getOffset(); + + const domURIFragmentType& uri = inputs[i]->getSource(); + daeElementRef elem = uri.getElement(); + domVertices* vertices = (domVertices*) elem.cast(); + if (!vertices) + { + return LLModel::BAD_ELEMENT; + } + domInputLocal_Array& v_inp = vertices->getInput_array(); + + for (U32 k = 0; k < v_inp.getCount(); ++k) + { + if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0) + { + const domURIFragmentType& uri = v_inp[k]->getSource(); + daeElementRef elem = uri.getElement(); + domSource* src = (domSource*) elem.cast(); + if (!src) + { + return LLModel::BAD_ELEMENT; + } + v = &(src->getFloat_array()->getValue()); + } + } + } + else if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[i]->getSemantic()) == 0) + { + n_offset = inputs[i]->getOffset(); + //found normal array for this triangle list + const domURIFragmentType& uri = inputs[i]->getSource(); + daeElementRef elem = uri.getElement(); + domSource* src = (domSource*) elem.cast(); + if (!src) + { + return LLModel::BAD_ELEMENT; + } + n = &(src->getFloat_array()->getValue()); + } + else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[i]->getSemantic()) == 0 && inputs[i]->getSet() == 0) + { //found texCoords + t_offset = inputs[i]->getOffset(); + const domURIFragmentType& uri = inputs[i]->getSource(); + daeElementRef elem = uri.getElement(); + domSource* src = (domSource*) elem.cast(); + if (!src) + { + return LLModel::BAD_ELEMENT; + } + t = &(src->getFloat_array()->getValue()); + } + } + + domP_Array& ps = poly->getP_array(); + + //make a triangle list in <verts> + for (U32 i = 0; i < ps.getCount(); ++i) + { //for each polygon + domListOfUInts& idx = ps[i]->getValue(); + for (U32 j = 0; j < idx.getCount()/stride; ++j) + { //for each vertex + if (j > 2) + { + U32 size = verts.size(); + LLVolumeFace::VertexData v0 = verts[size-3]; + LLVolumeFace::VertexData v1 = verts[size-1]; + + verts.push_back(v0); + verts.push_back(v1); + } + + LLVolumeFace::VertexData vert; + + + if (v) + { + U32 v_idx = idx[j*stride+v_offset]*3; + v_idx = llclamp(v_idx, (U32) 0, (U32) v->getCount()); + vert.getPosition().set(v->get(v_idx), + v->get(v_idx+1), + v->get(v_idx+2)); + } + + //bounds check n and t lookups because some FBX to DAE converters + //use negative indices and empty arrays to indicate data does not exist + //for a particular channel + if (n && n->getCount() > 0) + { + U32 n_idx = idx[j*stride+n_offset]*3; + n_idx = llclamp(n_idx, (U32) 0, (U32) n->getCount()); + vert.getNormal().set(n->get(n_idx), + n->get(n_idx+1), + n->get(n_idx+2)); + } + else + { + vert.getNormal().clear(); + } + + + if (t && t->getCount() > 0) + { + U32 t_idx = idx[j*stride+t_offset]*2; + t_idx = llclamp(t_idx, (U32) 0, (U32) t->getCount()); + vert.mTexCoord.setVec(t->get(t_idx), + t->get(t_idx+1)); + } + else + { + vert.mTexCoord.clear(); + } + + + verts.push_back(vert); + } + } + + if (verts.empty()) + { + return LLModel::NO_ERRORS; + } + + face.mExtents[0] = verts[0].getPosition(); + face.mExtents[1] = verts[0].getPosition(); + + //create a map of unique vertices to indices + std::map<LLVolumeFace::VertexData, U32> vert_idx; + + U32 cur_idx = 0; + for (U32 i = 0; i < verts.size(); ++i) + { + std::map<LLVolumeFace::VertexData, U32>::iterator iter = vert_idx.find(verts[i]); + if (iter == vert_idx.end()) + { + vert_idx[verts[i]] = cur_idx++; + } + } + + //build vertex array from map + std::vector<LLVolumeFace::VertexData> new_verts; + new_verts.resize(vert_idx.size()); + + for (std::map<LLVolumeFace::VertexData, U32>::iterator iter = vert_idx.begin(); iter != vert_idx.end(); ++iter) + { + new_verts[iter->second] = iter->first; + update_min_max(face.mExtents[0], face.mExtents[1], iter->first.getPosition()); + } + + //build index array from map + indices.resize(verts.size()); + + for (U32 i = 0; i < verts.size(); ++i) + { + indices[i] = vert_idx[verts[i]]; + llassert(!i || (indices[i-1] != indices[i])); + } + + // DEBUG just build an expanded triangle list + /*for (U32 i = 0; i < verts.size(); ++i) + { + indices.push_back((U16) i); + update_min_max(face.mExtents[0], face.mExtents[1], verts[i].getPosition()); + }*/ + + if (!new_verts.empty()) + { + std::string material; + + if (poly->getMaterial()) + { + material = std::string(poly->getMaterial()); + } + + materials.push_back(material); + face_list.push_back(face); + face_list.rbegin()->fillFromLegacyData(new_verts, indices); + + LLVolumeFace& new_face = *face_list.rbegin(); + if (!n) + { + //ll_aligned_free_16(new_face.mNormals); + new_face.mNormals = NULL; + } + + if (!t) + { + //ll_aligned_free_16(new_face.mTexCoords); + new_face.mTexCoords = NULL; + } + } + + return LLModel::NO_ERRORS ; +} + +//----------------------------------------------------------------------------- +// LLDAELoader +//----------------------------------------------------------------------------- +LLDAELoader::LLDAELoader( + std::string filename, + S32 lod, + load_callback_t load_cb, + joint_lookup_func_t joint_lookup_func, + texture_load_func_t texture_load_func, + state_callback_t state_cb, + void* opaque_userdata, + JointTransformMap& jointMap, + JointSet& jointsFromNodes, + U32 modelLimit) +: LLModelLoader( + filename, + lod, + load_cb, + joint_lookup_func, + texture_load_func, + state_cb, + opaque_userdata, + jointMap, + jointsFromNodes), +mGeneratedModelLimit(modelLimit) +{ +} + +LLDAELoader::~LLDAELoader() +{ +} + +struct ModelSort +{ + bool operator()(const LLPointer< LLModel >& lhs, const LLPointer< LLModel >& rhs) + { + if (lhs->mSubmodelID < rhs->mSubmodelID) + { + return true; + } + return LLStringUtil::compareInsensitive(lhs->mLabel, rhs->mLabel) < 0; + } +}; + +bool LLDAELoader::OpenFile(const std::string& filename) +{ + //no suitable slm exists, load from the .dae file + DAE dae; + domCOLLADA* dom = dae.open(filename); + + if (!dom) + { + LL_INFOS() <<" Error with dae - traditionally indicates a corrupt file."<<LL_ENDL; + setLoadState( ERROR_PARSING ); + return false; + } + //Dom version + daeString domVersion = dae.getDomVersion(); + std::string sldom(domVersion); + LL_INFOS()<<"Collada Importer Version: "<<sldom<<LL_ENDL; + //Dae version + domVersionType docVersion = dom->getVersion(); + //0=1.4 + //1=1.4.1 + //2=Currently unsupported, however may work + if (docVersion > 1 ) + { + docVersion = VERSIONTYPE_COUNT; + } + LL_INFOS()<<"Dae version "<<colladaVersion[docVersion]<<LL_ENDL; + + + daeDatabase* db = dae.getDatabase(); + + daeInt count = db->getElementCount(NULL, COLLADA_TYPE_MESH); + + daeDocument* doc = dae.getDoc(mFilename); + if (!doc) + { + LL_WARNS() << "can't find internal doc" << LL_ENDL; + return false; + } + + daeElement* root = doc->getDomRoot(); + if (!root) + { + LL_WARNS() << "document has no root" << LL_ENDL; + return false; + } + + //Verify some basic properties of the dae + //1. Basic validity check on controller + U32 controllerCount = (int) db->getElementCount( NULL, "controller" ); + bool result = false; + for ( int i=0; i<controllerCount; ++i ) + { + domController* pController = NULL; + db->getElement( (daeElement**) &pController, i , NULL, "controller" ); + result = verifyController( pController ); + if (!result) + { + LL_INFOS() << "Could not verify controller" << LL_ENDL; + setLoadState( ERROR_PARSING ); + return true; + } + } + + + //get unit scale + mTransform.setIdentity(); + + domAsset::domUnit* unit = daeSafeCast<domAsset::domUnit>(root->getDescendant(daeElement::matchType(domAsset::domUnit::ID()))); + + if (unit) + { + F32 meter = unit->getMeter(); + mTransform.mMatrix[0][0] = meter; + mTransform.mMatrix[1][1] = meter; + mTransform.mMatrix[2][2] = meter; + } + + //get up axis rotation + LLMatrix4 rotation; + + domUpAxisType up = UPAXISTYPE_Y_UP; // default is Y_UP + domAsset::domUp_axis* up_axis = + daeSafeCast<domAsset::domUp_axis>(root->getDescendant(daeElement::matchType(domAsset::domUp_axis::ID()))); + + if (up_axis) + { + up = up_axis->getValue(); + } + + if (up == UPAXISTYPE_X_UP) + { + rotation.initRotation(0.0f, 90.0f * DEG_TO_RAD, 0.0f); + } + else if (up == UPAXISTYPE_Y_UP) + { + rotation.initRotation(90.0f * DEG_TO_RAD, 0.0f, 0.0f); + } + + rotation *= mTransform; + mTransform = rotation; + + mTransform.condition(); + + U32 submodel_limit = count > 0 ? mGeneratedModelLimit/count : 0; + for (daeInt idx = 0; idx < count; ++idx) + { //build map of domEntities to LLModel + domMesh* mesh = NULL; + db->getElement((daeElement**) &mesh, idx, NULL, COLLADA_TYPE_MESH); + + if (mesh) + { + + std::vector<LLModel*> models; + + loadModelsFromDomMesh(mesh, models, submodel_limit); + + std::vector<LLModel*>::iterator i; + i = models.begin(); + while (i != models.end()) + { + LLModel* mdl = *i; + if(mdl->getStatus() != LLModel::NO_ERRORS) + { + setLoadState(ERROR_MODEL + mdl->getStatus()) ; + return false; //abort + } + + if (mdl && validate_model(mdl)) + { + mModelList.push_back(mdl); + mModelsMap[mesh].push_back(mdl); + } + i++; + } + } + } + + std::sort(mModelList.begin(), mModelList.end(), ModelSort()); + + model_list::iterator model_iter = mModelList.begin(); + while (model_iter != mModelList.end()) + { + LLModel* mdl = *model_iter; + U32 material_count = mdl->mMaterialList.size(); + LL_INFOS() << "Importing " << mdl->mLabel << " model with " << material_count << " material references" << LL_ENDL; + std::vector<std::string>::iterator mat_iter = mdl->mMaterialList.begin(); + std::vector<std::string>::iterator end_iter = material_count > LIMIT_MATERIALS_OUTPUT + ? mat_iter + LIMIT_MATERIALS_OUTPUT + : mdl->mMaterialList.end(); + while (mat_iter != end_iter) + { + LL_INFOS() << mdl->mLabel << " references " << (*mat_iter) << LL_ENDL; + mat_iter++; + } + model_iter++; + } + + count = db->getElementCount(NULL, COLLADA_TYPE_SKIN); + for (daeInt idx = 0; idx < count; ++idx) + { //add skinned meshes as instances + domSkin* skin = NULL; + db->getElement((daeElement**) &skin, idx, NULL, COLLADA_TYPE_SKIN); + + if (skin) + { + domGeometry* geom = daeSafeCast<domGeometry>(skin->getSource().getElement()); + + if (geom) + { + domMesh* mesh = geom->getMesh(); + if (mesh) + { + std::vector< LLPointer< LLModel > >::iterator i = mModelsMap[mesh].begin(); + while (i != mModelsMap[mesh].end()) + { + LLPointer<LLModel> mdl = *i; + LLDAELoader::processDomModel(mdl, &dae, root, mesh, skin); + i++; + } + } + } + } + } + + LL_INFOS()<< "Collada skins processed: " << count <<LL_ENDL; + + daeElement* scene = root->getDescendant("visual_scene"); + + if (!scene) + { + LL_WARNS() << "document has no visual_scene" << LL_ENDL; + setLoadState( ERROR_PARSING ); + return true; + } + + setLoadState( DONE ); + + bool badElement = false; + + processElement( scene, badElement, &dae ); + + if ( badElement ) + { + LL_INFOS()<<"Scene could not be parsed"<<LL_ENDL; + setLoadState( ERROR_PARSING ); + } + + return true; +} + +void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, domMesh* mesh, domSkin* skin) +{ + llassert(model && dae && mesh && skin); + + if (model) + { + LLVector3 mesh_scale_vector; + LLVector3 mesh_translation_vector; + model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector); + + LLMatrix4 normalized_transformation; + normalized_transformation.setTranslation(mesh_translation_vector); + + LLMatrix4 mesh_scale; + mesh_scale.initScale(mesh_scale_vector); + mesh_scale *= normalized_transformation; + normalized_transformation = mesh_scale; + + glh::matrix4f inv_mat((F32*) normalized_transformation.mMatrix); + inv_mat = inv_mat.inverse(); + LLMatrix4 inverse_normalized_transformation(inv_mat.m); + + domSkin::domBind_shape_matrix* bind_mat = skin->getBind_shape_matrix(); + + if (bind_mat) + { //get bind shape matrix + domFloat4x4& dom_value = bind_mat->getValue(); + + LLMeshSkinInfo& skin_info = model->mSkinInfo; + + for (int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + skin_info.mBindShapeMatrix.mMatrix[i][j] = dom_value[i + j*4]; + } + } + + LLMatrix4 trans = normalized_transformation; + trans *= skin_info.mBindShapeMatrix; + skin_info.mBindShapeMatrix = trans; + } + + + //Some collada setup for accessing the skeleton + daeElement* pElement = 0; + dae->getDatabase()->getElement( &pElement, 0, 0, "skeleton" ); + + //Try to get at the skeletal instance controller + domInstance_controller::domSkeleton* pSkeleton = daeSafeCast<domInstance_controller::domSkeleton>( pElement ); + bool missingSkeletonOrScene = false; + + //If no skeleton, do a breadth-first search to get at specific joints + bool rootNode = false; + + //Need to test for a skeleton that does not have a root node + //This occurs when your instance controller does not have an associated scene + if ( pSkeleton ) + { + daeElement* pSkeletonRootNode = pSkeleton->getValue().getElement(); + if ( pSkeletonRootNode ) + { + rootNode = true; + } + + } + if ( !pSkeleton || !rootNode ) + { + daeElement* pScene = root->getDescendant("visual_scene"); + if ( !pScene ) + { + LL_WARNS()<<"No visual scene - unable to parse bone offsets "<<LL_ENDL; + missingSkeletonOrScene = true; + } + else + { + //Get the children at this level + daeTArray< daeSmartRef<daeElement> > children = pScene->getChildren(); + S32 childCount = children.getCount(); + + //Process any children that are joints + //Not all children are joints, some code be ambient lights, cameras, geometry etc.. + for (S32 i = 0; i < childCount; ++i) + { + domNode* pNode = daeSafeCast<domNode>(children[i]); + if ( isNodeAJoint( pNode ) ) + { + processJointNode( pNode, mJointList ); + } + } + } + } + else + //Has Skeleton + { + //Get the root node of the skeleton + daeElement* pSkeletonRootNode = pSkeleton->getValue().getElement(); + if ( pSkeletonRootNode ) + { + //Once we have the root node - start acccessing it's joint components + const int jointCnt = mJointMap.size(); + JointMap :: const_iterator jointIt = mJointMap.begin(); + + //Loop over all the possible joints within the .dae - using the allowed joint list in the ctor. + for ( int i=0; i<jointCnt; ++i, ++jointIt ) + { + //Build a joint for the resolver to work with + char str[64]={0}; + sprintf(str,"./%s",(*jointIt).first.c_str() ); + //LL_WARNS()<<"Joint "<< str <<LL_ENDL; + + //Setup the resolver + daeSIDResolver resolver( pSkeletonRootNode, str ); + + //Look for the joint + domNode* pJoint = daeSafeCast<domNode>( resolver.getElement() ); + if ( pJoint ) + { + //Pull out the translate id and store it in the jointTranslations map + daeSIDResolver jointResolverA( pJoint, "./translate" ); + domTranslate* pTranslateA = daeSafeCast<domTranslate>( jointResolverA.getElement() ); + daeSIDResolver jointResolverB( pJoint, "./location" ); + domTranslate* pTranslateB = daeSafeCast<domTranslate>( jointResolverB.getElement() ); + + LLMatrix4 workingTransform; + + //Translation via SID + if ( pTranslateA ) + { + extractTranslation( pTranslateA, workingTransform ); + } + else + if ( pTranslateB ) + { + extractTranslation( pTranslateB, workingTransform ); + } + else + { + //Translation via child from element + daeElement* pTranslateElement = getChildFromElement( pJoint, "translate" ); + if ( pTranslateElement && pTranslateElement->typeID() != domTranslate::ID() ) + { + LL_WARNS()<< "The found element is not a translate node" <<LL_ENDL; + missingSkeletonOrScene = true; + } + else + if ( pTranslateElement ) + { + extractTranslationViaElement( pTranslateElement, workingTransform ); + } + else + { + extractTranslationViaSID( pJoint, workingTransform ); + } + + } + + //Store the joint transform w/respect to it's name. + mJointList[(*jointIt).second.c_str()] = workingTransform; + } + } + + //If anything failed in regards to extracting the skeleton, joints or translation id, + //mention it + if ( missingSkeletonOrScene ) + { + LL_WARNS()<< "Partial jointmap found in asset - did you mean to just have a partial map?" << LL_ENDL; + } + }//got skeleton? + } + + + domSkin::domJoints* joints = skin->getJoints(); + + domInputLocal_Array& joint_input = joints->getInput_array(); + + for (size_t i = 0; i < joint_input.getCount(); ++i) + { + domInputLocal* input = joint_input.get(i); + xsNMTOKEN semantic = input->getSemantic(); + + if (strcmp(semantic, COMMON_PROFILE_INPUT_JOINT) == 0) + { //found joint source, fill model->mJointMap and model->mSkinInfo.mJointNames + daeElement* elem = input->getSource().getElement(); + + domSource* source = daeSafeCast<domSource>(elem); + if (source) + { + + + domName_array* names_source = source->getName_array(); + + if (names_source) + { + domListOfNames &names = names_source->getValue(); + + for (size_t j = 0; j < names.getCount(); ++j) + { + std::string name(names.get(j)); + if (mJointMap.find(name) != mJointMap.end()) + { + name = mJointMap[name]; + } + model->mSkinInfo.mJointNames.push_back(name); + model->mSkinInfo.mJointMap[name] = j; + } + } + else + { + domIDREF_array* names_source = source->getIDREF_array(); + if (names_source) + { + xsIDREFS& names = names_source->getValue(); + + for (size_t j = 0; j < names.getCount(); ++j) + { + std::string name(names.get(j).getID()); + if (mJointMap.find(name) != mJointMap.end()) + { + name = mJointMap[name]; + } + model->mSkinInfo.mJointNames.push_back(name); + model->mSkinInfo.mJointMap[name] = j; + } + } + } + } + } + else if (strcmp(semantic, COMMON_PROFILE_INPUT_INV_BIND_MATRIX) == 0) + { //found inv_bind_matrix array, fill model->mInvBindMatrix + domSource* source = daeSafeCast<domSource>(input->getSource().getElement()); + if (source) + { + domFloat_array* t = source->getFloat_array(); + if (t) + { + domListOfFloats& transform = t->getValue(); + S32 count = transform.getCount()/16; + + for (S32 k = 0; k < count; ++k) + { + LLMatrix4 mat; + + for (int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + mat.mMatrix[i][j] = transform[k*16 + i + j*4]; + } + } + + model->mSkinInfo.mInvBindMatrix.push_back(mat); + } + } + } + } + } + + //Now that we've parsed the joint array, let's determine if we have a full rig + //(which means we have all the joint sthat are required for an avatar versus + //a skinned asset attached to a node in a file that contains an entire skeleton, + //but does not use the skeleton). + buildJointToNodeMappingFromScene( root ); + critiqueRigForUploadApplicability( model->mSkinInfo.mJointNames ); + + if ( !missingSkeletonOrScene ) + { + //Set the joint translations on the avatar - if it's a full mapping + //The joints are reset in the dtor + if ( getRigWithSceneParity() ) + { + JointMap :: const_iterator masterJointIt = mJointMap.begin(); + JointMap :: const_iterator masterJointItEnd = mJointMap.end(); + for (;masterJointIt!=masterJointItEnd;++masterJointIt ) + { + std::string lookingForJoint = (*masterJointIt).first.c_str(); + + if ( mJointList.find( lookingForJoint ) != mJointList.end() ) + { + //LL_INFOS()<<"joint "<<lookingForJoint.c_str()<<LL_ENDL; + LLMatrix4 jointTransform = mJointList[lookingForJoint]; + LLJoint* pJoint = mJointLookupFunc(lookingForJoint,mOpaqueData); + if ( pJoint ) + { + LLUUID fake_mesh_id; + fake_mesh_id.generate(); + pJoint->addAttachmentPosOverride( jointTransform.getTranslation(), fake_mesh_id, ""); + } + else + { + //Most likely an error in the asset. + LL_WARNS()<<"Tried to apply joint position from .dae, but it did not exist in the avatar rig." << LL_ENDL; + } + } + } + } + } //missingSkeletonOrScene + + //We need to construct the alternate bind matrix (which contains the new joint positions) + //in the same order as they were stored in the joint buffer. The joints associated + //with the skeleton are not stored in the same order as they are in the exported joint buffer. + //This remaps the skeletal joints to be in the same order as the joints stored in the model. + std::vector<std::string> :: const_iterator jointIt = model->mSkinInfo.mJointNames.begin(); + const int jointCnt = model->mSkinInfo.mJointNames.size(); + for ( int i=0; i<jointCnt; ++i, ++jointIt ) + { + std::string lookingForJoint = (*jointIt).c_str(); + //Look for the joint xform that we extracted from the skeleton, using the jointIt as the key + //and store it in the alternate bind matrix + if ( mJointList.find( lookingForJoint ) != mJointList.end() ) + { + LLMatrix4 jointTransform = mJointList[lookingForJoint]; + LLMatrix4 newInverse = model->mSkinInfo.mInvBindMatrix[i]; + newInverse.setTranslation( mJointList[lookingForJoint].getTranslation() ); + model->mSkinInfo.mAlternateBindMatrix.push_back( newInverse ); + } + else + { + LL_WARNS()<<"Possibly misnamed/missing joint [" <<lookingForJoint.c_str()<<" ] "<<LL_ENDL; + } + } + + //grab raw position array + + domVertices* verts = mesh->getVertices(); + if (verts) + { + domInputLocal_Array& inputs = verts->getInput_array(); + for (size_t i = 0; i < inputs.getCount() && model->mPosition.empty(); ++i) + { + if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_POSITION) == 0) + { + domSource* pos_source = daeSafeCast<domSource>(inputs[i]->getSource().getElement()); + if (pos_source) + { + domFloat_array* pos_array = pos_source->getFloat_array(); + if (pos_array) + { + domListOfFloats& pos = pos_array->getValue(); + + for (size_t j = 0; j < pos.getCount(); j += 3) + { + if (pos.getCount() <= j+2) + { + LL_ERRS() << "Invalid position array size." << LL_ENDL; + } + + LLVector3 v(pos[j], pos[j+1], pos[j+2]); + + //transform from COLLADA space to volume space + v = v * inverse_normalized_transformation; + + model->mPosition.push_back(v); + } + } + } + } + } + } + + //grab skin weights array + domSkin::domVertex_weights* weights = skin->getVertex_weights(); + if (weights) + { + domInputLocalOffset_Array& inputs = weights->getInput_array(); + domFloat_array* vertex_weights = NULL; + for (size_t i = 0; i < inputs.getCount(); ++i) + { + if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_WEIGHT) == 0) + { + domSource* weight_source = daeSafeCast<domSource>(inputs[i]->getSource().getElement()); + if (weight_source) + { + vertex_weights = weight_source->getFloat_array(); + } + } + } + + if (vertex_weights) + { + domListOfFloats& w = vertex_weights->getValue(); + domListOfUInts& vcount = weights->getVcount()->getValue(); + domListOfInts& v = weights->getV()->getValue(); + + U32 c_idx = 0; + for (size_t vc_idx = 0; vc_idx < vcount.getCount(); ++vc_idx) + { //for each vertex + daeUInt count = vcount[vc_idx]; + + //create list of weights that influence this vertex + LLModel::weight_list weight_list; + + for (daeUInt i = 0; i < count; ++i) + { //for each weight + daeInt joint_idx = v[c_idx++]; + daeInt weight_idx = v[c_idx++]; + + if (joint_idx == -1) + { + //ignore bindings to bind_shape_matrix + continue; + } + + F32 weight_value = w[weight_idx]; + + weight_list.push_back(LLModel::JointWeight(joint_idx, weight_value)); + } + + //sort by joint weight + std::sort(weight_list.begin(), weight_list.end(), LLModel::CompareWeightGreater()); + + std::vector<LLModel::JointWeight> wght; + + F32 total = 0.f; + + for (U32 i = 0; i < llmin((U32) 4, (U32) weight_list.size()); ++i) + { //take up to 4 most significant weights + if (weight_list[i].mWeight > 0.f) + { + wght.push_back( weight_list[i] ); + total += weight_list[i].mWeight; + } + } + + F32 scale = 1.f/total; + if (scale != 1.f) + { //normalize weights + for (U32 i = 0; i < wght.size(); ++i) + { + wght[i].mWeight *= scale; + } + } + + model->mSkinWeights[model->mPosition[vc_idx]] = wght; + } + } + + } + + //add instance to scene for this model + + LLMatrix4 transformation; + transformation.initScale(mesh_scale_vector); + transformation.setTranslation(mesh_translation_vector); + transformation *= mTransform; + + std::map<std::string, LLImportMaterial> materials; + for (U32 i = 0; i < model->mMaterialList.size(); ++i) + { + materials[model->mMaterialList[i]] = LLImportMaterial(); + } + mScene[transformation].push_back(LLModelInstance(model, model->mLabel, transformation, materials)); + stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform); + } +} + +//----------------------------------------------------------------------------- +// buildJointToNodeMappingFromScene() +//----------------------------------------------------------------------------- +void LLDAELoader::buildJointToNodeMappingFromScene( daeElement* pRoot ) +{ + daeElement* pScene = pRoot->getDescendant("visual_scene"); + if ( pScene ) + { + daeTArray< daeSmartRef<daeElement> > children = pScene->getChildren(); + S32 childCount = children.getCount(); + for (S32 i = 0; i < childCount; ++i) + { + domNode* pNode = daeSafeCast<domNode>(children[i]); + processJointToNodeMapping( pNode ); + } + } +} +//----------------------------------------------------------------------------- +// processJointToNodeMapping() +//----------------------------------------------------------------------------- +void LLDAELoader::processJointToNodeMapping( domNode* pNode ) +{ + if ( isNodeAJoint( pNode ) ) + { + //1.Store the parent + std::string nodeName = pNode->getName(); + if ( !nodeName.empty() ) + { + mJointsFromNode.push_front( pNode->getName() ); + } + //2. Handle the kiddo's + processChildJoints( pNode ); + } + else + { + //Determine if the're any children wrt to this failed node. + //This occurs when an armature is exported and ends up being what essentially amounts to + //as the root for the visual_scene + if ( pNode ) + { + processChildJoints( pNode ); + } + else + { + LL_INFOS()<<"Node is NULL"<<LL_ENDL; + } + + } +} +//----------------------------------------------------------------------------- +// processChildJoint() +//----------------------------------------------------------------------------- +void LLDAELoader::processChildJoints( domNode* pParentNode ) +{ + daeTArray< daeSmartRef<daeElement> > childOfChild = pParentNode->getChildren(); + S32 childOfChildCount = childOfChild.getCount(); + for (S32 i = 0; i < childOfChildCount; ++i) + { + domNode* pChildNode = daeSafeCast<domNode>( childOfChild[i] ); + if ( pChildNode ) + { + processJointToNodeMapping( pChildNode ); + } + } +} + +//----------------------------------------------------------------------------- +// isNodeAJoint() +//----------------------------------------------------------------------------- +bool LLDAELoader::isNodeAJoint( domNode* pNode ) +{ + if ( !pNode ) + { + LL_INFOS()<<"Created node is NULL"<<LL_ENDL; + return false; + } + + return LLModelLoader::isNodeAJoint(pNode->getName()); +} +//----------------------------------------------------------------------------- +// verifyCount +//----------------------------------------------------------------------------- +bool LLDAELoader::verifyCount( int expected, int result ) +{ + if ( expected != result ) + { + LL_INFOS()<< "Error: (expected/got)"<<expected<<"/"<<result<<"verts"<<LL_ENDL; + return false; + } + return true; +} +//----------------------------------------------------------------------------- +// verifyController +//----------------------------------------------------------------------------- +bool LLDAELoader::verifyController( domController* pController ) +{ + + bool result = true; + + domSkin* pSkin = pController->getSkin(); + + if ( pSkin ) + { + xsAnyURI & uri = pSkin->getSource(); + domElement* pElement = uri.getElement(); + + if ( !pElement ) + { + LL_INFOS()<<"Can't resolve skin source"<<LL_ENDL; + return false; + } + + daeString type_str = pElement->getTypeName(); + if ( stricmp(type_str, "geometry") == 0 ) + { + //Skin is reference directly by geometry and get the vertex count from skin + domSkin::domVertex_weights* pVertexWeights = pSkin->getVertex_weights(); + U32 vertexWeightsCount = pVertexWeights->getCount(); + domGeometry* pGeometry = (domGeometry*) (domElement*) uri.getElement(); + domMesh* pMesh = pGeometry->getMesh(); + + if ( pMesh ) + { + //Get vertex count from geometry + domVertices* pVertices = pMesh->getVertices(); + if ( !pVertices ) + { + LL_INFOS()<<"No vertices!"<<LL_ENDL; + return false; + } + + if ( pVertices ) + { + xsAnyURI src = pVertices->getInput_array()[0]->getSource(); + domSource* pSource = (domSource*) (domElement*) src.getElement(); + U32 verticesCount = pSource->getTechnique_common()->getAccessor()->getCount(); + result = verifyCount( verticesCount, vertexWeightsCount ); + if ( !result ) + { + return result; + } + } + } + + U32 vcountCount = (U32) pVertexWeights->getVcount()->getValue().getCount(); + result = verifyCount( vcountCount, vertexWeightsCount ); + if ( !result ) + { + return result; + } + + domInputLocalOffset_Array& inputs = pVertexWeights->getInput_array(); + U32 sum = 0; + for (size_t i=0; i<vcountCount; i++) + { + sum += pVertexWeights->getVcount()->getValue()[i]; + } + result = verifyCount( sum * inputs.getCount(), (domInt) pVertexWeights->getV()->getValue().getCount() ); + } + } + + return result; +} + +//----------------------------------------------------------------------------- +// extractTranslation() +//----------------------------------------------------------------------------- +void LLDAELoader::extractTranslation( domTranslate* pTranslate, LLMatrix4& transform ) +{ + domFloat3 jointTrans = pTranslate->getValue(); + LLVector3 singleJointTranslation( jointTrans[0], jointTrans[1], jointTrans[2] ); + transform.setTranslation( singleJointTranslation ); +} +//----------------------------------------------------------------------------- +// extractTranslationViaElement() +//----------------------------------------------------------------------------- +void LLDAELoader::extractTranslationViaElement( daeElement* pTranslateElement, LLMatrix4& transform ) +{ + if ( pTranslateElement ) + { + domTranslate* pTranslateChild = dynamic_cast<domTranslate*>( pTranslateElement ); + domFloat3 translateChild = pTranslateChild->getValue(); + LLVector3 singleJointTranslation( translateChild[0], translateChild[1], translateChild[2] ); + transform.setTranslation( singleJointTranslation ); + } +} +//----------------------------------------------------------------------------- +// extractTranslationViaSID() +//----------------------------------------------------------------------------- +void LLDAELoader::extractTranslationViaSID( daeElement* pElement, LLMatrix4& transform ) +{ + if ( pElement ) + { + daeSIDResolver resolver( pElement, "./transform" ); + domMatrix* pMatrix = daeSafeCast<domMatrix>( resolver.getElement() ); + //We are only extracting out the translational component atm + LLMatrix4 workingTransform; + if ( pMatrix ) + { + domFloat4x4 domArray = pMatrix->getValue(); + for ( int i = 0; i < 4; i++ ) + { + for( int j = 0; j < 4; j++ ) + { + workingTransform.mMatrix[i][j] = domArray[i + j*4]; + } + } + LLVector3 trans = workingTransform.getTranslation(); + transform.setTranslation( trans ); + } + } + else + { + LL_WARNS()<<"Element is nonexistent - empty/unsupported node."<<LL_ENDL; + } +} +//----------------------------------------------------------------------------- +// processJointNode() +//----------------------------------------------------------------------------- +void LLDAELoader::processJointNode( domNode* pNode, JointTransformMap& jointTransforms ) +{ + if (pNode->getName() == NULL) + { + LL_WARNS() << "nameless node, can't process" << LL_ENDL; + return; + } + + //LL_WARNS()<<"ProcessJointNode# Node:" <<pNode->getName()<<LL_ENDL; + + //1. handle the incoming node - extract out translation via SID or element + + LLMatrix4 workingTransform; + + //Pull out the translate id and store it in the jointTranslations map + daeSIDResolver jointResolverA( pNode, "./translate" ); + domTranslate* pTranslateA = daeSafeCast<domTranslate>( jointResolverA.getElement() ); + daeSIDResolver jointResolverB( pNode, "./location" ); + domTranslate* pTranslateB = daeSafeCast<domTranslate>( jointResolverB.getElement() ); + + //Translation via SID was successful + if ( pTranslateA ) + { + extractTranslation( pTranslateA, workingTransform ); + } + else + if ( pTranslateB ) + { + extractTranslation( pTranslateB, workingTransform ); + } + else + { + //Translation via child from element + daeElement* pTranslateElement = getChildFromElement( pNode, "translate" ); + if ( !pTranslateElement || pTranslateElement->typeID() != domTranslate::ID() ) + { + //LL_WARNS()<< "The found element is not a translate node" <<LL_ENDL; + daeSIDResolver jointResolver( pNode, "./matrix" ); + domMatrix* pMatrix = daeSafeCast<domMatrix>( jointResolver.getElement() ); + if ( pMatrix ) + { + //LL_INFOS()<<"A matrix SID was however found!"<<LL_ENDL; + domFloat4x4 domArray = pMatrix->getValue(); + for ( int i = 0; i < 4; i++ ) + { + for( int j = 0; j < 4; j++ ) + { + workingTransform.mMatrix[i][j] = domArray[i + j*4]; + } + } + } + else + { + LL_WARNS()<< "The found element is not translate or matrix node - most likely a corrupt export!" <<LL_ENDL; + } + } + else + { + extractTranslationViaElement( pTranslateElement, workingTransform ); + } + } + + //Store the working transform relative to the nodes name. + jointTransforms[ pNode->getName() ] = workingTransform; + + //2. handle the nodes children + + //Gather and handle the incoming nodes children + daeTArray< daeSmartRef<daeElement> > childOfChild = pNode->getChildren(); + S32 childOfChildCount = childOfChild.getCount(); + + for (S32 i = 0; i < childOfChildCount; ++i) + { + domNode* pChildNode = daeSafeCast<domNode>( childOfChild[i] ); + if ( pChildNode ) + { + processJointNode( pChildNode, jointTransforms ); + } + } +} +//----------------------------------------------------------------------------- +// getChildFromElement() +//----------------------------------------------------------------------------- +daeElement* LLDAELoader::getChildFromElement( daeElement* pElement, std::string const & name ) +{ + daeElement* pChildOfElement = pElement->getChild( name.c_str() ); + if ( pChildOfElement ) + { + return pChildOfElement; + } + LL_WARNS()<< "Could not find a child [" << name << "] for the element: \"" << pElement->getAttribute("id") << "\"" << LL_ENDL; + return NULL; +} + +void LLDAELoader::processElement( daeElement* element, bool& badElement, DAE* dae ) +{ + LLMatrix4 saved_transform; + bool pushed_mat = false; + + domNode* node = daeSafeCast<domNode>(element); + if (node) + { + pushed_mat = true; + saved_transform = mTransform; + } + + domTranslate* translate = daeSafeCast<domTranslate>(element); + if (translate) + { + domFloat3 dom_value = translate->getValue(); + + LLMatrix4 translation; + translation.setTranslation(LLVector3(dom_value[0], dom_value[1], dom_value[2])); + + translation *= mTransform; + mTransform = translation; + mTransform.condition(); + } + + domRotate* rotate = daeSafeCast<domRotate>(element); + if (rotate) + { + domFloat4 dom_value = rotate->getValue(); + + LLMatrix4 rotation; + rotation.initRotTrans(dom_value[3] * DEG_TO_RAD, LLVector3(dom_value[0], dom_value[1], dom_value[2]), LLVector3(0, 0, 0)); + + rotation *= mTransform; + mTransform = rotation; + mTransform.condition(); + } + + domScale* scale = daeSafeCast<domScale>(element); + if (scale) + { + 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(scale_vector); + + scaling *= mTransform; + mTransform = scaling; + mTransform.condition(); + } + + domMatrix* matrix = daeSafeCast<domMatrix>(element); + if (matrix) + { + domFloat4x4 dom_value = matrix->getValue(); + + LLMatrix4 matrix_transform; + + for (int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + matrix_transform.mMatrix[i][j] = dom_value[i + j*4]; + } + } + + matrix_transform *= mTransform; + mTransform = matrix_transform; + mTransform.condition(); + } + + domInstance_geometry* instance_geo = daeSafeCast<domInstance_geometry>(element); + if (instance_geo) + { + domGeometry* geo = daeSafeCast<domGeometry>(instance_geo->getUrl().getElement()); + if (geo) + { + domMesh* mesh = daeSafeCast<domMesh>(geo->getDescendant(daeElement::matchType(domMesh::ID()))); + if (mesh) + { + + std::vector< LLPointer< LLModel > >::iterator i = mModelsMap[mesh].begin(); + while (i != mModelsMap[mesh].end()) + { + LLModel* model = *i; + + LLMatrix4 transformation = mTransform; + + if (mTransform.determinant() < 0) + { //negative scales are not supported + LL_INFOS() << "Negative scale detected, unsupported transform. domInstance_geometry: " << getElementLabel(instance_geo) << LL_ENDL; + badElement = true; + } + + LLModelLoader::material_map materials = getMaterials(model, instance_geo, dae); + + // adjust the transformation to compensate for mesh normalization + LLVector3 mesh_scale_vector; + LLVector3 mesh_translation_vector; + model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector); + + LLMatrix4 mesh_translation; + mesh_translation.setTranslation(mesh_translation_vector); + mesh_translation *= transformation; + transformation = mesh_translation; + + LLMatrix4 mesh_scale; + mesh_scale.initScale(mesh_scale_vector); + mesh_scale *= transformation; + transformation = mesh_scale; + + if (transformation.determinant() < 0) + { //negative scales are not supported + LL_INFOS() << "Negative scale detected, unsupported post-normalization transform. domInstance_geometry: " << getElementLabel(instance_geo) << LL_ENDL; + badElement = true; + } + + std::string label; + + if (model->mLabel.empty()) + { + label = getLodlessLabel(instance_geo); + + llassert(!label.empty()); + + if (model->mSubmodelID) + { + label += (char)((int)'a' + model->mSubmodelID); + } + + model->mLabel = label + lod_suffix[mLod]; + } + else + { + // Don't change model's name if possible, it will play havoc with scenes that already use said model. + size_t ext_pos = getSuffixPosition(model->mLabel); + if (ext_pos != -1) + { + label = model->mLabel.substr(0, ext_pos); + } + else + { + label = model->mLabel; + } + } + + mScene[transformation].push_back(LLModelInstance(model, label, transformation, materials)); + stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform); + i++; + } + } + } + else + { + LL_INFOS()<<"Unable to resolve geometry URL."<<LL_ENDL; + badElement = true; + } + + } + + domInstance_node* instance_node = daeSafeCast<domInstance_node>(element); + if (instance_node) + { + daeElement* instance = instance_node->getUrl().getElement(); + if (instance) + { + processElement(instance,badElement, dae); + } + } + + //process children + daeTArray< daeSmartRef<daeElement> > children = element->getChildren(); + int childCount = children.getCount(); + for (S32 i = 0; i < childCount; i++) + { + processElement(children[i],badElement, dae); + } + + if (pushed_mat) + { //this element was a node, restore transform before processiing siblings + mTransform = saved_transform; + } +} + +std::map<std::string, LLImportMaterial> LLDAELoader::getMaterials(LLModel* model, domInstance_geometry* instance_geo, DAE* dae) +{ + std::map<std::string, LLImportMaterial> materials; + for (int i = 0; i < model->mMaterialList.size(); i++) + { + LLImportMaterial import_material; + + domInstance_material* instance_mat = NULL; + + domBind_material::domTechnique_common* technique = + daeSafeCast<domBind_material::domTechnique_common>(instance_geo->getDescendant(daeElement::matchType(domBind_material::domTechnique_common::ID()))); + + if (technique) + { + daeTArray< daeSmartRef<domInstance_material> > inst_materials = technique->getChildrenByType<domInstance_material>(); + for (int j = 0; j < inst_materials.getCount(); j++) + { + std::string symbol(inst_materials[j]->getSymbol()); + + if (symbol == model->mMaterialList[i]) // found the binding + { + instance_mat = inst_materials[j]; + break; + } + } + } + + if (instance_mat) + { + domMaterial* material = daeSafeCast<domMaterial>(instance_mat->getTarget().getElement()); + if (material) + { + domInstance_effect* instance_effect = + daeSafeCast<domInstance_effect>(material->getDescendant(daeElement::matchType(domInstance_effect::ID()))); + if (instance_effect) + { + domEffect* effect = daeSafeCast<domEffect>(instance_effect->getUrl().getElement()); + if (effect) + { + domProfile_COMMON* profile = + daeSafeCast<domProfile_COMMON>(effect->getDescendant(daeElement::matchType(domProfile_COMMON::ID()))); + if (profile) + { + import_material = profileToMaterial(profile, dae); + } + } + } + } + } + + import_material.mBinding = model->mMaterialList[i]; + materials[model->mMaterialList[i]] = import_material; + } + + return materials; +} + +LLImportMaterial LLDAELoader::profileToMaterial(domProfile_COMMON* material, DAE* dae) +{ + LLImportMaterial mat; + mat.mFullbright = FALSE; + + daeElement* diffuse = material->getDescendant("diffuse"); + if (diffuse) + { + domCommon_color_or_texture_type_complexType::domTexture* texture = + daeSafeCast<domCommon_color_or_texture_type_complexType::domTexture>(diffuse->getDescendant("texture")); + if (texture) + { + domCommon_newparam_type_Array newparams = material->getNewparam_array(); + if (newparams.getCount()) + { + + for (S32 i = 0; i < newparams.getCount(); i++) + { + domFx_surface_common* surface = newparams[i]->getSurface(); + if (surface) + { + domFx_surface_init_common* init = surface->getFx_surface_init_common(); + if (init) + { + domFx_surface_init_from_common_Array init_from = init->getInit_from_array(); + + if (init_from.getCount() > i) + { + domImage* image = daeSafeCast<domImage>(init_from[i]->getValue().getElement()); + if (image) + { + // we only support init_from now - embedded data will come later + domImage::domInit_from* init = image->getInit_from(); + if (init) + { + mat.mDiffuseMapFilename = cdom::uriToNativePath(init->getValue().str()); + mat.mDiffuseMapLabel = getElementLabel(material); + } + } + } + } + } + } + } + else if (texture->getTexture()) + { + domImage* image = NULL; + dae->getDatabase()->getElement((daeElement**) &image, 0, texture->getTexture(), COLLADA_TYPE_IMAGE); + if (image) + { + // we only support init_from now - embedded data will come later + domImage::domInit_from* init = image->getInit_from(); + if (init) + { + std::string image_path_value = cdom::uriToNativePath(init->getValue().str()); + +#if LL_WINDOWS + // Work-around DOM tendency to resort to UNC names which are only confusing for downstream... + // + std::string::iterator i = image_path_value.begin(); + while (*i == '\\') + i++; + mat.mDiffuseMapFilename.assign(i, image_path_value.end()); +#else + mat.mDiffuseMapFilename = image_path_value; +#endif + mat.mDiffuseMapLabel = getElementLabel(material); + } + } + } + } + + domCommon_color_or_texture_type_complexType::domColor* color = + daeSafeCast<domCommon_color_or_texture_type_complexType::domColor>(diffuse->getDescendant("color")); + if (color) + { + domFx_color_common domfx_color = color->getValue(); + LLColor4 value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]); + mat.mDiffuseColor = value; + } + } + + daeElement* emission = material->getDescendant("emission"); + if (emission) + { + LLColor4 emission_color = getDaeColor(emission); + if (((emission_color[0] + emission_color[1] + emission_color[2]) / 3.0) > 0.25) + { + mat.mFullbright = TRUE; + } + } + + return mat; +} + +// try to get a decent label for this element +std::string LLDAELoader::getElementLabel(daeElement *element) +{ + // if we have a name attribute, use it + std::string name = element->getAttribute("name"); + if (name.length()) + { + return name; + } + + // if we have an ID attribute, use it + if (element->getID()) + { + return std::string(element->getID()); + } + + // if we have a parent, use it + daeElement* parent = element->getParent(); + std::string index_string; + if (parent) + { + // retrieve index to distinguish items inside same parent + size_t ind = 0; + parent->getChildren().find(element, ind); + index_string = "_" + boost::lexical_cast<std::string>(ind); + + // if parent has a name or ID, use it + std::string name = parent->getAttribute("name"); + if (!name.length()) + { + name = std::string(parent->getID()); + } + + if (name.length()) + { + // make sure that index won't mix up with pre-named lod extensions + size_t ext_pos = getSuffixPosition(name); + + if (ext_pos == -1) + { + return name + index_string; + } + else + { + return name.insert(ext_pos, index_string); + } + } + } + + // try to use our type + daeString element_name = element->getElementName(); + if (element_name) + { + return std::string(element_name) + index_string; + } + + // if all else fails, use "object" + return std::string("object") + index_string; +} + +// static +size_t LLDAELoader::getSuffixPosition(std::string label) +{ + if ((label.find("_LOD") != -1) || (label.find("_PHYS") != -1)) + { + return label.rfind('_'); + } + return -1; +} + +// static +std::string LLDAELoader::getLodlessLabel(daeElement *element) +{ + std::string label = getElementLabel(element); + size_t ext_pos = getSuffixPosition(label); + if (ext_pos != -1) + { + return label.substr(0, ext_pos); + } + return label; +} + +LLColor4 LLDAELoader::getDaeColor(daeElement* element) +{ + LLColor4 value; + domCommon_color_or_texture_type_complexType::domColor* color = + daeSafeCast<domCommon_color_or_texture_type_complexType::domColor>(element->getDescendant("color")); + if (color) + { + domFx_color_common domfx_color = color->getValue(); + value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]); + } + + return value; +} + +bool LLDAELoader::addVolumeFacesFromDomMesh(LLModel* pModel,domMesh* mesh) +{ + LLModel::EModelStatus status = LLModel::NO_ERRORS; + domTriangles_Array& tris = mesh->getTriangles_array(); + + for (U32 i = 0; i < tris.getCount(); ++i) + { + domTrianglesRef& tri = tris.get(i); + + status = load_face_from_dom_triangles(pModel->getVolumeFaces(), pModel->getMaterialList(), tri); + pModel->mStatus = status; + if(status != LLModel::NO_ERRORS) + { + pModel->ClearFacesAndMaterials(); + return false; + } + } + + domPolylist_Array& polys = mesh->getPolylist_array(); + for (U32 i = 0; i < polys.getCount(); ++i) + { + domPolylistRef& poly = polys.get(i); + status = load_face_from_dom_polylist(pModel->getVolumeFaces(), pModel->getMaterialList(), poly); + + if(status != LLModel::NO_ERRORS) + { + pModel->ClearFacesAndMaterials(); + return false; + } + } + + domPolygons_Array& polygons = mesh->getPolygons_array(); + + for (U32 i = 0; i < polygons.getCount(); ++i) + { + domPolygonsRef& poly = polygons.get(i); + status = load_face_from_dom_polygons(pModel->getVolumeFaces(), pModel->getMaterialList(), poly); + + if(status != LLModel::NO_ERRORS) + { + pModel->ClearFacesAndMaterials(); + return false; + } + } + + return (status == LLModel::NO_ERRORS); +} + +//static +LLModel* LLDAELoader::loadModelFromDomMesh(domMesh *mesh) +{ + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + LLModel* ret = new LLModel(volume_params, 0.f); + createVolumeFacesFromDomMesh(ret, mesh); + if (ret->mLabel.empty()) + { + ret->mLabel = getElementLabel(mesh); + } + return ret; +} + +//static diff version supports creating multiple models when material counts spill +// over the 8 face server-side limit +// +bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& models_out, U32 submodel_limit) +{ + + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + + models_out.clear(); + + LLModel* ret = new LLModel(volume_params, 0.f); + + std::string model_name = getLodlessLabel(mesh); + ret->mLabel = model_name + lod_suffix[mLod]; + + llassert(!ret->mLabel.empty()); + + // Like a monkey, ready to be shot into space + // + ret->ClearFacesAndMaterials(); + + // Get the whole set of volume faces + // + addVolumeFacesFromDomMesh(ret, mesh); + + U32 volume_faces = ret->getNumVolumeFaces(); + + // Side-steps all manner of issues when splitting models + // and matching lower LOD materials to base models + // + ret->sortVolumeFacesByMaterialName(); + + bool normalized = false; + + int submodelID = 0; + + // remove all faces that definitely won't fit into one model and submodel limit + U32 face_limit = (submodel_limit + 1) * LL_SCULPT_MESH_MAX_FACES; + if (face_limit < volume_faces) + { + ret->setNumVolumeFaces(face_limit); + } + + LLVolume::face_list_t remainder; + do + { + // Insure we do this once with the whole gang and not per-model + // + if (!normalized && !mNoNormalize) + { + normalized = true; + ret->normalizeVolumeFaces(); + } + + ret->trimVolumeFacesToSize(LL_SCULPT_MESH_MAX_FACES, &remainder); + + if (!mNoOptimize) + { + ret->optimizeVolumeFaces(); + } + + volume_faces = remainder.size(); + + models_out.push_back(ret); + + // If we have left-over volume faces, create another model + // to absorb them... + // + if (volume_faces) + { + LLModel* next = new LLModel(volume_params, 0.f); + next->mSubmodelID = ++submodelID; + next->mLabel = model_name + (char)((int)'a' + next->mSubmodelID) + lod_suffix[mLod]; + next->getVolumeFaces() = remainder; + next->mNormalizedScale = ret->mNormalizedScale; + next->mNormalizedTranslation = ret->mNormalizedTranslation; + if ( ret->mMaterialList.size() > LL_SCULPT_MESH_MAX_FACES) + { + next->mMaterialList.assign(ret->mMaterialList.begin() + LL_SCULPT_MESH_MAX_FACES, ret->mMaterialList.end()); + } + ret = next; + } + + remainder.clear(); + + } while (volume_faces); + + return true; +} + +bool LLDAELoader::createVolumeFacesFromDomMesh(LLModel* pModel, domMesh* mesh) +{ + if (mesh) + { + pModel->ClearFacesAndMaterials(); + + addVolumeFacesFromDomMesh(pModel, mesh); + + if (pModel->getNumVolumeFaces() > 0) + { + pModel->normalizeVolumeFaces(); + pModel->optimizeVolumeFaces(); + + if (pModel->getNumVolumeFaces() > 0) + { + return true; + } + } + } + else + { + LL_WARNS() << "no mesh found" << LL_ENDL; + } + + return false; +} diff --git a/indra/llprimitive/lldaeloader.h b/indra/llprimitive/lldaeloader.h new file mode 100644 index 0000000000..7d91a6063b --- /dev/null +++ b/indra/llprimitive/lldaeloader.h @@ -0,0 +1,107 @@ +/** + * @file lldaeloader.h + * @brief LLDAELoader class definition + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2013, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLDAELOADER_H +#define LL_LLDAELOADER_H + +#include "llmodelloader.h" + +class DAE; +class daeElement; +class domProfile_COMMON; +class domInstance_geometry; +class domNode; +class domTranslate; +class domController; +class domSkin; +class domMesh; + +class LLDAELoader : public LLModelLoader +{ +public: + typedef std::map<std::string, LLImportMaterial> material_map; + typedef std::map<daeElement*, std::vector<LLPointer<LLModel> > > dae_model_map; + dae_model_map mModelsMap; + + LLDAELoader( + std::string filename, + S32 lod, + LLModelLoader::load_callback_t load_cb, + LLModelLoader::joint_lookup_func_t joint_lookup_func, + LLModelLoader::texture_load_func_t texture_load_func, + LLModelLoader::state_callback_t state_cb, + void* opaque_userdata, + JointTransformMap& jointMap, + JointSet& jointsFromNodes, + U32 modelLimit); + virtual ~LLDAELoader() ; + + virtual bool OpenFile(const std::string& filename); + +protected: + + void processElement(daeElement* element, bool& badElement, DAE* dae); + void processDomModel(LLModel* model, DAE* dae, daeElement* pRoot, domMesh* mesh, domSkin* skin); + + material_map getMaterials(LLModel* model, domInstance_geometry* instance_geo, DAE* dae); + LLImportMaterial profileToMaterial(domProfile_COMMON* material, DAE* dae); + LLColor4 getDaeColor(daeElement* element); + + daeElement* getChildFromElement( daeElement* pElement, std::string const & name ); + + bool isNodeAJoint( domNode* pNode ); + void processJointNode( domNode* pNode, std::map<std::string,LLMatrix4>& jointTransforms ); + void extractTranslation( domTranslate* pTranslate, LLMatrix4& transform ); + void extractTranslationViaElement( daeElement* pTranslateElement, LLMatrix4& transform ); + void extractTranslationViaSID( daeElement* pElement, LLMatrix4& transform ); + void buildJointToNodeMappingFromScene( daeElement* pRoot ); + void processJointToNodeMapping( domNode* pNode ); + void processChildJoints( domNode* pParentNode ); + + bool verifyCount( int expected, int result ); + + //Verify that a controller matches vertex counts + bool verifyController( domController* pController ); + + static bool addVolumeFacesFromDomMesh(LLModel* model, domMesh* mesh); + static bool createVolumeFacesFromDomMesh(LLModel* model, domMesh *mesh); + + static LLModel* loadModelFromDomMesh(domMesh* mesh); + + // Loads a mesh breaking it into one or more models as necessary + // to get around volume face limitations while retaining >8 materials + // + bool loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& models_out, U32 submodel_limit); + + static std::string getElementLabel(daeElement *element); + static size_t getSuffixPosition(std::string label); + static std::string getLodlessLabel(daeElement *element); + +private: + U32 mGeneratedModelLimit; // Attempt to limit amount of generated submodels + +}; +#endif // LL_LLDAELLOADER_H diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index ed903146ef..e494c55250 100755 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -31,18 +31,6 @@ #include "llconvexdecomposition.h" #include "llsdserialize.h" #include "llvector4a.h" -#if LL_MSVC -#pragma warning (disable : 4263) -#pragma warning (disable : 4264) -#endif -#include "dae.h" -#include "dae/daeErrorHandler.h" -#include "dom/domConstants.h" -#include "dom/domMesh.h" -#if LL_MSVC -#pragma warning (default : 4263) -#pragma warning (default : 4264) -#endif #ifdef LL_USESYSTEMLIBS # include <zlib.h> @@ -50,8 +38,6 @@ # include "zlib/zlib.h" #endif - - std::string model_names[] = { "lowest_lod", @@ -65,7 +51,7 @@ const int MODEL_NAMES_LENGTH = sizeof(model_names) / sizeof(std::string); LLModel::LLModel(LLVolumeParams& params, F32 detail) : LLVolume(params, detail), mNormalizedScale(1,1,1), mNormalizedTranslation(0,0,0) - , mPelvisOffset( 0.0f ), mStatus(NO_ERRORS) + , mPelvisOffset( 0.0f ), mStatus(NO_ERRORS), mSubmodelID(0) { mDecompID = -1; mLocalID = -1; @@ -79,939 +65,112 @@ LLModel::~LLModel() } } - -bool get_dom_sources(const domInputLocalOffset_Array& inputs, S32& pos_offset, S32& tc_offset, S32& norm_offset, S32 &idx_stride, - domSource* &pos_source, domSource* &tc_source, domSource* &norm_source) +//static +std::string LLModel::getStatusString(U32 status) { - idx_stride = 0; + const static std::string status_strings[(S32)INVALID_STATUS] = {"status_no_error", "status_vertex_number_overflow","bad_element"}; - for (U32 j = 0; j < inputs.getCount(); ++j) + if(status < INVALID_STATUS) { - idx_stride = llmax((S32) inputs[j]->getOffset(), idx_stride); - - if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[j]->getSemantic()) == 0) - { //found vertex array - const domURIFragmentType& uri = inputs[j]->getSource(); - daeElementRef elem = uri.getElement(); - domVertices* vertices = (domVertices*) elem.cast(); - if ( !vertices ) - { - return false; - } - - domInputLocal_Array& v_inp = vertices->getInput_array(); - - - for (U32 k = 0; k < v_inp.getCount(); ++k) - { - if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0) - { - pos_offset = inputs[j]->getOffset(); - - const domURIFragmentType& uri = v_inp[k]->getSource(); - daeElementRef elem = uri.getElement(); - pos_source = (domSource*) elem.cast(); - } - - if (strcmp(COMMON_PROFILE_INPUT_NORMAL, v_inp[k]->getSemantic()) == 0) - { - norm_offset = inputs[j]->getOffset(); - - const domURIFragmentType& uri = v_inp[k]->getSource(); - daeElementRef elem = uri.getElement(); - norm_source = (domSource*) elem.cast(); - } - } - } - - if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[j]->getSemantic()) == 0) + if(status_strings[status] == std::string()) { - //found normal array for this triangle list - norm_offset = inputs[j]->getOffset(); - const domURIFragmentType& uri = inputs[j]->getSource(); - daeElementRef elem = uri.getElement(); - norm_source = (domSource*) elem.cast(); + //LL_ERRS() << "No valid status string for this status: " << (U32)status << LL_ENDL(); } - else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[j]->getSemantic()) == 0) - { //found texCoords - tc_offset = inputs[j]->getOffset(); - const domURIFragmentType& uri = inputs[j]->getSource(); - daeElementRef elem = uri.getElement(); - tc_source = (domSource*) elem.cast(); - } - } - - idx_stride += 1; - - return true; -} - -LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domTrianglesRef& tri) -{ - LLVolumeFace face; - std::vector<LLVolumeFace::VertexData> verts; - std::vector<U16> indices; - - const domInputLocalOffset_Array& inputs = tri->getInput_array(); - - S32 pos_offset = -1; - S32 tc_offset = -1; - S32 norm_offset = -1; - - domSource* pos_source = NULL; - domSource* tc_source = NULL; - domSource* norm_source = NULL; - - S32 idx_stride = 0; - - if ( !get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source) || !pos_source ) - { - LL_WARNS() << "Could not find dom sources for basic geo data; invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; + return status_strings[status] ; } - if (!pos_source) - { - LL_WARNS() << "Unable to process mesh without position data; invalid model; invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - - domPRef p = tri->getP(); - domListOfUInts& idx = p->getValue(); - - domListOfFloats dummy ; - domListOfFloats& v = (pos_source && pos_source->getFloat_array()) ? pos_source->getFloat_array()->getValue() : dummy ; - domListOfFloats& tc = (tc_source && tc_source->getFloat_array()) ? tc_source->getFloat_array()->getValue() : dummy ; - domListOfFloats& n = (norm_source && norm_source->getFloat_array()) ? norm_source->getFloat_array()->getValue() : dummy ; + //LL_ERRS() << "Invalid model status: " << (U32)status << LL_ENDL(); - LLVolumeFace::VertexMapData::PointMap point_map; - - U32 index_count = idx.getCount(); - U32 vertex_count = (pos_source && pos_source->getFloat_array()) ? v.getCount() : 0; - U32 tc_count = (tc_source && tc_source->getFloat_array()) ? tc.getCount() : 0; - U32 norm_count = (norm_source && norm_source->getFloat_array()) ? n.getCount(): 0; + return std::string() ; +} - if (vertex_count == 0) - { - LL_WARNS() << "Unable to process mesh with empty position array; invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - face.mExtents[0].set(v[0], v[1], v[2]); - face.mExtents[1].set(v[0], v[1], v[2]); +void LLModel::offsetMesh( const LLVector3& pivotPoint ) +{ + LLVector4a pivot( pivotPoint[VX], pivotPoint[VY], pivotPoint[VZ] ); - for (U32 i = 0; i < index_count; i += idx_stride) - { - LLVolumeFace::VertexData cv; - if (pos_source) - { - // guard against model data specifiying out of range indices or verts - // - if (((i + pos_offset) > index_count) - || ((idx[i+pos_offset]*3+2) > vertex_count)) - { - LL_WARNS() << "Out of range index data; invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - - cv.setPosition(LLVector4a(v[idx[i+pos_offset]*3+0], - v[idx[i+pos_offset]*3+1], - v[idx[i+pos_offset]*3+2])); - - if (!cv.getPosition().isFinite3()) - { - LL_WARNS() << "Nan positional data, invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - } - - if (tc_source) - { - // guard against model data specifiying out of range indices or tcs - // - - if (((i + tc_offset) > index_count) - || ((idx[i+tc_offset]*2+1) > tc_count)) - { - LL_WARNS() << "Out of range TC indices." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - - cv.mTexCoord.setVec(tc[idx[i+tc_offset]*2+0], - tc[idx[i+tc_offset]*2+1]); - - if (!cv.mTexCoord.isFinite()) - { - LL_WARNS() << "Found NaN while loading tex coords from DAE-Model, invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - } - - if (norm_source) - { - // guard against model data specifiying out of range indices or norms - // - if (((i + norm_offset) > index_count) - || ((idx[i+norm_offset]*3+2) > norm_count)) - { - LL_WARNS() << "Found out of range norm indices, invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - - cv.setNormal(LLVector4a(n[idx[i+norm_offset]*3+0], - n[idx[i+norm_offset]*3+1], - n[idx[i+norm_offset]*3+2])); - - if (!cv.getNormal().isFinite3()) - { - LL_WARNS() << "Found NaN while loading normals from DAE-Model, invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - } - - BOOL found = FALSE; - - LLVolumeFace::VertexMapData::PointMap::iterator point_iter; - point_iter = point_map.find(LLVector3(cv.getPosition().getF32ptr())); - - if (point_iter != point_map.end()) - { - for (U32 j = 0; j < point_iter->second.size(); ++j) - { - if ((point_iter->second)[j] == cv) - { - found = TRUE; - indices.push_back((point_iter->second)[j].mIndex); - break; - } - } - } - - if (!found) - { - update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition()); - verts.push_back(cv); - if (verts.size() >= 65535) - { - //LL_ERRS() << "Attempted to write model exceeding 16-bit index buffer limitation." << LL_ENDL; - return LLModel::VERTEX_NUMBER_OVERFLOW ; - } - U16 index = (U16) (verts.size()-1); - indices.push_back(index); - - LLVolumeFace::VertexMapData d; - d.setPosition(cv.getPosition()); - d.mTexCoord = cv.mTexCoord; - d.setNormal(cv.getNormal()); - d.mIndex = index; - if (point_iter != point_map.end()) - { - point_iter->second.push_back(d); - } - else - { - point_map[LLVector3(d.getPosition().getF32ptr())].push_back(d); - } - } - - if (indices.size()%3 == 0 && verts.size() >= 65532) - { - std::string material; - - if (tri->getMaterial()) - { - material = std::string(tri->getMaterial()); - } - - materials.push_back(material); - face_list.push_back(face); - face_list.rbegin()->fillFromLegacyData(verts, indices); - LLVolumeFace& new_face = *face_list.rbegin(); - if (!norm_source) - { - //ll_aligned_free_16(new_face.mNormals); - new_face.mNormals = NULL; - } - - if (!tc_source) - { - //ll_aligned_free_16(new_face.mTexCoords); - new_face.mTexCoords = NULL; - } - - face = LLVolumeFace(); - point_map.clear(); - } - } - - if (!verts.empty()) + for (std::vector<LLVolumeFace>::iterator faceIt = mVolumeFaces.begin(); faceIt != mVolumeFaces.end(); ) { - std::string material; - - if (tri->getMaterial()) - { - material = std::string(tri->getMaterial()); - } + std::vector<LLVolumeFace>:: iterator currentFaceIt = faceIt++; + LLVolumeFace& face = *currentFaceIt; + LLVector4a *pos = (LLVector4a*) face.mPositions; - materials.push_back(material); - face_list.push_back(face); - - face_list.rbegin()->fillFromLegacyData(verts, indices); - LLVolumeFace& new_face = *face_list.rbegin(); - if (!norm_source) - { - //ll_aligned_free_16(new_face.mNormals); - new_face.mNormals = NULL; - } - - if (!tc_source) + for (U32 i=0; i<face.mNumVertices; ++i ) { - //ll_aligned_free_16(new_face.mTexCoords); - new_face.mTexCoords = NULL; + pos[i].add( pivot ); } } - - return LLModel::NO_ERRORS ; } -LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolylistRef& poly) +void LLModel::optimizeVolumeFaces() { - domPRef p = poly->getP(); - domListOfUInts& idx = p->getValue(); - - if (idx.getCount() == 0) - { - return LLModel::NO_ERRORS ; - } - - const domInputLocalOffset_Array& inputs = poly->getInput_array(); - - - domListOfUInts& vcount = poly->getVcount()->getValue(); - - S32 pos_offset = -1; - S32 tc_offset = -1; - S32 norm_offset = -1; - - domSource* pos_source = NULL; - domSource* tc_source = NULL; - domSource* norm_source = NULL; - - S32 idx_stride = 0; - - if (!get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source)) - { - LL_WARNS() << "Could not get DOM sources for basic geo data, invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - - LLVolumeFace face; - - std::vector<U16> indices; - std::vector<LLVolumeFace::VertexData> verts; - - domListOfFloats v; - domListOfFloats tc; - domListOfFloats n; - - if (pos_source) - { - v = pos_source->getFloat_array()->getValue(); - face.mExtents[0].set(v[0], v[1], v[2]); - face.mExtents[1].set(v[0], v[1], v[2]); - } - - if (tc_source) - { - tc = tc_source->getFloat_array()->getValue(); - } - - if (norm_source) - { - n = norm_source->getFloat_array()->getValue(); - } - - LLVolumeFace::VertexMapData::PointMap point_map; - - U32 index_count = idx.getCount(); - U32 vertex_count = pos_source ? v.getCount() : 0; - U32 tc_count = tc_source ? tc.getCount() : 0; - U32 norm_count = norm_source ? n.getCount() : 0; - - U32 cur_idx = 0; - for (U32 i = 0; i < vcount.getCount(); ++i) - { //for each polygon - U32 first_index = 0; - U32 last_index = 0; - for (U32 j = 0; j < vcount[i]; ++j) - { //for each vertex - - LLVolumeFace::VertexData cv; - - if (pos_source) - { - // guard against model data specifiying out of range indices or verts - // - if (((cur_idx + pos_offset) > index_count) - || ((idx[cur_idx+pos_offset]*3+2) > vertex_count)) - { - LL_WARNS() << "Out of range position indices, invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - - cv.getPosition().set(v[idx[cur_idx+pos_offset]*3+0], - v[idx[cur_idx+pos_offset]*3+1], - v[idx[cur_idx+pos_offset]*3+2]); - - if (!cv.getPosition().isFinite3()) - { - LL_WARNS() << "Found NaN while loading positions from DAE-Model, invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - - } - - if (tc_source) - { - // guard against model data specifiying out of range indices or tcs - // - if (((cur_idx + tc_offset) > index_count) - || ((idx[cur_idx+tc_offset]*2+1) > tc_count)) - { - LL_WARNS() << "Out of range TC indices, invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - - cv.mTexCoord.setVec(tc[idx[cur_idx+tc_offset]*2+0], - tc[idx[cur_idx+tc_offset]*2+1]); - - if (!cv.mTexCoord.isFinite()) - { - LL_WARNS() << "Found NaN while loading tex coords from DAE-Model, invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - } - - if (norm_source) - { - // guard against model data specifiying out of range indices or norms - // - if (((cur_idx + norm_offset) > index_count) - || ((idx[cur_idx+norm_offset]*3+2) > norm_count)) - { - LL_WARNS() << "Out of range norm indices, invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - - cv.getNormal().set(n[idx[cur_idx+norm_offset]*3+0], - n[idx[cur_idx+norm_offset]*3+1], - n[idx[cur_idx+norm_offset]*3+2]); - - if (!cv.getNormal().isFinite3()) - { - LL_WARNS() << "Found NaN while loading normals from DAE-Model, invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - } - - cur_idx += idx_stride; - - BOOL found = FALSE; - - LLVolumeFace::VertexMapData::PointMap::iterator point_iter; - LLVector3 pos3(cv.getPosition().getF32ptr()); - point_iter = point_map.find(pos3); - - if (point_iter != point_map.end()) - { - for (U32 k = 0; k < point_iter->second.size(); ++k) - { - if ((point_iter->second)[k] == cv) - { - found = TRUE; - U32 index = (point_iter->second)[k].mIndex; - if (j == 0) - { - first_index = index; - } - else if (j == 1) - { - last_index = index; - } - else - { - indices.push_back(first_index); - indices.push_back(last_index); - indices.push_back(index); - last_index = index; - } - - break; - } - } - } - - if (!found) - { - update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition()); - verts.push_back(cv); - if (verts.size() >= 65535) - { - //LL_ERRS() << "Attempted to write model exceeding 16-bit index buffer limitation." << LL_ENDL; - return LLModel::VERTEX_NUMBER_OVERFLOW ; - } - U16 index = (U16) (verts.size()-1); - - if (j == 0) - { - first_index = index; - } - else if (j == 1) - { - last_index = index; - } - else - { - indices.push_back(first_index); - indices.push_back(last_index); - indices.push_back(index); - last_index = index; - } - - LLVolumeFace::VertexMapData d; - d.setPosition(cv.getPosition()); - d.mTexCoord = cv.mTexCoord; - d.setNormal(cv.getNormal()); - d.mIndex = index; - if (point_iter != point_map.end()) - { - point_iter->second.push_back(d); - } - else - { - point_map[pos3].push_back(d); - } - } - - if (indices.size()%3 == 0 && indices.size() >= 65532) - { - std::string material; - - if (poly->getMaterial()) - { - material = std::string(poly->getMaterial()); - } - - materials.push_back(material); - face_list.push_back(face); - face_list.rbegin()->fillFromLegacyData(verts, indices); - LLVolumeFace& new_face = *face_list.rbegin(); - if (!norm_source) - { - //ll_aligned_free_16(new_face.mNormals); - new_face.mNormals = NULL; - } - - if (!tc_source) - { - //ll_aligned_free_16(new_face.mTexCoords); - new_face.mTexCoords = NULL; - } - - face = LLVolumeFace(); - verts.clear(); - indices.clear(); - point_map.clear(); - } - } - } - - if (!verts.empty()) + for (U32 i = 0; i < getNumVolumeFaces(); ++i) { - std::string material; - - if (poly->getMaterial()) - { - material = std::string(poly->getMaterial()); - } - - materials.push_back(material); - face_list.push_back(face); - face_list.rbegin()->fillFromLegacyData(verts, indices); - - LLVolumeFace& new_face = *face_list.rbegin(); - if (!norm_source) - { - //ll_aligned_free_16(new_face.mNormals); - new_face.mNormals = NULL; - } - - if (!tc_source) - { - //ll_aligned_free_16(new_face.mTexCoords); - new_face.mTexCoords = NULL; - } + mVolumeFaces[i].optimize(); } - - return LLModel::NO_ERRORS ; } -LLModel::EModelStatus load_face_from_dom_polygons(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolygonsRef& poly) +struct MaterialBinding { - LLVolumeFace face; - std::vector<U16> indices; - std::vector<LLVolumeFace::VertexData> verts; - - const domInputLocalOffset_Array& inputs = poly->getInput_array(); - - S32 v_offset = -1; - S32 n_offset = -1; - S32 t_offset = -1; - - domListOfFloats* v = NULL; - domListOfFloats* n = NULL; - domListOfFloats* t = NULL; - - U32 stride = 0; - for (U32 i = 0; i < inputs.getCount(); ++i) - { - stride = llmax((U32) inputs[i]->getOffset()+1, stride); - - if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[i]->getSemantic()) == 0) - { //found vertex array - v_offset = inputs[i]->getOffset(); - - const domURIFragmentType& uri = inputs[i]->getSource(); - daeElementRef elem = uri.getElement(); - domVertices* vertices = (domVertices*) elem.cast(); - if (!vertices) - { - LL_WARNS() << "Could not find vertex source, invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - domInputLocal_Array& v_inp = vertices->getInput_array(); - - for (U32 k = 0; k < v_inp.getCount(); ++k) - { - if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0) - { - const domURIFragmentType& uri = v_inp[k]->getSource(); - daeElementRef elem = uri.getElement(); - domSource* src = (domSource*) elem.cast(); - if (!src) - { - LL_WARNS() << "Could not find DOM source, invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - v = &(src->getFloat_array()->getValue()); - } - } - } - else if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[i]->getSemantic()) == 0) - { - n_offset = inputs[i]->getOffset(); - //found normal array for this triangle list - const domURIFragmentType& uri = inputs[i]->getSource(); - daeElementRef elem = uri.getElement(); - domSource* src = (domSource*) elem.cast(); - if (!src) - { - LL_WARNS() << "Could not find DOM source, invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - n = &(src->getFloat_array()->getValue()); - } - else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[i]->getSemantic()) == 0 && inputs[i]->getSet() == 0) - { //found texCoords - t_offset = inputs[i]->getOffset(); - const domURIFragmentType& uri = inputs[i]->getSource(); - daeElementRef elem = uri.getElement(); - domSource* src = (domSource*) elem.cast(); - if (!src) - { - LL_WARNS() << "Could not find DOM source, invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - t = &(src->getFloat_array()->getValue()); - } - } - - domP_Array& ps = poly->getP_array(); - - //make a triangle list in <verts> - for (U32 i = 0; i < ps.getCount(); ++i) - { //for each polygon - domListOfUInts& idx = ps[i]->getValue(); - for (U32 j = 0; j < idx.getCount()/stride; ++j) - { //for each vertex - if (j > 2) - { - U32 size = verts.size(); - LLVolumeFace::VertexData v0 = verts[size-3]; - LLVolumeFace::VertexData v1 = verts[size-1]; - - verts.push_back(v0); - verts.push_back(v1); - } - - LLVolumeFace::VertexData vert; - - - if (v) - { - U32 v_idx = idx[j*stride+v_offset]*3; - v_idx = llclamp(v_idx, (U32) 0, (U32) v->getCount()); - vert.getPosition().set(v->get(v_idx), - v->get(v_idx+1), - v->get(v_idx+2)); - - if (!vert.getPosition().isFinite3()) - { - LL_WARNS() << "Found NaN while loading position data from DAE-Model, invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - } - - //bounds check n and t lookups because some FBX to DAE converters - //use negative indices and empty arrays to indicate data does not exist - //for a particular channel - if (n && n->getCount() > 0) - { - U32 n_idx = idx[j*stride+n_offset]*3; - n_idx = llclamp(n_idx, (U32) 0, (U32) n->getCount()); - vert.getNormal().set(n->get(n_idx), - n->get(n_idx+1), - n->get(n_idx+2)); - - if (!vert.getNormal().isFinite3()) - { - LL_WARNS() << "Found NaN while loading normals from DAE-Model, invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - } - else - { - vert.getNormal().clear(); - } - - - if (t && t->getCount() > 0) - { - U32 t_idx = idx[j*stride+t_offset]*2; - t_idx = llclamp(t_idx, (U32) 0, (U32) t->getCount()); - vert.mTexCoord.setVec(t->get(t_idx), - t->get(t_idx+1)); - - if (!vert.mTexCoord.isFinite()) - { - LL_WARNS() << "Found NaN while loading tex coords from DAE-Model, invalid model." << LL_ENDL; - return LLModel::BAD_ELEMENT; - } - } - else - { - vert.mTexCoord.clear(); - } - - - verts.push_back(vert); - } - } - - if (verts.empty()) - { - return LLModel::NO_ERRORS; - } - - face.mExtents[0] = verts[0].getPosition(); - face.mExtents[1] = verts[0].getPosition(); - - //create a map of unique vertices to indices - std::map<LLVolumeFace::VertexData, U32> vert_idx; - - U32 cur_idx = 0; - for (U32 i = 0; i < verts.size(); ++i) - { - std::map<LLVolumeFace::VertexData, U32>::iterator iter = vert_idx.find(verts[i]); - if (iter == vert_idx.end()) - { - vert_idx[verts[i]] = cur_idx++; - } - } - - //build vertex array from map - std::vector<LLVolumeFace::VertexData> new_verts; - new_verts.resize(vert_idx.size()); - - for (std::map<LLVolumeFace::VertexData, U32>::iterator iter = vert_idx.begin(); iter != vert_idx.end(); ++iter) - { - new_verts[iter->second] = iter->first; - update_min_max(face.mExtents[0], face.mExtents[1], iter->first.getPosition()); - } - - //build index array from map - indices.resize(verts.size()); - - for (U32 i = 0; i < verts.size(); ++i) - { - indices[i] = vert_idx[verts[i]]; - } - - // DEBUG just build an expanded triangle list - /*for (U32 i = 0; i < verts.size(); ++i) - { - indices.push_back((U16) i); - update_min_max(face.mExtents[0], face.mExtents[1], verts[i].getPosition()); - }*/ - - if (!new_verts.empty()) - { - std::string material; - - if (poly->getMaterial()) - { - material = std::string(poly->getMaterial()); - } - - materials.push_back(material); - face_list.push_back(face); - face_list.rbegin()->fillFromLegacyData(new_verts, indices); - - LLVolumeFace& new_face = *face_list.rbegin(); - if (!n) - { - //ll_aligned_free_16(new_face.mNormals); - new_face.mNormals = NULL; - } - - if (!t) - { - //ll_aligned_free_16(new_face.mTexCoords); - new_face.mTexCoords = NULL; - } - } - - return LLModel::NO_ERRORS ; -} + int index; + std::string matName; +}; -//static -std::string LLModel::getStatusString(U32 status) +struct MaterialSort { - const static std::string status_strings[(S32)INVALID_STATUS] = {"status_no_error", "status_vertex_number_overflow","bad_element"}; - - if(status < INVALID_STATUS) + bool operator()(const MaterialBinding& lhs, const MaterialBinding& rhs) { - if(status_strings[status] == std::string()) - { - LL_ERRS() << "No valid status string for this status: " << (U32)status << LL_ENDL ; - } - return status_strings[status] ; + return LLStringUtil::compareInsensitive(lhs.matName, rhs.matName) < 0; } +}; - LL_ERRS() << "Invalid model status: " << (U32)status << LL_ENDL ; - - return std::string() ; -} - -void LLModel::addVolumeFacesFromDomMesh(domMesh* mesh) +void LLModel::sortVolumeFacesByMaterialName() { - domTriangles_Array& tris = mesh->getTriangles_array(); - - for (U32 i = 0; i < tris.getCount(); ++i) - { - domTrianglesRef& tri = tris.get(i); + std::vector<MaterialBinding> bindings; + bindings.resize(mVolumeFaces.size()); - mStatus = load_face_from_dom_triangles(mVolumeFaces, mMaterialList, tri); - - if(mStatus != NO_ERRORS) + for (int i = 0; i < bindings.size(); i++) + { + bindings[i].index = i; + if(i < mMaterialList.size()) { - mVolumeFaces.clear() ; - mMaterialList.clear() ; - return ; //abort + bindings[i].matName = mMaterialList[i]; } } + std::sort(bindings.begin(), bindings.end(), MaterialSort()); + std::vector< LLVolumeFace > new_faces; - domPolylist_Array& polys = mesh->getPolylist_array(); - for (U32 i = 0; i < polys.getCount(); ++i) + // remap the faces to be in the same order the mats now are... + // + new_faces.resize(bindings.size()); + for (int i = 0; i < bindings.size(); i++) { - domPolylistRef& poly = polys.get(i); - mStatus = load_face_from_dom_polylist(mVolumeFaces, mMaterialList, poly); - - if(mStatus != NO_ERRORS) + new_faces[i] = mVolumeFaces[bindings[i].index]; + if(i < mMaterialList.size()) { - mVolumeFaces.clear() ; - mMaterialList.clear() ; - return ; //abort + mMaterialList[i] = bindings[i].matName; } } - - domPolygons_Array& polygons = mesh->getPolygons_array(); - - for (U32 i = 0; i < polygons.getCount(); ++i) - { - domPolygonsRef& poly = polygons.get(i); - mStatus = load_face_from_dom_polygons(mVolumeFaces, mMaterialList, poly); - if(mStatus != NO_ERRORS) - { - mVolumeFaces.clear() ; - mMaterialList.clear() ; - return ; //abort - } - } - + mVolumeFaces = new_faces; } -BOOL LLModel::createVolumeFacesFromDomMesh(domMesh* mesh) +void LLModel::trimVolumeFacesToSize(U32 new_count, LLVolume::face_list_t* remainder) { - if (mesh) - { - mVolumeFaces.clear(); - mMaterialList.clear(); - - addVolumeFacesFromDomMesh(mesh); - - if (getNumVolumeFaces() > 0) - { - normalizeVolumeFaces(); - optimizeVolumeFaces(); - - if (getNumVolumeFaces() > 0) - { - return TRUE; - } - } - } - else - { - LL_WARNS() << "no mesh found" << LL_ENDL; - } - - return FALSE; -} + llassert(new_count <= LL_SCULPT_MESH_MAX_FACES); -void LLModel::offsetMesh( const LLVector3& pivotPoint ) -{ - LLVector4a pivot( pivotPoint[VX], pivotPoint[VY], pivotPoint[VZ] ); - - for (std::vector<LLVolumeFace>::iterator faceIt = mVolumeFaces.begin(); faceIt != mVolumeFaces.end(); ) + if (new_count && (getNumVolumeFaces() > new_count)) { - std::vector<LLVolumeFace>:: iterator currentFaceIt = faceIt++; - LLVolumeFace& face = *currentFaceIt; - LLVector4a *pos = (LLVector4a*) face.mPositions; - - for (U32 i=0; i<face.mNumVertices; ++i ) + // Copy out remaining volume faces for alternative handling, if provided + // + if (remainder) { - pos[i].add( pivot ); - } - } -} + (*remainder).assign(mVolumeFaces.begin() + new_count, mVolumeFaces.end()); + } -void LLModel::optimizeVolumeFaces() -{ - for (U32 i = 0; i < getNumVolumeFaces(); ++i) - { - mVolumeFaces[i].optimize(); + // Trim down to the final set of volume faces (now stuffed to the gills!) + // + mVolumeFaces.resize(new_count); } } @@ -1025,11 +184,6 @@ void LLModel::optimizeVolumeFaces() // within the unit cube. void LLModel::normalizeVolumeFaces() { - - // ensure we don't have too many faces - if (mVolumeFaces.size() > LL_SCULPT_MESH_MAX_FACES) - mVolumeFaces.resize(LL_SCULPT_MESH_MAX_FACES); - if (!mVolumeFaces.empty()) { LLVector4a min, max; @@ -1496,68 +650,10 @@ void LLModel::generateNormals(F32 angle_cutoff) } } -//static -std::string LLModel::getElementLabel(daeElement *element) -{ // try to get a decent label for this element - // if we have a name attribute, use it - std::string name = element->getAttribute("name"); - if (name.length()) - { - return name; - } - - // if we have an ID attribute, use it - if (element->getID()) - { - return std::string(element->getID()); - } - - // if we have a parent, use it - daeElement* parent = element->getParent(); - if (parent) - { - // if parent has a name, use it - std::string name = parent->getAttribute("name"); - if (name.length()) - { - return name; - } - - // if parent has an ID, use it - if (parent->getID()) - { - return std::string(parent->getID()); - } - } - - // try to use our type - daeString element_name = element->getElementName(); - if (element_name) - { - return std::string(element_name); - } - - // if all else fails, use "object" - return std::string("object"); -} - -//static -LLModel* LLModel::loadModelFromDomMesh(domMesh *mesh) -{ - LLVolumeParams volume_params; - volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); - LLModel* ret = new LLModel(volume_params, 0.f); - ret->createVolumeFacesFromDomMesh(mesh); - ret->mLabel = getElementLabel(mesh); - return ret; -} std::string LLModel::getName() const { - if (!mRequestedLabel.empty()) - return mRequestedLabel; - else - return mLabel; + return mRequestedLabel.empty() ? mLabel : mRequestedLabel; } //static @@ -1572,7 +668,8 @@ LLSD LLModel::writeModel( BOOL upload_skin, BOOL upload_joints, BOOL nowrite, - BOOL as_slm) + BOOL as_slm, + int submodel_id) { LLSD mdl; @@ -1601,6 +698,14 @@ LLSD LLModel::writeModel( model[LLModel::LOD_PHYSICS] = NULL; } } + else if (submodel_id) + { + const LLModel::Decomposition fake_decomp; + mdl["secondary"] = true; + mdl["submodel_id"] = submodel_id; + mdl["physics_convex"] = fake_decomp.asLLSD(); + model[LLModel::LOD_PHYSICS] = NULL; + } if (as_slm) { //save material list names @@ -1612,7 +717,7 @@ LLSD LLModel::writeModel( for (U32 idx = 0; idx < MODEL_NAMES_LENGTH; ++idx) { - if (model[idx] && model[idx]->getNumVolumeFaces() > 0) + if (model[idx] && (model[idx]->getNumVolumeFaces() > 0) && model[idx]->getVolumeFace(0).mPositions != NULL) { LLVector3 min_pos = LLVector3(model[idx]->getVolumeFace(0).mPositions[0].getF32ptr()); LLVector3 max_pos = min_pos; @@ -1845,6 +950,11 @@ LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite, BO } } + if (mdl.has("submodel_id")) + { //write out submodel id + header["submodel_id"] = (LLSD::Integer)mdl["submodel_id"]; + } + std::string out[MODEL_NAMES_LENGTH]; for (S32 i = 0; i < MODEL_NAMES_LENGTH; i++) @@ -2028,7 +1138,9 @@ bool LLModel::loadModel(std::istream& is) } } - static const std::string nm[] = + mSubmodelID = header.has("submodel_id") ? header["submodel_id"].asInteger() : false; + + static const std::string lod_name[] = { "lowest_lod", "low_lod", @@ -2041,8 +1153,8 @@ bool LLModel::loadModel(std::istream& is) S32 lod = llclamp((S32) mDetail, 0, MODEL_LODS); - if (header[nm[lod]]["offset"].asInteger() == -1 || - header[nm[lod]]["size"].asInteger() == 0 ) + if (header[lod_name[lod]]["offset"].asInteger() == -1 || + header[lod_name[lod]]["size"].asInteger() == 0 ) { //cannot load requested LOD LL_WARNS() << "LoD data is invalid!" << LL_ENDL; return false; @@ -2051,23 +1163,23 @@ bool LLModel::loadModel(std::istream& is) bool has_skin = header["skin"]["offset"].asInteger() >=0 && header["skin"]["size"].asInteger() > 0; - if (lod == LLModel::LOD_HIGH) + if ((lod == LLModel::LOD_HIGH) && !mSubmodelID) { //try to load skin info and decomp info std::ios::pos_type cur_pos = is.tellg(); loadSkinInfo(header, is); is.seekg(cur_pos); } - if (lod == LLModel::LOD_HIGH || lod == LLModel::LOD_PHYSICS) + if ((lod == LLModel::LOD_HIGH || lod == LLModel::LOD_PHYSICS) && !mSubmodelID) { std::ios::pos_type cur_pos = is.tellg(); loadDecomposition(header, is); is.seekg(cur_pos); } - is.seekg(header[nm[lod]]["offset"].asInteger(), std::ios_base::cur); + is.seekg(header[lod_name[lod]]["offset"].asInteger(), std::ios_base::cur); - if (unpackVolumeFaces(is, header[nm[lod]]["size"].asInteger())) + if (unpackVolumeFaces(is, header[lod_name[lod]]["size"].asInteger())) { if (has_skin) { @@ -2133,8 +1245,10 @@ bool LLModel::isMaterialListSubset( LLModel* ref ) break; } } + if (!foundRef) { + LL_INFOS() << "Could not find material " << mMaterialList[src] << " in reference model " << ref->mLabel << LL_ENDL; return false; } } @@ -2185,41 +1299,42 @@ bool LLModel::matchMaterialOrder(LLModel* ref, int& refFaceCnt, int& modelFaceCn for (U32 i = 0; i < mMaterialList.size(); i++) { index_map[ref->mMaterialList[i]] = i; - if (!reorder) - { //if any material name does not match reference, we need to reorder - reorder = ref->mMaterialList[i] != mMaterialList[i]; - } + //if any material name does not match reference, we need to reorder + reorder |= ref->mMaterialList[i] != mMaterialList[i]; base_mat.insert(ref->mMaterialList[i]); cur_mat.insert(mMaterialList[i]); } - if (reorder && - base_mat == cur_mat) //don't reorder if material name sets don't match + if (reorder && (base_mat == cur_mat)) //don't reorder if material name sets don't match { std::vector<LLVolumeFace> new_face_list; - new_face_list.resize(mVolumeFaces.size()); + new_face_list.resize(mMaterialList.size()); std::vector<std::string> new_material_list; - new_material_list.resize(mVolumeFaces.size()); + new_material_list.resize(mMaterialList.size()); //rebuild face list so materials have the same order //as the reference model for (U32 i = 0; i < mMaterialList.size(); ++i) { U32 ref_idx = index_map[mMaterialList[i]]; - new_face_list[ref_idx] = mVolumeFaces[i]; + if (i < mVolumeFaces.size()) + { + new_face_list[ref_idx] = mVolumeFaces[i]; + } new_material_list[ref_idx] = mMaterialList[i]; } llassert(new_material_list == ref->mMaterialList); mVolumeFaces = new_face_list; - } - //override material list with reference model ordering - mMaterialList = ref->mMaterialList; + //override material list with reference model ordering + mMaterialList = ref->mMaterialList; + } + return true; } @@ -2250,7 +1365,7 @@ bool LLModel::loadDecomposition(LLSD& header, std::istream& is) S32 offset = header["physics_convex"]["offset"].asInteger(); S32 size = header["physics_convex"]["size"].asInteger(); - if (offset >= 0 && size > 0) + if (offset >= 0 && size > 0 && !mSubmodelID) { is.seekg(offset, std::ios_base::cur); @@ -2658,3 +1773,227 @@ void LLModel::Decomposition::merge(const LLModel::Decomposition* rhs) } } +bool ll_is_degenerate(const LLVector4a& a, const LLVector4a& b, const LLVector4a& c, F32 tolerance) +{ + // small area check + { + LLVector4a edge1; edge1.setSub( a, b ); + LLVector4a edge2; edge2.setSub( a, c ); + ////////////////////////////////////////////////////////////////////////// + /// Linden Modified + ////////////////////////////////////////////////////////////////////////// + + // If no one edge is more than 10x longer than any other edge, we weaken + // the tolerance by a factor of 1e-4f. + + LLVector4a edge3; edge3.setSub( c, b ); + const F32 len1sq = edge1.dot3(edge1).getF32(); + const F32 len2sq = edge2.dot3(edge2).getF32(); + const F32 len3sq = edge3.dot3(edge3).getF32(); + bool abOK = (len1sq <= 100.f * len2sq) && (len1sq <= 100.f * len3sq); + bool acOK = (len2sq <= 100.f * len1sq) && (len1sq <= 100.f * len3sq); + bool cbOK = (len3sq <= 100.f * len1sq) && (len1sq <= 100.f * len2sq); + if ( abOK && acOK && cbOK ) + { + tolerance *= 1e-4f; + } + + ////////////////////////////////////////////////////////////////////////// + /// End Modified + ////////////////////////////////////////////////////////////////////////// + + LLVector4a cross; cross.setCross3( edge1, edge2 ); + + LLVector4a edge1b; edge1b.setSub( b, a ); + LLVector4a edge2b; edge2b.setSub( b, c ); + LLVector4a crossb; crossb.setCross3( edge1b, edge2b ); + + if ( ( cross.dot3(cross).getF32() < tolerance ) || ( crossb.dot3(crossb).getF32() < tolerance )) + { + return true; + } + } + + // point triangle distance check + { + LLVector4a Q; Q.setSub(a, b); + LLVector4a R; R.setSub(c, b); + + const F32 QQ = dot3fpu(Q, Q); + const F32 RR = dot3fpu(R, R); + const F32 QR = dot3fpu(R, Q); + + volatile F32 QQRR = QQ * RR; + volatile F32 QRQR = QR * QR; + F32 Det = (QQRR - QRQR); + + if( Det == 0.0f ) + { + return true; + } + } + + return false; +} + +bool validate_face(const LLVolumeFace& face) +{ + for (U32 i = 0; i < face.mNumIndices; ++i) + { + if (face.mIndices[i] >= face.mNumVertices) + { + LL_WARNS() << "Face has invalid index." << LL_ENDL; + return false; + } + } + + if (face.mNumIndices % 3 != 0 || face.mNumIndices == 0) + { + LL_WARNS() << "Face has invalid number of indices." << LL_ENDL; + return false; + } + + /*const LLVector4a scale(0.5f); + + for (U32 i = 0; i < face.mNumIndices; i+=3) + { + U16 idx1 = face.mIndices[i]; + U16 idx2 = face.mIndices[i+1]; + U16 idx3 = face.mIndices[i+2]; + + LLVector4a v1; v1.setMul(face.mPositions[idx1], scale); + LLVector4a v2; v2.setMul(face.mPositions[idx2], scale); + LLVector4a v3; v3.setMul(face.mPositions[idx3], scale); + + if (ll_is_degenerate(v1,v2,v3)) + { + llwarns << "Degenerate face found!" << LL_ENDL; + return false; + } + }*/ + + return true; +} + +bool validate_model(const LLModel* mdl) +{ + if (mdl->getNumVolumeFaces() == 0) + { + LL_WARNS() << "Model has no faces!" << LL_ENDL; + return false; + } + + for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) + { + if (mdl->getVolumeFace(i).mNumVertices == 0) + { + LL_WARNS() << "Face has no vertices." << LL_ENDL; + return false; + } + + if (mdl->getVolumeFace(i).mNumIndices == 0) + { + LL_WARNS() << "Face has no indices." << LL_ENDL; + return false; + } + + if (!validate_face(mdl->getVolumeFace(i))) + { + return false; + } + } + + return true; +} + +LLModelInstance::LLModelInstance(LLSD& data) + : LLModelInstanceBase() +{ + mLocalMeshID = data["mesh_id"].asInteger(); + mLabel = data["label"].asString(); + mTransform.setValue(data["transform"]); + + for (U32 i = 0; i < data["material"].size(); ++i) + { + LLImportMaterial mat(data["material"][i]); + mMaterial[mat.mBinding] = mat; + } +} + + +LLSD LLModelInstance::asLLSD() +{ + LLSD ret; + + ret["mesh_id"] = mModel->mLocalID; + ret["label"] = mLabel; + ret["transform"] = mTransform.getValue(); + + U32 i = 0; + for (std::map<std::string, LLImportMaterial>::iterator iter = mMaterial.begin(); iter != mMaterial.end(); ++iter) + { + ret["material"][i++] = iter->second.asLLSD(); + } + + return ret; +} + + +LLImportMaterial::~LLImportMaterial() +{ +} + +LLImportMaterial::LLImportMaterial(LLSD& data) +{ + mDiffuseMapFilename = data["diffuse"]["filename"].asString(); + mDiffuseMapLabel = data["diffuse"]["label"].asString(); + mDiffuseColor.setValue(data["diffuse"]["color"]); + mFullbright = data["fullbright"].asBoolean(); + mBinding = data["binding"].asString(); +} + + +LLSD LLImportMaterial::asLLSD() +{ + LLSD ret; + + ret["diffuse"]["filename"] = mDiffuseMapFilename; + ret["diffuse"]["label"] = mDiffuseMapLabel; + ret["diffuse"]["color"] = mDiffuseColor.getValue(); + ret["fullbright"] = mFullbright; + ret["binding"] = mBinding; + + return ret; +} + +bool LLImportMaterial::operator<(const LLImportMaterial &rhs) const +{ + + if (mDiffuseMapID != rhs.mDiffuseMapID) + { + return mDiffuseMapID < rhs.mDiffuseMapID; + } + + if (mDiffuseMapFilename != rhs.mDiffuseMapFilename) + { + return mDiffuseMapFilename < rhs.mDiffuseMapFilename; + } + + if (mDiffuseMapLabel != rhs.mDiffuseMapLabel) + { + return mDiffuseMapLabel < rhs.mDiffuseMapLabel; + } + + if (mDiffuseColor != rhs.mDiffuseColor) + { + return mDiffuseColor < rhs.mDiffuseColor; + } + + if (mBinding != rhs.mBinding) + { + return mBinding < rhs.mBinding; + } + + return mFullbright < rhs.mFullbright; +} + diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h index aaafc55258..ae602c09df 100755 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -31,6 +31,7 @@ #include "llvolume.h" #include "v4math.h" #include "m4math.h" +#include <queue> class daeElement; class domMesh; @@ -138,15 +139,16 @@ public: BOOL upload_skin, BOOL upload_joints, BOOL nowrite = FALSE, - BOOL as_slm = FALSE); + BOOL as_slm = FALSE, + int submodel_id = 0); static LLSD writeModelToStream( std::ostream& ostr, LLSD& mdl, BOOL nowrite = FALSE, BOOL as_slm = FALSE); + + void ClearFacesAndMaterials() { mVolumeFaces.clear(); mMaterialList.clear(); } - static LLModel* loadModelFromDomMesh(domMesh* mesh); - static std::string getElementLabel(daeElement* element); std::string getName() const; std::string getMetric() const {return mMetric;} EModelStatus getStatus() const {return mStatus;} @@ -169,20 +171,25 @@ public: void addFace(const LLVolumeFace& face); + void sortVolumeFacesByMaterialName(); void normalizeVolumeFaces(); + void trimVolumeFacesToSize(U32 new_count = LL_SCULPT_MESH_MAX_FACES, LLVolume::face_list_t* remainder = NULL); void optimizeVolumeFaces(); void offsetMesh( const LLVector3& pivotPoint ); void getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out); LLVector3 getTransformedCenter(const LLMatrix4& mat); - + //reorder face list based on mMaterialList in this and reference so //order matches that of reference (material ordering touchup) bool matchMaterialOrder(LLModel* ref, int& refFaceCnt, int& modelFaceCnt ); bool isMaterialListSubset( LLModel* ref ); bool needToAddFaces( LLModel* ref, int& refFaceCnt, int& modelFaceCnt ); - - std::vector<std::string> mMaterialList; + typedef std::vector<std::string> material_list; + + material_list mMaterialList; + + material_list& getMaterialList() { return mMaterialList; } //data used for skin weights class JointWeight @@ -275,9 +282,115 @@ public: Decomposition mPhysics; EModelStatus mStatus ; + + int mSubmodelID; +}; + +typedef std::vector<LLPointer<LLModel> > model_list; +typedef std::queue<LLPointer<LLModel> > model_queue; + +class LLModelMaterialBase +{ +public: + std::string mDiffuseMapFilename; + std::string mDiffuseMapLabel; + std::string mBinding; + LLColor4 mDiffuseColor; + bool mFullbright; + + LLModelMaterialBase() + : mFullbright(false) + { + mDiffuseColor.set(1,1,1,1); + } +}; + +class LLImportMaterial : public LLModelMaterialBase +{ +public: + friend class LLMeshUploadThread; + friend class LLModelPreview; + + bool operator<(const LLImportMaterial ¶ms) const; + + LLImportMaterial() : LLModelMaterialBase() + { + mDiffuseColor.set(1,1,1,1); + } + + LLImportMaterial(LLSD& data); + virtual ~LLImportMaterial(); + + LLSD asLLSD(); + + const LLUUID& getDiffuseMap() const { return mDiffuseMapID; } + void setDiffuseMap(const LLUUID& texId) { mDiffuseMapID = texId; } + protected: - void addVolumeFacesFromDomMesh(domMesh* mesh); - virtual BOOL createVolumeFacesFromDomMesh(domMesh *mesh); + + LLUUID mDiffuseMapID; + void* mOpaqueData; // allow refs to viewer/platform-specific structs for each material + // currently only stores an LLPointer< LLViewerFetchedTexture > > to + // maintain refs to textures associated with each material for free + // ref counting. }; +typedef std::map<std::string, LLImportMaterial> material_map; + +class LLModelInstanceBase +{ +public: + LLPointer<LLModel> mModel; + LLPointer<LLModel> mLOD[5]; + LLUUID mMeshID; + + LLMatrix4 mTransform; + material_map mMaterial; + + LLModelInstanceBase(LLModel* model, LLMatrix4& transform, material_map& materials) + : mModel(model), mTransform(transform), mMaterial(materials) + { + } + + LLModelInstanceBase() + : mModel(NULL) + { + } +}; + +typedef std::vector<LLModelInstanceBase> model_instance_list; + +class LLModelInstance : public LLModelInstanceBase +{ +public: + std::string mLabel; + LLUUID mMeshID; + S32 mLocalMeshID; + + LLModelInstance(LLModel* model, const std::string& label, LLMatrix4& transform, material_map& materials) + : LLModelInstanceBase(model, transform, materials), mLabel(label) + { + mLocalMeshID = -1; + } + + LLModelInstance(LLSD& data); + + LLSD asLLSD(); +}; + +#define LL_DEGENERACY_TOLERANCE 1e-7f + +inline F32 dot3fpu(const LLVector4a& a, const LLVector4a& b) +{ + volatile F32 p0 = a[0] * b[0]; + volatile F32 p1 = a[1] * b[1]; + volatile F32 p2 = a[2] * b[2]; + return p0 + p1 + p2; +} + +bool ll_is_degenerate(const LLVector4a& a, const LLVector4a& b, const LLVector4a& c, F32 tolerance = LL_DEGENERACY_TOLERANCE); + +bool validate_face(const LLVolumeFace& face); +bool validate_model(const LLModel* mdl); + #endif //LL_LLMODEL_H diff --git a/indra/llprimitive/llmodelloader.cpp b/indra/llprimitive/llmodelloader.cpp new file mode 100644 index 0000000000..81d92d151b --- /dev/null +++ b/indra/llprimitive/llmodelloader.cpp @@ -0,0 +1,640 @@ +/** + * @file llmodelloader.cpp + * @brief LLModelLoader class implementation + * + * $LicenseInfo:firstyear=2004&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 "llmodelloader.h" +#include "llsdserialize.h" +#include "lljoint.h" +#include "llcallbacklist.h" + +#include "glh/glh_linear.h" +#include "llmatrix4a.h" +#include <boost/bind.hpp> + +std::list<LLModelLoader*> LLModelLoader::sActiveLoaderList; + +void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, BOOL& first_transform) +{ + LLVector4a box[] = + { + LLVector4a(-1, 1,-1), + LLVector4a(-1, 1, 1), + LLVector4a(-1,-1,-1), + LLVector4a(-1,-1, 1), + LLVector4a( 1, 1,-1), + LLVector4a( 1, 1, 1), + LLVector4a( 1,-1,-1), + LLVector4a( 1,-1, 1), + }; + + for (S32 j = 0; j < model->getNumVolumeFaces(); ++j) + { + const LLVolumeFace& face = model->getVolumeFace(j); + + LLVector4a center; + center.setAdd(face.mExtents[0], face.mExtents[1]); + center.mul(0.5f); + LLVector4a size; + size.setSub(face.mExtents[1],face.mExtents[0]); + size.mul(0.5f); + + for (U32 i = 0; i < 8; i++) + { + LLVector4a t; + t.setMul(size, box[i]); + t.add(center); + + LLVector4a v; + + mat.affineTransform(t, v); + + if (first_transform) + { + first_transform = FALSE; + min = max = v; + } + else + { + update_min_max(min, max, v); + } + } + } +} + +void stretch_extents(LLModel* model, LLMatrix4& mat, LLVector3& min, LLVector3& max, BOOL& first_transform) +{ + LLVector4a mina, maxa; + LLMatrix4a mata; + + mata.loadu(mat); + mina.load3(min.mV); + maxa.load3(max.mV); + + stretch_extents(model, mata, mina, maxa, first_transform); + + min.set(mina.getF32ptr()); + max.set(maxa.getF32ptr()); +} + +//----------------------------------------------------------------------------- +// LLModelLoader +//----------------------------------------------------------------------------- +LLModelLoader::LLModelLoader( + std::string filename, + S32 lod, + load_callback_t load_cb, + joint_lookup_func_t joint_lookup_func, + texture_load_func_t texture_load_func, + state_callback_t state_cb, + void* opaque_userdata, + JointTransformMap& jointMap, + JointSet& jointsFromNodes ) +: mJointList( jointMap ) +, mJointsFromNode( jointsFromNodes ) +, LLThread("Model Loader") +, mFilename(filename) +, mLod(lod) +, mFirstTransform(TRUE) +, mNumOfFetchingTextures(0) +, mLoadCallback(load_cb) +, mJointLookupFunc(joint_lookup_func) +, mTextureLoadFunc(texture_load_func) +, mStateCallback(state_cb) +, mOpaqueData(opaque_userdata) +, mNoNormalize(false) +, mNoOptimize(false) +, mCacheOnlyHitIfRigged(false) +{ + mJointMap["mPelvis"] = "mPelvis"; + mJointMap["mTorso"] = "mTorso"; + mJointMap["mChest"] = "mChest"; + mJointMap["mNeck"] = "mNeck"; + mJointMap["mHead"] = "mHead"; + mJointMap["mSkull"] = "mSkull"; + mJointMap["mEyeRight"] = "mEyeRight"; + mJointMap["mEyeLeft"] = "mEyeLeft"; + mJointMap["mCollarLeft"] = "mCollarLeft"; + mJointMap["mShoulderLeft"] = "mShoulderLeft"; + mJointMap["mElbowLeft"] = "mElbowLeft"; + mJointMap["mWristLeft"] = "mWristLeft"; + mJointMap["mCollarRight"] = "mCollarRight"; + mJointMap["mShoulderRight"] = "mShoulderRight"; + mJointMap["mElbowRight"] = "mElbowRight"; + mJointMap["mWristRight"] = "mWristRight"; + mJointMap["mHipRight"] = "mHipRight"; + mJointMap["mKneeRight"] = "mKneeRight"; + mJointMap["mAnkleRight"] = "mAnkleRight"; + mJointMap["mFootRight"] = "mFootRight"; + mJointMap["mToeRight"] = "mToeRight"; + mJointMap["mHipLeft"] = "mHipLeft"; + mJointMap["mKneeLeft"] = "mKneeLeft"; + mJointMap["mAnkleLeft"] = "mAnkleLeft"; + mJointMap["mFootLeft"] = "mFootLeft"; + mJointMap["mToeLeft"] = "mToeLeft"; + + mJointMap["avatar_mPelvis"] = "mPelvis"; + mJointMap["avatar_mTorso"] = "mTorso"; + mJointMap["avatar_mChest"] = "mChest"; + mJointMap["avatar_mNeck"] = "mNeck"; + mJointMap["avatar_mHead"] = "mHead"; + mJointMap["avatar_mSkull"] = "mSkull"; + mJointMap["avatar_mEyeRight"] = "mEyeRight"; + mJointMap["avatar_mEyeLeft"] = "mEyeLeft"; + mJointMap["avatar_mCollarLeft"] = "mCollarLeft"; + mJointMap["avatar_mShoulderLeft"] = "mShoulderLeft"; + mJointMap["avatar_mElbowLeft"] = "mElbowLeft"; + mJointMap["avatar_mWristLeft"] = "mWristLeft"; + mJointMap["avatar_mCollarRight"] = "mCollarRight"; + mJointMap["avatar_mShoulderRight"] = "mShoulderRight"; + mJointMap["avatar_mElbowRight"] = "mElbowRight"; + mJointMap["avatar_mWristRight"] = "mWristRight"; + mJointMap["avatar_mHipRight"] = "mHipRight"; + mJointMap["avatar_mKneeRight"] = "mKneeRight"; + mJointMap["avatar_mAnkleRight"] = "mAnkleRight"; + mJointMap["avatar_mFootRight"] = "mFootRight"; + mJointMap["avatar_mToeRight"] = "mToeRight"; + mJointMap["avatar_mHipLeft"] = "mHipLeft"; + mJointMap["avatar_mKneeLeft"] = "mKneeLeft"; + mJointMap["avatar_mAnkleLeft"] = "mAnkleLeft"; + mJointMap["avatar_mFootLeft"] = "mFootLeft"; + mJointMap["avatar_mToeLeft"] = "mToeLeft"; + + + mJointMap["hip"] = "mPelvis"; + mJointMap["abdomen"] = "mTorso"; + mJointMap["chest"] = "mChest"; + mJointMap["neck"] = "mNeck"; + mJointMap["head"] = "mHead"; + mJointMap["figureHair"] = "mSkull"; + mJointMap["lCollar"] = "mCollarLeft"; + mJointMap["lShldr"] = "mShoulderLeft"; + mJointMap["lForeArm"] = "mElbowLeft"; + mJointMap["lHand"] = "mWristLeft"; + mJointMap["rCollar"] = "mCollarRight"; + mJointMap["rShldr"] = "mShoulderRight"; + mJointMap["rForeArm"] = "mElbowRight"; + mJointMap["rHand"] = "mWristRight"; + mJointMap["rThigh"] = "mHipRight"; + mJointMap["rShin"] = "mKneeRight"; + mJointMap["rFoot"] = "mFootRight"; + mJointMap["lThigh"] = "mHipLeft"; + mJointMap["lShin"] = "mKneeLeft"; + mJointMap["lFoot"] = "mFootLeft"; + + //move into joint mapper class + //1. joints for joint offset verification + mMasterJointList.push_front("mPelvis"); + mMasterJointList.push_front("mTorso"); + mMasterJointList.push_front("mChest"); + mMasterJointList.push_front("mNeck"); + mMasterJointList.push_front("mHead"); + mMasterJointList.push_front("mCollarLeft"); + mMasterJointList.push_front("mShoulderLeft"); + mMasterJointList.push_front("mElbowLeft"); + mMasterJointList.push_front("mWristLeft"); + mMasterJointList.push_front("mCollarRight"); + mMasterJointList.push_front("mShoulderRight"); + mMasterJointList.push_front("mElbowRight"); + mMasterJointList.push_front("mWristRight"); + mMasterJointList.push_front("mHipRight"); + mMasterJointList.push_front("mKneeRight"); + mMasterJointList.push_front("mFootRight"); + mMasterJointList.push_front("mHipLeft"); + mMasterJointList.push_front("mKneeLeft"); + mMasterJointList.push_front("mFootLeft"); + + //2. legacy joint list - used to verify rigs that will not be using joint offsets + mMasterLegacyJointList.push_front("mPelvis"); + mMasterLegacyJointList.push_front("mTorso"); + mMasterLegacyJointList.push_front("mChest"); + mMasterLegacyJointList.push_front("mNeck"); + mMasterLegacyJointList.push_front("mHead"); + mMasterLegacyJointList.push_front("mHipRight"); + mMasterLegacyJointList.push_front("mKneeRight"); + mMasterLegacyJointList.push_front("mFootRight"); + mMasterLegacyJointList.push_front("mHipLeft"); + mMasterLegacyJointList.push_front("mKneeLeft"); + mMasterLegacyJointList.push_front("mFootLeft"); + + assert_main_thread(); + sActiveLoaderList.push_back(this) ; +} + +LLModelLoader::~LLModelLoader() +{ + assert_main_thread(); + sActiveLoaderList.remove(this); +} + +void LLModelLoader::run() +{ + doLoadModel(); + doOnIdleOneTime(boost::bind(&LLModelLoader::loadModelCallback,this)); +} + +bool LLModelLoader::doLoadModel() +{ + //first, look for a .slm file of the same name that was modified later + //than the .dae + + if (mTrySLM) + { + std::string filename = mFilename; + + std::string::size_type i = filename.rfind("."); + if (i != std::string::npos) + { + filename.replace(i, filename.size()-1, ".slm"); + llstat slm_status; + if (LLFile::stat(filename, &slm_status) == 0) + { //slm file exists + llstat dae_status; + if (LLFile::stat(mFilename, &dae_status) != 0 || + dae_status.st_mtime < slm_status.st_mtime) + { + if (loadFromSLM(filename)) + { //slm successfully loaded, if this fails, fall through and + //try loading from dae + + mLod = -1; //successfully loading from an slm implicitly sets all + //LoDs + return true; + } + } + } + } + } + + return OpenFile(mFilename); +} + +void LLModelLoader::setLoadState(U32 state) +{ + mStateCallback(state, mOpaqueData); +} + +bool LLModelLoader::loadFromSLM(const std::string& filename) +{ + //only need to populate mScene with data from slm + llstat stat; + + if (LLFile::stat(filename, &stat)) + { //file does not exist + return false; + } + + S32 file_size = (S32) stat.st_size; + + llifstream ifstream(filename.c_str(), std::ifstream::in | std::ifstream::binary); + LLSD data; + LLSDSerialize::fromBinary(data, ifstream, file_size); + ifstream.close(); + + //build model list for each LoD + model_list model[LLModel::NUM_LODS]; + + if (data["version"].asInteger() != SLM_SUPPORTED_VERSION) + { //unsupported version + return false; + } + + LLSD& mesh = data["mesh"]; + + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + + for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod) + { + for (U32 i = 0; i < mesh.size(); ++i) + { + std::stringstream str(mesh[i].asString()); + LLPointer<LLModel> loaded_model = new LLModel(volume_params, (F32) lod); + if (loaded_model->loadModel(str)) + { + loaded_model->mLocalID = i; + model[lod].push_back(loaded_model); + + if (lod == LLModel::LOD_HIGH) + { + if (!loaded_model->mSkinInfo.mJointNames.empty()) + { + //check to see if rig is valid + critiqueRigForUploadApplicability( loaded_model->mSkinInfo.mJointNames ); + } + else if (mCacheOnlyHitIfRigged) + { + return false; + } + } + } + } + } + + if (model[LLModel::LOD_HIGH].empty()) + { //failed to load high lod + return false; + } + + //load instance list + model_instance_list instance_list; + + LLSD& instance = data["instance"]; + + for (U32 i = 0; i < instance.size(); ++i) + { + //deserialize instance list + instance_list.push_back(LLModelInstance(instance[i])); + + //match up model instance pointers + S32 idx = instance_list[i].mLocalMeshID; + std::string instance_label = instance_list[i].mLabel; + + for (U32 lod = 0; lod < LLModel::NUM_LODS; ++lod) + { + if (!model[lod].empty()) + { + if (idx >= model[lod].size()) + { + if (model[lod].size()) + { + instance_list[i].mLOD[lod] = model[lod][0]; + } + else + { + instance_list[i].mLOD[lod] = NULL; + } + continue; + } + + if (model[lod][idx] + && model[lod][idx]->mLabel.empty() + && !instance_label.empty()) + { + // restore model names + std::string name = instance_label; + switch (lod) + { + case LLModel::LOD_IMPOSTOR: name += "_LOD0"; break; + case LLModel::LOD_LOW: name += "_LOD1"; break; + case LLModel::LOD_MEDIUM: name += "_LOD2"; break; + case LLModel::LOD_PHYSICS: name += "_PHYS"; break; + case LLModel::LOD_HIGH: break; + } + model[lod][idx]->mLabel = name; + } + + instance_list[i].mLOD[lod] = model[lod][idx]; + } + } + + if (!instance_list[i].mModel) + instance_list[i].mModel = model[LLModel::LOD_HIGH][idx]; + } + + // Set name for UI to use + std::string name = data["name"]; + if (!name.empty()) + { + model[LLModel::LOD_HIGH][0]->mRequestedLabel = name; + } + + + //convert instance_list to mScene + mFirstTransform = TRUE; + for (U32 i = 0; i < instance_list.size(); ++i) + { + LLModelInstance& cur_instance = instance_list[i]; + mScene[cur_instance.mTransform].push_back(cur_instance); + stretch_extents(cur_instance.mModel, cur_instance.mTransform, mExtents[0], mExtents[1], mFirstTransform); + } + + setLoadState( DONE ); + + return true; +} + +//static +bool LLModelLoader::isAlive(LLModelLoader* loader) +{ + if(!loader) + { + return false ; + } + + std::list<LLModelLoader*>::iterator iter = sActiveLoaderList.begin() ; + for(; iter != sActiveLoaderList.end() && (*iter) != loader; ++iter) ; + + return *iter == loader ; +} + +void LLModelLoader::loadModelCallback() +{ + mLoadCallback(mScene,mModelList,mLod, mOpaqueData); + + while (!isStopped()) + { //wait until this thread is stopped before deleting self + apr_sleep(100); + } + + //double check if "this" is valid before deleting it, in case it is aborted during running. + if(!isAlive(this)) + { + return ; + } + + delete this; +} + +//----------------------------------------------------------------------------- +// critiqueRigForUploadApplicability() +//----------------------------------------------------------------------------- +void LLModelLoader::critiqueRigForUploadApplicability( const std::vector<std::string> &jointListFromAsset ) +{ + critiqueJointToNodeMappingFromScene(); + + //Determines the following use cases for a rig: + //1. It is suitable for upload with skin weights & joint positions, or + //2. It is suitable for upload as standard av with just skin weights + + bool isJointPositionUploadOK = isRigSuitableForJointPositionUpload( jointListFromAsset ); + bool isRigLegacyOK = isRigLegacy( jointListFromAsset ); + + //It's OK that both could end up being true, both default to false + if ( isJointPositionUploadOK ) + { + setRigValidForJointPositionUpload( true ); + } + + if ( isRigLegacyOK) + { + setLegacyRigValid( true ); + } + +} +//----------------------------------------------------------------------------- +// critiqueJointToNodeMappingFromScene() +//----------------------------------------------------------------------------- +void LLModelLoader::critiqueJointToNodeMappingFromScene( void ) +{ + //Do the actual nodes back the joint listing from the dae? + //if yes then this is a fully rigged asset, otherwise it's just a partial rig + + JointSet::iterator jointsFromNodeIt = mJointsFromNode.begin(); + JointSet::iterator jointsFromNodeEndIt = mJointsFromNode.end(); + bool result = true; + + if ( !mJointsFromNode.empty() ) + { + for ( ;jointsFromNodeIt!=jointsFromNodeEndIt;++jointsFromNodeIt ) + { + std::string name = *jointsFromNodeIt; + if ( mJointTransformMap.find( name ) != mJointTransformMap.end() ) + { + continue; + } + else + { + LL_INFOS() <<"critiqueJointToNodeMappingFromScene is missing a: " << name << LL_ENDL; + result = false; + } + } + } + else + { + result = false; + } + + //Determines the following use cases for a rig: + //1. Full av rig w/1-1 mapping from the scene and joint array + //2. Partial rig but w/o parity between the scene and joint array + if ( result ) + { + setRigWithSceneParity( true ); + } +} +//----------------------------------------------------------------------------- +// isRigLegacy() +//----------------------------------------------------------------------------- +bool LLModelLoader::isRigLegacy( const std::vector<std::string> &jointListFromAsset ) +{ + //No joints in asset + if ( jointListFromAsset.size() == 0 ) + { + return false; + } + + bool result = false; + + JointSet :: const_iterator masterJointIt = mMasterLegacyJointList.begin(); + JointSet :: const_iterator masterJointEndIt = mMasterLegacyJointList.end(); + + std::vector<std::string> :: const_iterator modelJointIt = jointListFromAsset.begin(); + std::vector<std::string> :: const_iterator modelJointItEnd = jointListFromAsset.end(); + + for ( ;masterJointIt!=masterJointEndIt;++masterJointIt ) + { + result = false; + modelJointIt = jointListFromAsset.begin(); + + for ( ;modelJointIt!=modelJointItEnd; ++modelJointIt ) + { + if ( *masterJointIt == *modelJointIt ) + { + result = true; + break; + } + } + if ( !result ) + { + LL_INFOS() <<" Asset did not contain the joint (if you're u/l a fully rigged asset w/joint positions - it is required)." << *masterJointIt<< LL_ENDL; + break; + } + } + return result; +} +//----------------------------------------------------------------------------- +// isRigSuitableForJointPositionUpload() +//----------------------------------------------------------------------------- +bool LLModelLoader::isRigSuitableForJointPositionUpload( const std::vector<std::string> &jointListFromAsset ) +{ + bool result = false; + + JointSet :: const_iterator masterJointIt = mMasterJointList.begin(); + JointSet :: const_iterator masterJointEndIt = mMasterJointList.end(); + + std::vector<std::string> :: const_iterator modelJointIt = jointListFromAsset.begin(); + std::vector<std::string> :: const_iterator modelJointItEnd = jointListFromAsset.end(); + + for ( ;masterJointIt!=masterJointEndIt;++masterJointIt ) + { + result = false; + modelJointIt = jointListFromAsset.begin(); + + for ( ;modelJointIt!=modelJointItEnd; ++modelJointIt ) + { + if ( *masterJointIt == *modelJointIt ) + { + result = true; + break; + } + } + if ( !result ) + { + LL_INFOS() <<" Asset did not contain the joint (if you're u/l a fully rigged asset w/joint positions - it is required)." << *masterJointIt<< LL_ENDL; + break; + } + } + return result; +} + + +//called in the main thread +void LLModelLoader::loadTextures() +{ + BOOL is_paused = isPaused() ; + pause() ; //pause the loader + + for(scene::iterator iter = mScene.begin(); iter != mScene.end(); ++iter) + { + for(U32 i = 0 ; i < iter->second.size(); i++) + { + for(std::map<std::string, LLImportMaterial>::iterator j = iter->second[i].mMaterial.begin(); + j != iter->second[i].mMaterial.end(); ++j) + { + LLImportMaterial& material = j->second; + + if(!material.mDiffuseMapFilename.empty()) + { + mNumOfFetchingTextures += mTextureLoadFunc(material, mOpaqueData); + } + } + } + } + + if(!is_paused) + { + unpause() ; + } +} diff --git a/indra/llprimitive/llmodelloader.h b/indra/llprimitive/llmodelloader.h new file mode 100644 index 0000000000..bb4d06dca3 --- /dev/null +++ b/indra/llprimitive/llmodelloader.h @@ -0,0 +1,212 @@ +/** + * @file llmodelloader.h + * @brief LLModelLoader class definition + * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLMODELLOADER_H +#define LL_LLMODELLOADER_H + +#include "llmodel.h" +#include "llthread.h" +#include <boost/function.hpp> +#include <list> + +class LLJoint; + +typedef std::map<std::string, LLMatrix4> JointTransformMap; +typedef std::map<std::string, LLMatrix4>:: iterator JointTransformMapIt; +typedef std::map<std::string, std::string> JointMap; +typedef std::deque<std::string> JointSet; + +const S32 SLM_SUPPORTED_VERSION = 3; +const S32 NUM_LOD = 4; + +class LLModelLoader : public LLThread +{ +public: + + typedef std::map<std::string, LLImportMaterial> material_map; + typedef std::vector<LLPointer<LLModel > > model_list; + typedef std::vector<LLModelInstance> model_instance_list; + typedef std::map<LLMatrix4, model_instance_list > scene; + + // Callback with loaded model data and loaded LoD + // + typedef boost::function<void (scene&,model_list&,S32,void*) > load_callback_t; + + // Function to provide joint lookup by name + // (within preview avi skeleton, for example) + // + typedef boost::function<LLJoint* (const std::string&,void*) > joint_lookup_func_t; + + // Func to load and associate material with all it's textures, + // returned value is the number of textures loaded + // intentionally non-const so func can modify material to + // store platform-specific data + // + typedef boost::function<U32 (LLImportMaterial&,void*) > texture_load_func_t; + + // Callback to inform client of state changes + // during loading process (errors will be reported + // as state changes here as well) + // + typedef boost::function<void (U32,void*) > state_callback_t; + + typedef enum + { + STARTING = 0, + READING_FILE, + CREATING_FACES, + GENERATING_VERTEX_BUFFERS, + GENERATING_LOD, + DONE, + ERROR_PARSING, //basically loading failed + ERROR_MATERIALS, + ERROR_PASSWORD_REQUIRED, + ERROR_NEED_MORE_MEMORY, + ERROR_INVALID_FILE, + ERROR_LOADER_SETUP, + ERROR_INVALID_PARAMETERS, + ERROR_OUT_OF_RANGE, + ERROR_FILE_VERSION_INVALID, + ERROR_MODEL // this error should always be last in this list, error code is passed as ERROR_MODEL+error_code + } eLoadState; + + U32 mState; + std::string mFilename; + + S32 mLod; + + LLMatrix4 mTransform; + BOOL mFirstTransform; + LLVector3 mExtents[2]; + + bool mTrySLM; + bool mCacheOnlyHitIfRigged; // ignore cached SLM if it does not contain rig info (and we want rig info) + + model_list mModelList; + scene mScene; + + typedef std::queue<LLPointer<LLModel> > model_queue; + + //queue of models that need a physics rep + model_queue mPhysicsQ; + + //map of avatar joints as named in COLLADA assets to internal joint names + JointMap mJointMap; + JointTransformMap& mJointList; + JointSet& mJointsFromNode; + + LLModelLoader( + std::string filename, + S32 lod, + LLModelLoader::load_callback_t load_cb, + LLModelLoader::joint_lookup_func_t joint_lookup_func, + LLModelLoader::texture_load_func_t texture_load_func, + LLModelLoader::state_callback_t state_cb, + void* opaque_userdata, + JointTransformMap& jointMap, + JointSet& jointsFromNodes); + virtual ~LLModelLoader() ; + + virtual void setNoNormalize() { mNoNormalize = true; } + virtual void setNoOptimize() { mNoOptimize = true; } + + virtual void run(); + + // Will try SLM or derived class OpenFile as appropriate + // + virtual bool doLoadModel(); + + // Derived classes need to provide their parsing of files here + // + virtual bool OpenFile(const std::string& filename) = 0; + + bool loadFromSLM(const std::string& filename); + + void loadModelCallback(); + void loadTextures() ; //called in the main thread. + void setLoadState(U32 state); + + + + S32 mNumOfFetchingTextures ; //updated in the main thread + bool areTexturesReady() { return !mNumOfFetchingTextures; } //called in the main thread. + + bool verifyCount( int expected, int result ); + + //Determines the viability of an asset to be used as an avatar rig (w or w/o joint upload caps) + void critiqueRigForUploadApplicability( const std::vector<std::string> &jointListFromAsset ); + void critiqueJointToNodeMappingFromScene( void ); + + //Determines if a rig is a legacy from the joint list + bool isRigLegacy( const std::vector<std::string> &jointListFromAsset ); + + //Determines if a rig is suitable for upload + bool isRigSuitableForJointPositionUpload( const std::vector<std::string> &jointListFromAsset ); + + void setRigWithSceneParity( bool state ) { mRigParityWithScene = state; } + const bool getRigWithSceneParity( void ) const { return mRigParityWithScene; } + + const bool isRigValidForJointPositionUpload( void ) const { return mRigValidJointUpload; } + void setRigValidForJointPositionUpload( bool rigValid ) { mRigValidJointUpload = rigValid; } + + const bool isLegacyRigValid( void ) const { return mLegacyRigValid; } + void setLegacyRigValid( bool rigValid ) { mLegacyRigValid = rigValid; } + + //----------------------------------------------------------------------------- + // isNodeAJoint() + //----------------------------------------------------------------------------- + bool isNodeAJoint(const char* name) + { + return mJointMap.find(name) != mJointMap.end(); + } + +protected: + + LLModelLoader::load_callback_t mLoadCallback; + LLModelLoader::joint_lookup_func_t mJointLookupFunc; + LLModelLoader::texture_load_func_t mTextureLoadFunc; + LLModelLoader::state_callback_t mStateCallback; + void* mOpaqueData; + + bool mRigParityWithScene; + bool mRigValidJointUpload; + bool mLegacyRigValid; + + bool mNoNormalize; + bool mNoOptimize; + + JointSet mMasterJointList; + JointSet mMasterLegacyJointList; + JointTransformMap mJointTransformMap; + + static std::list<LLModelLoader*> sActiveLoaderList; + static bool isAlive(LLModelLoader* loader) ; +}; +class LLMatrix4a; +void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, BOOL& first_transform); +void stretch_extents(LLModel* model, LLMatrix4& mat, LLVector3& min, LLVector3& max, BOOL& first_transform); + +#endif // LL_LLMODELLOADER_H diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index 29747cb09c..9eff74f1e8 100755 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -317,9 +317,9 @@ S32 LLPrimitive::setTEMaterialID(const U8 index, const LLMaterialID& pMaterialID return mTextureList.setMaterialID(index, pMaterialID); } -S32 LLPrimitive::setTEMaterialParams(const U8 index, const LLMaterialPtr pMaterialParams) +S32 LLPrimitive::setTEMaterialParams(const U8 index, const LLMaterialPtr pMaterialParams, bool isInitFromServer) { - return mTextureList.setMaterialParams(index, pMaterialParams); + return mTextureList.setMaterialParams(index, pMaterialParams, isInitFromServer); } LLMaterialPtr LLPrimitive::getTEMaterialParams(const U8 index) @@ -1869,9 +1869,12 @@ BOOL LLSculptParams::pack(LLDataPacker &dp) const BOOL LLSculptParams::unpack(LLDataPacker &dp) { - dp.unpackUUID(mSculptTexture, "texture"); - dp.unpackU8(mSculptType, "type"); - + U8 type; + LLUUID id; + dp.unpackUUID(id, "texture"); + dp.unpackU8(type, "type"); + + setSculptTexture(id, type); return TRUE; } @@ -1896,8 +1899,7 @@ bool LLSculptParams::operator==(const LLNetworkData& data) const void LLSculptParams::copy(const LLNetworkData& data) { const LLSculptParams *param = (LLSculptParams*)&data; - mSculptTexture = param->mSculptTexture; - mSculptType = param->mSculptType; + setSculptTexture(param->mSculptTexture, param->mSculptType); } @@ -1915,20 +1917,38 @@ LLSD LLSculptParams::asLLSD() const bool LLSculptParams::fromLLSD(LLSD& sd) { const char *w; - w = "texture"; + U8 type; + w = "type"; if (sd.has(w)) { - setSculptTexture( sd[w] ); - } else goto fail; - w = "type"; + type = sd[w].asInteger(); + } + else return false; + + w = "texture"; if (sd.has(w)) { - setSculptType( (U8)sd[w].asInteger() ); - } else goto fail; - + setSculptTexture(sd[w], type); + } + else return false; + return true; - fail: - return false; +} + +void LLSculptParams::setSculptTexture(const LLUUID& texture_id, U8 sculpt_type) +{ + U8 type = sculpt_type & LL_SCULPT_TYPE_MASK; + U8 flags = sculpt_type & LL_SCULPT_FLAG_MASK; + if (sculpt_type != (type | flags) || type > LL_SCULPT_TYPE_MAX) + { + mSculptTexture.set(SCULPT_DEFAULT_TEXTURE); + mSculptType = LL_SCULPT_TYPE_SPHERE; + } + else + { + mSculptTexture = texture_id; + mSculptType = sculpt_type; + } } //============================================================================ diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h index 1bf83e36b4..54870fbb10 100755 --- a/indra/llprimitive/llprimitive.h +++ b/indra/llprimitive/llprimitive.h @@ -259,9 +259,8 @@ public: operator LLSD() const { return asLLSD(); } bool fromLLSD(LLSD& sd); - void setSculptTexture(const LLUUID& id) { mSculptTexture = id; } + void setSculptTexture(const LLUUID& texture_id, U8 sculpt_type); LLUUID getSculptTexture() const { return mSculptTexture; } - void setSculptType(U8 type) { mSculptType = type; } U8 getSculptType() const { return mSculptType; } }; @@ -385,7 +384,7 @@ public: virtual S32 setTEMediaFlags(const U8 te, const U8 flags); virtual S32 setTEGlow(const U8 te, const F32 glow); virtual S32 setTEMaterialID(const U8 te, const LLMaterialID& pMaterialID); - virtual S32 setTEMaterialParams(const U8 index, const LLMaterialPtr pMaterialParams); + virtual S32 setTEMaterialParams(const U8 index, const LLMaterialPtr pMaterialParams, bool isInitFromServer); virtual BOOL setMaterial(const U8 material); // returns TRUE if material changed virtual void setTESelected(const U8 te, bool sel); diff --git a/indra/llprimitive/llprimtexturelist.cpp b/indra/llprimitive/llprimtexturelist.cpp index f4f08248b8..6aae2f97c6 100755 --- a/indra/llprimitive/llprimtexturelist.cpp +++ b/indra/llprimitive/llprimtexturelist.cpp @@ -368,11 +368,18 @@ S32 LLPrimTextureList::setMaterialID(const U8 index, const LLMaterialID& pMateri return TEM_CHANGE_NONE; } -S32 LLPrimTextureList::setMaterialParams(const U8 index, const LLMaterialPtr pMaterialParams) +S32 LLPrimTextureList::setMaterialParams(const U8 index, const LLMaterialPtr pMaterialParams, bool isInitFromServer) { if (index < mEntryList.size()) { - return mEntryList[index]->setMaterialParams(pMaterialParams); + if (!isInitFromServer && mEntryList[index]->isMatParamsInitFromServer()) + { + return TEM_CHANGE_NONE; + } + else + { + return mEntryList[index]->setMaterialParams(pMaterialParams); + } } return TEM_CHANGE_NONE; } diff --git a/indra/llprimitive/llprimtexturelist.h b/indra/llprimitive/llprimtexturelist.h index 49c636e40f..63e6372405 100755 --- a/indra/llprimitive/llprimtexturelist.h +++ b/indra/llprimitive/llprimtexturelist.h @@ -105,7 +105,7 @@ public: S32 setMediaFlags(const U8 index, const U8 media_flags); S32 setGlow(const U8 index, const F32 glow); S32 setMaterialID(const U8 index, const LLMaterialID& pMaterialID); - S32 setMaterialParams(const U8 index, const LLMaterialPtr pMaterialParams); + S32 setMaterialParams(const U8 index, const LLMaterialPtr pMaterialParams, bool isInitFromServer); LLMaterialPtr getMaterialParams(const U8 index); diff --git a/indra/llprimitive/lltextureentry.cpp b/indra/llprimitive/lltextureentry.cpp index 284dfc15f4..6020031099 100755 --- a/indra/llprimitive/lltextureentry.cpp +++ b/indra/llprimitive/lltextureentry.cpp @@ -63,6 +63,7 @@ LLTextureEntry::LLTextureEntry() : mMediaEntry(NULL) , mSelected(false) , mMaterialUpdatePending(false) + , mInitMatParamsFromServer(false) { init(LLUUID::null,1.f,1.f,0.f,0.f,0.f,DEFAULT_BUMP_CODE); } @@ -71,6 +72,7 @@ LLTextureEntry::LLTextureEntry(const LLUUID& tex_id) : mMediaEntry(NULL) , mSelected(false) , mMaterialUpdatePending(false) + , mInitMatParamsFromServer(false) { init(tex_id,1.f,1.f,0.f,0.f,0.f,DEFAULT_BUMP_CODE); } @@ -79,6 +81,7 @@ LLTextureEntry::LLTextureEntry(const LLTextureEntry &rhs) : mMediaEntry(NULL) , mSelected(false) , mMaterialUpdatePending(false) + , mInitMatParamsFromServer(false) { mID = rhs.mID; mScaleS = rhs.mScaleS; @@ -562,6 +565,7 @@ S32 LLTextureEntry::setMaterialParams(const LLMaterialPtr pMaterialParams) mMaterialUpdatePending = true; } mMaterial = pMaterialParams; + this->mInitMatParamsFromServer = TRUE; return TEM_CHANGE_TEXTURE; } diff --git a/indra/llprimitive/lltextureentry.h b/indra/llprimitive/lltextureentry.h index 19edcaa27d..81e2e8a5a2 100755 --- a/indra/llprimitive/lltextureentry.h +++ b/indra/llprimitive/lltextureentry.h @@ -89,6 +89,10 @@ public: bool operator==(const LLTextureEntry &rhs) const; bool operator!=(const LLTextureEntry &rhs) const; + + // Added to allow use with std::map + // + bool operator <(const LLTextureEntry &rhs) const; LLSD asLLSD() const; void asLLSD(LLSD& sd) const; @@ -156,6 +160,8 @@ public: const LLMaterialID& getMaterialID() const { return mMaterialID; }; const LLMaterialPtr getMaterialParams() const { return mMaterial; }; + bool isMatParamsInitFromServer() const { return mInitMatParamsFromServer; }; + // *NOTE: it is possible for hasMedia() to return true, but getMediaData() to return NULL. // CONVERSELY, it is also possible for hasMedia() to return false, but getMediaData() // to NOT return NULL. @@ -213,6 +219,8 @@ protected: bool mMaterialUpdatePending; LLMaterialID mMaterialID; LLMaterialPtr mMaterial; + bool mInitMatParamsFromServer; // Flag to identification when material paramas initialized from + // Note the media data is not sent via the same message structure as the rest of the TE LLMediaEntry* mMediaEntry; // The media data for the face diff --git a/indra/llui/lldockablefloater.cpp b/indra/llui/lldockablefloater.cpp index 3396213f1c..c937d190c6 100755 --- a/indra/llui/lldockablefloater.cpp +++ b/indra/llui/lldockablefloater.cpp @@ -153,7 +153,7 @@ void LLDockableFloater::setVisible(BOOL visible) mDockControl.get()->repositionDockable(); } - if (visible) + if (visible && !isMinimized()) { LLFloater::setFrontmost(getAutoFocus()); } diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index 81a0204bc5..3def0386e1 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -129,6 +129,7 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) mSelectPending(FALSE), mLabelStyle( LLFontGL::NORMAL ), mHasVisibleChildren(FALSE), + mIsFolderComplete(true), mLocalIndentation(p.folder_indentation), mIndentation(0), mItemHeight(p.item_height), @@ -674,7 +675,7 @@ void LLFolderViewItem::drawOpenFolderArrow(const Params& default_params, const L // const S32 TOP_PAD = default_params.item_top_pad; - if (hasVisibleChildren()) + if (hasVisibleChildren() || !isFolderComplete()) { LLUIImage* arrow_image = default_params.folder_arrow_image; gl_draw_scaled_rotated_image( @@ -934,6 +935,8 @@ LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ): mLastArrangeGeneration( -1 ), mLastCalculatedWidth(0) { + // folder might have children that are not loaded yet. Mark it as incomplete until chance to check it. + mIsFolderComplete = false; } void LLFolderViewFolder::updateLabelRotation() @@ -1016,6 +1019,12 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height ) mHasVisibleChildren = found; } + if (!mIsFolderComplete) + { + mIsFolderComplete = getFolderViewModel()->isFolderComplete(this); + } + + // calculate height as a single item (without any children), and reshapes rectangle to match LLFolderViewItem::arrange( width, height ); @@ -1463,31 +1472,37 @@ void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection) LLFolderView* root = getRoot(); - for (std::vector<LLFolderViewItem*>::iterator it = items_to_select_forward.begin(), end_it = items_to_select_forward.end(); + BOOL selection_reverse = new_selection->isSelected(); //indication that some elements are being deselected + + // array always go from 'will be selected' to ' will be unselected', iterate + // in opposite direction to simplify identification of 'point of origin' in + // case it is in the list we are working with + for (std::vector<LLFolderViewItem*>::reverse_iterator it = items_to_select_forward.rbegin(), end_it = items_to_select_forward.rend(); it != end_it; ++it) { LLFolderViewItem* item = *it; - if (item->isSelected()) + BOOL selected = item->isSelected(); + if (!selection_reverse && selected) { - root->removeFromSelectionList(item); + // it is our 'point of origin' where we shift/expand from + // don't deselect it + selection_reverse = TRUE; } else { - item->selectItem(); + root->changeSelection(item, !selected); } - root->addToSelectionList(item); } - if (new_selection->isSelected()) + if (selection_reverse) { - root->removeFromSelectionList(new_selection); + // at some point we reversed selection, first element should be deselected + root->changeSelection(last_selected_item_from_cur, FALSE); } - else - { - new_selection->selectItem(); - } - root->addToSelectionList(new_selection); + + // element we expand to should always be selected + root->changeSelection(new_selection, TRUE); } @@ -1677,7 +1692,9 @@ void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType r mIsOpen = openitem; if(!was_open && openitem) { - getViewModelItem()->openItem(); + getViewModelItem()->openItem(); + // openItem() will request content, it won't be incomplete + mIsFolderComplete = true; } else if(was_open && !openitem) { diff --git a/indra/llui/llfolderviewitem.h b/indra/llui/llfolderviewitem.h index ed4496cfaa..0322c8836d 100755 --- a/indra/llui/llfolderviewitem.h +++ b/indra/llui/llfolderviewitem.h @@ -115,6 +115,7 @@ protected: F32 mControlLabelRotation; LLFolderView* mRoot; bool mHasVisibleChildren, + mIsFolderComplete, // indicates that some children were not loaded/added yet mIsCurSelection, mDragAndDropTarget, mIsMouseOverTitle, @@ -212,6 +213,9 @@ public: BOOL hasVisibleChildren() { return mHasVisibleChildren; } + // true if object can't have children + BOOL isFolderComplete() { return mIsFolderComplete; } + // Call through to the viewed object and return true if it can be // removed. Returns true if it's removed. //virtual BOOL removeRecursively(BOOL single_item); diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h index d60e36183b..a395af537a 100755 --- a/indra/llui/llfolderviewmodel.h +++ b/indra/llui/llfolderviewmodel.h @@ -122,6 +122,7 @@ public: virtual void filter() = 0; virtual bool contentsReady() = 0; + virtual bool isFolderComplete(class LLFolderViewFolder*) = 0; virtual void setFolderView(LLFolderView* folder_view) = 0; virtual LLFolderViewFilter& getFilter() = 0; virtual const LLFolderViewFilter& getFilter() const = 0; @@ -444,6 +445,7 @@ public: // By default, we assume the content is available. If a network fetch mechanism is implemented for the model, // this method needs to be overloaded and return the relevant fetch status. virtual bool contentsReady() { return true; } + virtual bool isFolderComplete(LLFolderViewFolder* folder) { return true; } struct ViewModelCompare { diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 7cdbcb0621..848367f8a8 100755 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -225,7 +225,6 @@ BOOL LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask) BOOL LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask) { - setHover(TRUE); getWindow()->setCursor(UI_CURSOR_ARROW); return TRUE; } @@ -236,6 +235,18 @@ BOOL LLMenuItemGL::handleRightMouseDown(S32 x, S32 y, MASK mask) return LLUICtrl::handleRightMouseDown(x,y,mask); } +void LLMenuItemGL::onMouseEnter(S32 x, S32 y, MASK mask) +{ + setHover(TRUE); + LLUICtrl::onMouseEnter(x,y,mask); +} + +void LLMenuItemGL::onMouseLeave(S32 x, S32 y, MASK mask) +{ + setHover(FALSE); + LLUICtrl::onMouseLeave(x,y,mask); +} + //virtual BOOL LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK mask) { @@ -533,9 +544,6 @@ void LLMenuItemGL::draw( void ) gl_line_2d(x_begin, (MENU_ITEM_PADDING / 2) + 1, x_end, (MENU_ITEM_PADDING / 2) + 1); } } - - // clear got hover every frame - setHover(FALSE); } BOOL LLMenuItemGL::setLabelArg( const std::string& key, const LLStringExplicit& text ) @@ -1043,7 +1051,7 @@ void LLMenuItemBranchGL::onCommit( void ) // keyboard navigation automatically propagates highlight to sub-menu // to facilitate fast menu control via jump keys - if (LLMenuGL::getKeyboardMode() && getBranch()&& !getBranch()->getHighlightedItem()) + if (LLMenuGL::getKeyboardMode() && getBranch() && !getBranch()->getHighlightedItem()) { getBranch()->highlightNextItem(NULL); } @@ -1456,7 +1464,16 @@ BOOL LLMenuItemBranchDownGL::handleMouseDown( S32 x, S32 y, MASK mask ) { // switch to mouse control mode LLMenuGL::setKeyboardMode(FALSE); - onCommit(); + + if (getVisible() && isOpen()) + { + LLMenuGL::sMenuContainer->hideMenus(); + } + else + { + onCommit(); + } + make_ui_sound("UISndClick"); return TRUE; } @@ -1588,10 +1605,6 @@ void LLMenuItemBranchDownGL::draw( void ) gl_line_2d(x_begin, LABEL_BOTTOM_PAD_PIXELS, x_end, LABEL_BOTTOM_PAD_PIXELS); } } - - // reset every frame so that we only show highlight - // when we get hover events on that frame - setHover(FALSE); } @@ -3381,6 +3394,11 @@ BOOL LLMenuBarGL::handleMouseDown(S32 x, S32 y, MASK mask) return LLMenuGL::handleMouseDown(x, y, mask); } +BOOL LLMenuBarGL::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + return LLMenuGL::handleMouseDown(x, y, mask); +} + void LLMenuBarGL::draw() { LLMenuItemGL* itemp = getHighlightedItem(); @@ -3625,8 +3643,24 @@ BOOL LLMenuHolderGL::handleMouseDown( S32 x, S32 y, MASK mask ) BOOL handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL; if (!handled) { - // clicked off of menu, hide them all - hideMenus(); + LLMenuGL* visible_menu = (LLMenuGL*)getVisibleMenu(); + LLMenuItemGL* parent_menu = visible_menu ? visible_menu->getParentMenuItem() : NULL; + if (parent_menu && parent_menu->getVisible()) + { + // don't hide menu if parent was hit + LLRect parent_rect; + parent_menu->localRectToOtherView(parent_menu->getLocalRect(), &parent_rect, this); + if (!parent_rect.pointInRect(x, y)) + { + // clicked off of menu and parent, hide them all + hideMenus(); + } + } + else + { + // no visible parent, clicked off of menu, hide them all + hideMenus(); + } } return handled; } diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index ae9b169691..628dedb906 100755 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -158,6 +158,10 @@ public: virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask ); virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask ); virtual BOOL handleScrollWheel( S32 x, S32 y, S32 clicks ); + + virtual void onMouseEnter(S32 x, S32 y, MASK mask); + virtual void onMouseLeave(S32 x, S32 y, MASK mask); + virtual void draw( void ); BOOL getHover() const { return mGotHover; } @@ -753,6 +757,7 @@ public: /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); /*virtual*/ BOOL handleJumpKey(KEY key); /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); /*virtual*/ void draw(); /*virtual*/ BOOL jumpKeysActive(); diff --git a/indra/llui/llsearcheditor.cpp b/indra/llui/llsearcheditor.cpp index ea96fc573d..1fdd05a11c 100755 --- a/indra/llui/llsearcheditor.cpp +++ b/indra/llui/llsearcheditor.cpp @@ -29,6 +29,7 @@ #include "linden_common.h" #include "llsearcheditor.h" +#include "llkeyboard.h" LLSearchEditor::LLSearchEditor(const LLSearchEditor::Params& p) : LLUICtrl(p), @@ -166,4 +167,16 @@ void LLSearchEditor::handleKeystroke() { mKeystrokeCallback(this, getValue()); } + + KEY key = gKeyboard->currentKey(); + if (key == KEY_LEFT || + key == KEY_RIGHT) + { + return; + } + + if (mTextChangedCallback) + { + mTextChangedCallback(this, getValue()); + } } diff --git a/indra/llui/llsearcheditor.h b/indra/llui/llsearcheditor.h index c2d7916938..3b12868225 100755 --- a/indra/llui/llsearcheditor.h +++ b/indra/llui/llsearcheditor.h @@ -82,12 +82,14 @@ public: virtual void setFocus( BOOL b ); void setKeystrokeCallback( commit_callback_t cb ) { mKeystrokeCallback = cb; } + void setTextChangedCallback( commit_callback_t cb ) { mTextChangedCallback = cb; } protected: void onClearButtonClick(const LLSD& data); virtual void handleKeystroke(); commit_callback_t mKeystrokeCallback; + commit_callback_t mTextChangedCallback; LLLineEditor* mSearchEditor; LLButton* mSearchButton; LLButton* mClearButton; diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 602a703450..bf660849c4 100755 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -37,6 +37,7 @@ #include "lltextparser.h" #include "lltextutil.h" #include "lltooltip.h" +#include "lltrans.h" #include "lluictrl.h" #include "llurlaction.h" #include "llurlregistry.h" @@ -2060,27 +2061,33 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para // add icon before url if need LLTextUtil::processUrlMatch(&match, this, isContentTrusted() || match.isTrusted()); + if ((isContentTrusted() || match.isTrusted()) && !match.getIcon().empty() ) + { + setLastSegmentToolTip(LLTrans::getString("TooltipSLIcon")); + } // output the styled Url appendAndHighlightTextImpl(match.getLabel(), part, link_params, match.underlineOnHoverOnly()); + bool tooltip_required = !match.getTooltip().empty(); + + // set the tooltip for the Url label + if (tooltip_required) + { + setLastSegmentToolTip(match.getTooltip()); + } - // show query part of url with gray color only for LLUrlEntryHTTP and LLUrlEntryHTTPNoProtocol url entries + // show query part of url with gray color only for LLUrlEntryHTTP url entries std::string label = match.getQuery(); if (label.size()) { link_params.color = LLColor4::grey; link_params.readonly_color = LLColor4::grey; appendAndHighlightTextImpl(label, part, link_params, match.underlineOnHoverOnly()); - } - - // set the tooltip for the Url label - if (! match.getTooltip().empty()) - { - segment_set_t::iterator it = getSegIterContaining(getLength()-1); - if (it != mSegments.end()) + + // set the tooltip for the query part of url + if (tooltip_required) { - LLTextSegmentPtr segment = *it; - segment->setToolTip(match.getTooltip()); + setLastSegmentToolTip(match.getTooltip()); } } @@ -2107,6 +2114,16 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para } } +void LLTextBase::setLastSegmentToolTip(const std::string &tooltip) +{ + segment_set_t::iterator it = getSegIterContaining(getLength()-1); + if (it != mSegments.end()) + { + LLTextSegmentPtr segment = *it; + segment->setToolTip(tooltip); + } +} + static LLTrace::BlockTimerStatHandle FTM_APPEND_TEXT("Append Text"); void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params) @@ -3557,6 +3574,22 @@ S32 LLImageTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin return 0; } +BOOL LLImageTextSegment::handleToolTip(S32 x, S32 y, MASK mask) +{ + if (!mTooltip.empty()) + { + LLToolTipMgr::instance().show(mTooltip); + return TRUE; + } + + return FALSE; +} + +void LLImageTextSegment::setToolTip(const std::string& tooltip) +{ + mTooltip = tooltip; +} + F32 LLImageTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect) { if ( (start >= 0) && (end <= mEnd - mStart)) diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index dfc10923f3..87809aa8fb 100755 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -241,9 +241,15 @@ public: S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const; F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect); + /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); + /*virtual*/ void setToolTip(const std::string& tooltip); + private: class LLTextBase& mEditor; LLStyleConstSP mStyle; + +protected: + std::string mTooltip; }; typedef LLPointer<LLTextSegment> LLTextSegmentPtr; @@ -392,6 +398,8 @@ public: const std::string& getLabel() { return mLabel.getString(); } const LLWString& getWlabel() { return mLabel.getWString();} + void setLastSegmentToolTip(const std::string &tooltip); + /** * If label is set, draws text label (which is LLLabelTextSegment) * that is visible when no user text provided diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 6db0d88998..7f6cc22e90 100755 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -203,10 +203,10 @@ std::string LLUrlEntryBase::urlToGreyQuery(const std::string &url) const { LLUriParser up(unescapeUrl(url)); - std::string query; + std::string label; up.extractParts(); - up.glueSecond(query); - + up.glueFirst(label); + std::string query = url.substr(label.size()); return query; } @@ -229,7 +229,7 @@ static std::string getStringAfterToken(const std::string str, const std::string LLUrlEntryHTTP::LLUrlEntryHTTP() : LLUrlEntryBase() { - mPattern = boost::regex("https?://([-\\w\\.]+)+(:\\d+)?(:\\w+)?(@\\d+)?(@\\w+)?/?\\S*", + mPattern = boost::regex("https?://([-\\w\\.]+)+(:\\d+)?(:\\w+)?(@\\d+)?(@\\w+)?\\.[a-z](:\\d+)?(:\\w+)?(@\\d+)?(@\\w+)?/?\\S*", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_http.xml"; mTooltip = LLTrans::getString("TooltipHttpUrl"); @@ -287,46 +287,6 @@ std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) const return getUrlFromWikiLink(string); } -// -// LLUrlEntryHTTPNoProtocol Describes generic Urls like www.google.com -// -LLUrlEntryHTTPNoProtocol::LLUrlEntryHTTPNoProtocol() - : LLUrlEntryBase() -{ - mPattern = boost::regex("(" - "\\bwww\\.\\S+\\.\\S+" // i.e. www.FOO.BAR - "|" // or - "(?<!@)\\b[^[:space:]:@/>]+\\.(?:com|net|edu|org)([/:][^[:space:]<]*)?\\b" // i.e. FOO.net - ")", - boost::regex::perl|boost::regex::icase); - mMenuName = "menu_url_http.xml"; - mTooltip = LLTrans::getString("TooltipHttpUrl"); -} - -std::string LLUrlEntryHTTPNoProtocol::getLabel(const std::string &url, const LLUrlLabelCallback &cb) -{ - return urlToLabelWithGreyQuery(url); -} - -std::string LLUrlEntryHTTPNoProtocol::getQuery(const std::string &url) const -{ - return urlToGreyQuery(url); -} - -std::string LLUrlEntryHTTPNoProtocol::getUrl(const std::string &string) const -{ - if (string.find("://") == std::string::npos) - { - return "http://" + escapeUrl(string); - } - return escapeUrl(string); -} - -std::string LLUrlEntryHTTPNoProtocol::getTooltip(const std::string &url) const -{ - return unescapeUrl(url); -} - LLUrlEntryInvalidSLURL::LLUrlEntryInvalidSLURL() : LLUrlEntryBase() { @@ -489,7 +449,7 @@ std::string LLUrlEntrySLURL::getLocation(const std::string &url) const // LLUrlEntrySecondlifeURL::LLUrlEntrySecondlifeURL() { - mPattern = boost::regex("(https?://)?([-\\w\\.]*\\.)?(secondlife|lindenlab)\\.com(:\\d{1,5})?\\/\\S*", + mPattern = boost::regex("https?://([-\\w\\.]*\\.)?(secondlife|lindenlab)\\.com(:\\d{1,5})?\\/\\S*", boost::regex::perl|boost::regex::icase); mIcon = "Hand"; @@ -527,7 +487,7 @@ std::string LLUrlEntrySecondlifeURL::getTooltip(const std::string &url) const // LLUrlEntrySimpleSecondlifeURL::LLUrlEntrySimpleSecondlifeURL() { - mPattern = boost::regex("(https?://)?([-\\w\\.]*\\.)?(secondlife|lindenlab)\\.com(?!\\S)", + mPattern = boost::regex("https?://([-\\w\\.]*\\.)?(secondlife|lindenlab)\\.com(?!\\S)", boost::regex::perl|boost::regex::icase); mIcon = "Hand"; @@ -1401,9 +1361,43 @@ std::string LLUrlEntryIcon::getIcon(const std::string &url) return mIcon; } +// +// LLUrlEntryEmail Describes a generic mailto: Urls +// +LLUrlEntryEmail::LLUrlEntryEmail() + : LLUrlEntryBase() +{ + mPattern = boost::regex("(mailto:)?[\\w\\.\\-]+@[\\w\\.\\-]+\\.[a-z]{2,6}", + boost::regex::perl | boost::regex::icase); + mMenuName = "menu_url_email.xml"; + mTooltip = LLTrans::getString("TooltipEmail"); +} + +std::string LLUrlEntryEmail::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + int pos = url.find("mailto:"); + + if (pos == std::string::npos) + { + return escapeUrl(url); + } + + std::string ret = escapeUrl(url.substr(pos + 7, url.length() - pos + 8)); + return ret; +} + +std::string LLUrlEntryEmail::getUrl(const std::string &string) const +{ + if (string.find("mailto:") == std::string::npos) + { + return "mailto:" + escapeUrl(string); + } + return escapeUrl(string); +} + LLUrlEntryExperienceProfile::LLUrlEntryExperienceProfile() { - mPattern = boost::regex(APP_HEADER_REGEX "/experience/[\\da-f-]+/\\w+\\S*", + mPattern = boost::regex(APP_HEADER_REGEX "/experience/[\\da-f-]+/profile", boost::regex::perl|boost::regex::icase); mIcon = "Generic_Experience"; mMenuName = "menu_url_experience.xml"; diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index dd1f257a3d..413c20a657 100755 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -157,19 +157,6 @@ public: /*virtual*/ std::string getUrl(const std::string &string) const; }; -/// -/// LLUrlEntryHTTPNoProtocol Describes generic Urls like www.google.com -/// -class LLUrlEntryHTTPNoProtocol : public LLUrlEntryBase -{ -public: - LLUrlEntryHTTPNoProtocol(); - /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); - /*virtual*/ std::string getQuery(const std::string &url) const; - /*virtual*/ std::string getUrl(const std::string &string) const; - /*virtual*/ std::string getTooltip(const std::string &url) const; -}; - class LLUrlEntryInvalidSLURL : public LLUrlEntryBase { public: @@ -506,5 +493,16 @@ public: /*virtual*/ std::string getIcon(const std::string &url); }; +/// +/// LLUrlEntryEmail Describes a generic mailto: Urls +/// +class LLUrlEntryEmail : public LLUrlEntryBase +{ +public: + LLUrlEntryEmail(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getUrl(const std::string &string) const; +}; + #endif diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp index 2085505947..69eefa736c 100755 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -76,9 +76,7 @@ LLUrlRegistry::LLUrlRegistry() registerUrl(new LLUrlEntrySL()); mUrlEntrySLLabel = new LLUrlEntrySLLabel(); registerUrl(mUrlEntrySLLabel); - // most common pattern is a URL without any protocol, - // e.g., "secondlife.com" - registerUrl(new LLUrlEntryHTTPNoProtocol()); + registerUrl(new LLUrlEntryEmail()); } LLUrlRegistry::~LLUrlRegistry() @@ -155,11 +153,9 @@ static bool stringHasUrl(const std::string &text) return (text.find("://") != std::string::npos || text.find("www.") != std::string::npos || text.find(".com") != std::string::npos || - text.find(".net") != std::string::npos || - text.find(".edu") != std::string::npos || - text.find(".org") != std::string::npos || text.find("<nolink>") != std::string::npos || - text.find("<icon") != std::string::npos); + text.find("<icon") != std::string::npos || + text.find("@") != std::string::npos); } bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LLUrlLabelCallback &cb, bool is_content_trusted) @@ -218,9 +214,40 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL // did we find a match? if so, return its details in the match object if (match_entry) { + + // Skip if link is an email with an empty username (starting with @). See MAINT-5371. + if (match_start > 0 && text.substr(match_start - 1, 1) == "@") + return false; + // fill in the LLUrlMatch object and return it std::string url = text.substr(match_start, match_end - match_start + 1); + LLUrlEntryBase *stripped_entry = NULL; + if(LLStringUtil::containsNonprintable(url)) + { + LLStringUtil::stripNonprintable(url); + + std::vector<LLUrlEntryBase *>::iterator iter; + for (iter = mUrlEntry.begin(); iter != mUrlEntry.end(); ++iter) + { + LLUrlEntryBase *url_entry = *iter; + U32 start = 0, end = 0; + if (matchRegex(url.c_str(), url_entry->getPattern(), start, end)) + { + if (mLLUrlEntryInvalidSLURL == *iter) + { + if(url_entry && url_entry->isSLURLvalid(url)) + { + continue; + } + } + stripped_entry = url_entry; + break; + } + } + } + + if (match_entry == mUrlEntryTrusted) { LLUriParser up(url); @@ -228,10 +255,12 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL url = up.normalizedUri(); } + std::string url_label = stripped_entry? stripped_entry->getLabel(url, cb) : match_entry->getLabel(url, cb); + std::string url_query = stripped_entry? stripped_entry->getQuery(url) : match_entry->getQuery(url); match.setValues(match_start, match_end, match_entry->getUrl(url), - match_entry->getLabel(url, cb), - match_entry->getQuery(url), + url_label, + url_query, match_entry->getTooltip(url), match_entry->getIcon(url), match_entry->getStyle(), diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index 15f2354552..96e94c0f80 100755 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -653,79 +653,6 @@ namespace tut void object::test<11>() { // - // test LLUrlEntryHTTPNoProtocol - general URLs without a protocol - // - LLUrlEntryHTTPNoProtocol url; - - testRegex("naked .com URL", url, - "see google.com", - "http://google.com"); - - testRegex("naked .org URL", url, - "see en.wikipedia.org for details", - "http://en.wikipedia.org"); - - testRegex("naked .net URL", url, - "example.net", - "http://example.net"); - - testRegex("naked .edu URL (2 instances)", url, - "MIT web site is at web.mit.edu and also www.mit.edu", - "http://web.mit.edu"); - - testRegex("don't match e-mail addresses", url, - "test@lindenlab.com", - ""); - - testRegex(".com URL with path", url, - "see secondlife.com/status for grid status", - "http://secondlife.com/status"); - - testRegex(".com URL with port", url, - "secondlife.com:80", - "http://secondlife.com:80"); - - testRegex(".com URL with port and path", url, - "see secondlife.com:80/status", - "http://secondlife.com:80/status"); - - testRegex("www.*.com URL with port and path", url, - "see www.secondlife.com:80/status", - "http://www.secondlife.com:80/status"); - - testRegex("invalid .com URL [1]", url, - "..com", - ""); - - testRegex("invalid .com URL [2]", url, - "you.come", - ""); - - testRegex("invalid .com URL [3]", url, - "recommended", - ""); - - testRegex("invalid .edu URL", url, - "hi there scheduled maitenance has begun", - ""); - - testRegex("invalid .net URL", url, - "foo.netty", - ""); - - testRegex("XML tags around URL [1]", url, - "<foo>secondlife.com</foo>", - "http://secondlife.com"); - - testRegex("XML tags around URL [2]", url, - "<foo>secondlife.com/status?bar=1</foo>", - "http://secondlife.com/status?bar=1"); - } - - template<> template<> - void object::test<12>() - { - // // test LLUrlEntryNoLink - turn off hyperlinking // LLUrlEntryNoLink url; @@ -752,7 +679,7 @@ namespace tut } template<> template<> - void object::test<13>() + void object::test<12>() { // // test LLUrlEntryRegion - secondlife:///app/region/<location> URLs @@ -860,4 +787,101 @@ namespace tut "secondlife:///app/region/Product%20Engine", "Product Engine"); } + + template<> template<> + void object::test<13>() + { + // + // test LLUrlEntryemail - general emails + // + LLUrlEntryEmail url; + + // Regex tests. + testRegex("match e-mail addresses", url, + "test@lindenlab.com", + "mailto:test@lindenlab.com"); + + testRegex("match e-mail addresses with mailto: prefix", url, + "mailto:test@lindenlab.com", + "mailto:test@lindenlab.com"); + + testRegex("match e-mail addresses with different domains", url, + "test@foo.org.us", + "mailto:test@foo.org.us"); + + testRegex("match e-mail addresses with different domains", url, + "test@foo.bar", + "mailto:test@foo.bar"); + + testRegex("don't match incorrect e-mail addresses", url, + "test @foo.com", + ""); + + testRegex("don't match incorrect e-mail addresses", url, + "test@ foo.com", + ""); + } + + template<> template<> + void object::test<14>() + { + // + // test LLUrlEntrySimpleSecondlifeURL - http://*.secondlife.com/* and http://*lindenlab.com/* urls + // + LLUrlEntrySecondlifeURL url; + + testRegex("match urls with protocol", url, + "this url should match http://lindenlab.com/products/second-life", + "http://lindenlab.com/products/second-life"); + + testRegex("match urls with protocol", url, + "search something https://marketplace.secondlife.com/products/search on marketplace and test the https", + "https://marketplace.secondlife.com/products/search"); + + testRegex("match urls with port", url, + "let's specify some port http://secondlife.com:888/status", + "http://secondlife.com:888/status"); + + testRegex("don't match urls w/o protocol", url, + "looks like an url something www.marketplace.secondlife.com/products but no https prefix", + ""); + + testRegex("but with a protocol www is fine", url, + "so let's add a protocol http://www.marketplace.secondlife.com:8888/products", + "http://www.marketplace.secondlife.com:8888/products"); + + testRegex("don't match urls w/o protocol", url, + "and even no www something secondlife.com/status", + ""); + } + + template<> template<> + void object::test<15>() + { + // + // test LLUrlEntrySimpleSecondlifeURL - http://*.secondlife.com and http://*lindenlab.com urls + // + + LLUrlEntrySimpleSecondlifeURL url; + + testRegex("match urls with a protocol", url, + "this url should match http://lindenlab.com", + "http://lindenlab.com"); + + testRegex("match urls with a protocol", url, + "search something https://marketplace.secondlife.com on marketplace and test the https", + "https://marketplace.secondlife.com"); + + testRegex("don't match urls w/o protocol", url, + "looks like an url something www.marketplace.secondlife.com but no https prefix", + ""); + + testRegex("but with a protocol www is fine", url, + "so let's add a protocol http://www.marketplace.secondlife.com", + "http://www.marketplace.secondlife.com"); + + testRegex("don't match urls w/o protocol", url, + "and even no www something lindenlab.com", + ""); + } } diff --git a/indra/llwindow/llopenglview-objc.mm b/indra/llwindow/llopenglview-objc.mm index 1c9160a42d..deb8cb90d8 100644 --- a/indra/llwindow/llopenglview-objc.mm +++ b/indra/llwindow/llopenglview-objc.mm @@ -239,7 +239,11 @@ attributedStringInfo getSegments(NSAttributedString *str) { [glContext setValues:(const GLint*)1 forParameter:NSOpenGLCPSwapInterval]; } else { - [glContext setValues:(const GLint*)0 forParameter:NSOpenGLCPSwapInterval]; + // supress this error after move to Xcode 7: + // error: null passed to a callee that requires a non-null argument [-Werror,-Wnonnull] + // Tried using ObjC 'nonnull' keyword as per SO article but didn't build + GLint swapInterval=0; + [glContext setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval]; } mOldResize = false; diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp index 5720660034..1b24250618 100755 --- a/indra/llwindow/llwindow.cpp +++ b/indra/llwindow/llwindow.cpp @@ -49,8 +49,8 @@ LLSplashScreen *gSplashScreenp = NULL; BOOL gDebugClicks = FALSE; BOOL gDebugWindowProc = FALSE; -const S32 gURLProtocolWhitelistCount = 4; -const std::string gURLProtocolWhitelist[] = { "secondlife:", "http:", "https:", "data:" }; +const S32 gURLProtocolWhitelistCount = 5; +const std::string gURLProtocolWhitelist[] = { "secondlife:", "http:", "https:", "data:", "mailto:" }; // CP: added a handler list - this is what's used to open the protocol and is based on registry entry // only meaningful difference currently is that file: protocols are opened using http: diff --git a/indra/llwindow/llwindowmacosx-objc.h b/indra/llwindow/llwindowmacosx-objc.h index f02052ca6a..e6e8f27f53 100755 --- a/indra/llwindow/llwindowmacosx-objc.h +++ b/indra/llwindow/llwindowmacosx-objc.h @@ -53,6 +53,7 @@ bool runMainLoop(); void initMainLoop(); void cleanupViewer(); void handleUrl(const char* url); +void dispatchUrl(std::string url); /* Defined in llwindowmacosx-objc.mm: */ int createNSApp(int argc, const char **argv); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 536c7740bb..001cdf2de4 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -140,7 +140,6 @@ set(viewer_SOURCE_FILES llbreadcrumbview.cpp llbrowsernotification.cpp llbuycurrencyhtml.cpp - llcallbacklist.cpp llcallingcard.cpp llcapabilitylistener.cpp llcaphttpsender.cpp @@ -275,6 +274,7 @@ set(viewer_SOURCE_FILES llfloatermodeluploadbase.cpp llfloaternamedesc.cpp llfloaternotificationsconsole.cpp + llfloaternotificationstabbed.cpp llfloaterobjectweights.cpp llfloateropenobject.cpp llfloateroutbox.cpp @@ -409,6 +409,8 @@ set(viewer_SOURCE_FILES llnotificationgrouphandler.cpp llnotificationhandlerutil.cpp llnotificationhinthandler.cpp + llnotificationlistitem.cpp + llnotificationlistview.cpp llnotificationmanager.cpp llnotificationofferhandler.cpp llnotificationscripthandler.cpp @@ -756,7 +758,6 @@ set(viewer_HEADER_FILES llbox.h llbreadcrumbview.h llbuycurrencyhtml.h - llcallbacklist.h llcallingcard.h llcapabilitylistener.h llcapabilityprovider.h @@ -895,6 +896,7 @@ set(viewer_HEADER_FILES llfloatermodeluploadbase.h llfloaternamedesc.h llfloaternotificationsconsole.h + llfloaternotificationstabbed.h llfloaterobjectweights.h llfloateropenobject.h llfloateroutbox.h @@ -1022,6 +1024,8 @@ set(viewer_HEADER_FILES llnavigationbar.h llnetmap.h llnotificationhandler.h + llnotificationlistitem.h + llnotificationlistview.h llnotificationmanager.h llnotificationstorage.h lloutfitslist.h diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index ff313b8c21..4351a7e3a3 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -3.8.4 +3.8.7 diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 0371a819dd..5f378c64e8 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2,6 +2,39 @@ <llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> <map> + <key>ImporterDebug</key> + <map> + <key>Comment</key> + <string>Enable debug output to more precisely identify sources of import errors. Warning: the output can slow down import on many machines.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>ImporterLegacyMatching</key> + <map> + <key>Comment</key> + <string>Enable index based model matching.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>ImporterModelLimit</key> + <map> + <key>Comment</key> + <string>Limits amount of importer generated models for dae files</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>768</integer> + </map> <key>IMShowTime</key> <map> <key>Comment</key> @@ -14178,7 +14211,7 @@ <key>MaxFPS</key> <map> <key>Comment</key> - <string>Yield some time to the local host if we reach a threshold framerate.</string> + <string>OBSOLETE UNUSED setting.</string> <key>Persist</key> <integer>1</integer> <key>Type</key> diff --git a/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl b/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl index 6cd38d8ef5..3060307b21 100755 --- a/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl +++ b/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl @@ -37,8 +37,7 @@ mat4 getObjectSkinnedTransform() index = min(index, vec4(51.0)); index = max(index, vec4( 0.0)); - float scale = 1.0/(w.x+w.y+w.z+w.w); - w *= scale; + w *= 1.0/(w.x+w.y+w.z+w.w); int i1 = int(index.x); int i2 = int(index.y); diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl index 595c11fae2..58fb01d200 100755 --- a/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl @@ -29,6 +29,7 @@ out vec4 frag_data[3]; #define frag_data gl_FragData #endif +uniform float minimum_alpha; uniform sampler2D diffuseMap; uniform sampler2D bumpMap; @@ -47,16 +48,23 @@ vec2 encode_normal(vec3 n) void main() { - vec3 col = vertex_color.rgb * texture2D(diffuseMap, vary_texcoord0.xy).rgb; - vec3 norm = texture2D(bumpMap, vary_texcoord0.xy).rgb * 2.0 - 1.0; + vec4 col = texture2D(diffuseMap, vary_texcoord0.xy); + + if(col.a < minimum_alpha) + { + discard; + } + col *= vertex_color; + + vec3 norm = texture2D(bumpMap, vary_texcoord0.xy).rgb * 2.0 - 1.0; - vec3 tnorm = vec3(dot(norm,vary_mat0), + vec3 tnorm = vec3(dot(norm,vary_mat0), dot(norm,vary_mat1), dot(norm,vary_mat2)); - frag_data[0] = vec4(col, 0.0); - frag_data[1] = vertex_color.aaaa; // spec - //frag_data[1] = vec4(vec3(vertex_color.a), vertex_color.a+(1.0-vertex_color.a)*vertex_color.a); // spec - from former class3 - maybe better, but not so well tested - vec3 nvn = normalize(tnorm); - frag_data[2] = vec4(encode_normal(nvn.xyz), vertex_color.a, 0.0); + frag_data[0] = vec4(col.rgb, 0.0); + frag_data[1] = vertex_color.aaaa; // spec + //frag_data[1] = vec4(vec3(vertex_color.a), vertex_color.a+(1.0-vertex_color.a)*vertex_color.a); // spec - from former class3 - maybe better, but not so well tested + vec3 nvn = normalize(tnorm); + frag_data[2] = vec4(encode_normal(nvn), vertex_color.a, 0.0); } diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi index 95cdf90e99..b8677fd9e4 100755 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -165,7 +165,9 @@ lbl_configure_default_lang: StrCpy $LANGUAGE $0
# For silent installs, no language prompt, use default
- IfSilent lbl_return
+ IfSilent 0 +3
+ StrCpy $SKIP_AUTORUN "true"
+ Goto lbl_return
StrCmp $SKIP_DIALOGS "true" lbl_return
lbl_build_menu:
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 092d868bb9..297bd9a05b 100755 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -2843,7 +2843,7 @@ BOOL LLAgent::isInGroup(const LLUUID& group_id, BOOL ignore_god_mode /* FALSE */ // This implementation should mirror LLAgentInfo::hasPowerInGroup BOOL LLAgent::hasPowerInGroup(const LLUUID& group_id, U64 power) const { - if (isGodlike()) + if (isGodlikeWithoutAdminMenuFakery()) return true; // GP_NO_POWERS can also mean no power is enough to grant an ability. diff --git a/indra/newview/llagentdata.cpp b/indra/newview/llagentdata.cpp index 5f6a082d75..d2c644a06f 100755 --- a/indra/newview/llagentdata.cpp +++ b/indra/newview/llagentdata.cpp @@ -31,3 +31,4 @@ LLUUID gAgentID; LLUUID gAgentSessionID; +std::string gAgentUsername; diff --git a/indra/newview/llagentdata.h b/indra/newview/llagentdata.h index 83d6a53d5e..efdd97f6c4 100755 --- a/indra/newview/llagentdata.h +++ b/indra/newview/llagentdata.h @@ -30,5 +30,6 @@ extern LLUUID gAgentID; extern LLUUID gAgentSessionID; +extern std::string gAgentUsername; #endif diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 35593dd4ff..142a3250c8 100755 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -1869,6 +1869,10 @@ bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids) { ++n_clothes; } + else if (item->getType() == LLAssetType::AT_BODYPART) + { + return isAgentAvatarValid(); + } else { LL_WARNS() << "Unexpected wearable type" << LL_ENDL; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 9b9b591cd1..fbf2a04bcc 100755 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2784,10 +2784,12 @@ bool LLAppViewer::initConfiguration() // gWindowTitle = LLTrans::getString("APP_NAME"); #if LL_DEBUG - gWindowTitle += std::string(" [DEBUG] ") + gArgs; -#else - gWindowTitle += std::string(" ") + gArgs; + gWindowTitle += std::string(" [DEBUG]") #endif + if (!gArgs.empty()) + { + gWindowTitle += std::string(" ") + gArgs; + } LLStringUtil::truncate(gWindowTitle, 255); //RN: if we received a URL, hand it off to the existing instance. @@ -4948,6 +4950,7 @@ void LLAppViewer::idle() gIdleCallbacks.callFunctions(); gInventory.idleNotifyObservers(); + LLAvatarTracker::instance().idleNotifyObservers(); } // Metrics logging (LLViewerAssetStats, etc.) diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp index b85f3249bf..ca219fda59 100755 --- a/indra/newview/llappviewermacosx.cpp +++ b/indra/newview/llappviewermacosx.cpp @@ -62,9 +62,10 @@ namespace // They are not used immediately by the app. int gArgC; char** gArgV; - LLAppViewerMacOSX* gViewerAppPtr; + LLAppViewerMacOSX* gViewerAppPtr = NULL; void (*gOldTerminateHandler)() = NULL; + std::string gHandleSLURL; } static void exceptionTerminateHandler() @@ -103,7 +104,11 @@ bool initViewer() { LL_WARNS() << "Application init failed." << LL_ENDL; } - + else if (!gHandleSLURL.empty()) + { + dispatchUrl(gHandleSLURL); + gHandleSLURL = ""; + } return ok; } @@ -390,22 +395,31 @@ bool LLAppViewerMacOSX::getMasterSystemAudioMute() void handleUrl(const char* url_utf8) { - if (url_utf8) + if (url_utf8 && gViewerAppPtr) { - std::string url = url_utf8; - // Safari 3.2 silently mangles secondlife:///app/ URLs into - // secondlife:/app/ (only one leading slash). - // Fix them up to meet the URL specification. JC - const std::string prefix = "secondlife:/app/"; - std::string test_prefix = url.substr(0, prefix.length()); - LLStringUtil::toLower(test_prefix); - if (test_prefix == prefix) - { - url.replace(0, prefix.length(), "secondlife:///app/"); - } - - LLMediaCtrl* web = NULL; - const bool trusted_browser = false; - LLURLDispatcher::dispatch(url, "", web, trusted_browser); + gHandleSLURL = ""; + dispatchUrl(url_utf8); } + else if (url_utf8) + { + gHandleSLURL = url_utf8; + } +} + +void dispatchUrl(std::string url) +{ + // Safari 3.2 silently mangles secondlife:///app/ URLs into + // secondlife:/app/ (only one leading slash). + // Fix them up to meet the URL specification. JC + const std::string prefix = "secondlife:/app/"; + std::string test_prefix = url.substr(0, prefix.length()); + LLStringUtil::toLower(test_prefix); + if (test_prefix == prefix) + { + url.replace(0, prefix.length(), "secondlife:///app/"); + } + + LLMediaCtrl* web = NULL; + const bool trusted_browser = false; + LLURLDispatcher::dispatch(url, "", web, trusted_browser); } diff --git a/indra/newview/llavatariconctrl.cpp b/indra/newview/llavatariconctrl.cpp index 281e591b48..25a5df9781 100755 --- a/indra/newview/llavatariconctrl.cpp +++ b/indra/newview/llavatariconctrl.cpp @@ -311,6 +311,7 @@ bool LLAvatarIconCtrl::updateFromCache() else { LLIconCtrl::setValue(mDefaultIconName); + return false; } return true; diff --git a/indra/newview/llcallbacklist.cpp b/indra/newview/llcallbacklist.cpp index 59ecbdd0ea..59ecbdd0ea 100755..100644 --- a/indra/newview/llcallbacklist.cpp +++ b/indra/newview/llcallbacklist.cpp diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp index b6c5496c17..f79d1aa609 100755 --- a/indra/newview/llcallingcard.cpp +++ b/indra/newview/llcallingcard.cpp @@ -97,7 +97,8 @@ static void on_avatar_name_cache_notify(const LLUUID& agent_id, LLAvatarTracker::LLAvatarTracker() : mTrackingData(NULL), mTrackedAgentValid(false), - mModifyMask(0x0) + mModifyMask(0x0), + mIsNotifyObservers(FALSE) { } @@ -272,7 +273,7 @@ S32 LLAvatarTracker::addBuddyList(const LLAvatarTracker::buddy_map_t& buds) << "]" << LL_ENDL; } } - notifyObservers(); + // do not notify observers here - list can be large so let it be done on idle. return new_buddy_count; } @@ -473,8 +474,25 @@ void LLAvatarTracker::removeObserver(LLFriendObserver* observer) mObservers.end()); } +void LLAvatarTracker::idleNotifyObservers() +{ + if (mModifyMask == LLFriendObserver::NONE && mChangedBuddyIDs.size() == 0) + { + return; + } + notifyObservers(); +} + void LLAvatarTracker::notifyObservers() { + if (mIsNotifyObservers) + { + // Don't allow multiple calls. + // new masks and ids will be processed later from idle. + return; + } + mIsNotifyObservers = TRUE; + observer_list_t observers(mObservers); observer_list_t::iterator it = observers.begin(); observer_list_t::iterator end = observers.end(); @@ -490,6 +508,7 @@ void LLAvatarTracker::notifyObservers() mModifyMask = LLFriendObserver::NONE; mChangedBuddyIDs.clear(); + mIsNotifyObservers = FALSE; } void LLAvatarTracker::addParticularFriendObserver(const LLUUID& buddy_id, LLFriendObserver* observer) @@ -531,7 +550,7 @@ void LLAvatarTracker::notifyParticularFriendObservers(const LLUUID& buddy_id) // store flag for change // and id of object change applies to void LLAvatarTracker::addChangedMask(U32 mask, const LLUUID& referent) -{ +{ mModifyMask |= mask; if (referent.notNull()) { diff --git a/indra/newview/llcallingcard.h b/indra/newview/llcallingcard.h index 6e5fc01cd8..1f819a42fd 100755 --- a/indra/newview/llcallingcard.h +++ b/indra/newview/llcallingcard.h @@ -143,6 +143,7 @@ public: // observers left behind. void addObserver(LLFriendObserver* observer); void removeObserver(LLFriendObserver* observer); + void idleNotifyObservers(); void notifyObservers(); // Observers interested in updates of a particular avatar. @@ -209,6 +210,8 @@ private: LLAvatarTracker(const LLAvatarTracker&); bool operator==(const LLAvatarTracker&); + BOOL mIsNotifyObservers; + public: // don't you dare create or delete this object LLAvatarTracker(); diff --git a/indra/newview/llchannelmanager.cpp b/indra/newview/llchannelmanager.cpp index b0537a83f1..d6240838b6 100755 --- a/indra/newview/llchannelmanager.cpp +++ b/indra/newview/llchannelmanager.cpp @@ -35,6 +35,7 @@ #include "llviewerwindow.h" #include "llrootview.h" #include "llsyswellwindow.h" +#include "llfloaternotificationstabbed.h" #include "llfloaterreg.h" #include <algorithm> @@ -131,7 +132,7 @@ void LLChannelManager::onLoginCompleted() S32 channel_right_bound = gViewerWindow->getWorldViewRectScaled().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); mStartUpChannel->init(channel_right_bound - channel_width, channel_right_bound); - mStartUpChannel->setMouseDownCallback(boost::bind(&LLNotificationWellWindow::onStartUpToastClick, LLNotificationWellWindow::getInstance(), _2, _3, _4)); + mStartUpChannel->setMouseDownCallback(boost::bind(&LLFloaterNotificationsTabbed::onStartUpToastClick, LLFloaterNotificationsTabbed::getInstance(), _2, _3, _4)); mStartUpChannel->setCommitCallback(boost::bind(&LLChannelManager::onStartUpToastClose, this)); mStartUpChannel->createStartUpToast(away_notifications, gSavedSettings.getS32("StartUpToastLifeTime")); diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index b81b95462e..dc0835eb1c 100755 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -376,6 +376,10 @@ public: || mSourceType == CHAT_SOURCE_SYSTEM) { mFrom = LLTrans::getString("SECOND_LIFE"); + if(!chat.mFromName.empty() && (mFrom != chat.mFromName)) + { + mFrom += " (" + chat.mFromName + ")"; + } user_name->setValue(mFrom); updateMinUserNameWidth(); } diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index 46b7679915..dedb06c945 100755 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -35,6 +35,7 @@ #include "llscriptfloater.h" #include "llsingleton.h" #include "llsyswellwindow.h" +#include "llfloaternotificationstabbed.h" static LLDefaultChildRegistry::Register<LLChicletPanel> t1("chiclet_panel"); static LLDefaultChildRegistry::Register<LLNotificationChiclet> t2("chiclet_notification"); @@ -165,7 +166,7 @@ LLNotificationChiclet::LLNotificationChiclet(const Params& p) mNotificationChannel.reset(new ChicletNotificationChannel(this)); // ensure that notification well window exists, to synchronously // handle toast add/delete events. - LLNotificationWellWindow::getInstance()->setSysWellChiclet(this); + LLFloaterNotificationsTabbed::getInstance()->setSysWellChiclet(this); } void LLNotificationChiclet::onMenuItemClicked(const LLSD& user_data) @@ -173,7 +174,7 @@ void LLNotificationChiclet::onMenuItemClicked(const LLSD& user_data) std::string action = user_data.asString(); if("close all" == action) { - LLNotificationWellWindow::getInstance()->closeAll(); + LLFloaterNotificationsTabbed::getInstance()->closeAll(); LLIMWellWindow::getInstance()->closeAll(); } } @@ -224,7 +225,8 @@ bool LLNotificationChiclet::ChicletNotificationChannel::filterNotification( LLNo bool displayNotification; if ( (notification->getName() == "ScriptDialog") // special case for scripts // if there is no toast window for the notification, filter it - || (!LLNotificationWellWindow::getInstance()->findItemByID(notification->getID())) + //|| (!LLNotificationWellWindow::getInstance()->findItemByID(notification->getID())) + || (!LLFloaterNotificationsTabbed::getInstance()->findItemByID(notification->getID(), notification->getName())) ) { displayNotification = false; diff --git a/indra/newview/llchicletbar.cpp b/indra/newview/llchicletbar.cpp index d8b04f7004..254e3f61a8 100755 --- a/indra/newview/llchicletbar.cpp +++ b/indra/newview/llchicletbar.cpp @@ -31,6 +31,7 @@ #include "lllayoutstack.h" #include "llpaneltopinfobar.h" #include "llsyswellwindow.h" +#include "llfloaternotificationstabbed.h" namespace { @@ -49,7 +50,7 @@ BOOL LLChicletBar::postBuild() mToolbarStack = getChild<LLLayoutStack>("toolbar_stack"); mChicletPanel = getChild<LLChicletPanel>("chiclet_list"); - showWellButton("notification_well", !LLNotificationWellWindow::getInstance()->isWindowEmpty()); + showWellButton("notification_well", !LLFloaterNotificationsTabbed::getInstance()->isWindowEmpty()); LLPanelTopInfoBar::instance().setResizeCallback(boost::bind(&LLChicletBar::fitWithTopInfoBar, this)); LLPanelTopInfoBar::instance().setVisibleCallback(boost::bind(&LLChicletBar::fitWithTopInfoBar, this)); diff --git a/indra/newview/llcommandhandler.cpp b/indra/newview/llcommandhandler.cpp index 19dba3f917..5ea7efc045 100755 --- a/indra/newview/llcommandhandler.cpp +++ b/indra/newview/llcommandhandler.cpp @@ -30,6 +30,7 @@ #include "llcommandhandler.h" #include "llnotificationsutil.h" #include "llcommanddispatcherlistener.h" +#include "llstartup.h" #include "stringize.h" // system includes @@ -116,7 +117,11 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd, LL_WARNS_ONCE("SLURL") << "Blocked SLURL command from untrusted browser" << LL_ENDL; if (! slurl_blocked) { - LLNotificationsUtil::add("BlockedSLURL"); + if (LLStartUp::getStartupState() >= STATE_BROWSER_INIT) + { + // Note: commands can arrive before we initialize everything we need for Notification. + LLNotificationsUtil::add("BlockedSLURL"); + } slurl_blocked = true; } return true; @@ -138,7 +143,10 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd, LL_WARNS_ONCE("SLURL") << "Throttled SLURL command from untrusted browser" << LL_ENDL; if (! slurl_throttled) { - LLNotificationsUtil::add("ThrottledSLURL"); + if (LLStartUp::getStartupState() >= STATE_BROWSER_INIT) + { + LLNotificationsUtil::add("ThrottledSLURL"); + } slurl_throttled = true; } return true; diff --git a/indra/newview/llconversationlog.h b/indra/newview/llconversationlog.h index b38d472156..62f08144b9 100755 --- a/indra/newview/llconversationlog.h +++ b/indra/newview/llconversationlog.h @@ -153,6 +153,7 @@ public: * file name is conversation.log */ std::string getFileName(); + LLConversation* findConversation(const LLIMModel::LLIMSession* session); private: @@ -184,7 +185,7 @@ private: void updateConversationName(const LLIMModel::LLIMSession* session, const std::string& name); void updateOfflineIMs(const LLIMModel::LLIMSession* session, BOOL new_messages); - LLConversation* findConversation(const LLIMModel::LLIMSession* session); + typedef std::vector<LLConversation> conversations_vec_t; std::vector<LLConversation> mConversations; diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp index 6e32ce60ec..328a638f2f 100755 --- a/indra/newview/llconversationmodel.cpp +++ b/indra/newview/llconversationmodel.cpp @@ -136,7 +136,24 @@ void LLConversationItem::buildParticipantMenuOptions(menuentry_vec_t& items, U32 items.push_back(std::string("im")); items.push_back(std::string("offer_teleport")); items.push_back(std::string("request_teleport")); - items.push_back(std::string("voice_call")); + + if (getType() != CONV_SESSION_1_ON_1) + { + items.push_back(std::string("voice_call")); + } + else + { + LLVoiceChannel* voice_channel = LLIMModel::getInstance() ? LLIMModel::getInstance()->getVoiceChannel(this->getUUID()) : NULL; + if(voice_channel != LLVoiceChannel::getCurrentVoiceChannel()) + { + items.push_back(std::string("voice_call")); + } + else + { + items.push_back(std::string("disconnect_from_voice")); + } + } + items.push_back(std::string("chat_history")); items.push_back(std::string("separator_chat_history")); items.push_back(std::string("add_friend")); diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index 821d58a9b2..8bbc529244 100755 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -1553,7 +1553,8 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace* (drawable && drawable->isState(LLDrawable::REBUILD_ALL))) { if (drawable && drawable->isState(LLDrawable::REBUILD_ALL)) - { //rebuild EVERY face in the drawable, not just this one, to avoid missing drawable wide rebuild issues + { + //rebuild EVERY face in the drawable, not just this one, to avoid missing drawable wide rebuild issues for (S32 i = 0; i < drawable->getNumFaces(); ++i) { LLFace* facep = drawable->getFace(i); @@ -1570,13 +1571,15 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace* buffer = face->getVertexBuffer(); } else - { //just rebuild this face + { + //just rebuild this face getRiggedGeometry(face, buffer, data_mask, skin, volume, vol_face); } } if (sShaderLevel <= 0 && face->mLastSkinTime < avatar->getLastSkinTime()) - { //perform software vertex skinning for this face + { + //perform software vertex skinning for this face LLStrider<LLVector3> position; LLStrider<LLVector3> normal; @@ -1600,6 +1603,10 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace* for (U32 j = 0; j < count; ++j) { LLJoint* joint = avatar->getJoint(skin->mJointNames[j]); + if (!joint) + { + joint = avatar->getJoint("mPelvis"); + } if (joint) { mat[j] = skin->mInvBindMatrix[j]; @@ -1624,12 +1631,13 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace* { F32 w = weight[j][k]; - idx[k] = llclamp((S32) floorf(w), 0, JOINT_COUNT-1); + idx[k] = llclamp((S32) floorf(w), (S32)0, (S32)JOINT_COUNT-1); wght[k] = w - floorf(w); scale += wght[k]; } - + // This is enforced in unpackVolumeFaces() + llassert(scale>0.f); wght *= 1.f/scale; for (U32 k = 0; k < 4; k++) @@ -1729,6 +1737,10 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow) for (U32 i = 0; i < count; ++i) { LLJoint* joint = avatar->getJoint(skin->mJointNames[i]); + if (!joint) + { + joint = avatar->getJoint("mPelvis"); + } if (joint) { mat[i] = skin->mInvBindMatrix[i]; diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp index 33f7bc305c..7b9fd5c6c6 100755 --- a/indra/newview/lldrawpoolbump.cpp +++ b/indra/newview/lldrawpoolbump.cpp @@ -864,6 +864,7 @@ void LLDrawPoolBump::renderDeferred(S32 pass) { LLDrawInfo& params = **i; + gDeferredBumpProgram.setMinimumAlpha(params.mAlphaMaskCutoff); LLDrawPoolBump::bindBumpMap(params, bump_channel); pushBatch(params, mask, TRUE); } diff --git a/indra/newview/llexperienceassociationresponder.cpp b/indra/newview/llexperienceassociationresponder.cpp index b50c81eedc..7f2363aadc 100644 --- a/indra/newview/llexperienceassociationresponder.cpp +++ b/indra/newview/llexperienceassociationresponder.cpp @@ -29,6 +29,7 @@ #include "llviewerprecompiledheaders.h" #include "llexperienceassociationresponder.h" #include "llexperiencecache.h" +#include "llviewerobjectlist.h" #include "llviewerregion.h" #include "llagent.h" @@ -47,7 +48,13 @@ void ExperienceAssociationResponder::fetchAssociatedExperience( const LLUUID& ob void ExperienceAssociationResponder::fetchAssociatedExperience(LLSD& request, callback_t callback) { - LLViewerRegion* region = gAgent.getRegion(); + LLViewerObject* object = gObjectList.findObject(request["object-id"]); + if (!object) + { + LL_WARNS() << "Failed to find object with ID " << request["object-id"] << " in fetchAssociatedExperience" << LL_ENDL; + return; + } + LLViewerRegion* region = object->getRegion(); if (region) { std::string lookup_url=region->getCapability("GetMetadata"); diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index f2b369a9ad..8f3eaaa207 100755 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -380,9 +380,11 @@ LLFavoritesBarCtrl::LLFavoritesBarCtrl(const LLFavoritesBarCtrl::Params& p) mShowDragMarker(FALSE), mLandingTab(NULL), mLastTab(NULL), - mTabsHighlightEnabled(TRUE) - , mUpdateDropDownItems(true) -, mRestoreOverflowMenu(false) + mTabsHighlightEnabled(TRUE), + mUpdateDropDownItems(true), + mRestoreOverflowMenu(false), + mGetPrevItems(true), + mItemsChangedTimer() { // Register callback for menus with current registrar (will be parent panel's registrar) LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Favorites.DoToSelected", @@ -659,6 +661,15 @@ void LLFavoritesBarCtrl::changed(U32 mask) LLFavoritesOrderStorage::instance().getSLURL((*i)->getAssetUUID()); } updateButtons(); + if (!mItemsChangedTimer.getStarted()) + { + mItemsChangedTimer.start(); + } + else + { + mItemsChangedTimer.reset(); + } + } } @@ -693,6 +704,21 @@ void LLFavoritesBarCtrl::draw() // Once drawn, mark this false so we won't draw it again (unless we hit the favorite bar again) mShowDragMarker = FALSE; } + if (mItemsChangedTimer.getStarted()) + { + if (mItemsChangedTimer.getElapsedTimeF32() > 1.f) + { + LLFavoritesOrderStorage::instance().saveFavoritesRecord(); + mItemsChangedTimer.stop(); + } + } + + if(!mItemsChangedTimer.getStarted() && LLFavoritesOrderStorage::instance().mUpdateRequired) + { + LLFavoritesOrderStorage::instance().mUpdateRequired = false; + mItemsChangedTimer.start(); + } + } const LLButton::Params& LLFavoritesBarCtrl::getButtonParams() @@ -723,6 +749,12 @@ void LLFavoritesBarCtrl::updateButtons() return; } + if(mGetPrevItems) + { + LLFavoritesOrderStorage::instance().mPrevFavorites = mItems; + mGetPrevItems = false; + } + const LLButton::Params& button_params = getButtonParams(); if(mItems.empty()) @@ -844,6 +876,7 @@ void LLFavoritesBarCtrl::updateButtons() { mUpdateDropDownItems = false; } + } LLButton* LLFavoritesBarCtrl::createButton(const LLPointer<LLViewerInventoryItem> item, const LLButton::Params& button_params, S32 x_offset) @@ -912,9 +945,11 @@ BOOL LLFavoritesBarCtrl::postBuild() BOOL LLFavoritesBarCtrl::collectFavoriteItems(LLInventoryModel::item_array_t &items) { + if (mFavoriteFolderId.isNull()) return FALSE; + LLInventoryModel::cat_array_t cats; LLIsType is_type(LLAssetType::AT_LANDMARK); @@ -1411,6 +1446,7 @@ void LLFavoritesBarCtrl::insertItem(LLInventoryModel::item_array_t& items, const const std::string LLFavoritesOrderStorage::SORTING_DATA_FILE_NAME = "landmarks_sorting.xml"; const S32 LLFavoritesOrderStorage::NO_INDEX = -1; +bool LLFavoritesOrderStorage::mSaveOnExit = false; void LLFavoritesOrderStorage::setSortIndex(const LLViewerInventoryItem* inv_item, S32 sort_index) { @@ -1447,6 +1483,7 @@ void LLFavoritesOrderStorage::getSLURL(const LLUUID& asset_id) LL_DEBUGS("FavoritesBar") << "landmark for " << asset_id << " already loaded" << LL_ENDL; onLandmarkLoaded(asset_id, lm); } + return; } // static @@ -1482,13 +1519,16 @@ void LLFavoritesOrderStorage::destroyClass() LLFile::remove(old_filename); } - if (gSavedPerAccountSettings.getBOOL("ShowFavoritesOnLogin")) - { - LLFavoritesOrderStorage::instance().saveFavoritesSLURLs(); - } - else + std::string filename = getSavedOrderFileName(); + file.open(filename.c_str()); + if (file.is_open()) { - LLFavoritesOrderStorage::instance().removeFavoritesRecordOfUser(); + file.close(); + LLFile::remove(filename); + if(mSaveOnExit) + { + LLFavoritesOrderStorage::instance().saveFavoritesRecord(true); + } } } @@ -1503,108 +1543,57 @@ std::string LLFavoritesOrderStorage::getSavedOrderFileName() void LLFavoritesOrderStorage::load() { - // load per-resident sorting information std::string filename = getSavedOrderFileName(); - LLSD settings_llsd; llifstream file; file.open(filename.c_str()); if (file.is_open()) { LLSDSerialize::fromXML(settings_llsd, file); - LL_INFOS("FavoritesBar") << "loaded favorites order from '" << filename << "' " - << (settings_llsd.isMap() ? "" : "un") << "successfully" - << LL_ENDL; - file.close(); - } - else - { - LL_WARNS("FavoritesBar") << "unable to open favorites order file at '" << filename << "'" << LL_ENDL; - } - - for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); - iter != settings_llsd.endMap(); ++iter) - { - mSortIndexes.insert(std::make_pair(LLUUID(iter->first), (S32)iter->second.asInteger())); + LL_INFOS("FavoritesBar") << "loaded favorites order from '" << filename << "' " + << (settings_llsd.isMap() ? "" : "un") << "successfully" + << LL_ENDL; + file.close(); + mSaveOnExit = true; + + for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); + iter != settings_llsd.endMap(); ++iter) + { + mSortIndexes.insert(std::make_pair(LLUUID(iter->first), (S32)iter->second.asInteger())); + } } -} - -void LLFavoritesOrderStorage::saveFavoritesSLURLs() -{ - // Do not change the file if we are not logged in yet. - if (!LLLoginInstance::getInstance()->authSuccess()) + else { - LL_WARNS("FavoritesBar") << "Cannot save favorites: not logged in" << LL_ENDL; - return; + filename = getStoredFavoritesFilename(); + if (!filename.empty()) + { + llifstream in_file; + in_file.open(filename.c_str()); + LLSD fav_llsd; + LLSD user_llsd; + if (in_file.is_open()) + { + LLSDSerialize::fromXML(fav_llsd, in_file); + LL_INFOS("FavoritesBar") << "loaded favorites from '" << filename << "' " + << (fav_llsd.isMap() ? "" : "un") << "successfully" + << LL_ENDL; + in_file.close(); + user_llsd = fav_llsd[gAgentUsername]; + + S32 index = 0; + for (LLSD::array_iterator iter = user_llsd.beginArray(); + iter != user_llsd.endArray(); ++iter) + { + mSortIndexes.insert(std::make_pair(iter->get("id").asUUID(), index)); + index++; + } + } + else + { + LL_WARNS("FavoritesBar") << "unable to open favorites from '" << filename << "'" << LL_ENDL; + } + } } - - std::string filename = getStoredFavoritesFilename(); - if (!filename.empty()) - { - llifstream in_file; - in_file.open(filename.c_str()); - LLSD fav_llsd; - if (in_file.is_open()) - { - LLSDSerialize::fromXML(fav_llsd, in_file); - LL_INFOS("FavoritesBar") << "loaded favorites from '" << filename << "' " - << (fav_llsd.isMap() ? "" : "un") << "successfully" - << LL_ENDL; - in_file.close(); - } - else - { - LL_WARNS("FavoritesBar") << "unable to open favorites from '" << filename << "'" << LL_ENDL; - } - - const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - gInventory.collectDescendents(fav_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); - - LLSD user_llsd; - for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); it++) - { - LLSD value; - value["name"] = (*it)->getName(); - value["asset_id"] = (*it)->getAssetUUID(); - - slurls_map_t::iterator slurl_iter = mSLURLs.find(value["asset_id"]); - if (slurl_iter != mSLURLs.end()) - { - LL_DEBUGS("FavoritesBar") << "Saving favorite: idx=" << LLFavoritesOrderStorage::instance().getSortIndex((*it)->getUUID()) << ", SLURL=" << slurl_iter->second << ", value=" << value << LL_ENDL; - value["slurl"] = slurl_iter->second; - user_llsd[LLFavoritesOrderStorage::instance().getSortIndex((*it)->getUUID())] = value; - } - else - { - LL_WARNS("FavoritesBar") << "Not saving favorite " << value["name"] << ": no matching SLURL" << LL_ENDL; - } - } - - LLAvatarName av_name; - LLAvatarNameCache::get( gAgentID, &av_name ); - // Note : use the "John Doe" and not the "john.doe" version of the name - // as we'll compare it with the stored credentials in the login panel. - fav_llsd[av_name.getUserName()] = user_llsd; - - llofstream file; - file.open(filename.c_str()); - if ( file.is_open() ) - { - LLSDSerialize::toPrettyXML(fav_llsd, file); - LL_INFOS("FavoritesBar") << "saved favorites for '" << av_name.getUserName() - << "' to '" << filename << "' " - << LL_ENDL; - file.close(); - } - else - { - LL_WARNS("FavoritesBar") << "unable to open favorites storage for '" << av_name.getUserName() - << "' at '" << filename << "' " - << LL_ENDL; - } - } } void LLFavoritesOrderStorage::removeFavoritesRecordOfUser() @@ -1626,8 +1615,30 @@ void LLFavoritesOrderStorage::removeFavoritesRecordOfUser() // See saveFavoritesSLURLs() here above for the reason why. if (fav_llsd.has(av_name.getUserName())) { - LL_INFOS("FavoritesBar") << "Removed favorites for " << av_name.getUserName() << LL_ENDL; - fav_llsd.erase(av_name.getUserName()); + LLSD user_llsd = fav_llsd[av_name.getUserName()]; + + if (user_llsd.beginArray()->has("id")) + { + for (LLSD::array_iterator iter = user_llsd.beginArray();iter != user_llsd.endArray(); ++iter) + { + LLSD value; + value["id"]= iter->get("id").asUUID(); + iter->assign(value); + } + fav_llsd[av_name.getUserName()] = user_llsd; + llofstream file; + file.open(filename.c_str()); + if ( file.is_open() ) + { + LLSDSerialize::toPrettyXML(fav_llsd, file); + file.close(); + } + } + else + { + LL_INFOS("FavoritesBar") << "Removed favorites for " << av_name.getUserName() << LL_ENDL; + fav_llsd.erase(av_name.getUserName()); + } } llofstream out_file; @@ -1648,20 +1659,20 @@ void LLFavoritesOrderStorage::onLandmarkLoaded(const LLUUID& asset_id, LLLandmar if (landmark) { LL_DEBUGS("FavoritesBar") << "landmark for " << asset_id << " loaded" << LL_ENDL; - LLVector3d pos_global; - if (!landmark->getGlobalPos(pos_global)) - { - // If global position was unknown on first getGlobalPos() call - // it should be set for the subsequent calls. - landmark->getGlobalPos(pos_global); - } + LLVector3d pos_global; + if (!landmark->getGlobalPos(pos_global)) + { + // If global position was unknown on first getGlobalPos() call + // it should be set for the subsequent calls. + landmark->getGlobalPos(pos_global); + } - if (!pos_global.isExactlyZero()) - { - LL_DEBUGS("FavoritesBar") << "requesting slurl for landmark " << asset_id << LL_ENDL; - LLLandmarkActions::getSLURLfromPosGlobal(pos_global, + if (!pos_global.isExactlyZero()) + { + LL_DEBUGS("FavoritesBar") << "requesting slurl for landmark " << asset_id << LL_ENDL; + LLLandmarkActions::getSLURLfromPosGlobal(pos_global, boost::bind(&LLFavoritesOrderStorage::storeFavoriteSLURL, this, asset_id, _1)); - } + } } } @@ -1671,41 +1682,6 @@ void LLFavoritesOrderStorage::storeFavoriteSLURL(const LLUUID& asset_id, std::st mSLURLs[asset_id] = slurl; } -void LLFavoritesOrderStorage::save() -{ - if (mIsDirty) - { - // something changed, so save it - std::string filename = LLFavoritesOrderStorage::getInstance()->getSavedOrderFileName(); - if (!filename.empty()) - { - LLSD settings_llsd; - - for(sort_index_map_t::const_iterator iter = mSortIndexes.begin(); iter != mSortIndexes.end(); ++iter) - { - settings_llsd[iter->first.asString()] = iter->second; - } - - llofstream file; - file.open(filename.c_str()); - if ( file.is_open() ) - { - LLSDSerialize::toPrettyXML(settings_llsd, file); - LL_INFOS("FavoritesBar") << "saved favorites order to '" << filename << "' " << LL_ENDL; - } - else - { - LL_WARNS("FavoritesBar") << "failed to open favorites order file '" << filename << "' " << LL_ENDL; - } - } - else - { - LL_DEBUGS("FavoritesBar") << "no user directory available to store favorites order file" << LL_ENDL; - } - } -} - - void LLFavoritesOrderStorage::cleanup() { // nothing to clean @@ -1720,7 +1696,7 @@ void LLFavoritesOrderStorage::cleanup() sort_index_map_t aTempMap; //copy unremoved values from mSortIndexes to aTempMap - std::remove_copy_if(mSortIndexes.begin(), mSortIndexes.end(), + std::remove_copy_if(mSortIndexes.begin(), mSortIndexes.end(), inserter(aTempMap, aTempMap.begin()), is_not_in_fav); @@ -1752,8 +1728,8 @@ void LLFavoritesOrderStorage::saveOrder() void LLFavoritesOrderStorage::saveItemsOrder( const LLInventoryModel::item_array_t& items ) { - int sortField = 0; + int sortField = 0; // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field for (LLInventoryModel::item_array_t::const_iterator i = items.begin(); i != items.end(); ++i) { @@ -1793,6 +1769,110 @@ void LLFavoritesOrderStorage::rearrangeFavoriteLandmarks(const LLUUID& source_it saveItemsOrder(items); } +BOOL LLFavoritesOrderStorage::saveFavoritesRecord(bool pref_changed) +{ + + LLUUID favorite_folder= gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + if (favorite_folder.isNull()) + return FALSE; + + LLInventoryModel::item_array_t items; + LLInventoryModel::cat_array_t cats; + + LLIsType is_type(LLAssetType::AT_LANDMARK); + gInventory.collectDescendentsIf(favorite_folder, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); + + std::sort(items.begin(), items.end(), LLFavoritesSort()); + + if((items != mPrevFavorites) || pref_changed) + { + std::string filename = getStoredFavoritesFilename(); + if (!filename.empty()) + { + llifstream in_file; + in_file.open(filename.c_str()); + LLSD fav_llsd; + if (in_file.is_open()) + { + LLSDSerialize::fromXML(fav_llsd, in_file); + in_file.close(); + } + else + { + LL_WARNS("FavoritesBar") << "unable to open favorites from '" << filename << "'" << LL_ENDL; + } + + LLSD user_llsd; + S32 fav_iter = 0; + for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); it++) + { + LLSD value; + if (gSavedPerAccountSettings.getBOOL("ShowFavoritesOnLogin")) + { + value["name"] = (*it)->getName(); + value["asset_id"] = (*it)->getAssetUUID(); + value["id"] = (*it)->getUUID(); + slurls_map_t::iterator slurl_iter = mSLURLs.find(value["asset_id"]); + if (slurl_iter != mSLURLs.end()) + { + value["slurl"] = slurl_iter->second; + user_llsd[fav_iter] = value; + } + else + { + getSLURL((*it)->getAssetUUID()); + mUpdateRequired = true; + return FALSE; + } + } + else + { + value["id"] = (*it)->getUUID(); + user_llsd[fav_iter] = value; + } + + fav_iter ++; + } + + LLAvatarName av_name; + LLAvatarNameCache::get( gAgentID, &av_name ); + // Note : use the "John Doe" and not the "john.doe" version of the name + // as we'll compare it with the stored credentials in the login panel. + fav_llsd[av_name.getUserName()] = user_llsd; + llofstream file; + file.open(filename.c_str()); + if ( file.is_open() ) + { + LLSDSerialize::toPrettyXML(fav_llsd, file); + file.close(); + mSaveOnExit = false; + } + else + { + LL_WARNS("FavoritesBar") << "unable to open favorites storage for '" << av_name.getUserName() + << "' at '" << filename << "' " << LL_ENDL; + } + } + + mPrevFavorites = items; + } + + return TRUE; + +} + +void LLFavoritesOrderStorage::showFavoritesOnLoginChanged(BOOL show) +{ + if (show) + { + saveFavoritesRecord(true); + } + else + { + removeFavoritesRecordOfUser(); + } +} + void AddFavoriteLandmarkCallback::fire(const LLUUID& inv_item_id) { if (mTargetLandmarkId.isNull()) return; diff --git a/indra/newview/llfavoritesbar.h b/indra/newview/llfavoritesbar.h index a370724947..66fc8b2ae7 100755 --- a/indra/newview/llfavoritesbar.h +++ b/indra/newview/llfavoritesbar.h @@ -105,8 +105,10 @@ protected: bool mUpdateDropDownItems; bool mRestoreOverflowMenu; - LLUUID mSelectedItemID; + bool mGetPrevItems; + LLUUID mSelectedItemID; + LLFrameTimer mItemsChangedTimer; LLUIImage* mImageDragIndication; private: @@ -204,12 +206,23 @@ public: * @see cleanup() */ static void destroyClass(); + static std::string getStoredFavoritesFilename(); + static std::string getSavedOrderFileName(); + + BOOL saveFavoritesRecord(bool pref_changed = false); + void showFavoritesOnLoginChanged(BOOL show); + + LLInventoryModel::item_array_t mPrevFavorites; + const static S32 NO_INDEX; + static bool mSaveOnExit; + bool mUpdateRequired; + private: friend class LLSingleton<LLFavoritesOrderStorage>; - LLFavoritesOrderStorage() : mIsDirty(false) { load(); } - ~LLFavoritesOrderStorage() { save(); } + LLFavoritesOrderStorage() : mIsDirty(false), mUpdateRequired(false){ load(); } + ~LLFavoritesOrderStorage() {} /** * Removes sort indexes for items which are not in Favorites bar for now. @@ -217,13 +230,8 @@ private: void cleanup(); const static std::string SORTING_DATA_FILE_NAME; - std::string getSavedOrderFileName(); - static std::string getStoredFavoritesFilename(); - - void load(); - void save(); - void saveFavoritesSLURLs(); + void load(); // Remove record of current user's favorites from file on disk. void removeFavoritesRecordOfUser(); diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp index e71daa6067..c5d637d1fc 100755 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -34,10 +34,12 @@ // Viewer includes #include "llagent.h" #include "llagentui.h" -#include "llappviewer.h" +#include "llappviewer.h" +#include "llnotificationsutil.h" #include "llslurl.h" #include "llvoiceclient.h" #include "lluictrlfactory.h" +#include "llupdaterservice.h" #include "llviewertexteditor.h" #include "llviewercontrol.h" #include "llviewerstats.h" @@ -99,9 +101,23 @@ public: /// separated so that we can programmatically access the same info. static LLSD getInfo(); void onClickCopyToClipboard(); + void onClickUpdateCheck(); + + // checks state of updater service and starts a check outside of schedule. + // subscribes callback for closest state update + static void setUpdateListener(); private: void setSupportText(const std::string& server_release_notes_url); + + // notifications for user requested checks + static void showCheckUpdateNotification(S32 state); + + // callback method for manual checks + static bool callbackCheckUpdate(LLSD const & event); + + // listener name for update checks + static const std::string sCheckUpdateListenerName; }; @@ -132,6 +148,9 @@ BOOL LLFloaterAbout::postBuild() getChild<LLUICtrl>("copy_btn")->setCommitCallback( boost::bind(&LLFloaterAbout::onClickCopyToClipboard, this)); + getChild<LLUICtrl>("update_btn")->setCommitCallback( + boost::bind(&LLFloaterAbout::onClickUpdateCheck, this)); + static const LLUIColor about_color = LLUIColorTable::instance().getColor("TextFgReadOnlyColor"); if (gAgent.getRegion()) @@ -235,6 +254,11 @@ void LLFloaterAbout::onClickCopyToClipboard() support_widget->deselect(); } +void LLFloaterAbout::onClickUpdateCheck() +{ + setUpdateListener(); +} + void LLFloaterAbout::setSupportText(const std::string& server_release_notes_url) { #if LL_WINDOWS @@ -256,6 +280,68 @@ void LLFloaterAbout::setSupportText(const std::string& server_release_notes_url) } ///---------------------------------------------------------------------------- +/// Floater About Update-check related functions +///---------------------------------------------------------------------------- + +const std::string LLFloaterAbout::sCheckUpdateListenerName = "LLUpdateNotificationListener"; + +void LLFloaterAbout::showCheckUpdateNotification(S32 state) +{ + switch (state) + { + case LLUpdaterService::UP_TO_DATE: + LLNotificationsUtil::add("UpdateViewerUpToDate"); + break; + case LLUpdaterService::DOWNLOADING: + case LLUpdaterService::INSTALLING: + LLNotificationsUtil::add("UpdateDownloadInProgress"); + break; + case LLUpdaterService::TERMINAL: + // download complete, user triggered check after download pop-up appeared + LLNotificationsUtil::add("UpdateDownloadComplete"); + break; + default: + LLNotificationsUtil::add("UpdateCheckError"); + break; + } +} + +bool LLFloaterAbout::callbackCheckUpdate(LLSD const & event) +{ + if (!event.has("payload")) + { + return false; + } + + LLSD payload = event["payload"]; + if (payload.has("type") && payload["type"].asInteger() == LLUpdaterService::STATE_CHANGE) + { + LLEventPumps::instance().obtain("mainlooprepeater").stopListening(sCheckUpdateListenerName); + showCheckUpdateNotification(payload["state"].asInteger()); + } + return false; +} + +void LLFloaterAbout::setUpdateListener() +{ + LLUpdaterService update_service; + S32 service_state = update_service.getState(); + // Note: Do not set state listener before forceCheck() since it set's new state + if (update_service.forceCheck() || service_state == LLUpdaterService::CHECKING_FOR_UPDATE) + { + LLEventPump& mainloop(LLEventPumps::instance().obtain("mainlooprepeater")); + if (mainloop.getListener(sCheckUpdateListenerName) == LLBoundListener()) // dummy listener + { + mainloop.listen(sCheckUpdateListenerName, boost::bind(&callbackCheckUpdate, _1)); + } + } + else + { + showCheckUpdateNotification(service_state); + } +} + +///---------------------------------------------------------------------------- /// LLFloaterAboutUtil ///---------------------------------------------------------------------------- void LLFloaterAboutUtil::registerFloater() @@ -265,6 +351,11 @@ void LLFloaterAboutUtil::registerFloater() } +void LLFloaterAboutUtil::checkUpdatesAndNotify() +{ + LLFloaterAbout::setUpdateListener(); +} + ///---------------------------------------------------------------------------- /// Class LLServerReleaseNotesURLFetcher implementation ///---------------------------------------------------------------------------- diff --git a/indra/newview/llfloaterabout.h b/indra/newview/llfloaterabout.h index 8fc1aa4f29..be34b631cc 100755 --- a/indra/newview/llfloaterabout.h +++ b/indra/newview/llfloaterabout.h @@ -30,6 +30,9 @@ namespace LLFloaterAboutUtil { void registerFloater(); + + // Support for user initialized update/state checks + void checkUpdatesAndNotify(); } #endif // LL_LLFLOATERABOUT_H diff --git a/indra/newview/llfloaterbump.cpp b/indra/newview/llfloaterbump.cpp index 34904cf7ed..957c91b226 100755 --- a/indra/newview/llfloaterbump.cpp +++ b/indra/newview/llfloaterbump.cpp @@ -32,6 +32,7 @@ #include "llavataractions.h" #include "llfloaterbump.h" +#include "llfloaterreg.h" #include "llfloaterreporter.h" #include "llmutelist.h" #include "llpanelblockedlist.h" @@ -87,11 +88,11 @@ BOOL LLFloaterBump::postBuild() // virtual void LLFloaterBump::onOpen(const LLSD& key) { - mNames.clear(); - mList->deleteAllItems(); - if (gMeanCollisionList.empty()) { + mNames.clear(); + mList->deleteAllItems(); + std::string none_detected = getString("none_detected"); LLSD row; row["columns"][0]["value"] = none_detected; @@ -100,12 +101,20 @@ void LLFloaterBump::onOpen(const LLSD& key) } else { - for (mean_collision_list_t::iterator iter = gMeanCollisionList.begin(); - iter != gMeanCollisionList.end(); ++iter) - { - LLMeanCollisionData *mcd = *iter; - add(mList, mcd); - } + populateCollisionList(); + } +} + +void LLFloaterBump::populateCollisionList() +{ + mNames.clear(); + mList->deleteAllItems(); + + for (mean_collision_list_t::iterator iter = gMeanCollisionList.begin(); + iter != gMeanCollisionList.end(); ++iter) + { + LLMeanCollisionData *mcd = *iter; + add(mList, mcd); } } @@ -247,3 +256,8 @@ void LLFloaterBump::inviteToGroup() { LLAvatarActions::inviteToGroup(mItemUUID); } + +LLFloaterBump* LLFloaterBump::getInstance() +{ + return LLFloaterReg::getTypedInstance<LLFloaterBump>("bumps"); +} diff --git a/indra/newview/llfloaterbump.h b/indra/newview/llfloaterbump.h index 11b7db9fee..ce52c75255 100755 --- a/indra/newview/llfloaterbump.h +++ b/indra/newview/llfloaterbump.h @@ -46,6 +46,10 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); + static LLFloaterBump* getInstance(); + + void populateCollisionList(); + void startIM(); void startCall(); void reportAbuse(); diff --git a/indra/newview/llfloaterfacebook.cpp b/indra/newview/llfloaterfacebook.cpp index 3a2047cfef..da85d378b2 100644 --- a/indra/newview/llfloaterfacebook.cpp +++ b/indra/newview/llfloaterfacebook.cpp @@ -64,9 +64,9 @@ const std::string DEFAULT_CHECKIN_ICON_URL = "http://map.secondlife.com.s3.amazo const std::string DEFAULT_CHECKIN_QUERY_PARAMETERS = "?sourceid=slshare_checkin&utm_source=facebook&utm_medium=checkin&utm_campaign=slshare"; const std::string DEFAULT_PHOTO_QUERY_PARAMETERS = "?sourceid=slshare_photo&utm_source=facebook&utm_medium=photo&utm_campaign=slshare"; -const S32 MAX_QUALITY = 100; // Max quality value for jpeg images -const S32 MIN_QUALITY = 0; // Min quality value for jpeg images -const S32 TARGET_DATA_SIZE = 95000; // Size of the image (compressed) we're trying to send to Facebook +const S32 MAX_QUALITY = 100; // Max quality value for jpeg images +const S32 MIN_QUALITY = 0; // Min quality value for jpeg images +const S32 TARGET_DATA_SIZE = 950000; // Size of the image (compressed) we're trying to send to Facebook std::string get_map_url() { diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp index ab57e8c170..f1a6ef78a6 100755 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -261,6 +261,8 @@ BOOL LLFloaterIMContainer::postBuild() mInitialized = true; + mIsFirstOpen = true; + // Add callbacks: // We'll take care of view updates on idle gIdleCallbacks.addFunction(idle, this); @@ -636,14 +638,16 @@ void LLFloaterIMContainer::setVisible(BOOL visible) { // Make sure we have the Nearby Chat present when showing the conversation container nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"); - if (nearby_chat == NULL) + if ((nearby_chat == NULL) || mIsFirstOpen) { + mIsFirstOpen = false; // If not found, force the creation of the nearby chat conversation panel // *TODO: find a way to move this to XML as a default panel or something like that LLSD name("nearby_chat"); LLFloaterReg::toggleInstanceOrBringToFront(name); selectConversationPair(LLUUID(NULL), false, false); } + flashConversationItemWidget(mSelectedSession,false); LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(mSelectedSession); @@ -1216,7 +1220,22 @@ void LLFloaterIMContainer::doToSelectedConversation(const std::string& command, { if (selectedIDS.size() > 0) { - LLAvatarActions::viewChatHistory(selectedIDS.front()); + if(conversationItem->getType() == LLConversationItem::CONV_SESSION_GROUP) + { + LLFloaterReg::showInstance("preview_conversation", conversationItem->getUUID(), true); + } + else if(conversationItem->getType() == LLConversationItem::CONV_SESSION_AD_HOC) + { + LLConversation* conv = LLConversationLog::instance().findConversation(LLIMModel::getInstance()->findIMSession(conversationItem->getUUID())); + if(conv) + { + LLFloaterReg::showInstance("preview_conversation", conv->getSessionID(), true); + } + } + else + { + LLAvatarActions::viewChatHistory(selectedIDS.front()); + } } } else @@ -1316,6 +1335,15 @@ bool LLFloaterIMContainer::enableContextMenuItem(const LLSD& userdata) { return LLLogChat::isNearbyTranscriptExist(); } + else if (getCurSelectedViewModelItem()->getType() == LLConversationItem::CONV_SESSION_AD_HOC) + { + const LLConversation* conv = LLConversationLog::instance().findConversation(LLIMModel::getInstance()->findIMSession(uuids.front())); + if(conv) + { + return LLLogChat::isAdHocTranscriptExist(conv->getHistoryFileName()); + } + return false; + } else { bool is_group = (getCurSelectedViewModelItem()->getType() == LLConversationItem::CONV_SESSION_GROUP); @@ -1881,22 +1909,28 @@ bool LLFloaterIMContainer::canBanSelectedMember(const LLUUID& participant_uuid) return false; } - if (!gdatap->mMembers.size()) + if (gdatap->mPendingBanRequest) { return false; } - LLGroupMgrGroupData::member_list_t::iterator mi = gdatap->mMembers.find((participant_uuid)); - if (mi == gdatap->mMembers.end()) + if (gdatap->isRoleMemberDataComplete()) { - return false; - } + if (!gdatap->mMembers.size()) + { + return false; + } - LLGroupMemberData* member_data = (*mi).second; - // Is the member an owner? - if ( member_data && member_data->isInRole(gdatap->mOwnerRole) ) - { - return false; + LLGroupMgrGroupData::member_list_t::iterator mi = gdatap->mMembers.find((participant_uuid)); + if (mi != gdatap->mMembers.end()) + { + LLGroupMemberData* member_data = (*mi).second; + // Is the member an owner? + if (member_data && member_data->isInRole(gdatap->mOwnerRole)) + { + return false; + } + } } if( gAgent.hasPowerInGroup(group_uuid, GP_ROLE_REMOVE_MEMBER) && @@ -1924,20 +1958,8 @@ void LLFloaterIMContainer::banSelectedMember(const LLUUID& participant_uuid) LL_WARNS("Groups") << "Unable to get group data for group " << group_uuid << LL_ENDL; return; } - std::vector<LLUUID> ids; - ids.push_back(participant_uuid); - - LLGroupBanData ban_data; - gdatap->createBanEntry(participant_uuid, ban_data); - LLGroupMgr::getInstance()->sendGroupBanRequest(LLGroupMgr::REQUEST_POST, group_uuid, LLGroupMgr::BAN_CREATE, ids); - LLGroupMgr::getInstance()->sendGroupMemberEjects(group_uuid, ids); - LLGroupMgr::getInstance()->sendGroupMembersRequest(group_uuid); - LLSD args; - std::string name; - gCacheName->getFullName(participant_uuid, name); - args["AVATAR_NAME"] = name; - args["GROUP_NAME"] = gdatap->mName; - LLNotifications::instance().add(LLNotification::Params("EjectAvatarFromGroup").substitutions(args)); + + gdatap->banMemberById(participant_uuid); } diff --git a/indra/newview/llfloaterimcontainer.h b/indra/newview/llfloaterimcontainer.h index f21c0b9947..60cef83d9a 100755 --- a/indra/newview/llfloaterimcontainer.h +++ b/indra/newview/llfloaterimcontainer.h @@ -193,6 +193,8 @@ private: bool mInitialized; bool mIsFirstLaunch; + bool mIsFirstOpen; + LLUUID mSelectedSession; std::string mGeneralTitle; diff --git a/indra/newview/llfloaterjoystick.cpp b/indra/newview/llfloaterjoystick.cpp index b7fff6cae3..ee3d633dd0 100755 --- a/indra/newview/llfloaterjoystick.cpp +++ b/indra/newview/llfloaterjoystick.cpp @@ -326,7 +326,21 @@ void LLFloaterJoystick::onClickOK(void *joy_panel) } } +void LLFloaterJoystick::onClickCloseBtn(bool app_quitting) +{ + cancel(); + closeFloater(app_quitting); +} + void LLFloaterJoystick::setSNDefaults() { LLViewerJoystick::getInstance()->setSNDefaults(); } + +void LLFloaterJoystick::onClose(bool app_quitting) +{ + if (app_quitting) + { + cancel(); + } +} diff --git a/indra/newview/llfloaterjoystick.h b/indra/newview/llfloaterjoystick.h index 9c3752540d..a1b5951389 100755 --- a/indra/newview/llfloaterjoystick.h +++ b/indra/newview/llfloaterjoystick.h @@ -45,6 +45,11 @@ public: virtual void draw(); static void setSNDefaults(); +protected: + + void onClose(bool app_quitting); + void onClickCloseBtn(bool app_quitting); + private: LLFloaterJoystick(const LLSD& data); diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index ce32aeda94..f9fd5069af 100755 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -2439,6 +2439,8 @@ void LLPanelLandAccess::refresh() mListAccess->clearSortOrder(); mListAccess->deleteAllItems(); S32 count = parcel->mAccessList.size(); + getChild<LLUICtrl>("AllowedText")->setTextArg("[COUNT]", llformat("%d",count)); + getChild<LLUICtrl>("AccessList")->setToolTipArg(LLStringExplicit("[LISTED]"), llformat("%d",count)); getChild<LLUICtrl>("AccessList")->setToolTipArg(LLStringExplicit("[MAX]"), llformat("%d",PARCEL_MAX_ACCESS_LIST)); @@ -2484,6 +2486,7 @@ void LLPanelLandAccess::refresh() mListBanned->clearSortOrder(); mListBanned->deleteAllItems(); S32 count = parcel->mBanList.size(); + getChild<LLUICtrl>("BanCheck")->setTextArg("[COUNT]", llformat("%d",count)); getChild<LLUICtrl>("BannedList")->setToolTipArg(LLStringExplicit("[LISTED]"), llformat("%d",count)); getChild<LLUICtrl>("BannedList")->setToolTipArg(LLStringExplicit("[MAX]"), llformat("%d",PARCEL_MAX_ACCESS_LIST)); diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index 6804b21b28..41005144a7 100755 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -26,35 +26,8 @@ #include "llviewerprecompiledheaders.h" -#if LL_MSVC -#pragma warning (disable : 4263) -#pragma warning (disable : 4264) -#endif -#include "dae.h" -//#include "dom.h" -#include "dom/domAsset.h" -#include "dom/domBind_material.h" -#include "dom/domCOLLADA.h" -#include "dom/domConstants.h" -#include "dom/domController.h" -#include "dom/domEffect.h" -#include "dom/domGeometry.h" -#include "dom/domInstance_geometry.h" -#include "dom/domInstance_material.h" -#include "dom/domInstance_node.h" -#include "dom/domInstance_effect.h" -#include "dom/domMaterial.h" -#include "dom/domMatrix.h" -#include "dom/domNode.h" -#include "dom/domProfile_COMMON.h" -#include "dom/domRotate.h" -#include "dom/domScale.h" -#include "dom/domTranslate.h" -#include "dom/domVisual_scene.h" -#if LL_MSVC -#pragma warning (default : 4263) -#pragma warning (default : 4264) -#endif +#include "llmodelloader.h" +#include "lldaeloader.h" #include "llfloatermodelpreview.h" @@ -112,14 +85,15 @@ #include "llanimationstates.h" #include "llviewernetwork.h" #include "llviewershadermgr.h" -#include "glod/glod.h" -const S32 SLM_SUPPORTED_VERSION = 3; +#include "glod/glod.h" +#include <boost/algorithm/string.hpp> //static S32 LLFloaterModelPreview::sUploadAmount = 10; LLFloaterModelPreview* LLFloaterModelPreview::sInstance = NULL; -std::list<LLModelLoader*> LLModelLoader::sActiveLoaderList; + +bool LLModelPreview::sIgnoreLoadedCallback = false; // "Retain%" decomp parameter has values from 0.0 to 1.0 by 0.01 // But according to the UI spec for upload model floater, this parameter @@ -199,190 +173,46 @@ std::string lod_label_name[NUM_LOD+1] = "I went off the end of the lod_label_name array. Me so smart." }; -std::string colladaVersion[VERSIONTYPE_COUNT+1] = -{ - "1.4.0", - "1.4.1", - "Unsupported" -}; - - -#define LL_DEGENERACY_TOLERANCE 1e-7f - -inline F32 dot3fpu(const LLVector4a& a, const LLVector4a& b) -{ - volatile F32 p0 = a[0] * b[0]; - volatile F32 p1 = a[1] * b[1]; - volatile F32 p2 = a[2] * b[2]; - return p0 + p1 + p2; -} - -bool ll_is_degenerate(const LLVector4a& a, const LLVector4a& b, const LLVector4a& c, F32 tolerance = LL_DEGENERACY_TOLERANCE) -{ - // small area check - { - LLVector4a edge1; edge1.setSub( a, b ); - LLVector4a edge2; edge2.setSub( a, c ); - ////////////////////////////////////////////////////////////////////////// - /// Linden Modified - ////////////////////////////////////////////////////////////////////////// - - // If no one edge is more than 10x longer than any other edge, we weaken - // the tolerance by a factor of 1e-4f. - - LLVector4a edge3; edge3.setSub( c, b ); - const F32 len1sq = edge1.dot3(edge1).getF32(); - const F32 len2sq = edge2.dot3(edge2).getF32(); - const F32 len3sq = edge3.dot3(edge3).getF32(); - bool abOK = (len1sq <= 100.f * len2sq) && (len1sq <= 100.f * len3sq); - bool acOK = (len2sq <= 100.f * len1sq) && (len1sq <= 100.f * len3sq); - bool cbOK = (len3sq <= 100.f * len1sq) && (len1sq <= 100.f * len2sq); - if ( abOK && acOK && cbOK ) - { - tolerance *= 1e-4f; - } - - ////////////////////////////////////////////////////////////////////////// - /// End Modified - ////////////////////////////////////////////////////////////////////////// - - LLVector4a cross; cross.setCross3( edge1, edge2 ); - - LLVector4a edge1b; edge1b.setSub( b, a ); - LLVector4a edge2b; edge2b.setSub( b, c ); - LLVector4a crossb; crossb.setCross3( edge1b, edge2b ); - - if ( ( cross.dot3(cross).getF32() < tolerance ) || ( crossb.dot3(crossb).getF32() < tolerance )) - { - return true; - } - } - - // point triangle distance check - { - LLVector4a Q; Q.setSub(a, b); - LLVector4a R; R.setSub(c, b); - - const F32 QQ = dot3fpu(Q, Q); - const F32 RR = dot3fpu(R, R); - const F32 QR = dot3fpu(R, Q); - - volatile F32 QQRR = QQ * RR; - volatile F32 QRQR = QR * QR; - F32 Det = (QQRR - QRQR); - - if( Det == 0.0f ) - { - return true; - } - } - - return false; -} - -bool validate_face(const LLVolumeFace& face) +BOOL stop_gloderror() { + GLuint error = glodGetError(); - for (U32 v = 0; v < face.mNumVertices; v++) - { - if(face.mPositions && !face.mPositions[v].isFinite3()) - { - LL_WARNS() << "NaN position data in face found!" << LL_ENDL; - return false; - } - - if(face.mNormals && !face.mNormals[v].isFinite3()) - { - LL_WARNS() << "NaN normal data in face found!" << LL_ENDL; - return false; - } - } - - for (U32 i = 0; i < face.mNumIndices; ++i) - { - if (face.mIndices[i] >= face.mNumVertices) - { - LL_WARNS() << "Face has invalid index." << LL_ENDL; - return false; - } - } - - if (face.mNumIndices % 3 != 0 || face.mNumIndices == 0) + if (error != GLOD_NO_ERROR) { - LL_WARNS() << "Face has invalid number of indices." << LL_ENDL; - return false; + LL_WARNS() << "GLOD error detected, cannot generate LOD: " << std::hex << error << LL_ENDL; + return TRUE; } - - /*const LLVector4a scale(0.5f); - - - for (U32 i = 0; i < face.mNumIndices; i+=3) - { - U16 idx1 = face.mIndices[i]; - U16 idx2 = face.mIndices[i+1]; - U16 idx3 = face.mIndices[i+2]; - - LLVector4a v1; v1.setMul(face.mPositions[idx1], scale); - LLVector4a v2; v2.setMul(face.mPositions[idx2], scale); - LLVector4a v3; v3.setMul(face.mPositions[idx3], scale); - - if (ll_is_degenerate(v1,v2,v3)) - { - LL_WARNS() << "Degenerate face found!" << LL_ENDL; - return false; - } - }*/ - return true; + return FALSE; } -bool validate_model(const LLModel* mdl) +LLViewerFetchedTexture* bindMaterialDiffuseTexture(const LLImportMaterial& material) { - if (mdl->getNumVolumeFaces() == 0) - { - LL_WARNS() << "Model has no faces!" << LL_ENDL; - return false; - } + LLViewerFetchedTexture *texture = LLViewerTextureManager::getFetchedTexture(material.getDiffuseMap(), FTT_DEFAULT, TRUE, LLGLTexture::BOOST_PREVIEW); - for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) + if (texture) { - if (mdl->getVolumeFace(i).mNumVertices == 0) - { - LL_WARNS() << "Face has no vertices." << LL_ENDL; - return false; - } - - if (mdl->getVolumeFace(i).mNumIndices == 0) + if (texture->getDiscardLevel() > -1) { - LL_WARNS() << "Face has no indices." << LL_ENDL; - return false; - } - - if (!validate_face(mdl->getVolumeFace(i))) - { - return false; + gGL.getTexUnit(0)->bind(texture, true); + return texture; } } - return true; + return NULL; } -BOOL stop_gloderror() +std::string stripSuffix(std::string name) { - GLuint error = glodGetError(); - - if (error != GLOD_NO_ERROR) + if ((name.find("_LOD") != -1) || (name.find("_PHYS") != -1)) { - LL_WARNS() << "GLOD error detected, cannot generate LOD: " << std::hex << error << LL_ENDL; - return TRUE; + return name.substr(0, name.rfind('_')); } - - return FALSE; + return name; } - LLMeshFilePicker::LLMeshFilePicker(LLModelPreview* mp, S32 lod) - : LLFilePickerThread(LLFilePicker::FFLOAD_COLLADA) +: LLFilePickerThread(LLFilePicker::FFLOAD_COLLADA) { mMP = mp; mLOD = lod; @@ -393,6 +223,29 @@ void LLMeshFilePicker::notify(const std::string& filename) mMP->loadModel(mFile, mLOD); } +void FindModel(LLModelLoader::scene& scene, const std::string& name_to_match, LLModel*& baseModelOut, LLMatrix4& matOut) +{ + LLModelLoader::scene::iterator base_iter = scene.begin(); + bool found = false; + while (!found && (base_iter != scene.end())) + { + matOut = base_iter->first; + + LLModelLoader::model_instance_list::iterator base_instance_iter = base_iter->second.begin(); + while (!found && (base_instance_iter != base_iter->second.end())) + { + LLModelInstance& base_instance = *base_instance_iter++; + LLModel* base_model = base_instance.mModel; + + if (base_model && (base_model->mLabel == name_to_match)) + { + baseModelOut = base_model; + return; + } + } + base_iter++; + } +} //----------------------------------------------------------------------------- // LLFloaterModelPreview() @@ -613,6 +466,11 @@ void LLFloaterModelPreview::disableViewOption(const std::string& option) void LLFloaterModelPreview::loadModel(S32 lod) { mModelPreview->mLoading = true; + if (lod == LLModel::LOD_PHYSICS) + { + // loading physics from file + mModelPreview->mPhysicsSearchLOD = lod; + } (new LLMeshFilePicker(mModelPreview, lod))->getFile(); } @@ -791,9 +649,9 @@ void LLFloaterModelPreview::draw() childSetTextArg("status", "[STATUS]", getString("status_material_mismatch")); } else - if ( mModelPreview->getLoadState() > LLModelLoader::ERROR_PARSING ) - { - childSetTextArg("status", "[STATUS]", getString(LLModel::getStatusString(mModelPreview->getLoadState() - LLModelLoader::ERROR_PARSING))); + if ( mModelPreview->getLoadState() > LLModelLoader::ERROR_MODEL ) + { + childSetTextArg("status", "[STATUS]", getString(LLModel::getStatusString(mModelPreview->getLoadState() - LLModelLoader::ERROR_MODEL))); } else if ( mModelPreview->getLoadState() == LLModelLoader::ERROR_PARSING ) @@ -945,9 +803,16 @@ BOOL LLFloaterModelPreview::handleScrollWheel(S32 x, S32 y, S32 clicks) /*virtual*/ void LLFloaterModelPreview::onOpen(const LLSD& key) { + LLModelPreview::sIgnoreLoadedCallback = false; requestAgentUploadPermissions(); } +/*virtual*/ +void LLFloaterModelPreview::onClose(bool app_quitting) +{ + LLModelPreview::sIgnoreLoadedCallback = true; +} + //static void LLFloaterModelPreview::onPhysicsParamCommit(LLUICtrl* ctrl, void* data) { @@ -1308,1815 +1173,6 @@ void LLFloaterModelPreview::onMouseCaptureLostModelPreview(LLMouseHandler* handl } //----------------------------------------------------------------------------- -// LLModelLoader -//----------------------------------------------------------------------------- -LLModelLoader::LLModelLoader( std::string filename, S32 lod, LLModelPreview* preview, JointTransformMap& jointMap, - std::deque<std::string>& jointsFromNodes ) -: mJointList( jointMap ) -, mJointsFromNode( jointsFromNodes ) -, LLThread("Model Loader"), mFilename(filename), mLod(lod), mPreview(preview), mFirstTransform(TRUE), mNumOfFetchingTextures(0) -{ - mJointMap["mPelvis"] = "mPelvis"; - mJointMap["mTorso"] = "mTorso"; - mJointMap["mChest"] = "mChest"; - mJointMap["mNeck"] = "mNeck"; - mJointMap["mHead"] = "mHead"; - mJointMap["mSkull"] = "mSkull"; - mJointMap["mEyeRight"] = "mEyeRight"; - mJointMap["mEyeLeft"] = "mEyeLeft"; - mJointMap["mCollarLeft"] = "mCollarLeft"; - mJointMap["mShoulderLeft"] = "mShoulderLeft"; - mJointMap["mElbowLeft"] = "mElbowLeft"; - mJointMap["mWristLeft"] = "mWristLeft"; - mJointMap["mCollarRight"] = "mCollarRight"; - mJointMap["mShoulderRight"] = "mShoulderRight"; - mJointMap["mElbowRight"] = "mElbowRight"; - mJointMap["mWristRight"] = "mWristRight"; - mJointMap["mHipRight"] = "mHipRight"; - mJointMap["mKneeRight"] = "mKneeRight"; - mJointMap["mAnkleRight"] = "mAnkleRight"; - mJointMap["mFootRight"] = "mFootRight"; - mJointMap["mToeRight"] = "mToeRight"; - mJointMap["mHipLeft"] = "mHipLeft"; - mJointMap["mKneeLeft"] = "mKneeLeft"; - mJointMap["mAnkleLeft"] = "mAnkleLeft"; - mJointMap["mFootLeft"] = "mFootLeft"; - mJointMap["mToeLeft"] = "mToeLeft"; - - mJointMap["avatar_mPelvis"] = "mPelvis"; - mJointMap["avatar_mTorso"] = "mTorso"; - mJointMap["avatar_mChest"] = "mChest"; - mJointMap["avatar_mNeck"] = "mNeck"; - mJointMap["avatar_mHead"] = "mHead"; - mJointMap["avatar_mSkull"] = "mSkull"; - mJointMap["avatar_mEyeRight"] = "mEyeRight"; - mJointMap["avatar_mEyeLeft"] = "mEyeLeft"; - mJointMap["avatar_mCollarLeft"] = "mCollarLeft"; - mJointMap["avatar_mShoulderLeft"] = "mShoulderLeft"; - mJointMap["avatar_mElbowLeft"] = "mElbowLeft"; - mJointMap["avatar_mWristLeft"] = "mWristLeft"; - mJointMap["avatar_mCollarRight"] = "mCollarRight"; - mJointMap["avatar_mShoulderRight"] = "mShoulderRight"; - mJointMap["avatar_mElbowRight"] = "mElbowRight"; - mJointMap["avatar_mWristRight"] = "mWristRight"; - mJointMap["avatar_mHipRight"] = "mHipRight"; - mJointMap["avatar_mKneeRight"] = "mKneeRight"; - mJointMap["avatar_mAnkleRight"] = "mAnkleRight"; - mJointMap["avatar_mFootRight"] = "mFootRight"; - mJointMap["avatar_mToeRight"] = "mToeRight"; - mJointMap["avatar_mHipLeft"] = "mHipLeft"; - mJointMap["avatar_mKneeLeft"] = "mKneeLeft"; - mJointMap["avatar_mAnkleLeft"] = "mAnkleLeft"; - mJointMap["avatar_mFootLeft"] = "mFootLeft"; - mJointMap["avatar_mToeLeft"] = "mToeLeft"; - - - mJointMap["hip"] = "mPelvis"; - mJointMap["abdomen"] = "mTorso"; - mJointMap["chest"] = "mChest"; - mJointMap["neck"] = "mNeck"; - mJointMap["head"] = "mHead"; - mJointMap["figureHair"] = "mSkull"; - mJointMap["lCollar"] = "mCollarLeft"; - mJointMap["lShldr"] = "mShoulderLeft"; - mJointMap["lForeArm"] = "mElbowLeft"; - mJointMap["lHand"] = "mWristLeft"; - mJointMap["rCollar"] = "mCollarRight"; - mJointMap["rShldr"] = "mShoulderRight"; - mJointMap["rForeArm"] = "mElbowRight"; - mJointMap["rHand"] = "mWristRight"; - mJointMap["rThigh"] = "mHipRight"; - mJointMap["rShin"] = "mKneeRight"; - mJointMap["rFoot"] = "mFootRight"; - mJointMap["lThigh"] = "mHipLeft"; - mJointMap["lShin"] = "mKneeLeft"; - mJointMap["lFoot"] = "mFootLeft"; - - if (mPreview) - { - //only try to load from slm if viewer is configured to do so and this is the - //initial model load (not an LoD or physics shape) - mTrySLM = gSavedSettings.getBOOL("MeshImportUseSLM") && mPreview->mUploadData.empty(); - mPreview->setLoadState(STARTING); - } - else - { - mTrySLM = false; - } - - assert_main_thread(); - sActiveLoaderList.push_back(this) ; -} - -LLModelLoader::~LLModelLoader() -{ - assert_main_thread(); - sActiveLoaderList.remove(this); -} - -void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, BOOL& first_transform) -{ - LLVector4a box[] = - { - LLVector4a(-1, 1,-1), - LLVector4a(-1, 1, 1), - LLVector4a(-1,-1,-1), - LLVector4a(-1,-1, 1), - LLVector4a( 1, 1,-1), - LLVector4a( 1, 1, 1), - LLVector4a( 1,-1,-1), - LLVector4a( 1,-1, 1), - }; - - for (S32 j = 0; j < model->getNumVolumeFaces(); ++j) - { - const LLVolumeFace& face = model->getVolumeFace(j); - - LLVector4a center; - center.setAdd(face.mExtents[0], face.mExtents[1]); - center.mul(0.5f); - LLVector4a size; - size.setSub(face.mExtents[1],face.mExtents[0]); - size.mul(0.5f); - - for (U32 i = 0; i < 8; i++) - { - LLVector4a t; - t.setMul(size, box[i]); - t.add(center); - - LLVector4a v; - - mat.affineTransform(t, v); - - if (first_transform) - { - first_transform = FALSE; - min = max = v; - } - else - { - update_min_max(min, max, v); - } - } - } -} - -void stretch_extents(LLModel* model, LLMatrix4& mat, LLVector3& min, LLVector3& max, BOOL& first_transform) -{ - LLVector4a mina, maxa; - LLMatrix4a mata; - - mata.loadu(mat); - mina.load3(min.mV); - maxa.load3(max.mV); - - stretch_extents(model, mata, mina, maxa, first_transform); - - min.set(mina.getF32ptr()); - max.set(maxa.getF32ptr()); -} - -void LLModelLoader::run() -{ - doLoadModel(); - doOnIdleOneTime(boost::bind(&LLModelLoader::loadModelCallback,this)); -} - -bool LLModelLoader::doLoadModel() -{ - //first, look for a .slm file of the same name that was modified later - //than the .dae - - if (mTrySLM) - { - std::string filename = mFilename; - - std::string::size_type i = filename.rfind("."); - if (i != std::string::npos) - { - filename.replace(i, filename.size()-1, ".slm"); - llstat slm_status; - if (LLFile::stat(filename, &slm_status) == 0) - { //slm file exists - llstat dae_status; - if (LLFile::stat(mFilename, &dae_status) != 0 || - dae_status.st_mtime < slm_status.st_mtime) - { - if (loadFromSLM(filename)) - { //slm successfully loaded, if this fails, fall through and - //try loading from dae - - mLod = -1; //successfully loading from an slm implicitly sets all - //LoDs - return true; - } - } - } - } - } - - //no suitable slm exists, load from the .dae file - DAE dae; - domCOLLADA* dom = dae.open(mFilename); - - if (!dom) - { - LL_INFOS()<<" Error with dae - traditionally indicates a corrupt file."<<LL_ENDL; - setLoadState( ERROR_PARSING ); - return false; - } - //Dom version - daeString domVersion = dae.getDomVersion(); - std::string sldom(domVersion); - LL_INFOS()<<"Collada Importer Version: "<<sldom<<LL_ENDL; - //Dae version - domVersionType docVersion = dom->getVersion(); - //0=1.4 - //1=1.4.1 - //2=Currently unsupported, however may work - if (docVersion > 1 ) - { - docVersion = VERSIONTYPE_COUNT; - } - LL_INFOS()<<"Dae version "<<colladaVersion[docVersion]<<LL_ENDL; - - - daeDatabase* db = dae.getDatabase(); - - daeInt count = db->getElementCount(NULL, COLLADA_TYPE_MESH); - - daeDocument* doc = dae.getDoc(mFilename); - if (!doc) - { - LL_WARNS() << "can't find internal doc" << LL_ENDL; - return false; - } - - daeElement* root = doc->getDomRoot(); - if (!root) - { - LL_WARNS() << "document has no root" << LL_ENDL; - return false; - } - - //Verify some basic properties of the dae - //1. Basic validity check on controller - U32 controllerCount = (int) db->getElementCount( NULL, "controller" ); - bool result = false; - for ( int i=0; i<controllerCount; ++i ) - { - domController* pController = NULL; - db->getElement( (daeElement**) &pController, i , NULL, "controller" ); - result = mPreview->verifyController( pController ); - if (!result) - { - setLoadState( ERROR_PARSING ); - return true; - } - } - - - //get unit scale - mTransform.setIdentity(); - - domAsset::domUnit* unit = daeSafeCast<domAsset::domUnit>(root->getDescendant(daeElement::matchType(domAsset::domUnit::ID()))); - - if (unit) - { - F32 meter = unit->getMeter(); - mTransform.mMatrix[0][0] = meter; - mTransform.mMatrix[1][1] = meter; - mTransform.mMatrix[2][2] = meter; - } - - //get up axis rotation - LLMatrix4 rotation; - - domUpAxisType up = UPAXISTYPE_Y_UP; // default is Y_UP - domAsset::domUp_axis* up_axis = - daeSafeCast<domAsset::domUp_axis>(root->getDescendant(daeElement::matchType(domAsset::domUp_axis::ID()))); - - if (up_axis) - { - up = up_axis->getValue(); - } - - if (up == UPAXISTYPE_X_UP) - { - rotation.initRotation(0.0f, 90.0f * DEG_TO_RAD, 0.0f); - } - else if (up == UPAXISTYPE_Y_UP) - { - rotation.initRotation(90.0f * DEG_TO_RAD, 0.0f, 0.0f); - } - - rotation *= mTransform; - mTransform = rotation; - - - for (daeInt idx = 0; idx < count; ++idx) - { //build map of domEntities to LLModel - domMesh* mesh = NULL; - db->getElement((daeElement**) &mesh, idx, NULL, COLLADA_TYPE_MESH); - - if (mesh) - { - LLPointer<LLModel> model = LLModel::loadModelFromDomMesh(mesh); - - if(model->getStatus() != LLModel::NO_ERRORS) - { - setLoadState(ERROR_PARSING + model->getStatus()) ; - return false; //abort - } - - if (model.notNull() && validate_model(model)) - { - mModelList.push_back(model); - mModel[mesh] = model; - } - } - } - - count = db->getElementCount(NULL, COLLADA_TYPE_SKIN); - for (daeInt idx = 0; idx < count; ++idx) - { //add skinned meshes as instances - domSkin* skin = NULL; - db->getElement((daeElement**) &skin, idx, NULL, COLLADA_TYPE_SKIN); - - if (skin) - { - domGeometry* geom = daeSafeCast<domGeometry>(skin->getSource().getElement()); - - if (geom) - { - domMesh* mesh = geom->getMesh(); - if (mesh) - { - LLModel* model = mModel[mesh]; - if (model) - { - LLVector3 mesh_scale_vector; - LLVector3 mesh_translation_vector; - model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector); - - LLMatrix4 normalized_transformation; - normalized_transformation.setTranslation(mesh_translation_vector); - - LLMatrix4 mesh_scale; - mesh_scale.initScale(mesh_scale_vector); - mesh_scale *= normalized_transformation; - normalized_transformation = mesh_scale; - - glh::matrix4f inv_mat((F32*) normalized_transformation.mMatrix); - inv_mat = inv_mat.inverse(); - LLMatrix4 inverse_normalized_transformation(inv_mat.m); - - domSkin::domBind_shape_matrix* bind_mat = skin->getBind_shape_matrix(); - - if (bind_mat) - { //get bind shape matrix - domFloat4x4& dom_value = bind_mat->getValue(); - - LLMeshSkinInfo& skin_info = model->mSkinInfo; - - for (int i = 0; i < 4; i++) - { - for(int j = 0; j < 4; j++) - { - skin_info.mBindShapeMatrix.mMatrix[i][j] = dom_value[i + j*4]; - } - } - - LLMatrix4 trans = normalized_transformation; - trans *= skin_info.mBindShapeMatrix; - skin_info.mBindShapeMatrix = trans; - } - - - //Some collada setup for accessing the skeleton - daeElement* pElement = 0; - dae.getDatabase()->getElement( &pElement, 0, 0, "skeleton" ); - - //Try to get at the skeletal instance controller - domInstance_controller::domSkeleton* pSkeleton = daeSafeCast<domInstance_controller::domSkeleton>( pElement ); - bool missingSkeletonOrScene = false; - - //If no skeleton, do a breadth-first search to get at specific joints - bool rootNode = false; - - //Need to test for a skeleton that does not have a root node - //This occurs when your instance controller does not have an associated scene - if ( pSkeleton ) - { - daeElement* pSkeletonRootNode = pSkeleton->getValue().getElement(); - if ( pSkeletonRootNode ) - { - rootNode = true; - } - - } - if ( !pSkeleton || !rootNode ) - { - daeElement* pScene = root->getDescendant("visual_scene"); - if ( !pScene ) - { - LL_WARNS()<<"No visual scene - unable to parse bone offsets "<<LL_ENDL; - missingSkeletonOrScene = true; - } - else - { - //Get the children at this level - daeTArray< daeSmartRef<daeElement> > children = pScene->getChildren(); - S32 childCount = children.getCount(); - - //Process any children that are joints - //Not all children are joints, some code be ambient lights, cameras, geometry etc.. - for (S32 i = 0; i < childCount; ++i) - { - domNode* pNode = daeSafeCast<domNode>(children[i]); - if ( isNodeAJoint( pNode ) ) - { - processJointNode( pNode, mJointList ); - } - } - } - } - else - //Has Skeleton - { - //Get the root node of the skeleton - daeElement* pSkeletonRootNode = pSkeleton->getValue().getElement(); - if ( pSkeletonRootNode ) - { - //Once we have the root node - start acccessing it's joint components - const int jointCnt = mJointMap.size(); - std::map<std::string, std::string> :: const_iterator jointIt = mJointMap.begin(); - - //Loop over all the possible joints within the .dae - using the allowed joint list in the ctor. - for ( int i=0; i<jointCnt; ++i, ++jointIt ) - { - //Build a joint for the resolver to work with - char str[64]={0}; - sprintf(str,"./%s",(*jointIt).first.c_str() ); - //LL_WARNS()<<"Joint "<< str <<LL_ENDL; - - //Setup the resolver - daeSIDResolver resolver( pSkeletonRootNode, str ); - - //Look for the joint - domNode* pJoint = daeSafeCast<domNode>( resolver.getElement() ); - if ( pJoint ) - { - //Pull out the translate id and store it in the jointTranslations map - daeSIDResolver jointResolverA( pJoint, "./translate" ); - domTranslate* pTranslateA = daeSafeCast<domTranslate>( jointResolverA.getElement() ); - daeSIDResolver jointResolverB( pJoint, "./location" ); - domTranslate* pTranslateB = daeSafeCast<domTranslate>( jointResolverB.getElement() ); - - LLMatrix4 workingTransform; - - //Translation via SID - if ( pTranslateA ) - { - extractTranslation( pTranslateA, workingTransform ); - } - else - if ( pTranslateB ) - { - extractTranslation( pTranslateB, workingTransform ); - } - else - { - //Translation via child from element - daeElement* pTranslateElement = getChildFromElement( pJoint, "translate" ); - if ( pTranslateElement && pTranslateElement->typeID() != domTranslate::ID() ) - { - LL_WARNS()<< "The found element is not a translate node" <<LL_ENDL; - missingSkeletonOrScene = true; - } - else - if ( pTranslateElement ) - { - extractTranslationViaElement( pTranslateElement, workingTransform ); - } - else - { - extractTranslationViaSID( pJoint, workingTransform ); - } - - } - - //Store the joint transform w/respect to it's name. - mJointList[(*jointIt).second.c_str()] = workingTransform; - } - } - - //If anything failed in regards to extracting the skeleton, joints or translation id, - //mention it - if ( missingSkeletonOrScene ) - { - LL_WARNS()<< "Partial jointmap found in asset - did you mean to just have a partial map?" << LL_ENDL; - } - }//got skeleton? - } - - - domSkin::domJoints* joints = skin->getJoints(); - - domInputLocal_Array& joint_input = joints->getInput_array(); - - for (size_t i = 0; i < joint_input.getCount(); ++i) - { - domInputLocal* input = joint_input.get(i); - xsNMTOKEN semantic = input->getSemantic(); - - if (strcmp(semantic, COMMON_PROFILE_INPUT_JOINT) == 0) - { //found joint source, fill model->mJointMap and model->mSkinInfo.mJointNames - daeElement* elem = input->getSource().getElement(); - - domSource* source = daeSafeCast<domSource>(elem); - if (source) - { - - - domName_array* names_source = source->getName_array(); - - if (names_source) - { - domListOfNames &names = names_source->getValue(); - - for (size_t j = 0; j < names.getCount(); ++j) - { - std::string name(names.get(j)); - if (mJointMap.find(name) != mJointMap.end()) - { - name = mJointMap[name]; - } - model->mSkinInfo.mJointNames.push_back(name); - model->mSkinInfo.mJointMap[name] = j; - } - } - else - { - domIDREF_array* names_source = source->getIDREF_array(); - if (names_source) - { - xsIDREFS& names = names_source->getValue(); - - for (size_t j = 0; j < names.getCount(); ++j) - { - std::string name(names.get(j).getID()); - if (mJointMap.find(name) != mJointMap.end()) - { - name = mJointMap[name]; - } - model->mSkinInfo.mJointNames.push_back(name); - model->mSkinInfo.mJointMap[name] = j; - } - } - } - } - } - else if (strcmp(semantic, COMMON_PROFILE_INPUT_INV_BIND_MATRIX) == 0) - { //found inv_bind_matrix array, fill model->mInvBindMatrix - domSource* source = daeSafeCast<domSource>(input->getSource().getElement()); - if (source) - { - domFloat_array* t = source->getFloat_array(); - if (t) - { - domListOfFloats& transform = t->getValue(); - S32 count = transform.getCount()/16; - - for (S32 k = 0; k < count; ++k) - { - LLMatrix4 mat; - - for (int i = 0; i < 4; i++) - { - for(int j = 0; j < 4; j++) - { - mat.mMatrix[i][j] = transform[k*16 + i + j*4]; - } - } - - model->mSkinInfo.mInvBindMatrix.push_back(mat); - } - } - } - } - } - - //Now that we've parsed the joint array, let's determine if we have a full rig - //(which means we have all the joint sthat are required for an avatar versus - //a skinned asset attached to a node in a file that contains an entire skeleton, - //but does not use the skeleton). - buildJointToNodeMappingFromScene( root ); - mPreview->critiqueRigForUploadApplicability( model->mSkinInfo.mJointNames ); - - if ( !missingSkeletonOrScene ) - { - //Set the joint translations on the avatar - if it's a full mapping - //The joints are reset in the dtor - if ( mPreview->getRigWithSceneParity() ) - { - std::map<std::string, std::string> :: const_iterator masterJointIt = mJointMap.begin(); - std::map<std::string, std::string> :: const_iterator masterJointItEnd = mJointMap.end(); - for (;masterJointIt!=masterJointItEnd;++masterJointIt ) - { - std::string lookingForJoint = (*masterJointIt).first.c_str(); - - if ( mJointList.find( lookingForJoint ) != mJointList.end() ) - { - //LL_INFOS()<<"joint "<<lookingForJoint.c_str()<<LL_ENDL; - LLMatrix4 jointTransform = mJointList[lookingForJoint]; - LLJoint* pJoint = mPreview->getPreviewAvatar()->getJoint( lookingForJoint ); - if ( pJoint ) - { - LLUUID fake_mesh_id; - fake_mesh_id.generate(); - pJoint->addAttachmentPosOverride( jointTransform.getTranslation(), fake_mesh_id, gAgentAvatarp->avString()); - } - else - { - //Most likely an error in the asset. - LL_WARNS()<<"Tried to apply joint position from .dae, but it did not exist in the avatar rig." << LL_ENDL; - } - } - } - } - } //missingSkeletonOrScene - - - //We need to construct the alternate bind matrix (which contains the new joint positions) - //in the same order as they were stored in the joint buffer. The joints associated - //with the skeleton are not stored in the same order as they are in the exported joint buffer. - //This remaps the skeletal joints to be in the same order as the joints stored in the model. - std::vector<std::string> :: const_iterator jointIt = model->mSkinInfo.mJointNames.begin(); - const int jointCnt = model->mSkinInfo.mJointNames.size(); - for ( int i=0; i<jointCnt; ++i, ++jointIt ) - { - std::string lookingForJoint = (*jointIt).c_str(); - //Look for the joint xform that we extracted from the skeleton, using the jointIt as the key - //and store it in the alternate bind matrix - if ( mJointList.find( lookingForJoint ) != mJointList.end() ) - { - LLMatrix4 jointTransform = mJointList[lookingForJoint]; - LLMatrix4 newInverse = model->mSkinInfo.mInvBindMatrix[i]; - newInverse.setTranslation( mJointList[lookingForJoint].getTranslation() ); - model->mSkinInfo.mAlternateBindMatrix.push_back( newInverse ); - } - else - { - LL_WARNS()<<"Possibly misnamed/missing joint [" <<lookingForJoint.c_str()<<" ] "<<LL_ENDL; - } - } - - //grab raw position array - - domVertices* verts = mesh->getVertices(); - if (verts) - { - domInputLocal_Array& inputs = verts->getInput_array(); - for (size_t i = 0; i < inputs.getCount() && model->mPosition.empty(); ++i) - { - if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_POSITION) == 0) - { - domSource* pos_source = daeSafeCast<domSource>(inputs[i]->getSource().getElement()); - if (pos_source) - { - domFloat_array* pos_array = pos_source->getFloat_array(); - if (pos_array) - { - domListOfFloats& pos = pos_array->getValue(); - - for (size_t j = 0; j < pos.getCount(); j += 3) - { - if (pos.getCount() <= j+2) - { - LL_ERRS() << "Invalid position array size." << LL_ENDL; - } - - LLVector3 v(pos[j], pos[j+1], pos[j+2]); - - //transform from COLLADA space to volume space - v = v * inverse_normalized_transformation; - - model->mPosition.push_back(v); - } - } - } - } - } - } - - //grab skin weights array - domSkin::domVertex_weights* weights = skin->getVertex_weights(); - if (weights) - { - domInputLocalOffset_Array& inputs = weights->getInput_array(); - domFloat_array* vertex_weights = NULL; - for (size_t i = 0; i < inputs.getCount(); ++i) - { - if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_WEIGHT) == 0) - { - domSource* weight_source = daeSafeCast<domSource>(inputs[i]->getSource().getElement()); - if (weight_source) - { - vertex_weights = weight_source->getFloat_array(); - } - } - } - - if (vertex_weights) - { - domListOfFloats& w = vertex_weights->getValue(); - domListOfUInts& vcount = weights->getVcount()->getValue(); - domListOfInts& v = weights->getV()->getValue(); - - U32 c_idx = 0; - for (size_t vc_idx = 0; vc_idx < vcount.getCount(); ++vc_idx) - { //for each vertex - daeUInt count = vcount[vc_idx]; - - //create list of weights that influence this vertex - LLModel::weight_list weight_list; - - for (daeUInt i = 0; i < count; ++i) - { //for each weight - daeInt joint_idx = v[c_idx++]; - daeInt weight_idx = v[c_idx++]; - - if (joint_idx == -1) - { - //ignore bindings to bind_shape_matrix - continue; - } - - F32 weight_value = w[weight_idx]; - - weight_list.push_back(LLModel::JointWeight(joint_idx, weight_value)); - } - - //sort by joint weight - std::sort(weight_list.begin(), weight_list.end(), LLModel::CompareWeightGreater()); - - std::vector<LLModel::JointWeight> wght; - - F32 total = 0.f; - - for (U32 i = 0; i < llmin((U32) 4, (U32) weight_list.size()); ++i) - { //take up to 4 most significant weights - if (weight_list[i].mWeight > 0.f) - { - wght.push_back( weight_list[i] ); - total += weight_list[i].mWeight; - } - } - - F32 scale = 1.f/total; - if (scale != 1.f) - { //normalize weights - for (U32 i = 0; i < wght.size(); ++i) - { - wght[i].mWeight *= scale; - } - } - - model->mSkinWeights[model->mPosition[vc_idx]] = wght; - } - - //add instance to scene for this model - - LLMatrix4 transformation = mTransform; - // adjust the transformation to compensate for mesh normalization - - LLMatrix4 mesh_translation; - mesh_translation.setTranslation(mesh_translation_vector); - mesh_translation *= transformation; - transformation = mesh_translation; - - LLMatrix4 mesh_scale; - mesh_scale.initScale(mesh_scale_vector); - mesh_scale *= transformation; - transformation = mesh_scale; - - std::map<std::string, LLImportMaterial> materials; - for (U32 i = 0; i < model->mMaterialList.size(); ++i) - { - materials[model->mMaterialList[i]] = LLImportMaterial(); - } - mScene[transformation].push_back(LLModelInstance(model, model->mLabel, transformation, materials)); - stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform); - } - } - } - } - } - } - } - - daeElement* scene = root->getDescendant("visual_scene"); - - if (!scene) - { - LL_WARNS() << "document has no visual_scene" << LL_ENDL; - setLoadState( ERROR_PARSING ); - return true; - } - - setLoadState( DONE ); - - bool badElement = false; - - processElement( scene, badElement ); - - if ( badElement ) - { - setLoadState( ERROR_PARSING ); - } - - return true; -} - -void LLModelLoader::setLoadState(U32 state) -{ - if (mPreview) - { - mPreview->setLoadState(state); - } -} - -bool LLModelLoader::loadFromSLM(const std::string& filename) -{ - //only need to populate mScene with data from slm - llstat stat; - - if (LLFile::stat(filename, &stat)) - { //file does not exist - return false; - } - - S32 file_size = (S32) stat.st_size; - - llifstream ifstream(filename.c_str(), std::ifstream::in | std::ifstream::binary); - LLSD data; - LLSDSerialize::fromBinary(data, ifstream, file_size); - ifstream.close(); - - //build model list for each LoD - model_list model[LLModel::NUM_LODS]; - - if (data["version"].asInteger() != SLM_SUPPORTED_VERSION) - { //unsupported version - return false; - } - - LLSD& mesh = data["mesh"]; - - LLVolumeParams volume_params; - volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); - - for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod) - { - for (U32 i = 0; i < mesh.size(); ++i) - { - std::stringstream str(mesh[i].asString()); - LLPointer<LLModel> loaded_model = new LLModel(volume_params, (F32) lod); - if (loaded_model->loadModel(str)) - { - loaded_model->mLocalID = i; - model[lod].push_back(loaded_model); - - if (lod == LLModel::LOD_HIGH && !loaded_model->mSkinInfo.mJointNames.empty()) - { - //check to see if rig is valid - mPreview->critiqueRigForUploadApplicability( loaded_model->mSkinInfo.mJointNames ); - } - } - } - } - - if (model[LLModel::LOD_HIGH].empty()) - { //failed to load high lod - return false; - } - - // Set name. - std::string name = data["name"]; - if (!name.empty()) - { - model[LLModel::LOD_HIGH][0]->mLabel = name; - } - - - //load instance list - model_instance_list instance_list; - - LLSD& instance = data["instance"]; - - for (U32 i = 0; i < instance.size(); ++i) - { - //deserialize instance list - instance_list.push_back(LLModelInstance(instance[i])); - - //match up model instance pointers - S32 idx = instance_list[i].mLocalMeshID; - - for (U32 lod = 0; lod < LLModel::NUM_LODS; ++lod) - { - if (!model[lod].empty()) - { - instance_list[i].mLOD[lod] = model[lod][idx]; - } - } - - instance_list[i].mModel = model[LLModel::LOD_HIGH][idx]; - } - - - //convert instance_list to mScene - mFirstTransform = TRUE; - for (U32 i = 0; i < instance_list.size(); ++i) - { - LLModelInstance& cur_instance = instance_list[i]; - mScene[cur_instance.mTransform].push_back(cur_instance); - stretch_extents(cur_instance.mModel, cur_instance.mTransform, mExtents[0], mExtents[1], mFirstTransform); - } - - setLoadState( DONE ); - - return true; -} - -//static -bool LLModelLoader::isAlive(LLModelLoader* loader) -{ - if(!loader) - { - return false ; - } - - std::list<LLModelLoader*>::iterator iter = sActiveLoaderList.begin() ; - for(; iter != sActiveLoaderList.end() && (*iter) != loader; ++iter) ; - - return *iter == loader ; -} - -void LLModelLoader::loadModelCallback() -{ - assert_main_thread(); - - if (mPreview) - { - mPreview->loadModelCallback(mLod); - } - - while (!isStopped()) - { //wait until this thread is stopped before deleting self - apr_sleep(100); - } - - //doubel check if "this" is valid before deleting it, in case it is aborted during running. - if(!isAlive(this)) - { - return ; - } - - //cleanup model loader - if (mPreview) - { - mPreview->mModelLoader = NULL; - } - - delete this; -} -//----------------------------------------------------------------------------- -// buildJointToNodeMappingFromScene() -//----------------------------------------------------------------------------- -void LLModelLoader::buildJointToNodeMappingFromScene( daeElement* pRoot ) -{ - daeElement* pScene = pRoot->getDescendant("visual_scene"); - if ( pScene ) - { - daeTArray< daeSmartRef<daeElement> > children = pScene->getChildren(); - S32 childCount = children.getCount(); - for (S32 i = 0; i < childCount; ++i) - { - domNode* pNode = daeSafeCast<domNode>(children[i]); - processJointToNodeMapping( pNode ); - } - } -} -//----------------------------------------------------------------------------- -// processJointToNodeMapping() -//----------------------------------------------------------------------------- -void LLModelLoader::processJointToNodeMapping( domNode* pNode ) -{ - if ( isNodeAJoint( pNode ) ) - { - //1.Store the parent - std::string nodeName = pNode->getName(); - if ( !nodeName.empty() ) - { - mJointsFromNode.push_front( pNode->getName() ); - } - //2. Handle the kiddo's - processChildJoints( pNode ); - } - else - { - //Determine if the're any children wrt to this failed node. - //This occurs when an armature is exported and ends up being what essentially amounts to - //as the root for the visual_scene - if ( pNode ) - { - processChildJoints( pNode ); - } - else - { - LL_INFOS()<<"Node is NULL"<<LL_ENDL; - } - - } -} -//----------------------------------------------------------------------------- -// processChildJoint() -//----------------------------------------------------------------------------- -void LLModelLoader::processChildJoints( domNode* pParentNode ) -{ - daeTArray< daeSmartRef<daeElement> > childOfChild = pParentNode->getChildren(); - S32 childOfChildCount = childOfChild.getCount(); - for (S32 i = 0; i < childOfChildCount; ++i) - { - domNode* pChildNode = daeSafeCast<domNode>( childOfChild[i] ); - if ( pChildNode ) - { - processJointToNodeMapping( pChildNode ); - } - } -} - -//----------------------------------------------------------------------------- -// critiqueRigForUploadApplicability() -//----------------------------------------------------------------------------- -void LLModelPreview::critiqueRigForUploadApplicability( const std::vector<std::string> &jointListFromAsset ) -{ - critiqueJointToNodeMappingFromScene(); - - //Determines the following use cases for a rig: - //1. It is suitable for upload with skin weights & joint positions, or - //2. It is suitable for upload as standard av with just skin weights - - bool isJointPositionUploadOK = isRigSuitableForJointPositionUpload( jointListFromAsset ); - bool isRigLegacyOK = isRigLegacy( jointListFromAsset ); - - //It's OK that both could end up being true, both default to false - if ( isJointPositionUploadOK ) - { - setRigValidForJointPositionUpload( true ); - } - - if ( isRigLegacyOK) - { - setLegacyRigValid( true ); - } - -} -//----------------------------------------------------------------------------- -// critiqueJointToNodeMappingFromScene() -//----------------------------------------------------------------------------- -void LLModelPreview::critiqueJointToNodeMappingFromScene( void ) -{ - //Do the actual nodes back the joint listing from the dae? - //if yes then this is a fully rigged asset, otherwise it's just a partial rig - - std::deque<std::string>::iterator jointsFromNodeIt = mJointsFromNode.begin(); - std::deque<std::string>::iterator jointsFromNodeEndIt = mJointsFromNode.end(); - bool result = true; - - if ( !mJointsFromNode.empty() ) - { - for ( ;jointsFromNodeIt!=jointsFromNodeEndIt;++jointsFromNodeIt ) - { - std::string name = *jointsFromNodeIt; - if ( mJointTransformMap.find( name ) != mJointTransformMap.end() ) - { - continue; - } - else - { - LL_INFOS()<<"critiqueJointToNodeMappingFromScene is missing a: "<<name<<LL_ENDL; - result = false; - } - } - } - else - { - result = false; - } - - //Determines the following use cases for a rig: - //1. Full av rig w/1-1 mapping from the scene and joint array - //2. Partial rig but w/o parity between the scene and joint array - if ( result ) - { - setRigWithSceneParity( true ); - } -} -//----------------------------------------------------------------------------- -// isRigLegacy() -//----------------------------------------------------------------------------- -bool LLModelPreview::isRigLegacy( const std::vector<std::string> &jointListFromAsset ) -{ - //No joints in asset - if ( jointListFromAsset.size() == 0 ) - { - return false; - } - - bool result = false; - - std::deque<std::string> :: const_iterator masterJointIt = mMasterLegacyJointList.begin(); - std::deque<std::string> :: const_iterator masterJointEndIt = mMasterLegacyJointList.end(); - - std::vector<std::string> :: const_iterator modelJointIt = jointListFromAsset.begin(); - std::vector<std::string> :: const_iterator modelJointItEnd = jointListFromAsset.end(); - - for ( ;masterJointIt!=masterJointEndIt;++masterJointIt ) - { - result = false; - modelJointIt = jointListFromAsset.begin(); - - for ( ;modelJointIt!=modelJointItEnd; ++modelJointIt ) - { - if ( *masterJointIt == *modelJointIt ) - { - result = true; - break; - } - } - if ( !result ) - { - LL_INFOS()<<" Asset did not contain the joint (if you're u/l a fully rigged asset w/joint positions - it is required)." << *masterJointIt<< LL_ENDL; - break; - } - } - return result; -} -//----------------------------------------------------------------------------- -// isRigSuitableForJointPositionUpload() -//----------------------------------------------------------------------------- -bool LLModelPreview::isRigSuitableForJointPositionUpload( const std::vector<std::string> &jointListFromAsset ) -{ - bool result = false; - - std::deque<std::string> :: const_iterator masterJointIt = mMasterJointList.begin(); - std::deque<std::string> :: const_iterator masterJointEndIt = mMasterJointList.end(); - - std::vector<std::string> :: const_iterator modelJointIt = jointListFromAsset.begin(); - std::vector<std::string> :: const_iterator modelJointItEnd = jointListFromAsset.end(); - - for ( ;masterJointIt!=masterJointEndIt;++masterJointIt ) - { - result = false; - modelJointIt = jointListFromAsset.begin(); - - for ( ;modelJointIt!=modelJointItEnd; ++modelJointIt ) - { - if ( *masterJointIt == *modelJointIt ) - { - result = true; - break; - } - } - if ( !result ) - { - LL_INFOS()<<" Asset did not contain the joint (if you're u/l a fully rigged asset w/joint positions - it is required)." << *masterJointIt<< LL_ENDL; - break; - } - } - return result; -} - - -//called in the main thread -void LLModelLoader::loadTextures() -{ - BOOL is_paused = isPaused() ; - pause() ; //pause the loader - - for(scene::iterator iter = mScene.begin(); iter != mScene.end(); ++iter) - { - for(U32 i = 0 ; i < iter->second.size(); i++) - { - for(std::map<std::string, LLImportMaterial>::iterator j = iter->second[i].mMaterial.begin(); - j != iter->second[i].mMaterial.end(); ++j) - { - LLImportMaterial& material = j->second; - - if(!material.mDiffuseMapFilename.empty()) - { - material.mDiffuseMap = - LLViewerTextureManager::getFetchedTextureFromUrl("file://" + material.mDiffuseMapFilename, FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW); - material.mDiffuseMap->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, TRUE, FALSE, mPreview, NULL, FALSE); - material.mDiffuseMap->forceToSaveRawImage(0, F32_MAX); - mNumOfFetchingTextures++ ; - } - } - } - } - - if(!is_paused) - { - unpause() ; - } -} - -//----------------------------------------------------------------------------- -// isNodeAJoint() -//----------------------------------------------------------------------------- -bool LLModelLoader::isNodeAJoint( domNode* pNode ) -{ - if ( !pNode ) - { - LL_INFOS()<<"Created node is NULL"<<LL_ENDL; - return false; - } - - if ( pNode->getName() == NULL ) - { - LL_INFOS()<<"Parsed node has no name "<<LL_ENDL; - //Attempt to write the node id, if possible (aids in debugging the visual scene) - if ( pNode->getId() ) - { - LL_INFOS()<<"Parsed node ID: "<<pNode->getId()<<LL_ENDL; - } - return false; - } - - if ( mJointMap.find( pNode->getName() ) != mJointMap.end() ) - { - return true; - } - - return false; -} -//----------------------------------------------------------------------------- -// verifyCount -//----------------------------------------------------------------------------- -bool LLModelPreview::verifyCount( int expected, int result ) -{ - if ( expected != result ) - { - LL_INFOS()<< "Error: (expected/got)"<<expected<<"/"<<result<<"verts"<<LL_ENDL; - return false; - } - return true; -} -//----------------------------------------------------------------------------- -// verifyController -//----------------------------------------------------------------------------- -bool LLModelPreview::verifyController( domController* pController ) -{ - - bool result = true; - - domSkin* pSkin = pController->getSkin(); - - if ( pSkin ) - { - xsAnyURI & uri = pSkin->getSource(); - domElement* pElement = uri.getElement(); - - if ( !pElement ) - { - LL_INFOS()<<"Can't resolve skin source"<<LL_ENDL; - return false; - } - - daeString type_str = pElement->getTypeName(); - if ( stricmp(type_str, "geometry") == 0 ) - { - //Skin is reference directly by geometry and get the vertex count from skin - domSkin::domVertex_weights* pVertexWeights = pSkin->getVertex_weights(); - U32 vertexWeightsCount = pVertexWeights->getCount(); - domGeometry* pGeometry = (domGeometry*) (domElement*) uri.getElement(); - domMesh* pMesh = pGeometry->getMesh(); - - if ( pMesh ) - { - //Get vertex count from geometry - domVertices* pVertices = pMesh->getVertices(); - if ( !pVertices ) - { - LL_INFOS()<<"No vertices!"<<LL_ENDL; - return false; - } - - if ( pVertices ) - { - xsAnyURI src = pVertices->getInput_array()[0]->getSource(); - domSource* pSource = (domSource*) (domElement*) src.getElement(); - U32 verticesCount = pSource->getTechnique_common()->getAccessor()->getCount(); - result = verifyCount( verticesCount, vertexWeightsCount ); - if ( !result ) - { - return result; - } - } - } - - U32 vcountCount = (U32) pVertexWeights->getVcount()->getValue().getCount(); - result = verifyCount( vcountCount, vertexWeightsCount ); - if ( !result ) - { - return result; - } - - domInputLocalOffset_Array& inputs = pVertexWeights->getInput_array(); - U32 sum = 0; - for (size_t i=0; i<vcountCount; i++) - { - sum += pVertexWeights->getVcount()->getValue()[i]; - } - result = verifyCount( sum * inputs.getCount(), (domInt) pVertexWeights->getV()->getValue().getCount() ); - } - } - - return result; -} - -//----------------------------------------------------------------------------- -// extractTranslation() -//----------------------------------------------------------------------------- -void LLModelLoader::extractTranslation( domTranslate* pTranslate, LLMatrix4& transform ) -{ - domFloat3 jointTrans = pTranslate->getValue(); - LLVector3 singleJointTranslation( jointTrans[0], jointTrans[1], jointTrans[2] ); - transform.setTranslation( singleJointTranslation ); -} -//----------------------------------------------------------------------------- -// extractTranslationViaElement() -//----------------------------------------------------------------------------- -void LLModelLoader::extractTranslationViaElement( daeElement* pTranslateElement, LLMatrix4& transform ) -{ - if ( pTranslateElement ) - { - domTranslate* pTranslateChild = dynamic_cast<domTranslate*>( pTranslateElement ); - domFloat3 translateChild = pTranslateChild->getValue(); - LLVector3 singleJointTranslation( translateChild[0], translateChild[1], translateChild[2] ); - transform.setTranslation( singleJointTranslation ); - } -} -//----------------------------------------------------------------------------- -// extractTranslationViaSID() -//----------------------------------------------------------------------------- -void LLModelLoader::extractTranslationViaSID( daeElement* pElement, LLMatrix4& transform ) -{ - if ( pElement ) - { - daeSIDResolver resolver( pElement, "./transform" ); - domMatrix* pMatrix = daeSafeCast<domMatrix>( resolver.getElement() ); - //We are only extracting out the translational component atm - LLMatrix4 workingTransform; - if ( pMatrix ) - { - domFloat4x4 domArray = pMatrix->getValue(); - for ( int i = 0; i < 4; i++ ) - { - for( int j = 0; j < 4; j++ ) - { - workingTransform.mMatrix[i][j] = domArray[i + j*4]; - } - } - LLVector3 trans = workingTransform.getTranslation(); - transform.setTranslation( trans ); - } - } - else - { - LL_WARNS()<<"Element is nonexistent - empty/unsupported node."<<LL_ENDL; - } -} -//----------------------------------------------------------------------------- -// processJointNode() -//----------------------------------------------------------------------------- -void LLModelLoader::processJointNode( domNode* pNode, JointTransformMap& jointTransforms ) -{ - if (pNode->getName() == NULL) - { - LL_WARNS() << "nameless node, can't process" << LL_ENDL; - return; - } - - //LL_WARNS()<<"ProcessJointNode# Node:" <<pNode->getName()<<LL_ENDL; - - //1. handle the incoming node - extract out translation via SID or element - - LLMatrix4 workingTransform; - - //Pull out the translate id and store it in the jointTranslations map - daeSIDResolver jointResolverA( pNode, "./translate" ); - domTranslate* pTranslateA = daeSafeCast<domTranslate>( jointResolverA.getElement() ); - daeSIDResolver jointResolverB( pNode, "./location" ); - domTranslate* pTranslateB = daeSafeCast<domTranslate>( jointResolverB.getElement() ); - - //Translation via SID was successful - if ( pTranslateA ) - { - extractTranslation( pTranslateA, workingTransform ); - } - else - if ( pTranslateB ) - { - extractTranslation( pTranslateB, workingTransform ); - } - else - { - //Translation via child from element - daeElement* pTranslateElement = getChildFromElement( pNode, "translate" ); - if ( !pTranslateElement || pTranslateElement->typeID() != domTranslate::ID() ) - { - //LL_WARNS()<< "The found element is not a translate node" <<LL_ENDL; - daeSIDResolver jointResolver( pNode, "./matrix" ); - domMatrix* pMatrix = daeSafeCast<domMatrix>( jointResolver.getElement() ); - if ( pMatrix ) - { - //LL_INFOS()<<"A matrix SID was however found!"<<LL_ENDL; - domFloat4x4 domArray = pMatrix->getValue(); - for ( int i = 0; i < 4; i++ ) - { - for( int j = 0; j < 4; j++ ) - { - workingTransform.mMatrix[i][j] = domArray[i + j*4]; - } - } - } - else - { - LL_WARNS()<< "The found element is not translate or matrix node - most likely a corrupt export!" <<LL_ENDL; - } - } - else - { - extractTranslationViaElement( pTranslateElement, workingTransform ); - } - } - - //Store the working transform relative to the nodes name. - jointTransforms[ pNode->getName() ] = workingTransform; - - //2. handle the nodes children - - //Gather and handle the incoming nodes children - daeTArray< daeSmartRef<daeElement> > childOfChild = pNode->getChildren(); - S32 childOfChildCount = childOfChild.getCount(); - - for (S32 i = 0; i < childOfChildCount; ++i) - { - domNode* pChildNode = daeSafeCast<domNode>( childOfChild[i] ); - if ( pChildNode ) - { - processJointNode( pChildNode, jointTransforms ); - } - } -} -//----------------------------------------------------------------------------- -// getChildFromElement() -//----------------------------------------------------------------------------- -daeElement* LLModelLoader::getChildFromElement( daeElement* pElement, std::string const & name ) -{ - daeElement* pChildOfElement = pElement->getChild( name.c_str() ); - if ( pChildOfElement ) - { - return pChildOfElement; - } - LL_WARNS()<< "Could not find a child [" << name << "] for the element: \"" << pElement->getAttribute("id") << "\"" << LL_ENDL; - return NULL; -} - -void LLModelLoader::processElement( daeElement* element, bool& badElement ) -{ - LLMatrix4 saved_transform = mTransform; - - domTranslate* translate = daeSafeCast<domTranslate>(element); - if (translate) - { - domFloat3 dom_value = translate->getValue(); - - LLMatrix4 translation; - translation.setTranslation(LLVector3(dom_value[0], dom_value[1], dom_value[2])); - - translation *= mTransform; - mTransform = translation; - } - - domRotate* rotate = daeSafeCast<domRotate>(element); - if (rotate) - { - domFloat4 dom_value = rotate->getValue(); - - LLMatrix4 rotation; - rotation.initRotTrans(dom_value[3] * DEG_TO_RAD, LLVector3(dom_value[0], dom_value[1], dom_value[2]), LLVector3(0, 0, 0)); - - rotation *= mTransform; - mTransform = rotation; - } - - domScale* scale = daeSafeCast<domScale>(element); - if (scale) - { - 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(scale_vector); - - scaling *= mTransform; - mTransform = scaling; - } - - domMatrix* matrix = daeSafeCast<domMatrix>(element); - if (matrix) - { - domFloat4x4 dom_value = matrix->getValue(); - - LLMatrix4 matrix_transform; - - for (int i = 0; i < 4; i++) - { - for(int j = 0; j < 4; j++) - { - matrix_transform.mMatrix[i][j] = dom_value[i + j*4]; - } - } - - matrix_transform *= mTransform; - mTransform = matrix_transform; - } - - domInstance_geometry* instance_geo = daeSafeCast<domInstance_geometry>(element); - if (instance_geo) - { - domGeometry* geo = daeSafeCast<domGeometry>(instance_geo->getUrl().getElement()); - if (geo) - { - domMesh* mesh = daeSafeCast<domMesh>(geo->getDescendant(daeElement::matchType(domMesh::ID()))); - if (mesh) - { - LLModel* model = mModel[mesh]; - if (model) - { - LLMatrix4 transformation = mTransform; - - if (mTransform.determinant() < 0) - { //negative scales are not supported - LL_INFOS() << "Negative scale detected, unsupported transform. domInstance_geometry: " << LLModel::getElementLabel(instance_geo) << LL_ENDL; - badElement = true; - } - - std::map<std::string, LLImportMaterial> materials = getMaterials(model, instance_geo); - - // adjust the transformation to compensate for mesh normalization - LLVector3 mesh_scale_vector; - LLVector3 mesh_translation_vector; - model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector); - - LLMatrix4 mesh_translation; - mesh_translation.setTranslation(mesh_translation_vector); - mesh_translation *= transformation; - transformation = mesh_translation; - - LLMatrix4 mesh_scale; - mesh_scale.initScale(mesh_scale_vector); - mesh_scale *= transformation; - transformation = mesh_scale; - - std::string label = getElementLabel(instance_geo); - mScene[transformation].push_back(LLModelInstance(model, label, transformation, materials)); - - stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform); - } - } - } - else - { - LL_INFOS()<<"Unable to resolve geometry URL."<<LL_ENDL; - badElement = true; - } - - } - - domInstance_node* instance_node = daeSafeCast<domInstance_node>(element); - if (instance_node) - { - daeElement* instance = instance_node->getUrl().getElement(); - if (instance) - { - processElement(instance,badElement); - } - } - - //process children - daeTArray< daeSmartRef<daeElement> > children = element->getChildren(); - int childCount = children.getCount(); - for (S32 i = 0; i < childCount; i++) - { - processElement(children[i],badElement); - } - - domNode* node = daeSafeCast<domNode>(element); - if (node) - { //this element was a node, restore transform before processiing siblings - mTransform = saved_transform; - } -} - -std::map<std::string, LLImportMaterial> LLModelLoader::getMaterials(LLModel* model, domInstance_geometry* instance_geo) -{ - std::map<std::string, LLImportMaterial> materials; - for (int i = 0; i < model->mMaterialList.size(); i++) - { - LLImportMaterial import_material; - - domInstance_material* instance_mat = NULL; - - domBind_material::domTechnique_common* technique = - daeSafeCast<domBind_material::domTechnique_common>(instance_geo->getDescendant(daeElement::matchType(domBind_material::domTechnique_common::ID()))); - - if (technique) - { - daeTArray< daeSmartRef<domInstance_material> > inst_materials = technique->getChildrenByType<domInstance_material>(); - for (int j = 0; j < inst_materials.getCount(); j++) - { - std::string symbol(inst_materials[j]->getSymbol()); - - if (symbol == model->mMaterialList[i]) // found the binding - { - instance_mat = inst_materials[j]; - } - } - } - - if (instance_mat) - { - domMaterial* material = daeSafeCast<domMaterial>(instance_mat->getTarget().getElement()); - if (material) - { - domInstance_effect* instance_effect = - daeSafeCast<domInstance_effect>(material->getDescendant(daeElement::matchType(domInstance_effect::ID()))); - if (instance_effect) - { - domEffect* effect = daeSafeCast<domEffect>(instance_effect->getUrl().getElement()); - if (effect) - { - domProfile_COMMON* profile = - daeSafeCast<domProfile_COMMON>(effect->getDescendant(daeElement::matchType(domProfile_COMMON::ID()))); - if (profile) - { - import_material = profileToMaterial(profile); - } - } - } - } - } - - import_material.mBinding = model->mMaterialList[i]; - materials[model->mMaterialList[i]] = import_material; - } - - return materials; -} - -LLImportMaterial LLModelLoader::profileToMaterial(domProfile_COMMON* material) -{ - LLImportMaterial mat; - mat.mFullbright = FALSE; - - daeElement* diffuse = material->getDescendant("diffuse"); - if (diffuse) - { - domCommon_color_or_texture_type_complexType::domTexture* texture = - daeSafeCast<domCommon_color_or_texture_type_complexType::domTexture>(diffuse->getDescendant("texture")); - if (texture) - { - domCommon_newparam_type_Array newparams = material->getNewparam_array(); - for (S32 i = 0; i < newparams.getCount(); i++) - { - domFx_surface_common* surface = newparams[i]->getSurface(); - if (surface) - { - domFx_surface_init_common* init = surface->getFx_surface_init_common(); - if (init) - { - domFx_surface_init_from_common_Array init_from = init->getInit_from_array(); - - if (init_from.getCount() > i) - { - domImage* image = daeSafeCast<domImage>(init_from[i]->getValue().getElement()); - if (image) - { - // we only support init_from now - embedded data will come later - domImage::domInit_from* init = image->getInit_from(); - if (init) - { - mat.mDiffuseMapFilename = cdom::uriToNativePath(init->getValue().str()); - mat.mDiffuseMapLabel = getElementLabel(material); - } - } - } - } - } - } - } - - domCommon_color_or_texture_type_complexType::domColor* color = - daeSafeCast<domCommon_color_or_texture_type_complexType::domColor>(diffuse->getDescendant("color")); - if (color) - { - domFx_color_common domfx_color = color->getValue(); - LLColor4 value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]); - mat.mDiffuseColor = value; - } - } - - daeElement* emission = material->getDescendant("emission"); - if (emission) - { - LLColor4 emission_color = getDaeColor(emission); - if (((emission_color[0] + emission_color[1] + emission_color[2]) / 3.0) > 0.25) - { - mat.mFullbright = TRUE; - } - } - - return mat; -} - -// try to get a decent label for this element -std::string LLModelLoader::getElementLabel(daeElement *element) -{ - // if we have a name attribute, use it - std::string name = element->getAttribute("name"); - if (name.length()) - { - return name; - } - - // if we have an ID attribute, use it - if (element->getID()) - { - return std::string(element->getID()); - } - - // if we have a parent, use it - daeElement* parent = element->getParent(); - if (parent) - { - // if parent has a name, use it - std::string name = parent->getAttribute("name"); - if (name.length()) - { - return name; - } - - // if parent has an ID, use it - if (parent->getID()) - { - return std::string(parent->getID()); - } - } - - // try to use our type - daeString element_name = element->getElementName(); - if (element_name) - { - return std::string(element_name); - } - - // if all else fails, use "object" - return std::string("object"); -} - -LLColor4 LLModelLoader::getDaeColor(daeElement* element) -{ - LLColor4 value; - domCommon_color_or_texture_type_complexType::domColor* color = - daeSafeCast<domCommon_color_or_texture_type_complexType::domColor>(element->getDescendant("color")); - if (color) - { - domFx_color_common domfx_color = color->getValue(); - value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]); - } - - return value; -} - -//----------------------------------------------------------------------------- // LLModelPreview //----------------------------------------------------------------------------- @@ -3125,7 +1181,9 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) , mPelvisZOffset( 0.0f ) , mLegacyRigValid( false ) , mRigValidJointUpload( false ) +, mPhysicsSearchLOD( LLModel::LOD_PHYSICS ) , mResetJoints( false ) +, mModelNoErrors( true ) , mRigParityWithScene( false ) , mLastJointUpdate( false ) { @@ -3170,51 +1228,20 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) glodInit(); - //move into joint mapper class - //1. joints for joint offset verification - mMasterJointList.push_front("mPelvis"); - mMasterJointList.push_front("mTorso"); - mMasterJointList.push_front("mChest"); - mMasterJointList.push_front("mNeck"); - mMasterJointList.push_front("mHead"); - mMasterJointList.push_front("mCollarLeft"); - mMasterJointList.push_front("mShoulderLeft"); - mMasterJointList.push_front("mElbowLeft"); - mMasterJointList.push_front("mWristLeft"); - mMasterJointList.push_front("mCollarRight"); - mMasterJointList.push_front("mShoulderRight"); - mMasterJointList.push_front("mElbowRight"); - mMasterJointList.push_front("mWristRight"); - mMasterJointList.push_front("mHipRight"); - mMasterJointList.push_front("mKneeRight"); - mMasterJointList.push_front("mFootRight"); - mMasterJointList.push_front("mHipLeft"); - mMasterJointList.push_front("mKneeLeft"); - mMasterJointList.push_front("mFootLeft"); - //2. legacy joint list - used to verify rigs that will not be using joint offsets - mMasterLegacyJointList.push_front("mPelvis"); - mMasterLegacyJointList.push_front("mTorso"); - mMasterLegacyJointList.push_front("mChest"); - mMasterLegacyJointList.push_front("mNeck"); - mMasterLegacyJointList.push_front("mHead"); - mMasterLegacyJointList.push_front("mHipRight"); - mMasterLegacyJointList.push_front("mKneeRight"); - mMasterLegacyJointList.push_front("mFootRight"); - mMasterLegacyJointList.push_front("mHipLeft"); - mMasterLegacyJointList.push_front("mKneeLeft"); - mMasterLegacyJointList.push_front("mFootLeft"); - createPreviewAvatar(); } LLModelPreview::~LLModelPreview() { - if (mModelLoader) - { - mModelLoader->mPreview = NULL; - mModelLoader = NULL; - } - //*HACK : *TODO : turn this back on when we understand why this crashes + // glod apparently has internal mem alignment issues that are angering + // the heap-check code in windows, these should be hunted down in that + // TP code, if possible + // + // kernel32.dll!HeapFree() + 0x14 bytes + // msvcr100.dll!free(void * pBlock) Line 51 C + // glod.dll!glodGetGroupParameteriv() + 0x119 bytes + // glod.dll!glodShutdown() + 0x77 bytes + // //glodShutdown(); } @@ -3284,7 +1311,9 @@ U32 LLModelPreview::calcResourceCost() decomp, mFMP->childGetValue("upload_skin").asBoolean(), mFMP->childGetValue("upload_joints").asBoolean(), - TRUE); + TRUE, + FALSE, + instance.mModel->mSubmodelID); num_hulls += decomp.mHull.size(); for (U32 i = 0; i < decomp.mHull.size(); ++i) @@ -3351,29 +1380,12 @@ void LLModelPreview::rebuildUploadData() F32 max_scale = 0.f; - //reorder materials to match mBaseModel - for (U32 i = 0; i < LLModel::NUM_LODS-1; i++) - { - if (mBaseModel.size() == mModel[i].size()) - { - for (U32 j = 0; j < mBaseModel.size(); ++j) - { - - int refFaceCnt = 0; - int modelFaceCnt = 0; - - if ( !mModel[i][j]->matchMaterialOrder(mBaseModel[j], refFaceCnt, modelFaceCnt ) ) - { - setLoadState( LLModelLoader::ERROR_MATERIALS ); - mFMP->childDisable( "calculate_btn" ); - } - } - } - } + BOOL importerDebug = gSavedSettings.getBOOL("ImporterDebug"); + BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching"); for (LLModelLoader::scene::iterator iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter) { //for each transform in scene - LLMatrix4 mat = iter->first; + LLMatrix4 mat = iter->first; // compute position LLVector3 position = LLVector3(0, 0, 0) * mat; @@ -3390,38 +1402,171 @@ void LLModelPreview::rebuildUploadData() mat *= scale_mat; - for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) - { //for each instance with said transform applied - LLModelInstance instance = *model_iter; + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end();) + { //for each instance with said transform applied + LLModelInstance instance = *model_iter++; LLModel* base_model = instance.mModel; - if (base_model) + if (base_model && !requested_name.empty()) { base_model->mRequestedLabel = requested_name; base_model->mMetric = metric; } - S32 idx = 0; - for (idx = 0; idx < mBaseModel.size(); ++idx) - { //find reference instance for this model - if (mBaseModel[idx] == base_model) + for (int i = LLModel::NUM_LODS - 1; i >= LLModel::LOD_IMPOSTOR; i--) + { + LLModel* lod_model = NULL; + if (!legacyMatching) { - break; + // Fill LOD slots by finding matching meshes by label with name extensions + // in the appropriate scene for each LOD. This fixes all kinds of issues + // where the indexed method below fails in spectacular fashion. + // If you don't take the time to name your LOD and PHYS meshes + // with the name of their corresponding mesh in the HIGH LOD, + // then the indexed method will be attempted below. + + LLMatrix4 transform; + + std::string name_to_match = instance.mLabel; + llassert(!name_to_match.empty()); + + int extensionLOD; + if (i != LLModel::LOD_PHYSICS || mModel[LLModel::LOD_PHYSICS].empty()) + { + extensionLOD = i; + } + else + { + //Physics can be inherited from other LODs or loaded, so we need to adjust what extension we are searching for + extensionLOD = mPhysicsSearchLOD; + } + + std::string toAdd; + switch (extensionLOD) + { + case LLModel::LOD_IMPOSTOR: toAdd = "_LOD0"; break; + case LLModel::LOD_LOW: toAdd = "_LOD1"; break; + case LLModel::LOD_MEDIUM: toAdd = "_LOD2"; break; + case LLModel::LOD_PHYSICS: toAdd = "_PHYS"; break; + case LLModel::LOD_HIGH: break; + } + + if (name_to_match.find(toAdd) == -1) + { + name_to_match += toAdd; + } + + FindModel(mScene[i], name_to_match, lod_model, transform); + + if (!lod_model && i != LLModel::LOD_PHYSICS) + { + if (importerDebug) + { + LL_INFOS() << "Search of" << name_to_match << " in LOD" << i << " list failed. Searching for alternative among LOD lists." << LL_ENDL; + } + + int searchLOD = (i > LLModel::LOD_HIGH) ? LLModel::LOD_HIGH : i; + while ((searchLOD <= LLModel::LOD_HIGH) && !lod_model) + { + std::string name_to_match = instance.mLabel; + llassert(!name_to_match.empty()); + + std::string toAdd; + switch (searchLOD) + { + case LLModel::LOD_IMPOSTOR: toAdd = "_LOD0"; break; + case LLModel::LOD_LOW: toAdd = "_LOD1"; break; + case LLModel::LOD_MEDIUM: toAdd = "_LOD2"; break; + case LLModel::LOD_PHYSICS: toAdd = "_PHYS"; break; + case LLModel::LOD_HIGH: break; + } + + if (name_to_match.find(toAdd) == -1) + { + name_to_match += toAdd; + } + + // See if we can find an appropriately named model in LOD 'searchLOD' + // + FindModel(mScene[searchLOD], name_to_match, lod_model, transform); + searchLOD++; + } + } } - } + else + { + // Use old method of index-based association + U32 idx = 0; + for (idx = 0; idx < mBaseModel.size(); ++idx) + { + // find reference instance for this model + if (mBaseModel[idx] == base_model) + { + if (importerDebug) + { + LL_INFOS() << "Attempting to use model index " << idx << " for LOD " << i << " of " << instance.mLabel << LL_ENDL; + } + break; + } + } - if(idx < mBaseModel.size()) - { - for (U32 i = 0; i < LLModel::NUM_LODS; i++) - { //fill LOD slots based on reference model index + // If the model list for the current LOD includes that index... + // if (mModel[i].size() > idx) { - instance.mLOD[i] = mModel[i][idx]; + // Assign that index from the model list for our LOD as the LOD model for this instance + // + lod_model = mModel[i][idx]; + if (importerDebug) + { + LL_INFOS() << "Indexed match of model index " << idx << " at LOD " << i << " to model named " << lod_model->mLabel << LL_ENDL; + } + } + else if (importerDebug) + { + LL_INFOS() << "List of models does not include index " << idx << LL_ENDL; } - else + } + + if (lod_model) + { + if (importerDebug) + { + if (i == LLModel::LOD_PHYSICS) + { + LL_INFOS() << "Assigning collision for " << instance.mLabel << " to match " << lod_model->mLabel << LL_ENDL; + } + else + { + LL_INFOS() << "Assigning LOD" << i << " for " << instance.mLabel << " to found match " << lod_model->mLabel << LL_ENDL; + } + } + instance.mLOD[i] = lod_model; + } + else if (importerDebug) + { + LL_INFOS() << "List of models does not include " << instance.mLabel << LL_ENDL; + } + } + + LLModel* high_lod_model = instance.mLOD[LLModel::LOD_HIGH]; + if (!high_lod_model) + { + setLoadState( LLModelLoader::ERROR_MATERIALS ); + mFMP->childDisable( "calculate_btn" ); + } + else + { + for (U32 i = 0; i < LLModel::NUM_LODS-1; i++) + { + int refFaceCnt = 0; + int modelFaceCnt = 0; + llassert(instance.mLOD[i]); + if (instance.mLOD[i] && !instance.mLOD[i]->matchMaterialOrder(high_lod_model, refFaceCnt, modelFaceCnt ) ) { - instance.mLOD[i] = NULL; + setLoadState( LLModelLoader::ERROR_MATERIALS ); + mFMP->childDisable( "calculate_btn" ); } } } @@ -3430,6 +1575,34 @@ void LLModelPreview::rebuildUploadData() } } + for (U32 lod = 0; lod < LLModel::NUM_LODS-1; lod++) + { + // Search for models that are not included into upload data + // If we found any, that means something we loaded is not a sub-model. + for (U32 model_ind = 0; model_ind < mModel[lod].size(); ++model_ind) + { + bool found_model = false; + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) + { + LLModelInstance& instance = *iter; + if (instance.mLOD[lod] == mModel[lod][model_ind]) + { + found_model = true; + break; + } + } + if (!found_model && mModel[lod][model_ind] && !mModel[lod][model_ind]->mSubmodelID) + { + if (importerDebug) + { + LL_INFOS() << "Model " << mModel[lod][model_ind]->mLabel << " was not used - mismatching lod models." << LL_ENDL; + } + setLoadState( LLModelLoader::ERROR_MATERIALS ); + mFMP->childDisable( "calculate_btn" ); + } + } + } + F32 max_import_scale = (DEFAULT_MAX_PRIM_SCALE-0.1f)/max_scale; F32 max_axis = llmax(mPreviewScale.mV[0], mPreviewScale.mV[1]); @@ -3496,7 +1669,6 @@ void LLModelPreview::saveUploadData(const std::string& filename, bool save_skinw meshes.insert(instance.mModel); std::stringstream str; - LLModel::Decomposition& decomp = instance.mLOD[LLModel::LOD_PHYSICS].notNull() ? instance.mLOD[LLModel::LOD_PHYSICS]->mPhysics : @@ -3509,8 +1681,8 @@ void LLModelPreview::saveUploadData(const std::string& filename, bool save_skinw instance.mLOD[LLModel::LOD_LOW], instance.mLOD[LLModel::LOD_IMPOSTOR], decomp, - save_skinweights, save_joint_positions, FALSE, TRUE); - + save_skinweights, save_joint_positions, + FALSE, TRUE, instance.mModel->mSubmodelID); data["mesh"][instance.mModel->mLocalID] = str.str(); } @@ -3578,13 +1750,28 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable clearGLODGroup(); } - mModelLoader = new LLModelLoader(filename, lod, this, mJointTransformMap, mJointsFromNode ); + mModelLoader = new LLDAELoader( + filename, + lod, + &LLModelPreview::loadedCallback, + &LLModelPreview::lookupJointByName, + &LLModelPreview::loadTextures, + &LLModelPreview::stateChangedCallback, + this, + mJointTransformMap, + mJointsFromNode, + gSavedSettings.getU32("ImporterModelLimit")); if (force_disable_slm) { mModelLoader->mTrySLM = false; } - + else + { + //only try to load from slm if viewer is configured to do so and this is the + //initial model load (not an LoD or physics shape) + mModelLoader->mTrySLM = gSavedSettings.getBOOL("MeshImportUseSLM") && mUploadData.empty(); + } mModelLoader->start(); mFMP->childSetTextArg("status", "[STATUS]", mFMP->getString("status_reading_file")); @@ -3615,6 +1802,7 @@ void LLModelPreview::setPhysicsFromLOD(S32 lod) if (lod >= 0 && lod <= 3) { + mPhysicsSearchLOD = lod; mModel[LLModel::LOD_PHYSICS] = mModel[lod]; mScene[LLModel::LOD_PHYSICS] = mScene[lod]; mLODFile[LLModel::LOD_PHYSICS].clear(); @@ -3634,11 +1822,14 @@ void LLModelPreview::clearIncompatible(S32 lod) return; } + // at this point we don't care about sub-models, + // different amount of sub-models means face count mismatch, not incompatibility + U32 lod_size = countRootModels(mModel[lod]); for (U32 i = 0; i <= LLModel::LOD_HIGH; i++) { //clear out any entries that aren't compatible with this model if (i != lod) { - if (mModel[i].size() != mModel[lod].size()) + if (countRootModels(mModel[i]) != lod_size) { mModel[i].clear(); mScene[i].clear(); @@ -3673,7 +1864,7 @@ void LLModelPreview::clearGLODGroup() } } -void LLModelPreview::loadModelCallback(S32 lod) +void LLModelPreview::loadModelCallback(S32 loaded_lod) { assert_main_thread(); @@ -3686,12 +1877,18 @@ void LLModelPreview::loadModelCallback(S32 lod) if(getLoadState() >= LLModelLoader::ERROR_PARSING) { mLoading = false ; + mModelLoader = NULL; return ; } + // Copy determinations about rig so UI will reflect them + // + setRigValidForJointPositionUpload(mModelLoader->isRigValidForJointPositionUpload()); + setLegacyRigValid(mModelLoader->isLegacyRigValid()); + mModelLoader->loadTextures() ; - if (lod == -1) + if (loaded_lod == -1) { //populate all LoDs from model loader scene mBaseModel.clear(); mBaseScene.clear(); @@ -3723,6 +1920,11 @@ void LLModelPreview::loadModelCallback(S32 lod) //override displayed model with current LoD list_iter->mModel = list_iter->mLOD[lod]; + if (!list_iter->mModel) + { + continue; + } + //add current model to current LoD's model list (LLModel::mLocalID makes a good vector index) S32 idx = list_iter->mModel->mLocalID; @@ -3731,7 +1933,7 @@ void LLModelPreview::loadModelCallback(S32 lod) mModel[lod].resize(idx+1); } - mModel[lod][idx] = list_iter->mModel; + mModel[lod][idx] = list_iter->mModel; if (!list_iter->mModel->mSkinWeights.empty()) { skin_weights = true; @@ -3774,31 +1976,108 @@ void LLModelPreview::loadModelCallback(S32 lod) } else { //only replace given LoD - mModel[lod] = mModelLoader->mModelList; - mScene[lod] = mModelLoader->mScene; - mVertexBuffer[lod].clear(); + mModel[loaded_lod] = mModelLoader->mModelList; + mScene[loaded_lod] = mModelLoader->mScene; + mVertexBuffer[loaded_lod].clear(); - setPreviewLOD(lod); + setPreviewLOD(loaded_lod); - if (lod == LLModel::LOD_HIGH) + if (loaded_lod == LLModel::LOD_HIGH) { //save a copy of the highest LOD for automatic LOD manipulation if (mBaseModel.empty()) { //first time we've loaded a model, auto-gen LoD mGenLOD = true; } - mBaseModel = mModel[lod]; + mBaseModel = mModel[loaded_lod]; clearGLODGroup(); - mBaseScene = mScene[lod]; + mBaseScene = mScene[loaded_lod]; mVertexBuffer[5].clear(); } + else + { + BOOL importerDebug = gSavedSettings.getBOOL("ImporterDebug"); + BOOL legacyMatching = gSavedSettings.getBOOL("ImporterLegacyMatching"); + if (!legacyMatching) + { + if (!mBaseModel.empty()) + { + BOOL name_based = FALSE; + BOOL has_submodels = FALSE; + for (U32 idx = 0; idx < mBaseModel.size(); ++idx) + { + if (mBaseModel[idx]->mSubmodelID) + { // don't do index-based renaming when the base model has submodels + has_submodels = TRUE; + if (importerDebug) + { + LL_INFOS() << "High LOD has submodels" << LL_ENDL; + } + break; + } + } + + for (U32 idx = 0; idx < mModel[loaded_lod].size(); ++idx) + { + std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel); + + LLModel* found_model = NULL; + LLMatrix4 transform; + FindModel(mBaseScene, loaded_name, found_model, transform); + if (found_model) + { // don't rename correctly named models (even if they are placed in a wrong order) + name_based = TRUE; + } + + if (mModel[loaded_lod][idx]->mSubmodelID) + { // don't rename the models when loaded LOD model has submodels + has_submodels = TRUE; + } + } + + if (importerDebug) + { + LL_INFOS() << "Loaded LOD " << loaded_lod << ": correct names" << (name_based ? "" : "NOT ") << "found; submodels " << (has_submodels ? "" : "NOT ") << "found" << LL_ENDL; + } + + if (!name_based && !has_submodels) + { // replace the name of the model loaded for any non-HIGH LOD to match the others (MAINT-5601) + // this actually works like "ImporterLegacyMatching" for this particular LOD + for (U32 idx = 0; idx < mModel[loaded_lod].size() && idx < mBaseModel.size(); ++idx) + { + std::string name = mBaseModel[idx]->mLabel; + std::string loaded_name = stripSuffix(mModel[loaded_lod][idx]->mLabel); + + if (loaded_name != name) + { + switch (loaded_lod) + { + case LLModel::LOD_IMPOSTOR: name += "_LOD0"; break; + case LLModel::LOD_LOW: name += "_LOD1"; break; + case LLModel::LOD_MEDIUM: name += "_LOD2"; break; + case LLModel::LOD_PHYSICS: name += "_PHYS"; break; + case LLModel::LOD_HIGH: break; + } + + if (importerDebug) + { + LL_WARNS() << "Loded model name " << mModel[loaded_lod][idx]->mLabel << " for LOD " << loaded_lod << " doesn't match the base model. Renaming to " << name << LL_ENDL; + } + + mModel[loaded_lod][idx]->mLabel = name; + } + } + } + } + } + } - clearIncompatible(lod); + clearIncompatible(loaded_lod); mDirty = true; - if (lod == LLModel::LOD_HIGH) + if (loaded_lod == LLModel::LOD_HIGH) { resetPreviewTarget(); } @@ -3817,6 +2096,8 @@ void LLModelPreview::loadModelCallback(S32 lod) refresh(); mModelLoadedSignal(); + + mModelLoader = NULL; } void LLModelPreview::resetPreviewTarget() @@ -4165,6 +2446,20 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); mModel[lod][mdl_idx] = new LLModel(volume_params, 0.f); + std::string name = base->mLabel; + + switch (lod) + { + case LLModel::LOD_IMPOSTOR: name += "_LOD0"; break; + case LLModel::LOD_LOW: name += "_LOD1"; break; + case LLModel::LOD_MEDIUM: name += "_LOD2"; break; + case LLModel::LOD_PHYSICS: name += "_PHYS"; break; + case LLModel::LOD_HIGH: break; + } + + mModel[lod][mdl_idx]->mLabel = name; + mModel[lod][mdl_idx]->mSubmodelID = base->mSubmodelID; + GLint* sizes = new GLint[patch_count*2]; glodGetObjectParameteriv(mObject[base], GLOD_PATCH_SIZES, sizes); stop_gloderror(); @@ -4277,17 +2572,6 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim { shader->bind(); } - - /*if (which_lod == -1 && mScene[LLModel::LOD_PHYSICS].empty()) - { //build physics scene - mScene[LLModel::LOD_PHYSICS] = mScene[LLModel::LOD_LOW]; - mModel[LLModel::LOD_PHYSICS] = mModel[LLModel::LOD_LOW]; - - for (U32 i = 1; i < mModel[LLModel::LOD_PHYSICS].size(); ++i) - { - mPhysicsQ.push(mModel[LLModel::LOD_PHYSICS][i]); - } - }*/ } void LLModelPreview::updateStatusMessages() @@ -4304,43 +2588,89 @@ void LLModelPreview::updateStatusMessages() S32 total_verts[LLModel::NUM_LODS]; S32 total_submeshes[LLModel::NUM_LODS]; - for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod) + for (U32 i = 0; i < LLModel::NUM_LODS-1; i++) + { + total_tris[i] = 0; + total_verts[i] = 0; + total_submeshes[i] = 0; + } + + for (LLMeshUploadThread::instance_list::iterator iter = mUploadData.begin(); iter != mUploadData.end(); ++iter) { - //initialize total for this lod to 0 - total_tris[lod] = total_verts[lod] = total_submeshes[lod] = 0; + LLModelInstance& instance = *iter; - for (LLModelLoader::scene::iterator iter = mScene[lod].begin(), endIter = mScene[lod].end(); iter != endIter; ++iter) + LLModel* model_high_lod = instance.mLOD[LLModel::LOD_HIGH]; + if (!model_high_lod) { - for (LLModelLoader::model_instance_list::iterator instance = iter->second.begin(), end_instance = iter->second.end(); instance != end_instance; ++instance) - { - LLModel* model = instance->mModel; - if (model) - { - //for each model in the lod - S32 cur_tris = 0; - S32 cur_verts = 0; - S32 cur_submeshes = model->getNumVolumeFaces(); + setLoadState( LLModelLoader::ERROR_MATERIALS ); + mFMP->childDisable( "calculate_btn" ); + continue; + } - for (S32 j = 0; j < cur_submeshes; ++j) - { //for each submesh (face), add triangles and vertices to current total - const LLVolumeFace& face = model->getVolumeFace(j); - cur_tris += face.mNumIndices/3; - cur_verts += face.mNumVertices; - } + for (U32 i = 0; i < LLModel::NUM_LODS-1; i++) + { + LLModel* lod_model = instance.mLOD[i]; + if (!lod_model) + { + setLoadState( LLModelLoader::ERROR_MATERIALS ); + mFMP->childDisable( "calculate_btn" ); + } + + int refFaceCnt = 0; + int modelFaceCnt = 0; + + if (!lod_model->matchMaterialOrder(model_high_lod, refFaceCnt, modelFaceCnt ) ) + { + setLoadState( LLModelLoader::ERROR_MATERIALS ); + mFMP->childDisable( "calculate_btn" ); + } - //add this model to the lod total - total_tris[lod] += cur_tris; - total_verts[lod] += cur_verts; - total_submeshes[lod] += cur_submeshes; + if (lod_model) + { + //for each model in the lod + S32 cur_tris = 0; + S32 cur_verts = 0; + S32 cur_submeshes = lod_model->getNumVolumeFaces(); - //store this model's counts to asset data - tris[lod].push_back(cur_tris); - verts[lod].push_back(cur_verts); - submeshes[lod].push_back(cur_submeshes); + for (S32 j = 0; j < cur_submeshes; ++j) + { //for each submesh (face), add triangles and vertices to current total + const LLVolumeFace& face = lod_model->getVolumeFace(j); + cur_tris += face.mNumIndices/3; + cur_verts += face.mNumVertices; } + + std::string instance_name = instance.mLabel; + + BOOL importerDebug = gSavedSettings.getBOOL("ImporterDebug"); + if (importerDebug) + { + // Useful for debugging generalized complaints below about total submeshes which don't have enough + // context to address exactly what needs to be fixed to move towards compliance with the rules. + // + LL_INFOS() << "Instance " << lod_model->mLabel << " LOD " << i << " Verts: " << cur_verts << LL_ENDL; + LL_INFOS() << "Instance " << lod_model->mLabel << " LOD " << i << " Tris: " << cur_tris << LL_ENDL; + LL_INFOS() << "Instance " << lod_model->mLabel << " LOD " << i << " Faces: " << cur_submeshes << LL_ENDL; + + LLModel::material_list::iterator mat_iter = lod_model->mMaterialList.begin(); + while (mat_iter != lod_model->mMaterialList.end()) + { + LL_INFOS() << "Instance " << lod_model->mLabel << " LOD " << i << " Material " << *(mat_iter) << LL_ENDL; + mat_iter++; + } + } + + //add this model to the lod total + total_tris[i] += cur_tris; + total_verts[i] += cur_verts; + total_submeshes[i] += cur_submeshes; + + //store this model's counts to asset data + tris[i].push_back(cur_tris); + verts[i].push_back(cur_verts); + submeshes[i].push_back(cur_submeshes); } } - } + } if (mMaxTriangleLimit == 0) { @@ -4354,37 +2684,48 @@ void LLModelPreview::updateStatusMessages() const LLVector4a scale(0.5f); for (U32 i = 0; i < mModel[lod].size() && !has_degenerate; ++i) { //for each model in the lod - if (mModel[lod][i]->mPhysics.mHull.empty()) + if (mModel[lod][i] && mModel[lod][i]->mPhysics.mHull.empty()) { //no decomp exists S32 cur_submeshes = mModel[lod][i]->getNumVolumeFaces(); for (S32 j = 0; j < cur_submeshes && !has_degenerate; ++j) { //for each submesh (face), add triangles and vertices to current total - const LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j); - for (S32 k = 0; k < face.mNumIndices && !has_degenerate; ) + LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j); + for (S32 k = 0; (k < face.mNumIndices) && !has_degenerate; ) { - LLVector4a v1; v1.setMul(face.mPositions[face.mIndices[k++]], scale); - LLVector4a v2; v2.setMul(face.mPositions[face.mIndices[k++]], scale); - LLVector4a v3; v3.setMul(face.mPositions[face.mIndices[k++]], scale); + U16 index_a = face.mIndices[k+0]; + U16 index_b = face.mIndices[k+1]; + U16 index_c = face.mIndices[k+2]; + + LLVector4a v1; v1.setMul(face.mPositions[index_a], scale); + LLVector4a v2; v2.setMul(face.mPositions[index_b], scale); + LLVector4a v3; v3.setMul(face.mPositions[index_c], scale); if (ll_is_degenerate(v1,v2,v3)) { has_degenerate = true; } + else + { + k += 3; + } } } } } } - + mFMP->childSetTextArg("submeshes_info", "[SUBMESHES]", llformat("%d", total_submeshes[LLModel::LOD_HIGH])); std::string mesh_status_na = mFMP->getString("mesh_status_na"); S32 upload_status[LLModel::LOD_HIGH+1]; - bool upload_ok = true; + mModelNoErrors = true; - for (S32 lod = 0; lod <= LLModel::LOD_HIGH; ++lod) + const U32 lod_high = LLModel::LOD_HIGH; + U32 high_submodel_count = mModel[lod_high].size() - countRootModels(mModel[lod_high]); + + for (S32 lod = 0; lod <= lod_high; ++lod) { upload_status[lod] = 0; @@ -4397,7 +2738,7 @@ void LLModelPreview::updateStatusMessages() } else { - if (lod == LLModel::LOD_HIGH) + if (lod == lod_high) { upload_status[lod] = 2; message = "mesh_status_missing_lod"; @@ -4418,8 +2759,6 @@ void LLModelPreview::updateStatusMessages() mFMP->childSetValue(lod_vertices_name[lod], mesh_status_na); } - const U32 lod_high = LLModel::LOD_HIGH; - if (lod != lod_high) { if (total_submeshes[lod] && total_submeshes[lod] != total_submeshes[lod_high]) @@ -4427,6 +2766,13 @@ void LLModelPreview::updateStatusMessages() message = "mesh_status_submesh_mismatch"; upload_status[lod] = 2; } + else if (mModel[lod].size() - countRootModels(mModel[lod]) != high_submodel_count) + {//number of submodels is different, not all faces are matched correctly. + message = "mesh_status_submesh_mismatch"; + upload_status[lod] = 2; + // Note: Submodels in instance were loaded from higher LOD and as result face count + // returns same value and total_submeshes[lod] is identical to high_lod one. + } else if (!tris[lod].empty() && tris[lod].size() != tris[lod_high].size()) { //number of meshes is different message = "mesh_status_mesh_mismatch"; @@ -4447,7 +2793,7 @@ void LLModelPreview::updateStatusMessages() { //too many vertices in this lod message = "mesh_status_too_many_vertices"; - upload_status[lod] = 2; + upload_status[lod] = 1; } } } @@ -4459,7 +2805,7 @@ void LLModelPreview::updateStatusMessages() if (upload_status[lod] >= 2) { - upload_ok = false; + mModelNoErrors = false; } if (lod == mPreviewLOD) @@ -4473,23 +2819,41 @@ 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) + //warn if hulls have more than 256 points in them + BOOL physExceededVertexLimit = FALSE; + for (U32 i = 0; mModelNoErrors && 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) + if (mdl) { - if (mdl->mPhysics.mHull[j].size() > 256) + for (U32 j = 0; j < mdl->mPhysics.mHull.size(); ++j) { - upload_ok = false; + if (mdl->mPhysics.mHull[j].size() > 256) + { + physExceededVertexLimit = TRUE; + LL_INFOS() << "Physical model " << mdl->mLabel << " exceeds vertex per hull limitations." << LL_ENDL; + break; + } } } } + mFMP->childSetVisible("physics_status_message_text", physExceededVertexLimit); + LLIconCtrl* physStatusIcon = mFMP->getChild<LLIconCtrl>("physics_status_message_icon"); + physStatusIcon->setVisible(physExceededVertexLimit); + if (physExceededVertexLimit) + { + mFMP->childSetValue("physics_status_message_text", mFMP->getString("phys_status_vertex_limit_exceeded")); + LLUIImagePtr img = LLUI::getUIImage("ModelImport_Status_Warning"); + physStatusIcon->setImage(img); + } - bool errorStateFromLoader = getLoadState() >= LLModelLoader::ERROR_PARSING ? true : false; + if (getLoadState() >= LLModelLoader::ERROR_PARSING) + { + mModelNoErrors = false; + LL_INFOS() << "Loader returned errors, model can't be uploaded" << LL_ENDL; + } - bool skinAndRigOk = true; bool uploadingSkin = mFMP->childGetValue("upload_skin").asBoolean(); bool uploadingJointPositions = mFMP->childGetValue("upload_joints").asBoolean(); @@ -4497,19 +2861,23 @@ void LLModelPreview::updateStatusMessages() { if ( uploadingJointPositions && !isRigValidForJointPositionUpload() ) { - skinAndRigOk = false; - } + mModelNoErrors = false; + LL_INFOS() << "Invalid rig, there might be issues with uploading Joint positions" << LL_ENDL; + } } - - if(upload_ok && mModelLoader) + + if(mModelNoErrors && mModelLoader) { if(!mModelLoader->areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean()) { - upload_ok = false ; + // Some textures are still loading, prevent upload until they are done + mModelNoErrors = false; } } - if (!upload_ok || errorStateFromLoader || !skinAndRigOk || has_degenerate) + // Todo: investigate use of has_degenerate and include into mModelNoErrors upload blocking mechanics + // current use of has_degenerate won't block upload permanently - later checks will restore the button + if (!mModelNoErrors || has_degenerate) { mFMP->childDisable("ok_btn"); } @@ -4851,7 +3219,8 @@ void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights) LLModel* base_mdl = *base_iter; base_iter++; - for (S32 i = 0, e = mdl->getNumVolumeFaces(); i < e; ++i) + S32 num_faces = mdl->getNumVolumeFaces(); + for (S32 i = 0; i < num_faces; ++i) { const LLVolumeFace &vf = mdl->getVolumeFace(i); U32 num_vertices = vf.mNumVertices; @@ -4946,23 +3315,23 @@ void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights) void LLModelPreview::update() { - if (mDirty) + if (mGenLOD) { - mDirty = false; - mResourceCost = calcResourceCost(); + mGenLOD = false; + genLODs(); refresh(); updateStatusMessages(); } - if (mGenLOD) + if (mDirty) { - mGenLOD = false; - genLODs(); + mDirty = false; + mResourceCost = calcResourceCost(); refresh(); updateStatusMessages(); } - } + //----------------------------------------------------------------------------- // getTranslationForJointOffset() //----------------------------------------------------------------------------- @@ -4996,8 +3365,77 @@ void LLModelPreview::createPreviewAvatar( void ) } else { - LL_INFOS()<<"Failed to create preview avatar for upload model window"<<LL_ENDL; + LL_INFOS() << "Failed to create preview avatar for upload model window" << LL_ENDL; + } +} + +//static +U32 LLModelPreview::countRootModels(LLModelLoader::model_list models) +{ + U32 root_models = 0; + model_list::iterator model_iter = models.begin(); + while (model_iter != models.end()) + { + LLModel* mdl = *model_iter; + if (mdl && mdl->mSubmodelID == 0) + { + root_models++; + } + model_iter++; + } + return root_models; +} + +void LLModelPreview::loadedCallback( + LLModelLoader::scene& scene, + LLModelLoader::model_list& model_list, + S32 lod, + void* opaque) +{ + LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); + if (pPreview && !LLModelPreview::sIgnoreLoadedCallback) + { + pPreview->loadModelCallback(lod); + } +} + +void LLModelPreview::stateChangedCallback(U32 state,void* opaque) +{ + LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); + if (pPreview) + { + pPreview->setLoadState(state); + } +} + +LLJoint* LLModelPreview::lookupJointByName(const std::string& str, void* opaque) +{ + LLModelPreview* pPreview = static_cast< LLModelPreview* >(opaque); + if (pPreview) + { + return pPreview->getPreviewAvatar()->getJoint(str); + } + return NULL; +} + +U32 LLModelPreview::loadTextures(LLImportMaterial& material,void* opaque) +{ + (void)opaque; + + if (material.mDiffuseMapFilename.size()) + { + material.mOpaqueData = new LLPointer< LLViewerFetchedTexture >; + LLPointer< LLViewerFetchedTexture >& tex = (*reinterpret_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData)); + + tex = LLViewerTextureManager::getFetchedTextureFromUrl("file://" + material.mDiffuseMapFilename, FTT_LOCAL_FILE, TRUE, LLGLTexture::BOOST_PREVIEW); + tex->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, TRUE, FALSE, opaque, NULL, FALSE); + tex->forceToSaveRawImage(0, F32_MAX); + material.setDiffuseMap(tex->getID()); // record tex ID + return 1; } + + material.mOpaqueData = NULL; + return 0; } void LLModelPreview::addEmptyFace( LLModel* pTarget ) @@ -5253,23 +3691,9 @@ BOOL LLModelPreview::render() if (regen) { genBuffers(mPreviewLOD, skin_weight); - } - - //make sure material lists all match - for (U32 i = 0; i < LLModel::NUM_LODS-1; i++) - { - if (mBaseModel.size() == mModel[i].size()) { - for (U32 j = 0; j < mBaseModel.size(); ++j) - { - int refFaceCnt = 0; - int modelFaceCnt = 0; - - if ( !mModel[i][j]->matchMaterialOrder(mBaseModel[j], refFaceCnt, modelFaceCnt ) ) - { - mFMP->childDisable( "calculate_btn" ); - } - } + LL_INFOS() << "Vertex Buffer[" << mPreviewLOD << "]" << " is EMPTY!!!" << LL_ENDL; + regen = TRUE; } } @@ -5281,62 +3705,63 @@ BOOL LLModelPreview::render() LLModel* model = instance.mLOD[mPreviewLOD]; - if (!model) - { - continue; - } + if (!model) + { + continue; + } - gGL.pushMatrix(); - LLMatrix4 mat = instance.mTransform; + gGL.pushMatrix(); + LLMatrix4 mat = instance.mTransform; - gGL.multMatrix((GLfloat*) mat.mMatrix); + gGL.multMatrix((GLfloat*) mat.mMatrix); - for (U32 i = 0, e = mVertexBuffer[mPreviewLOD][model].size(); i < e; ++i) - { - LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; - - buffer->setBuffer(type_mask & buffer->getTypeMask()); - if (textures) + U32 num_models = mVertexBuffer[mPreviewLOD][model].size(); + for (U32 i = 0; i < num_models; ++i) { - int materialCnt = instance.mModel->mMaterialList.size(); - if ( i < materialCnt ) + LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; + + buffer->setBuffer(type_mask & buffer->getTypeMask()); + + if (textures) { - const std::string& binding = instance.mModel->mMaterialList[i]; - const LLImportMaterial& material = instance.mMaterial[binding]; + int materialCnt = instance.mModel->mMaterialList.size(); + if ( i < materialCnt ) + { + const std::string& binding = instance.mModel->mMaterialList[i]; + const LLImportMaterial& material = instance.mMaterial[binding]; - gGL.diffuseColor4fv(material.mDiffuseColor.mV); + gGL.diffuseColor4fv(material.mDiffuseColor.mV); - if (material.mDiffuseMap.notNull()) - { - if (material.mDiffuseMap->getDiscardLevel() > -1) + // Find the tex for this material, bind it, and add it to our set + // + LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material); + if (tex) { - gGL.getTexUnit(0)->bind(material.mDiffuseMap, true); - mTextureSet.insert(material.mDiffuseMap.get()); + mTextureSet.insert(tex); } } } - } - else - { - gGL.diffuseColor4f(1,1,1,1); - } - - buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.diffuseColor3f(0.4f, 0.4f, 0.4f); + else + { + gGL.diffuseColor4f(1,1,1,1); + } - if (edges) - { - glLineWidth(3.f); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glLineWidth(1.f); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.diffuseColor3f(0.4f, 0.4f, 0.4f); + + if (edges) + { + glLineWidth(3.f); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glLineWidth(1.f); + } } + gGL.popMatrix(); } - gGL.popMatrix(); - } if (physics) { @@ -5364,97 +3789,99 @@ BOOL LLModelPreview::render() LLModel* model = instance.mLOD[LLModel::LOD_PHYSICS]; - if (!model) - { - continue; - } + if (!model) + { + continue; + } - gGL.pushMatrix(); - LLMatrix4 mat = instance.mTransform; + gGL.pushMatrix(); + LLMatrix4 mat = instance.mTransform; gGL.multMatrix((GLfloat*) mat.mMatrix); - bool render_mesh = true; + bool render_mesh = true; - LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; - if (decomp) - { - LLMutexLock(decomp->mMutex); + LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; + if (decomp) + { + LLMutexLock(decomp->mMutex); - LLModel::Decomposition& physics = model->mPhysics; + LLModel::Decomposition& physics = model->mPhysics; - if (!physics.mHull.empty()) - { - render_mesh = false; + if (!physics.mHull.empty()) + { + render_mesh = false; - if (physics.mMesh.empty()) - { //build vertex buffer for physics mesh - gMeshRepo.buildPhysicsMesh(physics); - } + if (physics.mMesh.empty()) + { //build vertex buffer for physics mesh + gMeshRepo.buildPhysicsMesh(physics); + } - if (!physics.mMesh.empty()) - { //render hull instead of mesh - for (U32 i = 0; i < physics.mMesh.size(); ++i) - { - if (explode > 0.f) + if (!physics.mMesh.empty()) + { //render hull instead of mesh + for (U32 i = 0; i < physics.mMesh.size(); ++i) { - gGL.pushMatrix(); + if (explode > 0.f) + { + gGL.pushMatrix(); - LLVector3 offset = model->mHullCenter[i]-model->mCenterOfHullCenters; - offset *= explode; + LLVector3 offset = model->mHullCenter[i]-model->mCenterOfHullCenters; + offset *= explode; - gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); - } + gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); + } - static std::vector<LLColor4U> hull_colors; + static std::vector<LLColor4U> hull_colors; - if (i+1 >= hull_colors.size()) - { - hull_colors.push_back(LLColor4U(rand()%128+127, rand()%128+127, rand()%128+127, 128)); - } + if (i+1 >= hull_colors.size()) + { + hull_colors.push_back(LLColor4U(rand()%128+127, rand()%128+127, rand()%128+127, 128)); + } - gGL.diffuseColor4ubv(hull_colors[i].mV); - LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions, physics.mMesh[i].mNormals); + gGL.diffuseColor4ubv(hull_colors[i].mV); + LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions, physics.mMesh[i].mNormals); - if (explode > 0.f) - { - gGL.popMatrix(); + if (explode > 0.f) + { + gGL.popMatrix(); + } } } } } - } - - if (render_mesh) - { - if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) - { - genBuffers(LLModel::LOD_PHYSICS, false); - } - for (U32 i = 0; i < mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); ++i) + + if (render_mesh) { - LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i]; + if (mVertexBuffer[LLModel::LOD_PHYSICS].empty()) + { + genBuffers(LLModel::LOD_PHYSICS, false); + } + + U32 num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); + for (U32 i = 0; i < num_models; ++i) + { + LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i]; - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.diffuseColor4f(0.4f, 0.4f, 0.0f, 0.4f); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.diffuseColor4f(0.4f, 0.4f, 0.0f, 0.4f); - buffer->setBuffer(type_mask & buffer->getTypeMask()); - buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); + buffer->setBuffer(type_mask & buffer->getTypeMask()); + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); - gGL.diffuseColor3f(1.f, 1.f, 0.f); + gGL.diffuseColor3f(1.f, 1.f, 0.f); - glLineWidth(2.f); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); + glLineWidth(2.f); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glLineWidth(1.f); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glLineWidth(1.f); + } } - } - gGL.popMatrix(); - } + gGL.popMatrix(); + } glLineWidth(3.f); glPointSize(8.f); @@ -5635,20 +4062,20 @@ BOOL LLModelPreview::render() position[j] = v; } - llassert(model->mMaterialList.size() > i); + llassert(model->mMaterialList.size() > i); const std::string& binding = instance.mModel->mMaterialList[i]; const LLImportMaterial& material = instance.mMaterial[binding]; buffer->setBuffer(type_mask & buffer->getTypeMask()); gGL.diffuseColor4fv(material.mDiffuseColor.mV); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - if (material.mDiffuseMap.notNull()) + + // Find the tex for this material, bind it, and add it to our set + // + LLViewerFetchedTexture* tex = bindMaterialDiffuseTexture(material); + if (tex) { - if (material.mDiffuseMap->getDiscardLevel() > -1) - { - gGL.getTexUnit(0)->bind(material.mDiffuseMap, true); - mTextureSet.insert(material.mDiffuseMap.get()); - } + mTextureSet.insert(tex); } buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); @@ -5763,14 +4190,14 @@ void LLFloaterModelPreview::onReset(void* user_data) LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) user_data; fmp->childDisable("reset_btn"); LLModelPreview* mp = fmp->mModelPreview; - std::string filename = mp->mLODFile[3]; + std::string filename = mp->mLODFile[LLModel::LOD_HIGH]; fmp->resetDisplayOptions(); //reset model preview fmp->initModelPreview(); mp = fmp->mModelPreview; - mp->loadModel(filename,3,true); + mp->loadModel(filename,LLModel::LOD_HIGH,true); } //static @@ -5864,7 +4291,7 @@ void LLFloaterModelPreview::toggleCalculateButton(bool visible) } mUploadBtn->setVisible(!visible); - mUploadBtn->setEnabled(mHasUploadPerm && !mUploadModelUrl.empty()); + mUploadBtn->setEnabled(isModelUploadAllowed()); if (visible) { @@ -5930,7 +4357,7 @@ void LLFloaterModelPreview::handleModelPhysicsFeeReceived() childSetTextArg("price_breakdown", "[MODEL]", llformat("%d", result["upload_price_breakdown"]["model"].asInteger())); childSetVisible("upload_fee", true); childSetVisible("price_breakdown", true); - mUploadBtn->setEnabled(mHasUploadPerm && !mUploadModelUrl.empty()); + mUploadBtn->setEnabled(isModelUploadAllowed()); } void LLFloaterModelPreview::setModelPhysicsFeeErrorStatus(S32 status, const std::string& reason) @@ -5954,6 +4381,16 @@ void LLFloaterModelPreview::onModelUploadFailure() mUploadBtn->setEnabled(true); } +bool LLFloaterModelPreview::isModelUploadAllowed() +{ + bool allow_upload = mHasUploadPerm && !mUploadModelUrl.empty(); + if (mModelPreview) + { + allow_upload &= mModelPreview->mModelNoErrors; + } + return allow_upload; +} + S32 LLFloaterModelPreview::DecompRequest::statusCallback(const char* status, S32 p1, S32 p2) { if (mContinue) @@ -6003,8 +4440,8 @@ void LLFloaterModelPreview::onPermissionsReceived(const LLSD& result) // BAP HACK: handle "" for case that MeshUploadFlag cap is broken. mHasUploadPerm = (("" == upload_status) || ("valid" == upload_status)); - //mUploadBtn->setEnabled(mHasUploadPerm); - mUploadBtn->setEnabled(mHasUploadPerm && !mUploadModelUrl.empty()); + // isModelUploadAllowed() includes mHasUploadPerm + mUploadBtn->setEnabled(isModelUploadAllowed()); getChild<LLTextBox>("warning_title")->setVisible(!mHasUploadPerm); getChild<LLTextBox>("warning_message")->setVisible(!mHasUploadPerm); } diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h index 618748bd4e..7a518c798b 100755 --- a/indra/newview/llfloatermodelpreview.h +++ b/indra/newview/llfloatermodelpreview.h @@ -37,6 +37,8 @@ #include "llviewermenufile.h" #include "llfloatermodeluploadbase.h" +#include "lldaeloader.h" + class LLComboBox; class LLJoint; class LLViewerJointMesh; @@ -45,103 +47,18 @@ class LLTextBox; class LLVertexBuffer; class LLModelPreview; class LLFloaterModelPreview; +class DAE; class daeElement; class domProfile_COMMON; class domInstance_geometry; class domNode; class domTranslate; class domController; +class domSkin; +class domMesh; class LLMenuButton; class LLToggleableMenu; -typedef std::map<std::string, LLMatrix4> JointTransformMap; -typedef std::map<std::string, LLMatrix4>:: iterator JointTransformMapIt; - -const S32 NUM_LOD = 4; - -class LLModelLoader : public LLThread -{ -public: - typedef enum - { - STARTING = 0, - READING_FILE, - CREATING_FACES, - GENERATING_VERTEX_BUFFERS, - GENERATING_LOD, - DONE, - ERROR_PARSING, //basically loading failed - ERROR_MATERIALS, - } eLoadState; - - U32 mState; - std::string mFilename; - S32 mLod; - LLModelPreview* mPreview; - LLMatrix4 mTransform; - BOOL mFirstTransform; - LLVector3 mExtents[2]; - bool mTrySLM; - - std::map<daeElement*, LLPointer<LLModel> > mModel; - - typedef std::vector<LLPointer<LLModel> > model_list; - model_list mModelList; - - typedef std::vector<LLModelInstance> model_instance_list; - - typedef std::map<LLMatrix4, model_instance_list > scene; - - scene mScene; - - typedef std::queue<LLPointer<LLModel> > model_queue; - - //queue of models that need a physics rep - model_queue mPhysicsQ; - - LLModelLoader( std::string filename, S32 lod, LLModelPreview* preview, JointTransformMap& jointMap, - std::deque<std::string>& jointsFromNodes ); - ~LLModelLoader() ; - - virtual void run(); - bool doLoadModel(); - bool loadFromSLM(const std::string& filename); - void loadModelCallback(); - - void loadTextures() ; //called in the main thread. - void processElement(daeElement* element, bool& badElement); - std::map<std::string, LLImportMaterial> getMaterials(LLModel* model, domInstance_geometry* instance_geo); - LLImportMaterial profileToMaterial(domProfile_COMMON* material); - std::string getElementLabel(daeElement *element); - LLColor4 getDaeColor(daeElement* element); - - daeElement* getChildFromElement( daeElement* pElement, std::string const & name ); - - bool isNodeAJoint( domNode* pNode ); - void processJointNode( domNode* pNode, std::map<std::string,LLMatrix4>& jointTransforms ); - void extractTranslation( domTranslate* pTranslate, LLMatrix4& transform ); - void extractTranslationViaElement( daeElement* pTranslateElement, LLMatrix4& transform ); - void extractTranslationViaSID( daeElement* pElement, LLMatrix4& transform ); - - void setLoadState(U32 state); - - void buildJointToNodeMappingFromScene( daeElement* pRoot ); - void processJointToNodeMapping( domNode* pNode ); - void processChildJoints( domNode* pParentNode ); - - //map of avatar joints as named in COLLADA assets to internal joint names - std::map<std::string, std::string> mJointMap; - JointTransformMap& mJointList; - std::deque<std::string>& mJointsFromNode; - - S32 mNumOfFetchingTextures ; //updated in the main thread - bool areTexturesReady() { return !mNumOfFetchingTextures; } //called in the main thread. - -private: - static std::list<LLModelLoader*> sActiveLoaderList; - static bool isAlive(LLModelLoader* loader) ; -}; - class LLFloaterModelPreview : public LLFloaterModelUploadBase { public: @@ -172,6 +89,7 @@ public: BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); /*virtual*/ void onOpen(const LLSD& key); + /*virtual*/ void onClose(bool app_quitting); static void onMouseCaptureLostModelPreview(LLMouseHandler*); static void setUploadAmount(S32 amount) { sUploadAmount = amount; } @@ -210,6 +128,8 @@ public: /*virtual*/ void onModelUploadFailure(); + bool isModelUploadAllowed(); + protected: friend class LLModelPreview; friend class LLMeshFilePicker; @@ -359,21 +279,14 @@ public: void setHasPivot( bool val ) { mHasPivot = val; } void setModelPivot( const LLVector3& pivot ) { mModelPivot = pivot; } - //Determines the viability of an asset to be used as an avatar rig (w or w/o joint upload caps) - void critiqueRigForUploadApplicability( const std::vector<std::string> &jointListFromAsset ); - void critiqueJointToNodeMappingFromScene( void ); //Is a rig valid so that it can be used as a criteria for allowing for uploading of joint positions //Accessors for joint position upload friendly rigs const bool isRigValidForJointPositionUpload( void ) const { return mRigValidJointUpload; } void setRigValidForJointPositionUpload( bool rigValid ) { mRigValidJointUpload = rigValid; } - bool isRigSuitableForJointPositionUpload( const std::vector<std::string> &jointListFromAsset ); - //Determines if a rig is a legacy from the joint list - bool isRigLegacy( const std::vector<std::string> &jointListFromAsset ); + //Accessors for the legacy rigs const bool isLegacyRigValid( void ) const { return mLegacyRigValid; } - void setLegacyRigValid( bool rigValid ) { mLegacyRigValid = rigValid; } - //Verify that a controller matches vertex counts - bool verifyController( domController* pController ); + void setLegacyRigValid( bool rigValid ) { mLegacyRigValid = rigValid; } static void textureLoadedCallback( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata ); @@ -388,6 +301,16 @@ public: LLVector3 getTranslationForJointOffset( std::string joint ); + static bool sIgnoreLoadedCallback; + +protected: + + static void loadedCallback(LLModelLoader::scene& scene,LLModelLoader::model_list& model_list, S32 lod, void* opaque); + static void stateChangedCallback(U32 state, void* opaque); + + static LLJoint* lookupJointByName(const std::string&, void* opaque); + static U32 loadTextures(LLImportMaterial& material, void* opaque); + private: //Utility function for controller vertex compare bool verifyCount( int expected, int result ); @@ -395,6 +318,8 @@ private: void createPreviewAvatar( void ); //Accessor for the dummy avatar LLVOAvatar* getPreviewAvatar( void ) { return mPreviewAvatar; } + // Count amount of original models, excluding sub-models + static U32 countRootModels(LLModelLoader::model_list models); protected: friend class LLModelLoader; @@ -416,13 +341,15 @@ private: LLVector3 mPreviewTarget; LLVector3 mPreviewScale; S32 mPreviewLOD; + S32 mPhysicsSearchLOD; U32 mResourceCost; std::string mLODFile[LLModel::NUM_LODS]; bool mLoading; U32 mLoadState; bool mResetJoints; bool mRigParityWithScene; - + bool mModelNoErrors; + std::map<std::string, bool> mViewOption; //GLOD object parameters (must rebuild object if these change) @@ -459,7 +386,7 @@ private: U32 mMaxTriangleLimit; LLMeshUploadThread::instance_list mUploadData; - std::set<LLViewerFetchedTexture* > mTextureSet; + std::set<LLViewerFetchedTexture * > mTextureSet; //map of vertex buffers to models (one vertex buffer in vector per face in model std::map<LLModel*, std::vector<LLPointer<LLVertexBuffer> > > mVertexBuffer[LLModel::NUM_LODS+1]; @@ -478,10 +405,9 @@ private: bool mLastJointUpdate; - std::deque<std::string> mMasterJointList; - std::deque<std::string> mMasterLegacyJointList; - std::deque<std::string> mJointsFromNode; - JointTransformMap mJointTransformMap; + JointSet mJointsFromNode; + JointTransformMap mJointTransformMap; + LLPointer<LLVOAvatar> mPreviewAvatar; }; diff --git a/indra/newview/llfloaternotificationstabbed.cpp b/indra/newview/llfloaternotificationstabbed.cpp new file mode 100644 index 0000000000..4b5fe4989a --- /dev/null +++ b/indra/newview/llfloaternotificationstabbed.cpp @@ -0,0 +1,575 @@ +/** + * @file llfloaternotificationstabbed.cpp + * @brief + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2015, 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" // must be first include +#include "llfloaternotificationstabbed.h" + +#include "llchiclet.h" +#include "llchicletbar.h" +#include "llflatlistview.h" +#include "llfloaterreg.h" +#include "llnotificationmanager.h" +#include "llnotificationsutil.h" +#include "llscriptfloater.h" +#include "llspeakers.h" +#include "lltoastpanel.h" +#include "lltoastnotifypanel.h" + +//--------------------------------------------------------------------------------- +LLFloaterNotificationsTabbed::LLFloaterNotificationsTabbed(const LLSD& key) : LLTransientDockableFloater(NULL, true, key), + mChannel(NULL), + mSysWellChiclet(NULL), + mGroupInviteMessageList(NULL), + mGroupNoticeMessageList(NULL), + mTransactionMessageList(NULL), + mSystemMessageList(NULL), + mNotificationsSeparator(NULL), + mNotificationsTabContainer(NULL), + NOTIFICATION_TABBED_ANCHOR_NAME("notification_well_panel"), + IM_WELL_ANCHOR_NAME("im_well_panel"), + mIsReshapedByUser(false) + +{ + setOverlapsScreenChannel(true); + mNotificationUpdates.reset(new NotificationTabbedChannel(this)); + mNotificationsSeparator = new LLNotificationSeparator(); +} + +//--------------------------------------------------------------------------------- +BOOL LLFloaterNotificationsTabbed::postBuild() +{ + mGroupInviteMessageList = getChild<LLNotificationListView>("group_invite_notification_list"); + mGroupNoticeMessageList = getChild<LLNotificationListView>("group_notice_notification_list"); + mTransactionMessageList = getChild<LLNotificationListView>("transaction_notification_list"); + mSystemMessageList = getChild<LLNotificationListView>("system_notification_list"); + mNotificationsSeparator->initTaggedList(LLNotificationListItem::getGroupInviteTypes(), mGroupInviteMessageList); + mNotificationsSeparator->initTaggedList(LLNotificationListItem::getGroupNoticeTypes(), mGroupNoticeMessageList); + mNotificationsSeparator->initTaggedList(LLNotificationListItem::getTransactionTypes(), mTransactionMessageList); + mNotificationsSeparator->initUnTaggedList(mSystemMessageList); + mNotificationsTabContainer = getChild<LLTabContainer>("notifications_tab_container"); + + mDeleteAllBtn = getChild<LLButton>("delete_all_button"); + mDeleteAllBtn->setClickedCallback(boost::bind(&LLFloaterNotificationsTabbed::onClickDeleteAllBtn,this)); + + mCollapseAllBtn = getChild<LLButton>("collapse_all_button"); + mCollapseAllBtn->setClickedCallback(boost::bind(&LLFloaterNotificationsTabbed::onClickCollapseAllBtn,this)); + + // get a corresponding channel + initChannel(); + BOOL rv = LLTransientDockableFloater::postBuild(); + + setTitle(getString("title_notification_tabbed_window")); + return rv; +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::setMinimized(BOOL minimize) +{ + LLTransientDockableFloater::setMinimized(minimize); +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::handleReshape(const LLRect& rect, bool by_user) +{ + mIsReshapedByUser |= by_user; // mark floater that it is reshaped by user + LLTransientDockableFloater::handleReshape(rect, by_user); +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::onStartUpToastClick(S32 x, S32 y, MASK mask) +{ + // just set floater visible. Screen channels will be cleared. + setVisible(TRUE); +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::setSysWellChiclet(LLSysWellChiclet* chiclet) +{ + mSysWellChiclet = chiclet; + if(NULL != mSysWellChiclet) + { + mSysWellChiclet->updateWidget(isWindowEmpty()); + } +} + +//--------------------------------------------------------------------------------- +LLFloaterNotificationsTabbed::~LLFloaterNotificationsTabbed() +{ +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::removeItemByID(const LLUUID& id, std::string type) +{ + if(mNotificationsSeparator->removeItemByID(type, id)) + { + if (NULL != mSysWellChiclet) + { + mSysWellChiclet->updateWidget(isWindowEmpty()); + } + reshapeWindow(); + updateNotificationCounters(); + } + else + { + LL_WARNS() << "Unable to remove notification from the list, ID: " << id + << LL_ENDL; + } + + // hide chiclet window if there are no items left + if(isWindowEmpty()) + { + setVisible(FALSE); + } +} + +//--------------------------------------------------------------------------------- +LLPanel * LLFloaterNotificationsTabbed::findItemByID(const LLUUID& id, std::string type) +{ + return mNotificationsSeparator->findItemByID(type, id); +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::initChannel() +{ + LLNotificationsUI::LLScreenChannelBase* channel = LLNotificationsUI::LLChannelManager::getInstance()->findChannelByID( + LLUUID(gSavedSettings.getString("NotificationChannelUUID"))); + mChannel = dynamic_cast<LLNotificationsUI::LLScreenChannel*>(channel); + if(NULL == mChannel) + { + LL_WARNS() << "LLSysWellWindow::initChannel() - could not get a requested screen channel" << LL_ENDL; + } + + if(mChannel) + { + mChannel->addOnStoreToastCallback(boost::bind(&LLFloaterNotificationsTabbed::onStoreToast, this, _1, _2)); + } +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::setVisible(BOOL visible) +{ + if (visible) + { + // when Notification channel is cleared, storable toasts will be added into the list. + clearScreenChannels(); + } + if (visible) + { + if (NULL == getDockControl() && getDockTongue().notNull()) + { + setDockControl(new LLDockControl( + LLChicletBar::getInstance()->getChild<LLView>(getAnchorViewName()), this, + getDockTongue(), LLDockControl::BOTTOM)); + } + } + + // do not show empty window + if (NULL == mNotificationsSeparator || isWindowEmpty()) visible = FALSE; + + LLTransientDockableFloater::setVisible(visible); + + // update notification channel state + initChannel(); // make sure the channel still exists + if(mChannel) + { + mChannel->updateShowToastsState(); + mChannel->redrawToasts(); + } +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::setDocked(bool docked, bool pop_on_undock) +{ + LLTransientDockableFloater::setDocked(docked, pop_on_undock); + + // update notification channel state + if(mChannel) + { + mChannel->updateShowToastsState(); + mChannel->redrawToasts(); + } +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::reshapeWindow() +{ + // update notification channel state + // update on a window reshape is important only when a window is visible and docked + if(mChannel && getVisible() && isDocked()) + { + mChannel->updateShowToastsState(); + } +} + +//--------------------------------------------------------------------------------- +bool LLFloaterNotificationsTabbed::isWindowEmpty() +{ + return mNotificationsSeparator->size() == 0; +} + +//--------------------------------------------------------------------------------- +LLFloaterNotificationsTabbed::NotificationTabbedChannel::NotificationTabbedChannel(LLFloaterNotificationsTabbed* notifications_tabbed_window) + : LLNotificationChannel(LLNotificationChannel::Params().name(notifications_tabbed_window->getPathname())), + mNotificationsTabbedWindow(notifications_tabbed_window) +{ + connectToChannel("Notifications"); + connectToChannel("Group Notifications"); + connectToChannel("Offer"); +} + +// static +//--------------------------------------------------------------------------------- +LLFloaterNotificationsTabbed* LLFloaterNotificationsTabbed::getInstance(const LLSD& key /*= LLSD()*/) +{ + return LLFloaterReg::getTypedInstance<LLFloaterNotificationsTabbed>("notification_well_window", key); +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::updateNotificationCounter(S32 panelIndex, S32 counterValue, std::string stringName) +{ + LLStringUtil::format_map_t string_args; + string_args["[COUNT]"] = llformat("%d", counterValue); + std::string label = getString(stringName, string_args); + mNotificationsTabContainer->setPanelTitle(panelIndex, label); +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::updateNotificationCounters() +{ + updateNotificationCounter(0, mSystemMessageList->size(), "system_tab_title"); + updateNotificationCounter(1, mTransactionMessageList->size(), "transactions_tab_title"); + updateNotificationCounter(2, mGroupInviteMessageList->size(), "group_invitations_tab_title"); + updateNotificationCounter(3, mGroupNoticeMessageList->size(), "group_notices_tab_title"); +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::addItem(LLNotificationListItem::Params p) +{ + // do not add clones + if (mNotificationsSeparator->findItemByID(p.notification_name, p.notification_id)) + return; + LLNotificationListItem* new_item = LLNotificationListItem::create(p); + if (new_item == NULL) + { + return; + } + if (mNotificationsSeparator->addItem(new_item->getNotificationName(), new_item)) + { + mSysWellChiclet->updateWidget(isWindowEmpty()); + reshapeWindow(); + updateNotificationCounters(); + new_item->setOnItemCloseCallback(boost::bind(&LLFloaterNotificationsTabbed::onItemClose, this, _1)); + new_item->setOnItemClickCallback(boost::bind(&LLFloaterNotificationsTabbed::onItemClick, this, _1)); + } + else + { + LL_WARNS() << "Unable to add Notification into the list, notification ID: " << p.notification_id + << ", title: " << new_item->getTitle() + << LL_ENDL; + + new_item->die(); + } +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::closeAll() +{ + // Need to clear notification channel, to add storable toasts into the list. + clearScreenChannels(); + + std::vector<LLNotificationListItem*> items; + mNotificationsSeparator->getItems(items); + std::vector<LLNotificationListItem*>::iterator iter = items.begin(); + for (; iter != items.end(); ++iter) + { + onItemClose(*iter); + } +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::getAllItemsOnCurrentTab(std::vector<LLPanel*>& items) const +{ + switch (mNotificationsTabContainer->getCurrentPanelIndex()) + { + case 0: + mSystemMessageList->getItems(items); + break; + case 1: + mTransactionMessageList->getItems(items); + break; + case 2: + mGroupInviteMessageList->getItems(items); + break; + case 3: + mGroupNoticeMessageList->getItems(items); + break; + default: + break; + } +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::closeAllOnCurrentTab() +{ + // Need to clear notification channel, to add storable toasts into the list. + clearScreenChannels(); + std::vector<LLPanel*> items; + getAllItemsOnCurrentTab(items); + std::vector<LLPanel*>::iterator iter = items.begin(); + for (; iter != items.end(); ++iter) + { + LLNotificationListItem* notify_item = dynamic_cast<LLNotificationListItem*>(*iter); + if (notify_item) + onItemClose(notify_item); + } +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::collapseAllOnCurrentTab() +{ + std::vector<LLPanel*> items; + getAllItemsOnCurrentTab(items); + std::vector<LLPanel*>::iterator iter = items.begin(); + for (; iter != items.end(); ++iter) + { + LLNotificationListItem* notify_item = dynamic_cast<LLNotificationListItem*>(*iter); + if (notify_item) + notify_item->setExpanded(FALSE); + } +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::clearScreenChannels() +{ + // 1 - remove StartUp toast and channel if present + if(!LLNotificationsUI::LLScreenChannel::getStartUpToastShown()) + { + LLNotificationsUI::LLChannelManager::getInstance()->onStartUpToastClose(); + } + + // 2 - remove toasts in Notification channel + if(mChannel) + { + mChannel->removeAndStoreAllStorableToasts(); + } +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::onStoreToast(LLPanel* info_panel, LLUUID id) +{ + LLNotificationListItem::Params p; + p.notification_id = id; + p.title = static_cast<LLToastPanel*>(info_panel)->getTitle(); + LLNotificationPtr notify = mChannel->getToastByNotificationID(id)->getNotification(); + LLSD payload = notify->getPayload(); + p.notification_name = notify->getName(); + p.transaction_id = payload["transaction_id"]; + p.group_id = payload["group_id"]; + p.fee = payload["fee"]; + p.subject = payload["subject"].asString(); + p.message = payload["message"].asString(); + p.sender = payload["sender_name"].asString(); + p.time_stamp = notify->getDate(); + p.received_time = payload["received_time"].asDate(); + p.paid_from_id = payload["from_id"]; + p.paid_to_id = payload["dest_id"]; + p.inventory_offer = payload["inventory_offer"]; + p.notification_priority = notify->getPriority(); + addItem(p); +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::onItemClick(LLNotificationListItem* item) +{ + LLUUID id = item->getID(); + if (item->showPopup()) + { + LLFloaterReg::showInstance("inspect_toast", id); + } + else + { + item->setExpanded(TRUE); + } +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::onItemClose(LLNotificationListItem* item) +{ + LLUUID id = item->getID(); + + if(mChannel) + { + // removeItemByID() is invoked from killToastByNotificationID() and item will removed; + mChannel->killToastByNotificationID(id); + } + else + { + // removeItemByID() should be called one time for each item to remove it from notification well + removeItemByID(id, item->getNotificationName()); + } + +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::onAdd( LLNotificationPtr notify ) +{ + removeItemByID(notify->getID(), notify->getName()); +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::onClickDeleteAllBtn() +{ + closeAllOnCurrentTab(); +} + +//--------------------------------------------------------------------------------- +void LLFloaterNotificationsTabbed::onClickCollapseAllBtn() +{ + collapseAllOnCurrentTab(); +} + +//--------------------------------------------------------------------------------- +void LLNotificationSeparator::initTaggedList(const std::string& tag, LLNotificationListView* list) +{ + mNotificationListMap.insert(notification_list_map_t::value_type(tag, list)); + mNotificationLists.push_back(list); +} + +//--------------------------------------------------------------------------------- +void LLNotificationSeparator::initTaggedList(const std::set<std::string>& tags, LLNotificationListView* list) +{ + std::set<std::string>::const_iterator it = tags.begin(); + for(;it != tags.end();it++) + { + initTaggedList(*it, list); + } +} + +//--------------------------------------------------------------------------------- +void LLNotificationSeparator::initUnTaggedList(LLNotificationListView* list) +{ + mUnTaggedList = list; +} + +//--------------------------------------------------------------------------------- +bool LLNotificationSeparator::addItem(std::string& tag, LLNotificationListItem* item) +{ + notification_list_map_t::iterator it = mNotificationListMap.find(tag); + if (it != mNotificationListMap.end()) + { + return it->second->addNotification(item); + } + else if (mUnTaggedList != NULL) + { + return mUnTaggedList->addNotification(item); + } + return false; +} + +//--------------------------------------------------------------------------------- +bool LLNotificationSeparator::removeItemByID(std::string& tag, const LLUUID& id) +{ + notification_list_map_t::iterator it = mNotificationListMap.find(tag); + if (it != mNotificationListMap.end()) + { + return it->second->removeItemByValue(id); + } + else if (mUnTaggedList != NULL) + { + return mUnTaggedList->removeItemByValue(id); + } + return false; +} + +//--------------------------------------------------------------------------------- +U32 LLNotificationSeparator::size() const +{ + U32 size = 0; + notification_list_list_t::const_iterator it = mNotificationLists.begin(); + for (; it != mNotificationLists.end(); it++) + { + size = size + (*it)->size(); + } + if (mUnTaggedList != NULL) + { + size = size + mUnTaggedList->size(); + } + return size; +} + +//--------------------------------------------------------------------------------- +LLPanel* LLNotificationSeparator::findItemByID(std::string& tag, const LLUUID& id) +{ + notification_list_map_t::iterator it = mNotificationListMap.find(tag); + if (it != mNotificationListMap.end()) + { + return it->second->getItemByValue(id); + } + else if (mUnTaggedList != NULL) + { + return mUnTaggedList->getItemByValue(id); + } + + return NULL; +} + +//static +//--------------------------------------------------------------------------------- +void LLNotificationSeparator::getItemsFromList(std::vector<LLNotificationListItem*>& items, LLNotificationListView* list) +{ + std::vector<LLPanel*> list_items; + list->getItems(list_items); + std::vector<LLPanel*>::iterator it = list_items.begin(); + for (; it != list_items.end(); ++it) + { + LLNotificationListItem* notify_item = dynamic_cast<LLNotificationListItem*>(*it); + if (notify_item) + items.push_back(notify_item); + } +} + +//--------------------------------------------------------------------------------- +void LLNotificationSeparator::getItems(std::vector<LLNotificationListItem*>& items) const +{ + items.clear(); + notification_list_list_t::const_iterator lists_it = mNotificationLists.begin(); + for (; lists_it != mNotificationLists.end(); lists_it++) + { + getItemsFromList(items, *lists_it); + } + if (mUnTaggedList != NULL) + { + getItemsFromList(items, mUnTaggedList); + } +} + +//--------------------------------------------------------------------------------- +LLNotificationSeparator::LLNotificationSeparator() + : mUnTaggedList(NULL) +{} + +//--------------------------------------------------------------------------------- +LLNotificationSeparator::~LLNotificationSeparator() +{} diff --git a/indra/newview/llfloaternotificationstabbed.h b/indra/newview/llfloaternotificationstabbed.h new file mode 100644 index 0000000000..8dd20b18c4 --- /dev/null +++ b/indra/newview/llfloaternotificationstabbed.h @@ -0,0 +1,174 @@ +/** + * @file llfloaternotificationstabbed.h + * @brief + * + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2015, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_FLOATERNOTIFICATIONSTABBED_H +#define LL_FLOATERNOTIFICATIONSTABBED_H + +#include "llimview.h" +#include "llnotifications.h" +#include "llscreenchannel.h" +#include "llsyswellitem.h" +#include "lltransientdockablefloater.h" +#include "llnotificationlistview.h" +#include "lltabcontainer.h" + +class LLAvatarName; +class LLChiclet; +class LLFlatListView; +class LLIMChiclet; +class LLScriptChiclet; +class LLSysWellChiclet; + +class LLNotificationSeparator +{ +public: + LLNotificationSeparator(); + ~LLNotificationSeparator(); + void initTaggedList(const std::string& tag, LLNotificationListView* list); + void initTaggedList(const std::set<std::string>& tags, LLNotificationListView* list); + void initUnTaggedList(LLNotificationListView* list); + bool addItem(std::string& tag, LLNotificationListItem* item); + LLPanel* findItemByID(std::string& tag, const LLUUID& id); + bool removeItemByID(std::string& tag, const LLUUID& id); + void getItems(std::vector<LLNotificationListItem*>& items) const; + U32 size() const; +private: + static void getItemsFromList(std::vector<LLNotificationListItem*>& items, LLNotificationListView* list); + + typedef std::map<std::string, LLNotificationListView*> notification_list_map_t; + notification_list_map_t mNotificationListMap; + typedef std::list<LLNotificationListView*> notification_list_list_t; + notification_list_list_t mNotificationLists; + LLNotificationListView* mUnTaggedList; +}; + +class LLFloaterNotificationsTabbed : public LLTransientDockableFloater +{ +public: + LOG_CLASS(LLFloaterNotificationsTabbed); + + LLFloaterNotificationsTabbed(const LLSD& key); + virtual ~LLFloaterNotificationsTabbed(); + BOOL postBuild(); + + // other interface functions + // check is window empty + bool isWindowEmpty(); + + // Operating with items + void removeItemByID(const LLUUID& id, std::string type); + LLPanel * findItemByID(const LLUUID& id, std::string type); + void updateNotificationCounters(); + void updateNotificationCounter(S32 panelIndex, S32 counterValue, std::string stringName); + + // Operating with outfit + virtual void setVisible(BOOL visible); + + /*virtual*/ void setDocked(bool docked, bool pop_on_undock = true); + // override LLFloater's minimization according to EXT-1216 + /*virtual*/ void setMinimized(BOOL minimize); + /*virtual*/ void handleReshape(const LLRect& rect, bool by_user); + + void onStartUpToastClick(S32 x, S32 y, MASK mask); + /*virtual*/ void onAdd(LLNotificationPtr notify); + + void setSysWellChiclet(LLSysWellChiclet* chiclet); + void closeAll(); + + static LLFloaterNotificationsTabbed* getInstance(const LLSD& key = LLSD()); + + // size constants for the window and for its elements + static const S32 MAX_WINDOW_HEIGHT = 200; + static const S32 MIN_WINDOW_WIDTH = 318; + +private: + // init Window's channel + virtual void initChannel(); + + const std::string NOTIFICATION_TABBED_ANCHOR_NAME; + const std::string IM_WELL_ANCHOR_NAME; + //virtual const std::string& getAnchorViewName() = 0; + + void reshapeWindow(); + + // pointer to a corresponding channel's instance + LLNotificationsUI::LLScreenChannel* mChannel; + + /** + * Reference to an appropriate Well chiclet to release "new message" state. EXT-3147 + */ + LLSysWellChiclet* mSysWellChiclet; + + bool mIsReshapedByUser; + + struct NotificationTabbedChannel : public LLNotificationChannel + { + NotificationTabbedChannel(LLFloaterNotificationsTabbed*); + void onDelete(LLNotificationPtr notify) + { + mNotificationsTabbedWindow->removeItemByID(notify->getID(), notify->getName()); + } + + LLFloaterNotificationsTabbed* mNotificationsTabbedWindow; + }; + + LLNotificationChannelPtr mNotificationUpdates; + virtual const std::string& getAnchorViewName() { return NOTIFICATION_TABBED_ANCHOR_NAME; } + + // init Window's channel + // void initChannel(); + void clearScreenChannels(); + // Operating with items + void addItem(LLNotificationListItem::Params p); + void getAllItemsOnCurrentTab(std::vector<LLPanel*>& items) const; + + // Closes all notifications and removes them from the Notification Well + void closeAllOnCurrentTab(); + void collapseAllOnCurrentTab(); + + void onStoreToast(LLPanel* info_panel, LLUUID id); + void onClickDeleteAllBtn(); + void onClickCollapseAllBtn(); + // Handlers + void onItemClick(LLNotificationListItem* item); + void onItemClose(LLNotificationListItem* item); + // ID of a toast loaded by user (by clicking notification well item) + LLUUID mLoadedToastId; + + LLNotificationListView* mGroupInviteMessageList; + LLNotificationListView* mGroupNoticeMessageList; + LLNotificationListView* mTransactionMessageList; + LLNotificationListView* mSystemMessageList; + LLNotificationSeparator* mNotificationsSeparator; + LLTabContainer* mNotificationsTabContainer; + LLButton* mDeleteAllBtn; + LLButton* mCollapseAllBtn; +}; + +#endif // LL_FLOATERNOTIFICATIONSTABBED_H + + + diff --git a/indra/newview/llfloaterperms.cpp b/indra/newview/llfloaterperms.cpp index 042cf47070..04a818c2c0 100755 --- a/indra/newview/llfloaterperms.cpp +++ b/indra/newview/llfloaterperms.cpp @@ -166,15 +166,59 @@ void LLFloaterPermsDefault::onCommitCopy(const LLSD& user_data) xfer->setEnabled(copyable); } -class LLFloaterPermsResponder : public LLHTTPClient::Responder +const int MAX_HTTP_RETRIES = 5; +LLFloaterPermsRequester* LLFloaterPermsRequester::sPermsRequester = NULL; + +LLFloaterPermsRequester::LLFloaterPermsRequester(const std::string url, const LLSD report, + int maxRetries) + : mRetriesCount(0), mMaxRetries(maxRetries), mUrl(url), mReport(report) +{} + +//static +void LLFloaterPermsRequester::init(const std::string url, const LLSD report, int maxRetries) +{ + if (sPermsRequester == NULL) { + sPermsRequester = new LLFloaterPermsRequester(url, report, maxRetries); + } +} + +//static +void LLFloaterPermsRequester::finalize() +{ + if (sPermsRequester != NULL) + { + delete sPermsRequester; + sPermsRequester = NULL; + } +} + +//static +LLFloaterPermsRequester* LLFloaterPermsRequester::instance() +{ + return sPermsRequester; +} + +void LLFloaterPermsRequester::start() { -public: - LLFloaterPermsResponder(): LLHTTPClient::Responder() {} -private: - static std::string sPreviousReason; + ++mRetriesCount; + LLHTTPClient::post(mUrl, mReport, new LLFloaterPermsResponder()); +} + +bool LLFloaterPermsRequester::retry() +{ + if (++mRetriesCount < mMaxRetries) + { + LLHTTPClient::post(mUrl, mReport, new LLFloaterPermsResponder()); + return true; + } + return false; +} - void httpFailure() +void LLFloaterPermsResponder::httpFailure() +{ + if (!LLFloaterPermsRequester::instance() || !LLFloaterPermsRequester::instance()->retry()) { + LLFloaterPermsRequester::finalize(); const std::string& reason = getReason(); // Do not display the same error more than once in a row if (reason != sPreviousReason) @@ -185,27 +229,27 @@ private: LLNotificationsUtil::add("DefaultObjectPermissions", args); } } +} - void httpSuccess() - { - //const LLSD& content = getContent(); - //dump_sequential_xml("perms_responder_result.xml", content); - - // Since we have had a successful POST call be sure to display the next error message - // even if it is the same as a previous one. - sPreviousReason = ""; - LLFloaterPermsDefault::setCapSent(true); - LL_INFOS("ObjectPermissionsFloater") << "Default permissions successfully sent to simulator" << LL_ENDL; - } -}; +void LLFloaterPermsResponder::httpSuccess() +{ + //const LLSD& content = getContent(); + //dump_sequential_xml("perms_responder_result.xml", content); - std::string LLFloaterPermsResponder::sPreviousReason; + // Since we have had a successful POST call be sure to display the next error message + // even if it is the same as a previous one. + sPreviousReason = ""; + LL_INFOS("ObjectPermissionsFloater") << "Default permissions successfully sent to simulator" << LL_ENDL; +} + +std::string LLFloaterPermsResponder::sPreviousReason; void LLFloaterPermsDefault::sendInitialPerms() { if(!mCapSent) { updateCap(); + setCapSent(true); } } @@ -230,8 +274,8 @@ void LLFloaterPermsDefault::updateCap() LLSDSerialize::toPrettyXML(report, sent_perms_log); LL_CONT << sent_perms_log.str() << LL_ENDL; } - - LLHTTPClient::post(object_url, report, new LLFloaterPermsResponder()); + LLFloaterPermsRequester::init(object_url, report, MAX_HTTP_RETRIES); + LLFloaterPermsRequester::instance()->start(); } else { diff --git a/indra/newview/llfloaterperms.h b/indra/newview/llfloaterperms.h index 2bb0a19dc1..d3b52c1fe5 100755 --- a/indra/newview/llfloaterperms.h +++ b/indra/newview/llfloaterperms.h @@ -29,6 +29,7 @@ #define LL_LLFLOATERPERMPREFS_H #include "llfloater.h" +#include "llhttpclient.h" class LLFloaterPerms : public LLFloater { @@ -89,4 +90,36 @@ private: bool mNextOwnerTransfer[CAT_LAST]; }; +class LLFloaterPermsRequester +{ +public: + LLFloaterPermsRequester(const std::string url, const LLSD report, int maxRetries); + + static void init(const std::string url, const LLSD report, int maxRetries); + static void finalize(); + static LLFloaterPermsRequester* instance(); + + void start(); + bool retry(); + +private: + int mRetriesCount; + int mMaxRetries; + const std::string mUrl; + const LLSD mReport; +public: + static LLFloaterPermsRequester* sPermsRequester; +}; + +class LLFloaterPermsResponder : public LLHTTPClient::Responder +{ +public: + LLFloaterPermsResponder() : LLHTTPClient::Responder() {} +private: + static std::string sPreviousReason; + + void httpFailure(); + void httpSuccess(); +}; + #endif diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index ee4396758e..dac610eda1 100755 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -49,6 +49,7 @@ //#include "llfirstuse.h" #include "llfloaterreg.h" #include "llfloaterabout.h" +#include "llfavoritesbar.h" #include "llfloaterhardwaresettings.h" #include "llfloatersidepanelcontainer.h" #include "llfloaterimsession.h" @@ -1919,7 +1920,7 @@ BOOL LLPanelPreference::postBuild() } if (hasChild("favorites_on_login_check", TRUE)) { - getChild<LLCheckBoxCtrl>("favorites_on_login_check")->setCommitCallback(boost::bind(&showFavoritesOnLoginWarning, _1, _2)); + getChild<LLCheckBoxCtrl>("favorites_on_login_check")->setCommitCallback(boost::bind(&handleFavoritesOnLoginChanged, _1, _2)); bool show_favorites_at_login = LLPanelLogin::getShowFavorites(); getChild<LLCheckBoxCtrl>("favorites_on_login_check")->setValue(show_favorites_at_login); } @@ -2004,11 +2005,15 @@ void LLPanelPreference::showFriendsOnlyWarning(LLUICtrl* checkbox, const LLSD& v } } -void LLPanelPreference::showFavoritesOnLoginWarning(LLUICtrl* checkbox, const LLSD& value) +void LLPanelPreference::handleFavoritesOnLoginChanged(LLUICtrl* checkbox, const LLSD& value) { - if (checkbox && checkbox->getValue()) + if (checkbox) { - LLNotificationsUtil::add("FavoritesOnLogin"); + LLFavoritesOrderStorage::instance().showFavoritesOnLoginChanged(checkbox->getValue().asBoolean()); + if(checkbox->getValue()) + { + LLNotificationsUtil::add("FavoritesOnLogin"); + } } } @@ -2235,6 +2240,11 @@ void LLFloaterPreferenceProxy::onOpen(const LLSD& key) void LLFloaterPreferenceProxy::onClose(bool app_quitting) { + if(app_quitting) + { + cancel(); + } + if (mSocksSettingsDirty) { @@ -2334,6 +2344,11 @@ void LLFloaterPreferenceProxy::onBtnCancel() cancel(); } +void LLFloaterPreferenceProxy::onClickCloseBtn(bool app_quitting) +{ + cancel(); +} + void LLFloaterPreferenceProxy::cancel() { @@ -2344,7 +2359,7 @@ void LLFloaterPreferenceProxy::cancel() LLSD ctrl_value = iter->second; control->set(ctrl_value); } - + mSocksSettingsDirty = false; closeFloater(); } diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 7bf6ae7d79..04e5e37731 100755 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -229,7 +229,7 @@ private: //for "Only friends and groups can call or IM me" static void showFriendsOnlyWarning(LLUICtrl*, const LLSD&); //for "Show my Favorite Landmarks at Login" - static void showFavoritesOnLoginWarning(LLUICtrl* checkbox, const LLSD& value); + static void handleFavoritesOnLoginChanged(LLUICtrl* checkbox, const LLSD& value); typedef std::map<std::string, LLColor4> string_color_map_t; string_color_map_t mSavedColors; @@ -269,6 +269,7 @@ protected: void saveSettings(); void onBtnOk(); void onBtnCancel(); + void onClickCloseBtn(bool app_quitting = false); void onChangeSocksSettings(); diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 2f4d2a93b2..99a5cf8002 100755 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -35,6 +35,7 @@ #include "llassetstorage.h" #include "llavatarnamecache.h" #include "llcachename.h" +#include "llcheckboxctrl.h" #include "llfontgl.h" #include "llimagej2c.h" #include "llinventory.h" @@ -137,6 +138,7 @@ BOOL LLFloaterReporter::postBuild() mOwnerName = LLStringUtil::null; getChild<LLUICtrl>("summary_edit")->setFocus(TRUE); + getChild<LLCheckBoxCtrl>("screen_check")->set(TRUE); mDefaultSummary = getChild<LLUICtrl>("details_edit")->getValue().asString(); diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index 16fa4684ab..afec981d56 100755 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -663,14 +663,20 @@ void LLFloaterSnapshot::Impl::onCommitFreezeFrame(LLUICtrl* ctrl, void* data) { LLCheckBoxCtrl* check_box = (LLCheckBoxCtrl*)ctrl; LLFloaterSnapshot *view = (LLFloaterSnapshot *)data; + LLSnapshotLivePreview* previewp = getPreviewView(view); - if (!view || !check_box) + if (!view || !check_box || !previewp) { return; } gSavedSettings.setBOOL("UseFreezeFrame", check_box->get()); + if (check_box->get()) + { + previewp->prepareFreezeFrame(); + } + updateLayout(view); } diff --git a/indra/newview/llfloatertos.cpp b/indra/newview/llfloatertos.cpp index c1c21c593e..ae33acb842 100755 --- a/indra/newview/llfloatertos.cpp +++ b/indra/newview/llfloatertos.cpp @@ -141,6 +141,12 @@ BOOL LLFloaterTOS::postBuild() // Don't use the start_url parameter for this browser instance -- it may finish loading before we get to add our observer. // Store the URL separately and navigate here instead. web_browser->navigateTo( getString( "loading_url" ) ); + LLPluginClassMedia* media_plugin = web_browser->getMediaPlugin(); + if (media_plugin) + { + // All links from tos_html should be opened in external browser + media_plugin->setOverrideClickTarget("_external"); + } } return TRUE; diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index 1b1c24b19a..ece3e10faa 100755 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -284,12 +284,12 @@ BOOL LLFloaterWorldMap::postBuild() LLComboBox *avatar_combo = getChild<LLComboBox>("friend combo"); avatar_combo->selectFirstItem(); avatar_combo->setPrearrangeCallback( boost::bind(&LLFloaterWorldMap::onAvatarComboPrearrange, this) ); - avatar_combo->setTextEntryCallback( boost::bind(&LLFloaterWorldMap::onComboTextEntry, this) ); + avatar_combo->setTextChangedCallback( boost::bind(&LLFloaterWorldMap::onComboTextEntry, this) ); mListFriendCombo = dynamic_cast<LLCtrlListInterface *>(avatar_combo); LLSearchEditor *location_editor = getChild<LLSearchEditor>("location"); location_editor->setFocusChangedCallback(boost::bind(&LLFloaterWorldMap::onLocationFocusChanged, this, _1)); - location_editor->setKeystrokeCallback( boost::bind(&LLFloaterWorldMap::onSearchTextEntry, this)); + location_editor->setTextChangedCallback( boost::bind(&LLFloaterWorldMap::onSearchTextEntry, this)); getChild<LLScrollListCtrl>("search_results")->setDoubleClickCallback( boost::bind(&LLFloaterWorldMap::onClickTeleportBtn, this)); mListSearchResults = childGetListInterface("search_results"); @@ -297,7 +297,7 @@ BOOL LLFloaterWorldMap::postBuild() LLComboBox *landmark_combo = getChild<LLComboBox>( "landmark combo"); landmark_combo->selectFirstItem(); landmark_combo->setPrearrangeCallback( boost::bind(&LLFloaterWorldMap::onLandmarkComboPrearrange, this) ); - landmark_combo->setTextEntryCallback( boost::bind(&LLFloaterWorldMap::onComboTextEntry, this) ); + landmark_combo->setTextChangedCallback( boost::bind(&LLFloaterWorldMap::onComboTextEntry, this) ); mListLandmarkCombo = dynamic_cast<LLCtrlListInterface *>(landmark_combo); mCurZoomVal = log(LLWorldMapView::sMapScale)/log(2.f); diff --git a/indra/newview/llfolderviewmodelinventory.cpp b/indra/newview/llfolderviewmodelinventory.cpp index 44eda4d6c0..e9f80b795a 100755 --- a/indra/newview/llfolderviewmodelinventory.cpp +++ b/indra/newview/llfolderviewmodelinventory.cpp @@ -109,6 +109,29 @@ bool LLFolderViewModelInventory::contentsReady() return !LLInventoryModelBackgroundFetch::instance().folderFetchActive(); } +bool LLFolderViewModelInventory::isFolderComplete(LLFolderViewFolder* folder) +{ + LLFolderViewModelItemInventory* modelp = static_cast<LLFolderViewModelItemInventory*>(folder->getViewModelItem()); + LLUUID cat_id = modelp->getUUID(); + if (cat_id.isNull()) + { + return false; + } + LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); + if (cat) + { + // don't need to check version - descendents_server == -1 if we have no data + S32 descendents_server = cat->getDescendentCount(); + S32 descendents_actual = cat->getViewerDescendentCount(); + if (descendents_server == descendents_actual + || (descendents_actual > 0 && descendents_server == -1)) // content was loaded in previous session + { + return true; + } + } + return false; +} + void LLFolderViewModelItemInventory::requestSort() { LLFolderViewModelItemCommon::requestSort(); diff --git a/indra/newview/llfolderviewmodelinventory.h b/indra/newview/llfolderviewmodelinventory.h index 0516fc3b4b..dea54cbe57 100755 --- a/indra/newview/llfolderviewmodelinventory.h +++ b/indra/newview/llfolderviewmodelinventory.h @@ -115,6 +115,7 @@ public: void sort(LLFolderViewFolder* folder); bool contentsReady(); + bool isFolderComplete(LLFolderViewFolder* folder); bool startDrag(std::vector<LLFolderViewModelItem*>& items); private: diff --git a/indra/newview/llgroupmgr.cpp b/indra/newview/llgroupmgr.cpp index 86f9da6318..4d92fee04f 100755 --- a/indra/newview/llgroupmgr.cpp +++ b/indra/newview/llgroupmgr.cpp @@ -239,7 +239,8 @@ LLGroupMgrGroupData::LLGroupMgrGroupData(const LLUUID& id) : mRoleMemberDataComplete(false), mGroupPropertiesDataComplete(false), mPendingRoleMemberRequest(false), - mAccessTime(0.0f) + mAccessTime(0.0f), + mPendingBanRequest(false) { mMemberVersion.generate(); } @@ -761,8 +762,69 @@ void LLGroupMgrGroupData::removeBanEntry(const LLUUID& ban_id) mBanList.erase(ban_id); } +void LLGroupMgrGroupData::banMemberById(const LLUUID& participant_uuid) +{ + if (!mMemberDataComplete || + !mRoleDataComplete || + !(mRoleMemberDataComplete && mMembers.size())) + { + LL_WARNS() << "No Role-Member data yet, setting ban request to pending." << LL_ENDL; + mPendingBanRequest = true; + mPendingBanMemberID = participant_uuid; + + if (!mMemberDataComplete) + { + LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mID); + } + + if (!mRoleDataComplete) + { + LLGroupMgr::getInstance()->sendGroupRoleDataRequest(mID); + } + + return; + } + + LLGroupMgrGroupData::member_list_t::iterator mi = mMembers.find((participant_uuid)); + if (mi == mMembers.end()) + { + if (!mPendingBanRequest) + { + mPendingBanRequest = true; + mPendingBanMemberID = participant_uuid; + LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mID); // member isn't in members list, request reloading + } + else + { + mPendingBanRequest = false; + } + return; + } + mPendingBanRequest = false; + + LLGroupMemberData* member_data = (*mi).second; + if (member_data && member_data->isInRole(mOwnerRole)) + { + return; // can't ban group owner + } + + std::vector<LLUUID> ids; + ids.push_back(participant_uuid); + + LLGroupBanData ban_data; + createBanEntry(participant_uuid, ban_data); + LLGroupMgr::getInstance()->sendGroupBanRequest(LLGroupMgr::REQUEST_POST, mID, LLGroupMgr::BAN_CREATE, ids); + LLGroupMgr::getInstance()->sendGroupMemberEjects(mID, ids); + LLGroupMgr::getInstance()->sendGroupMembersRequest(mID); + LLSD args; + std::string name; + gCacheName->getFullName(participant_uuid, name); + args["AVATAR_NAME"] = name; + args["GROUP_NAME"] = mName; + LLNotifications::instance().add(LLNotification::Params("EjectAvatarFromGroup").substitutions(args)); +} // // LLGroupMgr @@ -1245,6 +1307,11 @@ void LLGroupMgr::processGroupRoleMembersReply(LLMessageSystem* msg, void** data) group_datap->mChanged = TRUE; LLGroupMgr::getInstance()->notifyObservers(GC_ROLE_MEMBER_DATA); + + if (group_datap->mPendingBanRequest) + { + group_datap->banMemberById(group_datap->mPendingBanMemberID); + } } // static @@ -1993,8 +2060,6 @@ void LLGroupMgr::processGroupBanRequest(const LLSD& content) LLGroupMgr::getInstance()->notifyObservers(GC_BANLIST); } - - // Responder class for capability group management class GroupMemberDataResponder : public LLHTTPClient::Responder { @@ -2083,11 +2148,6 @@ void LLGroupMgr::processCapGroupMembersRequest(const LLSD& content) return; } - // If we have no members, there's no reason to do anything else - S32 num_members = content["member_count"]; - if(num_members < 1) - return; - LLUUID group_id = content["group_id"].asUUID(); LLGroupMgrGroupData* group_datap = LLGroupMgr::getInstance()->getGroupData(group_id); @@ -2097,6 +2157,18 @@ void LLGroupMgr::processCapGroupMembersRequest(const LLSD& content) return; } + // If we have no members, there's no reason to do anything else + S32 num_members = content["member_count"]; + if (num_members < 1) + { + LL_INFOS("GrpMgr") << "Received empty group members list for group id: " << group_id.asString() << LL_ENDL; + // Set mMemberDataComplete for correct handling of empty responses. See MAINT-5237 + group_datap->mMemberDataComplete = true; + group_datap->mChanged = TRUE; + LLGroupMgr::getInstance()->notifyObservers(GC_MEMBER_DATA); + return; + } + group_datap->mMemberCount = num_members; LLSD member_list = content["members"]; diff --git a/indra/newview/llgroupmgr.h b/indra/newview/llgroupmgr.h index 2e94e8d9a0..5307c4de92 100755 --- a/indra/newview/llgroupmgr.h +++ b/indra/newview/llgroupmgr.h @@ -149,7 +149,7 @@ public: const uuid_vec_t& getRoleMembers() const { return mMemberIDs; } S32 getMembersInRole(uuid_vec_t members, BOOL needs_sort = TRUE); - S32 getTotalMembersInRole() { return mMemberIDs.size(); } + S32 getTotalMembersInRole() { return mMemberCount ? mMemberCount : mMemberIDs.size(); } //FIXME: Returns 0 for Everyone role when Member list isn't yet loaded, see MAINT-5225 LLRoleData getRoleData() const { return mRoleData; } void setRoleData(LLRoleData data) { mRoleData = data; } @@ -269,8 +269,8 @@ public: void createBanEntry(const LLUUID& ban_id, const LLGroupBanData& ban_data = LLGroupBanData()); void removeBanEntry(const LLUUID& ban_id); + void banMemberById(const LLUUID& participant_uuid); - public: typedef std::map<LLUUID,LLGroupMemberData*> member_list_t; typedef std::map<LLUUID,LLGroupRoleData*> role_list_t; @@ -302,6 +302,9 @@ public: S32 mMemberCount; S32 mRoleCount; + bool mPendingBanRequest; + LLUUID mPendingBanMemberID; + protected: void sendRoleChanges(); void cancelRoleChanges(); diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 605f71f412..c990eda074 100755 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -121,7 +121,7 @@ bool isAddAction(const std::string& action) bool isRemoveAction(const std::string& action) { - return ("take_off" == action || "detach" == action || "deactivate" == action); + return ("take_off" == action || "detach" == action); } bool isMarketplaceCopyAction(const std::string& action) @@ -777,14 +777,6 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, { items.push_back(std::string("Marketplace Separator")); - if (gMenuHolder->getChild<LLView>("MerchantOutbox")->getVisible()) - { - items.push_back(std::string("Merchant Copy")); - if (!canListOnOutboxNow()) - { - disabled_items.push_back(std::string("Merchant Copy")); - } - } if (gMenuHolder->getChild<LLView>("MarketplaceListings")->getVisible()) { items.push_back(std::string("Marketplace Copy")); @@ -915,15 +907,7 @@ void LLInvFVBridge::addDeleteContextMenuOptions(menuentry_vec_t &items, return; } - // "Remove link" and "Delete" are the same operation. - if (obj && obj->getIsLinkType() && !get_is_item_worn(mUUID)) - { - items.push_back(std::string("Remove Link")); - } - else - { - items.push_back(std::string("Delete")); - } + items.push_back(std::string("Delete")); if (!isItemRemovable()) { @@ -1714,16 +1698,6 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) folder_view_itemp->getViewModelItem()->pasteLinkFromClipboard(); return; } - else if (isMarketplaceCopyAction(action)) - { - LL_INFOS() << "Copy item to marketplace action!" << LL_ENDL; - - LLInventoryItem* itemp = model->getItem(mUUID); - if (!itemp) return; - - const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); - copy_item_to_outbox(itemp, outbox_id, LLUUID::null, LLToolDragAndDrop::getOperationId()); - } else if (("move_to_marketplace_listings" == action) || ("copy_to_marketplace_listings" == action) || ("copy_or_move_to_marketplace_listings" == action)) { LLInventoryItem* itemp = model->getItem(mUUID); @@ -1810,7 +1784,9 @@ void LLItemBridge::restoreItem() if(item) { LLInventoryModel* model = getInventoryModel(); - const LLUUID new_parent = model->findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(item->getType())); + bool is_snapshot = (item->getInventoryType() == LLInventoryType::IT_SNAPSHOT); + + const LLUUID new_parent = model->findCategoryUUIDForType(is_snapshot? LLFolderType::FT_SNAPSHOT_CATEGORY : LLFolderType::assetTypeToFolderType(item->getType())); // do not restamp on restore. LLInvFVBridge::changeItemParent(model, item, new_parent, FALSE); } @@ -2437,7 +2413,8 @@ BOOL LLFolderBridge::isClipboardPasteableAsLink() const BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, BOOL drop, std::string& tooltip_msg, - BOOL user_confirm) + BOOL is_link, + BOOL user_confirm) { LLInventoryModel* model = getInventoryModel(); @@ -2483,6 +2460,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, const BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id); const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); const BOOL move_is_into_outfit = move_is_into_my_outfits || (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT); + const BOOL move_is_into_current_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_CURRENT_OUTFIT); const BOOL move_is_into_landmarks = (mUUID == landmarks_id) || model->isObjectDescendentOf(mUUID, landmarks_id); //-------------------------------------------------------------------------------- @@ -2520,8 +2498,18 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } if (is_movable && move_is_into_outfit) { + if((mUUID == my_outifts_id) || (getCategory() && getCategory()->getPreferredType() == LLFolderType::FT_NONE)) + { + is_movable = ((inv_cat->getPreferredType() == LLFolderType::FT_NONE) || (inv_cat->getPreferredType() == LLFolderType::FT_OUTFIT)); + } + else + { + is_movable = false; + } + } + if(is_movable && move_is_into_current_outfit && is_link) + { is_movable = FALSE; - // tooltip? } if (is_movable && (mUUID == model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE))) { @@ -2705,9 +2693,11 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } } } + // if target is current outfit folder we use link if (move_is_into_current_outfit && - inv_cat->getPreferredType() == LLFolderType::FT_NONE) + (inv_cat->getPreferredType() == LLFolderType::FT_NONE || + inv_cat->getPreferredType() == LLFolderType::FT_OUTFIT)) { // traverse category and add all contents to currently worn. BOOL append = true; @@ -3296,16 +3286,6 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) removeSystemFolder(); } #endif - else if (isMarketplaceCopyAction(action)) - { - LL_INFOS() << "Copy folder to marketplace action!" << LL_ENDL; - - LLInventoryCategory * cat = gInventory.getCategory(mUUID); - if (!cat) return; - - const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); - copy_folder_to_outbox(cat, outbox_id, cat->getUUID(), LLToolDragAndDrop::getOperationId()); - } else if (("move_to_marketplace_listings" == action) || ("copy_to_marketplace_listings" == action) || ("copy_or_move_to_marketplace_listings" == action)) { LLInventoryCategory * cat = gInventory.getCategory(mUUID); @@ -4141,7 +4121,7 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop, LLInventoryCategory* linked_category = gInventory.getCategory(inv_item->getLinkedUUID()); if (linked_category) { - accept = dragCategoryIntoFolder((LLInventoryCategory*)linked_category, drop, tooltip_msg); + accept = dragCategoryIntoFolder((LLInventoryCategory*)linked_category, drop, tooltip_msg, TRUE); } } else @@ -4561,7 +4541,7 @@ void LLFolderBridge::callback_dropCategoryIntoFolder(const LLSD& notification, c if (option == 0) // YES { std::string tooltip_msg; - dragCategoryIntoFolder(inv_category, TRUE, tooltip_msg, FALSE); + dragCategoryIntoFolder(inv_category, TRUE, tooltip_msg, FALSE, FALSE); } } @@ -5760,7 +5740,7 @@ void LLGestureBridge::performAction(LLInventoryModel* model, std::string action) gInventory.updateItem(item); gInventory.notifyObservers(); } - else if (isRemoveAction(action)) + else if ("deactivate" == action || isRemoveAction(action)) { LLGestureMgr::instance().deactivateGesture(mUUID); @@ -6382,12 +6362,8 @@ void LLWearableBridge::performAction(LLInventoryModel* model, std::string action void LLWearableBridge::openItem() { - LLViewerInventoryItem* item = getItem(); - - if (item) - { - LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel()); - } + performAction(getInventoryModel(), + get_is_item_worn(mUUID) ? "take_off" : "wear"); } void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags) diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 7e7cf9c7dd..03e19cc4da 100755 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -274,7 +274,7 @@ public: {} BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop, std::string& tooltip_msg, BOOL user_confirm = TRUE); - BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop, std::string& tooltip_msg, BOOL user_confirm = TRUE); + BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop, std::string& tooltip_msg, BOOL is_link = FALSE, BOOL user_confirm = TRUE); void callback_dropItemIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryItem* inv_item); void callback_dropCategoryIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryCategory* inv_category); diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 990343c205..6aaf45c35d 100755 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -2473,7 +2473,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root { LLSD args; args["QUESTION"] = LLTrans::getString(root->getSelectedCount() > 1 ? "DeleteItems" : "DeleteItem"); - LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&onItemsRemovalConfirmation, _1, _2, root->getHandle())); + LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&LLInventoryAction::onItemsRemovalConfirmation, _1, _2, root->getHandle())); // Note: marketplace listings will be updated in the callback if delete confirmed return; } diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 6ae8fd0f13..b93bf9a163 100755 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -465,9 +465,9 @@ struct LLInventoryAction static void removeItemFromDND(LLFolderView* root); private: - static void buildMarketplaceFolders(LLFolderView* root); - static void updateMarketplaceFolders(); - static std::list<LLUUID> sMarketplaceFolders; // Marketplace folders that will need update once the action is completed + static void buildMarketplaceFolders(LLFolderView* root); + static void updateMarketplaceFolders(); + static std::list<LLUUID> sMarketplaceFolders; // Marketplace folders that will need update once the action is completed }; diff --git a/indra/newview/lllocalbitmaps.cpp b/indra/newview/lllocalbitmaps.cpp index 4e9947fca0..46c1ffa789 100755 --- a/indra/newview/lllocalbitmaps.cpp +++ b/indra/newview/lllocalbitmaps.cpp @@ -515,7 +515,7 @@ void LLLocalBitmap::updateUserSculpts(LLUUID old_id, LLUUID new_id) { LLSculptParams* old_params = (LLSculptParams*)object->getParameterEntry(LLNetworkData::PARAMS_SCULPT); LLSculptParams new_params(*old_params); - new_params.setSculptTexture(new_id); + new_params.setSculptTexture(new_id, (*old_params).getSculptType()); object->setParameterEntry(LLNetworkData::PARAMS_SCULPT, new_params, TRUE); } } diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp index cf04c5f7a5..8d21fda8f9 100755 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -862,14 +862,11 @@ void LLLocationInputCtrl::refreshParcelIcons() bool see_avs = current_parcel->getSeeAVs(); bool pathfinding_dynamic_enabled = agent_region->dynamicPathfindingEnabled(); - bool is_parcel_owner = (gAgent.getID() == current_parcel->getOwnerID()); - bool allow_group_modify = (gAgent.isInGroup(current_parcel->getGroupID()) && current_parcel->getAllowGroupModify()); - // Most icons are "block this ability" mParcelIcon[VOICE_ICON]->setVisible( !allow_voice ); mParcelIcon[FLY_ICON]->setVisible( !allow_fly ); mParcelIcon[PUSH_ICON]->setVisible( !allow_push ); - mParcelIcon[BUILD_ICON]->setVisible( !allow_build && !is_parcel_owner && !allow_group_modify ); + mParcelIcon[BUILD_ICON]->setVisible( !allow_build ); mParcelIcon[SCRIPTS_ICON]->setVisible( !allow_scripts ); mParcelIcon[DAMAGE_ICON]->setVisible( allow_damage ); mParcelIcon[PATHFINDING_DIRTY_ICON]->setVisible(mIsNavMeshDirty); diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index 7ddacf3033..4116e38f11 100755 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -804,6 +804,22 @@ bool LLLogChat::isNearbyTranscriptExist() return false; } +bool LLLogChat::isAdHocTranscriptExist(std::string file_name) +{ + std::vector<std::string> list_of_transcriptions; + LLLogChat::getListOfTranscriptFiles(list_of_transcriptions); + + file_name = makeLogFileName(file_name); + BOOST_FOREACH(std::string& transcript_file_name, list_of_transcriptions) + { + if (transcript_file_name == file_name) + { + return true; + } + } + return false; +} + //*TODO mark object's names in a special way so that they will be distinguishable form avatar name //which are more strict by its nature (only firstname and secondname) //Example, an object's name can be written like "Object <actual_object's_name>" diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h index ca597599dd..6022e539a9 100755 --- a/indra/newview/lllogchat.h +++ b/indra/newview/lllogchat.h @@ -120,6 +120,7 @@ public: static void deleteTranscripts(); static bool isTranscriptExist(const LLUUID& avatar_id, bool is_group=false); static bool isNearbyTranscriptExist(); + static bool isAdHocTranscriptExist(std::string file_name); static bool historyThreadsFinished(LLUUID session_id); static LLLoadHistoryThread* getLoadHistoryThread(LLUUID session_id); diff --git a/indra/newview/llmanipscale.cpp b/indra/newview/llmanipscale.cpp index 407613d32c..85faa70552 100755 --- a/indra/newview/llmanipscale.cpp +++ b/indra/newview/llmanipscale.cpp @@ -1677,12 +1677,12 @@ void LLManipScale::renderSnapGuides(const LLBBox& bbox) F32 alpha = (1.f - (1.f * ((F32)llabs(i) / (F32)num_ticks_per_side1))); LLVector3 tick_pos = mScaleCenter + (mScaleDir * (grid_multiple1 + i) * smallest_subdivision1); - F32 cur_subdivisions = llclamp(getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit1, mTickPixelSpacing1), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel); - - if (fmodf((F32)(i + sub_div_offset_1), (sGridMaxSubdivisionLevel / cur_subdivisions)) != 0.f) + //No need check this condition to prevent tick position scaling (FIX MAINT-5207/5208) + //F32 cur_subdivisions = llclamp(getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit1, mTickPixelSpacing1), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel); + /*if (fmodf((F32)(i + sub_div_offset_1), (sGridMaxSubdivisionLevel / cur_subdivisions)) != 0.f) { continue; - } + }*/ F32 tick_scale = 1.f; for (F32 division_level = sGridMaxSubdivisionLevel; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f) @@ -1710,12 +1710,12 @@ void LLManipScale::renderSnapGuides(const LLBBox& bbox) F32 alpha = (1.f - (1.f * ((F32)llabs(i) / (F32)num_ticks_per_side2))); LLVector3 tick_pos = mScaleCenter + (mScaleDir * (grid_multiple2 + i) * smallest_subdivision2); - F32 cur_subdivisions = llclamp(getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit2, mTickPixelSpacing2), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel); - - if (fmodf((F32)(i + sub_div_offset_2), (sGridMaxSubdivisionLevel / cur_subdivisions)) != 0.f) + //No need check this condition to prevent tick position scaling (FIX MAINT-5207/5208) + //F32 cur_subdivisions = llclamp(getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit2, mTickPixelSpacing2), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel); + /*if (fmodf((F32)(i + sub_div_offset_2), (sGridMaxSubdivisionLevel / cur_subdivisions)) != 0.f) { continue; - } + }*/ F32 tick_scale = 1.f; for (F32 division_level = sGridMaxSubdivisionLevel; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f) diff --git a/indra/newview/llmaniptranslate.cpp b/indra/newview/llmaniptranslate.cpp index 394db71fb9..b4259a456c 100755 --- a/indra/newview/llmaniptranslate.cpp +++ b/indra/newview/llmaniptranslate.cpp @@ -1285,12 +1285,12 @@ void LLManipTranslate::renderSnapGuides() { tick_start = selection_center + (translate_axis * (smallest_grid_unit_scale * (F32)i - offset_nearest_grid_unit)); - F32 cur_subdivisions = getSubdivisionLevel(tick_start, translate_axis, getMinGridScale()); - - if (fmodf((F32)(i + sub_div_offset), (max_subdivisions / cur_subdivisions)) != 0.f) + //No need check this condition to prevent tick position scaling (FIX MAINT-5207/5208) + //F32 cur_subdivisions = getSubdivisionLevel(tick_start, translate_axis, getMinGridScale()); + /*if (fmodf((F32)(i + sub_div_offset), (max_subdivisions / cur_subdivisions)) != 0.f) { continue; - } + }*/ // add in off-axis offset tick_start += (mSnapOffsetAxis * mSnapOffsetMeters); diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp index b96bdd73ff..73faed7ef5 100755 --- a/indra/newview/llmediactrl.cpp +++ b/indra/newview/llmediactrl.cpp @@ -95,6 +95,7 @@ LLMediaCtrl::LLMediaCtrl( const Params& p) : mStretchToFill( true ), mMaintainAspectRatio ( true ), mDecoupleTextureSize ( false ), + mUpdateScrolls( false ), mTextureWidth ( 1024 ), mTextureHeight ( 1024 ), mClearCache(false), @@ -682,7 +683,13 @@ bool LLMediaCtrl::ensureMediaSourceExists() mMediaSource->addObserver( this ); mMediaSource->setBackgroundColor( getBackgroundColor() ); mMediaSource->setTrustedBrowser(mTrusted); - mMediaSource->setPageZoomFactor( LLUI::getScaleFactor().mV[ VX ] ); + + F32 scale_factor = LLUI::getScaleFactor().mV[ VX ]; + if (scale_factor != mMediaSource->getPageZoomFactor()) + { + mMediaSource->setPageZoomFactor( scale_factor ); + mUpdateScrolls = true; + } if(mClearCache) { @@ -720,10 +727,11 @@ void LLMediaCtrl::draw() { F32 alpha = getDrawContext().mAlpha; - if ( gRestoreGL == 1 ) + if ( gRestoreGL == 1 || mUpdateScrolls) { LLRect r = getRect(); reshape( r.getWidth(), r.getHeight(), FALSE ); + mUpdateScrolls = false; return; } @@ -765,7 +773,12 @@ void LLMediaCtrl::draw() { gGL.pushUIMatrix(); { - mMediaSource->setPageZoomFactor( LLUI::getScaleFactor().mV[ VX ] ); + F32 scale_factor = LLUI::getScaleFactor().mV[ VX ]; + if (scale_factor != mMediaSource->getPageZoomFactor()) + { + mMediaSource->setPageZoomFactor( scale_factor ); + mUpdateScrolls = true; + } // scale texture to fit the space using texture coords gGL.getTexUnit(0)->bind(media_texture); @@ -970,11 +983,11 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) case MEDIA_EVENT_CLICK_LINK_HREF: { - LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_HREF, target is \"" << self->getClickTarget() << "\", uri is " << self->getClickURL() << LL_ENDL; // retrieve the event parameters std::string url = self->getClickURL(); - std::string target = self->getClickTarget(); + std::string target = self->isOverrideClickTarget() ? self->getOverrideClickTarget() : self->getClickTarget(); std::string uuid = self->getClickUUID(); + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_HREF, target is \"" << target << "\", uri is " << url << LL_ENDL; LLNotification::Params notify_params; notify_params.name = "PopupAttempt"; diff --git a/indra/newview/llmediactrl.h b/indra/newview/llmediactrl.h index 785c57b78a..988733b85a 100755 --- a/indra/newview/llmediactrl.h +++ b/indra/newview/llmediactrl.h @@ -192,7 +192,8 @@ public: mHidingInitialLoad, mClearCache, mHoverTextChanged, - mDecoupleTextureSize; + mDecoupleTextureSize, + mUpdateScrolls; std::string mHomePageUrl, mHomePageMimeType, diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 9a0bd9d1bc..55b94aa141 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -493,6 +493,12 @@ void get_vertex_buffer_from_mesh(LLCDMeshData& mesh, LLModel::PhysicsMesh& res, } } +LLViewerFetchedTexture* LLMeshUploadThread::FindViewerTexture(const LLImportMaterial& material) +{ + LLPointer< LLViewerFetchedTexture > * ppTex = static_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData); + return ppTex ? (*ppTex).get() : NULL; +} + volatile S32 LLMeshRepoThread::sActiveHeaderRequests = 0; volatile S32 LLMeshRepoThread::sActiveLODRequests = 0; U32 LLMeshRepoThread::sMaxConcurrentRequests = 1; @@ -587,16 +593,16 @@ public: LLMeshLODHandler(const LLVolumeParams & mesh_params, S32 lod, U32 offset, U32 requested_bytes) : LLMeshHandlerBase(offset, requested_bytes), mLOD(lod) - { + { mMeshParams = mesh_params; LLMeshRepoThread::incActiveLODRequests(); } virtual ~LLMeshLODHandler(); - + protected: LLMeshLODHandler(const LLMeshLODHandler &); // Not defined void operator=(const LLMeshLODHandler &); // Not defined - + public: virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size); virtual void processFailure(LLCore::HttpStatus status); @@ -796,7 +802,7 @@ LLMeshRepoThread::LLMeshRepoThread() mHttpLargePolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_LARGE_MESH); } - + LLMeshRepoThread::~LLMeshRepoThread() { LL_INFOS(LOG_MESH) << "Small GETs issued: " << LLMeshRepository::sHTTPRequestCount @@ -865,16 +871,16 @@ void LLMeshRepoThread::run() { break; } - + if (! mHttpRequestSet.empty()) { // Dispatch all HttpHandler notifications mHttpRequest->update(0L); } sRequestWaterLevel = mHttpRequestSet.size(); // Stats data update - - // NOTE: order of queue processing intentionally favors LOD requests over header requests + // NOTE: order of queue processing intentionally favors LOD requests over header requests + while (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) { if (! mMutex) @@ -938,7 +944,7 @@ void LLMeshRepoThread::run() mSkinRequests.erase(iter); mMutex->unlock(); - if (!fetchMeshSkinInfo(mesh_id)) + if (! fetchMeshSkinInfo(mesh_id)) { incomplete.insert(mesh_id); } @@ -967,7 +973,7 @@ void LLMeshRepoThread::run() mDecompositionRequests.erase(iter); mMutex->unlock(); - if (!fetchMeshDecomposition(mesh_id)) + if (! fetchMeshDecomposition(mesh_id)) { incomplete.insert(mesh_id); } @@ -993,7 +999,7 @@ void LLMeshRepoThread::run() mPhysicsShapeRequests.erase(iter); mMutex->unlock(); - if (!fetchMeshPhysicsShape(mesh_id)) + if (! fetchMeshPhysicsShape(mesh_id)) { incomplete.insert(mesh_id); } @@ -1219,7 +1225,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) } ++LLMeshRepository::sMeshRequestCount; - bool ret = true ; + bool ret = true; U32 header_size = mMeshHeaderSize[mesh_id]; if (header_size > 0) @@ -1267,7 +1273,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) constructUrl(mesh_id, &http_url, &cap_version); if (!http_url.empty()) - { + { LLMeshSkinInfoHandler * handler = new LLMeshSkinInfoHandler(mesh_id, offset, size); LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) @@ -1313,7 +1319,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) ++LLMeshRepository::sMeshRequestCount; U32 header_size = mMeshHeaderSize[mesh_id]; - bool ret = true ; + bool ret = true; if (header_size > 0) { @@ -1359,9 +1365,9 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) int cap_version(2); std::string http_url; constructUrl(mesh_id, &http_url, &cap_version); - + if (!http_url.empty()) - { + { LLMeshDecompositionHandler * handler = new LLMeshDecompositionHandler(mesh_id, offset, size); LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) @@ -1407,7 +1413,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) ++LLMeshRepository::sMeshRequestCount; U32 header_size = mMeshHeaderSize[mesh_id]; - bool ret = true ; + bool ret = true; if (header_size > 0) { @@ -1452,9 +1458,9 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) int cap_version(2); std::string http_url; constructUrl(mesh_id, &http_url, &cap_version); - + if (!http_url.empty()) - { + { LLMeshPhysicsShapeHandler * handler = new LLMeshPhysicsShapeHandler(mesh_id, offset, size); LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) @@ -1543,11 +1549,11 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) } //either cache entry doesn't exist or is corrupt, request header from simulator - bool retval = true ; + bool retval = true; int cap_version(2); std::string http_url; constructUrl(mesh_params.getSculptID(), &http_url, &cap_version); - + if (!http_url.empty()) { //grab first 4KB if we're going to bother with a fetch. Cache will prevent future fetches if a full mesh fits @@ -1635,9 +1641,9 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) int cap_version(2); std::string http_url; constructUrl(mesh_id, &http_url, &cap_version); - + if (!http_url.empty()) - { + { LLMeshLODHandler * handler = new LLMeshLODHandler(mesh_params, lod, offset, size); LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) @@ -1909,7 +1915,7 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, mOrigin += gAgent.getAtAxis() * scale.magVec(); - mMeshUploadTimeOut = gSavedSettings.getS32("MeshUploadTimeOut") ; + mMeshUploadTimeOut = gSavedSettings.getS32("MeshUploadTimeOut"); mHttpRequest = new LLCore::HttpRequest; mHttpOptions = new LLCore::HttpOptions; @@ -1977,14 +1983,14 @@ void LLMeshUploadThread::preStart() void LLMeshUploadThread::discard() { - LLMutexLock lock(mMutex) ; + LLMutexLock lock(mMutex); mDiscarded = true; } bool LLMeshUploadThread::isDiscarded() const { - LLMutexLock lock(mMutex) ; - return mDiscarded ; + LLMutexLock lock(mMutex); + return mDiscarded; } void LLMeshUploadThread::run() @@ -2049,6 +2055,14 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures) { LLMeshUploadData data; data.mBaseModel = iter->first; + + if (data.mBaseModel->mSubmodelID) + { + // These are handled below to insure correct parenting order on creation + // due to map walking being based on model address (aka random) + continue; + } + LLModelInstance& first_instance = *(iter->second.begin()); for (S32 i = 0; i < 5; i++) { @@ -2086,7 +2100,10 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures) data.mModel[LLModel::LOD_IMPOSTOR], decomp, mUploadSkin, - mUploadJoints); + mUploadJoints, + FALSE, + FALSE, + data.mBaseModel->mSubmodelID); data.mAssetData = ostr.str(); std::string str = ostr.str(); @@ -2120,17 +2137,26 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures) instance_entry["scale"] = ll_sd_from_vector3(scale); instance_entry["material"] = LL_MCODE_WOOD; - instance_entry["physics_shape_type"] = (U8)(LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL); + instance_entry["physics_shape_type"] = data.mModel[LLModel::LOD_PHYSICS].notNull() ? (U8)(LLViewerObject::PHYSICS_SHAPE_PRIM) : (U8)(LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL); instance_entry["mesh"] = mesh_index[data.mBaseModel]; instance_entry["face_list"] = LLSD::emptyArray(); - S32 end = llmin((S32)data.mBaseModel->mMaterialList.size(), data.mBaseModel->getNumVolumeFaces()) ; + // We want to be able to allow more than 8 materials... + // + S32 end = llmin((S32)instance.mMaterial.size(), instance.mModel->getNumVolumeFaces()) ; + for (S32 face_num = 0; face_num < end; face_num++) { LLImportMaterial& material = instance.mMaterial[data.mBaseModel->mMaterialList[face_num]]; LLSD face_entry = LLSD::emptyMap(); - LLViewerFetchedTexture *texture = material.mDiffuseMap.get(); + + LLViewerFetchedTexture *texture = NULL; + + if (material.mDiffuseMapFilename.size()) + { + texture = FindViewerTexture(material); + } if ((texture != NULL) && (textures.find(texture) == textures.end())) @@ -2145,9 +2171,171 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures) { LLPointer<LLImageJ2C> upload_file = LLViewerTextureList::convertToUploadFile(texture->getSavedRawImage()); + + if (!upload_file.isNull() && upload_file->getDataSize()) + { + texture_str.write((const char*) upload_file->getData(), upload_file->getDataSize()); + } + } + } + + if (texture != NULL && + mUploadTextures && + texture_index.find(texture) == texture_index.end()) + { + texture_index[texture] = texture_num; + std::string str = texture_str.str(); + res["texture_list"][texture_num] = LLSD::Binary(str.begin(),str.end()); + texture_num++; + } + + // Subset of TextureEntry fields. + if (texture != NULL && mUploadTextures) + { + face_entry["image"] = texture_index[texture]; + face_entry["scales"] = 1.0; + face_entry["scalet"] = 1.0; + face_entry["offsets"] = 0.0; + face_entry["offsett"] = 0.0; + face_entry["imagerot"] = 0.0; + } + face_entry["diffuse_color"] = ll_sd_from_color4(material.mDiffuseColor); + face_entry["fullbright"] = material.mFullbright; + instance_entry["face_list"][face_num] = face_entry; + } + + res["instance_list"][instance_num] = instance_entry; + instance_num++; + } + } + + for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter) + { + LLMeshUploadData data; + data.mBaseModel = iter->first; + + if (!data.mBaseModel->mSubmodelID) + { + // These were handled above already... + // + continue; + } + + LLModelInstance& first_instance = *(iter->second.begin()); + for (S32 i = 0; i < 5; i++) + { + data.mModel[i] = first_instance.mLOD[i]; + } + + if (mesh_index.find(data.mBaseModel) == mesh_index.end()) + { + // Have not seen this model before - create a new mesh_list entry for it. + if (model_name.empty()) + { + model_name = data.mBaseModel->getName(); + } + + if (model_metric.empty()) + { + model_metric = data.mBaseModel->getMetric(); + } + + std::stringstream ostr; + + LLModel::Decomposition& decomp = + data.mModel[LLModel::LOD_PHYSICS].notNull() ? + data.mModel[LLModel::LOD_PHYSICS]->mPhysics : + data.mBaseModel->mPhysics; + + 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, + FALSE, + FALSE, + data.mBaseModel->mSubmodelID); + + data.mAssetData = ostr.str(); + std::string str = ostr.str(); + + res["mesh_list"][mesh_num] = LLSD::Binary(str.begin(),str.end()); + mesh_index[data.mBaseModel] = mesh_num; + mesh_num++; + } + + // For all instances that use this model + for (instance_list::iterator instance_iter = iter->second.begin(); + instance_iter != iter->second.end(); + ++instance_iter) + { + + LLModelInstance& instance = *instance_iter; + + LLSD instance_entry; + + for (S32 i = 0; i < 5; i++) + { + data.mModel[i] = instance.mLOD[i]; + } + + LLVector3 pos, scale; + LLQuaternion rot; + LLMatrix4 transformation = instance.mTransform; + decomposeMeshMatrix(transformation,pos,rot,scale); + instance_entry["position"] = ll_sd_from_vector3(pos); + instance_entry["rotation"] = ll_sd_from_quaternion(rot); + instance_entry["scale"] = ll_sd_from_vector3(scale); + + instance_entry["material"] = LL_MCODE_WOOD; + instance_entry["physics_shape_type"] = (U8)(LLViewerObject::PHYSICS_SHAPE_NONE); + instance_entry["mesh"] = mesh_index[data.mBaseModel]; + + instance_entry["face_list"] = LLSD::emptyArray(); + + // We want to be able to allow more than 8 materials... + // + S32 end = llmin((S32)instance.mMaterial.size(), instance.mModel->getNumVolumeFaces()) ; + + for (S32 face_num = 0; face_num < end; face_num++) + { + LLImportMaterial& material = instance.mMaterial[data.mBaseModel->mMaterialList[face_num]]; + LLSD face_entry = LLSD::emptyMap(); + + LLViewerFetchedTexture *texture = NULL; + + if (material.mDiffuseMapFilename.size()) + { + texture = FindViewerTexture(material); + } + + if ((texture != NULL) && + (textures.find(texture) == textures.end())) + { + textures.insert(texture); + } + + std::stringstream texture_str; + if (texture != NULL && include_textures && mUploadTextures) + { + if(texture->hasSavedRawImage()) + { + LLPointer<LLImageJ2C> upload_file = + LLViewerTextureList::convertToUploadFile(texture->getSavedRawImage()); + + if (!upload_file.isNull() && upload_file->getDataSize()) + { texture_str.write((const char*) upload_file->getData(), upload_file->getDataSize()); } } + } if (texture != NULL && mUploadTextures && @@ -2235,7 +2423,7 @@ void LLMeshUploadThread::generateHulls() } } - if(has_valid_requests) + if (has_valid_requests) { // *NOTE: Interesting livelock condition on shutdown. If there // is an upload request in generateHulls() when shutdown starts, @@ -2340,7 +2528,7 @@ void LLMeshUploadThread::requestWholeModelFee() else { U32 sleep_time(10); - + mHttpRequest->update(0); while (! LLApp::isQuitting() && ! finished() && ! isDiscarded()) { @@ -2435,7 +2623,7 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp // model fee case LLWholeModelFeeObserver* observer(mFeeObserverHandle.get()); mWholeModelUploadURL.clear(); - + if (! status) { LL_WARNS(LOG_MESH) << "Fee request failed. Reason: " << reason @@ -2465,7 +2653,7 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp LLCoreHttpUtil::responseToLLSD(response, true, body); } dump_llsd_to_file(body, make_dump_name("whole_model_fee_response_", dump_num)); - + if (body["state"].asString() == "upload") { mWholeModelUploadURL = body["uploader"].asString(); @@ -2536,7 +2724,7 @@ void LLMeshRepoThread::notifyLoadedMeshes() LODRequest req = mUnavailableQ.front(); mUnavailableQ.pop(); mMutex->unlock(); - + update_metrics = true; gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD); } @@ -2672,7 +2860,7 @@ void LLMeshRepository::cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header) void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) { mProcessed = true; - + unsigned int retries(0U); response->getRetries(NULL, &retries); LLMeshRepository::sHTTPRetryCount += retries; @@ -2728,7 +2916,7 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo // 200 case, typically offset = 0; } - + // *DEBUG: To test validation below // offset += 1; @@ -2844,7 +3032,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b const std::string & lod_name = header_lod[i]; lod_bytes = llmax(lod_bytes, header[lod_name]["offset"].asInteger()+header[lod_name]["size"].asInteger()); } - + // just in case skin info or decomposition is at the end of the file (which it shouldn't be) lod_bytes = llmax(lod_bytes, header["skin"]["offset"].asInteger() + header["skin"]["size"].asInteger()); lod_bytes = llmax(lod_bytes, header["physics_convex"]["offset"].asInteger() + header["physics_convex"]["size"].asInteger()); @@ -2852,7 +3040,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b 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); @@ -2864,11 +3052,11 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b ++LLMeshRepository::sCacheWrites; file.write(data, data_size); - + // zero out the rest of the file U8 block[MESH_HEADER_SIZE]; memset(block, 0, sizeof(block)); - + while (bytes-file.tell() > sizeof(block)) { file.write(block, sizeof(block)); @@ -2894,8 +3082,8 @@ LLMeshLODHandler::~LLMeshLODHandler() gMeshRepo.mThread->lockAndLoadMeshLOD(mMeshParams, mLOD); } LLMeshRepoThread::decActiveLODRequests(); - } } +} void LLMeshLODHandler::processFailure(LLCore::HttpStatus status) { @@ -2913,7 +3101,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body { if ((! MESH_LOD_PROCESS_FAILED) && gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size)) { - //good fetch from sim, write to VFS for caching + // good fetch from sim, write to VFS for caching LLVFile file(gVFS, mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE); S32 offset = mOffset; @@ -2958,7 +3146,7 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* { if ((! MESH_SKIN_INFO_PROCESS_FAILED) && gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size)) { - //good fetch from sim, write to VFS for caching + // good fetch from sim, write to VFS for caching LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); S32 offset = mOffset; @@ -3282,7 +3470,7 @@ void LLMeshRepository::notifyLoadedMeshes() REQUEST2_LOW_WATER_MIN, REQUEST2_LOW_WATER_MAX); } - + //clean up completed upload threads for (std::vector<LLMeshUploadThread*>::iterator iter = mUploads.begin(); iter != mUploads.end(); ) { @@ -3359,7 +3547,7 @@ void LLMeshRepository::notifyLoadedMeshes() //call completed callbacks on finished decompositions mDecompThread->notifyCompleted(); - + // For major operations, attempt to get the required locks // without blocking and punt if they're not available. The // longest run of holdoffs is kept in sMaxLockHoldoffs just @@ -3378,12 +3566,12 @@ void LLMeshRepository::notifyLoadedMeshes() return; } hold_offs = 0; - + if (gAgent.getRegion()) { // Update capability urls static std::string region_name("never name a region this"); - + if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived()) { region_name = gAgent.getRegion()->getName(); @@ -3399,7 +3587,7 @@ void LLMeshRepository::notifyLoadedMeshes() << LL_ENDL; } } - + //popup queued error messages from background threads while (!mUploadErrorQ.empty()) { @@ -3754,7 +3942,7 @@ LLSD& LLMeshRepoThread::getMeshHeader(const LLUUID& mesh_id) void LLMeshRepository::uploadModel(std::vector<LLModelInstance>& data, LLVector3& scale, bool upload_textures, - bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload, + bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload, LLHandle<LLWholeModelFeeObserver> fee_observer, LLHandle<LLWholeModelUploadObserver> upload_observer) { LLMeshUploadThread* thread = new LLMeshUploadThread(data, scale, upload_textures, upload_skin, upload_joints, upload_url, @@ -3825,37 +4013,6 @@ void LLMeshUploadThread::decomposeMeshMatrix(LLMatrix4& transformation, result_rot = quat_rotation; } -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; - } - - if (mBinding != rhs.mBinding) - { - return mBinding < rhs.mBinding; - } - - return mFullbright < rhs.mFullbright; -} - - void LLMeshRepository::updateInventory(inventory_data data) { LLMutexLock lock(mMeshMutex); @@ -4253,7 +4410,7 @@ void LLPhysicsDecomp::doDecompositionSingleHull() setMeshData(mesh, true); LLCDResult ret = decomp->buildSingleHull() ; - if(ret) + if (ret) { LL_WARNS(LOG_MESH) << "Could not execute decomposition stage when attempting to create single hull." << LL_ENDL; make_box(mCurRequest); @@ -4441,60 +4598,6 @@ 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) - { - LLImportMaterial mat(data["material"][i]); - mMaterial[mat.mBinding] = mat; - } -} - - -LLSD LLModelInstance::asLLSD() -{ - LLSD ret; - - ret["mesh_id"] = mModel->mLocalID; - ret["label"] = mLabel; - ret["transform"] = mTransform.getValue(); - - U32 i = 0; - for (std::map<std::string, LLImportMaterial>::iterator iter = mMaterial.begin(); iter != mMaterial.end(); ++iter) - { - ret["material"][i++] = iter->second.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(); - mBinding = data["binding"].asString(); -} - - -LLSD LLImportMaterial::asLLSD() -{ - LLSD ret; - - ret["diffuse"]["filename"] = mDiffuseMapFilename; - ret["diffuse"]["label"] = mDiffuseMapLabel; - ret["diffuse"]["color"] = mDiffuseColor.getValue(); - ret["fullbright"] = mFullbright; - ret["binding"] = mBinding; - - return ret; -} - void LLMeshRepository::buildPhysicsMesh(LLModel::Decomposition& decomp) { decomp.mMesh.resize(decomp.mHull.size()); diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 39280bea3a..688cd01a87 100755 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -90,54 +90,6 @@ public: } }; -class LLImportMaterial -{ -public: - LLPointer<LLViewerFetchedTexture> mDiffuseMap; - std::string mDiffuseMapFilename; - std::string mDiffuseMapLabel; - std::string mBinding; - LLColor4 mDiffuseColor; - bool mFullbright; - - bool operator<(const LLImportMaterial ¶ms) const; - - LLImportMaterial() - : mFullbright(false) - { - mDiffuseColor.set(1,1,1,1); - } - - LLImportMaterial(LLSD& data); - - LLSD asLLSD(); -}; - -class LLModelInstance -{ -public: - LLPointer<LLModel> mModel; - LLPointer<LLModel> mLOD[5]; - - std::string mLabel; - - LLUUID mMeshID; - S32 mLocalMeshID; - - LLMatrix4 mTransform; - std::map<std::string, LLImportMaterial> mMaterial; - - LLModelInstance(LLModel* model, const std::string& label, LLMatrix4& transform, std::map<std::string, LLImportMaterial>& materials) - : mModel(model), mLabel(label), mTransform(transform), mMaterial(materials) - { - mLocalMeshID = -1; - } - - LLModelInstance(LLSD& data); - - LLSD asLLSD(); -}; - class LLPhysicsDecomp : public LLThread { public: @@ -483,6 +435,8 @@ public: // Inherited from LLCore::HttpHandler virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + LLViewerFetchedTexture* FindViewerTexture(const LLImportMaterial& material); + private: LLHandle<LLWholeModelFeeObserver> mFeeObserverHandle; LLHandle<LLWholeModelUploadObserver> mUploadObserverHandle; diff --git a/indra/newview/llmoveview.cpp b/indra/newview/llmoveview.cpp index fe562baf96..70035bcc74 100755 --- a/indra/newview/llmoveview.cpp +++ b/indra/newview/llmoveview.cpp @@ -303,7 +303,15 @@ void LLFloaterMove::onFlyButtonClick() void LLFloaterMove::setMovementMode(const EMovementMode mode) { mCurrentMode = mode; - gAgent.setFlying(MM_FLY == mode); + + if(MM_FLY == mode) + { + LLAgent::toggleFlying(); + } + else + { + gAgent.setFlying(FALSE); + } // attempts to set avatar flying can not set it real flying in some cases. // For ex. when avatar fell down & is standing up. diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp index 79988a0800..5510598ae7 100755 --- a/indra/newview/llnamelistctrl.cpp +++ b/indra/newview/llnamelistctrl.cpp @@ -130,8 +130,14 @@ BOOL LLNameListCtrl::handleDragAndDrop( return handled; } -void LLNameListCtrl::showInspector(const LLUUID& avatar_id, bool is_group) +void LLNameListCtrl::showInspector(const LLUUID& avatar_id, bool is_group, bool is_experience) { + if(is_experience) + { + LLFloaterReg::showInstance("experience_profile", avatar_id, true); + return; + } + if (is_group) LLFloaterReg::showInstance("inspect_group", LLSD().with("group_id", avatar_id)); else @@ -230,10 +236,11 @@ BOOL LLNameListCtrl::handleToolTip(S32 x, S32 y, MASK mask) // Should we show a group or an avatar inspector? bool is_group = hit_item->isGroup(); + bool is_experience = hit_item->isExperience(); LLToolTip::Params params; params.background_visible( false ); - params.click_callback( boost::bind(&LLNameListCtrl::showInspector, this, avatar_id, is_group) ); + params.click_callback( boost::bind(&LLNameListCtrl::showInspector, this, avatar_id, is_group, is_experience) ); params.delay_time(0.0f); // spawn instantly on hover params.image( icon ); params.message(""); @@ -295,7 +302,7 @@ LLScrollListItem* LLNameListCtrl::addNameItemRow( const std::string& prefix) { LLUUID id = name_item.value().asUUID(); - LLNameListItem* item = new LLNameListItem(name_item,name_item.target() == GROUP); + LLNameListItem* item = new LLNameListItem(name_item,name_item.target() == GROUP, name_item.target() == EXPERIENCE); if (!item) return NULL; @@ -353,6 +360,8 @@ LLScrollListItem* LLNameListCtrl::addNameItemRow( } break; } + case EXPERIENCE: + // just use supplied name default: break; } diff --git a/indra/newview/llnamelistctrl.h b/indra/newview/llnamelistctrl.h index 515962df7d..19ce3c7aed 100755 --- a/indra/newview/llnamelistctrl.h +++ b/indra/newview/llnamelistctrl.h @@ -44,22 +44,30 @@ class LLNameListItem : public LLScrollListItem, public LLHandleProvider<LLNameLi public: bool isGroup() const { return mIsGroup; } void setIsGroup(bool is_group) { mIsGroup = is_group; } + bool isExperience() const { return mIsExperience; } + void setIsExperience(bool is_experience) { mIsExperience = is_experience; } protected: friend class LLNameListCtrl; LLNameListItem( const LLScrollListItem::Params& p ) - : LLScrollListItem(p), mIsGroup(false) + : LLScrollListItem(p), mIsGroup(false), mIsExperience(false) { } LLNameListItem( const LLScrollListItem::Params& p, bool is_group ) - : LLScrollListItem(p), mIsGroup(is_group) + : LLScrollListItem(p), mIsGroup(is_group), mIsExperience(false) + { + } + + LLNameListItem( const LLScrollListItem::Params& p, bool is_group, bool is_experience ) + : LLScrollListItem(p), mIsGroup(is_group), mIsExperience(is_experience) { } private: bool mIsGroup; + bool mIsExperience; }; @@ -73,7 +81,8 @@ public: { INDIVIDUAL, GROUP, - SPECIAL + SPECIAL, + EXPERIENCE } ENameType; // provide names for enums @@ -160,7 +169,7 @@ public: /*virtual*/ void mouseOverHighlightNthItem( S32 index ); private: - void showInspector(const LLUUID& avatar_id, bool is_group); + void showInspector(const LLUUID& avatar_id, bool is_group, bool is_experience = false); void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name, std::string suffix, LLHandle<LLNameListItem> item); private: diff --git a/indra/newview/llnotificationlistitem.cpp b/indra/newview/llnotificationlistitem.cpp new file mode 100644 index 0000000000..8cdc2d7c0b --- /dev/null +++ b/indra/newview/llnotificationlistitem.cpp @@ -0,0 +1,645 @@ +/** + * @file llnotificationlistitem.cpp + * @brief + * + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2015, 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" // must be first include + +#include "llnotificationlistitem.h" + +#include "llagent.h" +#include "llgroupactions.h" +#include "llinventoryicon.h" +#include "llwindow.h" +#include "v4color.h" +#include "lltrans.h" +#include "lluicolortable.h" +#include "message.h" +#include "llnotificationsutil.h" +#include <boost/regex.hpp> + +LLNotificationListItem::LLNotificationListItem(const Params& p) : LLPanel(p), + mParams(p), + mTitleBox(NULL), + mExpandBtn(NULL), + mCondenseBtn(NULL), + mCloseBtn(NULL), + mCondensedViewPanel(NULL), + mExpandedViewPanel(NULL), + mCondensedHeight(0), + mExpandedHeight(0), + mExpandedHeightResize(0), + mExpanded(false) +{ + mNotificationName = p.notification_name; +} + +BOOL LLNotificationListItem::postBuild() +{ + BOOL rv = LLPanel::postBuild(); + mTitleBox = getChild<LLTextBox>("notification_title"); + mTitleBoxExp = getChild<LLTextBox>("notification_title_exp"); + mNoticeTextExp = getChild<LLChatEntry>("notification_text_exp"); + + mTimeBox = getChild<LLTextBox>("notification_time"); + mTimeBoxExp = getChild<LLTextBox>("notification_time_exp"); + mExpandBtn = getChild<LLButton>("expand_btn"); + mCondenseBtn = getChild<LLButton>("condense_btn"); + mCloseBtn = getChild<LLButton>("close_btn"); + mCloseBtnExp = getChild<LLButton>("close_expanded_btn"); + + mTitleBox->setValue(mParams.title); + mTitleBoxExp->setValue(mParams.title); + mNoticeTextExp->setValue(mParams.title); + mNoticeTextExp->setEnabled(FALSE); + mNoticeTextExp->setTextExpandedCallback(boost::bind(&LLNotificationListItem::reshapeNotification, this)); + + mTitleBox->setContentTrusted(false); + mTitleBoxExp->setContentTrusted(false); + mNoticeTextExp->setContentTrusted(false); + + mTimeBox->setValue(buildNotificationDate(mParams.time_stamp)); + mTimeBoxExp->setValue(buildNotificationDate(mParams.time_stamp)); + + mExpandBtn->setClickedCallback(boost::bind(&LLNotificationListItem::onClickExpandBtn,this)); + mCondenseBtn->setClickedCallback(boost::bind(&LLNotificationListItem::onClickCondenseBtn,this)); + + //mCloseBtn and mCloseExpandedBtn share the same callback + mCloseBtn->setClickedCallback(boost::bind(&LLNotificationListItem::onClickCloseBtn,this)); + mCloseBtnExp->setClickedCallback(boost::bind(&LLNotificationListItem::onClickCloseBtn,this)); + + mCondensedViewPanel = getChild<LLPanel>("layout_panel_condensed_view"); + mExpandedViewPanel = getChild<LLPanel>("layout_panel_expanded_view"); + + std::string expanded_height_str = getString("item_expanded_height"); + std::string condensed_height_str = getString("item_condensed_height"); + + mExpandedHeight = (S32)atoi(expanded_height_str.c_str()); + mCondensedHeight = (S32)atoi(condensed_height_str.c_str()); + + setExpanded(FALSE); + + return rv; +} + +LLNotificationListItem::~LLNotificationListItem() +{ +} + +//static +std::string LLNotificationListItem::buildNotificationDate(const LLDate& time_stamp, ETimeType time_type) +{ + std::string timeStr; + switch(time_type) + { + case Local: + timeStr = "[" + LLTrans::getString("LTimeMthNum") + "]/[" + +LLTrans::getString("LTimeDay")+"]/[" + +LLTrans::getString("LTimeYear")+"] [" + +LLTrans::getString("LTimeHour")+"]:[" + +LLTrans::getString("LTimeMin")+ "]"; + break; + case UTC: + timeStr = "[" + LLTrans::getString("UTCTimeMth") + "]/[" + +LLTrans::getString("UTCTimeDay")+"]/[" + +LLTrans::getString("UTCTimeYr")+"] [" + +LLTrans::getString("UTCTimeHr")+"]:[" + +LLTrans::getString("UTCTimeMin")+"] [" + +LLTrans::getString("UTCTimeTimezone")+"]"; + break; + case SLT: + default: + timeStr = "[" + LLTrans::getString("TimeMonth") + "]/[" + +LLTrans::getString("TimeDay")+"]/[" + +LLTrans::getString("TimeYear")+"] [" + +LLTrans::getString("TimeHour")+"]:[" + +LLTrans::getString("TimeMin")+"]"; + break; + } + LLSD substitution; + substitution["datetime"] = time_stamp; + LLStringUtil::format(timeStr, substitution); + return timeStr; +} + +void LLNotificationListItem::onClickCloseBtn() +{ + mOnItemClose(this); + close(); +} + +BOOL LLNotificationListItem::handleMouseUp(S32 x, S32 y, MASK mask) +{ + BOOL res = LLPanel::handleMouseUp(x, y, mask); + mOnItemClick(this); + return res; +} + +void LLNotificationListItem::onMouseEnter(S32 x, S32 y, MASK mask) +{ + mCondensedViewPanel->setTransparentColor(LLUIColorTable::instance().getColor( "ScrollHoveredColor" )); + mExpandedViewPanel->setTransparentColor(LLUIColorTable::instance().getColor( "ScrollHoveredColor" )); +} + +void LLNotificationListItem::onMouseLeave(S32 x, S32 y, MASK mask) +{ + mCondensedViewPanel->setTransparentColor(LLUIColorTable::instance().getColor( "SysWellItemUnselected" )); + mExpandedViewPanel->setTransparentColor(LLUIColorTable::instance().getColor( "SysWellItemUnselected" )); +} + +//static +LLNotificationListItem* LLNotificationListItem::create(const Params& p) +{ + if (LLNotificationListItem::getGroupInviteTypes().count(p.notification_name)) + { + return new LLGroupInviteNotificationListItem(p); + } + else if (LLNotificationListItem::getGroupNoticeTypes().count(p.notification_name)) + { + return new LLGroupNoticeNotificationListItem(p); + } + else if (LLNotificationListItem::getTransactionTypes().count(p.notification_name)) + { + return new LLTransactionNotificationListItem(p); + } + return new LLSystemNotificationListItem(p); +} + +//static +std::set<std::string> LLNotificationListItem::getGroupInviteTypes() +{ + return LLGroupInviteNotificationListItem::getTypes(); +} + + +std::set<std::string> LLNotificationListItem::getGroupNoticeTypes() +{ + return LLGroupNoticeNotificationListItem::getTypes(); +} + +//static +std::set<std::string> LLNotificationListItem::getTransactionTypes() +{ + return LLTransactionNotificationListItem::getTypes(); +} + +void LLNotificationListItem::onClickExpandBtn() +{ + setExpanded(TRUE); +} + +void LLNotificationListItem::onClickCondenseBtn() +{ + setExpanded(FALSE); +} + +void LLNotificationListItem::reshapeNotification() +{ + if(mExpanded) + { + S32 width = this->getRect().getWidth(); + this->reshape(width, mNoticeTextExp->getRect().getHeight() + mExpandedHeight, FALSE); + } +} + +void LLNotificationListItem::setExpanded(BOOL value) +{ + mCondensedViewPanel->setVisible(!value); + mExpandedViewPanel->setVisible(value); + S32 width = this->getRect().getWidth(); + + if (value) + { + this->reshape(width, mNoticeTextExp->getRect().getHeight() + mExpandedHeight, FALSE); + } + else + { + this->reshape(width, mCondensedHeight, FALSE); + } + mExpanded = value; + +} + +std::set<std::string> LLGroupInviteNotificationListItem::getTypes() +{ + std::set<std::string> types; + types.insert("JoinGroup"); + return types; +} + +std::set<std::string> LLGroupNoticeNotificationListItem::getTypes() +{ + std::set<std::string> types; + types.insert("GroupNotice"); + return types; +} + +std::set<std::string> LLTransactionNotificationListItem::getTypes() +{ + std::set<std::string> types; + types.insert("PaymentReceived"); + types.insert("PaymentSent"); + return types; +} + +LLGroupNotificationListItem::LLGroupNotificationListItem(const Params& p) + : LLNotificationListItem(p), + mSenderOrFeeBox(NULL) +{ +} + +LLGroupInviteNotificationListItem::LLGroupInviteNotificationListItem(const Params& p) + : LLGroupNotificationListItem(p) +{ + buildFromFile("panel_notification_list_item.xml"); +} + +BOOL LLGroupInviteNotificationListItem::postBuild() +{ + BOOL rv = LLGroupNotificationListItem::postBuild(); + setFee(mParams.fee); + mInviteButtonPanel = getChild<LLPanel>("button_panel"); + mInviteButtonPanel->setVisible(TRUE); + mJoinBtn = getChild<LLButton>("join_btn"); + mDeclineBtn = getChild<LLButton>("decline_btn"); + mInfoBtn = getChild<LLButton>("info_btn"); + + //invitation with any non-default group role, doesn't have newline characters at the end unlike simple invitations + std::string invitation_desc = mNoticeTextExp->getValue().asString(); + boost::regex pattern = boost::regex("\n\n$", boost::regex::perl|boost::regex::icase); + boost::match_results<std::string::const_iterator> matches; + if(!boost::regex_search(invitation_desc, matches, pattern)) + { + invitation_desc += "\n\n"; + mNoticeTextExp->setValue(invitation_desc); + } + + mJoinBtn->setClickedCallback(boost::bind(&LLGroupInviteNotificationListItem::onClickJoinBtn,this)); + mDeclineBtn->setClickedCallback(boost::bind(&LLGroupInviteNotificationListItem::onClickDeclineBtn,this)); + mInfoBtn->setClickedCallback(boost::bind(&LLGroupInviteNotificationListItem::onClickInfoBtn,this)); + + std::string expanded_height_resize_str = getString("expanded_height_resize_for_attachment"); + mExpandedHeightResize = (S32)atoi(expanded_height_resize_str.c_str()); + + return rv; +} + +void LLGroupInviteNotificationListItem::onClickJoinBtn() +{ + if (!gAgent.canJoinGroups()) + { + LLNotificationsUtil::add("JoinedTooManyGroups"); + return; + } + + if(mParams.fee > 0) + { + LLSD args; + args["COST"] = llformat("%d", mParams.fee); + // Set the fee for next time to 0, so that we don't keep + // asking about a fee. + LLSD next_payload; + next_payload["group_id"]= mParams.group_id; + next_payload["transaction_id"]= mParams.transaction_id; + next_payload["fee"] = 0; + LLNotificationsUtil::add("JoinGroupCanAfford", args, next_payload); + } + else + { + send_improved_im(mParams.group_id, + std::string("name"), + std::string("message"), + IM_ONLINE, + IM_GROUP_INVITATION_ACCEPT, + mParams.transaction_id); + } + LLNotificationListItem::onClickCloseBtn(); +} + +void LLGroupInviteNotificationListItem::onClickDeclineBtn() +{ + send_improved_im(mParams.group_id, + std::string("name"), + std::string("message"), + IM_ONLINE, + IM_GROUP_INVITATION_DECLINE, + mParams.transaction_id); + LLNotificationListItem::onClickCloseBtn(); +} + +void LLGroupInviteNotificationListItem::onClickInfoBtn() +{ + LLGroupActions::show(mParams.group_id); +} + +void LLGroupInviteNotificationListItem::setFee(S32 fee) +{ + LLStringUtil::format_map_t string_args; + string_args["[GROUP_FEE]"] = llformat("%d", fee); + std::string fee_text = getString("group_fee_text", string_args); + mSenderOrFeeBox->setValue(fee_text); + mSenderOrFeeBoxExp->setValue(fee_text); + mSenderOrFeeBox->setVisible(TRUE); + mSenderOrFeeBoxExp->setVisible(TRUE); +} + +LLGroupNoticeNotificationListItem::LLGroupNoticeNotificationListItem(const Params& p) + : LLGroupNotificationListItem(p), + mAttachmentPanel(NULL), + mAttachmentTextBox(NULL), + mAttachmentIcon(NULL), + mAttachmentIconExp(NULL), + mInventoryOffer(NULL) +{ + if (mParams.inventory_offer.isDefined()) + { + mInventoryOffer = new LLOfferInfo(mParams.inventory_offer); + } + + buildFromFile("panel_notification_list_item.xml"); +} + +LLGroupNoticeNotificationListItem::~LLGroupNoticeNotificationListItem() +{ + LLGroupMgr::getInstance()->removeObserver(this); +} + +BOOL LLGroupNoticeNotificationListItem::postBuild() +{ + BOOL rv = LLGroupNotificationListItem::postBuild(); + + mAttachmentTextBox = getChild<LLTextBox>("attachment_text"); + mAttachmentIcon = getChild<LLIconCtrl>("attachment_icon"); + mAttachmentIconExp = getChild<LLIconCtrl>("attachment_icon_exp"); + mAttachmentPanel = getChild<LLPanel>("attachment_panel"); + mAttachmentPanel->setVisible(FALSE); + + + mTitleBox->setValue(mParams.subject); + mTitleBoxExp->setValue(mParams.subject); + mNoticeTextExp->setValue(mParams.message); + + mTimeBox->setValue(buildNotificationDate(mParams.time_stamp, UTC)); + mTimeBoxExp->setValue(buildNotificationDate(mParams.time_stamp, UTC)); + //Workaround: in case server timestamp is 0 - we use the time when notification was actually received + if (mParams.time_stamp.isNull()) + { + mTimeBox->setValue(buildNotificationDate(mParams.received_time, UTC)); + mTimeBoxExp->setValue(buildNotificationDate(mParams.received_time, UTC)); + } + setSender(mParams.sender); + + if (mInventoryOffer != NULL) + { + mAttachmentTextBox->setValue(mInventoryOffer->mDesc); + mAttachmentTextBox->setVisible(TRUE); + mAttachmentIcon->setVisible(TRUE); + + std::string icon_name = LLInventoryIcon::getIconName(mInventoryOffer->mType, + LLInventoryType::IT_TEXTURE); + mAttachmentIconExp->setValue(icon_name); + mAttachmentIconExp->setVisible(TRUE); + + mAttachmentTextBox->setClickedCallback(boost::bind( + &LLGroupNoticeNotificationListItem::onClickAttachment, this)); + + std::string expanded_height_resize_str = getString("expanded_height_resize_for_attachment"); + mExpandedHeightResize = (S32)atoi(expanded_height_resize_str.c_str()); + + mAttachmentPanel->setVisible(TRUE); + } + return rv; +} + +BOOL LLGroupNotificationListItem::postBuild() +{ + BOOL rv = LLNotificationListItem::postBuild(); + + mGroupIcon = getChild<LLGroupIconCtrl>("group_icon"); + mGroupIconExp = getChild<LLGroupIconCtrl>("group_icon_exp"); + mGroupNameBoxExp = getChild<LLTextBox>("group_name_exp"); + + mGroupIcon->setValue(mParams.group_id); + mGroupIconExp->setValue(mParams.group_id); + + mGroupIcon->setVisible(TRUE); + mGroupIconExp->setVisible(TRUE); + + mGroupId = mParams.group_id; + + mSenderOrFeeBox = getChild<LLTextBox>("sender_or_fee_box"); + mSenderOrFeeBoxExp = getChild<LLTextBox>("sender_or_fee_box_exp"); + + LLSD value(mParams.group_id); + setGroupId(value); + + return rv; +} + +void LLGroupNotificationListItem::changed(LLGroupChange gc) +{ + if (GC_PROPERTIES == gc) + { + updateFromCache(); + LLGroupMgr::getInstance()->removeObserver(this); + } +} + +bool LLGroupNotificationListItem::updateFromCache() +{ + LLGroupMgrGroupData* group_data = LLGroupMgr::getInstance()->getGroupData(mGroupId); + if (!group_data) return false; + setGroupName(group_data->mName); + return true; +} + +void LLGroupNotificationListItem::setGroupId(const LLUUID& value) +{ + LLGroupMgr* gm = LLGroupMgr::getInstance(); + if (mGroupId.notNull()) + { + gm->removeObserver(this); + + + mID = mGroupId; + + // Check if cache already contains image_id for that group + if (!updateFromCache()) + { + gm->addObserver(this); + gm->sendGroupPropertiesRequest(mGroupId); + } + } +} + +void LLGroupNotificationListItem::setGroupName(std::string name) +{ + if (!name.empty()) + { + LLStringUtil::format_map_t string_args; + string_args["[GROUP_NAME]"] = llformat("%s", name.c_str()); + std::string group_box_str = getString("group_name_text", string_args); + mGroupNameBoxExp->setValue(group_box_str); + mGroupNameBoxExp->setVisible(TRUE); + } + else + { + mGroupNameBoxExp->setValue(LLStringUtil::null); + mGroupNameBoxExp->setVisible(FALSE); + } +} + +void LLGroupNoticeNotificationListItem::setSender(std::string sender) +{ + if (!sender.empty()) + { + LLStringUtil::format_map_t string_args; + string_args["[SENDER_RESIDENT]"] = llformat("%s", sender.c_str()); + std::string sender_text = getString("sender_resident_text", string_args); + mSenderOrFeeBox->setValue(sender_text); + mSenderOrFeeBoxExp->setValue(sender_text); + mSenderOrFeeBox->setVisible(TRUE); + mSenderOrFeeBoxExp->setVisible(TRUE); + } else { + mSenderOrFeeBox->setValue(LLStringUtil::null); + mSenderOrFeeBoxExp->setValue(LLStringUtil::null); + mSenderOrFeeBox->setVisible(FALSE); + mSenderOrFeeBoxExp->setVisible(FALSE); + } +} +void LLGroupNoticeNotificationListItem::close() +{ + // The group notice dialog may be an inventory offer. + // If it has an inventory save button and that button is still enabled + // Then we need to send the inventory declined message + if (mInventoryOffer != NULL) + { + mInventoryOffer->forceResponse(IOR_DECLINE); + mInventoryOffer = NULL; + } + LLGroupMgr::getInstance()->removeObserver(this); +} + +void LLGroupNoticeNotificationListItem::onClickAttachment() +{ + if (mInventoryOffer != NULL) { + mInventoryOffer->forceResponse(IOR_ACCEPT); + + static const LLUIColor textColor = LLUIColorTable::instance().getColor( + "GroupNotifyDimmedTextColor"); + mAttachmentTextBox->setColor(textColor); + mAttachmentIconExp->setEnabled(FALSE); + + //if attachment isn't openable - notify about saving + if (!isAttachmentOpenable(mInventoryOffer->mType)) { + LLNotifications::instance().add("AttachmentSaved", LLSD(), LLSD()); + } + + mInventoryOffer = NULL; + } +} + +//static +bool LLGroupNoticeNotificationListItem::isAttachmentOpenable(LLAssetType::EType type) +{ + switch (type) + { + case LLAssetType::AT_LANDMARK: + case LLAssetType::AT_NOTECARD: + case LLAssetType::AT_IMAGE_JPEG: + case LLAssetType::AT_IMAGE_TGA: + case LLAssetType::AT_TEXTURE: + case LLAssetType::AT_TEXTURE_TGA: + return true; + default: + return false; + } +} + +LLTransactionNotificationListItem::LLTransactionNotificationListItem(const Params& p) + : LLNotificationListItem(p), + mAvatarIcon(NULL) +{ + buildFromFile("panel_notification_list_item.xml"); +} + +BOOL LLTransactionNotificationListItem::postBuild() +{ + BOOL rv = LLNotificationListItem::postBuild(); + mAvatarIcon = getChild<LLAvatarIconCtrl>("avatar_icon"); + mAvatarIconExp = getChild<LLAvatarIconCtrl>("avatar_icon_exp"); + mAvatarIcon->setValue("System_Notification"); + mAvatarIconExp->setValue("System_Notification"); + + mAvatarIcon->setVisible(TRUE); + mAvatarIconExp->setVisible(TRUE); + if((GOVERNOR_LINDEN_ID == mParams.paid_to_id) || + (GOVERNOR_LINDEN_ID == mParams.paid_from_id)) + { + return rv; + } + + if (mParams.notification_name == "PaymentReceived") + { + mAvatarIcon->setValue(mParams.paid_from_id); + mAvatarIconExp->setValue(mParams.paid_from_id); + } + else if (mParams.notification_name == "PaymentSent") + { + mAvatarIcon->setValue(mParams.paid_to_id); + mAvatarIconExp->setValue(mParams.paid_to_id); + } + + return rv; +} + +LLSystemNotificationListItem::LLSystemNotificationListItem(const Params& p) + : LLNotificationListItem(p), + mSystemNotificationIcon(NULL), + mIsCaution(false) +{ + buildFromFile("panel_notification_list_item.xml"); + mIsCaution = p.notification_priority >= NOTIFICATION_PRIORITY_HIGH; + if (mIsCaution) + { + mTitleBox->setColor(LLUIColorTable::instance().getColor("NotifyCautionBoxColor")); + mTitleBoxExp->setColor(LLUIColorTable::instance().getColor("NotifyCautionBoxColor")); + mNoticeTextExp->setReadOnlyColor(LLUIColorTable::instance().getColor("NotifyCautionBoxColor")); + mTimeBox->setColor(LLUIColorTable::instance().getColor("NotifyCautionBoxColor")); + mTimeBoxExp->setColor(LLUIColorTable::instance().getColor("NotifyCautionBoxColor")); + } +} + +BOOL LLSystemNotificationListItem::postBuild() +{ + BOOL rv = LLNotificationListItem::postBuild(); + mSystemNotificationIcon = getChild<LLIconCtrl>("system_notification_icon"); + mSystemNotificationIconExp = getChild<LLIconCtrl>("system_notification_icon_exp"); + if (mSystemNotificationIcon) + mSystemNotificationIcon->setVisible(TRUE); + if (mSystemNotificationIconExp) + mSystemNotificationIconExp->setVisible(TRUE); + return rv; +} diff --git a/indra/newview/llnotificationlistitem.h b/indra/newview/llnotificationlistitem.h new file mode 100644 index 0000000000..9a4ce2be4b --- /dev/null +++ b/indra/newview/llnotificationlistitem.h @@ -0,0 +1,250 @@ +/** + * @file llnotificationlistitem.h + * @brief + * + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2015, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLNOTIFICATIONLISTITEM_H +#define LL_LLNOTIFICATIONLISTITEM_H + +#include "llpanel.h" +#include "lllayoutstack.h" +#include "lltextbox.h" +#include "llviewertexteditor.h" +#include "llbutton.h" +#include "llgroupiconctrl.h" +#include "llavatariconctrl.h" +#include "llchatentry.h" +#include "llgroupmgr.h" +#include "llviewermessage.h" + +#include <string> + +class LLNotificationListItem : public LLPanel +{ +public: + struct Params : public LLInitParam::Block<Params, LLPanel::Params> + { + LLUUID notification_id; + LLUUID transaction_id; + LLUUID group_id; + LLUUID paid_from_id; + LLUUID paid_to_id; + std::string notification_name; + std::string title; + std::string subject; + std::string message; + std::string sender; + S32 fee; + LLDate time_stamp; + LLDate received_time; + LLSD inventory_offer; + e_notification_priority notification_priority; + Params() {}; + }; + + static LLNotificationListItem* create(const Params& p); + + static std::set<std::string> getTransactionTypes(); + static std::set<std::string> getGroupInviteTypes(); + static std::set<std::string> getGroupNoticeTypes(); + + // title + void setTitle( std::string title ); + + // get item's ID + LLUUID getID() { return mParams.notification_id; } + std::string& getTitle() { return mTitle; } + std::string& getNotificationName() { return mNotificationName; } + + // handlers + virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); + virtual void onMouseEnter(S32 x, S32 y, MASK mask); + virtual void onMouseLeave(S32 x, S32 y, MASK mask); + + //callbacks + typedef boost::function<void (LLNotificationListItem* item)> item_callback_t; + typedef boost::signals2::signal<void (LLNotificationListItem* item)> item_signal_t; + item_signal_t mOnItemClose; + item_signal_t mOnItemClick; + boost::signals2::connection setOnItemCloseCallback(item_callback_t cb) { return mOnItemClose.connect(cb); } + boost::signals2::connection setOnItemClickCallback(item_callback_t cb) { return mOnItemClick.connect(cb); } + + virtual bool showPopup() { return true; } + void setExpanded(BOOL value); + virtual BOOL postBuild(); + void reshapeNotification(); + + typedef enum e_time_type + { + SLT = 1, + Local = 2, + UTC = 3, + }ETimeType; + +protected: + LLNotificationListItem(const Params& p); + virtual ~LLNotificationListItem(); + + static std::string buildNotificationDate(const LLDate& time_stamp, ETimeType time_type = SLT); + void onClickExpandBtn(); + void onClickCondenseBtn(); + void onClickCloseBtn(); + virtual void close() {}; + + Params mParams; + LLTextBox* mTitleBox; + LLTextBox* mTitleBoxExp; + LLChatEntry* mNoticeTextExp; + LLTextBox* mTimeBox; + LLTextBox* mTimeBoxExp; + LLButton* mExpandBtn; + LLButton* mCondenseBtn; + LLButton* mCloseBtn; + LLButton* mCloseBtnExp; + LLPanel* mCondensedViewPanel; + LLPanel* mExpandedViewPanel; + std::string mTitle; + std::string mNotificationName; + S32 mCondensedHeight; + S32 mExpandedHeight; + S32 mExpandedHeightResize; + bool mExpanded; +}; + +class LLGroupNotificationListItem + : public LLNotificationListItem, public LLGroupMgrObserver +{ +public: + virtual BOOL postBuild(); + + void setGroupId(const LLUUID& value); + // LLGroupMgrObserver observer trigger + virtual void changed(LLGroupChange gc); + + friend class LLNotificationListItem; +protected: + LLGroupNotificationListItem(const Params& p); + + LLGroupIconCtrl* mGroupIcon; + LLGroupIconCtrl* mGroupIconExp; + LLUUID mGroupId; + LLTextBox* mSenderOrFeeBox; + LLTextBox* mSenderOrFeeBoxExp; + LLTextBox* mGroupNameBoxExp; + +private: + LLGroupNotificationListItem(const LLGroupNotificationListItem &); + LLGroupNotificationListItem & operator=(LLGroupNotificationListItem &); + + void setGroupName(std::string name); + bool updateFromCache(); +}; + +class LLGroupInviteNotificationListItem + : public LLGroupNotificationListItem +{ +public: + static std::set<std::string> getTypes(); + virtual BOOL postBuild(); + + /*virtual*/ bool showPopup() { return false; } + +private: + friend class LLNotificationListItem; + LLGroupInviteNotificationListItem(const Params& p); + LLGroupInviteNotificationListItem(const LLGroupInviteNotificationListItem &); + LLGroupInviteNotificationListItem & operator=(LLGroupInviteNotificationListItem &); + + void setFee(S32 fee); + + void onClickJoinBtn(); + void onClickDeclineBtn(); + void onClickInfoBtn(); + + LLPanel* mInviteButtonPanel; + LLButton* mJoinBtn; + LLButton* mDeclineBtn; + LLButton* mInfoBtn; +}; + +class LLGroupNoticeNotificationListItem + : public LLGroupNotificationListItem +{ +public: + ~LLGroupNoticeNotificationListItem(); + static std::set<std::string> getTypes(); + virtual BOOL postBuild(); + + /*virtual*/ bool showPopup() { return false; } + +private: + friend class LLNotificationListItem; + LLGroupNoticeNotificationListItem(const Params& p); + LLGroupNoticeNotificationListItem(const LLGroupNoticeNotificationListItem &); + LLGroupNoticeNotificationListItem & operator=(LLGroupNoticeNotificationListItem &); + + void setSender(std::string sender); + void onClickAttachment(); + /*virtual*/ void close(); + + static bool isAttachmentOpenable(LLAssetType::EType); + + LLPanel* mAttachmentPanel; + LLTextBox* mAttachmentTextBox; + LLIconCtrl* mAttachmentIcon; + LLIconCtrl* mAttachmentIconExp; + LLOfferInfo* mInventoryOffer; +}; + +class LLTransactionNotificationListItem : public LLNotificationListItem +{ +public: + static std::set<std::string> getTypes(); + virtual BOOL postBuild(); +private: + friend class LLNotificationListItem; + LLTransactionNotificationListItem(const Params& p); + LLTransactionNotificationListItem(const LLTransactionNotificationListItem &); + LLTransactionNotificationListItem & operator=(LLTransactionNotificationListItem &); + LLAvatarIconCtrl* mAvatarIcon; + LLAvatarIconCtrl* mAvatarIconExp; +}; + +class LLSystemNotificationListItem : public LLNotificationListItem +{ +public: + virtual BOOL postBuild(); +private: + friend class LLNotificationListItem; + LLSystemNotificationListItem(const Params& p); + LLSystemNotificationListItem(const LLSystemNotificationListItem &); + LLSystemNotificationListItem & operator=(LLSystemNotificationListItem &); + LLIconCtrl* mSystemNotificationIcon; + LLIconCtrl* mSystemNotificationIconExp; + bool mIsCaution; +}; + +#endif // LL_LLNOTIFICATIONLISTITEM_H + + diff --git a/indra/newview/llnotificationlistview.cpp b/indra/newview/llnotificationlistview.cpp new file mode 100644 index 0000000000..9dce68c9c6 --- /dev/null +++ b/indra/newview/llnotificationlistview.cpp @@ -0,0 +1,44 @@ +/** + * @file llnotificationlistview.cpp + * @brief + * + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2015, 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 "llnotificationlistview.h" + +static const LLDefaultChildRegistry::Register<LLNotificationListView> notification_list_view("notification_list_view"); + +LLNotificationListView::LLNotificationListView(const Params& p) + : LLFlatListView(p) +{} + +LLNotificationListView::~LLNotificationListView() +{} + +bool LLNotificationListView::addNotification(LLNotificationListItem * item) +{ + return LLFlatListView::addItem(item, item->getID(), ADD_TOP); +} + +//EOF diff --git a/indra/newview/llnotificationlistview.h b/indra/newview/llnotificationlistview.h new file mode 100644 index 0000000000..307ad87789 --- /dev/null +++ b/indra/newview/llnotificationlistview.h @@ -0,0 +1,49 @@ +/** + * @file llnotificationlistview.h + * @brief LLNotificationListView class to support notifications list contained in enclosing floater. + * + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2015, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLNOTIFICATIONLISTVIEW_H +#define LL_LLNOTIFICATIONLISTVIEW_H + +#include "llflatlistview.h" +#include "llnotificationlistitem.h" + +/** + * Notification list + */ +class LLNotificationListView : public LLFlatListView +{ + LOG_CLASS(LLNotificationListView); +public: + struct Params : public LLInitParam::Block<Params, LLFlatListView::Params> {}; + + LLNotificationListView(const Params& p); + ~LLNotificationListView(); + friend class LLUICtrlFactory; + + virtual bool addNotification(LLNotificationListItem * item); +}; + +#endif diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index 9bd6007772..b7e1b2d3a4 100755 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -1168,6 +1168,8 @@ void LLPanelEditWearable::showWearable(LLViewerWearable* wearable, BOOL show, BO targetPanel->setVisible(show); toggleTypeSpecificControls(type); + // Update type controls here + updateTypeSpecificControls(type); if (show) { @@ -1179,7 +1181,6 @@ void LLPanelEditWearable::showWearable(LLViewerWearable* wearable, BOOL show, BO mNameEditor->setText(mWearableItem->getName()); updatePanelPickerControls(type); - updateTypeSpecificControls(type); // clear and rebuild visual param list U8 num_subparts = wearable_entry->mSubparts.size(); diff --git a/indra/newview/llpanelexperiencelisteditor.cpp b/indra/newview/llpanelexperiencelisteditor.cpp index fc4ee9862e..7c07301762 100644 --- a/indra/newview/llpanelexperiencelisteditor.cpp +++ b/indra/newview/llpanelexperiencelisteditor.cpp @@ -33,6 +33,7 @@ #include "llfloaterexperiencepicker.h" #include "llfloaterreg.h" #include "llhandle.h" +#include "llnamelistctrl.h" #include "llscrolllistctrl.h" #include "llviewerregion.h" #include "llagent.h" @@ -54,7 +55,7 @@ LLPanelExperienceListEditor::LLPanelExperienceListEditor() BOOL LLPanelExperienceListEditor::postBuild() { - mItems = getChild<LLScrollListCtrl>("experience_list"); + mItems = getChild<LLNameListCtrl>("experience_list"); mAdd = getChild<LLButton>("btn_add"); mRemove = getChild<LLButton>("btn_remove"); mProfile = getChild<LLButton>("btn_profile"); @@ -178,12 +179,13 @@ void LLPanelExperienceListEditor::onItems() { const LLUUID& experience = *it; item["id"]=experience; + item["target"] = LLNameListCtrl::EXPERIENCE; LLSD& columns = item["columns"]; columns[0]["column"] = "experience_name"; columns[0]["value"] = getString("loading"); mItems->addElement(item); - LLExperienceCache::get(experience, boost::bind(&LLPanelExperienceListEditor::experienceDetailsCallback, + LLExperienceCache::get(experience, boost::bind(&LLPanelExperienceListEditor::experienceDetailsCallback, getDerivedHandle<LLPanelExperienceListEditor>(), _1)); } diff --git a/indra/newview/llpanelexperiencelisteditor.h b/indra/newview/llpanelexperiencelisteditor.h index f69f0509be..bc9867752d 100644 --- a/indra/newview/llpanelexperiencelisteditor.h +++ b/indra/newview/llpanelexperiencelisteditor.h @@ -31,6 +31,7 @@ #include "lluuid.h" #include <set> +class LLNameListCtrl; class LLScrollListCtrl; class LLButton; class LLFloaterExperiencePicker; @@ -82,7 +83,7 @@ private: uuid_list_t mExperienceIds; - LLScrollListCtrl* mItems; + LLNameListCtrl* mItems; filter_list mFilters; LLButton* mAdd; LLButton* mRemove; diff --git a/indra/newview/llpanelexperiencepicker.cpp b/indra/newview/llpanelexperiencepicker.cpp index 70d826a407..43dc7569a4 100644 --- a/indra/newview/llpanelexperiencepicker.cpp +++ b/indra/newview/llpanelexperiencepicker.cpp @@ -42,6 +42,7 @@ #include "llviewercontrol.h" #include "llfloater.h" #include "lltrans.h" +#include <boost/regex.hpp> #define BTN_FIND "find" #define BTN_OK "ok_btn" @@ -147,6 +148,46 @@ void LLPanelExperiencePicker::editKeystroke( class LLLineEditor* caller, void* u void LLPanelExperiencePicker::onBtnFind() { mCurrentPage=1; + boost::cmatch what; + std::string text = getChild<LLUICtrl>(TEXT_EDIT)->getValue().asString(); + const boost::regex expression("secondlife:///app/experience/[\\da-f-]+/profile"); + if (boost::regex_match(text.c_str(), what, expression)) + { + LLURI uri(text); + LLSD path_array = uri.pathArray(); + if (path_array.size() == 4) + { + std::string exp_id = path_array.get(2).asString(); + LLUUID experience_id(exp_id); + if (!experience_id.isNull()) + { + const LLSD& experience_details = LLExperienceCache::get(experience_id); + if(!experience_details.isUndefined()) + { + std::string experience_name_string = experience_details[LLExperienceCache::NAME].asString(); + if(!experience_name_string.empty()) + { + getChild<LLUICtrl>(TEXT_EDIT)->setValue(experience_name_string); + } + } + else + { + getChild<LLScrollListCtrl>(LIST_RESULTS)->deleteAllItems(); + getChild<LLScrollListCtrl>(LIST_RESULTS)->setCommentText(getString("searching")); + + getChildView(BTN_OK)->setEnabled(FALSE); + getChildView(BTN_PROFILE)->setEnabled(FALSE); + + getChildView(BTN_RIGHT)->setEnabled(FALSE); + getChildView(BTN_LEFT)->setEnabled(FALSE); + LLExperienceCache::get(experience_id, boost::bind(&LLPanelExperiencePicker::onBtnFind, this)); + return; + } + } + } + } + + find(); } @@ -183,7 +224,6 @@ void LLPanelExperiencePicker::find() getChildView(BTN_LEFT)->setEnabled(FALSE); } - bool LLPanelExperiencePicker::isSelectButtonEnabled() { LLScrollListCtrl* list=getChild<LLScrollListCtrl>(LIST_RESULTS); diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h index 9823e84cd9..d7e89b4832 100755 --- a/indra/newview/llpanelface.h +++ b/indra/newview/llpanelface.h @@ -307,7 +307,7 @@ private: LLMaterialMgr::getInstance()->put(object->getID(),face,*new_material); } - object->setTEMaterialParams(face, new_material); + object->setTEMaterialParams(face, new_material, TRUE); return new_material; } return NULL; diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp index f67a90fd01..342b57ba4a 100755 --- a/indra/newview/llpanelgroup.cpp +++ b/indra/newview/llpanelgroup.cpp @@ -338,7 +338,7 @@ void LLPanelGroup::update(LLGroupChange gc) group_name_ctrl->setToolTip(group_name); LLGroupData agent_gdatap; - bool is_member = gAgent.getGroupData(mID,agent_gdatap) || gAgent.isGodlike(); + bool is_member = gAgent.getGroupData(mID,agent_gdatap) || gAgent.isGodlikeWithoutAdminMenuFakery(); bool join_btn_visible = !is_member && gdatap->mOpenEnrollment; mButtonJoin->setVisible(join_btn_visible); @@ -470,7 +470,7 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) } LLGroupData agent_gdatap; - bool is_member = gAgent.getGroupData(mID,agent_gdatap) || gAgent.isGodlike(); + bool is_member = gAgent.getGroupData(mID,agent_gdatap) || gAgent.isGodlikeWithoutAdminMenuFakery(); tab_roles->setVisible(is_member); tab_notices->setVisible(is_member); diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index 7ffaa05919..b2164c1f21 100755 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -287,11 +287,6 @@ void LLPanelGroupGeneral::activate() { LLGroupMgr::getInstance()->sendGroupTitlesRequest(mGroupID); LLGroupMgr::getInstance()->sendGroupPropertiesRequest(mGroupID); - - if (!gdatap || !gdatap->isMemberDataComplete() ) - { - LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID); - } mFirstUse = FALSE; } diff --git a/indra/newview/llpanelgroupinvite.cpp b/indra/newview/llpanelgroupinvite.cpp index e662a05dfc..866cb8dbef 100755 --- a/indra/newview/llpanelgroupinvite.cpp +++ b/indra/newview/llpanelgroupinvite.cpp @@ -243,56 +243,59 @@ void LLPanelGroupInvite::impl::addRoleNames(LLGroupMgrGroupData* gdatap) LLGroupMgrGroupData::member_list_t::iterator agent_iter = gdatap->mMembers.find(gAgent.getID()); + //loop over the agent's roles in the group + //then add those roles to the list of roles that the agent + //can invite people to be. + //if the user is the owner then we add + //all of the roles in the group, + //else if they have the add to roles power + //we add every role but owner, + //else if they have the limited add to roles power + //we add every role the user is in, + //else we just add to everyone + bool is_owner = FALSE; + bool can_assign_any = gAgent.hasPowerInGroup(mGroupID, + GP_ROLE_ASSIGN_MEMBER); + bool can_assign_limited = gAgent.hasPowerInGroup(mGroupID, + GP_ROLE_ASSIGN_MEMBER_LIMITED); + LLGroupMemberData* member_data = NULL; //get the member data for the agent if it exists - if ( agent_iter != gdatap->mMembers.end() ) + if (agent_iter != gdatap->mMembers.end()) { - LLGroupMemberData* member_data = (*agent_iter).second; - - //loop over the agent's roles in the group - //then add those roles to the list of roles that the agent - //can invite people to be - if ( member_data && mRoleNames) + member_data = (*agent_iter).second; + if (member_data && mRoleNames) { - //if the user is the owner then we add - //all of the roles in the group - //else if they have the add to roles power - //we add every role but owner, - //else if they have the limited add to roles power - //we add every role the user is in - //else we just add to everyone - bool is_owner = member_data->isOwner(); - bool can_assign_any = gAgent.hasPowerInGroup(mGroupID, - GP_ROLE_ASSIGN_MEMBER); - bool can_assign_limited = gAgent.hasPowerInGroup(mGroupID, - GP_ROLE_ASSIGN_MEMBER_LIMITED); + is_owner = member_data->isOwner(); + }//end if member data is not null + }//end if agent is in the group + - LLGroupMgrGroupData::role_list_t::iterator rit = gdatap->mRoles.begin(); - LLGroupMgrGroupData::role_list_t::iterator end = gdatap->mRoles.end(); - //populate the role list - for ( ; rit != end; ++rit) + LLGroupMgrGroupData::role_list_t::iterator rit = gdatap->mRoles.begin(); + LLGroupMgrGroupData::role_list_t::iterator end = gdatap->mRoles.end(); + + //populate the role list: + for ( ; rit != end; ++rit) + { + LLUUID role_id = (*rit).first; + LLRoleData rd; + if ( gdatap->getRoleData(role_id,rd) ) + { + // Owners can add any role. + if ( is_owner + // Even 'can_assign_any' can't add owner role. + || (can_assign_any && role_id != gdatap->mOwnerRole) + // Add all roles user is in + || (can_assign_limited && member_data && member_data->isInRole(role_id)) + // Everyone role. + || role_id == LLUUID::null ) { - LLUUID role_id = (*rit).first; - LLRoleData rd; - if ( gdatap->getRoleData(role_id,rd) ) - { - // Owners can add any role. - if ( is_owner - // Even 'can_assign_any' can't add owner role. - || (can_assign_any && role_id != gdatap->mOwnerRole) - // Add all roles user is in - || (can_assign_limited && member_data->isInRole(role_id)) - // Everyone role. - || role_id == LLUUID::null ) - { - mRoleNames->add(rd.mRoleName, - role_id, - ADD_BOTTOM); - } - } + mRoleNames->add(rd.mRoleName, + role_id, + ADD_BOTTOM); } - }//end if member data is not null - }//end if agent is in the group + } + } } //static @@ -579,7 +582,8 @@ void LLPanelGroupInvite::updateLists() { waiting = true; } - if (gdatap->isRoleDataComplete() && gdatap->isMemberDataComplete() && gdatap->isRoleMemberDataComplete()) + if (gdatap->isRoleDataComplete() && gdatap->isMemberDataComplete() + && (gdatap->isRoleMemberDataComplete() || !gdatap->mMembers.size())) // MAINT-5270: large groups receives an empty members list without some powers, so RoleMemberData wouldn't be complete for them { if ( mImplementation->mRoleNames ) { diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index c3a10b3fa0..00c204e702 100755 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -350,12 +350,10 @@ void LLPanelGroupRoles::update(LLGroupChange gc) void LLPanelGroupRoles::activate() { + if (!gAgent.isInGroup(mGroupID)) return; + // Start requesting member and role data if needed. LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID); - if (!gdatap || !gdatap->isMemberDataComplete() ) - { - LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID); - } if (!gdatap || !gdatap->isRoleDataComplete() ) { @@ -364,13 +362,7 @@ void LLPanelGroupRoles::activate() LLGroupMgr::getInstance()->sendGroupRoleDataRequest(mGroupID); } - - // Check role-member mapping data. - if (!gdatap || !gdatap->isRoleMemberDataComplete() ) - { - LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(mGroupID); - } - + // Need this to get base group member powers if (!gdatap || !gdatap->isGroupPropertiesDataComplete() ) { @@ -1163,7 +1155,37 @@ void LLPanelGroupMembersSubTab::onEjectMembers(void *userdata) if ( selfp ) { - selfp->handleEjectMembers(); + selfp->confirmEjectMembers(); + } +} + +void LLPanelGroupMembersSubTab::confirmEjectMembers() +{ + std::vector<LLScrollListItem*> selection = mMembersList->getAllSelected(); + if (selection.empty()) return; + + S32 selection_count = selection.size(); + if (selection_count == 1) + { + LLSD args; + std::string fullname; + gCacheName->getFullName(mMembersList->getValue(), fullname); + args["AVATAR_NAME"] = fullname; + LLSD payload; + LLNotificationsUtil::add("EjectGroupMemberWarning", + args, + payload, + boost::bind(&LLPanelGroupMembersSubTab::handleEjectCallback, this, _1, _2)); + } + else + { + LLSD args; + args["COUNT"] = llformat("%d", selection_count); + LLSD payload; + LLNotificationsUtil::add("EjectGroupMembersWarning", + args, + payload, + boost::bind(&LLPanelGroupMembersSubTab::handleEjectCallback, this, _1, _2)); } } @@ -1190,6 +1212,16 @@ void LLPanelGroupMembersSubTab::handleEjectMembers() LLGroupMgr::getInstance()->sendGroupMemberEjects(mGroupID, selected_members); } +bool LLPanelGroupMembersSubTab::handleEjectCallback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (0 == option) // Eject button + { + handleEjectMembers(); + } + return false; +} + void LLPanelGroupMembersSubTab::sendEjectNotifications(const LLUUID& group_id, const uuid_vec_t& selected_members) { LLGroupMgrGroupData* group_data = LLGroupMgr::getInstance()->getGroupData(group_id); @@ -1327,15 +1359,26 @@ void LLPanelGroupMembersSubTab::handleMemberDoubleClick() void LLPanelGroupMembersSubTab::activate() { + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID); + LLPanelGroupSubTab::activate(); if(!mActivated) { + if (!gdatap || !gdatap->isMemberDataComplete()) + { + LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID); + } + + if (!gdatap || !gdatap->isRoleMemberDataComplete()) + { + LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(mGroupID); + } + update(GC_ALL); mActivated = true; } else { - LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID); // Members can be removed outside of this tab, checking changes if (!gdatap || (gdatap->isMemberDataComplete() && gdatap->mMembers.size() != mMembersList->getItemCount())) { @@ -1636,7 +1679,13 @@ void LLPanelGroupMembersSubTab::update(LLGroupChange gc) { // Build a string with info on retrieval progress. std::ostringstream retrieved; - if ( !gdatap->isMemberDataComplete() ) + + if ( gdatap->isRoleDataComplete() && gdatap->isMemberDataComplete() && !gdatap->mMembers.size() ) + { + // MAINT-5237 + retrieved << "Member list not available."; + } + else if ( !gdatap->isMemberDataComplete() ) { // Still busy retreiving member list. retrieved << "Retrieving member list (" << gdatap->mMembers.size() @@ -1783,7 +1832,7 @@ void LLPanelGroupMembersSubTab::updateMembers() { mMembersList->setEnabled(TRUE); } - else + else if (gdatap->mMembers.size()) { mMembersList->setEnabled(FALSE); mMembersList->setCommentText(std::string("No match.")); @@ -1801,7 +1850,47 @@ void LLPanelGroupMembersSubTab::updateMembers() void LLPanelGroupMembersSubTab::onBanMember(void* user_data) { LLPanelGroupMembersSubTab* self = static_cast<LLPanelGroupMembersSubTab*>(user_data); - self->handleBanMember(); + self->confirmBanMembers(); +} + +void LLPanelGroupMembersSubTab::confirmBanMembers() +{ + std::vector<LLScrollListItem*> selection = mMembersList->getAllSelected(); + if (selection.empty()) return; + + S32 selection_count = selection.size(); + if (selection_count == 1) + { + LLSD args; + std::string fullname; + gCacheName->getFullName(mMembersList->getValue(), fullname); + args["AVATAR_NAME"] = fullname; + LLSD payload; + LLNotificationsUtil::add("BanGroupMemberWarning", + args, + payload, + boost::bind(&LLPanelGroupMembersSubTab::handleBanCallback, this, _1, _2)); + } + else + { + LLSD args; + args["COUNT"] = llformat("%d", selection_count); + LLSD payload; + LLNotificationsUtil::add("BanGroupMembersWarning", + args, + payload, + boost::bind(&LLPanelGroupMembersSubTab::handleBanCallback, this, _1, _2)); + } +} + +bool LLPanelGroupMembersSubTab::handleBanCallback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (0 == option) // Eject button + { + handleBanMember(); + } + return false; } void LLPanelGroupMembersSubTab::handleBanMember() @@ -2111,20 +2200,7 @@ void LLPanelGroupRolesSubTab::update(LLGroupChange gc) mDeleteRoleButton->setEnabled(FALSE); } } - - if(!mFirstOpen) - { - if (!gdatap || !gdatap->isMemberDataComplete()) - { - LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID); - } - - if (!gdatap || !gdatap->isRoleMemberDataComplete()) - { - LLGroupMgr::getInstance()->sendGroupRoleMembersRequest(mGroupID); - } - } - + if ((GC_ROLE_MEMBER_DATA == gc || GC_MEMBER_DATA == gc) && gdatap && gdatap->isMemberDataComplete() diff --git a/indra/newview/llpanelgrouproles.h b/indra/newview/llpanelgrouproles.h index 540b24ada6..9a696124a8 100755 --- a/indra/newview/llpanelgrouproles.h +++ b/indra/newview/llpanelgrouproles.h @@ -171,6 +171,7 @@ public: void handleEjectMembers(); void sendEjectNotifications(const LLUUID& group_id, const uuid_vec_t& selected_members); bool handleEjectCallback(const LLSD& notification, const LLSD& response); + void confirmEjectMembers(); static void onRoleCheck(LLUICtrl* check, void* user_data); void handleRoleCheck(const LLUUID& role_id, @@ -178,6 +179,8 @@ public: static void onBanMember(void* user_data); void handleBanMember(); + bool handleBanCallback(const LLSD& notification, const LLSD& response); + void confirmBanMembers(); void applyMemberChanges(); diff --git a/indra/newview/llpanellandmarkinfo.cpp b/indra/newview/llpanellandmarkinfo.cpp index d4894d4a42..06bb886ae8 100755 --- a/indra/newview/llpanellandmarkinfo.cpp +++ b/indra/newview/llpanellandmarkinfo.cpp @@ -147,6 +147,7 @@ void LLPanelLandmarkInfo::setInfoType(EInfoType type) } else { + LLAgentUI::buildLocationString(desc, LLAgentUI::LOCATION_FORMAT_NORMAL, agent_pos); region_name = desc; } diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 7aaf9510eb..4229419fce 100755 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -604,7 +604,7 @@ void LLPanelMainInventory::updateItemcountText() } else { - text = getString("ItemcountUnknown"); + text = getString("ItemcountUnknown", string_args); } mCounterCtrl->setValue(text); diff --git a/indra/newview/llpanelmediasettingsgeneral.cpp b/indra/newview/llpanelmediasettingsgeneral.cpp index 2856ea9db1..d7c43c224c 100755 --- a/indra/newview/llpanelmediasettingsgeneral.cpp +++ b/indra/newview/llpanelmediasettingsgeneral.cpp @@ -319,6 +319,10 @@ void LLPanelMediaSettingsGeneral::initValues( void* userdata, const LLSD& _media // Helper to set media control to media URL as required void LLPanelMediaSettingsGeneral::updateMediaPreview() { + if(LLTrans::getString("Multiple Media") == mHomeURL->getValue().asString()) + { + return; + } if ( mHomeURL->getValue().asString().length() > 0 ) { if(mPreviewMedia->getCurrentNavUrl() != mHomeURL->getValue().asString()) diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index dcd0aab3ab..5dd44b4444 100755 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -1739,9 +1739,10 @@ void LLPanelObject::sendSculpt() return; LLSculptParams sculpt_params; + LLUUID sculpt_id = LLUUID::null; if (mCtrlSculptTexture) - sculpt_params.setSculptTexture(mCtrlSculptTexture->getImageAssetID()); + sculpt_id = mCtrlSculptTexture->getImageAssetID(); U8 sculpt_type = 0; @@ -1765,7 +1766,7 @@ void LLPanelObject::sendSculpt() if ((mCtrlSculptInvert) && (mCtrlSculptInvert->get())) sculpt_type |= LL_SCULPT_FLAG_INVERT; - sculpt_params.setSculptType(sculpt_type); + sculpt_params.setSculptTexture(sculpt_id, sculpt_type); mObject->setParameterEntry(LLNetworkData::PARAMS_SCULPT, sculpt_params, TRUE); } @@ -2000,7 +2001,11 @@ void LLPanelObject::onCancelSculpt(const LLSD& data) LLTextureCtrl* mTextureCtrl = getChild<LLTextureCtrl>("sculpt texture control"); if(!mTextureCtrl) return; - + + if(mSculptTextureRevert == LLUUID::null) + { + mSculptTextureRevert = LLUUID(SCULPT_DEFAULT_TEXTURE); + } mTextureCtrl->setImageAssetID(mSculptTextureRevert); sendSculpt(); diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index 5977d558d3..de4efc8612 100755 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -85,6 +85,8 @@ static const std::string RECENT_TAB_NAME = "recent_panel"; static const std::string BLOCKED_TAB_NAME = "blocked_panel"; // blocked avatars static const std::string COLLAPSED_BY_USER = "collapsed_by_user"; +const S32 BASE_MAX_AGENT_GROUPS = 42; +const S32 PREMIUM_MAX_AGENT_GROUPS = 60; extern S32 gMaxAgentGroups; @@ -585,6 +587,7 @@ BOOL LLPanelPeople::postBuild() getChild<LLFilterEditor>("groups_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); getChild<LLFilterEditor>("recent_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); getChild<LLFilterEditor>("fbc_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); + getChild<LLTextBox>("groupcount")->setURLClickedCallback(boost::bind(&LLPanelPeople::onGroupLimitInfo, this)); mTabContainer = getChild<LLTabContainer>("tabs"); mTabContainer->setCommitCallback(boost::bind(&LLPanelPeople::onTabSelected, this, _2)); @@ -902,8 +905,11 @@ void LLPanelPeople::updateButtons() LLPanel* groups_panel = mTabContainer->getCurrentPanel(); groups_panel->getChildView("minus_btn")->setEnabled(item_selected && selected_id.notNull()); // a real group selected - groups_panel->getChild<LLUICtrl>("groupcount")->setTextArg("[COUNT]", llformat("%d",gAgent.mGroups.size())); - groups_panel->getChild<LLUICtrl>("groupcount")->setTextArg("[REMAINING]", llformat("%d",(gMaxAgentGroups-gAgent.mGroups.size()))); + + U32 groups_count = gAgent.mGroups.size(); + U32 groups_ramaining = gMaxAgentGroups > groups_count ? gMaxAgentGroups - groups_count : 0; + groups_panel->getChild<LLUICtrl>("groupcount")->setTextArg("[COUNT]", llformat("%d", groups_count)); + groups_panel->getChild<LLUICtrl>("groupcount")->setTextArg("[REMAINING]", llformat("%d", groups_ramaining)); } else { @@ -1114,6 +1120,14 @@ void LLPanelPeople::onFilterEdit(const std::string& search_string) } } +void LLPanelPeople::onGroupLimitInfo() +{ + LLSD args; + args["MAX_BASIC"] = BASE_MAX_AGENT_GROUPS; + args["MAX_PREMIUM"] = PREMIUM_MAX_AGENT_GROUPS; + LLNotificationsUtil::add("GroupLimitInfo", args); +} + void LLPanelPeople::onTabSelected(const LLSD& param) { std::string tab_name = getChild<LLPanel>(param.asString())->getName(); diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h index c1d7a134fa..eb7e76a772 100755 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -93,6 +93,7 @@ private: // UI callbacks void onFilterEdit(const std::string& search_string); + void onGroupLimitInfo(); void onTabSelected(const LLSD& param); void onAddFriendButtonClicked(); void onAddFriendWizButtonClicked(); diff --git a/indra/newview/llpanelpicks.cpp b/indra/newview/llpanelpicks.cpp index d73a5b402e..55c09d85ea 100755 --- a/indra/newview/llpanelpicks.cpp +++ b/indra/newview/llpanelpicks.cpp @@ -432,7 +432,7 @@ void LLPanelPicks::processProperties(void* data, EAvatarProcessorType type) mNoPicks = !mPicksList->size(); } - else if(APT_CLASSIFIEDS == type) + else if((APT_CLASSIFIEDS == type) || (APT_CLASSIFIED_INFO == type)) { LLAvatarClassifieds* c_info = static_cast<LLAvatarClassifieds*>(data); if(c_info && getAvatarId() == c_info->target_id) diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp index 4bcd932d4b..a9a0c30e26 100755 --- a/indra/newview/llpanelplaces.cpp +++ b/indra/newview/llpanelplaces.cpp @@ -921,7 +921,12 @@ void LLPanelPlaces::onBackButtonClicked() void LLPanelPlaces::togglePickPanel(BOOL visible) { if (mPickPanel) + { mPickPanel->setVisible(visible); + mPlaceProfile->setVisible(!visible); + updateVerbs(); + } + } void LLPanelPlaces::togglePlaceInfoPanel(BOOL visible) @@ -1141,16 +1146,21 @@ void LLPanelPlaces::updateVerbs() bool is_agent_place_info_visible = mPlaceInfoType == AGENT_INFO_TYPE; bool is_create_landmark_visible = mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE; + bool is_pick_panel_visible = false; + if(mPickPanel) + { + is_pick_panel_visible = mPickPanel->isInVisibleChain(); + } bool have_3d_pos = ! mPosGlobal.isExactlyZero(); - mTeleportBtn->setVisible(!is_create_landmark_visible && !isLandmarkEditModeOn); - mShowOnMapBtn->setVisible(!is_create_landmark_visible && !isLandmarkEditModeOn); + mTeleportBtn->setVisible(!is_create_landmark_visible && !isLandmarkEditModeOn && !is_pick_panel_visible); + mShowOnMapBtn->setVisible(!is_create_landmark_visible && !isLandmarkEditModeOn && !is_pick_panel_visible); mOverflowBtn->setVisible(is_place_info_visible && !is_create_landmark_visible && !isLandmarkEditModeOn); mEditBtn->setVisible(mPlaceInfoType == LANDMARK_INFO_TYPE && !isLandmarkEditModeOn); mSaveBtn->setVisible(isLandmarkEditModeOn); mCancelBtn->setVisible(isLandmarkEditModeOn); mCloseBtn->setVisible(is_create_landmark_visible && !isLandmarkEditModeOn); - mPlaceInfoBtn->setVisible(!is_place_info_visible && !is_create_landmark_visible && !isLandmarkEditModeOn); + mPlaceInfoBtn->setVisible(!is_place_info_visible && !is_create_landmark_visible && !isLandmarkEditModeOn && !is_pick_panel_visible); mPlaceInfoBtn->setEnabled(!is_create_landmark_visible && !isLandmarkEditModeOn && have_3d_pos); diff --git a/indra/newview/llpaneltopinfobar.cpp b/indra/newview/llpaneltopinfobar.cpp index ac9a31ce4e..109013498e 100755 --- a/indra/newview/llpaneltopinfobar.cpp +++ b/indra/newview/llpaneltopinfobar.cpp @@ -313,14 +313,11 @@ void LLPanelTopInfoBar::updateParcelIcons() bool allow_damage = vpm->allowAgentDamage(agent_region, current_parcel); bool see_avs = current_parcel->getSeeAVs(); - bool is_parcel_owner = (gAgent.getID() == current_parcel->getOwnerID()); - bool allow_group_modify = (gAgent.isInGroup(current_parcel->getGroupID()) && current_parcel->getAllowGroupModify()); - // Most icons are "block this ability" mParcelIcon[VOICE_ICON]->setVisible( !allow_voice ); mParcelIcon[FLY_ICON]->setVisible( !allow_fly ); mParcelIcon[PUSH_ICON]->setVisible( !allow_push ); - mParcelIcon[BUILD_ICON]->setVisible( !allow_build && !is_parcel_owner && !allow_group_modify ); + mParcelIcon[BUILD_ICON]->setVisible( !allow_build ); mParcelIcon[SCRIPTS_ICON]->setVisible( !allow_scripts ); mParcelIcon[DAMAGE_ICON]->setVisible( allow_damage ); mDamageText->setVisible(allow_damage); diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 1bbb22416d..67832c5994 100755 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -1391,6 +1391,8 @@ void LLLiveLSLEditor::buildExperienceList() else { mExperiences->setEnabled(TRUE); + mExperiences->sortByName(TRUE); + mExperiences->setCurrentByIndex(mExperiences->getCurrentIndex()); getChild<LLButton>("view_profile")->setVisible(TRUE); } } @@ -2621,8 +2623,12 @@ void LLLiveLSLEditor::onLoad(void* userdata) void LLLiveLSLEditor::onSave(void* userdata, BOOL close_after_save) { LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata; - self->mCloseAfterSave = close_after_save; - self->saveIfNeeded(); + if(self) + { + self->mCloseAfterSave = close_after_save; + self->mScriptEd->mErrorList->setCommentText(""); + self->saveIfNeeded(); + } } diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp index f91a18d8d3..c8cf0faa15 100755 --- a/indra/newview/llpreviewtexture.cpp +++ b/indra/newview/llpreviewtexture.cpp @@ -618,4 +618,5 @@ void LLPreviewTexture::setObjectID(const LLUUID& object_id) mAssetStatus = PREVIEW_ASSET_UNLOADED; loadAsset(); } + refreshFromItem(); } diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 4fafbf917b..55bcb3dc65 100755 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -2127,7 +2127,7 @@ void LLSelectMgr::selectionRemoveMaterial() { LL_DEBUGS("Materials") << "Removing material from object " << object->getID() << " face " << face << LL_ENDL; LLMaterialMgr::getInstance()->remove(object->getID(),face); - object->setTEMaterialParams(face, NULL); + object->setTEMaterialParams(face, NULL, FALSE); } return true; } @@ -4950,6 +4950,7 @@ void LLSelectMgr::sendListToRegions(const std::string& message_name, ESendType send_type) { LLSelectNode* node; + LLSelectNode* linkset_root = NULL; LLViewerRegion* last_region; LLViewerRegion* current_region; @@ -4957,6 +4958,8 @@ void LLSelectMgr::sendListToRegions(const std::string& message_name, S32 packets_sent = 0; S32 objects_in_this_packet = 0; + bool link_operation = message_name == "ObjectLink"; + //clear update override data (allow next update through) struct f : public LLSelectedNodeFunctor { @@ -5065,6 +5068,12 @@ void LLSelectMgr::sendListToRegions(const std::string& message_name, && (! gMessageSystem->isSendFull(NULL)) && (objects_in_this_packet < MAX_OBJECTS_PER_PACKET)) { + if (link_operation && linkset_root == NULL) + { + // linksets over 254 will be split into multiple messages, + // but we need to provide same root for all messages or we will get separate linksets + linkset_root = node; + } // add another instance of the body of the data (*pack_body)(node, user_data); // do any related logging @@ -5093,6 +5102,22 @@ void LLSelectMgr::sendListToRegions(const std::string& message_name, gMessageSystem->newMessage(message_name.c_str()); (*pack_header)(user_data); + if (linkset_root != NULL) + { + if (current_region != last_region) + { + // root should be in one region with the child, reset it + linkset_root = NULL; + } + else + { + // add root instance into new message + (*pack_body)(linkset_root, user_data); + ++objects_sent; + ++objects_in_this_packet; + } + } + // don't move to the next object, we still need to add the // body data. } diff --git a/indra/newview/llsidepaneltaskinfo.cpp b/indra/newview/llsidepaneltaskinfo.cpp index 17ecfab4fb..2548d730f0 100755 --- a/indra/newview/llsidepaneltaskinfo.cpp +++ b/indra/newview/llsidepaneltaskinfo.cpp @@ -370,18 +370,25 @@ void LLSidepanelTaskInfo::refresh() // Update creator text field getChildView("Creator:")->setEnabled(TRUE); + std::string creator_name; - LLSelectMgr::getInstance()->selectGetCreator(mCreatorID, creator_name); + LLUUID creator_id; + LLSelectMgr::getInstance()->selectGetCreator(creator_id, creator_name); - getChild<LLUICtrl>("Creator Name")->setValue(creator_name); - getChildView("Creator Name")->setEnabled(TRUE); + if(creator_id != mCreatorID ) + { + mDACreatorName->setValue(creator_name); + mCreatorID = creator_id; + } + mDACreatorName->setEnabled(TRUE); // Update owner text field getChildView("Owner:")->setEnabled(TRUE); std::string owner_name; - const BOOL owners_identical = LLSelectMgr::getInstance()->selectGetOwner(mOwnerID, owner_name); - if (mOwnerID.isNull()) + LLUUID owner_id; + const BOOL owners_identical = LLSelectMgr::getInstance()->selectGetOwner(owner_id, owner_name); + if (owner_id.isNull()) { if (LLSelectMgr::getInstance()->selectIsGroupOwned()) { @@ -402,7 +409,12 @@ void LLSidepanelTaskInfo::refresh() } } } - getChild<LLUICtrl>("Owner Name")->setValue(owner_name); + + if(owner_id.isNull() || (owner_id != mOwnerID)) + { + mDAOwnerName->setValue(owner_name); + mOwnerID = owner_id; + } getChildView("Owner Name")->setEnabled(TRUE); // update group text field diff --git a/indra/newview/llslurl.cpp b/indra/newview/llslurl.cpp index 728fc69723..a8e012bfa1 100755 --- a/indra/newview/llslurl.cpp +++ b/indra/newview/llslurl.cpp @@ -271,7 +271,14 @@ LLSLURL::LLSLURL(const std::string& slurl) // at this point, head of the path array should be [ <region>, <x>, <y>, <z> ] where x, y and z // are collectively optional // are optional + mRegion = LLURI::unescape(path_array[0].asString()); + + if(LLStringUtil::containsNonprintable(mRegion)) + { + LLStringUtil::stripNonprintable(mRegion); + } + path_array.erase(0); // parse the x, y, and optionally z diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index 0ae8a338e0..6af9d61a54 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -195,7 +195,8 @@ void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot, BOOL new_thumbnail // Stop shining animation. mShineAnimTimer.stop(); mSnapshotDelayTimer.start(); - mSnapshotDelayTimer.setTimerExpirySec(delay); + mSnapshotDelayTimer.resetWithExpiry(delay); + mPosTakenGlobal = gAgentCamera.getCameraPositionGlobal(); @@ -670,10 +671,27 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) return FALSE; } - // If we're in freeze-frame mode and camera has moved, update snapshot. + if (previewp->mSnapshotDelayTimer.getStarted()) // Wait for a snapshot delay timer + { + if (!previewp->mSnapshotDelayTimer.hasExpired()) + { + return FALSE; + } + previewp->mSnapshotDelayTimer.stop(); + } + + if (LLToolCamera::getInstance()->hasMouseCapture()) // Hide full-screen preview while camming, either don't take snapshots while ALT-zoom active + { + previewp->setVisible(FALSE); + return FALSE; + } + + // If we're in freeze-frame and/or auto update mode and camera has moved, update snapshot. LLVector3 new_camera_pos = LLViewerCamera::getInstance()->getOrigin(); LLQuaternion new_camera_rot = LLViewerCamera::getInstance()->getQuaternion(); - if (previewp->mForceUpdateSnapshot || (gSavedSettings.getBOOL("FreezeTime") && previewp->mAllowFullScreenPreview && + if (previewp->mForceUpdateSnapshot || + (((gSavedSettings.getBOOL("AutoSnapshot") && LLView::isAvailable(previewp->mViewContainer)) || + (gSavedSettings.getBOOL("FreezeTime") && previewp->mAllowFullScreenPreview)) && (new_camera_pos != previewp->mCameraPos || dot(new_camera_rot, previewp->mCameraRot) < 0.995f))) { previewp->mCameraPos = new_camera_pos; @@ -688,11 +706,7 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) previewp->mForceUpdateSnapshot = FALSE; } - // see if it's time yet to snap the shot and bomb out otherwise. - previewp->mSnapshotActive = - (previewp->mSnapshotDelayTimer.getStarted() && previewp->mSnapshotDelayTimer.hasExpired()) - && !LLToolCamera::getInstance()->hasMouseCapture(); // don't take snapshots while ALT-zoom active - if (!previewp->mSnapshotActive && previewp->getSnapshotUpToDate() && previewp->getThumbnailUpToDate()) + if (previewp->getSnapshotUpToDate() && previewp->getThumbnailUpToDate()) { return FALSE; } @@ -706,6 +720,8 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) previewp->mPreviewImage = new LLImageRaw; } + previewp->mSnapshotActive = TRUE; + previewp->setVisible(FALSE); previewp->setEnabled(FALSE); @@ -734,40 +750,9 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) // Full size preview is set: get the decoded image result and save it for animation if (gSavedSettings.getBOOL("UseFreezeFrame") && previewp->mAllowFullScreenPreview) { - // Get the decoded version of the formatted image - previewp->getEncodedImage(); - - // We need to scale that a bit for display... - LLPointer<LLImageRaw> scaled = new LLImageRaw( - previewp->mPreviewImageEncoded->getData(), - previewp->mPreviewImageEncoded->getWidth(), - previewp->mPreviewImageEncoded->getHeight(), - previewp->mPreviewImageEncoded->getComponents()); - - if (!scaled->isBufferInvalid()) - { - // leave original image dimensions, just scale up texture buffer - if (previewp->mPreviewImageEncoded->getWidth() > 1024 || previewp->mPreviewImageEncoded->getHeight() > 1024) - { - // go ahead and shrink image to appropriate power of 2 for display - scaled->biasedScaleToPowerOfTwo(1024); - previewp->setImageScaled(TRUE); - } - else - { - // expand image but keep original image data intact - scaled->expandToPowerOfTwo(1024, FALSE); - } - - previewp->mViewerImage[previewp->mCurImageIndex] = LLViewerTextureManager::getLocalTexture(scaled.get(), FALSE); - LLPointer<LLViewerTexture> curr_preview_image = previewp->mViewerImage[previewp->mCurImageIndex]; - gGL.getTexUnit(0)->bind(curr_preview_image); - curr_preview_image->setFilteringOption(previewp->getSnapshotType() == SNAPSHOT_TEXTURE ? LLTexUnit::TFO_ANISOTROPIC : LLTexUnit::TFO_POINT); - curr_preview_image->setAddressMode(LLTexUnit::TAM_CLAMP); - - previewp->mShineCountdown = 4; // wait a few frames to avoid animation glitch due to readback this frame - } + previewp->prepareFreezeFrame(); } + // The snapshot is updated now... previewp->mSnapshotUpToDate = TRUE; @@ -777,7 +762,6 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) } previewp->getWindow()->decBusyCount(); previewp->setVisible(gSavedSettings.getBOOL("UseFreezeFrame") && previewp->mAllowFullScreenPreview); // only show fullscreen preview when in freeze frame mode - previewp->mSnapshotDelayTimer.stop(); previewp->mSnapshotActive = FALSE; LL_DEBUGS() << "done creating snapshot" << LL_ENDL; } @@ -796,6 +780,47 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview ) return TRUE; } +void LLSnapshotLivePreview::prepareFreezeFrame() +{ + // Get the decoded version of the formatted image + getEncodedImage(); + + // We need to scale that a bit for display... + LLPointer<LLImageRaw> scaled = new LLImageRaw( + mPreviewImageEncoded->getData(), + mPreviewImageEncoded->getWidth(), + mPreviewImageEncoded->getHeight(), + mPreviewImageEncoded->getComponents()); + + if (!scaled->isBufferInvalid()) + { + // leave original image dimensions, just scale up texture buffer + if (mPreviewImageEncoded->getWidth() > 1024 || mPreviewImageEncoded->getHeight() > 1024) + { + // go ahead and shrink image to appropriate power of 2 for display + scaled->biasedScaleToPowerOfTwo(1024); + setImageScaled(TRUE); + } + else + { + // expand image but keep original image data intact + scaled->expandToPowerOfTwo(1024, FALSE); + } + + mViewerImage[mCurImageIndex] = LLViewerTextureManager::getLocalTexture(scaled.get(), FALSE); + LLPointer<LLViewerTexture> curr_preview_image = mViewerImage[mCurImageIndex]; + gGL.getTexUnit(0)->bind(curr_preview_image); + curr_preview_image->setFilteringOption(getSnapshotType() == SNAPSHOT_TEXTURE ? LLTexUnit::TFO_ANISOTROPIC : LLTexUnit::TFO_POINT); + curr_preview_image->setAddressMode(LLTexUnit::TAM_CLAMP); + + + if (gSavedSettings.getBOOL("UseFreezeFrame") && mAllowFullScreenPreview) + { + mShineCountdown = 4; // wait a few frames to avoid animation glitch due to readback this frame + } + } +} + S32 LLSnapshotLivePreview::getEncodedImageWidth() const { S32 width = getWidth(); diff --git a/indra/newview/llsnapshotlivepreview.h b/indra/newview/llsnapshotlivepreview.h index fed33bf37c..57e5d83f8e 100644 --- a/indra/newview/llsnapshotlivepreview.h +++ b/indra/newview/llsnapshotlivepreview.h @@ -119,7 +119,7 @@ public: void generateThumbnailImage(BOOL force_update = FALSE) ; void resetThumbnailImage() { mThumbnailImage = NULL ; } void drawPreviewRect(S32 offset_x, S32 offset_y) ; - + void prepareFreezeFrame(); LLViewerTexture* getBigThumbnailImage(); S32 getBigThumbnailWidth() const { return mBigThumbnailWidth ; } diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index 7867e1573c..44c980c96f 100755 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -547,21 +547,8 @@ void LLSpeakerMgr::updateSpeakerList() // For groups, we need to hit the group manager. // Note: The session uuid and the group uuid are actually one and the same. If that was to change, this will fail. LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(session_id); - F32 large_group_delay = 0.f; - if (gdatap) - { - //This is a viewer-side bandaid for maint-4414 it does not fix the core issue. - large_group_delay = (F32)(gdatap->mMemberCount / 5000); - } - - const F32 load_group_timeout = gSavedSettings.getF32("ChatLoadGroupTimeout") + large_group_delay; - - if (!gdatap && (mGetListTime.getElapsedTimeF32() >= load_group_timeout)) - { - // Request the data the first time around - LLGroupMgr::getInstance()->sendCapGroupMembersRequest(session_id); - } - else if (gdatap && gdatap->isMemberDataComplete() && !gdatap->mMembers.empty()) + + if (gdatap && gdatap->isMemberDataComplete() && !gdatap->mMembers.empty()) { // Add group members when we get the complete list (note: can take a while before we get that list) LLGroupMgrGroupData::member_list_t::iterator member_it = gdatap->mMembers.begin(); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index d2050aec3e..965aad517d 100755 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -421,9 +421,7 @@ bool idle_startup() gSavedSettings.setS32("LastFeatureVersion", LLFeatureManager::getInstance()->getVersion()); gSavedSettings.setString("LastGPUString", thisGPU); - // load dynamic GPU/feature tables from website (S3) - LLFeatureManager::getInstance()->fetchHTTPTables(); - + std::string xml_file = LLUI::locateSkin("xui_version.xml"); LLXMLNodePtr root; bool xml_ok = false; @@ -3238,6 +3236,23 @@ bool process_login_success_response() LLStringUtil::trim(gDisplayName); } } + std::string first_name; + if(response.has("first_name")) + { + first_name = response["first_name"].asString(); + LLStringUtil::replaceChar(first_name, '"', ' '); + LLStringUtil::trim(first_name); + gAgentUsername = first_name; + } + + if(response.has("last_name") && !gAgentUsername.empty() && (gAgentUsername != "Resident")) + { + std::string last_name = response["last_name"].asString(); + LLStringUtil::replaceChar(last_name, '"', ' '); + LLStringUtil::trim(last_name); + gAgentUsername = gAgentUsername + " " + last_name; + } + if(gDisplayName.empty()) { if(response.has("first_name")) @@ -3258,6 +3273,7 @@ bool process_login_success_response() gDisplayName += text; } } + if(gDisplayName.empty()) { gDisplayName.assign(gUserCredential->asString()); diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index 8babb874f8..8f64cff47c 100755 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -296,158 +296,6 @@ BOOL LLIMWellWindow::ObjectRowPanel::handleRightMouseDown(S32 x, S32 y, MASK mas } /************************************************************************/ -/* LLNotificationWellWindow implementation */ -/************************************************************************/ - -////////////////////////////////////////////////////////////////////////// -// PUBLIC METHODS -LLNotificationWellWindow::WellNotificationChannel::WellNotificationChannel(LLNotificationWellWindow* well_window) -: LLNotificationChannel(LLNotificationChannel::Params().name(well_window->getPathname())), - mWellWindow(well_window) -{ - connectToChannel("Notifications"); - connectToChannel("Group Notifications"); - connectToChannel("Offer"); -} - -LLNotificationWellWindow::LLNotificationWellWindow(const LLSD& key) -: LLSysWellWindow(key) -{ - mNotificationUpdates.reset(new WellNotificationChannel(this)); -} - -// static -LLNotificationWellWindow* LLNotificationWellWindow::getInstance(const LLSD& key /*= LLSD()*/) -{ - return LLFloaterReg::getTypedInstance<LLNotificationWellWindow>("notification_well_window", key); -} - -// virtual -BOOL LLNotificationWellWindow::postBuild() -{ - BOOL rv = LLSysWellWindow::postBuild(); - setTitle(getString("title_notification_well_window")); - return rv; -} - -// virtual -void LLNotificationWellWindow::setVisible(BOOL visible) -{ - if (visible) - { - // when Notification channel is cleared, storable toasts will be added into the list. - clearScreenChannels(); - } - - LLSysWellWindow::setVisible(visible); -} - -//--------------------------------------------------------------------------------- -void LLNotificationWellWindow::addItem(LLSysWellItem::Params p) -{ - LLSD value = p.notification_id; - // do not add clones - if( mMessageList->getItemByValue(value)) - return; - - LLSysWellItem* new_item = new LLSysWellItem(p); - if (mMessageList->addItem(new_item, value, ADD_TOP)) - { - mSysWellChiclet->updateWidget(isWindowEmpty()); - reshapeWindow(); - new_item->setOnItemCloseCallback(boost::bind(&LLNotificationWellWindow::onItemClose, this, _1)); - new_item->setOnItemClickCallback(boost::bind(&LLNotificationWellWindow::onItemClick, this, _1)); - } - else - { - LL_WARNS() << "Unable to add Notification into the list, notification ID: " << p.notification_id - << ", title: " << p.title - << LL_ENDL; - - new_item->die(); - } -} - -void LLNotificationWellWindow::closeAll() -{ - // Need to clear notification channel, to add storable toasts into the list. - clearScreenChannels(); - std::vector<LLPanel*> items; - mMessageList->getItems(items); - for (std::vector<LLPanel*>::iterator - iter = items.begin(), - iter_end = items.end(); - iter != iter_end; ++iter) - { - LLSysWellItem* sys_well_item = dynamic_cast<LLSysWellItem*>(*iter); - if (sys_well_item) - onItemClose(sys_well_item); - } -} - -////////////////////////////////////////////////////////////////////////// -// PRIVATE METHODS -void LLNotificationWellWindow::initChannel() -{ - LLSysWellWindow::initChannel(); - if(mChannel) - { - mChannel->addOnStoreToastCallback(boost::bind(&LLNotificationWellWindow::onStoreToast, this, _1, _2)); - } -} - -void LLNotificationWellWindow::clearScreenChannels() -{ - // 1 - remove StartUp toast and channel if present - if(!LLNotificationsUI::LLScreenChannel::getStartUpToastShown()) - { - LLNotificationsUI::LLChannelManager::getInstance()->onStartUpToastClose(); - } - - // 2 - remove toasts in Notification channel - if(mChannel) - { - mChannel->removeAndStoreAllStorableToasts(); - } -} - -void LLNotificationWellWindow::onStoreToast(LLPanel* info_panel, LLUUID id) -{ - LLSysWellItem::Params p; - p.notification_id = id; - p.title = static_cast<LLToastPanel*>(info_panel)->getTitle(); - addItem(p); -} - -void LLNotificationWellWindow::onItemClick(LLSysWellItem* item) -{ - LLUUID id = item->getID(); - LLFloaterReg::showInstance("inspect_toast", id); -} - -void LLNotificationWellWindow::onItemClose(LLSysWellItem* item) -{ - LLUUID id = item->getID(); - - if(mChannel) - { - // removeItemByID() is invoked from killToastByNotificationID() and item will removed; - mChannel->killToastByNotificationID(id); - } - else - { - // removeItemByID() should be called one time for each item to remove it from notification well - removeItemByID(id); - } - -} - -void LLNotificationWellWindow::onAdd( LLNotificationPtr notify ) -{ - removeItemByID(notify->getID()); -} - -/************************************************************************/ /* LLIMWellWindow implementation */ /************************************************************************/ diff --git a/indra/newview/llsyswellwindow.h b/indra/newview/llsyswellwindow.h index 71b41476f5..d02293e6ff 100755 --- a/indra/newview/llsyswellwindow.h +++ b/indra/newview/llsyswellwindow.h @@ -96,57 +96,6 @@ protected: }; /** - * Class intended to manage incoming notifications. - * - * It contains a list of notifications that have not been responded to. - */ -class LLNotificationWellWindow : public LLSysWellWindow -{ -public: - LLNotificationWellWindow(const LLSD& key); - static LLNotificationWellWindow* getInstance(const LLSD& key = LLSD()); - - /*virtual*/ BOOL postBuild(); - /*virtual*/ void setVisible(BOOL visible); - /*virtual*/ void onAdd(LLNotificationPtr notify); - // Operating with items - void addItem(LLSysWellItem::Params p); - - // Closes all notifications and removes them from the Notification Well - void closeAll(); - -protected: - struct WellNotificationChannel : public LLNotificationChannel - { - WellNotificationChannel(LLNotificationWellWindow*); - void onDelete(LLNotificationPtr notify) - { - mWellWindow->removeItemByID(notify->getID()); - } - - LLNotificationWellWindow* mWellWindow; - }; - - LLNotificationChannelPtr mNotificationUpdates; - /*virtual*/ const std::string& getAnchorViewName() { return NOTIFICATION_WELL_ANCHOR_NAME; } - -private: - // init Window's channel - void initChannel(); - void clearScreenChannels(); - - void onStoreToast(LLPanel* info_panel, LLUUID id); - - // Handlers - void onItemClick(LLSysWellItem* item); - void onItemClose(LLSysWellItem* item); - - // ID of a toast loaded by user (by clicking notification well item) - LLUUID mLoadedToastId; - -}; - -/** * Class intended to manage incoming messages in IM chats. * * It contains a list list of all active IM sessions. diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index ef852bc905..bcdf8360ed 100755 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -1544,17 +1544,20 @@ void LLTextureCache::purgeAllTextures(bool purge_directories) { std::string dirname = mTexturesDirName + delem + subdirs[i]; LL_INFOS() << "Deleting files in directory: " << dirname << LL_ENDL; - gDirUtilp->deleteFilesInDir(dirname, mask); if (purge_directories) { - LLFile::rmdir(dirname); + gDirUtilp->deleteDirAndContents(dirname); + } + else + { + gDirUtilp->deleteFilesInDir(dirname, mask); } } if (purge_directories) { gDirUtilp->deleteFilesInDir(mTexturesDirName, mask); LLFile::rmdir(mTexturesDirName); - } + } } mHeaderIDMap.clear(); mTexturesSizeMap.clear(); diff --git a/indra/newview/lltoastgroupnotifypanel.cpp b/indra/newview/lltoastgroupnotifypanel.cpp index e00b18dedb..e22f527a65 100755 --- a/indra/newview/lltoastgroupnotifypanel.cpp +++ b/indra/newview/lltoastgroupnotifypanel.cpp @@ -92,7 +92,7 @@ LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(const LLNotificationPtr& notifi +LLTrans::getString("UTCTimeSec")+"] [" +LLTrans::getString("UTCTimeTimezone")+"]"; const LLDate timeStamp = notification->getDate(); - LLDate notice_date = timeStamp.notNull() ? timeStamp : LLDate::now(); + LLDate notice_date = timeStamp.notNull() ? timeStamp : payload["received_time"].asDate(); LLSD substitution; substitution["datetime"] = (S32) notice_date.secondsSinceEpoch(); LLStringUtil::format(timeStr, substitution); diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 81fbc471b3..5c9aedcf8f 100755 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -1155,8 +1155,7 @@ void LLToolDragAndDrop::dropMesh(LLViewerObject* hit_obj, } LLSculptParams sculpt_params; - sculpt_params.setSculptTexture(asset_id); - sculpt_params.setSculptType(LL_SCULPT_TYPE_MESH); + sculpt_params.setSculptTexture(asset_id, LL_SCULPT_TYPE_MESH); hit_obj->setParameterEntry(LLNetworkData::PARAMS_SCULPT, sculpt_params, TRUE); dialog_refresh_all(); diff --git a/indra/newview/llurllineeditorctrl.cpp b/indra/newview/llurllineeditorctrl.cpp index cad5769042..8a61114852 100755 --- a/indra/newview/llurllineeditorctrl.cpp +++ b/indra/newview/llurllineeditorctrl.cpp @@ -84,7 +84,7 @@ void LLURLLineEditor::copyEscapedURLToClipboard() const std::string unescaped_text = wstring_to_utf8str(mText.getWString().substr(left_pos, length)); LLWString text_to_copy; // *HACK: Because LLSLURL is currently broken we cannot use it to check if unescaped_text is a valid SLURL (see EXT-8335). - if (LLStringUtil::startsWith(unescaped_text, "http://")) // SLURL + if (LLStringUtil::startsWith(unescaped_text, "http://") || LLStringUtil::startsWith(unescaped_text, "secondlife://")) // SLURL text_to_copy = utf8str_to_wstring(LLWeb::escapeURL(unescaped_text)); else // human-readable location text_to_copy = utf8str_to_wstring(unescaped_text); diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 1178652408..14a2627f27 100755 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -90,6 +90,7 @@ #include "llfloatermodelpreview.h" #include "llfloaternamedesc.h" #include "llfloaternotificationsconsole.h" +#include "llfloaternotificationstabbed.h" #include "llfloaterobjectweights.h" #include "llfloateropenobject.h" #include "llfloateroutbox.h" @@ -268,7 +269,8 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("mini_map", "floater_map.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMap>); LLFloaterReg::add("notifications_console", "floater_notifications_console.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterNotificationConsole>); - LLFloaterReg::add("notification_well_window", "floater_sys_well.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLNotificationWellWindow>); + + LLFloaterReg::add("notification_well_window", "floater_notifications_tabbed.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterNotificationsTabbed>); LLFloaterReg::add("object_weights", "floater_object_weights.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterObjectWeights>); LLFloaterReg::add("openobject", "floater_openobject.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterOpenObject>); diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index 6803adfaa2..1ce42e97b8 100755 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -248,6 +248,7 @@ public: void setHomeURL(const std::string& home_url, const std::string& mime_type = LLStringUtil::null) { mHomeURL = home_url; mHomeMimeType = mime_type;}; void clearCache(); void setPageZoomFactor( double factor ); + double getPageZoomFactor() {return mZoomFactor;} std::string getMimeType() { return mMimeType; } void scaleMouse(S32 *mouse_x, S32 *mouse_y); void scaleTextureCoords(const LLVector2& texture_coords, S32 *x, S32 *y); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 2505ae6a9c..38d62dee5e 100755 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -57,6 +57,7 @@ #include "llfacebookconnect.h" #include "llfilepicker.h" #include "llfirstuse.h" +#include "llfloaterabout.h" #include "llfloaterbuy.h" #include "llfloaterbuycontents.h" #include "llbuycurrencyhtml.h" @@ -2139,6 +2140,22 @@ class LLAdvancedCheckShowObjectUpdates : public view_listener_t +/////////////////////// +// CHECK FOR UPDATES // +/////////////////////// + + + +class LLAdvancedCheckViewerUpdates : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLFloaterAboutUtil::checkUpdatesAndNotify(); + return true; + } +}; + + //////////////////// // COMPRESS IMAGE // //////////////////// @@ -7140,11 +7157,10 @@ void handle_selected_texture_info(void*) { msg.append( llformat("%d ", (S32)(it->second[i]))); } - - LLSD args; - args["MESSAGE"] = msg; - LLNotificationsUtil::add("SystemMessage", args); } + LLSD args; + args["MESSAGE"] = msg; + LLNotificationsUtil::add("SystemMessage", args); } } @@ -8920,6 +8936,7 @@ void initialize_menus() // Advanced (toplevel) view_listener_t::addMenu(new LLAdvancedToggleShowObjectUpdates(), "Advanced.ToggleShowObjectUpdates"); view_listener_t::addMenu(new LLAdvancedCheckShowObjectUpdates(), "Advanced.CheckShowObjectUpdates"); + view_listener_t::addMenu(new LLAdvancedCheckViewerUpdates(), "Advanced.CheckViewerUpdates"); view_listener_t::addMenu(new LLAdvancedCompressImage(), "Advanced.CompressImage"); view_listener_t::addMenu(new LLAdvancedShowDebugSettings(), "Advanced.ShowDebugSettings"); view_listener_t::addMenu(new LLAdvancedEnableViewAdminOptions(), "Advanced.EnableViewAdminOptions"); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 886725be79..6ba10373b9 100755 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -57,6 +57,7 @@ #include "llcallingcard.h" #include "llbuycurrencyhtml.h" #include "llfirstuse.h" +#include "llfloaterbump.h" #include "llfloaterbuyland.h" #include "llfloaterland.h" #include "llfloaterregioninfo.h" @@ -2349,7 +2350,8 @@ static void god_message_name_cb(const LLAvatarName& av_name, LLChat chat, std::s LLNotificationsUtil::add("GodMessage", args); // Treat like a system message and put in chat history. - chat.mText = av_name.getCompleteName() + ": " + message; + chat.mSourceType = CHAT_SOURCE_SYSTEM; + chat.mText = message; LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"); if (nearby_chat) @@ -2681,6 +2683,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) payload["sender_name"] = name; payload["group_id"] = group_id; payload["inventory_name"] = item_name; + payload["received_time"] = LLDate::now(); if(info && info->asLLSD()) { payload["inventory_offer"] = info->asLLSD(); @@ -2860,6 +2863,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) case IM_FROM_TASK: { + if (is_do_not_disturb && !is_owned_by_me) { return; @@ -2943,17 +2947,13 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) payload["from_id"] = from_id; payload["slurl"] = location; payload["name"] = name; - std::string session_name; + if (from_group) { payload["group_owned"] = "true"; } - LLNotification::Params params("ServerObjectMessage"); - params.substitutions = substitutions; - params.payload = payload; - - LLPostponedNotification::add<LLPostponedServerObjectNotification>(params, from_id, from_group); + LLNotificationsUtil::add("ServerObjectMessage", substitutions, payload); } break; @@ -3789,11 +3789,15 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) } } - LLSD msg_notify = LLSD(LLSD::emptyMap()); - msg_notify["session_id"] = LLUUID(); - msg_notify["from_id"] = chat.mFromID; - msg_notify["source_type"] = chat.mSourceType; - on_new_message(msg_notify); + if (mesg != "") + { + LLSD msg_notify = LLSD(LLSD::emptyMap()); + msg_notify["session_id"] = LLUUID(); + msg_notify["from_id"] = chat.mFromID; + msg_notify["source_type"] = chat.mSourceType; + on_new_message(msg_notify); + } + } } @@ -5635,6 +5639,7 @@ static void process_money_balance_reply_extended(LLMessageSystem* msg) } } final_args["MESSAGE"] = message; + payload["dest_id"] = dest_id; notification = success ? "PaymentSent" : "PaymentFailure"; } else { @@ -6263,6 +6268,11 @@ void process_mean_collision_alert_message(LLMessageSystem *msgsystem, void **use gCacheName->get(perp, false, boost::bind(&mean_name_callback, _1, _2, _3)); } } + LLFloaterBump* bumps_floater = LLFloaterBump::getInstance(); + if(bumps_floater && bumps_floater->isInVisibleChain()) + { + bumps_floater->populateCollisionList(); + } } void process_frozen_message(LLMessageSystem *msgsystem, void **user_data) diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index ac3f07fcd8..05d116704e 100755 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -2885,6 +2885,9 @@ struct LLFilenameAndTask { LLUUID mTaskID; std::string mFilename; + + // for sequencing in case of multiple updates + S16 mSerial; #ifdef _DEBUG static S32 sCount; LLFilenameAndTask() @@ -2920,9 +2923,17 @@ void LLViewerObject::processTaskInv(LLMessageSystem* msg, void** user_data) return; } - msg->getS16Fast(_PREHASH_InventoryData, _PREHASH_Serial, object->mInventorySerialNum); LLFilenameAndTask* ft = new LLFilenameAndTask; ft->mTaskID = task_id; + // we can receive multiple task updates simultaneously, make sure we will not rewrite newer with older update + msg->getS16Fast(_PREHASH_InventoryData, _PREHASH_Serial, ft->mSerial); + + if (ft->mSerial < object->mInventorySerialNum) + { + // viewer did some changes to inventory that were not saved yet. + LL_DEBUGS() << "Task inventory serial might be out of sync, server serial: " << ft->mSerial << " client serial: " << object->mInventorySerialNum << LL_ENDL; + object->mInventorySerialNum = ft->mSerial; + } std::string unclean_filename; msg->getStringFast(_PREHASH_InventoryData, _PREHASH_Filename, unclean_filename); @@ -2962,9 +2973,13 @@ void LLViewerObject::processTaskInvFile(void** user_data, S32 error_code, LLExtS { LLFilenameAndTask* ft = (LLFilenameAndTask*)user_data; LLViewerObject* object = NULL; - if(ft && (0 == error_code) && - (object = gObjectList.findObject(ft->mTaskID))) + + if (ft + && (0 == error_code) + && (object = gObjectList.findObject(ft->mTaskID)) + && ft->mSerial >= object->mInventorySerialNum) { + object->mInventorySerialNum = ft->mSerial; if (object->loadTaskInvFile(ft->mFilename)) { @@ -2995,7 +3010,7 @@ void LLViewerObject::processTaskInvFile(void** user_data, S32 error_code, LLExtS } else { - // This Occurs When to requests were made, and the first one + // This Occurs When two requests were made, and the first one // has already handled it. LL_DEBUGS() << "Problem loading task inventory. Return code: " << error_code << LL_ENDL; @@ -4637,7 +4652,7 @@ S32 LLViewerObject::setTEMaterialID(const U8 te, const LLMaterialID& pMaterialID return retval; } -S32 LLViewerObject::setTEMaterialParams(const U8 te, const LLMaterialPtr pMaterialParams) +S32 LLViewerObject::setTEMaterialParams(const U8 te, const LLMaterialPtr pMaterialParams, bool isInitFromServer) { S32 retval = 0; const LLTextureEntry *tep = getTE(te); @@ -4647,13 +4662,14 @@ S32 LLViewerObject::setTEMaterialParams(const U8 te, const LLMaterialPtr pMateri return 0; } - retval = LLPrimitive::setTEMaterialParams(te, pMaterialParams); + setTENormalMap(te, (pMaterialParams) ? pMaterialParams->getNormalID() : LLUUID::null); + setTESpecularMap(te, (pMaterialParams) ? pMaterialParams->getSpecularID() : LLUUID::null); + + retval = LLPrimitive::setTEMaterialParams(te, pMaterialParams, isInitFromServer); LL_DEBUGS("Material") << "Changing material params for te " << (S32)te << ", object " << mID << " (" << retval << ")" << LL_ENDL; - setTENormalMap(te, (pMaterialParams) ? pMaterialParams->getNormalID() : LLUUID::null); - setTESpecularMap(te, (pMaterialParams) ? pMaterialParams->getSpecularID() : LLUUID::null); refreshMaterials(); return retval; diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 05c87c153b..db2749f413 100755 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -330,7 +330,7 @@ public: /*virtual*/ S32 setTEMediaFlags(const U8 te, const U8 media_flags ); /*virtual*/ S32 setTEGlow(const U8 te, const F32 glow); /*virtual*/ S32 setTEMaterialID(const U8 te, const LLMaterialID& pMaterialID); - /*virtual*/ S32 setTEMaterialParams(const U8 te, const LLMaterialPtr pMaterialParams); + /*virtual*/ S32 setTEMaterialParams(const U8 te, const LLMaterialPtr pMaterialParams, bool isInitFromServer); // Used by Materials update functions to properly kick off rebuilds // of VBs etc when materials updates require changes. diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index f60829e9e8..24a6758312 100755 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -60,6 +60,7 @@ #include "llfeaturemanager.h" #include "llviewernetwork.h" #include "llmeshrepository.h" //for LLMeshRepository::sBytesReceived +#include "llsdserialize.h" namespace LLStatViewer { @@ -616,8 +617,10 @@ void send_stats() body["DisplayNamesShowUsername"] = gSavedSettings.getBOOL("NameTagShowUsernames"); body["MinimalSkin"] = false; - + LLViewerStats::getInstance()->addToMessage(body); + + LL_INFOS("LogViewerStatsPacket") << "Sending viewer statistics: " << body << LL_ENDL; LLHTTPClient::post(url, body, new ViewerStatsResponder()); LLViewerStats::instance().getRecording().resume(); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index f753448770..b97a1bde99 100755 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -160,6 +160,9 @@ const F32 HEAD_MOVEMENT_AVG_TIME = 0.9f; const S32 MORPH_MASK_REQUESTED_DISCARD = 0; +const F32 MAX_STANDOFF_FROM_ORIGIN = 3; +const F32 MAX_STANDOFF_DISTANCE_CHANGE = 32; + // Discard level at which to switch to baked textures // Should probably be 4 or 3, but didn't want to change it while change other logic - SJB const S32 SWITCH_TO_BAKED_DISCARD = 5; @@ -2132,7 +2135,7 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time) // animate the character // store off last frame's root position to be consistent with camera position - LLVector3 root_pos_last = mRoot->getWorldPosition(); + mLastRootPos = mRoot->getWorldPosition(); BOOL detailed_update = updateCharacter(agent); static LLUICachedControl<bool> visualizers_in_calls("ShowVoiceVisualizersInCalls", false); @@ -2150,7 +2153,7 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time) idleUpdateWindEffect(); } - idleUpdateNameTag( root_pos_last ); + idleUpdateNameTag( mLastRootPos ); idleUpdateRenderCost(); } @@ -6080,10 +6083,10 @@ void LLVOAvatar::getOffObject() { return; } - + LLViewerObject* sit_object = (LLViewerObject*)getParent(); - if (sit_object) + if (sit_object) { stopMotionFromSource(sit_object->getID()); LLFollowCamMgr::setCameraActive(sit_object->getID(), FALSE); @@ -6100,9 +6103,19 @@ void LLVOAvatar::getOffObject() } // assumes that transform will not be updated with drawable still having a parent + // or that drawable had no parent from the start LLVector3 cur_position_world = mDrawable->getWorldPosition(); LLQuaternion cur_rotation_world = mDrawable->getWorldRotation(); + if (mLastRootPos.length() >= MAX_STANDOFF_FROM_ORIGIN + && (cur_position_world.length() < MAX_STANDOFF_FROM_ORIGIN + || dist_vec(cur_position_world, mLastRootPos) > MAX_STANDOFF_DISTANCE_CHANGE)) + { + // Most likely drawable got updated too early or some updates were missed - we got relative position to non-existing parent + // restore coordinates from cache + cur_position_world = mLastRootPos; + } + // set *local* position based on last *world* position, since we're unparenting the avatar mDrawable->mXform.setPosition(cur_position_world); mDrawable->mXform.setRotation(cur_rotation_world); @@ -6278,6 +6291,7 @@ void LLVOAvatar::onGlobalColorChanged(const LLTexGlobalColor* global_color) BOOL LLVOAvatar::isVisible() const { return mDrawable.notNull() + && (!mOrphaned || isSelf()) && (mDrawable->isVisible() || mIsDummy); } diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 1c3f4f2aa7..5b4379165a 100755 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -869,6 +869,8 @@ public: private: // set this property only with LLVOAvatar::sitDown method BOOL mIsSitting; + // position backup in case of missing data + LLVector3 mLastRootPos; /** Hierarchy ** ** diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 0432f6f27c..19d61f6e33 100755 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -1624,6 +1624,66 @@ static LLTrace::BlockTimerStatHandle FTM_GEN_FLEX("Generate Flexies"); static LLTrace::BlockTimerStatHandle FTM_UPDATE_PRIMITIVES("Update Primitives"); static LLTrace::BlockTimerStatHandle FTM_UPDATE_RIGGED_VOLUME("Update Rigged"); +bool LLVOVolume::lodOrSculptChanged(LLDrawable *drawable, BOOL &compiled) +{ + bool regen_faces = false; + + LLVolume *old_volumep, *new_volumep; + F32 old_lod, new_lod; + S32 old_num_faces, new_num_faces; + + old_volumep = getVolume(); + old_lod = old_volumep->getDetail(); + old_num_faces = old_volumep->getNumFaces(); + old_volumep = NULL; + + { + LL_RECORD_BLOCK_TIME(FTM_GEN_VOLUME); + const LLVolumeParams &volume_params = getVolume()->getParams(); + setVolume(volume_params, 0); + } + + new_volumep = getVolume(); + new_lod = new_volumep->getDetail(); + new_num_faces = new_volumep->getNumFaces(); + new_volumep = NULL; + + if ((new_lod != old_lod) || mSculptChanged) + { + compiled = TRUE; + sNumLODChanges += new_num_faces; + + if ((S32)getNumTEs() != getVolume()->getNumFaces()) + { + setNumTEs(getVolume()->getNumFaces()); //mesh loading may change number of faces. + } + + drawable->setState(LLDrawable::REBUILD_VOLUME); // for face->genVolumeTriangles() + + { + LL_RECORD_BLOCK_TIME(FTM_GEN_TRIANGLES); + regen_faces = new_num_faces != old_num_faces || mNumFaces != (S32)getNumTEs(); + if (regen_faces) + { + regenFaces(); + } + + if (mSculptChanged) + { //changes in sculpt maps can thrash an object bounding box without + //triggering a spatial group bounding box update -- force spatial group + //to update bounding boxes + LLSpatialGroup* group = mDrawable->getSpatialGroup(); + if (group) + { + group->unbound(); + } + } + } + } + + return regen_faces; +} + BOOL LLVOVolume::updateGeometry(LLDrawable *drawable) { LL_RECORD_BLOCK_TIME(FTM_UPDATE_PRIMITIVES); @@ -1664,83 +1724,35 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable) return TRUE; // No update to complete } - if (mVolumeChanged || mFaceMappingChanged ) + if (mVolumeChanged || mFaceMappingChanged) { dirtySpatialGroup(drawable->isState(LLDrawable::IN_REBUILD_Q1)); - compiled = TRUE; + bool was_regen_faces = false; if (mVolumeChanged) { - LL_RECORD_BLOCK_TIME(FTM_GEN_VOLUME); - LLVolumeParams volume_params = getVolume()->getParams(); - setVolume(volume_params, 0); + was_regen_faces = lodOrSculptChanged(drawable, compiled); drawable->setState(LLDrawable::REBUILD_VOLUME); } - + else if (mSculptChanged || mLODChanged) { + compiled = TRUE; + was_regen_faces = lodOrSculptChanged(drawable, compiled); + } + + if (!was_regen_faces) { LL_RECORD_BLOCK_TIME(FTM_GEN_TRIANGLES); regenFaces(); - genBBoxes(FALSE); } + + genBBoxes(FALSE); } - else if ((mLODChanged) || (mSculptChanged)) + else if (mLODChanged || mSculptChanged) { dirtySpatialGroup(drawable->isState(LLDrawable::IN_REBUILD_Q1)); - - LLVolume *old_volumep, *new_volumep; - F32 old_lod, new_lod; - S32 old_num_faces, new_num_faces ; - - old_volumep = getVolume(); - old_lod = old_volumep->getDetail(); - old_num_faces = old_volumep->getNumFaces() ; - old_volumep = NULL ; - - { - LL_RECORD_BLOCK_TIME(FTM_GEN_VOLUME); - LLVolumeParams volume_params = getVolume()->getParams(); - setVolume(volume_params, 0); - } - - new_volumep = getVolume(); - new_lod = new_volumep->getDetail(); - new_num_faces = new_volumep->getNumFaces() ; - new_volumep = NULL ; - - if ((new_lod != old_lod) || mSculptChanged) - { - compiled = TRUE; - sNumLODChanges += new_num_faces ; - - if((S32)getNumTEs() != getVolume()->getNumFaces()) - { - setNumTEs(getVolume()->getNumFaces()); //mesh loading may change number of faces. - } - - drawable->setState(LLDrawable::REBUILD_VOLUME); // for face->genVolumeTriangles() - - { - LL_RECORD_BLOCK_TIME(FTM_GEN_TRIANGLES); - if (new_num_faces != old_num_faces || mNumFaces != (S32)getNumTEs()) - { - regenFaces(); - } - genBBoxes(FALSE); - - if (mSculptChanged) - { //changes in sculpt maps can thrash an object bounding box without - //triggering a spatial group bounding box update -- force spatial group - //to update bounding boxes - LLSpatialGroup* group = mDrawable->getSpatialGroup(); - if (group) - { - group->unbound(); - } - } - } - } - + compiled = TRUE; + lodOrSculptChanged(drawable, compiled); genBBoxes(FALSE); } // it has its own drawable (it's moved) or it has changed UVs or it has changed xforms from global<->local @@ -2010,7 +2022,7 @@ void LLVOVolume::setTEMaterialParamsCallbackTE(const LLUUID& objectID, const LLM LLTextureEntry* texture_entry = pVol->getTE(te); if (texture_entry && (texture_entry->getMaterialID() == pMaterialID)) { - pVol->setTEMaterialParams(te, pMaterialParams); + pVol->setTEMaterialParams(te, pMaterialParams, FALSE); } } } @@ -2081,7 +2093,7 @@ bool LLVOVolume::notifyAboutCreatingTexture(LLViewerTexture *texture) for(map_te_material::const_iterator it = new_material.begin(), end = new_material.end(); it != end; ++it) { LLMaterialMgr::getInstance()->put(getID(), it->first, *it->second); - LLViewerObject::setTEMaterialParams(it->first, it->second); + LLViewerObject::setTEMaterialParams(it->first, it->second, FALSE); } //clear wait-list @@ -2158,7 +2170,7 @@ bool LLVOVolume::notifyAboutMissingAsset(LLViewerTexture *texture) for(map_te_material::const_iterator it = new_material.begin(), end = new_material.end(); it != end; ++it) { LLMaterialMgr::getInstance()->put(getID(), it->first, *it->second); - LLViewerObject::setTEMaterialParams(it->first, it->second); + LLViewerObject::setTEMaterialParams(it->first, it->second, FALSE); } //clear wait-list @@ -2167,7 +2179,7 @@ bool LLVOVolume::notifyAboutMissingAsset(LLViewerTexture *texture) return 0 != new_material.size(); } -S32 LLVOVolume::setTEMaterialParams(const U8 te, const LLMaterialPtr pMaterialParams) +S32 LLVOVolume::setTEMaterialParams(const U8 te, const LLMaterialPtr pMaterialParams, bool isInitFromServer) { LLMaterialPtr pMaterial = const_cast<LLMaterialPtr&>(pMaterialParams); @@ -2264,7 +2276,7 @@ S32 LLVOVolume::setTEMaterialParams(const U8 te, const LLMaterialPtr pMaterialPa } } - S32 res = LLViewerObject::setTEMaterialParams(te, pMaterial); + S32 res = LLViewerObject::setTEMaterialParams(te, pMaterial, isInitFromServer); LL_DEBUGS("MaterialTEs") << "te " << (S32)te << " material " << ((pMaterial) ? pMaterial->asLLSD() : LLSD("null")) << " res " << res << ( LLSelectMgr::getInstance()->getSelection()->contains(const_cast<LLVOVolume*>(this), te) ? " selected" : " not selected" ) @@ -4163,7 +4175,7 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons } //build matrix palette - static const size_t kMaxJoints = 64; + static const size_t kMaxJoints = 52; LLMatrix4a mp[kMaxJoints]; LLMatrix4* mat = (LLMatrix4*) mp; @@ -4172,6 +4184,12 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons for (U32 j = 0; j < maxJoints; ++j) { LLJoint* joint = avatar->getJoint(skin->mJointNames[j]); + if (!joint) + { + // Fall back to a point inside the avatar if mesh is + // rigged to an unknown joint. + joint = avatar->getJoint("mPelvis"); + } if (joint) { mat[j] = skin->mInvBindMatrix[j]; @@ -4216,8 +4234,9 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons wght[k] = w - floorf(w); scale += wght[k]; } - - wght *= 1.f/scale; + // This is enforced in unpackVolumeFaces() + llassert(scale>0.f); + wght *= 1.f / scale; for (U32 k = 0; k < 4; k++) { @@ -4225,9 +4244,9 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons LLMatrix4a src; // Insure ref'd bone is in our clamped array of mats - llassert(idx[k] < kMaxJoints); - // clamp k to kMaxJoints to avoid reading garbage off stack in release - src.setMul(mp[idx[(k < kMaxJoints) ? k : 0]], w); + // clamp idx to maxJoints to avoid reading garbage off stack in release + S32 index = llclamp((S32)idx[k],(S32)0,(S32)kMaxJoints-1); + src.setMul(mp[index], w); final_mat.add(src); } diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index bbaca316b0..ff7438ac09 100755 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -192,7 +192,7 @@ public: static void setTEMaterialParamsCallbackTE(const LLUUID& objectID, const LLMaterialID& pMaterialID, const LLMaterialPtr pMaterialParams, U32 te); - /*virtual*/ S32 setTEMaterialParams(const U8 te, const LLMaterialPtr pMaterialParams); + /*virtual*/ S32 setTEMaterialParams(const U8 te, const LLMaterialPtr pMaterialParams, bool isInitFromServer); /*virtual*/ S32 setTEScale(const U8 te, const F32 s, const F32 t); /*virtual*/ S32 setTEScaleS(const U8 te, const F32 s); /*virtual*/ S32 setTEScaleT(const U8 te, const F32 t); @@ -339,6 +339,10 @@ protected: void cleanUpMediaImpls(); void addMediaImpl(LLViewerMediaImpl* media_impl, S32 texture_index) ; void removeMediaImpl(S32 texture_index) ; + +private: + bool lodOrSculptChanged(LLDrawable *drawable, BOOL &compiled); + public: static S32 getRenderComplexityMax() {return mRenderComplexity_last;} diff --git a/indra/newview/llworldmap.cpp b/indra/newview/llworldmap.cpp index bfae142812..837b30586b 100755 --- a/indra/newview/llworldmap.cpp +++ b/indra/newview/llworldmap.cpp @@ -383,6 +383,7 @@ void LLWorldMap::reloadItems(bool force) LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_MATURE_EVENT); LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_ADULT_EVENT); LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_LAND_FOR_SALE); + LLWorldMapMessage::getInstance()->sendItemRequest(MAP_ITEM_LAND_FOR_SALE_ADULT); } } diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index bdc884885f..8533625e50 100755 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -873,8 +873,11 @@ <color name="ToolbarDropZoneColor" value=".48 .69 1 .5" /> - - <!-- Generic color names (legacy) --> + <color + name="PanelNotificationListItem" + value="0.3 0.3 0.3 .3" /> + + <!-- Generic color names (legacy) --> <color name="white" value="1 1 1 1"/> diff --git a/indra/newview/skins/default/textures/icons/Icon_Attachment_Large.png b/indra/newview/skins/default/textures/icons/Icon_Attachment_Large.png Binary files differnew file mode 100644 index 0000000000..0732a33d93 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Icon_Attachment_Large.png diff --git a/indra/newview/skins/default/textures/icons/Icon_Attachment_Small.png b/indra/newview/skins/default/textures/icons/Icon_Attachment_Small.png Binary files differnew file mode 100644 index 0000000000..8124554902 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Icon_Attachment_Small.png diff --git a/indra/newview/skins/default/textures/icons/Icon_Notification_Condense.png b/indra/newview/skins/default/textures/icons/Icon_Notification_Condense.png Binary files differnew file mode 100644 index 0000000000..4d245eb57a --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Icon_Notification_Condense.png diff --git a/indra/newview/skins/default/textures/icons/Icon_Notification_Expand.png b/indra/newview/skins/default/textures/icons/Icon_Notification_Expand.png Binary files differnew file mode 100644 index 0000000000..186822da43 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Icon_Notification_Expand.png diff --git a/indra/newview/skins/default/textures/icons/System_Notification_Large.png b/indra/newview/skins/default/textures/icons/System_Notification_Large.png Binary files differnew file mode 100644 index 0000000000..434ce3e8b6 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/System_Notification_Large.png diff --git a/indra/newview/skins/default/textures/icons/System_Notification_Small.png b/indra/newview/skins/default/textures/icons/System_Notification_Small.png Binary files differnew file mode 100644 index 0000000000..027a8446d8 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/System_Notification_Small.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index a5f2ce1f84..e453d94883 100755 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -804,4 +804,9 @@ with the same filename but different name <texture name="Camera_Drag_Dot" file_name="world/CameraDragDot.png"/> <texture name="NavBar Separator" file_name="navbar/separator.png"/> + <texture name="Notification_Condense" file_name="icons/Icon_Notification_Condense.png" preload="true"/> + <texture name="Notification_Expand" file_name="icons/Icon_Notification_Expand.png" preload="true"/> + <texture name="System_Notification" file_name="icons/SL_Logo.png" preload="true"/> + <texture name="Icon_Attachment_Small" file_name="icons/Icon_Attachment_Small.png" preload="true"/> + <texture name="Icon_Attachment_Large" file_name="icons/Icon_Attachment_Large.png" preload="true"/> </textures> diff --git a/indra/newview/skins/default/xui/da/menu_viewer.xml b/indra/newview/skins/default/xui/da/menu_viewer.xml index aa6bc53672..299322001b 100755 --- a/indra/newview/skins/default/xui/da/menu_viewer.xml +++ b/indra/newview/skins/default/xui/da/menu_viewer.xml @@ -37,7 +37,7 @@ <menu_item_separator/> <menu_item_call label="Profil for sted" name="Place Profile"/> <menu_item_call label="Om land" name="About Land"/> - <menu_item_call label="Region/Estate" name="Region/Estate"/> + <menu_item_call label="Region/Estate" name="RegionEstate"/> <menu_item_call label="Køb dette land" name="Buy Land"/> <menu_item_call label="Mit land" name="My Land"/> <menu label="Vis" name="LandShow"> diff --git a/indra/newview/skins/default/xui/da/notifications.xml b/indra/newview/skins/default/xui/da/notifications.xml index 33b876bdb9..aad3b9d062 100755 --- a/indra/newview/skins/default/xui/da/notifications.xml +++ b/indra/newview/skins/default/xui/da/notifications.xml @@ -1311,9 +1311,6 @@ Prøv igen om lidt. <notification name="NoValidCircuit"> Ingen gyldig kode for kredsløb. </notification> - <notification name="NoValidTimestamp"> - Ikke et gyldigt klokkeslæt. - </notification> <notification name="NoPendingConnection"> Kunne ikke skabe fast forbindelse. </notification> diff --git a/indra/newview/skins/default/xui/de/menu_viewer.xml b/indra/newview/skins/default/xui/de/menu_viewer.xml index 1924ff4ec3..956530c990 100755 --- a/indra/newview/skins/default/xui/de/menu_viewer.xml +++ b/indra/newview/skins/default/xui/de/menu_viewer.xml @@ -15,7 +15,7 @@ <menu_item_check label="Fliegen" name="Fly"/> <menu_item_check label="Immer rennen" name="Always Run"/> <menu_item_call label="Animation meines Avatars stoppen" name="Stop Animating My Avatar"/> - <menu_item_call label="Gehen/Rennen/Fliegen..." name="Walk / run / fly"/> + <menu_item_call label="Gehen/Rennen/Fliegen..." name="WalkRunFly"/> </menu> <menu label="Status" name="Status"> <menu_item_check label="Abwesend" name="Away"/> @@ -64,7 +64,7 @@ <menu_item_call label="Foto" name="Take Snapshot"/> <menu_item_call label="Ortsprofil" name="Place Profile"/> <menu_item_call label="Landinformationen" name="About Land"/> - <menu_item_call label="Region/Grundbesitz" name="Region/Estate"/> + <menu_item_call label="Region/Grundbesitz" name="RegionEstate"/> <menu_item_call label="Mein Landbesitz..." name="My Land"/> <menu_item_call label="Dieses Land kaufen" name="Buy Land"/> <menu label="Anzeigen" name="LandShow"> diff --git a/indra/newview/skins/default/xui/de/notifications.xml b/indra/newview/skins/default/xui/de/notifications.xml index 0b1c18cd51..fa7db0a8a3 100755 --- a/indra/newview/skins/default/xui/de/notifications.xml +++ b/indra/newview/skins/default/xui/de/notifications.xml @@ -2802,9 +2802,6 @@ Versuchen Sie es in einigen Minuten erneut. <notification name="NoValidCircuit"> Kein gültiger Verbindungscode. </notification> - <notification name="NoValidTimestamp"> - Kein gültiger Zeitstempel. - </notification> <notification name="NoPendingConnection"> Verbindung kann nicht hergestellt werden. </notification> diff --git a/indra/newview/skins/default/xui/en/floater_about.xml b/indra/newview/skins/default/xui/en/floater_about.xml index 60f36770bb..ec87b3684e 100755 --- a/indra/newview/skins/default/xui/en/floater_about.xml +++ b/indra/newview/skins/default/xui/en/floater_about.xml @@ -43,6 +43,14 @@ top_pad="5" height="25" width="180" /> + <button + follows="left|top" + label="Check for updates" + name="update_btn" + left_pad="70" + top_delta="0" + height="25" + width="180" /> </panel> <panel border="true" diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml index 90d45d5ebc..edf028bf60 100755 --- a/indra/newview/skins/default/xui/en/floater_about_land.xml +++ b/indra/newview/skins/default/xui/en/floater_about_land.xml @@ -2013,7 +2013,7 @@ Only large parcels can be listed in search. name="AllowedText" top="0" width="230"> - Allowed Residents + Allowed Residents ([COUNT]) </text> <name_list column_padding="0" @@ -2062,7 +2062,7 @@ Only large parcels can be listed in search. name="BanCheck" top="0" width="200"> - Banned Residents + Banned Residents ([COUNT]) </text> <name_list column_padding="0" diff --git a/indra/newview/skins/default/xui/en/floater_bumps.xml b/indra/newview/skins/default/xui/en/floater_bumps.xml index 1f2fe62b3c..126e3aac48 100755 --- a/indra/newview/skins/default/xui/en/floater_bumps.xml +++ b/indra/newview/skins/default/xui/en/floater_bumps.xml @@ -7,7 +7,7 @@ help_topic="floater_bumps" save_rect="true" title="BUMPS, PUSHES & HITS" - width="400"> + width="420"> <floater.string name="none_detected"> None detected @@ -34,7 +34,7 @@ </floater.string> <floater.string name="timeStr"> - [[hour,datetime,slt]:[min,datetime,slt]] + [[hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt]] </floater.string> <scroll_list draw_border="false" @@ -45,5 +45,5 @@ multi_select="true" name="bump_list" top="20" - width="388" /> + width="408" /> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_experiences.xml b/indra/newview/skins/default/xui/en/floater_experiences.xml index 70e7507907..442da887c5 100644 --- a/indra/newview/skins/default/xui/en/floater_experiences.xml +++ b/indra/newview/skins/default/xui/en/floater_experiences.xml @@ -12,7 +12,7 @@ name="floater_experiences" save_rect="true" single_instance="true" - reuse_instance="false" + bg_opaque_color="0 0.5 0 0.3" title="EXPERIENCES"> <tab_container 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 a4acd1df78..7183b2f1f9 100755 --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml @@ -31,6 +31,7 @@ <string name="mesh_status_too_many_vertices">Level of detail has too many vertices.</string> <string name="mesh_status_missing_lod">Missing required level of detail.</string> <string name="mesh_status_invalid_material_list">LOD materials are not a subset of reference model.</string> + <string name="phys_status_vertex_limit_exceeded">Some physical hulls exceed vertex limitations.</string> <string name="layer_all">All</string> <!-- Text to display in physics layer combo box for "all layers" --> <string name="decomposing">Analyzing...</string> <string name="simplifying">Simplifying...</string> @@ -66,7 +67,7 @@ follows="top|left" layout="topleft" height="19" - max_length_bytes="64" + max_length_bytes="63" name="description_form" prevalidate_callback="ascii" top_pad="5" @@ -1027,19 +1028,19 @@ bg_alpha_color="0 0 0 0" bg_opaque_color="0 0 0 0.3" follows="left|top" - height="16" + height="19" layout="topleft" left="18" name="physics info" - top_pad="15" - width="589"> + top_pad="12" + width="319"> <text follows="top|left" height="15" layout="topleft" left="0" text_color="White" - top_pad="0" + top_pad="3" name="results_text" width="50"> Results: @@ -1077,6 +1078,33 @@ Hulls: [HULLS] </text> </panel> + <panel + bg_alpha_color="0 0 0 0" + bg_opaque_color="0 0 0 0.3" + follows="left|top" + height="19" + layout="topleft" + left_pad="5" + top_delta="0" + name="physics message" + width="270"> + <icon + follows="left|top" + height="16" + left="0" + layout="topleft" + name="physics_status_message_icon" + top_pad="0" + width="16" /> + <text + follows="left|top" + height="15" + layout="topleft" + left_pad="2" + name="physics_status_message_text" + width="252" + top_delta="3"/> + </panel> </panel> <!-- MODIFIERS PANEL --> <panel diff --git a/indra/newview/skins/default/xui/en/floater_notifications_tabbed.xml b/indra/newview/skins/default/xui/en/floater_notifications_tabbed.xml new file mode 100644 index 0000000000..afc609de52 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_notifications_tabbed.xml @@ -0,0 +1,154 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater + legacy_header_height="18" + bevel_style="in" + layout="topleft" + name="floater_notifications_tabbed" + help_topic="notification_chiclet" + save_rect="true" + title="NOTIFICATIONS" + width="350" + min_width="435" + height="550" + min_height="150" + can_minimize="false" + can_tear_off="false" + can_resize="true" + can_drag_on_left="false" + can_dock="true" + save_dock_state="true" + save_visibility="true" + single_instance="true" +> + <floater.string + name="system_tab_title"> + System ([COUNT]) + </floater.string> + <floater.string + name="transactions_tab_title"> + Transactions ([COUNT]) + </floater.string> + <floater.string + name="group_invitations_tab_title"> + Invitations ([COUNT]) + </floater.string> + <floater.string + name="group_notices_tab_title"> + Group ([COUNT]) + </floater.string> + + <string + name="title_notification_tabbed_window"> + NOTIFICATIONS + </string> + <layout_stack + width="336" + height="533" + enabled="true" + orientation="vertical" + name="TabButtonsStack" + follows="left|top|right|bottom" + top="17"> + <layout_panel + width="336" + height="530" + enabled="true" + name="TabButtonsLayoutPanel"> + <tab_container + follows="left|top|right|bottom" + halign="center" + layout="topleft" + tab_position="top" + left="7" + top="7" + width="336" + height="491" + mouse_opaque="true" + name="notifications_tab_container"> + <panel + border="true" + bevel_style="none" + follows="left|top|right|bottom" + label="System (0)" + layout="topleft" + name="system_notification_list_tab"> + <notification_list_view + color="FloaterDefaultBackgroundColor" + follows="all" + layout="topleft" + name="system_notification_list" + left="0" + top="5" + height="0" + width="330"/> + </panel> + <panel + border="true" + bevel_style="none" + follows="left|top|right|bottom" + label="Transactions (0)" + layout="topleft" + name="transaction_notifications_tab"> + <notification_list_view + color="FloaterDefaultBackgroundColor" + follows="all" + layout="topleft" + name="transaction_notification_list" + left="0" + top="5" + height="0" + width="328"/> + </panel> + <panel + border="true" + bevel_style="none" + follows="left|top|right|bottom" + label="Invitations (0)" + layout="topleft" + name="group_invite_notifications_tab"> + <notification_list_view + color="FloaterDefaultBackgroundColor" + follows="all" + layout="topleft" + name="group_invite_notification_list" + left="0" + top="5" + height="0" + width="328"/> + </panel> + <panel + border="true" + bevel_style="none" + follows="left|top|right|bottom" + label="Group (0)" + layout="topleft" + name="group_notice_notifications_tab"> + <notification_list_view + color="FloaterDefaultBackgroundColor" + follows="all" + layout="topleft" + name="group_notice_notification_list" + left="0" + top="5" + height="0" + width="328"/> + </panel> + </tab_container> + + <layout_stack width="336" height="26" enabled="true" orientation="horizontal" follows="left|right" name="ButtonsStack"> + <layout_panel width="336" height="30" enabled="true" orientation="horizontal" name="CondenseAllButtonPanel"> + <button width="93" height="21" left="2" label="Collapse all" name="collapse_all_button"> + </button> + </layout_panel> + <layout_panel width="336" height="30" enabled="true" orientation="horizontal" name="GapLayoutPanel"> + <panel width="90" height="21" left="2" label="Gap Panel" border="false" name="GapPanel"> + </panel> + </layout_panel> + <layout_panel width="336" height="30" enabled="true" orientation="horizontal" name="DeleteAllButtonPanel"> + <button width="93" height="21" left="2" label="Delete all" name="delete_all_button"> + </button> + </layout_panel> + </layout_stack> + </layout_panel> + </layout_stack> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_sys_well.xml b/indra/newview/skins/default/xui/en/floater_sys_well.xml index ecedb27438..2c5176cf01 100755 --- a/indra/newview/skins/default/xui/en/floater_sys_well.xml +++ b/indra/newview/skins/default/xui/en/floater_sys_well.xml @@ -23,10 +23,6 @@ name="title_im_well_window"> CONVERSATIONS </string> - <string - name="title_notification_well_window"> - NOTIFICATIONS - </string> <flat_list_view color="FloaterDefaultBackgroundColor" diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml index 91adec0789..61002bf1b5 100755 --- a/indra/newview/skins/default/xui/en/menu_inventory.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory.xml @@ -565,14 +565,6 @@ <menu_item_call label="Delete" layout="topleft" - name="Remove Link"> - <menu_item_call.on_click - function="Inventory.DoToSelected" - parameter="delete" /> - </menu_item_call> - <menu_item_call - label="Delete" - layout="topleft" name="Delete"> <menu_item_call.on_click function="Inventory.DoToSelected" @@ -785,14 +777,6 @@ layout="topleft" name="Marketplace Separator" /> <menu_item_call - label="Copy to Merchant Outbox" - layout="topleft" - name="Merchant Copy"> - <menu_item_call.on_click - function="Inventory.DoToSelected" - parameter="copy_to_outbox" /> - </menu_item_call> - <menu_item_call label="Copy to Marketplace Listings" layout="topleft" name="Marketplace Copy"> diff --git a/indra/newview/skins/default/xui/en/menu_login.xml b/indra/newview/skins/default/xui/en/menu_login.xml index e91eea04d1..73ca7c529d 100755 --- a/indra/newview/skins/default/xui/en/menu_login.xml +++ b/indra/newview/skins/default/xui/en/menu_login.xml @@ -108,6 +108,12 @@ function="Floater.Show" parameter="sl_about" /> </menu_item_call> + <menu_item_call + label="Check for Updates" + name="Check for Updates"> + <menu_item_call.on_click + function="Advanced.CheckViewerUpdates"/> + </menu_item_call> </menu> <menu_item_check label="Show Debug Menu" diff --git a/indra/newview/skins/default/xui/en/menu_url_email.xml b/indra/newview/skins/default/xui/en/menu_url_email.xml new file mode 100644 index 0000000000..6467fe5c90 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_url_email.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<context_menu + layout="topleft" + name="Email Popup"> + <menu_item_call + label="Compose Email in an External client" + layout="topleft" + name="email_open_external"> + <menu_item_call.on_click + function="Url.OpenExternal" /> + </menu_item_call> + <menu_item_separator + layout="topleft" /> + <menu_item_call + label="Copy Email to clipboard" + layout="topleft" + name="email_copy"> + <menu_item_call.on_click + function="Url.CopyLabel" /> + </menu_item_call> +</context_menu> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 2463c5f43b..3f557d0d0f 100755 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -134,7 +134,7 @@ </menu_item_call> <menu_item_call label="Walk / run / fly..." - name="Walk / run / fly"> + name="WalkRunFly"> <menu_item_call.on_click function="Floater.ToggleOrBringToFront" parameter="moveview" /> @@ -509,7 +509,7 @@ </menu_item_call> <menu_item_call label="Region / Estate" - name="Region/Estate"> + name="RegionEstate"> <menu_item_call.on_click function="Floater.Show" parameter="region_info" /> @@ -1438,7 +1438,7 @@ function="Floater.Show" parameter="bumps" /> </menu_item_call> - <menu_item_separator/> + <menu_item_separator/> <menu_item_call label="About [APP_NAME]" name="About Second Life"> @@ -1446,6 +1446,12 @@ function="Floater.Show" parameter="sl_about" /> </menu_item_call> + <menu_item_call + label="Check for Updates" + name="Check for Updates"> + <menu_item_call.on_click + function="Advanced.CheckViewerUpdates"/> + </menu_item_call> </menu> <menu create_jump_keys="true" diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index f847c73287..70ba4d5077 100755 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -819,6 +819,33 @@ If you no longer wish to have these abilities granted to this role, disable them notext="Cancel" yestext="Eject"/> </notification> + + <notification + icon="alertmodal.tga" + name="BanGroupMemberWarning" + type="alertmodal"> + You are about to ban [AVATAR_NAME] from the group. + <tag>group</tag> + <tag>confirm</tag> + <usetemplate + ignoretext="Confirm banning a participant from group" + name="okcancelignore" + notext="Cancel" + yestext="Ban"/> + </notification> + <notification + icon="alertmodal.tga" + name="BanGroupMembersWarning" + type="alertmodal"> + You are about to ban [COUNT] members from group. + <tag>group</tag> + <tag>confirm</tag> + <usetemplate + ignoretext="Confirm banning multiple members from group" + name="okcancelignore" + notext="Cancel" + yestext="Ban"/> + </notification> <notification icon="alertmodal.tga" @@ -3892,6 +3919,53 @@ see [[INFO_URL] Information about this update] name="okbutton" yestext="OK"/> </notification> + + <notification + icon="alertmodal.tga" + name="UpdateDownloadInProgress" + type="alertmodal"> +An update is available! +It's downloading in the background and we will prompt you to restart your viewer to finish installing it as soon as it's ready. + <tag>confirm</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + + <notification + icon="alertmodal.tga" + name="UpdateDownloadComplete" + type="alertmodal"> +An update was downloaded. It will be installed during restart. + <tag>confirm</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + + <notification + icon="alertmodal.tga" + name="UpdateCheckError" + type="alertmodal"> +An error occured while checking for update. +Please try again later. + <tag>confirm</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + + <notification + icon="alertmodal.tga" + name="UpdateViewerUpToDate" + type="alertmodal"> +Your viewer is up to date! +If you can't wait to try out the latest features and fixes, check out the Alternate Viewers page. http://wiki.secondlife.com/wiki/Linden_Lab_Official:Alternate_Viewers. + <tag>confirm</tag> + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> <notification icon="alertmodal.tga" @@ -4217,6 +4291,21 @@ You have reached your maximum number of groups. Please leave some group before j <notification icon="alert.tga" + name="GroupLimitInfo" + type="alert"> +The group limit for base accounts is [MAX_BASIC], and for [https://secondlife.com/premium/ premium] +accounts is [MAX_PREMIUM]. +If you downgraded your account, you will need to get below [MAX_BASIC] group limit before you can join more. + +[https://secondlife.com/my/account/membership.php Upgrade today!] + <tag>group</tag> + <usetemplate + name="okbutton" + yestext="Close"/> + </notification> + + <notification + icon="alert.tga" name="KickUser" type="alert"> <tag>win</tag> @@ -7029,15 +7118,6 @@ No valid circuit code. <notification icon="notify.tga" - name="NoValidTimestamp" - persist="true" - type="notify"> - <tag>fail</tag> -No valid timestamp. - </notification> - - <notification - icon="notify.tga" name="NoPendingConnection" persist="true" type="notify"> diff --git a/indra/newview/skins/default/xui/en/panel_experience_list_editor.xml b/indra/newview/skins/default/xui/en/panel_experience_list_editor.xml index c76b958eda..c357f9e7d5 100644 --- a/indra/newview/skins/default/xui/en/panel_experience_list_editor.xml +++ b/indra/newview/skins/default/xui/en/panel_experience_list_editor.xml @@ -47,19 +47,19 @@ height="12" follows="top|left"> </text> - <scroll_list + <name_list draw_heading="false" left="3" width="225" height="75" follows="all" name="experience_list"> - <columns + <name_list.columns width="225" user_resize="false" name="experience_name" label="Name"/> - </scroll_list> + </name_list> <button layout="topleft" follows="top|right" diff --git a/indra/newview/skins/default/xui/en/panel_main_inventory.xml b/indra/newview/skins/default/xui/en/panel_main_inventory.xml index 0518688f45..0a85477bf4 100755 --- a/indra/newview/skins/default/xui/en/panel_main_inventory.xml +++ b/indra/newview/skins/default/xui/en/panel_main_inventory.xml @@ -21,7 +21,7 @@ </panel.string> <panel.string name="ItemcountUnknown"> - + Fetched [ITEM_COUNT] Items [FILTER] </panel.string> <text type="string" diff --git a/indra/newview/skins/default/xui/en/panel_notification_list_item.xml b/indra/newview/skins/default/xui/en/panel_notification_list_item.xml new file mode 100644 index 0000000000..a909028f9f --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_notification_list_item.xml @@ -0,0 +1,144 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<!-- All our XML is utf-8 encoded. --> +<panel + translate="false" + name="main_panel" + title="panel_notification_list_item" + visible="true" + top="0" + left="0" + width="331" + height="202" + can_resize="true" + layout="topleft" + follows="left|top|right|bottom" > + <!-- background_opaque="false" --> + <!-- background_visible="true"> --> + <!-- bg_alpha_color="PanelNotificationListItem"> --> + <panel.string + name="sender_resident_text"> + Sender: "[SENDER_RESIDENT]" + </panel.string> + <panel.string + name="group_name_text"> + Group: "[GROUP_NAME]" + </panel.string> + <panel.string + name="group_fee_text"> + Fee: [GROUP_FEE] + </panel.string> + <panel.string + name="item_condensed_height"> + 50 + </panel.string> + <panel.string + name="item_expanded_height"> + 87 + </panel.string> + <panel.string + name="expanded_height_resize_for_attachment"> + 27 + </panel.string> + + <panel top="0" left="0" width="331" height="196" bevel_style="none" layout="topleft" follows="left|top|right|bottom" name="panel_total_view"> + <layout_stack top="0" left="0" width="331" height="196" orientation="vertical" follows="left|top|right|bottom" name="item_vertical_stack"> + <layout_panel top="0" left="0" height="30" follows="left|top|right|bottom" layout="topleft" name="layout_panel_condensed_view" visible="false" background_opaque="false" background_visible="true" bg_alpha_color="SysWellItemUnselected"> + <panel border="true" top="0" left="5" height="30" bevel_style="none" layout="topleft" follows="left|top|right|bottom" name="panel_condensed_view"> + <layout_stack top="0" left="0" width="325" height="50" orientation="horizontal" follows="left|top|right|bottom" name="horizontal_stack"> + <layout_panel width="30" height="39" orientation="horizontal" follows="left|top|right|bottom" name="layout_panel_right"> + <group_icon left="5" top="6" width="25" height="25" mouse_opaque="true" name="group_icon" tool_tip="Group" default_icon_name="Generic_Group" visible="true"/> + <avatar_icon left="5" top="6" width="25" height="25" mouse_opaque="true" name="avatar_icon" tool_tip="Avatar" default_icon_name="Generic_Person" visible="false"/> + <icon left="5" top="6" width="25" height="25" mouse_opaque="true" name="system_notification_icon" tool_tip="Icon" image_name="System_Notification" visible="false"/> + </layout_panel> + <layout_panel width="260" height="50" orientation="horizontal" name="layout_panel_middle"> + <panel border="false" top="0" width="260" height="38" bevel_style="none" follows="left|top|right" layout="topleft" name="main_info_panel"> + <panel border="false" top="0" left="0" width="260" height="19" bevel_style="none" follows="left|top|right|bottom" layout="topleft" name="notification_title_panel"> + <text allow_scroll="false" font="SansSerifSmall" top="6" left="0" width="260" height="12" layout="topleft" follows="right|left" text_color="White" + use_ellipses="true" word_wrap="true" mouse_opaque="false" name="notification_title" > + Group Name:Notice Title N o t i c e T i t l e N o t i c e T i t l e N o t i c e T i t l e N oticeTitle + </text> + <icon top="1" left="242" width="21" height="21" image_name="Icon_Attachment_Small" follows="right" mouse_opaque="true" name="attachment_icon" tool_tip="Attachment" visible="false"/> + </panel> + <panel border="false" top="23" left="0" width="260" height="15" bevel_style="none" follows="left|top|right|bottom" layout="topleft" name="sender_time_panel"> + <text allow_scroll="false" font="SansSerifSmall" top="0" left="0" width="170" height="13" layout="topleft" follows="right|left" + use_ellipses="true" word_wrap="false" mouse_opaque="false" name="sender_or_fee_box" visible="false"> + Sender: "Resident R e s i d e n t R e s i d e n t" + </text> + <text allow_scroll="false" font="SansSerifSmall" top="0" right="-5" width="95" height="13" follows="right" halign="right" layout="topleft" left_pad="5" + name="notification_time" value="2014/12/24 23:30" /> + </panel> + </panel> + </layout_panel> + <layout_panel width="18" height="48" orientation="horizontal" follows="right|top|bottom" name="layout_panel_right"> + <panel top="0" left="0" width="17" height="39" follows="left|top|right|bottom" layout="topleft" name="close_expand_panel"> + <button top="0" left="0" width="17" height="17" layout="topleft" follows="top" name="close_btn" mouse_opaque="true" + tab_stop="false" image_unselected="Icon_Close_Foreground" image_selected="Icon_Close_Press" /> + <button bottom="-16" right="15" width="17" height="17" layout="topleft" follows="bottom" name="expand_btn" mouse_opaque="true" + tab_stop="false" image_unselected="Notification_Expand" image_selected="Notification_Expand" /> + </panel> + </layout_panel> + </layout_stack> + </panel> + </layout_panel> + <layout_panel top="0" left="0" height="196" follows="left|top|right|bottom" layout="topleft" name="layout_panel_expanded_view" visible="true" background_opaque="false" background_visible="true" bg_alpha_color="SysWellItemUnselected"> + <panel border="true" top="0" left="5" height="196" bevel_style="none" follows="left|top|right|bottom" layout="topleft" name="panel_expanded_view"> + <layout_stack top="0" left="0" width="325" height="196" orientation="horizontal" follows="left|top|right|bottom" name="horizontal_stack"> + <layout_panel width="30" height="170" orientation="horizontal" follows="left|top|bottom" name="layout_panel_right_exp"> + <group_icon left="5" top="6" width="25" height="25" mouse_opaque="true" name="group_icon_exp" tool_tip="Group" default_icon_name="Generic_Group" visible="true"/> + <avatar_icon left="5" top="6" width="25" height="25" mouse_opaque="true" name="avatar_icon_exp" tool_tip="Avatar" default_icon_name="Generic_Person" visible="false"/> + <icon left="5" top="6" width="25" height="25" mouse_opaque="true" name="system_notification_icon_exp" tool_tip="Icon" image_name="System_Notification" visible="false"/> + <icon left="12" top="144" width="20" height="20" name="attachment_icon_exp" tool_tip="Attachment" image_name="Icon_Attachment_Large" follows="left" mouse_opaque="true" visible="false"/> + </layout_panel> + <layout_panel width="230" height="196" orientation="horizontal" follows="left|top|right|bottom" name="layout_panel_middle_exp"> + <panel border="false" top="0" width="230" height="196" bevel_style="none" follows="left|top|right|bottom" layout="topleft" name="main_info_panel_expanded"> + <panel border="false" top="0" left="0" width="230" height="30" bevel_style="none" follows="left|top|right" layout="topleft" name="notification_title_panel_exp" > + <text allow_scroll="false" font="SansSerif" top="6" left="0" width="233" height="10" layout="topleft" follows="right|left" text_color="White" + use_ellipses="true" word_wrap="false" mouse_opaque="false" name="notification_title_exp"> + Notice Title Notice Title N o t i c e T i t l e N o t i c e T i t l e + </text> + <text allow_scroll="false" font="SansSerif" left="0" width="233" height="10" layout="topleft" follows="right|left" text_color="White" + use_ellipses="true" word_wrap="false" mouse_opaque="false" name="group_name_exp" parse_urls="false" visible="false"> + Group Name Group Name Group Na m e e + </text> + </panel> + <panel border="false" left="0" width="230" height="15" bevel_style="none" follows="left|top|right" layout="topleft" name="sender_time_panel_exp"> + <text allow_scroll="false" font="SansSerifSmall" top="0" left="0" width="145" height="13" layout="topleft" follows="right|left" + use_ellipses="true" word_wrap="false" mouse_opaque="false" name="sender_or_fee_box_exp" visible="false"> + Sender: "Resident R e s i d e n t R e s i d e n t" + </text> + <text allow_scroll="false" font="SansSerifSmall" top="0" right="-1" width="95" height="13" follows="right" halign="right" layout="topleft" left_pad="5" + name="notification_time_exp" value="2014/12/24 23:30" /> + </panel> + <panel border="false" left="0" height="115" width="230" bevel_style="none" follows="all" layout="topleft" name="notification_text_panel_exp" visible="true"> + <chat_editor is_expandable="true" top="0" left="0" width="230" height="110" layout="topleft" follows="left|right" + word_wrap="true" max_length="65536" name="notification_text_exp" parse_urls="true"> + Notice text goes here b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla. bla bla bla bla bla bla bla bla bla bla bla bla bla . + </chat_editor> + </panel> + <panel border="false" left="1" bottom="-5" width="230" height="22" bevel_style="none" follows="left|right|bottom" layout="topleft" name="attachment_panel" visible="false"> + <text allow_scroll="false" font="SansSerifSmall" top="4" left="5" width="220" height="12" layout="topleft" follows="left|top|right|bottom" + use_ellipses="true" word_wrap="true" max_length="96" name="attachment_text"> + Attachment goes here b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a b l a bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla. bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla . + </text> + </panel> + <panel border="false" left="1" bottom="-5" height="55" bevel_style="none" follows="left|right|bottom" layout="topleft" name="button_panel" visible="false"> + <button top="2" left="0" width="65" height="23" layout="topleft" follows="left|top|bottom" name="join_btn" mouse_opaque="true" tab_stop="false" label = "Join"/> + <button top="2" left_pad="12" width="65" height="23" layout="topleft" follows="left|top|bottom" name="decline_btn" mouse_opaque="true" tab_stop="false" label = "Decline"/> + <button top="2" left_pad="12" width="65" height="23" layout="topleft" follows="left|top|bottom" name="info_btn" mouse_opaque="true" tab_stop="false" label = "Info"/> + </panel> + </panel> + </layout_panel> + <layout_panel width="18" orientation="horizontal" follows="right|top|bottom" name="layout_panel_left_exp"> + <panel top="0" left="0" width="17" follows="left|top|right|bottom" layout="topleft" name="close_expand_panel_exp"> + <button top="0" left="2" width="17" height="17" layout="topleft" follows="top" name="close_expanded_btn" mouse_opaque="true" + tab_stop="false" image_unselected="Icon_Close_Foreground" image_selected="Icon_Close_Press" /> + <button bottom="5" left="0" width="17" height="17" layout="topleft" follows="bottom" name="condense_btn" mouse_opaque="true" + tab_stop="false" image_unselected="Notification_Condense" image_selected="Notification_Condense" /> + </panel> + </layout_panel> + </layout_stack> + </panel> + </layout_panel> + </layout_stack> + </panel> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index 2d4665c128..4fb8b9a67f 100755 --- a/indra/newview/skins/default/xui/en/panel_people.xml +++ b/indra/newview/skins/default/xui/en/panel_people.xml @@ -506,7 +506,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M left="3" use_ellipses="true" name="groupcount"> - You belong to [COUNT] groups, and can join [REMAINING] more. + You belong to [COUNT] groups, and can join [REMAINING] more. [secondlife:/// Want more?] </text> <group_list allow_select="true" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml index 2e778014c5..3e96160834 100755 --- a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml @@ -216,7 +216,7 @@ control_name="QAMode" follows="top|left" height="15" - label="Show Developer Menu" + label="Show Develop Menu" layout="topleft" left="30" name="show_develop_menu_check" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml index 1e9a1aa27c..b201e071ef 100755 --- a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml @@ -270,7 +270,7 @@ value="1" /> --> <combo_box.item - label="Download and install updates manually" + label="I will download and install updates manually" name="Install_manual" value="0" /> </combo_box> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index b7e0cff146..4eb6e2462d 100755 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -238,6 +238,7 @@ Please try logging in again in a minute.</string> <string name="TooltipMustSingleDrop">Only a single item can be dragged here</string> <string name="TooltipTooManyWearables">You can't wear a folder containing more than [AMOUNT] items. You can change this limit in Advanced > Show Debug Settings > WearFolderLimit.</string> <string name="TooltipPrice" value="L$[AMOUNT]: "/> + <string name="TooltipSLIcon">This links to a page on the official SecondLife.com or LindenLab.com domain.</string> <string name="TooltipOutboxDragToWorld">You can't rez items from the Marketplace Listings folder</string> <string name="TooltipOutboxWorn">You can't put items you are wearing in the Marketplace Listings folder</string> @@ -277,6 +278,7 @@ Please try logging in again in a minute.</string> <string name="TooltipMapUrl">Click to view this location on a map</string> <string name="TooltipSLAPP">Click to run the secondlife:// command</string> <string name="CurrentURL" value=" CurrentURL: [CurrentURL]" /> + <string name="TooltipEmail">Click to compose an email</string> <!-- text for SLURL labels --> <string name="SLurlLabelTeleport">Teleport to</string> diff --git a/indra/newview/skins/default/xui/en/widgets/notification_list_view.xml b/indra/newview/skins/default/xui/en/widgets/notification_list_view.xml new file mode 100644 index 0000000000..150225af27 --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/notification_list_view.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<notification_list_view + allow_select="false" + color="PanelFocusBackgroundColor" + item_pad="0" + keep_one_selected="false" + multi_select="false" + opaque="true"> + <flat_list_view + color="FloaterDefaultBackgroundColor" + follows="all" + layout="topleft" + name="notification_list" + left="1" + top="20" + height="0" + width="318"/> +</notification_list_view>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/es/menu_viewer.xml b/indra/newview/skins/default/xui/es/menu_viewer.xml index 5118171d80..f6ebb498ec 100755 --- a/indra/newview/skins/default/xui/es/menu_viewer.xml +++ b/indra/newview/skins/default/xui/es/menu_viewer.xml @@ -15,7 +15,7 @@ <menu_item_check label="Volar" name="Fly"/> <menu_item_check label="Correr siempre" name="Always Run"/> <menu_item_call label="Parar mis animaciones" name="Stop Animating My Avatar"/> - <menu_item_call label="Caminar / Correr / Volar..." name="Walk / run / fly"/> + <menu_item_call label="Caminar / Correr / Volar..." name="WalkRunFly"/> </menu> <menu label="Estado" name="Status"> <menu_item_check label="Ausente" name="Away"/> @@ -63,7 +63,7 @@ <menu_item_call label="Foto" name="Take Snapshot"/> <menu_item_call label="Perfil del lugar" name="Place Profile"/> <menu_item_call label="Acerca del terreno" name="About Land"/> - <menu_item_call label="Región/Estado" name="Region/Estate"/> + <menu_item_call label="Región/Estado" name="RegionEstate"/> <menu_item_call label="Mis terrenos..." name="My Land"/> <menu_item_call label="Comprar este terreno" name="Buy Land"/> <menu label="Mostrar" name="LandShow"> diff --git a/indra/newview/skins/default/xui/es/notifications.xml b/indra/newview/skins/default/xui/es/notifications.xml index 9578e1b863..1e367b33fc 100755 --- a/indra/newview/skins/default/xui/es/notifications.xml +++ b/indra/newview/skins/default/xui/es/notifications.xml @@ -2796,9 +2796,6 @@ Por favor, vuelve a intentarlo en unos momentos. <notification name="NoValidCircuit"> Circuito de código inválido. </notification> - <notification name="NoValidTimestamp"> - Fecha inválida. - </notification> <notification name="NoPendingConnection"> No se puede crear la conexión. </notification> diff --git a/indra/newview/skins/default/xui/fr/menu_viewer.xml b/indra/newview/skins/default/xui/fr/menu_viewer.xml index 33b2ba6982..788cdbf856 100755 --- a/indra/newview/skins/default/xui/fr/menu_viewer.xml +++ b/indra/newview/skins/default/xui/fr/menu_viewer.xml @@ -15,7 +15,7 @@ <menu_item_check label="Voler" name="Fly"/> <menu_item_check label="Toujours courir" name="Always Run"/> <menu_item_call label="Arrêter mon animation" name="Stop Animating My Avatar"/> - <menu_item_call label="Marcher / Courir / Voler..." name="Walk / run / fly"/> + <menu_item_call label="Marcher / Courir / Voler..." name="WalkRunFly"/> </menu> <menu label="Statut" name="Status"> <menu_item_check label="Absent" name="Away"/> @@ -64,7 +64,7 @@ <menu_item_call label="Photo" name="Take Snapshot"/> <menu_item_call label="Profil du lieu" name="Place Profile"/> <menu_item_call label="À propos du terrain" name="About Land"/> - <menu_item_call label="Région/Domaine" name="Region/Estate"/> + <menu_item_call label="Région/Domaine" name="RegionEstate"/> <menu_item_call label="Mes terrains..." name="My Land"/> <menu_item_call label="Acheter ce terrain" name="Buy Land"/> <menu label="Afficher" name="LandShow"> diff --git a/indra/newview/skins/default/xui/fr/notifications.xml b/indra/newview/skins/default/xui/fr/notifications.xml index f13a80fe61..29e6fe1979 100755 --- a/indra/newview/skins/default/xui/fr/notifications.xml +++ b/indra/newview/skins/default/xui/fr/notifications.xml @@ -2788,9 +2788,6 @@ Veuillez réessayer dans quelques minutes. <notification name="NoValidCircuit"> Aucun code de circuit valide. </notification> - <notification name="NoValidTimestamp"> - Timestamp non valide. - </notification> <notification name="NoPendingConnection"> Impossible de créer la connexion en attente. </notification> diff --git a/indra/newview/skins/default/xui/fr/panel_login.xml b/indra/newview/skins/default/xui/fr/panel_login.xml index 40082cb265..2b8249c8a9 100755 --- a/indra/newview/skins/default/xui/fr/panel_login.xml +++ b/indra/newview/skins/default/xui/fr/panel_login.xml @@ -1,9 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <panel name="panel_login"> <panel.string name="forgot_password_url">http://secondlife.com/account/request.php?lang=fr</panel.string> - <panel.string name="forgot_password_url"> - http://secondlife.com/account/request.php?lang=fr - </panel.string> <layout_stack name="ui_stack"> <layout_panel name="ui_container"> <combo_box label="Nom d'utilisateur" name="username_combo" tool_tip="Nom d'utilisateur que vous avez choisi lors de votre inscription (par exemple, bobsmith12 ou Steller Sunshine)."/> diff --git a/indra/newview/skins/default/xui/it/menu_viewer.xml b/indra/newview/skins/default/xui/it/menu_viewer.xml index 3f4c370ccd..18ddad5ee8 100755 --- a/indra/newview/skins/default/xui/it/menu_viewer.xml +++ b/indra/newview/skins/default/xui/it/menu_viewer.xml @@ -15,7 +15,7 @@ <menu_item_check label="Vola" name="Fly"/> <menu_item_check label="Corri sempre" name="Always Run"/> <menu_item_call label="Ferma animazione" name="Stop Animating My Avatar"/> - <menu_item_call label="Cammina / corri / vola..." name="Walk / run / fly"/> + <menu_item_call label="Cammina / corri / vola..." name="WalkRunFly"/> </menu> <menu label="Stato" name="Status"> <menu_item_check label="Assente" name="Away"/> @@ -64,7 +64,7 @@ <menu_item_call label="Istantanea" name="Take Snapshot"/> <menu_item_call label="Profilo del luogo" name="Place Profile"/> <menu_item_call label="Informazioni sul terreno" name="About Land"/> - <menu_item_call label="Regione/proprietà immobiliare" name="Region/Estate"/> + <menu_item_call label="Regione/proprietà immobiliare" name="RegionEstate"/> <menu_item_call label="Terreni posseduti..." name="My Land"/> <menu_item_call label="Acquista questo terreno" name="Buy Land"/> <menu label="Mostra" name="LandShow"> diff --git a/indra/newview/skins/default/xui/it/notifications.xml b/indra/newview/skins/default/xui/it/notifications.xml index 06f2b70dcf..61131b09c3 100755 --- a/indra/newview/skins/default/xui/it/notifications.xml +++ b/indra/newview/skins/default/xui/it/notifications.xml @@ -2793,9 +2793,6 @@ Riprova tra qualche istante. <notification name="NoValidCircuit"> Nessun codice circuito valido. </notification> - <notification name="NoValidTimestamp"> - Nessuna data/timestamp valido. - </notification> <notification name="NoPendingConnection"> Impossibile creare la connessione in sospeso. </notification> diff --git a/indra/newview/skins/default/xui/ja/menu_viewer.xml b/indra/newview/skins/default/xui/ja/menu_viewer.xml index 3f756c5f94..0384dc1efc 100755 --- a/indra/newview/skins/default/xui/ja/menu_viewer.xml +++ b/indra/newview/skins/default/xui/ja/menu_viewer.xml @@ -15,7 +15,7 @@ <menu_item_check label="飛ぶ" name="Fly"/> <menu_item_check label="常に走る" name="Always Run"/> <menu_item_call label="私のアニメーションを停止する" name="Stop Animating My Avatar"/> - <menu_item_call label="歩行/走行/飛行..." name="Walk / run / fly"/> + <menu_item_call label="歩行/走行/飛行..." name="WalkRunFly"/> </menu> <menu label="ログイン" name="Status"> <menu_item_check label="一時退席中" name="Away"/> @@ -64,7 +64,7 @@ <menu_item_call label="スナップショット" name="Take Snapshot"/> <menu_item_call label="場所のプロフィール" name="Place Profile"/> <menu_item_call label="土地情報" name="About Land"/> - <menu_item_call label="地域 / 不動産" name="Region/Estate"/> + <menu_item_call label="地域 / 不動産" name="RegionEstate"/> <menu_item_call label="保有地..." name="My Land"/> <menu_item_call label="この土地を購入" name="Buy Land"/> <menu label="表示" name="LandShow"> diff --git a/indra/newview/skins/default/xui/ja/notifications.xml b/indra/newview/skins/default/xui/ja/notifications.xml index 694f38467e..5f0ce7a73b 100755 --- a/indra/newview/skins/default/xui/ja/notifications.xml +++ b/indra/newview/skins/default/xui/ja/notifications.xml @@ -2836,9 +2836,6 @@ Web ページにリンクすると、他人がこの場所に簡単にアクセ <notification name="NoValidCircuit"> 回路コードが無効です。 </notification> - <notification name="NoValidTimestamp"> - タイムスタンプが無効です。 - </notification> <notification name="NoPendingConnection"> 接続を生成できません。 </notification> diff --git a/indra/newview/skins/default/xui/pl/menu_viewer.xml b/indra/newview/skins/default/xui/pl/menu_viewer.xml index ef3fe71945..6fd498eea8 100755 --- a/indra/newview/skins/default/xui/pl/menu_viewer.xml +++ b/indra/newview/skins/default/xui/pl/menu_viewer.xml @@ -33,7 +33,7 @@ <menu_item_call label="Zrób zdjęcie" name="Take Snapshot"/> <menu_item_call label="Profil miejsca" name="Place Profile"/> <menu_item_call label="O posiadłości" name="About Land"/> - <menu_item_call label="Region/Majątek" name="Region/Estate"/> + <menu_item_call label="Region/Majątek" name="RegionEstate"/> <menu_item_call label="Moje posiadłości" name="My Land"/> <menu_item_call label="Kup posiadłość" name="Buy Land"/> <menu label="Pokaż" name="LandShow"> diff --git a/indra/newview/skins/default/xui/pl/notifications.xml b/indra/newview/skins/default/xui/pl/notifications.xml index 62fda0d601..c4a65d92b4 100755 --- a/indra/newview/skins/default/xui/pl/notifications.xml +++ b/indra/newview/skins/default/xui/pl/notifications.xml @@ -2368,9 +2368,6 @@ Spróbuj ponowanie za kilka minut. <notification name="NoValidCircuit"> Nieważny obwód kodowania. </notification> - <notification name="NoValidTimestamp"> - Niewłaściwy czas zapisu. - </notification> <notification name="NoPendingConnection"> Brak możliwości wykonania połączenia. </notification> diff --git a/indra/newview/skins/default/xui/pt/menu_viewer.xml b/indra/newview/skins/default/xui/pt/menu_viewer.xml index 9b3b6077ed..3d5d9eccc6 100755 --- a/indra/newview/skins/default/xui/pt/menu_viewer.xml +++ b/indra/newview/skins/default/xui/pt/menu_viewer.xml @@ -15,7 +15,7 @@ <menu_item_check label="Voar" name="Fly"/> <menu_item_check label="Correr sempre" name="Always Run"/> <menu_item_call label="Parar minha animação" name="Stop Animating My Avatar"/> - <menu_item_call label="Andar/correr/voar..." name="Walk / run / fly"/> + <menu_item_call label="Andar/correr/voar..." name="WalkRunFly"/> </menu> <menu label="Status" name="Status"> <menu_item_check label="Ausente" name="Away"/> @@ -64,7 +64,7 @@ <menu_item_call label="Foto" name="Take Snapshot"/> <menu_item_call label="Perfil da região" name="Place Profile"/> <menu_item_call label="Sobre terrenos" name="About Land"/> - <menu_item_call label="Região/Propriedade" name="Region/Estate"/> + <menu_item_call label="Região/Propriedade" name="RegionEstate"/> <menu_item_call label="Meus terrenos..." name="My Land"/> <menu_item_call label="Comprar este terreno" name="Buy Land"/> <menu label="Mostrar" name="LandShow"> diff --git a/indra/newview/skins/default/xui/pt/notifications.xml b/indra/newview/skins/default/xui/pt/notifications.xml index 29b85d9e97..a264495404 100755 --- a/indra/newview/skins/default/xui/pt/notifications.xml +++ b/indra/newview/skins/default/xui/pt/notifications.xml @@ -2777,9 +2777,6 @@ Por favor, tente novamente em alguns instantes. <notification name="NoValidCircuit"> Código de circuito inválido. </notification> - <notification name="NoValidTimestamp"> - Hora inválida. - </notification> <notification name="NoPendingConnection"> Impossível criar a conexão pendente. </notification> diff --git a/indra/newview/skins/default/xui/ru/menu_viewer.xml b/indra/newview/skins/default/xui/ru/menu_viewer.xml index 958105a70f..d22ca845f9 100755 --- a/indra/newview/skins/default/xui/ru/menu_viewer.xml +++ b/indra/newview/skins/default/xui/ru/menu_viewer.xml @@ -15,7 +15,7 @@ <menu_item_check label="Полет" name="Fly"/> <menu_item_check label="Всегда бегать" name="Always Run"/> <menu_item_call label="Остановить анимацию" name="Stop Animating My Avatar"/> - <menu_item_call label="Ходьба / бег / полет..." name="Walk / run / fly"/> + <menu_item_call label="Ходьба / бег / полет..." name="WalkRunFly"/> </menu> <menu label="Статус" name="Status"> <menu_item_check label="Нет на месте" name="Away"/> @@ -62,7 +62,7 @@ <menu_item_call label="Снимок" name="Take Snapshot"/> <menu_item_call label="Профиль места" name="Place Profile"/> <menu_item_call label="О земле" name="About Land"/> - <menu_item_call label="Регион/землевладение" name="Region/Estate"/> + <menu_item_call label="Регион/землевладение" name="RegionEstate"/> <menu_item_call label="Мои владения..." name="My Land"/> <menu_item_call label="Купить эту землю" name="Buy Land"/> <menu label="Показать" name="LandShow"> diff --git a/indra/newview/skins/default/xui/ru/notifications.xml b/indra/newview/skins/default/xui/ru/notifications.xml index acf3ce608f..70b9a25590 100755 --- a/indra/newview/skins/default/xui/ru/notifications.xml +++ b/indra/newview/skins/default/xui/ru/notifications.xml @@ -2788,9 +2788,6 @@ http://secondlife.com/download. <notification name="NoValidCircuit"> Нет подходящего кода канала. </notification> - <notification name="NoValidTimestamp"> - Нет подходящей метки времени. - </notification> <notification name="NoPendingConnection"> Невозможно создать отложенное соединение. </notification> diff --git a/indra/newview/skins/default/xui/tr/menu_viewer.xml b/indra/newview/skins/default/xui/tr/menu_viewer.xml index cc8d8c895b..cea57011dd 100755 --- a/indra/newview/skins/default/xui/tr/menu_viewer.xml +++ b/indra/newview/skins/default/xui/tr/menu_viewer.xml @@ -15,7 +15,7 @@ <menu_item_check label="Uç" name="Fly"/> <menu_item_check label="Daima Koş" name="Always Run"/> <menu_item_call label="Beni Anime Etmeyi Durdur" name="Stop Animating My Avatar"/> - <menu_item_call label="Yürü / koş / uç..." name="Walk / run / fly"/> + <menu_item_call label="Yürü / koş / uç..." name="WalkRunFly"/> </menu> <menu label="Durum" name="Status"> <menu_item_check label="Uzakta" name="Away"/> @@ -62,7 +62,7 @@ <menu_item_call label="Anlık Görüntü" name="Take Snapshot"/> <menu_item_call label="Profili yerleştir" name="Place Profile"/> <menu_item_call label="Arazi hakkında" name="About Land"/> - <menu_item_call label="Bölge / Gayrimenkul" name="Region/Estate"/> + <menu_item_call label="Bölge / Gayrimenkul" name="RegionEstate"/> <menu_item_call label="Sahip olduğum arazi parçaları..." name="My Land"/> <menu_item_call label="Bu araziyi satın al" name="Buy Land"/> <menu label="Göster" name="LandShow"> diff --git a/indra/newview/skins/default/xui/tr/notifications.xml b/indra/newview/skins/default/xui/tr/notifications.xml index a6c69c7ab2..df22251b3d 100755 --- a/indra/newview/skins/default/xui/tr/notifications.xml +++ b/indra/newview/skins/default/xui/tr/notifications.xml @@ -2788,9 +2788,6 @@ Lütfen biraz sonra tekrar deneyin. <notification name="NoValidCircuit"> Geçerli bir devre kodu yok. </notification> - <notification name="NoValidTimestamp"> - Geçerli bir zaman damgası yok. - </notification> <notification name="NoPendingConnection"> Beklemedeki bağlantı oluşturulamıyor. </notification> diff --git a/indra/newview/skins/default/xui/zh/menu_viewer.xml b/indra/newview/skins/default/xui/zh/menu_viewer.xml index e94f52a401..9572ad49d3 100755 --- a/indra/newview/skins/default/xui/zh/menu_viewer.xml +++ b/indra/newview/skins/default/xui/zh/menu_viewer.xml @@ -15,7 +15,7 @@ <menu_item_check label="飛行" name="Fly"/> <menu_item_check label="以跑代步" name="Always Run"/> <menu_item_call label="停止我身上的動作" name="Stop Animating My Avatar"/> - <menu_item_call label="行走 / 跑步 / 飛行…" name="Walk / run / fly"/> + <menu_item_call label="行走 / 跑步 / 飛行…" name="WalkRunFly"/> </menu> <menu label="狀態" name="Status"> <menu_item_check label="離開" name="Away"/> @@ -62,7 +62,7 @@ <menu_item_call label="快照" name="Take Snapshot"/> <menu_item_call label="地點小檔案" name="Place Profile"/> <menu_item_call label="土地資料" name="About Land"/> - <menu_item_call label="地區/領地" name="Region/Estate"/> + <menu_item_call label="地區/領地" name="RegionEstate"/> <menu_item_call label="我所擁有的土地…" name="My Land"/> <menu_item_call label="購買這塊土地" name="Buy Land"/> <menu label="顯示" name="LandShow"> diff --git a/indra/newview/skins/default/xui/zh/notifications.xml b/indra/newview/skins/default/xui/zh/notifications.xml index 179f01e90d..0a98101b60 100755 --- a/indra/newview/skins/default/xui/zh/notifications.xml +++ b/indra/newview/skins/default/xui/zh/notifications.xml @@ -2778,9 +2778,6 @@ SHA1 指紋:[MD5_DIGEST] <notification name="NoValidCircuit"> 沒有有效的線路碼。 </notification> - <notification name="NoValidTimestamp"> - 沒有有效的時間戳記。 - </notification> <notification name="NoPendingConnection"> 無法建立待通的連線。 </notification> diff --git a/indra/viewer_components/updater/llupdaterservice.cpp b/indra/viewer_components/updater/llupdaterservice.cpp index c152493a51..788955a1b2 100755 --- a/indra/viewer_components/updater/llupdaterservice.cpp +++ b/indra/viewer_components/updater/llupdaterservice.cpp @@ -132,6 +132,7 @@ public: void startChecking(bool install_if_ready); void stopChecking(); + bool forceCheck(); bool isChecking(); LLUpdaterService::eUpdaterState getState(); @@ -266,6 +267,46 @@ void LLUpdaterServiceImpl::stopChecking() setState(LLUpdaterService::TERMINAL); } +bool LLUpdaterServiceImpl::forceCheck() +{ + if (!mIsDownloading && getState() != LLUpdaterService::CHECKING_FOR_UPDATE) + { + if (mIsChecking) + { + // Service is running, just reset the timer + if (mTimer.getStarted()) + { + mTimer.setTimerExpirySec(0); + setState(LLUpdaterService::CHECKING_FOR_UPDATE); + return true; + } + } + else if (!mChannel.empty() && !mVersion.empty()) + { + // one time check + bool has_install = checkForInstall(false); + if (!has_install) + { + std::string query_url = LLGridManager::getInstance()->getUpdateServiceURL(); + if (!query_url.empty()) + { + setState(LLUpdaterService::CHECKING_FOR_UPDATE); + mUpdateChecker.checkVersion(query_url, mChannel, mVersion, + mPlatform, mPlatformVersion, mUniqueId, + mWillingToTest); + return true; + } + else + { + LL_WARNS("UpdaterService") + << "No updater service defined for grid '" << LLGridManager::getInstance()->getGrid() << LL_ENDL; + } + } + } + } + return false; +} + bool LLUpdaterServiceImpl::isChecking() { return mIsChecking; @@ -402,9 +443,9 @@ bool LLUpdaterServiceImpl::checkForResume() void LLUpdaterServiceImpl::error(std::string const & message) { + setState(LLUpdaterService::TEMPORARY_ERROR); if(mIsChecking) { - setState(LLUpdaterService::TEMPORARY_ERROR); restartTimer(mCheckPeriod); } } @@ -449,8 +490,12 @@ void LLUpdaterServiceImpl::response(LLSD const & content) else { LL_WARNS("UpdaterService") << "Invalid update query response ignored; retry in " - << mCheckPeriod << " seconds" << LL_ENDL; - restartTimer(mCheckPeriod); + << mCheckPeriod << " seconds" << LL_ENDL; + setState(LLUpdaterService::TEMPORARY_ERROR); + if (mIsChecking) + { + restartTimer(mCheckPeriod); + } } } @@ -672,6 +717,11 @@ void LLUpdaterService::stopChecking() mImpl->stopChecking(); } +bool LLUpdaterService::forceCheck() +{ + return mImpl->forceCheck(); +} + bool LLUpdaterService::isChecking() { return mImpl->isChecking(); diff --git a/indra/viewer_components/updater/llupdaterservice.h b/indra/viewer_components/updater/llupdaterservice.h index 0ddf24935b..95bbe1695c 100755 --- a/indra/viewer_components/updater/llupdaterservice.h +++ b/indra/viewer_components/updater/llupdaterservice.h @@ -84,6 +84,7 @@ public: void startChecking(bool install_if_ready = false); void stopChecking(); + bool forceCheck(); bool isChecking(); eUpdaterState getState(); |