diff --git a/indra/cmake/LLWindow.cmake b/indra/cmake/LLWindow.cmake index 5fb05696e5..1b0370cc14 100644 --- a/indra/cmake/LLWindow.cmake +++ b/indra/cmake/LLWindow.cmake @@ -4,6 +4,9 @@ include(Variables) include(GLEXT) include(Prebuilt) +include_guard() + +add_library( sdl INTERFACE IMPORTED ) if (USESYSTEMLIBS) include(FindSDL) @@ -18,11 +21,17 @@ else (USESYSTEMLIBS) if( NOT USE_SDL2 ) use_prebuilt_binary(SDL) set (SDL_FOUND TRUE) - set (SDL_LIBRARY SDL directfb fusion direct X11) + + target_link_libraries (sdl INTERFACE SDL directfb fusion direct X11) + target_compile_definitions( sdl INTERFACE LL_SDL=1 ) + else() use_prebuilt_binary(SDL2) set (SDL2_FOUND TRUE) - set (SDL_LIBRARY SDL2 SDL2_mixer X11) + + target_link_libraries( sdl INTERFACE SDL2 X11 ) + target_compile_definitions( sdl INTERFACE LL_SDL2=1 LL_SDL=1 ) + endif() endif (LINUX) diff --git a/indra/llaudio/CMakeLists.txt b/indra/llaudio/CMakeLists.txt index 18c262cb00..5db4896bca 100644 --- a/indra/llaudio/CMakeLists.txt +++ b/indra/llaudio/CMakeLists.txt @@ -76,11 +76,6 @@ if (OPENAL) ) endif (OPENAL) -if(SDL2_FOUND) - list(APPEND llaudio_SOURCE_FILES llaudioengine_sdl2.cpp ) - list(APPEND llaudio_HEADER_FILES llaudioengine_sdl2.h ) -endif() - set_source_files_properties(${llaudio_HEADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) diff --git a/indra/llaudio/llaudioengine_sdl2.cpp b/indra/llaudio/llaudioengine_sdl2.cpp deleted file mode 100644 index 468ddccfeb..0000000000 --- a/indra/llaudio/llaudioengine_sdl2.cpp +++ /dev/null @@ -1,489 +0,0 @@ -#include "llaudioengine_sdl2.h" -#include "llstreamingaudio.h" -#include "lllistener.h" - -#include "SDL2/SDL_mixer.h" - -#include "curl/curl.h" - -namespace -{ - std::string getMixerError() - { - auto pError = Mix_GetError(); - if(!pError) - return "Mix_Error: returned nullptr"; - - return std::string{ "Mix_error: " } + pError; - } -} - -struct RWData -{ - std::string mURL; - CURLM *mMulti{ nullptr }; - CURL *mEasy{ nullptr }; -}; - -Sint64 SDLCALL stream_size (struct SDL_RWops * context) -{ - return -1; -} - -Sint64(SDLCALL stream_seek) (struct SDL_RWops * context, Sint64 offset, - int whence) -{ - if( !context ) - return -1; - - RWData *pData = reinterpret_cast(context->hidden.unknown.data1); - return -1; -} - -size_t SDLCALL stream_read (struct SDL_RWops * context, void *ptr, size_t size, size_t maxnum) -{ - if( !context ) - return 0; - - RWData *pData = reinterpret_cast(context->hidden.unknown.data1); - return 0; -} - -size_t SDLCALL stream_write (struct SDL_RWops * context, const void *ptr, - size_t size, size_t num) -{ - if( !context ) - return 0; - - RWData *pData = reinterpret_cast(context->hidden.unknown.data1); - return 0; -} - -int SDLCALL stream_close (struct SDL_RWops * context) -{ - if( !context ) - return 0; - - RWData *pData = reinterpret_cast(context->hidden.unknown.data1); - // Free curl multi and easy - - - return 0; -} - - -class LLListenerSDL2: public LLListener -{ - LLAudioEngineSDL2 *mEngine; -public: - LLListenerSDL2(LLAudioEngineSDL2 *aEngine ) - : mEngine( aEngine ) - { - - } - -}; - -class LLStreamingAudioSDL2: public LLStreamingAudioInterface -{ - std::string mURL; - F32 mVolume { 1.0 }; - Mix_Music *mMusic{nullptr}; - SDL_RWops *mReader{ nullptr }; - RWData *mData; -public: - - void start(const std::string& url) override - { - mURL = url; - //mMusic = Mix_LoadMUS( url.c_str() ); - mReader = SDL_AllocRW(); - mData = new RWData{}; - mReader->hidden.unknown.data1 = mData; - - mReader->size = stream_size; - mReader->seek = stream_seek; - mReader->read = stream_read; - mReader->write = stream_write; - mReader->close = stream_close; - - mMusic = Mix_LoadMUS_RW( mReader, 0 ); - if( mMusic ) - { - auto x = Mix_PlayMusic( mMusic, 0 ); - LL_WARNS() << "SDL2: " << getMixerError() << " " << x << LL_ENDL; - } - else - { - LL_WARNS() << "SDL2: MixLoadMUS failed: " << getMixerError() << LL_ENDL; - } - - } - - void stop() override - { - } - - void pause(int pause) override - { - } - - void update() override - { - } - - int isPlaying() override - { - return mMusic != nullptr; - } - - void setGain(F32 vol) override - { - mVolume = vol; - } - - F32 getGain() override - { - return mVolume; - } - - std::string getURL() override - { - return mURL; - } - - bool supportsAdjustableBufferSizes() override - { - return false; - } - - void setBufferSizes(U32 streambuffertime, U32 decodebuffertime) override - { - } -}; - -class LLAudioBufferSDL2: public LLAudioBuffer -{ -public: - Mix_Chunk *mChunk; - - ~LLAudioBufferSDL2() - { - if( mChunk ) - Mix_FreeChunk(mChunk); - } - - bool loadWAV(const std::string& filename) - { - mChunk = Mix_LoadWAV( filename.c_str() ); - return mChunk != nullptr; - } - - U32 getLength() - { - return 0; // Not needed here - } - -}; - -class LLAudioChannelSDL2: public LLAudioChannel -{ - S32 mChannel; - LLAudioEngineSDL2 *mEngine; - bool mPlayback{false}; - - uint32_t caclulateVolume() - { - if( mChannel < 0 || !mCurrentSourcep || !mEngine) - return 0.0f; - - - F32 gain = mCurrentSourcep->getGain() * getSecondaryGain() * mEngine->getMasterGain(); - llclamp(gain, 0.0f, 1.f ); - gain *= 255.f; - return static_cast(gain); - - } -public: - LLAudioChannelSDL2( S32 nChannel, LLAudioEngineSDL2 *aEngine ) - : mChannel( nChannel ) - , mEngine( aEngine ) - { - - } - - ~LLAudioChannelSDL2() - { - mEngine->deleteChannel( mChannel ); - } - - void play() override - { - startPlayback(); - } - - void startPlayback() - { - if( mChannel < 0 ) - return; - - //if( mPlayback ) - // return; - - Mix_HaltChannel( mChannel ); - LLAudioBufferSDL2* pBuffer = (LLAudioBufferSDL2*)mCurrentBufferp; - if( !pBuffer ) - return; - - mPlayback = true; - Mix_PlayChannel( mChannel, pBuffer->mChunk, mCurrentSourcep->isLoop()?-1:0 ); - Mix_Volume( mChannel, caclulateVolume()); - } - - void playSynced(LLAudioChannel *channelp) override - { - play(); - } - - void cleanup() override - { - if( mChannel < 0 ) - return; - - mPlayback = false; - Mix_HaltChannel( mChannel ); - } - - bool isPlaying() override - { - if( mChannel < 0 ) - return false; - - if( !mPlayback ) - return false; - - bool bRet = Mix_Playing( mChannel ) == 1; - if( !bRet ) - { - mPlayback = false; - Mix_HaltChannel( mChannel ); - } - - return bRet; - } - - bool updateBuffer() override - { - if( !mCurrentSourcep || mChannel < 0) - return false; - - if( LLAudioChannel::updateBuffer() ) - { - if(mCurrentSourcep) - { - startPlayback(); - return true; - } - } - else if( mCurrentSourcep ) - { - if( mPlayback ) - Mix_Volume( mChannel, caclulateVolume()); - } - else - { - if( mPlayback ) - Mix_HaltChannel(mChannel); - mPlayback = false; - } - return mCurrentSourcep != 0; - } - - void update3DPosition() override - { - if(!mCurrentSourcep || mChannel < 0) - return; - - if(mPlayback) - { - auto pos = mEngine->getListenerPos(); - LLVector3 soundpos{mCurrentSourcep->getPositionGlobal()}; - F32 dist = dist_vec(soundpos, pos); - dist = llclamp(dist, 0.0f, 255.0f); - Mix_SetPosition(mChannel, 0, (uint8_t) dist); - } - } - - void updateLoop() override - { - } - -}; - -struct LLAudioEngineSDL2::ImplData -{ - std::vector< uint8_t > mChannels; - bool mReady { false }; -}; - -LLAudioEngineSDL2::LLAudioEngineSDL2() -{ - -} - -LLAudioEngineSDL2::~LLAudioEngineSDL2() -{ - -} - -bool LLAudioEngineSDL2::init(const S32 num_channels, void *userdata, const std::string &app_title) -{ - LL_INFOS() << "Initializing SDL2 audio" << LL_ENDL; - mData = std::make_unique(); - - LLAudioEngine::init(num_channels, userdata, app_title); - - int flags = MIX_INIT_FLAC | MIX_INIT_MP3 | MIX_INIT_MP3; - if( flags != Mix_Init( flags ) ) - LL_WARNS() << "Mix_Init failed to intialize all formats: " << getMixerError () << LL_ENDL; - - if( 0 != Mix_OpenAudio(44100, AUDIO_S16LSB, MIX_DEFAULT_CHANNELS, 1024 ) ) - { - LL_WARNS() << "Mix_OpenAudio failed " << getMixerError() << LL_ENDL; - return false; - } - mData->mChannels.resize( num_channels+2 ); - Mix_AllocateChannels( num_channels ); - mData->mReady = true; - - // Impl is stubbed out, but nothing more -// if (!getStreamingAudioImpl()) -// setStreamingAudioImpl( new LLStreamingAudioSDL2() ); - - - return true; - -} -std::string LLAudioEngineSDL2::getDriverName(bool verbose) -{ - return "SDL mixer"; -} - -void LLAudioEngineSDL2::shutdown() -{ - Mix_CloseAudio(); - Mix_Quit(); - - mData = nullptr; -} - -void LLAudioEngineSDL2::updateWind(LLVector3 direction, F32 camera_height_above_water) -{ - -} - -void LLAudioEngineSDL2::idle(F32 max_decode_time ) -{ - LLAudioEngine::idle(max_decode_time); -} - -void LLAudioEngineSDL2::updateChannels() -{ - LLAudioEngine::updateChannels(); -} - -LLAudioEngineSDL2::output_device_map_t LLAudioEngineSDL2::getDevices() -{ -// Impl looks like this, but it might not be possible to support this right now -/* - if(mDevices.size()) - return; - - auto numDevices{SDL_GetNumAudioDevices(0)}; - if( numDevices <= 0 ) - return {}; - - LLAudioEngineSDL2::output_device_map_t devices{}; - for( auto i{0}; i < numDevices; ++i ) - { - auto pName{SDL_GetAudioDeviceName(i, 0)}; - if( !pName ) - continue; - - std::string strName{ pName }; - - devices[ LLUUID::generateNewID( strName )] = strName; - } -*/ - - return mDevices; -} - -void LLAudioEngineSDL2::setDevice(const LLUUID &device_uuid) -{ - -} - -LLAudioBuffer *LLAudioEngineSDL2::createBuffer() -{ - return new LLAudioBufferSDL2(); -} - -LLAudioChannel *LLAudioEngineSDL2::createChannel() -{ - S32 channelNum{ -1 }; - if( mData && mData->mReady ) - { - for( S32 i = 0; i < mData->mChannels.size(); ++i ) - { - if( !mData->mChannels[i]) - { - channelNum = i; - mData->mChannels[i] = 1; - break; - } - } - - if( channelNum == -1 ) - { - channelNum = mData->mChannels.size(); - mData->mChannels.push_back(0); - Mix_AllocateChannels( mData->mChannels.size() ); - } - } - - return new LLAudioChannelSDL2(channelNum, this); -} - -void LLAudioEngineSDL2::deleteChannel(S32 aChannel) -{ - if( !mData ) - return; - - if( aChannel < 0 || aChannel >= mData->mChannels.size() ) - return; - - mData->mChannels[aChannel] = 0; -} - -bool LLAudioEngineSDL2::initWind() -{ - // Not supported - return false; -} - -void LLAudioEngineSDL2::cleanupWind() -{ - -} - -void LLAudioEngineSDL2::setInternalGain(F32 gain) -{ - -} - -void LLAudioEngineSDL2::allocateListener() -{ - mListenerp = new LLListenerSDL2(this); -} - diff --git a/indra/llaudio/llaudioengine_sdl2.h b/indra/llaudio/llaudioengine_sdl2.h deleted file mode 100644 index 1c501b6e1a..0000000000 --- a/indra/llaudio/llaudioengine_sdl2.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef LL_AUDIOENGINE_SDL2_H -#define LL_AUDIOENGINE_SDL2_H - -#include "llaudioengine.h" -#include "llwindgen.h" - -class LLAudioEngineSDL2 : public LLAudioEngine -{ -public: - LLAudioEngineSDL2(); - ~LLAudioEngineSDL2(); - - bool init(const S32 num_channels, void *userdata, const std::string &app_title) override; - std::string getDriverName(bool verbose) override; - void shutdown() override; - - void updateWind(LLVector3 direction, F32 camera_height_above_water) override; - void idle(F32 max_decode_time = 0.f) override; - void updateChannels() override; - - output_device_map_t getDevices() override; - void setDevice(const LLUUID& device_uuid) override; - - - void deleteChannel( S32 aChannel ); - -protected: - LLAudioBuffer *createBuffer() override; - LLAudioChannel *createChannel() override; - - bool initWind() override; - void cleanupWind() override; - void setInternalGain(F32 gain) override; - - void allocateListener() override; - - output_device_map_t mDevices; - - struct ImplData; - std::unique_ptr< ImplData > mData; -}; - -#endif \ No newline at end of file diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt index 644bb19672..529c7ecb75 100644 --- a/indra/llmath/CMakeLists.txt +++ b/indra/llmath/CMakeLists.txt @@ -4,12 +4,14 @@ project(llmath) include(00-Common) include(LLCommon) +include(LLMeshOptimizer) include(bugsplat) include(Boost) include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLCOMMON_SYSTEM_INCLUDE_DIRS} + ${LLMESHOPTIMIZER_INCLUDE_DIRS} ) set(llmath_SOURCE_FILES @@ -114,6 +116,7 @@ add_library (llmath ${llmath_SOURCE_FILES}) target_link_libraries(llmath ${LLCOMMON_LIBRARIES} + ${LLMESHOPTIMIZER_LIBRARIES} ) # Add tests diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 2b3fd05d87..795e51f819 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -49,6 +49,7 @@ #include "llsdserialize.h" #include "llvector4a.h" #include "llmatrix4a.h" +#include "llmeshoptimizer.h" #include "lltimer.h" #define DEBUG_SILHOUETTE_BINORMALS 0 @@ -5021,6 +5022,50 @@ bool LLVolumeFace::VertexMapData::ComparePosition::operator()(const LLVector3& a return a.mV[2] < b.mV[2]; } +void LLVolumeFace::remap() +{ + // Generate a remap buffer + std::vector remap(mNumVertices); + S32 remap_vertices_count = LLMeshOptimizer::generateRemapMultiU16(&remap[0], + mIndices, + mNumIndices, + mPositions, + mNormals, + mTexCoords, + mNumVertices); + + // Allocate new buffers + S32 size = ((mNumIndices * sizeof(U16)) + 0xF) & ~0xF; + U16* remap_indices = (U16*)ll_aligned_malloc_16(size); + + S32 tc_bytes_size = ((remap_vertices_count * sizeof(LLVector2)) + 0xF) & ~0xF; + LLVector4a* remap_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * remap_vertices_count + tc_bytes_size); + LLVector4a* remap_normals = remap_positions + remap_vertices_count; + LLVector2* remap_tex_coords = (LLVector2*)(remap_normals + remap_vertices_count); + + // Fill the buffers + LLMeshOptimizer::remapIndexBufferU16(remap_indices, mIndices, mNumIndices, &remap[0]); + LLMeshOptimizer::remapPositionsBuffer(remap_positions, mPositions, mNumVertices, &remap[0]); + LLMeshOptimizer::remapNormalsBuffer(remap_normals, mNormals, mNumVertices, &remap[0]); + LLMeshOptimizer::remapUVBuffer(remap_tex_coords, mTexCoords, mNumVertices, &remap[0]); + + // Free unused buffers + ll_aligned_free_16(mIndices); + ll_aligned_free<64>(mPositions); + + // Tangets are now invalid + ll_aligned_free_16(mTangents); + mTangents = NULL; + + // Assign new values + mIndices = remap_indices; + mPositions = remap_positions; + mNormals = remap_normals; + mTexCoords = remap_tex_coords; + mNumVertices = remap_vertices_count; + mNumAllocatedVertices = remap_vertices_count; +} + void LLVolumeFace::optimize(F32 angle_cutoff) { LLVolumeFace new_face; diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index 1a057186ca..e2afee35d5 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -905,6 +905,10 @@ public: typedef std::map, VertexMapData::ComparePosition > PointMap; }; + // Eliminates non unique triangles, takes positions, + // normals and texture coordinates into account. + void remap(); + void optimize(F32 angle_cutoff = 2.f); bool cacheOptimize(); diff --git a/indra/llmeshoptimizer/llmeshoptimizer.cpp b/indra/llmeshoptimizer/llmeshoptimizer.cpp index a879389c5a..c178348968 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.cpp +++ b/indra/llmeshoptimizer/llmeshoptimizer.cpp @@ -28,6 +28,9 @@ #include "meshoptimizer.h" +#include "llmath.h" +#include "v2math.h" + LLMeshOptimizer::LLMeshOptimizer() { // Todo: Looks like for memory management, we can add allocator and deallocator callbacks @@ -40,24 +43,218 @@ LLMeshOptimizer::~LLMeshOptimizer() } //static -void LLMeshOptimizer::generateShadowIndexBuffer(U16 *destination, - const U16 *indices, +void LLMeshOptimizer::generateShadowIndexBufferU32(U32 *destination, + const U32 *indices, U64 index_count, - const LLVector4a *vertex_positions, - U64 vertex_count, - U64 vertex_positions_stride + const LLVector4a * vertex_positions, + const LLVector4a * normals, + const LLVector2 * text_coords, + U64 vertex_count ) { - meshopt_generateShadowIndexBuffer(destination, + meshopt_Stream streams[3]; + + S32 index = 0; + if (vertex_positions) + { + streams[index].data = (const float*)vertex_positions; + // Despite being LLVector4a, only x, y and z are in use + streams[index].size = sizeof(F32) * 3; + streams[index].stride = sizeof(F32) * 4; + index++; + } + if (normals) + { + streams[index].data = (const float*)normals; + streams[index].size = sizeof(F32) * 3; + streams[index].stride = sizeof(F32) * 4; + index++; + } + if (text_coords) + { + streams[index].data = (const float*)text_coords; + streams[index].size = sizeof(F32) * 2; + streams[index].stride = sizeof(F32) * 2; + index++; + } + + if (index == 0) + { + // invalid + return; + } + + meshopt_generateShadowIndexBufferMulti(destination, indices, index_count, - (const float*)vertex_positions, // verify that it is correct to convert to float vertex_count, - sizeof(LLVector4a), - vertex_positions_stride + streams, + index ); } +//static +void LLMeshOptimizer::generateShadowIndexBufferU16(U16 *destination, + const U16 *indices, + U64 index_count, + const LLVector4a * vertex_positions, + const LLVector4a * normals, + const LLVector2 * text_coords, + U64 vertex_count +) +{ + meshopt_Stream streams[3]; + + S32 index = 0; + if (vertex_positions) + { + streams[index].data = (const float*)vertex_positions; + streams[index].size = sizeof(F32) * 3; + streams[index].stride = sizeof(F32) * 4; + index++; + } + if (normals) + { + streams[index].data = (const float*)normals; + streams[index].size = sizeof(F32) * 3; + streams[index].stride = sizeof(F32) * 4; + index++; + } + if (text_coords) + { + streams[index].data = (const float*)text_coords; + streams[index].size = sizeof(F32) * 2; + streams[index].stride = sizeof(F32) * 2; + index++; + } + + if (index == 0) + { + // invalid + return; + } + + meshopt_generateShadowIndexBufferMulti(destination, + indices, + index_count, + vertex_count, + streams, + index); +} + +void LLMeshOptimizer::optimizeVertexCacheU32(U32 * destination, const U32 * indices, U64 index_count, U64 vertex_count) +{ + meshopt_optimizeVertexCache(destination, indices, index_count, vertex_count); +} + +void LLMeshOptimizer::optimizeVertexCacheU16(U16 * destination, const U16 * indices, U64 index_count, U64 vertex_count) +{ + meshopt_optimizeVertexCache(destination, indices, index_count, vertex_count); +} + +size_t LLMeshOptimizer::generateRemapMultiU32( + unsigned int* remap, + const U32 * indices, + U64 index_count, + const LLVector4a * vertex_positions, + const LLVector4a * normals, + const LLVector2 * text_coords, + U64 vertex_count) +{ + meshopt_Stream streams[] = { + {(const float*)vertex_positions, sizeof(F32) * 3, sizeof(F32) * 4}, + {(const float*)normals, sizeof(F32) * 3, sizeof(F32) * 4}, + {(const float*)text_coords, sizeof(F32) * 2, sizeof(F32) * 2}, + }; + + // Remap can function without indices, + // but providing indices helps with removing unused vertices + U64 indeces_cmp = indices ? index_count : vertex_count; + + // meshopt_generateVertexRemapMulti will throw an assert if (indices[i] >= vertex_count) + return meshopt_generateVertexRemapMulti(&remap[0], indices, indeces_cmp, vertex_count, streams, sizeof(streams) / sizeof(streams[0])); +} + +size_t LLMeshOptimizer::generateRemapMultiU16( + unsigned int* remap, + const U16 * indices, + U64 index_count, + const LLVector4a * vertex_positions, + const LLVector4a * normals, + const LLVector2 * text_coords, + U64 vertex_count) +{ + S32 out_of_range_count = 0; + U32* indices_u32 = NULL; + if (indices) + { + indices_u32 = (U32*)ll_aligned_malloc_32(index_count * sizeof(U32)); + for (U64 i = 0; i < index_count; i++) + { + if (indices[i] < vertex_count) + { + indices_u32[i] = (U32)indices[i]; + } + else + { + out_of_range_count++; + indices_u32[i] = 0; + } + } + } + + if (out_of_range_count) + { + LL_WARNS() << out_of_range_count << " indices are out of range." << LL_ENDL; + } + + size_t unique = generateRemapMultiU32(remap, indices_u32, index_count, vertex_positions, normals, text_coords, vertex_count); + + ll_aligned_free_32(indices_u32); + + return unique; +} + +void LLMeshOptimizer::remapIndexBufferU32(U32 * destination_indices, + const U32 * indices, + U64 index_count, + const unsigned int* remap) +{ + meshopt_remapIndexBuffer(destination_indices, indices, index_count, remap); +} + +void LLMeshOptimizer::remapIndexBufferU16(U16 * destination_indices, + const U16 * indices, + U64 index_count, + const unsigned int* remap) +{ + meshopt_remapIndexBuffer(destination_indices, indices, index_count, remap); +} + +void LLMeshOptimizer::remapPositionsBuffer(LLVector4a * destination_vertices, + const LLVector4a * vertex_positions, + U64 vertex_count, + const unsigned int* remap) +{ + meshopt_remapVertexBuffer((float*)destination_vertices, (const float*)vertex_positions, vertex_count, sizeof(LLVector4a), remap); +} + +void LLMeshOptimizer::remapNormalsBuffer(LLVector4a * destination_normalss, + const LLVector4a * normals, + U64 mormals_count, + const unsigned int* remap) +{ + meshopt_remapVertexBuffer((float*)destination_normalss, (const float*)normals, mormals_count, sizeof(LLVector4a), remap); +} + +void LLMeshOptimizer::remapUVBuffer(LLVector2 * destination_uvs, + const LLVector2 * uv_positions, + U64 uv_count, + const unsigned int* remap) +{ + meshopt_remapVertexBuffer((float*)destination_uvs, (const float*)uv_positions, uv_count, sizeof(LLVector2), remap); +} + //static U64 LLMeshOptimizer::simplifyU32(U32 *destination, const U32 *indices, diff --git a/indra/llmeshoptimizer/llmeshoptimizer.h b/indra/llmeshoptimizer/llmeshoptimizer.h index e8dd16dae9..ea965d6b47 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.h +++ b/indra/llmeshoptimizer/llmeshoptimizer.h @@ -28,7 +28,8 @@ #include "linden_common.h" -#include "llmath.h" +class LLVector4a; +class LLVector2; class LLMeshOptimizer { @@ -36,13 +37,85 @@ public: LLMeshOptimizer(); ~LLMeshOptimizer(); - static void generateShadowIndexBuffer( + static void generateShadowIndexBufferU32( + U32 *destination, + const U32 *indices, + U64 index_count, + const LLVector4a * vertex_positions, + const LLVector4a * normals, + const LLVector2 * text_coords, + U64 vertex_count); + + static void generateShadowIndexBufferU16( U16 *destination, const U16 *indices, U64 index_count, - const LLVector4a *vertex_positions, + const LLVector4a * vertex_positions, + const LLVector4a * normals, + const LLVector2 * text_coords, + U64 vertex_count); + + static void optimizeVertexCacheU32( + U32 *destination, + const U32 *indices, + U64 index_count, + U64 vertex_count); + + static void optimizeVertexCacheU16( + U16 *destination, + const U16 *indices, + U64 index_count, + U64 vertex_count); + + // Remap functions + // Welds indentical vertexes together. + // Removes unused vertices if indices were provided. + + static size_t generateRemapMultiU32( + unsigned int* remap, + const U32 * indices, + U64 index_count, + const LLVector4a * vertex_positions, + const LLVector4a * normals, + const LLVector2 * text_coords, + U64 vertex_count); + + static size_t generateRemapMultiU16( + unsigned int* remap, + const U16 * indices, + U64 index_count, + const LLVector4a * vertex_positions, + const LLVector4a * normals, + const LLVector2 * text_coords, + U64 vertex_count); + + static void remapIndexBufferU32(U32 * destination_indices, + const U32 * indices, + U64 index_count, + const unsigned int* remap); + + static void remapIndexBufferU16(U16 * destination_indices, + const U16 * indices, + U64 index_count, + const unsigned int* remap); + + + static void remapPositionsBuffer(LLVector4a * destination_vertices, + const LLVector4a * vertex_positions, U64 vertex_count, - U64 vertex_positions_stride); + const unsigned int* remap); + + static void remapNormalsBuffer(LLVector4a * destination_normalss, + const LLVector4a * normals, + U64 mormals_count, + const unsigned int* remap); + + static void remapUVBuffer(LLVector2 * destination_uvs, + const LLVector2 * uv_positions, + U64 uv_count, + const unsigned int* remap); + + // Simplification // returns amount of indices in destiantion // sloppy engages a variant of a mechanizm that does not respect topology as much diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index 47c5fb7924..2ab09db340 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -2721,7 +2721,7 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector& mo if (!mNoOptimize) { - ret->optimizeVolumeFaces(); + ret->remapVolumeFaces(); } volume_faces = remainder.size(); diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index 0762053b8e..d25bbf63d8 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -110,6 +110,14 @@ void LLModel::offsetMesh( const LLVector3& pivotPoint ) } } +void LLModel::remapVolumeFaces() +{ + for (U32 i = 0; i < getNumVolumeFaces(); ++i) + { + mVolumeFaces[i].remap(); + } +} + void LLModel::optimizeVolumeFaces() { for (U32 i = 0; i < getNumVolumeFaces(); ++i) diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h index 882f26d6cb..10d5457582 100644 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -189,6 +189,7 @@ public: void sortVolumeFacesByMaterialName(); void normalizeVolumeFaces(); void trimVolumeFacesToSize(U32 new_count = LL_SCULPT_MESH_MAX_FACES, LLVolume::face_list_t* remainder = NULL); + void remapVolumeFaces(); void optimizeVolumeFaces(); void offsetMesh( const LLVector3& pivotPoint ); void getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out); diff --git a/indra/llrender/CMakeLists.txt b/indra/llrender/CMakeLists.txt index d414ec78c5..32eb92432a 100644 --- a/indra/llrender/CMakeLists.txt +++ b/indra/llrender/CMakeLists.txt @@ -111,17 +111,6 @@ endif (BUILD_HEADLESS) add_library (llrender ${llrender_SOURCE_FILES}) -if (SDL_FOUND) - set_property(TARGET llrender - PROPERTY COMPILE_DEFINITIONS LL_SDL=1 - ) -endif (SDL_FOUND) -if (SDL2_FOUND) - set_property(TARGET llrender - PROPERTY COMPILE_DEFINITIONS LL_SDL2=1 LL_SDL=1 - ) -endif () - # Libraries on which this library depends, needed for Linux builds # Sort by high-level to low-level target_link_libraries(llrender diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index b896280bb8..dfc84fb4ac 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -105,6 +105,7 @@ LLButton::Params::Params() scale_image("scale_image", true), hover_glow_amount("hover_glow_amount"), commit_on_return("commit_on_return", true), + commit_on_capture_lost("commit_on_capture_lost", false), display_pressed_state("display_pressed_state", true), use_draw_context_alpha("use_draw_context_alpha", true), badge("badge"), @@ -172,6 +173,7 @@ LLButton::LLButton(const LLButton::Params& p) mBottomVPad(p.pad_bottom), mHoverGlowStrength(p.hover_glow_amount), mCommitOnReturn(p.commit_on_return), + mCommitOnCaptureLost(p.commit_on_capture_lost), mFadeWhenDisabled(FALSE), mForcePressedState(false), mDisplayPressedState(p.display_pressed_state), @@ -532,6 +534,10 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask) // We only handle the click if the click both started and ended within us if( hasMouseCapture() ) { + // reset timers before focus change, to not cause + // additional commits if mCommitOnCaptureLost. + resetMouseDownTimer(); + // Always release the mouse gFocusMgr.setMouseCapture( NULL ); @@ -546,8 +552,6 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask) // Regardless of where mouseup occurs, handle callback if(mMouseUpSignal) (*mMouseUpSignal)(this, LLSD()); - resetMouseDownTimer(); - // DO THIS AT THE VERY END to allow the button to be destroyed as a result of being clicked. // If mouseup in the widget, it's been clicked if (pointInView(x, y)) @@ -1270,6 +1274,18 @@ void LLButton::setImageOverlay(const LLUUID& image_id, LLFontGL::HAlign alignmen void LLButton::onMouseCaptureLost() { + if (mCommitOnCaptureLost + && mMouseDownTimer.getStarted()) + { + if (mMouseUpSignal) (*mMouseUpSignal)(this, LLSD()); + + if (mIsToggle) + { + toggleState(); + } + + LLUICtrl::onCommit(); + } resetMouseDownTimer(); } diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 783a93ff69..6225ba7165 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -124,6 +124,7 @@ public: Optional is_toggle, scale_image, commit_on_return, + commit_on_capture_lost, display_pressed_state; Optional hover_glow_amount; @@ -391,6 +392,7 @@ protected: F32 mCurGlowStrength; bool mCommitOnReturn; + bool mCommitOnCaptureLost; bool mFadeWhenDisabled; bool mForcePressedState; bool mDisplayPressedState; diff --git a/indra/llui/lldockablefloater.cpp b/indra/llui/lldockablefloater.cpp index c937d190c6..b822379049 100644 --- a/indra/llui/lldockablefloater.cpp +++ b/indra/llui/lldockablefloater.cpp @@ -166,6 +166,7 @@ void LLDockableFloater::setMinimized(BOOL minimize) { // minimizing a docked floater just hides it setVisible(FALSE); + gFocusMgr.releaseFocusIfNeeded(this); // FIRE-31882: Release focus or main menu bar would receive it in LLViewerWindow::updateKeyboardFocus() } else { diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index f871252d7a..c11803b0d1 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -106,16 +106,21 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p) // Spin buttons LLButton::Params up_button_params(p.up_button); up_button_params.rect = LLRect(btn_left, getRect().getHeight(), btn_right, getRect().getHeight() - spinctrl_btn_height); - up_button_params.click_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2)); + // Click callback starts within the button and ends within the button, + // but LLSpinCtrl handles the action continuosly so subsribers needs to + // be informed about click ending even if outside view, use 'up' instead + up_button_params.mouse_up_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2)); up_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2)); + up_button_params.commit_on_capture_lost = true; mUpBtn = LLUICtrlFactory::create(up_button_params); addChild(mUpBtn); LLButton::Params down_button_params(p.down_button); down_button_params.rect = LLRect(btn_left, getRect().getHeight() - spinctrl_btn_height, btn_right, getRect().getHeight() - 2 * spinctrl_btn_height); - down_button_params.click_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2)); + down_button_params.mouse_up_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2)); down_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2)); + down_button_params.commit_on_capture_lost = true; mDownBtn = LLUICtrlFactory::create(down_button_params); addChild(mDownBtn); diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt index 64f722e94e..299a434d76 100644 --- a/indra/llwindow/CMakeLists.txt +++ b/indra/llwindow/CMakeLists.txt @@ -76,7 +76,7 @@ if (LINUX) #${LLWINDOW_LIBRARIES} # Don't link to itself - causes CMP0038 ${LLXML_LIBRARIES} ${UI_LIBRARIES} # for GTK - ${SDL_LIBRARY} + sdl libfontconfig.a # For FCInit and other FC* functions. libfreetype.a ) @@ -201,16 +201,5 @@ endif (llwindow_HEADER_FILES) ${viewer_SOURCE_FILES} ) -if (SDL_FOUND) - set_property(TARGET llwindow - PROPERTY COMPILE_DEFINITIONS LL_SDL=1 - ) -endif () -if (SDL2_FOUND) - set_property(TARGET llwindow - PROPERTY COMPILE_DEFINITIONS LL_SDL2=1 LL_SDL=1 - ) -endif () - target_link_libraries (llwindow ${llwindow_LINK_LIBRARIES}) diff --git a/indra/llwindow/llkeyboardsdl2.cpp b/indra/llwindow/llkeyboardsdl2.cpp index d14c5b97a6..402feed1d5 100644 --- a/indra/llwindow/llkeyboardsdl2.cpp +++ b/indra/llwindow/llkeyboardsdl2.cpp @@ -565,22 +565,26 @@ U32 LLKeyboardSDL::mapSDL2toWin( U32 aSymbol ) mSDL2_to_Win[ SDLK_ESCAPE ] = (U32)WindowsVK::VK_ESCAPE; mSDL2_to_Win[ SDLK_DELETE ] = (U32)WindowsVK::VK_DELETE; - mSDL2_to_Win[ SDLK_KP_PERIOD ] = (U32)WindowsVK::VK_OEM_PERIOD; // VK_DECIMAL? mSDL2_to_Win[ SDLK_KP_DIVIDE ] = (U32)WindowsVK::VK_DIVIDE; mSDL2_to_Win[ SDLK_KP_MULTIPLY] = (U32)WindowsVK::VK_MULTIPLY; mSDL2_to_Win[ SDLK_KP_MINUS ] = (U32)WindowsVK::VK_OEM_MINUS; // VK_SUBSTRACT? mSDL2_to_Win[ SDLK_KP_PLUS ] = (U32)WindowsVK::VK_OEM_PLUS; // VK_ADD? mSDL2_to_Win[ SDLK_KP_ENTER ] = (U32)WindowsVK::VK_RETURN; - mSDL2_to_Win[ SDLK_KP_0 ] = (U32)WindowsVK::VK_NUMPAD0; - mSDL2_to_Win[ SDLK_KP_1 ] = (U32)WindowsVK::VK_NUMPAD1; - mSDL2_to_Win[ SDLK_KP_2 ] = (U32)WindowsVK::VK_NUMPAD2; - mSDL2_to_Win[ SDLK_KP_3 ] = (U32)WindowsVK::VK_NUMPAD3; - mSDL2_to_Win[ SDLK_KP_4 ] = (U32)WindowsVK::VK_NUMPAD4; - mSDL2_to_Win[ SDLK_KP_5 ] = (U32)WindowsVK::VK_NUMPAD5; - mSDL2_to_Win[ SDLK_KP_6 ] = (U32)WindowsVK::VK_NUMPAD6; - mSDL2_to_Win[ SDLK_KP_7 ] = (U32)WindowsVK::VK_NUMPAD7; - mSDL2_to_Win[ SDLK_KP_8 ] = (U32)WindowsVK::VK_NUMPAD8; - mSDL2_to_Win[ SDLK_KP_9 ] = (U32)WindowsVK::VK_NUMPAD9; + + // map numpad keys as best we can, mapping to VK_NUMPADx will break things + // for SDL2, so we use the actual functions + mSDL2_to_Win[ SDLK_KP_0 ] = (U32)WindowsVK::VK_INSERT; // VK_NUMPAD0 + mSDL2_to_Win[ SDLK_KP_1 ] = (U32)WindowsVK::VK_END; // VK_NUMPAD1 + mSDL2_to_Win[ SDLK_KP_2 ] = (U32)WindowsVK::VK_DOWN; // VK_NUMPAD2 + mSDL2_to_Win[ SDLK_KP_3 ] = (U32)WindowsVK::VK_NEXT; // VK_NUMPAD3 + mSDL2_to_Win[ SDLK_KP_4 ] = (U32)WindowsVK::VK_LEFT; // VK_NUMPAD4 + mSDL2_to_Win[ SDLK_KP_5 ] = (U32)WindowsVK::VK_NUMPAD5; // has no function + mSDL2_to_Win[ SDLK_KP_6 ] = (U32)WindowsVK::VK_RIGHT; // VK_NUMPAD6 + mSDL2_to_Win[ SDLK_KP_7 ] = (U32)WindowsVK::VK_HOME; // VK_NUMPAD7 + mSDL2_to_Win[ SDLK_KP_8 ] = (U32)WindowsVK::VK_UP; // VK_NUMPAD8 + mSDL2_to_Win[ SDLK_KP_9 ] = (U32)WindowsVK::VK_PRIOR; // VK_NUMPAD9 + + mSDL2_to_Win[ SDLK_KP_PERIOD ] = (U32)WindowsVK::VK_DELETE; // VK_OEM_PERIOD; // ? diff --git a/indra/llwindow/llwindowsdl2.cpp b/indra/llwindow/llwindowsdl2.cpp index 082d2a93b5..4f7e58fd55 100644 --- a/indra/llwindow/llwindowsdl2.cpp +++ b/indra/llwindow/llwindowsdl2.cpp @@ -1783,7 +1783,13 @@ void LLWindowSDL::gatherInput() for( auto key: string ) { mKeyVirtualKey = key; - handleUnicodeUTF16( key, mKeyModifiers ); + + // filter ctrl and left-alt keypresses from line inputs so we don't end up with e.g. + // "h" in teleport history filter input after pressing alt+h to call up the floater + if (mKeyModifiers & (MASK_CONTROL | MASK_ALT)) + gKeyboard->handleKeyDown(mKeyVirtualKey, mKeyModifiers); + else + handleUnicodeUTF16(key, mKeyModifiers); } break; } diff --git a/indra/media_plugins/cef/CMakeLists.txt b/indra/media_plugins/cef/CMakeLists.txt index c81a2f0385..5e90353f3c 100644 --- a/indra/media_plugins/cef/CMakeLists.txt +++ b/indra/media_plugins/cef/CMakeLists.txt @@ -61,6 +61,7 @@ if (LINUX) list(APPEND media_plugin_cef_SOURCE_FILES ${LINUX_VOLUME_CATCHER}) set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--build-id -Wl,-rpath,'$ORIGIN:$ORIGIN/../../lib'") + list(APPEND media_plugin_cef_LINK_LIBRARIES llwindow ) elseif (DARWIN) list(APPEND media_plugin_cef_SOURCE_FILES mac_volume_catcher_null.cpp) find_library(CORESERVICES_LIBRARY CoreServices) @@ -87,6 +88,7 @@ add_library(media_plugin_cef # ${MEDIA_PLUGIN_BASE_LIBRARIES} #) + target_link_libraries(media_plugin_cef ${media_plugin_cef_LINK_LIBRARIES} ) diff --git a/indra/media_plugins/cef/media_plugin_cef.cpp b/indra/media_plugins/cef/media_plugin_cef.cpp index ca185049ce..8f789279bc 100644 --- a/indra/media_plugins/cef/media_plugin_cef.cpp +++ b/indra/media_plugins/cef/media_plugin_cef.cpp @@ -1051,13 +1051,37 @@ void MediaPluginCEF::keyEvent(dullahan::EKeyEvent key_event, LLSD native_key_dat // Keyboard handling for Linux. #if LL_LINUX +#if LL_SDL2 + + uint32_t native_virtual_key = (uint32_t)(native_key_data["virtual_key"].asInteger()); // this is actually the SDL event.key.keysym.sym; + uint32_t native_virtual_key_win = (uint32_t)(native_key_data["virtual_key_win"].asInteger()); + uint32_t native_modifiers = (uint32_t)(native_key_data["modifiers"].asInteger()); + + // only for non-printable keysyms, the actual text input is done in unicodeInput() below + if (native_virtual_key <= 0x1b || native_virtual_key >= 0x7f) + { + // set keypad flag, not sure if this even does anything + bool keypad = false; + if (native_virtual_key_win >= 0x60 && native_virtual_key_win <= 0x6f) + { + keypad = true; + } + + // yes, we send native_virtual_key_win twice because native_virtual_key breaks it + mCEFLib->nativeKeyboardEventSDL2(key_event, native_virtual_key_win, native_virtual_key_win, native_modifiers, keypad); + } + +#else + uint32_t native_scan_code = (uint32_t)(native_key_data["sdl_sym"].asInteger()); uint32_t native_virtual_key = (uint32_t)(native_key_data["virtual_key"].asInteger()); uint32_t native_modifiers = (uint32_t)(native_key_data["cef_modifiers"].asInteger()); if( native_scan_code == '\n' ) native_scan_code = '\r'; mCEFLib->nativeKeyboardEvent(key_event, native_scan_code, native_virtual_key, native_modifiers); -#endif + +#endif // LL_SDL2 +#endif // LL_LINUX // }; @@ -1089,6 +1113,18 @@ void MediaPluginCEF::unicodeInput(std::string event, LLSD native_key_data = LLSD U64 lparam = ll_U32_from_sd(native_key_data["l_param"]); mCEFLib->nativeKeyboardEventWin(msg, wparam, lparam); #endif + +#if LL_LINUX +# if LL_SDL2 + + uint32_t native_scan_code = (uint32_t)(native_key_data["sdl_sym"].asInteger()); + uint32_t native_virtual_key = (uint32_t)(native_key_data["virtual_key"].asInteger()); + uint32_t native_modifiers = (uint32_t)(native_key_data["modifiers"].asInteger()); + + mCEFLib->nativeKeyboardEvent(dullahan::KE_KEY_DOWN, native_scan_code, native_virtual_key, native_modifiers); + +#endif // LL_SDL2 +#endif // LL_LINUX }; //////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 336d2979a6..5dd6cbcd2f 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -2201,17 +2201,6 @@ add_executable(${VIEWER_BINARY_NAME} ${viewer_SOURCE_FILES} ) -if (SDL_FOUND) - set_property(TARGET ${VIEWER_BINARY_NAME} - PROPERTY COMPILE_DEFINITIONS LL_SDL=1 - ) -endif (SDL_FOUND) -if (SDL2_FOUND) - set_property(TARGET ${VIEWER_BINARY_NAME} - PROPERTY COMPILE_DEFINITIONS LL_SDL2=1 LL_SDL=1 - ) -endif () - if (USE_BUGSPLAT) set_property(TARGET ${VIEWER_BINARY_NAME} PROPERTY COMPILE_DEFINITIONS "${BUGSPLAT_DEFINE}") @@ -2525,7 +2514,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${GLOD_LIBRARIES} # restore GLOD dependencies ${OPENGL_LIBRARIES} ${JSONCPP_LIBRARIES} - ${SDL_LIBRARY} + #${SDL_LIBRARY} ${SMARTHEAP_LIBRARY} ${UI_LIBRARIES} ${WINDOWS_LIBRARIES} diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 28179fc1f5..e411592c25 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -6.6.2 +6.6.3 diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 8ce4ef2de2..d467775e61 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -3223,19 +3223,14 @@ bool LLAppViewer::initConfiguration() if (clp.hasOption("graphicslevel")) { - // User explicitly requested --graphicslevel on the command line. We - // expect this switch has already set RenderQualityPerformance. Check - // that value for validity. - U32 graphicslevel = gSavedSettings.getU32("RenderQualityPerformance"); - if (LLFeatureManager::instance().isValidGraphicsLevel(graphicslevel)) - { - // graphicslevel is valid: save it and engage it later. Capture - // the requested value separately from the settings variable - // because, if this is the first run, LLViewerWindow's constructor - // will call LLFeatureManager::applyRecommendedSettings(), which - // overwrites this settings variable! - mForceGraphicsLevel = graphicslevel; - } + // User explicitly requested --graphicslevel on the command line. We + // expect this switch has already set RenderQualityPerformance. Check + // that value for validity later. + // Capture the requested value separately from the settings variable + // because, if this is the first run, LLViewerWindow's constructor + // will call LLFeatureManager::applyRecommendedSettings(), which + // overwrites this settings variable! + mForceGraphicsLevel = gSavedSettings.getU32("RenderQualityPerformance"); } // Start profiling immediately unless deferred. @@ -3676,7 +3671,7 @@ bool LLAppViewer::initWindow() // Initialize GL stuff // - if (mForceGraphicsLevel) + if (mForceGraphicsLevel && (LLFeatureManager::instance().isValidGraphicsLevel(*mForceGraphicsLevel))) { LLFeatureManager::getInstance()->setGraphicsLevel(*mForceGraphicsLevel, false); gSavedSettings.setU32("RenderQualityPerformance", *mForceGraphicsLevel); diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index 694b077720..6b94217043 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -825,7 +825,7 @@ void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit) { case LLModelPreview::MESH_OPTIMIZER_AUTO: case LLModelPreview::MESH_OPTIMIZER_SLOPPY: - case LLModelPreview::MESH_OPTIMIZER_COMBINE: + case LLModelPreview::MESH_OPTIMIZER_PRECISE: mModelPreview->onLODMeshOptimizerParamCommit(lod, enforce_tri_limit, mode); break; case LLModelPreview::GENERATE: @@ -1962,7 +1962,7 @@ void LLFloaterModelPreview::onLoDSourceCommit(S32 lod) if (index == LLModelPreview::MESH_OPTIMIZER_AUTO || index == LLModelPreview::GENERATE // Improved LOD generation || index == LLModelPreview::MESH_OPTIMIZER_SLOPPY - || index == LLModelPreview::MESH_OPTIMIZER_COMBINE) + || index == LLModelPreview::MESH_OPTIMIZER_PRECISE) { //rebuild LoD to update triangle counts onLODParamCommit(lod, true); } diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index 500ec37738..701cfc4f16 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -642,53 +642,75 @@ void LLFloaterTools::refresh() else #endif { - F32 link_cost = LLSelectMgr::getInstance()->getSelection()->getSelectedLinksetCost(); + LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); + F32 link_cost = selection->getSelectedLinksetCost(); // FIRE-9287 - LI/Prim count not reflected on OpenSim #ifdef OPENSIM - S32 prim_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); + S32 prim_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); #endif // OPENSIM // - S32 link_count = LLSelectMgr::getInstance()->getSelection()->getRootObjectCount(); + S32 link_count = selection->getRootObjectCount(); + //S32 object_count = selection->getObjectCount(); // We got this already - LLCrossParcelFunctor func; - if (LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func, true)) - { - // Selection crosses parcel bounds. - // We don't display remaining land capacity in this case. - const LLStringExplicit empty_str(""); - childSetTextArg("remaining_capacity", "[CAPACITY_STRING]", empty_str); - } - else - { - LLViewerObject* selected_object = mObjectSelection->getFirstObject(); - if (selected_object) - { - // Select a parcel at the currently selected object's position. - // FIRE-20387: Editing HUD attachment shows [CAPACITY_STRING] in tools floater - //LLViewerParcelMgr::getInstance()->selectParcelAt(selected_object->getPositionGlobal()); - if (!selected_object->isAttachment()) - { - LLViewerParcelMgr::getInstance()->selectParcelAt(selected_object->getPositionGlobal()); - } - else - { - const LLStringExplicit empty_str(""); - childSetTextArg("remaining_capacity", "[CAPACITY_STRING]", empty_str); - } - // - } - else - { - LL_WARNS() << "Failed to get selected object" << LL_ENDL; - } - } + LLCrossParcelFunctor func; + if (!LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func, true)) + { + // Unless multiple parcels selected, higlight parcel object is at. + LLViewerObject* selected_object = mObjectSelection->getFirstObject(); + if (selected_object) + { + // Select a parcel at the currently selected object's position. + LLViewerParcelMgr::getInstance()->selectParcelAt(selected_object->getPositionGlobal()); + } + else + { + LL_WARNS() << "Failed to get selected object" << LL_ENDL; + } + } + + // We got this already + //if (object_count == 1) + //{ + // // "selection_faces" shouldn't be visible if not LLToolFace::getInstance() + // // But still need to be populated in case user switches + + // std::string faces_str = ""; + + // for (LLObjectSelection::iterator iter = selection->begin(); iter != selection->end();) + // { + // LLObjectSelection::iterator nextiter = iter++; // not strictly needed, we have only one object + // LLSelectNode* node = *nextiter; + // LLViewerObject* object = (*nextiter)->getObject(); + // if (!object) + // continue; + // S32 num_tes = llmin((S32)object->getNumTEs(), (S32)object->getNumFaces()); + // for (S32 te = 0; te < num_tes; ++te) + // { + // if (node->isTESelected(te)) + // { + // if (!faces_str.empty()) + // { + // faces_str += ", "; + // } + // faces_str += llformat("%d", te); + // } + // } + // } + + // childSetTextArg("selection_faces", "[FACES_STRING]", faces_str); + //} + + //bool show_faces = (object_count == 1) + // && LLToolFace::getInstance() == LLToolMgr::getInstance()->getCurrentTool(); + //getChildView("selection_faces")->setVisible(show_faces); + // LLStringUtil::format_map_t selection_args; selection_args["OBJ_COUNT"] = llformat("%.1d", link_count); // FIRE-9287 - LI/Prim count not reflected on OpenSim #ifdef OPENSIM if (LLGridManager::getInstance()->isInOpenSim()) - selection_args["LAND_IMPACT"] = llformat("%.1d", (link_cost ? (S32)link_cost : (S32)prim_count)); + selection_args["LAND_IMPACT"] = llformat("%.1d", (link_cost ? (S32)link_cost : prim_count)); else #endif // OPENSIM // @@ -1046,8 +1068,11 @@ void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask) bool have_selection = !LLSelectMgr::getInstance()->getSelection()->isEmpty(); + // + getChildView("more info label")->setVisible(!land_visible && have_selection); getChildView("selection_count")->setVisible(!land_visible && have_selection); - getChildView("remaining_capacity")->setVisible(!land_visible && have_selection); + getChildView("selection_faces")->setVisible(LLToolFace::getInstance() == LLToolMgr::getInstance()->getCurrentTool() + && LLSelectMgr::getInstance()->getSelection()->getObjectCount() == 1); getChildView("selection_empty")->setVisible(!land_visible && !have_selection); //mTab->setVisible(!land_visible); @@ -1371,7 +1396,7 @@ void LLFloaterTools::onClickGridOptions() { LLFloater* floaterp = LLFloaterReg::showInstance("build_options"); // position floater next to build tools, not over - floaterp->setRect(gFloaterView->findNeighboringPosition(this, floaterp)); + floaterp->setShape(gFloaterView->findNeighboringPosition(this, floaterp), true); } // static @@ -1460,26 +1485,6 @@ void LLFloaterTools::updateLandImpacts() return; } - S32 rezzed_prims = parcel->getSimWidePrimCount(); - S32 total_capacity = parcel->getSimWideMaxPrimCapacity(); - LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion(); - if (region) - { - S32 max_tasks_per_region = (S32)region->getMaxTasks(); - total_capacity = llmin(total_capacity, max_tasks_per_region); - } - std::string remaining_capacity_str = ""; - - bool show_mesh_cost = gMeshRepo.meshRezEnabled(); - if (show_mesh_cost) - { - LLStringUtil::format_map_t remaining_capacity_args; - remaining_capacity_args["LAND_CAPACITY"] = llformat("%d", total_capacity - rezzed_prims); - remaining_capacity_str = getString("status_remaining_capacity", remaining_capacity_args); - } - - childSetTextArg("remaining_capacity", "[CAPACITY_STRING]", remaining_capacity_str); - // Update land impacts info in the weights floater LLFloaterObjectWeights* object_weights_floater = LLFloaterReg::findTypedInstance("object_weights"); if(object_weights_floater) diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index 0513d1c4ba..9ff8dc1513 100644 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -45,6 +45,7 @@ //#include "llfirstuse.h" #include "llfloaterreg.h" // getTypedInstance() #include "llfocusmgr.h" +//#include "lliconctrl.h" // Use own expand/collapse function #include "llinventoryfunctions.h" #include "llinventorymodel.h" #include "llinventorymodelbackgroundfetch.h" @@ -368,6 +369,9 @@ BOOL LLFloaterWorldMap::postBuild() mCurZoomVal = log(LLWorldMapView::sMapScale/256.f)/log(2.f); getChild("zoom slider")->setValue(mCurZoomVal); + + // Use own expand/collapse function + //getChild("expand_btn_panel")->setMouseDownCallback(boost::bind(&LLFloaterWorldMap::onExpandCollapseBtn, this)); setDefaultBtn(NULL); @@ -1648,6 +1652,24 @@ void LLFloaterWorldMap::onCopySLURL() LLNotificationsUtil::add("CopySLURL", args); } +// Use own expand/collapse function +//void LLFloaterWorldMap::onExpandCollapseBtn() +//{ +// LLLayoutStack* floater_stack = getChild("floater_map_stack"); +// LLLayoutPanel* controls_panel = getChild("controls_lp"); + +// bool toggle_collapse = !controls_panel->isCollapsed(); +// floater_stack->collapsePanel(controls_panel, toggle_collapse); +// floater_stack->updateLayout(); + +// std::string image_name = getString(toggle_collapse ? "expand_icon" : "collapse_icon"); +// std::string tooltip = getString(toggle_collapse ? "expand_tooltip" : "collapse_tooltip"); +// getChild("expand_collapse_icon")->setImage(LLUI::getUIImage(image_name)); +// getChild("expand_collapse_icon")->setToolTip(tooltip); +// getChild("expand_btn_panel")->setToolTip(tooltip); +//} +// + // Alchemy region tracker void LLFloaterWorldMap::onTrackRegion() { diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h index c51ea21be6..f3cf0541d9 100644 --- a/indra/newview/llfloaterworldmap.h +++ b/indra/newview/llfloaterworldmap.h @@ -155,6 +155,9 @@ protected: // Alchemy region tracker void onTrackRegion(); + // Use own expand/collapse function + //void onExpandCollapseBtn(); + void centerOnTarget(BOOL animate); void updateLocation(); diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index ef9ac5bbb6..f2f6ffe6f7 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -855,6 +855,19 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, // disabled_items.push_back(std::string("Copy")); //} + if (isAgentInventory()) + { + items.push_back(std::string("New folder from selected")); + items.push_back(std::string("Subfolder Separator")); + std::set selected_uuid_set = LLAvatarActions::getInventorySelectedUUIDs(); + uuid_vec_t ids; + std::copy(selected_uuid_set.begin(), selected_uuid_set.end(), std::back_inserter(ids)); + if (!is_only_items_selected(ids) && !is_only_cats_selected(ids)) + { + disabled_items.push_back(std::string("New folder from selected")); + } + } + if (obj->getIsLinkType()) { items.push_back(std::string("Find Original")); @@ -4723,7 +4736,16 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items.push_back(std::string("Conference Chat Folder")); items.push_back(std::string("IM All Contacts In Folder")); } + + if (((flags & ITEM_IN_MULTI_SELECTION) == 0) && hasChildren()) + { + items.push_back(std::string("Ungroup folder items")); + } } + else + { + disabled_items.push_back(std::string("New folder from selected")); + } #ifndef LL_RELEASE_FOR_DOWNLOAD if (LLFolderType::lookupIsProtectedType(type) && is_agent_inventory) diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index a12b06685b..c0577920be 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -1995,6 +1995,86 @@ void change_item_parent(const LLUUID& item_id, const LLUUID& new_parent_id) } } +void move_items_to_folder(const LLUUID& new_cat_uuid, const uuid_vec_t& selected_uuids) +{ + for (uuid_vec_t::const_iterator it = selected_uuids.begin(); it != selected_uuids.end(); ++it) + { + LLInventoryItem* inv_item = gInventory.getItem(*it); + if (inv_item) + { + change_item_parent(*it, new_cat_uuid); + } + else + { + LLInventoryCategory* inv_cat = gInventory.getCategory(*it); + if (inv_cat && !LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())) + { + gInventory.changeCategoryParent((LLViewerInventoryCategory*)inv_cat, new_cat_uuid, false); + } + } + } + + LLFloater* floater_inventory = LLFloaterReg::getInstance("inventory"); + if (!floater_inventory) + { + LL_WARNS() << "Could not find My Inventory floater" << LL_ENDL; + return; + } + LLSidepanelInventory *sidepanel_inventory = LLFloaterSidePanelContainer::getPanel("inventory"); + if (sidepanel_inventory) + { + if (sidepanel_inventory->getActivePanel()) + { + sidepanel_inventory->getActivePanel()->setSelection(new_cat_uuid, TAKE_FOCUS_YES); + LLFolderViewItem* fv_folder = sidepanel_inventory->getActivePanel()->getItemByID(new_cat_uuid); + if (fv_folder) + { + fv_folder->setOpen(TRUE); + } + } + } +} + +bool is_only_cats_selected(const uuid_vec_t& selected_uuids) +{ + for (uuid_vec_t::const_iterator it = selected_uuids.begin(); it != selected_uuids.end(); ++it) + { + LLInventoryCategory* inv_cat = gInventory.getCategory(*it); + if (!inv_cat) + { + return false; + } + } + return true; +} + +bool is_only_items_selected(const uuid_vec_t& selected_uuids) +{ + for (uuid_vec_t::const_iterator it = selected_uuids.begin(); it != selected_uuids.end(); ++it) + { + LLViewerInventoryItem* inv_item = gInventory.getItem(*it); + if (!inv_item) + { + return false; + } + } + return true; +} + + +void move_items_to_new_subfolder(const uuid_vec_t& selected_uuids, const std::string& folder_name) +{ + LLInventoryObject* first_item = gInventory.getObject(*selected_uuids.begin()); + if (!first_item) + { + return; + } + + inventory_func_type func = boost::bind(&move_items_to_folder, _1, selected_uuids); + gInventory.createNewCategory(first_item->getParentUUID(), LLFolderType::FT_NONE, folder_name, func); + +} + ///---------------------------------------------------------------------------- /// LLInventoryCollectFunctor implementations ///---------------------------------------------------------------------------- @@ -2767,6 +2847,81 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root { (new LLDirPickerThread(boost::bind(&LLInventoryAction::saveMultipleTextures, _1, selected_items, model), std::string()))->getFile(); } + else if ("new_folder_from_selected" == action) + { + + LLInventoryObject* first_item = gInventory.getObject(*ids.begin()); + if (!first_item) + { + return; + } + const LLUUID& parent_uuid = first_item->getParentUUID(); + for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + LLInventoryObject *item = gInventory.getObject(*it); + if (!item || item->getParentUUID() != parent_uuid) + { + LLNotificationsUtil::add("SameFolderRequired"); + return; + } + } + + LLSD args; + args["DESC"] = LLTrans::getString("New Folder"); + + LLNotificationsUtil::add("CreateSubfolder", args, LLSD(), + [ids](const LLSD& notification, const LLSD& response) + { + S32 opt = LLNotificationsUtil::getSelectedOption(notification, response); + if (opt == 0) + { + std::string settings_name = response["message"].asString(); + + LLInventoryObject::correctInventoryName(settings_name); + if (settings_name.empty()) + { + settings_name = LLTrans::getString("New Folder"); + } + move_items_to_new_subfolder(ids, settings_name); + } + }); + } + else if ("ungroup_folder_items" == action) + { + if (selected_uuid_set.size() == 1) + { + LLInventoryCategory* inv_cat = gInventory.getCategory(*ids.begin()); + if (!inv_cat || LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())) + { + return; + } + const LLUUID &new_cat_uuid = inv_cat->getParentUUID(); + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cat_array, item_array); + LLInventoryModel::cat_array_t cats = *cat_array; + LLInventoryModel::item_array_t items = *item_array; + + for (LLInventoryModel::cat_array_t::const_iterator cat_iter = cats.begin(); cat_iter != cats.end(); ++cat_iter) + { + LLViewerInventoryCategory* cat = *cat_iter; + if (cat) + { + gInventory.changeCategoryParent(cat, new_cat_uuid, false); + } + } + for (LLInventoryModel::item_array_t::const_iterator item_iter = items.begin(); item_iter != items.end(); ++item_iter) + { + LLViewerInventoryItem* item = *item_iter; + if(item) + { + gInventory.changeItemParent(item, new_cat_uuid, false); + } + } + gInventory.removeCategory(inv_cat->getUUID()); + gInventory.notifyObservers(); + } + } // FIRE-22851: Show texture "Save as" file picker subsequently instead all at once else if (action == "save_as") // "save_as" is only available for textures as of 01/08/2018 { diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 79dd948d3d..9be37e33f0 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -100,6 +100,10 @@ LLUUID nested_parent_id(LLUUID cur_uuid, S32 depth); S32 compute_stock_count(LLUUID cat_uuid, bool force_count = false); void change_item_parent(const LLUUID& item_id, const LLUUID& new_parent_id); +void move_items_to_new_subfolder(const uuid_vec_t& selected_uuids, const std::string& folder_name); +void move_items_to_folder(const LLUUID& new_cat_uuid, const uuid_vec_t& selected_uuids); +bool is_only_cats_selected(const uuid_vec_t& selected_uuids); +bool is_only_items_selected(const uuid_vec_t& selected_uuids); /** Miscellaneous global functions ** ** diff --git a/indra/newview/lllocalbitmaps.h b/indra/newview/lllocalbitmaps.h index e3514cd441..7aa5d3b229 100644 --- a/indra/newview/lllocalbitmaps.h +++ b/indra/newview/lllocalbitmaps.h @@ -126,7 +126,7 @@ public: LLUUID getWorldID(LLUUID tracking_id); bool isLocal(LLUUID world_id); std::string getFilename(LLUUID tracking_id); - + void feedScrollList(LLScrollListCtrl* ctrl); void doUpdates(); void setNeedsRebake(); diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index cbeeff5404..104a4c8c62 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -259,6 +259,7 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp) } mViewOption["show_textures"] = false; + mFMP = fmp; glodInit(); // Improved LOD generation mHasPivot = false; @@ -597,7 +598,7 @@ void LLModelPreview::rebuildUploadData() std::ostringstream out; out << "Attempting to use model index " << idx; // better debug (watch for dangling single line else) - if(i==4) + if (i==4) { out << " for PHYS"; } @@ -953,7 +954,7 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable std::map joint_alias_map; getJointAliases(joint_alias_map); - std::array lod_suffix; + std::array lod_suffix; for(int i=0; i < LLModel::NUM_LODS; i++) { lod_suffix[i] = gSavedSettings.getString(sSuffixVarNames[i]); @@ -1077,8 +1078,10 @@ void LLModelPreview::clearIncompatible(S32 lod) // 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]); + bool replaced_base_model = (lod == LLModel::LOD_HIGH); for (U32 i = 0; i <= LLModel::LOD_HIGH; i++) - { //clear out any entries that aren't compatible with this model + { + // Clear out any entries that aren't compatible with this model if (i != lod) { if (countRootModels(mModel[i]) != lod_size) @@ -1092,11 +1095,50 @@ void LLModelPreview::clearIncompatible(S32 lod) mBaseModel = mModel[lod]; mBaseScene = mScene[lod]; mVertexBuffer[5].clear(); + replaced_base_model = true; + clearGLODGroup(); // Improved LOD generation } } } } + + if (replaced_base_model && !mGenLOD) + { + // In case base was replaced, we might need to restart generation + + // Check if already started + bool subscribe_for_generation = mLodsQuery.empty(); + + // Remove previously scheduled work + mLodsQuery.clear(); + + LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance; + if (!fmp) return; + + // Schedule new work + for (S32 i = LLModel::LOD_HIGH; i >= 0; --i) + { + if (mModel[i].empty()) + { + // Base model was replaced, regenerate this lod if applicable + LLComboBox* lod_combo = mFMP->findChild("lod_source_" + lod_name[i]); + if (!lod_combo) return; + + S32 lod_mode = lod_combo->getCurrentIndex(); + if (lod_mode != LOD_FROM_FILE) + { + mLodsQuery.push_back(i); + } + } + } + + // Subscribe if we have pending work and not subscribed yet + if (!mLodsQuery.empty() && subscribe_for_generation) + { + doOnIdleRepeating(lodQueryCallback); + } + } } // Improved LOD generation @@ -1951,8 +1993,9 @@ void LLModelPreview::genGlodLODs(S32 which_lod, U32 decimation, bool enforce_tri // Runs per object, but likely it is a better way to run per model+submodels // returns a ratio of base model indices to resulting indices // returns -1 in case of failure -F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *target_model, F32 indices_decimator, F32 error_threshold, bool sloppy) +F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *target_model, F32 indices_decimator, F32 error_threshold, eSimplificationMode simplification_mode) { + // I. Weld faces together // Figure out buffer size S32 size_indices = 0; S32 size_vertices = 0; @@ -1987,20 +2030,21 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe { const LLVolumeFace &face = base_model->getVolumeFace(face_idx); - // vertices + // Vertices S32 copy_bytes = face.mNumVertices * sizeof(LLVector4a); LLVector4a::memcpyNonAliased16((F32*)(combined_positions + combined_positions_shift), (F32*)face.mPositions, copy_bytes); - // normals + // Normals LLVector4a::memcpyNonAliased16((F32*)(combined_normals + combined_positions_shift), (F32*)face.mNormals, copy_bytes); - // tex coords + // Tex coords copy_bytes = face.mNumVertices * sizeof(LLVector2); memcpy((void*)(combined_tex_coords + combined_positions_shift), (void*)face.mTexCoords, copy_bytes); combined_positions_shift += face.mNumVertices; - // indices, sadly can't do dumb memcpy for indices, need to adjust each value + // Indices + // Sadly can't do dumb memcpy for indices, need to adjust each value for (S32 i = 0; i < face.mNumIndices; ++i) { U16 idx = face.mIndices[i]; @@ -2011,10 +2055,42 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe indices_idx_shift += face.mNumVertices; } - // Now that we have buffers, optimize + // II. Generate a shadow buffer if nessesary. + // Welds together vertices if possible + + U32* shadow_indices = NULL; + // if MESH_OPTIMIZER_FULL, just leave as is, since generateShadowIndexBufferU32 + // won't do anything new, model was remaped on a per face basis. + // Similar for MESH_OPTIMIZER_NO_TOPOLOGY, it's pointless + // since 'simplifySloppy' ignores all topology, including normals and uvs. + // Note: simplifySloppy can affect UVs significantly. + if (simplification_mode == MESH_OPTIMIZER_NO_NORMALS) + { + // strip normals, reflections should restore relatively correctly + shadow_indices = (U32*)ll_aligned_malloc_32(size_indices * sizeof(U32)); + LLMeshOptimizer::generateShadowIndexBufferU32(shadow_indices, combined_indices, size_indices, combined_positions, NULL, combined_tex_coords, size_vertices); + } + if (simplification_mode == MESH_OPTIMIZER_NO_UVS) + { + // strip uvs, can heavily affect textures + shadow_indices = (U32*)ll_aligned_malloc_32(size_indices * sizeof(U32)); + LLMeshOptimizer::generateShadowIndexBufferU32(shadow_indices, combined_indices, size_indices, combined_positions, NULL, NULL, size_vertices); + } + + U32* source_indices = NULL; + if (shadow_indices) + { + source_indices = shadow_indices; + } + else + { + source_indices = combined_indices; + } + + // III. Simplify S32 target_indices = 0; F32 result_error = 0; // how far from original the model is, 1 == 100% - S32 new_indices = 0; + S32 size_new_indices = 0; if (indices_decimator > 0) { @@ -2024,30 +2100,30 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe { target_indices = 3; } - new_indices = LLMeshOptimizer::simplifyU32( + + size_new_indices = LLMeshOptimizer::simplifyU32( output_indices, - combined_indices, + source_indices, size_indices, combined_positions, size_vertices, LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_VERTEX], target_indices, error_threshold, - sloppy, + simplification_mode == MESH_OPTIMIZER_NO_TOPOLOGY, &result_error); - if (result_error < 0) { // Log these properly // LL_WARNS() << "Negative result error from meshoptimizer for model " << target_model->mLabel // << " target Indices: " << target_indices - // << " new Indices: " << new_indices + // << " new Indices: " << size_new_indices // << " original count: " << size_indices << LL_ENDL; std::ostringstream out; out << "Negative result error from meshoptimizer for model " << target_model->mLabel << " target Indices: " << target_indices - << " new Indices: " << new_indices + << " new Indices: " << size_new_indices << " original count: " << size_indices ; LL_WARNS() << out.str() << LL_ENDL; LLFloaterModelPreview::addStringToLog(out, true); @@ -2059,7 +2135,7 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe std::ostringstream out; out << "Good result error from meshoptimizer for model " << target_model->mLabel << " target Indices: " << target_indices - << " new Indices: " << new_indices + << " new Indices: " << size_new_indices << " original count: " << size_indices << " (result error:" << result_error << ")"; LL_DEBUGS() << out.str() << LL_ENDL; LLFloaterModelPreview::addStringToLog(out, true); @@ -2067,17 +2143,22 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe // } - if (new_indices < 3) + // free unused buffers + ll_aligned_free_32(combined_indices); + ll_aligned_free_32(shadow_indices); + combined_indices = NULL; + shadow_indices = NULL; + + if (size_new_indices < 3) { // Model should have at least one visible triangle ll_aligned_free<64>(combined_positions); ll_aligned_free_32(output_indices); - ll_aligned_free_32(combined_indices); return -1; } - // repack back into individual faces + // IV. Repack back into individual faces LLVector4a* buffer_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * size_vertices + tc_bytes_size); LLVector4a* buffer_normals = buffer_positions + size_vertices; @@ -2108,7 +2189,7 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe } // Copy relevant indices and vertices - for (S32 i = 0; i < new_indices; ++i) + for (S32 i = 0; i < size_new_indices; ++i) { U32 idx = output_indices[i]; @@ -2132,7 +2213,7 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe // LL_WARNS() << "Over triangle limit. Failed to optimize in 'per object' mode, falling back to per face variant for" // << " model " << target_model->mLabel // << " target Indices: " << target_indices - // << " new Indices: " << new_indices + // << " new Indices: " << size_new_indices // << " original count: " << size_indices // << " error treshold: " << error_threshold // << LL_ENDL; @@ -2142,20 +2223,20 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe out << "Over triangle limit. Failed to optimize in 'per object' mode, falling back to per face variant for" << " model " << target_model->mLabel << " target Indices: " << target_indices - << " new Indices: " << new_indices + << " new Indices: " << size_new_indices << " original count: " << size_indices << " error treshold: " << error_threshold; LL_DEBUGS() << out.str() << LL_ENDL; LLFloaterModelPreview::addStringToLog(out, true); } // U16 vertices overflow shouldn't happen, but just in case - new_indices = 0; + size_new_indices = 0; valid_faces = 0; for (U32 face_idx = 0; face_idx < base_model->getNumVolumeFaces(); ++face_idx) { - genMeshOptimizerPerFace(base_model, target_model, face_idx, indices_decimator, error_threshold, false); + genMeshOptimizerPerFace(base_model, target_model, face_idx, indices_decimator, error_threshold, simplification_mode); const LLVolumeFace &face = target_model->getVolumeFace(face_idx); - new_indices += face.mNumIndices; + size_new_indices += face.mNumIndices; if (face.mNumIndices >= 3) { valid_faces++; @@ -2163,7 +2244,7 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe } if (valid_faces) { - return (F32)size_indices / (F32)new_indices; + return (F32)size_indices / (F32)size_new_indices; } else { @@ -2233,18 +2314,17 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe ll_aligned_free<64>(buffer_positions); ll_aligned_free_32(output_indices); ll_aligned_free_16(buffer_indices); - ll_aligned_free_32(combined_indices); - if (new_indices < 3 || valid_faces == 0) + if (size_new_indices < 3 || valid_faces == 0) { // Model should have at least one visible triangle return -1; } - return (F32)size_indices / (F32)new_indices; + return (F32)size_indices / (F32)size_new_indices; } -F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target_model, U32 face_idx, F32 indices_decimator, F32 error_threshold, bool sloppy) +F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target_model, U32 face_idx, F32 indices_decimator, F32 error_threshold, eSimplificationMode simplification_mode) { const LLVolumeFace &face = base_model->getVolumeFace(face_idx); S32 size_indices = face.mNumIndices; @@ -2252,14 +2332,40 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target { return -1; } - // todo: do not allocate per each face, add one large buffer somewhere - // faces have limited amount of indices + S32 size = (size_indices * sizeof(U16) + 0xF) & ~0xF; - U16* output = (U16*)ll_aligned_malloc_16(size); + U16* output_indices = (U16*)ll_aligned_malloc_16(size); + + U16* shadow_indices = NULL; + // if MESH_OPTIMIZER_FULL, just leave as is, since generateShadowIndexBufferU32 + // won't do anything new, model was remaped on a per face basis. + // Similar for MESH_OPTIMIZER_NO_TOPOLOGY, it's pointless + // since 'simplifySloppy' ignores all topology, including normals and uvs. + if (simplification_mode == MESH_OPTIMIZER_NO_NORMALS) + { + U16* shadow_indices = (U16*)ll_aligned_malloc_16(size); + LLMeshOptimizer::generateShadowIndexBufferU16(shadow_indices, face.mIndices, size_indices, face.mPositions, NULL, face.mTexCoords, face.mNumVertices); + } + if (simplification_mode == MESH_OPTIMIZER_NO_UVS) + { + U16* shadow_indices = (U16*)ll_aligned_malloc_16(size); + LLMeshOptimizer::generateShadowIndexBufferU16(shadow_indices, face.mIndices, size_indices, face.mPositions, NULL, NULL, face.mNumVertices); + } + // Don't run ShadowIndexBuffer for MESH_OPTIMIZER_NO_TOPOLOGY, it's pointless + + U16* source_indices = NULL; + if (shadow_indices) + { + source_indices = shadow_indices; + } + else + { + source_indices = face.mIndices; + } S32 target_indices = 0; F32 result_error = 0; // how far from original the model is, 1 == 100% - S32 new_indices = 0; + S32 size_new_indices = 0; if (indices_decimator > 0) { @@ -2269,26 +2375,26 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target { target_indices = 3; } - new_indices = LLMeshOptimizer::simplify( - output, - face.mIndices, + + size_new_indices = LLMeshOptimizer::simplify( + output_indices, + source_indices, size_indices, face.mPositions, face.mNumVertices, LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_VERTEX], target_indices, error_threshold, - sloppy, + simplification_mode == MESH_OPTIMIZER_NO_TOPOLOGY, &result_error); - if (result_error < 0) { // Log these properly // LL_WARNS() << "Negative result error from meshoptimizer for face " << face_idx // << " of model " << target_model->mLabel // << " target Indices: " << target_indices - // << " new Indices: " << new_indices + // << " new Indices: " << size_new_indices // << " original count: " << size_indices // << " error treshold: " << error_threshold // << LL_ENDL; @@ -2296,7 +2402,7 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target out << "Negative result error from meshoptimizer for face " << face_idx << " of model " << target_model->mLabel << " target Indices: " << target_indices - << " new Indices: " << new_indices + << " new Indices: " << size_new_indices << " original count: " << size_indices << " error treshold: " << error_threshold; LL_WARNS() << out.str() << LL_ENDL; @@ -2310,7 +2416,7 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target out << "Good result error from meshoptimizer for face " << face_idx << " of model " << target_model->mLabel << " target Indices: " << target_indices - << " new Indices: " << new_indices + << " new Indices: " << size_new_indices << " original count: " << size_indices << " error treshold: " << error_threshold << " (result error:" << result_error << ")"; LL_DEBUGS("MeshUpload") << out.str() << LL_ENDL; @@ -2324,10 +2430,9 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target // Copy old values new_face = face; - - if (new_indices < 3) + if (size_new_indices < 3) { - if (!sloppy) + if (simplification_mode != MESH_OPTIMIZER_NO_TOPOLOGY) { // meshopt_optimizeSloppy() can optimize triangles away even if target_indices is > 2, // but optimize() isn't supposed to @@ -2359,23 +2464,24 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target else { // Assign new values - new_face.resizeIndices(new_indices); // will wipe out mIndices, so new_face can't substitute output - S32 idx_size = (new_indices * sizeof(U16) + 0xF) & ~0xF; - LLVector4a::memcpyNonAliased16((F32*)new_face.mIndices, (F32*)output, idx_size); + new_face.resizeIndices(size_new_indices); // will wipe out mIndices, so new_face can't substitute output + S32 idx_size = (size_new_indices * sizeof(U16) + 0xF) & ~0xF; + LLVector4a::memcpyNonAliased16((F32*)new_face.mIndices, (F32*)output_indices, idx_size); - // clear unused values + // Clear unused values new_face.optimize(); } - ll_aligned_free_16(output); + ll_aligned_free_16(output_indices); + ll_aligned_free_16(shadow_indices); - if (new_indices < 3) + if (size_new_indices < 3) { // At least one triangle is needed return -1; } - return (F32)size_indices / (F32)new_indices; + return (F32)size_indices / (F32)size_new_indices; } void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 decimation, bool enforce_tri_limit) @@ -2506,7 +2612,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d // std::string name = base->mLabel + getLodSuffix(lod); std::string name = stripSuffix(base->mLabel); std::string suffix = getLodSuffix(lod); - if ( suffix.size() > 0 ) + if (suffix.size() > 0) { name += suffix; } @@ -2522,16 +2628,19 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d // Ideally this should run not per model, // but combine all submodels with origin model as well - if (model_meshopt_mode == MESH_OPTIMIZER_COMBINE) + if (model_meshopt_mode == MESH_OPTIMIZER_PRECISE) { - // Run meshoptimizer for each model/object, up to 8 faces in one model. - - // Ideally this should run not per model, - // but combine all submodels with origin model as well - F32 res = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); - if (res < 0) + // Run meshoptimizer for each face + for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) { - target_model->copyVolumeFaces(base); + F32 res = genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL); + if (res < 0) + { + // Mesh optimizer failed and returned an invalid model + const LLVolumeFace &face = base->getVolumeFace(face_idx); + LLVolumeFace &new_face = target_model->getVolumeFace(face_idx); + new_face = face; + } } } @@ -2540,19 +2649,29 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d // Run meshoptimizer for each face for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx) { - if (genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, true) < 0) + if (genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY) < 0) { // Sloppy failed and returned an invalid model - genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, false); + genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL); } } } if (model_meshopt_mode == MESH_OPTIMIZER_AUTO) { - // Switches between 'combine' method and 'sloppy' based on combine's result. - F32 allowed_ratio_drift = 2.f; - F32 precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); + // Remove progressively more data if we can't reach the target. + F32 allowed_ratio_drift = 1.8f; + F32 precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL); + + if (precise_ratio < 0 || (precise_ratio * allowed_ratio_drift < indices_decimator)) + { + precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_NORMALS); + } + + if (precise_ratio < 0 || (precise_ratio * allowed_ratio_drift < indices_decimator)) + { + precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_UVS); + } if (precise_ratio < 0 || (precise_ratio * allowed_ratio_drift < indices_decimator)) { @@ -2560,10 +2679,11 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d // Sloppy variant can fail entirely and has issues with precision, // so code needs to do multiple attempts with different decimators. // Todo: this is a bit of a mess, needs to be refined and improved + F32 last_working_decimator = 0.f; F32 last_working_ratio = F32_MAX; - F32 sloppy_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, true); + F32 sloppy_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY); if (sloppy_ratio > 0) { @@ -2586,13 +2706,13 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d // side due to overal lack of precision, and we don't need an ideal result, which // likely does not exist, just a better one, so a partial correction is enough. F32 sloppy_decimator = indices_decimator * (indices_decimator / sloppy_ratio + 1) / 2; - sloppy_ratio = genMeshOptimizerPerModel(base, target_model, sloppy_decimator, lod_error_threshold, true); + sloppy_ratio = genMeshOptimizerPerModel(base, target_model, sloppy_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY); } if (last_working_decimator > 0 && sloppy_ratio < last_working_ratio) { // Compensation didn't work, return back to previous decimator - sloppy_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, true); + sloppy_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY); } if (sloppy_ratio < 0) @@ -2630,7 +2750,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d && sloppy_decimator > precise_ratio && sloppy_decimator > 1)// precise_ratio isn't supposed to be below 1, but check just in case { - sloppy_ratio = genMeshOptimizerPerModel(base, target_model, sloppy_decimator, lod_error_threshold, true); + sloppy_ratio = genMeshOptimizerPerModel(base, target_model, sloppy_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY); sloppy_decimator = sloppy_decimator / sloppy_decimation_step; } } @@ -2650,7 +2770,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d else { // Fallback to normal method - precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false); + precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL); } // Log stuff properly // LL_INFOS() << "Model " << target_model->getName() @@ -2874,7 +2994,6 @@ void LLModelPreview::updateStatusMessages() {//check for degenerate triangles in physics mesh U32 lod = LLModel::LOD_PHYSICS; const LLVector4a scale(0.5f); - for (U32 i = 0; i < mModel[lod].size() && !mHasDegenerate; ++i) { //for each model in the lod if (mModel[lod][i] && mModel[lod][i]->mPhysics.mHull.empty()) @@ -3360,7 +3479,7 @@ void LLModelPreview::updateStatusMessages() if (phys_tris || phys_hulls > 0) { - fmp->childEnable("Decompose"); + fmp->childEnable("Decompose"); } } else @@ -4879,7 +4998,7 @@ bool LLModelPreview::lodQueryCallback() } // return false to continue cycle - return false; + return preview->mLodsQuery.empty(); } } // nothing to process diff --git a/indra/newview/llmodelpreview.h b/indra/newview/llmodelpreview.h index c3b56ba8f2..28605af374 100644 --- a/indra/newview/llmodelpreview.h +++ b/indra/newview/llmodelpreview.h @@ -126,7 +126,7 @@ public: { LOD_FROM_FILE = 0, MESH_OPTIMIZER_AUTO, // automatically selects method based on model or face - MESH_OPTIMIZER_COMBINE, // combines faces into a single model, simplifies, then splits back into faces + MESH_OPTIMIZER_PRECISE, // combines faces into a single model, simplifies, then splits back into faces MESH_OPTIMIZER_SLOPPY, // uses sloppy method, works per face GENERATE, // Use GLOD Improved LOD generation USE_LOD_ABOVE, @@ -231,13 +231,21 @@ private: // Count amount of original models, excluding sub-models static U32 countRootModels(LLModelLoader::model_list models); + typedef enum + { + MESH_OPTIMIZER_FULL, + MESH_OPTIMIZER_NO_NORMALS, + MESH_OPTIMIZER_NO_UVS, + MESH_OPTIMIZER_NO_TOPOLOGY, + } eSimplificationMode; + // Merges faces into single mesh, simplifies using mesh optimizer, // then splits back into faces. // Returns reached simplification ratio. -1 in case of a failure. - F32 genMeshOptimizerPerModel(LLModel *base_model, LLModel *target_model, F32 indices_ratio, F32 error_threshold, bool sloppy); + F32 genMeshOptimizerPerModel(LLModel *base_model, LLModel *target_model, F32 indices_ratio, F32 error_threshold, eSimplificationMode simplification_mode); // Simplifies specified face using mesh optimizer. // Returns reached simplification ratio. -1 in case of a failure. - F32 genMeshOptimizerPerFace(LLModel *base_model, LLModel *target_model, U32 face_idx, F32 indices_ratio, F32 error_threshold, bool sloppy); + F32 genMeshOptimizerPerFace(LLModel *base_model, LLModel *target_model, U32 face_idx, F32 indices_ratio, F32 error_threshold, eSimplificationMode simplification_mode); protected: friend class LLModelLoader; diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index f5bb006e3a..e54d35b203 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -1439,7 +1439,8 @@ void LLPanelEditWearable::changeCamera(U8 subpart) gMorphView->setCameraOffset( subpart_entry->mCameraOffset ); if (gSavedSettings.getBOOL("AppearanceCameraMovement")) { - gMorphView->updateCamera(); + gAgentCamera.setFocusOnAvatar(FALSE, FALSE); + gMorphView->updateCamera(); } } diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index 8c4562dd65..890b546fe0 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -38,6 +38,7 @@ #include "llfontgl.h" // project includes +#include "llagent.h" #include "llagentdata.h" #include "llbutton.h" #include "llcheckboxctrl.h" @@ -45,9 +46,13 @@ #include "llcombobox.h" #include "lldrawpoolbump.h" #include "llface.h" +#include "llinventoryfunctions.h" +#include "llinventorymodel.h" // gInventory +#include "llinventorymodelbackgroundfetch.h" #include "lllineeditor.h" #include "llmaterialmgr.h" #include "llmediaentry.h" +#include "llmenubutton.h" #include "llnotificationsutil.h" #include "llradiogroup.h" #include "llresmgr.h" @@ -57,6 +62,8 @@ #include "lltexturectrl.h" #include "lltextureentry.h" #include "lltooldraganddrop.h" +#include "lltoolface.h" +#include "lltoolmgr.h" #include "lltrans.h" #include "llui.h" #include "llviewercontrol.h" @@ -69,8 +76,6 @@ #include "llpluginclassmedia.h" #include "llviewertexturelist.h"// Update sel manager as to which channel we're editing so it can reflect the correct overlay UI -#include "llagent.h" - // // Constant definitions for comboboxes // Must match the commbobox definitions in panel_tools_texture.xml @@ -167,8 +172,6 @@ BOOL LLPanelFace::postBuild() // childSetCommitCallback("checkbox_sync_settings", &LLPanelFace::onClickMapsSync, this); - childSetAction("copytextures",&LLPanelFace::onClickCopy,this); - childSetAction("pastetextures",&LLPanelFace::onClickPaste,this); mCtrlTexScaleU = getChild("TexScaleU"); if (mCtrlTexScaleU) @@ -399,6 +402,15 @@ BOOL LLPanelFace::postBuild() mCtrlGlow->setCommitCallback(LLPanelFace::onCommitGlow, this); } + // Extended copy & paste buttons + //mMenuClipboardColor = getChild("clipboard_color_params_btn"); + //mMenuClipboardTexture = getChild("clipboard_texture_params_btn"); + mBtnCopyFaces = getChild("copy_face_btn"); + mBtnCopyFaces->setCommitCallback(boost::bind(&LLPanelFace::onCopyFaces, this)); + mBtnPasteFaces = getChild("paste_face_btn"); + mBtnPasteFaces->setCommitCallback(boost::bind(&LLPanelFace::onPasteFaces, this)); + // + clearCtrls(); return TRUE; @@ -408,7 +420,11 @@ LLPanelFace::LLPanelFace() : LLPanel(), mIsAlpha(false) { - USE_TEXTURE = LLTrans::getString("use_texture"); + USE_TEXTURE = LLTrans::getString("use_texture"); + // Extended copy & paste buttons + //mCommitCallbackRegistrar.add("PanelFace.menuDoToSelected", boost::bind(&LLPanelFace::menuDoToSelected, this, _2)); + //mEnableCallbackRegistrar.add("PanelFace.menuEnable", boost::bind(&LLPanelFace::menuEnableItem, this, _2)); + // } @@ -418,6 +434,13 @@ LLPanelFace::~LLPanelFace() } +void LLPanelFace::draw() +{ + updateCopyTexButton(); + + LLPanel::draw(); +} + void LLPanelFace::sendTexture() { //LLTextureCtrl* mTextureCtrl = getChild("texture control"); @@ -913,12 +936,6 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) // BOOL enable_material_controls = (!gSavedSettings.getBOOL("SyncMaterialSettings")); - S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); - BOOL single_volume = ((LLSelectMgr::getInstance()->selectionAllPCode( LL_PCODE_VOLUME )) - && (selected_count == 1)); - getChildView("copytextures")->setEnabled(single_volume && editable); - getChildView("pastetextures")->setEnabled(editable); - // //LLComboBox* combobox_matmedia = getChild("combobox matmedia"); if (mComboMatMedia) @@ -1641,6 +1658,15 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) } } } + S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); + BOOL single_volume = (selected_count == 1); + // Extended copy & paste buttons + //mMenuClipboardColor->setEnabled(editable && single_volume); + mBtnCopyFaces->setEnabled(editable && single_volume); + mBtnPasteFaces->setEnabled(editable && !mClipboardParams.emptyMap() && (mClipboardParams.has("color") || mClipboardParams.has("texture"))); + + LL_INFOS() << "ADBG: enabled = " << (editable && !mClipboardParams.emptyMap() && (mClipboardParams.has("color") || mClipboardParams.has("texture")) ? "true" : "false") << LL_ENDL; + // // Set variable values for numeric expressions LLCalc* calcp = LLCalc::getInstance(); @@ -1685,6 +1711,11 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) getChildView("button align")->setEnabled(FALSE); //getChildView("has media")->setEnabled(FALSE); //getChildView("media info set")->setEnabled(FALSE); + + // Extended copy & paste buttons + mBtnCopyFaces->setEnabled(FALSE); + mBtnPasteFaces->setEnabled(FALSE); + // updateVisibility(); @@ -1701,6 +1732,23 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) } +void LLPanelFace::updateCopyTexButton() +{ + LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); + // Extended copy & paste buttons + //mMenuClipboardTexture->setEnabled(objectp && objectp->getPCode() == LL_PCODE_VOLUME && objectp->permModify() + // && !objectp->isPermanentEnforced() && !objectp->isInventoryPending() + // && (LLSelectMgr::getInstance()->getSelection()->getObjectCount() == 1)); + //std::string tooltip = (objectp && objectp->isInventoryPending()) ? LLTrans::getString("LoadingContents") : getString("paste_options"); + //mMenuClipboardTexture->setToolTip(tooltip); + mBtnCopyFaces->setEnabled(objectp && objectp->getPCode() == LL_PCODE_VOLUME && objectp->permModify() + && !objectp->isPermanentEnforced() && !objectp->isInventoryPending() + && (LLSelectMgr::getInstance()->getSelection()->getObjectCount() == 1)); + std::string tooltip = (objectp && objectp->isInventoryPending()) ? LLTrans::getString("LoadingContents") : getString("paste_options"); + mBtnCopyFaces->setToolTip(tooltip); + // +} + void LLPanelFace::refresh() { LL_DEBUGS("Materials") << LL_ENDL; @@ -1792,8 +1840,7 @@ void LLPanelFace::updateVisibility() getChildView("combobox texgen")->setVisible(combo_matmedia->getEnabled()); getChildView("checkbox planar align")->setVisible(combo_matmedia->getEnabled()); getChildView("checkbox_sync_settings")->setVisible(combo_matmedia->getEnabled()); - getChildView("copytextures")->setVisible(combo_matmedia->getEnabled()); - getChildView("pastetextures")->setVisible(combo_matmedia->getEnabled()); + getChildView("button align textures")->setVisible(combo_matmedia->getEnabled()); // and other additions... getChildView("flipTextureScaleU")->setVisible(combo_matmedia->getEnabled()); getChildView("flipTextureScaleV")->setVisible(combo_matmedia->getEnabled()); @@ -1854,6 +1901,8 @@ void LLPanelFace::updateVisibility() getChildView("bumpyRot")->setVisible(show_bumpiness); getChildView("bumpyOffsetU")->setVisible(show_bumpiness); getChildView("bumpyOffsetV")->setVisible(show_bumpiness); + + } // static @@ -2681,6 +2730,835 @@ void LLPanelFace::onAlignTexture(void* userdata) self->alignTestureLayer(); } +enum EPasteMode +{ + PASTE_COLOR, + PASTE_TEXTURE +}; + +struct LLPanelFacePasteTexFunctor : public LLSelectedTEFunctor +{ + LLPanelFacePasteTexFunctor(LLPanelFace* panel, EPasteMode mode) : + mPanelFace(panel), mMode(mode) {} + + virtual bool apply(LLViewerObject* objectp, S32 te) + { + switch (mMode) + { + case PASTE_COLOR: + mPanelFace->onPasteColor(objectp, te); + break; + case PASTE_TEXTURE: + mPanelFace->onPasteTexture(objectp, te); + break; + } + return true; + } +private: + LLPanelFace *mPanelFace; + EPasteMode mMode; +}; + +struct LLPanelFaceUpdateFunctor : public LLSelectedObjectFunctor +{ + LLPanelFaceUpdateFunctor(bool update_media) : mUpdateMedia(update_media) {} + virtual bool apply(LLViewerObject* object) + { + object->sendTEUpdate(); + if (mUpdateMedia) + { + LLVOVolume *vo = dynamic_cast(object); + if (vo && vo->hasMedia()) + { + vo->sendMediaDataUpdate(); + } + } + return true; + } +private: + bool mUpdateMedia; +}; + +struct LLPanelFaceNavigateHomeFunctor : public LLSelectedTEFunctor +{ + virtual bool apply(LLViewerObject* objectp, S32 te) + { + if (objectp && objectp->getTE(te)) + { + LLTextureEntry* tep = objectp->getTE(te); + const LLMediaEntry *media_data = tep->getMediaData(); + if (media_data) + { + if (media_data->getCurrentURL().empty() && media_data->getAutoPlay()) + { + viewer_media_t media_impl = + LLViewerMedia::getInstance()->getMediaImplFromTextureID(tep->getMediaData()->getMediaID()); + if (media_impl) + { + media_impl->navigateHome(); + } + } + } + } + return true; + } +}; + +void LLPanelFace::onCopyColor() +{ + LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(); + S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); + if (!objectp || !node + || objectp->getPCode() != LL_PCODE_VOLUME + || !objectp->permModify() + || objectp->isPermanentEnforced() + || selected_count > 1) + { + return; + } + + if (mClipboardParams.has("color")) + { + mClipboardParams["color"].clear(); + } + else + { + mClipboardParams["color"] = LLSD::emptyArray(); + } + + std::map asset_item_map; + + // a way to resolve situations where source and target have different amount of faces + S32 num_tes = llmin((S32)objectp->getNumTEs(), (S32)objectp->getNumFaces()); + mClipboardParams["color_all_tes"] = (num_tes != 1) || (LLToolFace::getInstance() == LLToolMgr::getInstance()->getCurrentTool()); + for (S32 te = 0; te < num_tes; ++te) + { + if (node->isTESelected(te)) + { + LLTextureEntry* tep = objectp->getTE(te); + if (tep) + { + LLSD te_data; + + // asLLSD() includes media + te_data["te"] = tep->asLLSD(); // Note: includes a lot more than just color/alpha/glow + + mClipboardParams["color"].append(te_data); + } + } + } +} + +void LLPanelFace::onPasteColor() +{ + if (!mClipboardParams.has("color")) + { + return; + } + + LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(); + S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); + if (!objectp || !node + || objectp->getPCode() != LL_PCODE_VOLUME + || !objectp->permModify() + || objectp->isPermanentEnforced() + || selected_count > 1) + { + // not supposed to happen + LL_WARNS() << "Failed to paste color due to missing or wrong selection" << LL_ENDL; + return; + } + + bool face_selection_mode = LLToolFace::getInstance() == LLToolMgr::getInstance()->getCurrentTool(); + LLSD &clipboard = mClipboardParams["color"]; // array + S32 num_tes = llmin((S32)objectp->getNumTEs(), (S32)objectp->getNumFaces()); + S32 compare_tes = num_tes; + + if (face_selection_mode) + { + compare_tes = 0; + for (S32 te = 0; te < num_tes; ++te) + { + if (node->isTESelected(te)) + { + compare_tes++; + } + } + } + + // we can copy if single face was copied in edit face mode or if face count matches + if (!((clipboard.size() == 1) && mClipboardParams["color_all_tes"].asBoolean()) + && compare_tes != clipboard.size()) + { + LLSD notif_args; + if (face_selection_mode) + { + static std::string reason = getString("paste_error_face_selection_mismatch"); + notif_args["REASON"] = reason; + } + else + { + static std::string reason = getString("paste_error_object_face_count_mismatch"); + notif_args["REASON"] = reason; + } + LLNotificationsUtil::add("FacePasteFailed", notif_args); + return; + } + + LLObjectSelectionHandle selected_objects = LLSelectMgr::getInstance()->getSelection(); + + LLPanelFacePasteTexFunctor paste_func(this, PASTE_COLOR); + selected_objects->applyToTEs(&paste_func); + + LLPanelFaceUpdateFunctor sendfunc(false); + selected_objects->applyToObjects(&sendfunc); +} + +void LLPanelFace::onPasteColor(LLViewerObject* objectp, S32 te) +{ + LLSD te_data; + LLSD &clipboard = mClipboardParams["color"]; // array + if ((clipboard.size() == 1) && mClipboardParams["color_all_tes"].asBoolean()) + { + te_data = *(clipboard.beginArray()); + } + else if (clipboard[te]) + { + te_data = clipboard[te]; + } + else + { + return; + } + + LLTextureEntry* tep = objectp->getTE(te); + if (tep) + { + if (te_data.has("te")) + { + // Color / Alpha + if (te_data["te"].has("colors")) + { + LLColor4 color = tep->getColor(); + + LLColor4 clip_color; + clip_color.setValue(te_data["te"]["colors"]); + + // Color + color.mV[VRED] = clip_color.mV[VRED]; + color.mV[VGREEN] = clip_color.mV[VGREEN]; + color.mV[VBLUE] = clip_color.mV[VBLUE]; + + // Alpha + color.mV[VALPHA] = clip_color.mV[VALPHA]; + + objectp->setTEColor(te, color); + } + + // Color/fullbright + if (te_data["te"].has("fullbright")) + { + objectp->setTEFullbright(te, te_data["te"]["fullbright"].asInteger()); + } + + // Glow + if (te_data["te"].has("glow")) + { + objectp->setTEGlow(te, (F32)te_data["te"]["glow"].asReal()); + } + } + } +} + +void LLPanelFace::onCopyTexture() +{ + LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(); + S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); + if (!objectp || !node + || objectp->getPCode() != LL_PCODE_VOLUME + || !objectp->permModify() + || objectp->isPermanentEnforced() + || selected_count > 1) + { + return; + } + + if (mClipboardParams.has("texture")) + { + mClipboardParams["texture"].clear(); + } + else + { + mClipboardParams["texture"] = LLSD::emptyArray(); + } + + std::map asset_item_map; + + // a way to resolve situations where source and target have different amount of faces + S32 num_tes = llmin((S32)objectp->getNumTEs(), (S32)objectp->getNumFaces()); + mClipboardParams["texture_all_tes"] = (num_tes != 1) || (LLToolFace::getInstance() == LLToolMgr::getInstance()->getCurrentTool()); + for (S32 te = 0; te < num_tes; ++te) + { + if (node->isTESelected(te)) + { + LLTextureEntry* tep = objectp->getTE(te); + if (tep) + { + LLSD te_data; + + // asLLSD() includes media + te_data["te"] = tep->asLLSD(); + te_data["te"]["shiny"] = tep->getShiny(); + te_data["te"]["bumpmap"] = tep->getBumpmap(); + te_data["te"]["bumpshiny"] = tep->getBumpShiny(); + te_data["te"]["bumpfullbright"] = tep->getBumpShinyFullbright(); + + if (te_data["te"].has("imageid")) + { + LLUUID item_id; + LLUUID id = te_data["te"]["imageid"].asUUID(); + bool from_library = get_is_predefined_texture(id); + bool full_perm = from_library; + + if (!full_perm + && objectp->permCopy() + && objectp->permTransfer() + && objectp->permModify()) + { + // If agent created this object and nothing is limiting permissions, mark as full perm + // If agent was granted permission to edit objects owned and created by somebody else, mark full perm + // This check is not perfect since we can't figure out whom textures belong to so this ended up restrictive + std::string creator_app_link; + LLUUID creator_id; + LLSelectMgr::getInstance()->selectGetCreator(creator_id, creator_app_link); + full_perm = objectp->mOwnerID == creator_id; + } + + if (id.notNull() && !full_perm) + { + std::map::iterator iter = asset_item_map.find(id); + if (iter != asset_item_map.end()) + { + item_id = iter->second; + } + else + { + // What this does is simply searches inventory for item with same asset id, + // as result it is Hightly unreliable, leaves little control to user, borderline hack + // but there are little options to preserve permissions - multiple inventory + // items might reference same asset and inventory search is expensive. + bool no_transfer = false; + if (objectp->getInventoryItemByAsset(id)) + { + no_transfer = !objectp->getInventoryItemByAsset(id)->getIsFullPerm(); + } + item_id = get_copy_free_item_by_asset_id(id, no_transfer); + // record value to avoid repeating inventory search when possible + asset_item_map[id] = item_id; + } + } + + if (item_id.notNull() && gInventory.isObjectDescendentOf(item_id, gInventory.getLibraryRootFolderID())) + { + full_perm = true; + from_library = true; + } + + { + te_data["te"]["itemfullperm"] = full_perm; + te_data["te"]["fromlibrary"] = from_library; + + // If full permission object, texture is free to copy, + // but otherwise we need to check inventory and extract permissions + // + // Normally we care only about restrictions for current user and objects + // don't inherit any 'next owner' permissions from texture, so there is + // no need to record item id if full_perm==true + if (!full_perm && !from_library && item_id.notNull()) + { + LLViewerInventoryItem* itemp = gInventory.getItem(item_id); + if (itemp) + { + LLPermissions item_permissions = itemp->getPermissions(); + if (item_permissions.allowOperationBy(PERM_COPY, + gAgent.getID(), + gAgent.getGroupID())) + { + te_data["te"]["imageitemid"] = item_id; + te_data["te"]["itemfullperm"] = itemp->getIsFullPerm(); + if (!itemp->isFinished()) + { + // needed for dropTextureAllFaces + LLInventoryModelBackgroundFetch::instance().start(item_id, false); + } + } + } + } + } + } + + LLMaterialPtr material_ptr = tep->getMaterialParams(); + if (!material_ptr.isNull()) + { + LLSD mat_data; + + mat_data["NormMap"] = material_ptr->getNormalID(); + mat_data["SpecMap"] = material_ptr->getSpecularID(); + + mat_data["NormRepX"] = material_ptr->getNormalRepeatX(); + mat_data["NormRepY"] = material_ptr->getNormalRepeatY(); + mat_data["NormOffX"] = material_ptr->getNormalOffsetX(); + mat_data["NormOffY"] = material_ptr->getNormalOffsetY(); + mat_data["NormRot"] = material_ptr->getNormalRotation(); + + mat_data["SpecRepX"] = material_ptr->getSpecularRepeatX(); + mat_data["SpecRepY"] = material_ptr->getSpecularRepeatY(); + mat_data["SpecOffX"] = material_ptr->getSpecularOffsetX(); + mat_data["SpecOffY"] = material_ptr->getSpecularOffsetY(); + mat_data["SpecRot"] = material_ptr->getSpecularRotation(); + + mat_data["SpecColor"] = material_ptr->getSpecularLightColor().getValue(); + mat_data["SpecExp"] = material_ptr->getSpecularLightExponent(); + mat_data["EnvIntensity"] = material_ptr->getEnvironmentIntensity(); + mat_data["AlphaMaskCutoff"] = material_ptr->getAlphaMaskCutoff(); + mat_data["DiffuseAlphaMode"] = material_ptr->getDiffuseAlphaMode(); + + // Replace no-copy textures, destination texture will get used instead if available + if (mat_data.has("NormMap")) + { + LLUUID id = mat_data["NormMap"].asUUID(); + if (id.notNull() && !get_can_copy_texture(id)) + { + mat_data["NormMap"] = LLUUID(gSavedSettings.getString("DefaultObjectTexture")); + mat_data["NormMapNoCopy"] = true; + } + + } + if (mat_data.has("SpecMap")) + { + LLUUID id = mat_data["SpecMap"].asUUID(); + if (id.notNull() && !get_can_copy_texture(id)) + { + mat_data["SpecMap"] = LLUUID(gSavedSettings.getString("DefaultObjectTexture")); + mat_data["SpecMapNoCopy"] = true; + } + + } + + te_data["material"] = mat_data; + } + + mClipboardParams["texture"].append(te_data); + } + } + } +} + +void LLPanelFace::onPasteTexture() +{ + if (!mClipboardParams.has("texture")) + { + return; + } + + LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(); + S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); + if (!objectp || !node + || objectp->getPCode() != LL_PCODE_VOLUME + || !objectp->permModify() + || objectp->isPermanentEnforced() + || selected_count > 1) + { + // not supposed to happen + LL_WARNS() << "Failed to paste texture due to missing or wrong selection" << LL_ENDL; + return; + } + + bool face_selection_mode = LLToolFace::getInstance() == LLToolMgr::getInstance()->getCurrentTool(); + LLSD &clipboard = mClipboardParams["texture"]; // array + S32 num_tes = llmin((S32)objectp->getNumTEs(), (S32)objectp->getNumFaces()); + S32 compare_tes = num_tes; + + if (face_selection_mode) + { + compare_tes = 0; + for (S32 te = 0; te < num_tes; ++te) + { + if (node->isTESelected(te)) + { + compare_tes++; + } + } + } + + // we can copy if single face was copied in edit face mode or if face count matches + if (!((clipboard.size() == 1) && mClipboardParams["texture_all_tes"].asBoolean()) + && compare_tes != clipboard.size()) + { + LLSD notif_args; + if (face_selection_mode) + { + static std::string reason = getString("paste_error_face_selection_mismatch"); + notif_args["REASON"] = reason; + } + else + { + static std::string reason = getString("paste_error_object_face_count_mismatch"); + notif_args["REASON"] = reason; + } + LLNotificationsUtil::add("FacePasteFailed", notif_args); + return; + } + + bool full_perm_object = true; + LLSD::array_const_iterator iter = clipboard.beginArray(); + LLSD::array_const_iterator end = clipboard.endArray(); + for (; iter != end; ++iter) + { + const LLSD& te_data = *iter; + if (te_data.has("te") && te_data["te"].has("imageid")) + { + bool full_perm = te_data["te"].has("itemfullperm") && te_data["te"]["itemfullperm"].asBoolean(); + full_perm_object &= full_perm; + if (!full_perm) + { + if (te_data["te"].has("imageitemid")) + { + LLUUID item_id = te_data["te"]["imageitemid"].asUUID(); + if (item_id.notNull()) + { + LLViewerInventoryItem* itemp = gInventory.getItem(item_id); + if (!itemp) + { + // image might be in object's inventory, but it can be not up to date + LLSD notif_args; + static std::string reason = getString("paste_error_inventory_not_found"); + notif_args["REASON"] = reason; + LLNotificationsUtil::add("FacePasteFailed", notif_args); + return; + } + } + } + else + { + // Item was not found on 'copy' stage + // Since this happened at copy, might be better to either show this + // at copy stage or to drop clipboard here + LLSD notif_args; + static std::string reason = getString("paste_error_inventory_not_found"); + notif_args["REASON"] = reason; + LLNotificationsUtil::add("FacePasteFailed", notif_args); + return; + } + } + } + } + + if (!full_perm_object) + { + LLNotificationsUtil::add("FacePasteTexturePermissions"); + } + + LLObjectSelectionHandle selected_objects = LLSelectMgr::getInstance()->getSelection(); + + LLPanelFacePasteTexFunctor paste_func(this, PASTE_TEXTURE); + selected_objects->applyToTEs(&paste_func); + + LLPanelFaceUpdateFunctor sendfunc(true); + selected_objects->applyToObjects(&sendfunc); + + LLPanelFaceNavigateHomeFunctor navigate_home_func; + selected_objects->applyToTEs(&navigate_home_func); +} + +void LLPanelFace::onPasteTexture(LLViewerObject* objectp, S32 te) +{ + LLSD te_data; + LLSD &clipboard = mClipboardParams["texture"]; // array + if ((clipboard.size() == 1) && mClipboardParams["texture_all_tes"].asBoolean()) + { + te_data = *(clipboard.beginArray()); + } + else if (clipboard[te]) + { + te_data = clipboard[te]; + } + else + { + return; + } + + LLTextureEntry* tep = objectp->getTE(te); + if (tep) + { + if (te_data.has("te")) + { + // Texture + bool full_perm = te_data["te"].has("itemfullperm") && te_data["te"]["itemfullperm"].asBoolean(); + bool from_library = te_data["te"].has("fromlibrary") && te_data["te"]["fromlibrary"].asBoolean(); + if (te_data["te"].has("imageid")) + { + const LLUUID& imageid = te_data["te"]["imageid"].asUUID(); //texture or asset id + LLViewerInventoryItem* itemp_res = NULL; + + if (te_data["te"].has("imageitemid")) + { + LLUUID item_id = te_data["te"]["imageitemid"].asUUID(); + if (item_id.notNull()) + { + LLViewerInventoryItem* itemp = gInventory.getItem(item_id); + if (itemp && itemp->isFinished()) + { + // dropTextureAllFaces will fail if incomplete + itemp_res = itemp; + } + else + { + // Theoretically shouldn't happend, but if it does happen, we + // might need to add a notification to user that paste will fail + // since inventory isn't fully loaded + LL_WARNS() << "Item " << item_id << " is incomplete, paste might fail silently." << LL_ENDL; + } + } + } + // for case when item got removed from inventory after we pressed 'copy' + // or texture got pasted into previous object + if (!itemp_res && !full_perm) + { + // Due to checks for imageitemid in LLPanelFace::onPasteTexture() this should no longer be reachable. + LL_INFOS() << "Item " << te_data["te"]["imageitemid"].asUUID() << " no longer in inventory." << LL_ENDL; + // Todo: fix this, we are often searching same texture multiple times (equal to number of faces) + // Perhaps just mPanelFace->onPasteTexture(objectp, te, &asset_to_item_id_map); ? Not pretty, but will work + LLViewerInventoryCategory::cat_array_t cats; + LLViewerInventoryItem::item_array_t items; + LLAssetIDMatches asset_id_matches(imageid); + gInventory.collectDescendentsIf(LLUUID::null, + cats, + items, + LLInventoryModel::INCLUDE_TRASH, + asset_id_matches); + + // Extremely unreliable and perfomance unfriendly. + // But we need this to check permissions and it is how texture control finds items + for (S32 i = 0; i < items.size(); i++) + { + LLViewerInventoryItem* itemp = items[i]; + if (itemp && itemp->isFinished()) + { + // dropTextureAllFaces will fail if incomplete + LLPermissions item_permissions = itemp->getPermissions(); + if (item_permissions.allowOperationBy(PERM_COPY, + gAgent.getID(), + gAgent.getGroupID())) + { + itemp_res = itemp; + break; // first match + } + } + } + } + + if (itemp_res) + { + if (te == -1) // all faces + { + LLToolDragAndDrop::dropTextureAllFaces(objectp, + itemp_res, + from_library ? LLToolDragAndDrop::SOURCE_LIBRARY : LLToolDragAndDrop::SOURCE_AGENT, + LLUUID::null); + } + else // one face + { + LLToolDragAndDrop::dropTextureOneFace(objectp, + te, + itemp_res, + from_library ? LLToolDragAndDrop::SOURCE_LIBRARY : LLToolDragAndDrop::SOURCE_AGENT, + LLUUID::null, + 0); + } + } + // not an inventory item or no complete items + else if (full_perm) + { + // Either library, local or existed as fullperm when user made a copy + LLViewerTexture* image = LLViewerTextureManager::getFetchedTexture(imageid, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + objectp->setTEImage(U8(te), image); + } + } + + if (te_data["te"].has("bumpmap")) + { + objectp->setTEBumpmap(te, (U8)te_data["te"]["bumpmap"].asInteger()); + } + if (te_data["te"].has("bumpshiny")) + { + objectp->setTEBumpShiny(te, (U8)te_data["te"]["bumpshiny"].asInteger()); + } + if (te_data["te"].has("bumpfullbright")) + { + objectp->setTEBumpShinyFullbright(te, (U8)te_data["te"]["bumpfullbright"].asInteger()); + } + + // Texture map + if (te_data["te"].has("scales") && te_data["te"].has("scalet")) + { + objectp->setTEScale(te, (F32)te_data["te"]["scales"].asReal(), (F32)te_data["te"]["scalet"].asReal()); + } + if (te_data["te"].has("offsets") && te_data["te"].has("offsett")) + { + objectp->setTEOffset(te, (F32)te_data["te"]["offsets"].asReal(), (F32)te_data["te"]["offsett"].asReal()); + } + if (te_data["te"].has("imagerot")) + { + objectp->setTERotation(te, (F32)te_data["te"]["imagerot"].asReal()); + } + + // Media + if (te_data["te"].has("media_flags")) + { + U8 media_flags = te_data["te"]["media_flags"].asInteger(); + objectp->setTEMediaFlags(te, media_flags); + LLVOVolume *vo = dynamic_cast(objectp); + if (vo && te_data["te"].has(LLTextureEntry::TEXTURE_MEDIA_DATA_KEY)) + { + vo->syncMediaData(te, te_data["te"][LLTextureEntry::TEXTURE_MEDIA_DATA_KEY], true/*merge*/, true/*ignore_agent*/); + } + } + else + { + // Keep media flags on destination unchanged + } + } + + if (te_data.has("material")) + { + LLUUID object_id = objectp->getID(); + + LLSelectedTEMaterial::setAlphaMaskCutoff(this, (U8)te_data["material"]["SpecRot"].asInteger(), te, object_id); + + // Normal + // Replace placeholders with target's + if (te_data["material"].has("NormMapNoCopy")) + { + LLMaterialPtr material = tep->getMaterialParams(); + if (material.notNull()) + { + LLUUID id = material->getNormalID(); + if (id.notNull()) + { + te_data["material"]["NormMap"] = id; + } + } + } + LLSelectedTEMaterial::setNormalID(this, te_data["material"]["NormMap"].asUUID(), te, object_id); + LLSelectedTEMaterial::setNormalRepeatX(this, (F32)te_data["material"]["NormRepX"].asReal(), te, object_id); + LLSelectedTEMaterial::setNormalRepeatY(this, (F32)te_data["material"]["NormRepY"].asReal(), te, object_id); + LLSelectedTEMaterial::setNormalOffsetX(this, (F32)te_data["material"]["NormOffX"].asReal(), te, object_id); + LLSelectedTEMaterial::setNormalOffsetY(this, (F32)te_data["material"]["NormOffY"].asReal(), te, object_id); + LLSelectedTEMaterial::setNormalRotation(this, (F32)te_data["material"]["NormRot"].asReal(), te, object_id); + + // Specular + // Replace placeholders with target's + if (te_data["material"].has("SpecMapNoCopy")) + { + LLMaterialPtr material = tep->getMaterialParams(); + if (material.notNull()) + { + LLUUID id = material->getSpecularID(); + if (id.notNull()) + { + te_data["material"]["SpecMap"] = id; + } + } + } + LLSelectedTEMaterial::setSpecularID(this, te_data["material"]["SpecMap"].asUUID(), te, object_id); + LLSelectedTEMaterial::setSpecularRepeatX(this, (F32)te_data["material"]["SpecRepX"].asReal(), te, object_id); + LLSelectedTEMaterial::setSpecularRepeatY(this, (F32)te_data["material"]["SpecRepY"].asReal(), te, object_id); + LLSelectedTEMaterial::setSpecularOffsetX(this, (F32)te_data["material"]["SpecOffX"].asReal(), te, object_id); + LLSelectedTEMaterial::setSpecularOffsetY(this, (F32)te_data["material"]["SpecOffY"].asReal(), te, object_id); + LLSelectedTEMaterial::setSpecularRotation(this, (F32)te_data["material"]["SpecRot"].asReal(), te, object_id); + LLColor4 spec_color(te_data["material"]["SpecColor"]); + LLSelectedTEMaterial::setSpecularLightColor(this, spec_color, te); + LLSelectedTEMaterial::setSpecularLightExponent(this, (U8)te_data["material"]["SpecExp"].asInteger(), te, object_id); + LLSelectedTEMaterial::setEnvironmentIntensity(this, (U8)te_data["material"]["EnvIntensity"].asInteger(), te, object_id); + LLSelectedTEMaterial::setDiffuseAlphaMode(this, (U8)te_data["material"]["SpecRot"].asInteger(), te, object_id); + if (te_data.has("te") && te_data["te"].has("shiny")) + { + objectp->setTEShiny(te, (U8)te_data["te"]["shiny"].asInteger()); + } + } + } +} + +// Extended copy & paste buttons +//void LLPanelFace::menuDoToSelected(const LLSD& userdata) +//{ +// std::string command = userdata.asString(); +// +// // paste +// if (command == "color_paste") +// { +// onPasteColor(); +// } +// else if (command == "texture_paste") +// { +// onPasteTexture(); +// } +// // copy +// else if (command == "color_copy") +// { +// onCopyColor(); +// } +// else if (command == "texture_copy") +// { +// onCopyTexture(); +// } +//} +// +//bool LLPanelFace::menuEnableItem(const LLSD& userdata) +//{ +// std::string command = userdata.asString(); +// +// // paste options +// if (command == "color_paste") +// { +// return mClipboardParams.has("color"); +// } +// else if (command == "texture_paste") +// { +// return mClipboardParams.has("texture"); +// } +// return false; +//} +void LLPanelFace::onCopyFaces() +{ + onCopyTexture(); + onCopyColor(); + mBtnPasteFaces->setEnabled(!mClipboardParams.emptyMap() && (mClipboardParams.has("color") || mClipboardParams.has("texture"))); +} + +void LLPanelFace::onPasteFaces() +{ + LLObjectSelectionHandle selected_objects = LLSelectMgr::getInstance()->getSelection(); + + LLPanelFacePasteTexFunctor paste_texture_func(this, PASTE_TEXTURE); + selected_objects->applyToTEs(&paste_texture_func); + + LLPanelFacePasteTexFunctor paste_color_func(this, PASTE_COLOR); + selected_objects->applyToTEs(&paste_color_func); + + LLPanelFaceUpdateFunctor sendfunc(true); + selected_objects->applyToObjects(&sendfunc); + + LLPanelFaceNavigateHomeFunctor navigate_home_func; + selected_objects->applyToTEs(&navigate_home_func); +} +// + // TODO: I don't know who put these in or what these are for??? void LLPanelFace::setMediaURL(const std::string& url) @@ -2773,7 +3651,7 @@ void LLPanelFace::LLSelectedTE::getFace(LLFace*& face_to_return, bool& identical void LLPanelFace::LLSelectedTE::getImageFormat(LLGLenum& image_format_to_return, bool& identical_face) { - LLGLenum image_format(0); + LLGLenum image_format; struct LLSelectedTEGetImageFormat : public LLSelectedTEGetFunctor { LLGLenum get(LLViewerObject* object, S32 te_index) @@ -2969,96 +3847,6 @@ void LLPanelFace::LLSelectedTE::getMaxDiffuseRepeats(F32& repeats, bool& identic identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &max_diff_repeats_func, repeats ); } -// Texture params/copy paste -static LLSD texture_clipboard; - -//static -void LLPanelFace::onClickCopy(void* userdata) -{ - LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstRootObject(); - LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(); - if(!objectp || !node) - { - objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); - node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(); - if (!objectp || !node) - return; - } - - texture_clipboard.clear(); - - const bool is_fullperm = (gAgent.getID() == node->mPermissions->getOwner()) && (gAgent.getID() == node->mPermissions->getCreator()); - - const S32 te_count = objectp->getNumFaces(); - for (S32 i = 0; i < te_count; i++) - { - LLSD face = objectp->getTE(i)->asLLSD(); - if (!is_fullperm) - face.erase("imageid"); - - //KC: remove media, it does not be applied right on the viewer after pasting - //TODO: fix updating media viewer side after pasting - face["media_flags"] = 0; - face.erase("media_data"); - - texture_clipboard.append(face); - } - - //debug - // std::ostringstream texXML; - // LLPointer formatter = new LLSDXMLFormatter(); - // formatter->format(texture_clipboard, texXML, LLSDFormatter::OPTIONS_PRETTY); - // LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(texXML.str())); -} - -struct LLPanelFacePasteTexFunctor : public LLSelectedTEFunctor -{ - virtual bool apply(LLViewerObject* object, S32 te) - { - if(texture_clipboard[te]) - { - LLSD face(texture_clipboard[te]); - - //KC: LLTextureEntry::fromLLSD will bail on missing fields - //replace the missing imageid with the current face's texture - if (!face.has("imageid")) - face["imageid"] = object->getTE(te)->getID(); - - LLTextureEntry tex; - if(tex.fromLLSD(face)) - { - //KC: if setTETexture is not called before setTE - //the new texture does not show to the viewer till the selection changes - object->setTETexture(U8(te), tex.getID()); - object->setTE(te, tex); - - //TODO: This didn't work, but it might be close to a fix for pasting media - // LLTextureEntry *tep = object->getTE(te); - // if (face.has("media_flags") && face.has(LLTextureEntry::TEXTURE_MEDIA_DATA_KEY)) - // { - // tep->setMediaTexGen(face["media_flags"].asInteger()); - // tep->updateMediaData(face[LLTextureEntry::TEXTURE_MEDIA_DATA_KEY]); - // } - } - else - { - LL_WARNS() << "LLPanelFace::onClickPaste : LLPanelFacePasteTexFunctor: Failed to read clipboard for face: " << te << LL_ENDL; - } - } - return true; - }; -}; - -//static -void LLPanelFace::onClickPaste(void* userdata) -{ - LLPanelFacePasteTexFunctor setfunc; - LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc); - - LLPanelFaceSendFunctor sendfunc; - LLSelectMgr::getInstance()->getSelection()->applyToObjects(&sendfunc); -} - // Materials alignment //static void LLPanelFace::onClickMapsSync(LLUICtrl* ctrl, void *userdata) diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h index 7aae9ff3e1..f8901e998c 100644 --- a/indra/newview/llpanelface.h +++ b/indra/newview/llpanelface.h @@ -48,6 +48,7 @@ class LLUICtrl; class LLViewerObject; class LLFloater; class LLMaterialID; +class LLMenuButton; // Represents an edit for use in replicating the op across one or more materials in the selection set. // @@ -97,6 +98,8 @@ public: LLPanelFace(); virtual ~LLPanelFace(); + void draw(); + void refresh(); void setMediaURL(const std::string& url); void setMediaType(const std::string& mime_type); @@ -130,6 +133,8 @@ protected: void sendMedia(); void alignTestureLayer(); + void updateCopyTexButton(); + // this function is to return TRUE if the drag should succeed. static BOOL onDragTexture(LLUICtrl* ctrl, LLInventoryItem* item); @@ -207,11 +212,27 @@ protected: static void onClickAutoFix(void*); static void onAlignTexture(void*); + // Extended copy & paste buttons + void onCopyFaces(); + void onPasteFaces(); + // + +public: // needs to be accessible to selection manager + void onCopyColor(); // records all selected faces + void onPasteColor(); // to specific face + void onPasteColor(LLViewerObject* objectp, S32 te); // to specific face + void onCopyTexture(); + void onPasteTexture(); + void onPasteTexture(LLViewerObject* objectp, S32 te); + +protected: + // Extended copy & paste buttons + //void menuDoToSelected(const LLSD& userdata); + //bool menuEnableItem(const LLSD& userdata); + // + static F32 valueGlow(LLViewerObject* object, S32 face); - // Texture params copy/paste - static void onClickCopy(void*); - static void onClickPaste(void*); // Build tool enhancements static void onClickMapsSync(LLUICtrl* ctrl, void *userdata); static void alignMaterialsProperties(LLPanelFace* self); @@ -455,7 +476,14 @@ private: * If agent selects texture which is not allowed to be applied for the currently selected object, * all controls of the floater texture picker which allow to apply the texture will be disabled. */ - void onTextureSelectionChanged(LLInventoryItem* itemp); + void onTextureSelectionChanged(LLInventoryItem* itemp); + + // Extended copy & paste buttons + //LLMenuButton* mMenuClipboardColor; + //LLMenuButton* mMenuClipboardTexture; + LLButton* mBtnCopyFaces; + LLButton* mBtnPasteFaces; + // bool mIsAlpha; @@ -470,7 +498,9 @@ private: * up-arrow on a spinner, and avoids running afoul of its throttle. */ bool mUpdateInFlight; - bool mUpdatePending; + bool mUpdatePending; + + LLSD mClipboardParams; public: #if defined(DEF_GET_MAT_STATE) diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index 24bd275d8e..a183a98ca5 100644 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -46,6 +46,7 @@ #include "llcombobox.h" #include "llfocusmgr.h" #include "llmanipscale.h" +#include "llmenubutton.h" #include "llpreviewscript.h" #include "llresmgr.h" #include "llselectmgr.h" @@ -70,17 +71,6 @@ #include "llvoavatarself.h" // [/RLVa:KB] -#include "llwindow.h" // for clipboad operations -KC - -// [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1a) -#include "rlvhandler.h" -#include "llvoavatarself.h" -// [/RLVa:KB] -#include "llinventoryfunctions.h" -#include "llinventorymodel.h" - -#include "lldrawpool.h" - // // Constants // @@ -146,8 +136,9 @@ BOOL LLPanelObject::postBuild() // Phantom checkbox mCheckPhantom = getChild("Phantom Checkbox Ctrl"); childSetCommitCallback("Phantom Checkbox Ctrl",onCommitPhantom,this); - + // Position + //mMenuClipboardPos = getChild("clipboard_pos_btn"); // Extended copy & paste buttons mLabelPosition = getChild("label position"); mCtrlPosX = getChild("Pos X"); childSetCommitCallback("Pos X",onCommitPosition,this); @@ -157,6 +148,7 @@ BOOL LLPanelObject::postBuild() childSetCommitCallback("Pos Z",onCommitPosition,this); // Scale + //mMenuClipboardSize = getChild("clipboard_size_btn"); // Extended copy & paste buttons mLabelSize = getChild("label size"); mCtrlScaleX = getChild("Scale X"); childSetCommitCallback("Scale X",onCommitScale,this); @@ -170,6 +162,7 @@ BOOL LLPanelObject::postBuild() childSetCommitCallback("Scale Z",onCommitScale,this); // Rotation + //mMenuClipboardRot = getChild("clipboard_rot_btn"); // Extended copy & paste buttons mLabelRotation = getChild("label rotation"); mCtrlRotX = getChild("Rot X"); childSetCommitCallback("Rot X",onCommitRotation,this); @@ -178,35 +171,41 @@ BOOL LLPanelObject::postBuild() mCtrlRotZ = getChild("Rot Z"); childSetCommitCallback("Rot Z",onCommitRotation,this); - // Copy/paste pos - mBtnCopyPos = getChild("copypos"); - mBtnCopyPos->setCommitCallback( boost::bind(&LLPanelObject::onCopyPos, this, _2 )); - mBtnPastePos = getChild("pastepos"); - mBtnPastePos->setCommitCallback( boost::bind(&LLPanelObject::onPastePos, this, _2 )); - mBtnPastePosClip = getChild("pasteposclip"); - mBtnPastePosClip->setCommitCallback( boost::bind(&LLPanelObject::onPastePosClip, this, _2 )); + // Copy/paste pos + mBtnCopyPos = getChild("copy_pos_btn"); + mBtnCopyPos->setCommitCallback(boost::bind(&LLPanelObject::onCopyPos, this)); + mBtnPastePos = getChild("paste_pos_btn"); + mBtnPastePos->setCommitCallback(boost::bind(&LLPanelObject::onPastePos, this)); + // Extended copy & paste buttons + mBtnPastePosClip = getChild("paste_pos_clip_btn"); + mBtnPastePosClip->setCommitCallback(boost::bind(&LLPanelObject::onPastePosClip, this)); + // - // Copy/paste size - mBtnCopySize = getChild("copysize"); - mBtnCopySize->setCommitCallback( boost::bind(&LLPanelObject::onCopySize, this, _2 )); - mBtnPasteSize = getChild("pastesize"); - mBtnPasteSize->setCommitCallback( boost::bind(&LLPanelObject::onPasteSize, this, _2 )); - mBtnPasteSizeClip = getChild("pastesizeclip"); - mBtnPasteSizeClip->setCommitCallback( boost::bind(&LLPanelObject::onPasteSizeClip, this, _2 )); + // Copy/paste size + mBtnCopySize = getChild("copy_size_btn"); + mBtnCopySize->setCommitCallback(boost::bind(&LLPanelObject::onCopySize, this)); + mBtnPasteSize = getChild("paste_size_btn"); + mBtnPasteSize->setCommitCallback(boost::bind(&LLPanelObject::onPasteSize, this)); + // Extended copy & paste buttons + mBtnPasteSizeClip = getChild("paste_size_clip_btn"); + mBtnPasteSizeClip->setCommitCallback(boost::bind(&LLPanelObject::onPasteSizeClip, this)); + // - // Copy/paste rot - mBtnCopyRot = getChild("copyrot"); - mBtnCopyRot->setCommitCallback( boost::bind(&LLPanelObject::onCopyRot, this, _2 )); - mBtnPasteRot = getChild("pasterot"); - mBtnPasteRot->setCommitCallback( boost::bind(&LLPanelObject::onPasteRot, this, _2 )); - mBtnPasteRotClip = getChild("pasterotclip"); - mBtnPasteRotClip->setCommitCallback( boost::bind(&LLPanelObject::onPasteRotClip, this, _2 )); + // Copy/paste rot + mBtnCopyRot = getChild("copy_rot_btn"); + mBtnCopyRot->setCommitCallback(boost::bind(&LLPanelObject::onCopyRot, this)); + mBtnPasteRot = getChild("paste_rot_btn"); + mBtnPasteRot->setCommitCallback(boost::bind(&LLPanelObject::onPasteRot, this));; + // Extended copy & paste buttons + mBtnPasteRotClip = getChild("paste_rot_clip_btn"); + mBtnPasteRotClip->setCommitCallback(boost::bind(&LLPanelObject::onPasteRotClip, this)); + // - // Copy/paste obj prams - mBtnCopyParams = getChild("copyparams"); - mBtnCopyParams->setCommitCallback( boost::bind(&LLPanelObject::onCopyParams, this, _2 )); - mBtnPasteParams = getChild("pasteparams"); - mBtnPasteParams->setCommitCallback( boost::bind(&LLPanelObject::onPasteParams, this, _2 )); + // Copy/paste obj prams + mBtnCopyParams = getChild("copy_params_btn"); + mBtnCopyParams->setCommitCallback(boost::bind(&LLPanelObject::onCopyParams, this)); + mBtnPasteParams = getChild("paste_params_btn"); + mBtnPasteParams->setCommitCallback(boost::bind(&LLPanelObject::onPasteParams, this)); //-------------------------------------------------------- @@ -214,6 +213,8 @@ BOOL LLPanelObject::postBuild() mComboBaseType = getChild("comboBaseType"); childSetCommitCallback("comboBaseType",onCommitParametric,this); + //mMenuClipboardParams = getChild("clipboard_obj_params_btn"); // Extended copy & paste buttons + // Cut mLabelCut = getChild("text cut"); mSpinCutBegin = getChild("cut begin"); @@ -353,15 +354,15 @@ LLPanelObject::LLPanelObject() mSelectedType(MI_BOX), mSculptTextureRevert(LLUUID::null), mSculptTypeRevert(0), - mSizeChanged(FALSE), - mHasPosClipboard(FALSE), - mHasSizeClipboard(FALSE), - mHasRotClipboard(FALSE), - mHasParamsClipboard(FALSE), - mHasFlexiParam(FALSE), - mHasSculptParam(FALSE), - mHasLightParam(FALSE) + mHasClipboardPos(false), + mHasClipboardSize(false), + mHasClipboardRot(false), + mSizeChanged(FALSE) { + // Extended copy & paste buttons + //mCommitCallbackRegistrar.add("PanelObject.menuDoToSelected", boost::bind(&LLPanelObject::menuDoToSelected, this, _2)); + //mEnableCallbackRegistrar.add("PanelObject.menuEnable", boost::bind(&LLPanelObject::menuEnableItem, this, _2)); + // } @@ -534,11 +535,16 @@ void LLPanelObject::getState( ) calcp->clearVar(LLCalc::Z_POS); } - + //mMenuClipboardPos->setEnabled(enable_move); // Extended copy & paste buttons mLabelPosition->setEnabled( enable_move ); mCtrlPosX->setEnabled(enable_move); mCtrlPosY->setEnabled(enable_move); mCtrlPosZ->setEnabled(enable_move); + // Extended copy & paste buttons + mBtnCopyPos->setEnabled(enable_move); + mBtnPastePos->setEnabled(enable_move && mHasClipboardPos); + mBtnPastePosClip->setEnabled( enable_move ); + // if (enable_scale) { @@ -560,10 +566,16 @@ void LLPanelObject::getState( ) calcp->setVar(LLCalc::Z_SCALE, 0.f); } + //mMenuClipboardSize->setEnabled(enable_scale); // Extended copy & paste buttons mLabelSize->setEnabled( enable_scale ); mCtrlScaleX->setEnabled( enable_scale ); mCtrlScaleY->setEnabled( enable_scale ); mCtrlScaleZ->setEnabled( enable_scale ); + // Extended copy & paste buttons + mBtnCopySize->setEnabled( enable_scale ); + mBtnPasteSize->setEnabled( enable_scale && mHasClipboardSize ); + mBtnPasteSizeClip->setEnabled( enable_scale ); + // LLQuaternion object_rot = objectp->getRotationEdit(); object_rot.getEulerAngles(&(mCurEulerDegrees.mV[VX]), &(mCurEulerDegrees.mV[VY]), &(mCurEulerDegrees.mV[VZ])); @@ -591,22 +603,19 @@ void LLPanelObject::getState( ) calcp->clearVar(LLCalc::Z_ROT); } + //mMenuClipboardRot->setEnabled(enable_rotate); // Extended copy & paste buttons mLabelRotation->setEnabled( enable_rotate ); mCtrlRotX->setEnabled( enable_rotate ); mCtrlRotY->setEnabled( enable_rotate ); mCtrlRotZ->setEnabled( enable_rotate ); + // Extended copy & paste buttons + mBtnCopyRot->setEnabled( enable_rotate ); + mBtnPasteRot->setEnabled( enable_rotate && mHasClipboardRot ); + mBtnPasteRotClip->setEnabled( enable_rotate ); - mBtnCopyPos->setEnabled(enable_move); - mBtnPastePos->setEnabled(enable_move); - mBtnPastePosClip->setEnabled(enable_move); - mBtnCopySize->setEnabled( enable_scale ); - mBtnPasteSize->setEnabled( enable_scale ); - mBtnPasteSizeClip->setEnabled( enable_scale ); - mBtnCopyRot->setEnabled( enable_rotate ); - mBtnPasteRot->setEnabled( enable_rotate ); - mBtnPasteRotClip->setEnabled( enable_rotate ); - mBtnCopyParams->setEnabled( single_volume && objectp->permModify() ); - mBtnPasteParams->setEnabled( single_volume && objectp->permModify() ); + mBtnCopyParams->setEnabled( single_volume && enable_modify ); + mBtnPasteParams->setEnabled( single_volume && enable_modify && !mClipboardParams.emptyMap() && (mClipboardParams["volume_params"] || mClipboardParams["sculpt"])); + // LLUUID owner_id; std::string owner_name; @@ -830,7 +839,7 @@ void LLPanelObject::getState( ) //Working33 --> else { - LL_INFOS() << "Unknown path " << (S32) path << " profile " << (S32) profile << " in getState" << LL_ENDL; + LL_INFOS("FloaterTools") << "Unknown path " << (S32) path << " profile " << (S32) profile << " in getState" << LL_ENDL; selected_item = MI_BOX; } @@ -1198,6 +1207,7 @@ void LLPanelObject::getState( ) // Update field enablement mComboBaseType ->setEnabled( enabled ); + //mMenuClipboardParams->setEnabled(enabled); // Extended copy & paste buttons mLabelCut ->setEnabled( enabled ); mSpinCutBegin ->setEnabled( enabled ); @@ -1374,6 +1384,8 @@ void LLPanelObject::getState( ) } mComboBaseType->setEnabled(!isMesh); + //mMenuClipboardParams->setEnabled(!isMesh); // Extended copy & paste buttons + if (mCtrlSculptType) { if (sculpt_stitching == LL_SCULPT_TYPE_NONE) @@ -1640,11 +1652,11 @@ void LLPanelObject::sendIsPhysical() LLSelectMgr::getInstance()->selectionUpdatePhysics(value); mIsPhysical = value; - LL_INFOS() << "update physics sent" << LL_ENDL; + LL_INFOS("FloaterTools") << "update physics sent" << LL_ENDL; } else { - LL_INFOS() << "update physics not changed" << LL_ENDL; + LL_INFOS("FloaterTools") << "update physics not changed" << LL_ENDL; } } @@ -1656,11 +1668,11 @@ void LLPanelObject::sendIsTemporary() LLSelectMgr::getInstance()->selectionUpdateTemporary(value); mIsTemporary = value; - LL_INFOS() << "update temporary sent" << LL_ENDL; + LL_INFOS("FloaterTools") << "update temporary sent" << LL_ENDL; } else { - LL_INFOS() << "update temporary not changed" << LL_ENDL; + LL_INFOS("FloaterTools") << "update temporary not changed" << LL_ENDL; } } @@ -1673,11 +1685,11 @@ void LLPanelObject::sendIsPhantom() LLSelectMgr::getInstance()->selectionUpdatePhantom(value); mIsPhantom = value; - LL_INFOS() << "update phantom sent" << LL_ENDL; + LL_INFOS("FloaterTools") << "update phantom sent" << LL_ENDL; } else { - LL_INFOS() << "update phantom not changed" << LL_ENDL; + LL_INFOS("FloaterTools") << "update phantom not changed" << LL_ENDL; } } @@ -1872,7 +1884,7 @@ void LLPanelObject::getVolumeParams(LLVolumeParams& volume_params) //Working33 --> default: - LL_WARNS() << "Unknown base type " << selected_type + LL_WARNS("FloaterTools") << "Unknown base type " << selected_type << " in getVolumeParams()" << LL_ENDL; // assume a box selected_type = MI_BOX; @@ -2244,14 +2256,16 @@ void LLPanelObject::sendPosition(BOOL btn_down) LLViewerRegion* regionp = mObject->getRegion(); - // ## Zi: Building spin controls for attachments + if (!regionp) return; + + // Building spin controls for attachments LLVector3d new_pos_global; if (mObject->isAttachment()) { newpos.clamp(LLVector3(-MAX_ATTACHMENT_DIST,-MAX_ATTACHMENT_DIST,-MAX_ATTACHMENT_DIST),LLVector3(MAX_ATTACHMENT_DIST,MAX_ATTACHMENT_DIST,MAX_ATTACHMENT_DIST)); } - // ## Zi: Building spin controls for attachments + // Building spin controls for attachments else { // Clamp the Z height @@ -2670,357 +2684,389 @@ void LLPanelObject::onCommitSculptType(LLUICtrl *ctrl, void* userdata) self->sendSculpt(); } -std::string get_vector_format_string() -{ - S32 precision = gSavedSettings.getS32("FSBuildToolDecimalPrecision"); - return llformat("<%%.%df, %%.%df, %%.%df>", precision, precision, precision); -} +// Extended copy & paste buttons +//void LLPanelObject::menuDoToSelected(const LLSD& userdata) +//{ +// std::string command = userdata.asString(); +// +// // paste +// if (command == "psr_paste") +// { +// onPastePos(); +// onPasteSize(); +// onPasteRot(); +// } +// else if (command == "pos_paste") +// { +// onPastePos(); +// } +// else if (command == "size_paste") +// { +// onPasteSize(); +// } +// else if (command == "rot_paste") +// { +// onPasteRot(); +// } +// else if (command == "params_paste") +// { +// onPasteParams(); +// } +// // copy +// else if (command == "psr_copy") +// { +// onCopyPos(); +// onCopySize(); +// onCopyRot(); +// } +// else if (command == "pos_copy") +// { +// onCopyPos(); +// } +// else if (command == "size_copy") +// { +// onCopySize(); +// } +// else if (command == "rot_copy") +// { +// onCopyRot(); +// } +// else if (command == "params_copy") +// { +// onCopyParams(); +// } +//} +// +//bool LLPanelObject::menuEnableItem(const LLSD& userdata) +//{ +// std::string command = userdata.asString(); +// +// // paste options +// if (command == "psr_paste") +// { +// S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); +// BOOL single_volume = (LLSelectMgr::getInstance()->selectionAllPCode(LL_PCODE_VOLUME)) +// && (selected_count == 1); +// +// if (!single_volume) +// { +// return false; +// } +// +// bool enable_move; +// bool enable_modify; +// +// LLSelectMgr::getInstance()->selectGetEditMoveLinksetPermissions(enable_move, enable_modify); +// +// return enable_move && enable_modify && mHasClipboardPos && mHasClipboardSize && mHasClipboardRot; +// } +// else if (command == "pos_paste") +// { +// // assumes that menu won't be active if there is no move permission +// return mHasClipboardPos; +// } +// else if (command == "size_paste") +// { +// return mHasClipboardSize; +// } +// else if (command == "rot_paste") +// { +// return mHasClipboardRot; +// } +// else if (command == "params_paste") +// { +// return mHasClipboardParams; +// } +// // copy options +// else if (command == "psr_copy") +// { +// S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); +// BOOL single_volume = (LLSelectMgr::getInstance()->selectionAllPCode(LL_PCODE_VOLUME)) +// && (selected_count == 1); +// +// if (!single_volume) +// { +// return false; +// } +// +// bool enable_move; +// bool enable_modify; +// +// LLSelectMgr::getInstance()->selectGetEditMoveLinksetPermissions(enable_move, enable_modify); +// +// // since we forbid seeing values we also should forbid copying them +// return enable_move && enable_modify; +// } +// return false; +//} void copy_vector_to_clipboard(const LLVector3& vec) { - std::string stringVec = llformat(get_vector_format_string().c_str(), vec.mV[VX], vec.mV[VY], vec.mV[VZ]); + S32 precision = gSavedSettings.getS32("FSBuildToolDecimalPrecision"); + std::string format = llformat("<%%.%df, %%.%df, %%.%df>", precision, precision, precision); + std::string stringVec = llformat(format.c_str(), vec.mV[VX], vec.mV[VY], vec.mV[VZ]); LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(stringVec)); } +// -void LLPanelObject::onCopyPos(const LLSD& data) +void LLPanelObject::onCopyPos() { - mClipboardPos = LLVector3(mCtrlPosX->get(), mCtrlPosY->get(), mCtrlPosZ->get()); - copy_vector_to_clipboard(mClipboardPos); - LLStringUtil::format_map_t args; - args["VALUE"] = llformat(get_vector_format_string().c_str(), mClipboardPos.mV[VX], mClipboardPos.mV[VY], mClipboardPos.mV[VZ]); - mBtnPastePos->setToolTip(getString("Paste Position", args)); - mHasPosClipboard = TRUE; + mClipboardPos = LLVector3(mCtrlPosX->get(), mCtrlPosY->get(), mCtrlPosZ->get()); + + std::string stringVec = llformat("<%g, %g, %g>", mClipboardPos.mV[VX], mClipboardPos.mV[VY], mClipboardPos.mV[VZ]); + // Extended copy & paste buttons + //LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(stringVec)); + copy_vector_to_clipboard(mClipboardPos); + + LLStringUtil::format_map_t args; + args["VALUE"] = stringVec; + mBtnPastePos->setToolTip(getString("paste_position", args)); + + mBtnPastePos->setEnabled(TRUE); + // + + mHasClipboardPos = true; } -void LLPanelObject::onCopySize(const LLSD& data) +void LLPanelObject::onCopySize() { - mClipboardSize = LLVector3(mCtrlScaleX->get(), mCtrlScaleY->get(), mCtrlScaleZ->get()); - copy_vector_to_clipboard(mClipboardSize); - LLStringUtil::format_map_t args; - args["VALUE"] = llformat(get_vector_format_string().c_str(), mClipboardSize.mV[VX], mClipboardSize.mV[VY], mClipboardSize.mV[VZ]); - mBtnPasteSize->setToolTip(getString("Paste Size", args)); - mHasSizeClipboard = TRUE; + mClipboardSize = LLVector3(mCtrlScaleX->get(), mCtrlScaleY->get(), mCtrlScaleZ->get()); + + std::string stringVec = llformat("<%g, %g, %g>", mClipboardSize.mV[VX], mClipboardSize.mV[VY], mClipboardSize.mV[VZ]); + // Extended copy & paste buttons + //LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(stringVec)); + copy_vector_to_clipboard(mClipboardSize); + + LLStringUtil::format_map_t args; + args["VALUE"] = stringVec; + mBtnPasteSize->setToolTip(getString("paste_size", args)); + + mBtnPasteSize->setEnabled(TRUE); + // + + mHasClipboardSize = true; } -void LLPanelObject::onCopyRot(const LLSD& data) +void LLPanelObject::onCopyRot() { - mClipboardRot = LLVector3(mCtrlRotX->get(), mCtrlRotY->get(), mCtrlRotZ->get()); - copy_vector_to_clipboard(mClipboardRot); - LLStringUtil::format_map_t args; - args["VALUE"] = llformat(get_vector_format_string().c_str(), mClipboardRot.mV[VX], mClipboardRot.mV[VY], mClipboardRot.mV[VZ]); - mBtnPasteRot->setToolTip(getString("Paste Rotation", args)); - mHasRotClipboard = TRUE; + mClipboardRot = LLVector3(mCtrlRotX->get(), mCtrlRotY->get(), mCtrlRotZ->get()); + + std::string stringVec = llformat("<%g, %g, %g>", mClipboardRot.mV[VX], mClipboardRot.mV[VY], mClipboardRot.mV[VZ]); + // Extended copy & paste buttons + //LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(stringVec)); + copy_vector_to_clipboard(mClipboardRot); + + LLStringUtil::format_map_t args; + args["VALUE"] = stringVec; + mBtnPasteRot->setToolTip(getString("paste_rotation", args)); + + mBtnPasteRot->setEnabled(TRUE); + // + + mHasClipboardRot = true; } - -void LLPanelObject::onPastePos(const LLSD& data) +void LLPanelObject::onPastePos() { - if(!mHasPosClipboard) return; + if (!mHasClipboardPos) return; if (mObject.isNull()) return; LLViewerRegion* regionp = mObject->getRegion(); if (!regionp) return; - //clamp pos on non-attachments, just keep the prims on the sim - if (!mObject->isAttachment()) - { -// Aurora Sim - //mClipboardPos.mV[VX] = llclamp( mClipboardPos.mV[VX], 0.f, 256.f); - //mClipboardPos.mV[VY] = llclamp( mClipboardPos.mV[VY], 0.f, 256.f); - mClipboardPos.mV[VX] = llclamp( mClipboardPos.mV[VX], 0.f, regionp->getWidth()); - mClipboardPos.mV[VY] = llclamp( mClipboardPos.mV[VY], 0.f, regionp->getWidth()); -// Aurora Sim - //height will get properly clammed by sendPosition - } + // Clamp pos on non-attachments, just keep the prims within the region + if (!mObject->isAttachment()) + { + F32 max_width = regionp->getWidth(); // meters + mClipboardPos.mV[VX] = llclamp( mClipboardPos.mV[VX], 0.f, max_width); + mClipboardPos.mV[VY] = llclamp( mClipboardPos.mV[VY], 0.f, max_width); + //height will get properly clammed by sendPosition + } - mCtrlPosX->set( mClipboardPos.mV[VX] ); - mCtrlPosY->set( mClipboardPos.mV[VY] ); - mCtrlPosZ->set( mClipboardPos.mV[VZ] ); + mCtrlPosX->set( mClipboardPos.mV[VX] ); + mCtrlPosY->set( mClipboardPos.mV[VY] ); + mCtrlPosZ->set( mClipboardPos.mV[VZ] ); - LLCalc* calcp = LLCalc::getInstance(); - calcp->setVar(LLCalc::X_POS, mClipboardPos.mV[VX]); - calcp->setVar(LLCalc::Y_POS, mClipboardPos.mV[VY]); - calcp->setVar(LLCalc::Z_POS, mClipboardPos.mV[VZ]); - - sendPosition(FALSE); + sendPosition(FALSE); } -void LLPanelObject::onPasteSize(const LLSD& data) +void LLPanelObject::onPasteSize() { - if(!mHasSizeClipboard) return; -// -// mClipboardSize.mV[VX] = llclamp(mClipboardSize.mV[VX], MIN_PRIM_SCALE, llpanelobject_max_prim_scale()); -// mClipboardSize.mV[VY] = llclamp(mClipboardSize.mV[VY], MIN_PRIM_SCALE, llpanelobject_max_prim_scale()); -// mClipboardSize.mV[VZ] = llclamp(mClipboardSize.mV[VZ], MIN_PRIM_SCALE, llpanelobject_max_prim_scale()); - mClipboardSize.mV[VX] = llclamp(mClipboardSize.mV[VX], mMinScale, llpanelobject_max_prim_scale()); - mClipboardSize.mV[VY] = llclamp(mClipboardSize.mV[VY], mMinScale, llpanelobject_max_prim_scale()); - mClipboardSize.mV[VZ] = llclamp(mClipboardSize.mV[VZ], mMinScale, llpanelobject_max_prim_scale()); -// + if (!mHasClipboardSize) return; - mCtrlScaleX->set( mClipboardSize.mV[VX] ); - mCtrlScaleY->set( mClipboardSize.mV[VY] ); - mCtrlScaleZ->set( mClipboardSize.mV[VZ] ); + // + //mClipboardSize.mV[VX] = llclamp(mClipboardSize.mV[VX], MIN_PRIM_SCALE, DEFAULT_MAX_PRIM_SCALE); + //mClipboardSize.mV[VY] = llclamp(mClipboardSize.mV[VY], MIN_PRIM_SCALE, DEFAULT_MAX_PRIM_SCALE); + //mClipboardSize.mV[VZ] = llclamp(mClipboardSize.mV[VZ], MIN_PRIM_SCALE, DEFAULT_MAX_PRIM_SCALE); + mClipboardSize.mV[VX] = llclamp(mClipboardSize.mV[VX], mMinScale, llpanelobject_max_prim_scale()); + mClipboardSize.mV[VY] = llclamp(mClipboardSize.mV[VY], mMinScale, llpanelobject_max_prim_scale()); + mClipboardSize.mV[VZ] = llclamp(mClipboardSize.mV[VZ], mMinScale, llpanelobject_max_prim_scale()); + // - LLCalc* calcp = LLCalc::getInstance(); - calcp->setVar(LLCalc::X_SCALE, mClipboardSize.mV[VX]); - calcp->setVar(LLCalc::Y_SCALE, mClipboardSize.mV[VY]); - calcp->setVar(LLCalc::Z_SCALE, mClipboardSize.mV[VZ]); + mCtrlScaleX->set(mClipboardSize.mV[VX]); + mCtrlScaleY->set(mClipboardSize.mV[VY]); + mCtrlScaleZ->set(mClipboardSize.mV[VZ]); - sendScale(FALSE); + sendScale(FALSE); } -void LLPanelObject::onPasteRot(const LLSD& data) +void LLPanelObject::onPasteRot() { - if(!mHasRotClipboard) return; - - mCtrlRotX->set( mClipboardRot.mV[VX] ); - mCtrlRotY->set( mClipboardRot.mV[VY] ); - mCtrlRotZ->set( mClipboardRot.mV[VZ] ); + if (!mHasClipboardRot) return; - LLCalc* calcp = LLCalc::getInstance(); - calcp->setVar(LLCalc::X_ROT, mClipboardRot.mV[VX]); - calcp->setVar(LLCalc::Y_ROT, mClipboardRot.mV[VY]); - calcp->setVar(LLCalc::Z_ROT, mClipboardRot.mV[VZ]); + mCtrlRotX->set(mClipboardRot.mV[VX]); + mCtrlRotY->set(mClipboardRot.mV[VY]); + mCtrlRotZ->set(mClipboardRot.mV[VZ]); - sendRotation(FALSE); + sendRotation(FALSE); } -//Paste from clip board -BOOL get_vector_from_clipboard(LLVector3* value) +void LLPanelObject::onCopyParams() +{ + LLViewerObject* objectp = mObject; + if (!objectp || objectp->isMesh()) + { + return; + } + + mClipboardParams.clear(); + + // Parametrics + LLVolumeParams params; + getVolumeParams(params); + mClipboardParams["volume_params"] = params.asLLSD(); + + // Sculpted Prim + if (objectp->getParameterEntryInUse(LLNetworkData::PARAMS_SCULPT)) + { + LLSculptParams *sculpt_params = (LLSculptParams *)objectp->getParameterEntry(LLNetworkData::PARAMS_SCULPT); + + LLUUID texture_id = sculpt_params->getSculptTexture(); + if (get_can_copy_texture(texture_id)) + { + LL_DEBUGS("FloaterTools") << "Recording texture" << LL_ENDL; + mClipboardParams["sculpt"]["id"] = texture_id; + } + else + { + mClipboardParams["sculpt"]["id"] = LLUUID(SCULPT_DEFAULT_TEXTURE); + } + + mClipboardParams["sculpt"]["type"] = sculpt_params->getSculptType(); + } + + // Extended copy & paste buttons + mBtnPasteParams->setEnabled(!mClipboardParams.emptyMap() && (mClipboardParams["volume_params"] || mClipboardParams["sculpt"])); +} + +void LLPanelObject::onPasteParams() +{ + LLViewerObject* objectp = mObject; + if (!objectp) + { + return; + } + + // Sculpted Prim + if (mClipboardParams.has("sculpt")) + { + LLSculptParams sculpt_params; + LLUUID sculpt_id = mClipboardParams["sculpt"]["id"].asUUID(); + U8 sculpt_type = (U8)mClipboardParams["sculpt"]["type"].asInteger(); + sculpt_params.setSculptTexture(sculpt_id, sculpt_type); + objectp->setParameterEntry(LLNetworkData::PARAMS_SCULPT, sculpt_params, TRUE); + } + else + { + LLSculptParams *sculpt_params = (LLSculptParams *)objectp->getParameterEntry(LLNetworkData::PARAMS_SCULPT); + if (sculpt_params) + { + objectp->setParameterEntryInUse(LLNetworkData::PARAMS_SCULPT, FALSE, TRUE); + } + } + + // volume params + // make sure updateVolume() won't affect flexible + if (mClipboardParams.has("volume_params")) + { + LLVolumeParams params; + params.fromLLSD(mClipboardParams["volume_params"]); + LLVOVolume *volobjp = (LLVOVolume *)objectp; + if (volobjp->isFlexible()) + { + if (params.getPathParams().getCurveType() == LL_PCODE_PATH_LINE) + { + params.getPathParams().setCurveType(LL_PCODE_PATH_FLEXIBLE); + } + } + else if (params.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE) + { + params.getPathParams().setCurveType(LL_PCODE_PATH_LINE); + } + + objectp->updateVolume(params); + } +} + +// Extended copy & paste buttons +bool get_vector_from_clipboard(LLVector3* value) { LLWString temp_string; LLView::getWindow()->pasteTextFromClipboard(temp_string); const std::string stringVec = wstring_to_utf8str(temp_string); - if(stringVec.empty() || value == NULL) return FALSE; + if (stringVec.empty() || !value) + { + return false; + } LLVector3 vec; - S32 count = sscanf( stringVec.c_str(), "<%f, %f, %f>", vec.mV + 0, vec.mV + 1, vec.mV + 2 ); - if( 3 == count ) + S32 count = sscanf(stringVec.c_str(), "<%f, %f, %f>", vec.mV + 0, vec.mV + 1, vec.mV + 2); + if (count == 3) { - value->setVec( vec ); - return TRUE; + value->setVec(vec); + return true; } - return FALSE; + return false; } -void LLPanelObject::onPastePosClip(const LLSD& data) + +void LLPanelObject::onPastePosClip() { - if(get_vector_from_clipboard(&mClipboardPos)) + if (get_vector_from_clipboard(&mClipboardPos)) { - mHasPosClipboard = TRUE; - onPastePos(data); + mHasClipboardPos = true; + onPastePos(); } else { - LL_INFOS() << "Couldn't get position vector from clipboard" << LL_ENDL; + LL_INFOS("FloaterTools") << "Couldn't get position vector from clipboard" << LL_ENDL; } } -void LLPanelObject::onPasteSizeClip(const LLSD& data) + +void LLPanelObject::onPasteSizeClip() { - if(get_vector_from_clipboard(&mClipboardSize)) + if (get_vector_from_clipboard(&mClipboardSize)) { - mHasSizeClipboard = TRUE; - onPasteSize(data); + mHasClipboardSize = true; + onPasteSize(); } else { - LL_INFOS() << "Couldn't get size vector from clipboard" << LL_ENDL; + LL_INFOS("FloaterTools") << "Couldn't get size vector from clipboard" << LL_ENDL; } } -void LLPanelObject::onPasteRotClip(const LLSD& data) + +void LLPanelObject::onPasteRotClip() { - if(get_vector_from_clipboard(&mClipboardRot)) + if (get_vector_from_clipboard(&mClipboardRot)) { - mHasRotClipboard = TRUE; - onPasteRot(data); + mHasClipboardRot = true; + onPasteRot(); } else { - LL_INFOS() << "Couldn't get rotation vector from clipboard" << LL_ENDL; + LL_INFOS("FloaterTools") << "Couldn't get rotation vector from clipboard" << LL_ENDL; } } - - -void LLPanelObject::onCopyParams(const LLSD& data) -{ - getVolumeParams(mClipboardVolumeParams); - mHasParamsClipboard = TRUE; - - LLViewerObject* objectp = mObject; - if (!objectp) - return; - - LLVOVolume *volobjp = NULL; - if ( objectp && (objectp->getPCode() == LL_PCODE_VOLUME)) - volobjp = (LLVOVolume *)objectp; - - mHasFlexiParam = FALSE; - if(volobjp && volobjp->isFlexible()) - { - LLFlexibleObjectData *attributes = (LLFlexibleObjectData *)objectp->getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE); - if (attributes) - { - mParamsClipboard["lod"] = attributes->getSimulateLOD(); - mParamsClipboard["gav"] = attributes->getGravity(); - mParamsClipboard["ten"] = attributes->getTension(); - mParamsClipboard["fri"] = attributes->getAirFriction(); - mParamsClipboard["sen"] = attributes->getWindSensitivity(); - LLVector3 force = attributes->getUserForce(); - mParamsClipboard["forx"] = force.mV[0]; - mParamsClipboard["fory"] = force.mV[1]; - mParamsClipboard["forz"] = force.mV[2]; - mHasFlexiParam = TRUE; - } - } - - if (objectp->getParameterEntryInUse(LLNetworkData::PARAMS_SCULPT)) - { - LLSculptParams *sculpt_params = (LLSculptParams *)objectp->getParameterEntry(LLNetworkData::PARAMS_SCULPT); - - LLUUID image_id = sculpt_params->getSculptTexture(); - BOOL allow_texture = FALSE; - if (gInventory.isObjectDescendentOf(image_id, gInventory.getLibraryRootFolderID()) - || image_id == LLUUID(gSavedSettings.getString( "DefaultObjectTexture" )) - || image_id == LLUUID(gSavedSettings.getString( "UIImgWhiteUUID" )) - || image_id == LLUUID(gSavedSettings.getString( "UIImgInvisibleUUID" )) - || image_id == LLUUID(SCULPT_DEFAULT_TEXTURE) - ) - allow_texture = TRUE; - else - { - LLUUID inventory_item_id; - LLViewerInventoryCategory::cat_array_t cats; - LLViewerInventoryItem::item_array_t items; - LLAssetIDMatches asset_id_matches(image_id); - gInventory.collectDescendentsIf(LLUUID::null, - cats, - items, - LLInventoryModel::INCLUDE_TRASH, - asset_id_matches); - - if (items.size()) - { - // search for copyable version first - for (S32 i = 0; i < items.size(); i++) - { - LLInventoryItem* itemp = items[i]; - LLPermissions item_permissions = itemp->getPermissions(); - if (item_permissions.allowCopyBy(gAgent.getID(), gAgent.getGroupID())) - { - inventory_item_id = itemp->getUUID(); - break; - } - } - } - if (inventory_item_id.notNull()) - { - LLInventoryItem* itemp = gInventory.getItem(inventory_item_id); - if (itemp) - { - LLPermissions perm = itemp->getPermissions(); - if ( (perm.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED ) - allow_texture = TRUE; - } - } - } - if (allow_texture) - mParamsClipboard["sculptid"] = image_id; - else - mParamsClipboard["sculptid"] = LLUUID(SCULPT_DEFAULT_TEXTURE); - - mParamsClipboard["sculpt_type"] = sculpt_params->getSculptType(); - mHasSculptParam = TRUE; - } - else - { - mHasSculptParam = FALSE; - } - - if (volobjp && volobjp->getIsLight()) - { - mParamsClipboard["Light Intensity"] = volobjp->getLightIntensity(); - mParamsClipboard["Light Radius"] = volobjp->getLightRadius(); - mParamsClipboard["Light Falloff"] = volobjp->getLightFalloff(); - LLColor3 color = volobjp->getLightSRGBColor(); - mParamsClipboard["r"] = color.mV[0]; - mParamsClipboard["g"] = color.mV[1]; - mParamsClipboard["b"] = color.mV[2]; - mHasLightParam = TRUE; - } - else - { - mHasLightParam = FALSE; - } - - mParamsClipboard["physics_shape"] = objectp->getPhysicsShapeType(); -} - -void LLPanelObject::onPasteParams(const LLSD& data) -{ - LLViewerObject* objectp = mObject; - if (!objectp) - return; - - if (mHasFlexiParam && (objectp->getPCode() == LL_PCODE_VOLUME)) - { - LLFlexibleObjectData *attributes = (LLFlexibleObjectData *)objectp->getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE); - if (attributes) - { - LLFlexibleObjectData new_attributes; - new_attributes = *attributes; - - new_attributes.setSimulateLOD(mParamsClipboard["lod"].asInteger()); - new_attributes.setGravity(mParamsClipboard["gav"].asReal()); - new_attributes.setTension(mParamsClipboard["ten"].asReal()); - new_attributes.setAirFriction(mParamsClipboard["fri"].asReal()); - new_attributes.setWindSensitivity(mParamsClipboard["sen"].asReal()); - F32 fx = (F32)mParamsClipboard["forx"].asReal(); - F32 fy = (F32)mParamsClipboard["fory"].asReal(); - F32 fz = (F32)mParamsClipboard["forz"].asReal(); - LLVector3 force(fx,fy,fz); - new_attributes.setUserForce(force); - objectp->setParameterEntry(LLNetworkData::PARAMS_FLEXIBLE, new_attributes, true); - } - } - - if (mHasSculptParam) - { - LLSculptParams sculpt_params; - - if (mParamsClipboard.has("sculptid")) - sculpt_params.setSculptTexture(mParamsClipboard["sculptid"].asUUID(), (U8)mParamsClipboard["sculpt_type"].asInteger()); - - objectp->setParameterEntry(LLNetworkData::PARAMS_SCULPT, sculpt_params, TRUE); - } - else - { - LLSculptParams *sculpt_params = (LLSculptParams *)objectp->getParameterEntry(LLNetworkData::PARAMS_SCULPT); - if (sculpt_params) - objectp->setParameterEntryInUse(LLNetworkData::PARAMS_SCULPT, FALSE, TRUE); - } - - LLVOVolume *volobjp = NULL; - if ( objectp && (objectp->getPCode() == LL_PCODE_VOLUME)) - volobjp = (LLVOVolume *)objectp; - - if (volobjp && mHasLightParam) - { - volobjp->setIsLight(TRUE); - volobjp->setLightIntensity((F32)mParamsClipboard["Light Intensity"].asReal()); - volobjp->setLightRadius((F32)mParamsClipboard["Light Radius"].asReal()); - volobjp->setLightFalloff((F32)mParamsClipboard["Light Falloff"].asReal()); - F32 r = (F32)mParamsClipboard["r"].asReal(); - F32 g = (F32)mParamsClipboard["g"].asReal(); - F32 b = (F32)mParamsClipboard["b"].asReal(); - volobjp->setLightSRGBColor(LLColor3(r,g,b)); - } - - if (mParamsClipboard.has("physics_shape")) - { - objectp->setPhysicsShapeType((U8)mParamsClipboard["physics_shape"].asInteger()); - } - - if(mHasParamsClipboard) - objectp->updateVolume(mClipboardVolumeParams); -} +// diff --git a/indra/newview/llpanelobject.h b/indra/newview/llpanelobject.h index e97d3277d7..69a5d70b11 100644 --- a/indra/newview/llpanelobject.h +++ b/indra/newview/llpanelobject.h @@ -37,6 +37,7 @@ class LLCheckBoxCtrl; class LLTextBox; class LLUICtrl; class LLButton; +class LLMenuButton; class LLViewerObject; class LLComboBox; class LLColorSwatchCtrl; @@ -70,17 +71,20 @@ public: // FIRE-21445 - Display specific LOD void onCommitLOD(); // - void onCopyPos( const LLSD& data); - void onPastePos( const LLSD& data); - void onPastePosClip( const LLSD& data); - void onCopySize( const LLSD& data); - void onPasteSize( const LLSD& data); - void onPasteSizeClip( const LLSD& data); - void onCopyRot( const LLSD& data); - void onPasteRot( const LLSD& data); - void onPasteRotClip( const LLSD& data); - void onCopyParams( const LLSD& data); - void onPasteParams( const LLSD& data); + + void onCopyPos(); + void onPastePos(); + void onCopySize(); + void onPasteSize(); + void onCopyRot(); + void onPasteRot(); + void onCopyParams(); + void onPasteParams(); + // Extended copy & paste buttons + void onPastePosClip(); + void onPasteSizeClip(); + void onPasteRotClip(); + // static void onCommitParametric(LLUICtrl* ctrl, void* userdata); @@ -91,6 +95,11 @@ public: BOOL onDropSculpt(LLInventoryItem* item); static void onCommitSculptType( LLUICtrl *ctrl, void* userdata); + // Extended copy & paste buttons + //void menuDoToSelected(const LLSD& userdata); + //bool menuEnableItem(const LLSD& userdata); + // + protected: void getState(); // FIRE-21445 + Mesh Info in object panel @@ -111,28 +120,15 @@ protected: void getVolumeParams(LLVolumeParams& volume_params); protected: - - LLVector3 mClipboardPos; - LLVector3 mClipboardSize; - LLVector3 mClipboardRot; - - BOOL mHasPosClipboard; - BOOL mHasSizeClipboard; - BOOL mHasRotClipboard; - - LLSD mParamsClipboard; - LLVolumeParams mClipboardVolumeParams; - BOOL mHasParamsClipboard; - BOOL mHasFlexiParam; - BOOL mHasSculptParam; - BOOL mHasLightParam; - S32 mComboMaterialItemCount; LLComboBox* mComboMaterial; // Per-object options LLComboBox* mComboBaseType; + + //LLMenuButton* mMenuClipboardParams; // Extended copy & paste buttons + LLComboBox* mComboLOD; LLTextBox* mLabelCut; @@ -181,36 +177,39 @@ protected: LLTextBox* mLabelRevolutions; LLSpinCtrl* mSpinRevolutions; + //LLMenuButton* mMenuClipboardPos; // Extended copy & paste buttons LLTextBox* mLabelPosition; LLSpinCtrl* mCtrlPosX; LLSpinCtrl* mCtrlPosY; LLSpinCtrl* mCtrlPosZ; + //LLMenuButton* mMenuClipboardSize; // Extended copy & paste buttons LLTextBox* mLabelSize; LLSpinCtrl* mCtrlScaleX; LLSpinCtrl* mCtrlScaleY; LLSpinCtrl* mCtrlScaleZ; BOOL mSizeChanged; + //LLMenuButton* mMenuClipboardRot; // Extended copy & paste buttons LLTextBox* mLabelRotation; LLSpinCtrl* mCtrlRotX; LLSpinCtrl* mCtrlRotY; LLSpinCtrl* mCtrlRotZ; - LLButton *mBtnCopyPos; - LLButton *mBtnPastePos; - LLButton *mBtnPastePosClip; - - LLButton *mBtnCopySize; - LLButton *mBtnPasteSize; - LLButton *mBtnPasteSizeClip; - - LLButton *mBtnCopyRot; - LLButton *mBtnPasteRot; - LLButton *mBtnPasteRotClip; + LLButton *mBtnCopyPos; + LLButton *mBtnPastePos; + LLButton *mBtnCopySize; + LLButton *mBtnPasteSize; + LLButton *mBtnCopyRot; + LLButton *mBtnPasteRot; + LLButton *mBtnCopyParams; + LLButton *mBtnPasteParams; - LLButton* mBtnCopyParams; - LLButton* mBtnPasteParams; + // Extended copy & paste buttons + LLButton *mBtnPastePosClip; + LLButton *mBtnPasteSizeClip; + LLButton *mBtnPasteRotClip; + // LLCheckBoxCtrl *mCheckLock; LLCheckBoxCtrl *mCheckPhysics; @@ -222,7 +221,7 @@ protected: LLComboBox *mCtrlSculptType; LLCheckBoxCtrl *mCtrlSculptMirror; LLCheckBoxCtrl *mCtrlSculptInvert; - + LLVector3 mCurEulerDegrees; // to avoid sending rotation when not changed BOOL mIsPhysical; // to avoid sending "physical" when not changed BOOL mIsTemporary; // to avoid sending "temporary" when not changed @@ -232,6 +231,15 @@ protected: LLUUID mSculptTextureRevert; // so we can revert the sculpt texture on cancel U8 mSculptTypeRevert; // so we can revert the sculpt type on cancel + LLVector3 mClipboardPos; + LLVector3 mClipboardSize; + LLVector3 mClipboardRot; + LLSD mClipboardParams; + + bool mHasClipboardPos; + bool mHasClipboardSize; + bool mHasClipboardRot; + LLPointer mObject; LLPointer mRootObject; }; diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index d5afe9a853..587b2c8691 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -710,7 +710,18 @@ const std::string& LLTaskCategoryBridge::getDisplayName() const if (cat) { // FIRE-24142 - Show number of elements in object inventory - //mDisplayName.assign(cat->getName()); + //std::string name = cat->getName(); + //if (mChildren.size() > 0) + //{ + // // Add item count + // // Normally we would be using getLabelSuffix for this + // // but object's inventory just uses displaynames + // LLStringUtil::format_map_t args; + // args["[ITEMS_COUNT]"] = llformat("%d", mChildren.size()); + + // name.append(" " + LLTrans::getString("InventoryItemsCount", args)); + //} + //mDisplayName.assign(name); if (cat->getParentUUID().isNull() && cat->getName() == "Contents") { LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID()); @@ -1706,6 +1717,8 @@ void LLPanelObjectInventory::createFolderViews(LLInventoryObject* inventory_root { createViewsForCategory(&contents, inventory_root, new_folder); } + // Refresh for label to add item count + new_folder->refresh(); } } diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp index 2c2e5b7777..8559a77006 100644 --- a/indra/newview/llpanelvolume.cpp +++ b/indra/newview/llpanelvolume.cpp @@ -51,6 +51,7 @@ #include "llfocusmgr.h" #include "llmanipscale.h" #include "llinventorymodel.h" +//#include "llmenubutton.h" // Extended copy & paste buttons #include "llpreviewscript.h" #include "llresmgr.h" #include "llselectmgr.h" @@ -168,6 +169,15 @@ BOOL LLPanelVolume::postBuild() mSpinPhysicsRestitution->setCommitCallback(boost::bind(&LLPanelVolume::sendPhysicsRestitution, this, _1, mSpinPhysicsRestitution)); } + // Extended copy & paste buttons + //mMenuClipboardFeatures = getChild("clipboard_features_params_btn"); + //mMenuClipboardLight = getChild("clipboard_light_params_btn"); + mBtnCopyFeatures = getChild("copy_features_btn"); + mBtnCopyFeatures->setCommitCallback(boost::bind(&LLPanelVolume::onFSCopyFeatures, this)); + mBtnPasteFeatures = getChild("paste_features_btn"); + mBtnPasteFeatures->setCommitCallback(boost::bind(&LLPanelVolume::onFSPasteFeatures, this)); + // + std::map material_name_map; material_name_map["Stone"]= LLTrans::getString("Stone"); material_name_map["Metal"]= LLTrans::getString("Metal"); @@ -409,7 +419,6 @@ void LLPanelVolume::getState( ) gAgentAvatarp->updateMeshVisibility(); } } - // Flexible properties BOOL is_flexible = volobjp && volobjp->isFlexible(); @@ -564,6 +573,13 @@ void LLPanelVolume::getState( ) mObject = objectp; mRootObject = root_objectp; + + // Extended copy & paste buttons + //mMenuClipboardFeatures->setEnabled(editable && single_volume && volobjp); // Note: physics doesn't need to be limited by single volume + //mMenuClipboardLight->setEnabled(editable && single_volume && volobjp); + mBtnCopyFeatures->setEnabled(editable && single_volume && volobjp); + mBtnPasteFeatures->setEnabled(editable && single_volume && volobjp && !mClipboardParams.emptyMap() && (mClipboardParams.has("features") || mClipboardParams.has("light"))); + // } // static @@ -667,6 +683,13 @@ void LLPanelVolume::clearCtrls() getChildView("PhysicsViewToggle")->setEnabled(true); // mComboMaterial->setEnabled( FALSE ); + + // Extended copy & paste buttons + //mMenuClipboardFeatures->setEnabled(false); + //mMenuClipboardLight->setEnabled(false); + mBtnCopyFeatures->setEnabled(FALSE); + mBtnPasteFeatures->setEnabled(FALSE); + // } // @@ -834,6 +857,304 @@ void LLPanelVolume::onLightSelectTexture(const LLSD& data) } } +void LLPanelVolume::onCopyFeatures() +{ + LLViewerObject* objectp = mObject; + if (!objectp) + { + return; + } + + LLSD clipboard; + + LLVOVolume *volobjp = NULL; + if (objectp && (objectp->getPCode() == LL_PCODE_VOLUME)) + { + volobjp = (LLVOVolume *)objectp; + } + + // Flexi Prim + if (volobjp && volobjp->isFlexible()) + { + LLFlexibleObjectData *attributes = (LLFlexibleObjectData *)objectp->getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE); + if (attributes) + { + clipboard["flex"]["lod"] = attributes->getSimulateLOD(); + clipboard["flex"]["gav"] = attributes->getGravity(); + clipboard["flex"]["ten"] = attributes->getTension(); + clipboard["flex"]["fri"] = attributes->getAirFriction(); + clipboard["flex"]["sen"] = attributes->getWindSensitivity(); + LLVector3 force = attributes->getUserForce(); + clipboard["flex"]["forx"] = force.mV[0]; + clipboard["flex"]["fory"] = force.mV[1]; + clipboard["flex"]["forz"] = force.mV[2]; + } + } + + // Physics + { + clipboard["physics"]["shape"] = objectp->getPhysicsShapeType(); + clipboard["physics"]["gravity"] = objectp->getPhysicsGravity(); + clipboard["physics"]["friction"] = objectp->getPhysicsFriction(); + clipboard["physics"]["density"] = objectp->getPhysicsDensity(); + clipboard["physics"]["restitution"] = objectp->getPhysicsRestitution(); + + U8 material_code = 0; + struct f : public LLSelectedTEGetFunctor + { + U8 get(LLViewerObject* object, S32 te) + { + return object->getMaterial(); + } + } func; + bool material_same = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&func, material_code); + // This should always be true since material should be per object. + if (material_same) + { + clipboard["physics"]["material"] = material_code; + } + } + + mClipboardParams["features"] = clipboard; +} + +void LLPanelVolume::onPasteFeatures() +{ + LLViewerObject* objectp = mObject; + if (!objectp && mClipboardParams.has("features")) + { + return; + } + + LLSD &clipboard = mClipboardParams["features"]; + + LLVOVolume *volobjp = NULL; + if (objectp && (objectp->getPCode() == LL_PCODE_VOLUME)) + { + volobjp = (LLVOVolume *)objectp; + } + + // Physics + bool is_root = objectp->isRoot(); + + // Not sure if phantom should go under physics, but doesn't fit elsewhere + BOOL is_phantom = clipboard["is_phantom"].asBoolean() && is_root; + LLSelectMgr::getInstance()->selectionUpdatePhantom(is_phantom); + + BOOL is_physical = clipboard["is_physical"].asBoolean() && is_root; + LLSelectMgr::getInstance()->selectionUpdatePhysics(is_physical); + + if (clipboard.has("physics")) + { + objectp->setPhysicsShapeType((U8)clipboard["physics"]["shape"].asInteger()); + U8 cur_material = objectp->getMaterial(); + U8 material = (U8)clipboard["physics"]["material"].asInteger() | (cur_material & ~LL_MCODE_MASK); + + objectp->setMaterial(material); + objectp->sendMaterialUpdate(); + objectp->setPhysicsGravity(clipboard["physics"]["gravity"].asReal()); + objectp->setPhysicsFriction(clipboard["physics"]["friction"].asReal()); + objectp->setPhysicsDensity(clipboard["physics"]["density"].asReal()); + objectp->setPhysicsRestitution(clipboard["physics"]["restitution"].asReal()); + objectp->updateFlags(TRUE); + } + + // Flexible + bool is_flexible = clipboard.has("flex"); + if (is_flexible && volobjp->canBeFlexible()) + { + LLVOVolume *volobjp = (LLVOVolume *)objectp; + BOOL update_shape = FALSE; + + // do before setParameterEntry or it will think that it is already flexi + update_shape = volobjp->setIsFlexible(is_flexible); + + if (objectp->getClickAction() == CLICK_ACTION_SIT) + { + objectp->setClickAction(CLICK_ACTION_NONE); + } + + LLFlexibleObjectData *attributes = (LLFlexibleObjectData *)objectp->getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE); + if (attributes) + { + LLFlexibleObjectData new_attributes; + new_attributes = *attributes; + new_attributes.setSimulateLOD(clipboard["flex"]["lod"].asInteger()); + new_attributes.setGravity(clipboard["flex"]["gav"].asReal()); + new_attributes.setTension(clipboard["flex"]["ten"].asReal()); + new_attributes.setAirFriction(clipboard["flex"]["fri"].asReal()); + new_attributes.setWindSensitivity(clipboard["flex"]["sen"].asReal()); + F32 fx = (F32)clipboard["flex"]["forx"].asReal(); + F32 fy = (F32)clipboard["flex"]["fory"].asReal(); + F32 fz = (F32)clipboard["flex"]["forz"].asReal(); + LLVector3 force(fx, fy, fz); + new_attributes.setUserForce(force); + objectp->setParameterEntry(LLNetworkData::PARAMS_FLEXIBLE, new_attributes, true); + } + + if (update_shape) + { + mObject->sendShapeUpdate(); + LLSelectMgr::getInstance()->selectionUpdatePhantom(volobjp->flagPhantom()); + } + } + else + { + LLVOVolume *volobjp = (LLVOVolume *)objectp; + if (volobjp->setIsFlexible(false)) + { + mObject->sendShapeUpdate(); + LLSelectMgr::getInstance()->selectionUpdatePhantom(volobjp->flagPhantom()); + } + } +} + +void LLPanelVolume::onCopyLight() +{ + LLViewerObject* objectp = mObject; + if (!objectp) + { + return; + } + + LLSD clipboard; + + LLVOVolume *volobjp = NULL; + if (objectp && (objectp->getPCode() == LL_PCODE_VOLUME)) + { + volobjp = (LLVOVolume *)objectp; + } + + // Light Source + if (volobjp && volobjp->getIsLight()) + { + clipboard["light"]["intensity"] = volobjp->getLightIntensity(); + clipboard["light"]["radius"] = volobjp->getLightRadius(); + clipboard["light"]["falloff"] = volobjp->getLightFalloff(); + LLColor3 color = volobjp->getLightSRGBColor(); + clipboard["light"]["r"] = color.mV[0]; + clipboard["light"]["g"] = color.mV[1]; + clipboard["light"]["b"] = color.mV[2]; + + // Spotlight + if (volobjp->isLightSpotlight()) + { + LLUUID id = volobjp->getLightTextureID(); + if (id.notNull() && get_can_copy_texture(id)) + { + clipboard["spot"]["id"] = id; + LLVector3 spot_params = volobjp->getSpotLightParams(); + clipboard["spot"]["fov"] = spot_params.mV[0]; + clipboard["spot"]["focus"] = spot_params.mV[1]; + clipboard["spot"]["ambiance"] = spot_params.mV[2]; + } + } + } + + mClipboardParams["light"] = clipboard; +} + +void LLPanelVolume::onPasteLight() +{ + LLViewerObject* objectp = mObject; + if (!objectp && mClipboardParams.has("light")) + { + return; + } + + LLSD &clipboard = mClipboardParams["light"]; + + LLVOVolume *volobjp = NULL; + if (objectp && (objectp->getPCode() == LL_PCODE_VOLUME)) + { + volobjp = (LLVOVolume *)objectp; + } + + // Light Source + if (volobjp) + { + if (clipboard.has("light")) + { + volobjp->setIsLight(TRUE); + volobjp->setLightIntensity((F32)clipboard["light"]["intensity"].asReal()); + volobjp->setLightRadius((F32)clipboard["light"]["radius"].asReal()); + volobjp->setLightFalloff((F32)clipboard["light"]["falloff"].asReal()); + F32 r = (F32)clipboard["light"]["r"].asReal(); + F32 g = (F32)clipboard["light"]["g"].asReal(); + F32 b = (F32)clipboard["light"]["b"].asReal(); + volobjp->setLightSRGBColor(LLColor3(r, g, b)); + } + else + { + volobjp->setIsLight(FALSE); + } + + if (clipboard.has("spot")) + { + volobjp->setLightTextureID(clipboard["spot"]["id"].asUUID()); + LLVector3 spot_params; + spot_params.mV[0] = (F32)clipboard["spot"]["fov"].asReal(); + spot_params.mV[1] = (F32)clipboard["spot"]["focus"].asReal(); + spot_params.mV[2] = (F32)clipboard["spot"]["ambiance"].asReal(); + volobjp->setSpotLightParams(spot_params); + } + } +} + +// Extended copy & paste buttons +//void LLPanelVolume::menuDoToSelected(const LLSD& userdata) +//{ +// std::string command = userdata.asString(); +// +// // paste +// if (command == "features_paste") +// { +// onPasteFeatures(); +// } +// else if (command == "light_paste") +// { +// onPasteLight(); +// } +// // copy +// else if (command == "features_copy") +// { +// onCopyFeatures(); +// } +// else if (command == "light_copy") +// { +// onCopyLight(); +// } +//} +// +//bool LLPanelVolume::menuEnableItem(const LLSD& userdata) +//{ +// std::string command = userdata.asString(); +// +// // paste options +// if (command == "features_paste") +// { +// return mClipboardParams.has("features"); +// } +// else if (command == "light_paste") +// { +// return mClipboardParams.has("light"); +// } +// return false; +//} +void LLPanelVolume::onFSCopyFeatures() +{ + onCopyFeatures(); + onCopyLight(); + mBtnPasteFeatures->setEnabled(!mClipboardParams.emptyMap() && (mClipboardParams.has("features") || mClipboardParams.has("light"))); +} + +void LLPanelVolume::onFSPasteFeatures() +{ + onPasteFeatures(); + onPasteLight(); +} +// + // static void LLPanelVolume::onCommitMaterial( LLUICtrl* ctrl, void* userdata ) { diff --git a/indra/newview/llpanelvolume.h b/indra/newview/llpanelvolume.h index 6e49ccb742..3e8792e683 100644 --- a/indra/newview/llpanelvolume.h +++ b/indra/newview/llpanelvolume.h @@ -37,6 +37,7 @@ class LLCheckBoxCtrl; class LLTextBox; class LLUICtrl; class LLButton; +//class LLMenuButton; // Extended copy & paste buttons class LLViewerObject; class LLComboBox; class LLColorSwatchCtrl; @@ -74,8 +75,19 @@ public: void onLightCancelTexture(const LLSD& data); void onLightSelectTexture(const LLSD& data); - static void setLightTextureID(const LLUUID &asset_id, const LLUUID &item_id, LLVOVolume* volobjp); + void onCopyFeatures(); + void onPasteFeatures(); + void onCopyLight(); + void onPasteLight(); + + // Extended copy & paste buttons + //void menuDoToSelected(const LLSD& userdata); + //bool menuEnableItem(const LLSD& userdata); + void onFSCopyFeatures(); + void onFSPasteFeatures(); + // + static void setLightTextureID(const LLUUID &asset_id, const LLUUID &item_id, LLVOVolume* volobjp); protected: void getState(); @@ -123,6 +135,15 @@ protected: LLSpinCtrl* mSpinPhysicsFriction; LLSpinCtrl* mSpinPhysicsDensity; LLSpinCtrl* mSpinPhysicsRestitution; + + // Extended copy & paste buttons + //LLMenuButton* mMenuClipboardFeatures; + //LLMenuButton* mMenuClipboardLight; + LLButton* mBtnCopyFeatures; + LLButton* mBtnPasteFeatures; + // + + LLSD mClipboardParams; }; #endif diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 0e6d58b36b..ec90fb88cc 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -44,10 +44,6 @@ # include "llaudioengine_fmodstudio.h" #endif -#ifdef LL_SDL2 -#include "llaudioengine_sdl2.h" -#endif - #ifdef LL_OPENAL #include "llaudioengine_openal.h" #endif @@ -1008,11 +1004,6 @@ bool idle_startup() } #endif -#ifdef LL_SDL2 - if( !gAudiop ) - gAudiop = new LLAudioEngineSDL2(); -#endif - #ifdef LL_OPENAL #if !LL_WINDOWS // if (NULL == getenv("LL_BAD_OPENAL_DRIVER")) diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 2c377e4f3f..f73410499d 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -82,6 +82,67 @@ static const S32 LOCAL_TRACKING_ID_COLUMN = 1; //static const char WHITE_IMAGE_NAME[] = "Blank Texture"; //static const char NO_IMAGE_NAME[] = "None"; + + +//static +bool get_is_predefined_texture(LLUUID asset_id) +{ + if (asset_id == LLUUID(gSavedSettings.getString("DefaultObjectTexture")) + || asset_id == LLUUID(gSavedSettings.getString("UIImgWhiteUUID")) + || asset_id == LLUUID(gSavedSettings.getString("UIImgInvisibleUUID")) + || asset_id == LLUUID(SCULPT_DEFAULT_TEXTURE)) + { + return true; + } + return false; +} + +LLUUID get_copy_free_item_by_asset_id(LLUUID asset_id, bool no_trans_perm) +{ + LLViewerInventoryCategory::cat_array_t cats; + LLViewerInventoryItem::item_array_t items; + LLAssetIDMatches asset_id_matches(asset_id); + gInventory.collectDescendentsIf(LLUUID::null, + cats, + items, + LLInventoryModel::INCLUDE_TRASH, + asset_id_matches); + + LLUUID res; + if (items.size()) + { + for (S32 i = 0; i < items.size(); i++) + { + LLViewerInventoryItem* itemp = items[i]; + if (itemp) + { + LLPermissions item_permissions = itemp->getPermissions(); + if (item_permissions.allowOperationBy(PERM_COPY, + gAgent.getID(), + gAgent.getGroupID())) + { + bool allow_trans = item_permissions.allowOperationBy(PERM_TRANSFER, gAgent.getID(), gAgent.getGroupID()); + if (allow_trans != no_trans_perm) + { + return itemp->getUUID(); + } + res = itemp->getUUID(); + } + } + } + } + return res; +} + +bool get_can_copy_texture(LLUUID asset_id) +{ + // User is allowed to copy a texture if: + // library asset or default texture, + // or copy perm asset exists in user's inventory + + return get_is_predefined_texture(asset_id) || get_copy_free_item_by_asset_id(asset_id).notNull(); +} + LLFloaterTexturePicker::LLFloaterTexturePicker( LLView* owner, LLUUID image_asset_id, diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h index 4266a80975..e3a9b5c1e0 100644 --- a/indra/newview/lltexturectrl.h +++ b/indra/newview/lltexturectrl.h @@ -54,6 +54,16 @@ class LLViewerFetchedTexture; typedef boost::function drag_n_drop_callback; typedef boost::function texture_selected_callback; +// Helper functions for UI that work with picker +bool get_is_predefined_texture(LLUUID asset_id); + +// texture picker works by asset ids since objects normaly do +// not retain inventory ids as result these functions are looking +// for textures in inventory by asset ids +// This search can be performance unfriendly and doesn't warranty +// that the texture is original source of asset +LLUUID get_copy_free_item_by_asset_id(LLUUID image_id, bool no_trans_perm = false); +bool get_can_copy_texture(LLUUID image_id); ////////////////////////////////////////////////////////////////////////////////////////// // LLTextureCtrl diff --git a/indra/newview/lltoastalertpanel.cpp b/indra/newview/lltoastalertpanel.cpp index 457226d2f2..922ea9b32e 100644 --- a/indra/newview/lltoastalertpanel.cpp +++ b/indra/newview/lltoastalertpanel.cpp @@ -297,7 +297,7 @@ LLToastAlertPanel::LLToastAlertPanel( LLNotificationPtr notification, bool modal mLineEditor->setText(edit_text_contents); std::string notif_name = mNotification->getName(); - if (("SaveOutfitAs" == notif_name) || ("SaveSettingAs" == notif_name) || ("CreateLandmarkFolder" == notif_name)) + if (("SaveOutfitAs" == notif_name) || ("SaveSettingAs" == notif_name) || ("CreateLandmarkFolder" == notif_name) || ("CreateSubfolder" == notif_name)) { mLineEditor->setPrevalidate(&LLTextValidate::validateASCII); } diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 77d7c9bbca..61f03dd631 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -1109,7 +1109,8 @@ void LLToolDragAndDrop::dropTextureOneFace(LLViewerObject* hit_obj, S32 hit_face, LLInventoryItem* item, LLToolDragAndDrop::ESource source, - const LLUUID& src_id) + const LLUUID& src_id, + S32 tex_channel) { if (hit_face == -1) return; if (!item) @@ -1133,7 +1134,8 @@ void LLToolDragAndDrop::dropTextureOneFace(LLViewerObject* hit_obj, if (gFloaterTools->getVisible() && panel_face) { - switch (LLSelectMgr::getInstance()->getTextureChannel()) + tex_channel = (tex_channel > -1) ? tex_channel : LLSelectMgr::getInstance()->getTextureChannel(); + switch (tex_channel) { case 0: diff --git a/indra/newview/lltooldraganddrop.h b/indra/newview/lltooldraganddrop.h index 24a712029c..4537d73332 100644 --- a/indra/newview/lltooldraganddrop.h +++ b/indra/newview/lltooldraganddrop.h @@ -244,7 +244,8 @@ public: static void dropTextureOneFace(LLViewerObject* hit_obj, S32 hit_face, LLInventoryItem* item, ESource source, - const LLUUID& src_id); + const LLUUID& src_id, + S32 tex_channel = -1); static void dropTextureAllFaces(LLViewerObject* hit_obj, LLInventoryItem* item, ESource source, diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 3634da9a60..0f6ccbe91d 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -3374,6 +3374,39 @@ void handle_object_touch() send_ObjectDeGrab_message(object, pick); } +void handle_object_show_original() +{ + LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + if (!object) + { + return; + } + + LLViewerObject *parent = (LLViewerObject*)object->getParent(); + while (parent) + { + if(parent->isAvatar()) + { + break; + } + object = parent; + parent = (LLViewerObject*)parent->getParent(); + } + + if (!object || object->isAvatar()) + { + return; + } + + show_item_original(object->getAttachmentItemID()); +} + +// Disable if prevented by RLVa +bool enable_object_show_original() +{ + return !RlvActions::hasBehaviour(RLV_BHVR_SHOWINV); +} +// static void init_default_item_label(const std::string& item_name) { @@ -12309,6 +12342,7 @@ void initialize_menus() // Object pie menu view_listener_t::addMenu(new LLObjectBuild(), "Object.Build"); commit.add("Object.Touch", boost::bind(&handle_object_touch)); + commit.add("Object.ShowOriginal", boost::bind(&handle_object_show_original)); commit.add("Object.SitOrStand", boost::bind(&handle_object_sit_or_stand)); commit.add("Object.Delete", boost::bind(&handle_object_delete)); view_listener_t::addMenu(new LLObjectAttachToAvatar(true), "Object.AttachToAvatar"); @@ -12350,6 +12384,8 @@ void initialize_menus() enable.add("Object.EnableBuy", boost::bind(&enable_buy_object)); commit.add("Object.ZoomIn", boost::bind(&handle_look_at_selection, "zoom")); enable.add("Object.EnableScriptInfo", boost::bind(&enable_script_info)); // + enable.add("Object.EnableShowOriginal", boost::bind(&enable_object_show_original)); // Disable if prevented by RLVa + // Attachment pie menu enable.add("Attachment.Label", boost::bind(&onEnableAttachmentLabel, _1, _2)); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index ae89760523..4c13dc7fb6 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -109,7 +109,6 @@ #include "llfilepicker.h" #include "llfirstuse.h" #include "llfloater.h" -#include "llfloaterbuildoptions.h" #include "llfloaterbuyland.h" #include "llfloatercamera.h" #include "llfloaterland.h" diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index a0ff720680..925fe3a3c0 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -6364,7 +6364,7 @@ void LLVivoxVoiceClient::clearSessionHandle(const sessionStatePtr_t &session) { if (session) { - if (session->mHandle.empty()) + if (!session->mHandle.empty()) { sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle); if (iter != mSessionsByHandle.end()) diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index cfc8b57bd0..d7f6ecd737 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -4157,7 +4157,7 @@ void LLPipeline::postSort(LLCamera& camera) } LL_PUSH_CALLSTACKS(); // If managing your telehub, draw beacons at telehub and currently selected spawnpoint. - if (LLFloaterTelehub::renderBeacons()) + if (LLFloaterTelehub::renderBeacons() && !sShadowRender) { LLFloaterTelehub::addBeacons(); } diff --git a/indra/newview/skins/default/textures/icons/ClipboardMenu_Disabled.png b/indra/newview/skins/default/textures/icons/ClipboardMenu_Disabled.png new file mode 100644 index 0000000000..9a81c5f94b Binary files /dev/null and b/indra/newview/skins/default/textures/icons/ClipboardMenu_Disabled.png differ diff --git a/indra/newview/skins/default/textures/icons/ClipboardMenu_Off.png b/indra/newview/skins/default/textures/icons/ClipboardMenu_Off.png new file mode 100644 index 0000000000..88012cf8d1 Binary files /dev/null and b/indra/newview/skins/default/textures/icons/ClipboardMenu_Off.png differ diff --git a/indra/newview/skins/default/textures/icons/ClipboardMenu_Press.png b/indra/newview/skins/default/textures/icons/ClipboardMenu_Press.png new file mode 100644 index 0000000000..ab02e7d42d Binary files /dev/null and b/indra/newview/skins/default/textures/icons/ClipboardMenu_Press.png differ diff --git a/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Disabled.png b/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Disabled.png new file mode 100644 index 0000000000..63b4bd2127 Binary files /dev/null and b/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Disabled.png differ diff --git a/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Off.png b/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Off.png new file mode 100644 index 0000000000..4200182b0c Binary files /dev/null and b/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Off.png differ diff --git a/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Press.png b/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Press.png new file mode 100644 index 0000000000..e12887f489 Binary files /dev/null and b/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Press.png differ diff --git a/indra/newview/skins/default/textures/icons/Paste.png b/indra/newview/skins/default/textures/icons/Paste.png new file mode 100644 index 0000000000..10211df427 Binary files /dev/null and b/indra/newview/skins/default/textures/icons/Paste.png differ diff --git a/indra/newview/skins/default/textures/map_ui_collapse_icon.png b/indra/newview/skins/default/textures/map_ui_collapse_icon.png new file mode 100644 index 0000000000..e4de49d4af Binary files /dev/null and b/indra/newview/skins/default/textures/map_ui_collapse_icon.png differ diff --git a/indra/newview/skins/default/textures/map_ui_expand_icon.png b/indra/newview/skins/default/textures/map_ui_expand_icon.png new file mode 100644 index 0000000000..08734b4cc0 Binary files /dev/null and b/indra/newview/skins/default/textures/map_ui_expand_icon.png differ diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 9df7021212..d7082d8bb3 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -208,6 +208,8 @@ with the same filename but different name + + @@ -520,6 +522,13 @@ with the same filename but different name + + + + + + + @@ -989,6 +998,8 @@ with the same filename but different name + + diff --git a/indra/newview/skins/default/xui/de/floater_tools.xml b/indra/newview/skins/default/xui/de/floater_tools.xml index 301c752ceb..2987fcd3af 100644 --- a/indra/newview/skins/default/xui/de/floater_tools.xml +++ b/indra/newview/skins/default/xui/de/floater_tools.xml @@ -91,10 +91,10 @@ þ: [COUNT] - - - - + + + + @@ -142,8 +142,11 @@ Nichts ausgewählt. - - [CAPACITY_STRING] [secondlife:///app/openfloater/object_weights Mehr Infos] + + Flächen ausgewählt: [FACES_STRING] + + + [secondlife:///app/openfloater/object_weights Mehr Infos] Physikgewicht [PHYS_WEIGHT], Darstellungskosten [DISP_WEIGHT]. @@ -282,15 +285,15 @@ - + Position einfügen [VALUE] - + Größe einfügen [VALUE] - + Rotation einfügen [VALUE] @@ -302,34 +305,31 @@ Position (Meter) - - + + + width="149"> Mesh Information: @@ -2646,7 +2640,7 @@ Lowest: value="LL" visible="false" width="30" /> - [APPNAME] Viewer default ([FACTOR]) + [APP_NAME] Viewer default ([FACTOR]) + + Physics Shape Type: @@ -3053,7 +3075,7 @@ Low ↔ Lwst layout="topleft" name="material" top_pad="5" - width="150" + width="134" left="144"> - + name="Show original"> + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_copy_paste_features.xml b/indra/newview/skins/default/xui/en/menu_copy_paste_features.xml new file mode 100644 index 0000000000..4823d74a26 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_copy_paste_features.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_copy_paste_light.xml b/indra/newview/skins/default/xui/en/menu_copy_paste_light.xml new file mode 100644 index 0000000000..5de23dfee3 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_copy_paste_light.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_copy_paste_object.xml b/indra/newview/skins/default/xui/en/menu_copy_paste_object.xml new file mode 100644 index 0000000000..bdc4537a9d --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_copy_paste_object.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_copy_paste_pos.xml b/indra/newview/skins/default/xui/en/menu_copy_paste_pos.xml new file mode 100644 index 0000000000..3ea95b281f --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_copy_paste_pos.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_copy_paste_rot.xml b/indra/newview/skins/default/xui/en/menu_copy_paste_rot.xml new file mode 100644 index 0000000000..06ce80f897 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_copy_paste_rot.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_copy_paste_size.xml b/indra/newview/skins/default/xui/en/menu_copy_paste_size.xml new file mode 100644 index 0000000000..7082a0e65b --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_copy_paste_size.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_copy_paste_texture.xml b/indra/newview/skins/default/xui/en/menu_copy_paste_texture.xml new file mode 100644 index 0000000000..f358affc23 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_copy_paste_texture.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml index f0c3bd5e71..fa43182ce8 100644 --- a/indra/newview/skins/default/xui/en/menu_inventory.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory.xml @@ -973,6 +973,25 @@ function="Inventory.DoToSelected" parameter="apply_settings_parcel" /> + + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_pie_attachment_self.xml b/indra/newview/skins/default/xui/en/menu_pie_attachment_self.xml index 2373f6c348..cbe9cf946d 100644 --- a/indra/newview/skins/default/xui/en/menu_pie_attachment_self.xml +++ b/indra/newview/skins/default/xui/en/menu_pie_attachment_self.xml @@ -95,7 +95,14 @@ function="Object.EnableExport" /> - + + + + diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 4a151cdbee..81b8abd24a 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -9762,6 +9762,29 @@ We cannot display a preview of this texture because it is no-copy and/or no-tran yestext="OK"/> + +Paste failed. [REASON] + + + + + You applied a texture with limited permissions, object will inherit permissions from texture. + + + + + + + Name the new folder: + confirm +
+ + [DESC] + +
+ + top="65" + width="90"> - + + + width="149"> - Mesh Information: + Mesh Information:
@@ -2655,7 +2649,7 @@ Lowest: name="LOD_swap_fs_default" top_delta="0" visible="false" - width="30"> + width="30" > [APP_NAME_ABBR] [FACTOR] @@ -2683,7 +2677,7 @@ Lowest: width="65" > High ↔ Med Med ↔ Low -Low ↔ Lwst +Low ↔ Lwst - + This table shows the LOD change boundaries in metres from the camera. - - + + + - Physics Shape Type: @@ -3036,25 +3057,25 @@ Low ↔ Lwst tool_tip="Choose the physics shape type" width="108"/>