diff options
73 files changed, 3191 insertions, 303 deletions
| diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt index 552e820127..4617309606 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 @@ -109,6 +111,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 5de1fe5a8d..3c90aa2963 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 @@ -4953,6 +4954,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<unsigned int> 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 a984aa127e..3ccaed47f1 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -903,6 +903,10 @@ public:  		typedef std::map<LLVector3, std::vector<VertexMapData>, 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,25 +43,219 @@ 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<unsigned short>(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<unsigned int>(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<unsigned short>(destination, +        indices, +        index_count, +        vertex_count, +        streams, +        index); +} + +void LLMeshOptimizer::optimizeVertexCacheU32(U32 * destination, const U32 * indices, U64 index_count, U64 vertex_count) +{ +    meshopt_optimizeVertexCache<unsigned int>(destination, indices, index_count, vertex_count); +} + +void LLMeshOptimizer::optimizeVertexCacheU16(U16 * destination, const U16 * indices, U64 index_count, U64 vertex_count) +{ +    meshopt_optimizeVertexCache<unsigned short>(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<unsigned int>(destination_indices, indices, index_count, remap); +} + +void LLMeshOptimizer::remapIndexBufferU16(U16 * destination_indices, +    const U16 * indices, +    U64 index_count, +    const unsigned int* remap) +{ +    meshopt_remapIndexBuffer<unsigned short>(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,      U64 index_count, 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 e89690438e..68654486a4 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -2577,7 +2577,7 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& mo  		if (!mNoOptimize)  		{ -			ret->optimizeVolumeFaces(); +			ret->remapVolumeFaces();  		}  		volume_faces = remainder.size(); diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index 74f5e3f0c8..1f5e7a9b4b 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -107,6 +107,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 3881b1338c..354ceb26b7 100644 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -184,6 +184,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/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 0e59fdf519..8028f397f3 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -102,6 +102,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"), @@ -165,6 +166,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), @@ -475,6 +477,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 ); @@ -489,8 +495,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)) @@ -1195,6 +1199,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 572d36996c..ccd31e90c0 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -124,6 +124,7 @@ public:  		Optional<bool>			is_toggle,  								scale_image,  								commit_on_return, +								commit_on_capture_lost,  								display_pressed_state;  		Optional<F32>				hover_glow_amount; @@ -374,6 +375,7 @@ protected:  	F32							mCurGlowStrength;  	bool						mCommitOnReturn; +    bool						mCommitOnCaptureLost;  	bool						mFadeWhenDisabled;  	bool						mForcePressedState;  	bool						mDisplayPressedState; diff --git a/indra/llui/llprogressbar.cpp b/indra/llui/llprogressbar.cpp index 209796565c..cf57b1fe76 100644 --- a/indra/llui/llprogressbar.cpp +++ b/indra/llui/llprogressbar.cpp @@ -69,16 +69,22 @@ void LLProgressBar::draw()  	static LLTimer timer;  	F32 alpha = getDrawContext().mAlpha; -	LLColor4 image_bar_color = mColorBackground.get(); -	image_bar_color.setAlpha(alpha); -	mImageBar->draw(getLocalRect(), image_bar_color); +    if (mImageBar) // optional according to parameters +    { +        LLColor4 image_bar_color = mColorBackground.get(); +        image_bar_color.setAlpha(alpha); +        mImageBar->draw(getLocalRect(), image_bar_color); +    } -	alpha *= 0.5f + 0.5f*0.5f*(1.f + (F32)sin(3.f*timer.getElapsedTimeF32())); -	LLColor4 bar_color = mColorBar.get(); -	bar_color.mV[VALPHA] *= alpha; // modulate alpha -	LLRect progress_rect = getLocalRect(); -	progress_rect.mRight = ll_round(getRect().getWidth() * (mPercentDone / 100.f)); -	mImageFill->draw(progress_rect, bar_color); +    if (mImageFill) +    { +        alpha *= 0.5f + 0.5f*0.5f*(1.f + (F32)sin(3.f*timer.getElapsedTimeF32())); +        LLColor4 bar_color = mColorBar.get(); +        bar_color.mV[VALPHA] *= alpha; // modulate alpha +        LLRect progress_rect = getLocalRect(); +        progress_rect.mRight = ll_round(getRect().getWidth() * (mPercentDone / 100.f)); +        mImageFill->draw(progress_rect, bar_color); +    }  }  void LLProgressBar::setValue(const LLSD& value) diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index ee78b82429..ef7c8ec012 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -103,6 +103,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)  	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));  	up_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2)); +    up_button_params.commit_on_capture_lost = true;  	mUpBtn = LLUICtrlFactory::create<LLButton>(up_button_params);  	addChild(mUpBtn); @@ -111,6 +112,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)  	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_held_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2)); +    down_button_params.commit_on_capture_lost = true;  	mDownBtn = LLUICtrlFactory::create<LLButton>(down_button_params);  	addChild(mDownBtn); 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 a5c1f9178d..b5d396f3b4 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2730,19 +2730,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");  	}  	LLFastTimerView::sAnalyzePerformance = gSavedSettings.getBOOL("AnalyzePerformance"); @@ -3100,7 +3095,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/llconversationview.cpp b/indra/newview/llconversationview.cpp index 20fa6d490b..48c7df40df 100644 --- a/indra/newview/llconversationview.cpp +++ b/indra/newview/llconversationview.cpp @@ -272,9 +272,9 @@ BOOL LLConversationViewSession::postBuild()  		default:  			break;  		} -	} -	refresh(); +        refresh(); // requires vmi +	}  	return TRUE;  } @@ -490,17 +490,20 @@ void LLConversationViewSession::refresh()  {  	// Refresh the session view from its model data  	LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(getViewModelItem()); -	vmi->resetRefresh(); +    if (vmi) +    { +        vmi->resetRefresh(); -	if (mSessionTitle) -	{		 -		if (!highlightFriendTitle(vmi)) -		{ -			LLStyle::Params title_style; -			title_style.color = LLUIColorTable::instance().getColor("LabelTextColor"); -			mSessionTitle->setText(vmi->getDisplayName(), title_style); -		} -	} +        if (mSessionTitle) +        { +            if (!highlightFriendTitle(vmi)) +            { +                LLStyle::Params title_style; +                title_style.color = LLUIColorTable::instance().getColor("LabelTextColor"); +                mSessionTitle->setText(vmi->getDisplayName(), title_style); +            } +        } +    }  	// Update all speaking indicators  	LLSpeakingIndicatorManager::updateSpeakingIndicators(); @@ -524,8 +527,11 @@ void LLConversationViewSession::refresh()  	}  	requestArrange(); -	// Do the regular upstream refresh -	LLFolderViewFolder::refresh(); +    if (vmi) +    { +        // Do the regular upstream refresh +        LLFolderViewFolder::refresh(); +    }  }  void LLConversationViewSession::onCurrentVoiceSessionChanged(const LLUUID& session_id) @@ -627,8 +633,11 @@ BOOL LLConversationViewParticipant::postBuild()      }      updateChildren(); -	LLFolderViewItem::postBuild(); -    refresh(); +    if (getViewModelItem()) +    { +        LLFolderViewItem::postBuild(); +        refresh(); +    }      return TRUE;  } @@ -712,10 +721,10 @@ void LLConversationViewParticipant::refresh()          // *TODO: We should also do something with vmi->isModerator() to echo that state in the UI somewhat          mSpeakingIndicator->setIsModeratorMuted(participant_model->isModeratorMuted()); +         +        // Do the regular upstream refresh +        LLFolderViewItem::refresh();      } - -	// Do the regular upstream refresh -	LLFolderViewItem::refresh();  }  void LLConversationViewParticipant::addToFolder(LLFolderViewFolder* folder) diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index 34499ac170..93a0b39e02 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -551,7 +551,7 @@ void LLFloaterIMSessionTab::removeConversationViewParticipant(const LLUUID& part  void LLFloaterIMSessionTab::updateConversationViewParticipant(const LLUUID& participant_id)  {  	LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,participant_id); -	if (widget) +	if (widget && widget->getViewModelItem())  	{  		widget->refresh();  	} @@ -576,8 +576,11 @@ void LLFloaterIMSessionTab::refreshConversation()  		{  			participants_uuids.push_back(widget_it->first);  		} -		widget_it->second->refresh(); -		widget_it->second->setVisible(TRUE); +        if (widget_it->second->getViewModelItem()) +        { +            widget_it->second->refresh(); +            widget_it->second->setVisible(TRUE); +        }  		++widget_it;  	}  	if (is_ad_hoc || mIsP2PChat) @@ -1126,7 +1129,10 @@ void LLFloaterIMSessionTab::getSelectedUUIDs(uuid_vec_t& selected_uuids)      for (; it != it_end; ++it)      {          LLConversationItem* conversation_item = static_cast<LLConversationItem *>((*it)->getViewModelItem()); -        selected_uuids.push_back(conversation_item->getUUID()); +        if (conversation_item) +        { +            selected_uuids.push_back(conversation_item->getUUID()); +        }      }  } diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index aae0883564..f114ec0db0 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -744,7 +744,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;      default: @@ -1755,7 +1755,7 @@ void LLFloaterModelPreview::onLoDSourceCommit(S32 lod)      S32 index = lod_source_combo->getCurrentIndex();  	if (index == LLModelPreview::MESH_OPTIMIZER_AUTO          || 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 0429749e11..77a04bc5d7 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -480,30 +480,61 @@ void LLFloaterTools::refresh()  	else  #endif  	{ -		F32 link_cost  = LLSelectMgr::getInstance()->getSelection()->getSelectedLinksetCost(); -		S32 link_count = LLSelectMgr::getInstance()->getSelection()->getRootObjectCount(); +        LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); +        F32 link_cost = selection->getSelectedLinksetCost(); +        S32 link_count = selection->getRootObjectCount(); +        S32 object_count = selection->getObjectCount(); -		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. -				LLViewerParcelMgr::getInstance()->selectParcelAt(selected_object->getPositionGlobal()); -			} -			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; +            } +        } + +        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); @@ -824,7 +855,8 @@ void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask)  	bool have_selection = !LLSelectMgr::getInstance()->getSelection()->isEmpty();  	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); @@ -1095,7 +1127,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 @@ -1181,26 +1213,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<LLFloaterObjectWeights>("object_weights");  	if(object_weights_floater) diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index 6bb0ed73e4..f363733261 100755 --- 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"  #include "llinventoryfunctions.h"  #include "llinventorymodel.h"  #include "llinventorymodelbackgroundfetch.h" @@ -306,6 +307,8 @@ BOOL LLFloaterWorldMap::postBuild()      F32 slider_zoom = mMapView->getZoom();      getChild<LLUICtrl>("zoom slider")->setValue(slider_zoom); +     +    getChild<LLPanel>("expand_btn_panel")->setMouseDownCallback(boost::bind(&LLFloaterWorldMap::onExpandCollapseBtn, this));  	setDefaultBtn(NULL); @@ -1298,6 +1301,22 @@ void LLFloaterWorldMap::onCopySLURL()  	LLNotificationsUtil::add("CopySLURL", args);  } +void LLFloaterWorldMap::onExpandCollapseBtn() +{ +    LLLayoutStack* floater_stack = getChild<LLLayoutStack>("floater_map_stack"); +    LLLayoutPanel* controls_panel = getChild<LLLayoutPanel>("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<LLIconCtrl>("expand_collapse_icon")->setImage(LLUI::getUIImage(image_name)); +    getChild<LLIconCtrl>("expand_collapse_icon")->setToolTip(tooltip); +    getChild<LLPanel>("expand_btn_panel")->setToolTip(tooltip); +} +  // protected  void LLFloaterWorldMap::centerOnTarget(BOOL animate)  { diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h index 378339d0ad..3702226d23 100644 --- a/indra/newview/llfloaterworldmap.h +++ b/indra/newview/llfloaterworldmap.h @@ -131,6 +131,8 @@ protected:  	void			onShowAgentBtn();  	void			onCopySLURL(); +    void            onExpandCollapseBtn(); +  	void			centerOnTarget(BOOL animate);  	void			updateLocation(); diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp index 10814ac076..f357899be0 100644 --- a/indra/newview/llinspectavatar.cpp +++ b/indra/newview/llinspectavatar.cpp @@ -348,7 +348,7 @@ void LLInspectAvatar::onClickMuteVolume()  	LLMuteList* mute_list = LLMuteList::getInstance();  	bool is_muted = mute_list->isMuted(mAvatarID, LLMute::flagVoiceChat); -	LLMute mute(mAvatarID, mAvatarName.getDisplayName(), LLMute::AGENT); +	LLMute mute(mAvatarID, mAvatarName.getUserName(), LLMute::AGENT);  	if (!is_muted)  	{  		mute_list->add(mute, LLMute::flagVoiceChat); diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 62dfaafed1..dff6b3dfa4 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -792,6 +792,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<LLUUID> 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")); @@ -4279,7 +4292,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 d239b23e83..27edc8148e 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -1868,6 +1868,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<LLSidepanelInventory>("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  ///---------------------------------------------------------------------------- @@ -2522,6 +2602,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(); +        } +    }      else      {          std::set<LLFolderViewItem*>::iterator set_iter; diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 8915bfa1e0..ba9f157e47 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -93,6 +93,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/llinventorylistitem.cpp b/indra/newview/llinventorylistitem.cpp index 12bb609df8..de6a850b57 100644 --- a/indra/newview/llinventorylistitem.cpp +++ b/indra/newview/llinventorylistitem.cpp @@ -298,31 +298,41 @@ LLPanelInventoryListItemBase::LLPanelInventoryListItemBase(LLViewerInventoryItem  	applyXUILayout(icon_params, this);  	mIconCtrl = LLUICtrlFactory::create<LLIconCtrl>(icon_params); -	if (mIconCtrl) -	{ -		addChild(mIconCtrl); -	} -	else +    if (!mIconCtrl)  	{  		LLIconCtrl::Params icon_params;  		icon_params.name = "item_icon";  		mIconCtrl = LLUICtrlFactory::create<LLIconCtrl>(icon_params);  	} +    if (mIconCtrl) +    { +        addChild(mIconCtrl); +    } +    else +    { +        LL_ERRS() << "Failed to create mIconCtrl" << LL_ENDL; +    } +  	LLTextBox::Params text_params(params.item_name);  	applyXUILayout(text_params, this);  	mTitleCtrl = LLUICtrlFactory::create<LLTextBox>(text_params); -	if (mTitleCtrl) +	if (!mTitleCtrl)  	{ -		addChild(mTitleCtrl); -	} -	else -	{ -		LLTextBox::Params text_aprams; +		LLTextBox::Params text_params;  		text_params.name = "item_title";  		mTitleCtrl = LLUICtrlFactory::create<LLTextBox>(text_params);  	} + +    if (mTitleCtrl) +    { +        addChild(mTitleCtrl); +    } +    else +    { +        LL_ERRS() << "Failed to create mTitleCtrl" << LL_ENDL; +    }  }  class WidgetVisibilityChanger diff --git a/indra/newview/lllocalbitmaps.h b/indra/newview/lllocalbitmaps.h index d5ee7efdc6..def5a6bd6e 100644 --- a/indra/newview/lllocalbitmaps.h +++ b/indra/newview/lllocalbitmaps.h @@ -122,7 +122,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 d64f1a36f5..a2b75e17e3 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -831,8 +831,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) @@ -846,9 +848,47 @@ void LLModelPreview::clearIncompatible(S32 lod)                      mBaseModel = mModel[lod];                      mBaseScene = mScene[lod];                      mVertexBuffer[5].clear(); +                    replaced_base_model = true; +                } +            } +        } +    } + +    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<LLComboBox>("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); +        }      }  } @@ -1242,8 +1282,9 @@ void LLModelPreview::restoreNormals()  // 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; @@ -1278,20 +1319,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]; @@ -1302,10 +1344,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)      { @@ -1315,38 +1389,43 @@ 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)      {          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;      } -    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; @@ -1377,7 +1456,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]; @@ -1400,19 +1479,19 @@ 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;                          // 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++; @@ -1420,7 +1499,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                          { @@ -1490,18 +1569,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; @@ -1509,14 +1587,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)      { @@ -1526,25 +1630,25 @@ 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)      {          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; @@ -1555,10 +1659,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 @@ -1582,23 +1685,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) @@ -1732,16 +1836,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; +                    }                  }              } @@ -1750,19 +1857,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))                  { @@ -1770,10 +1887,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)                      { @@ -1796,13 +1914,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) @@ -1835,7 +1953,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;                              }                          } @@ -1855,7 +1973,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);                          }                          LL_INFOS() << "Model " << target_model->getName() @@ -3761,7 +3879,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 18e04914e5..032e4c836e 100644 --- a/indra/newview/llmodelpreview.h +++ b/indra/newview/llmodelpreview.h @@ -125,7 +125,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          USE_LOD_ABOVE,      } eLoDMode; @@ -240,13 +240,21 @@ private:      /// Not read unless mWarnOfUnmatchedPhyicsMeshes is true.      LLModel* mDefaultPhysicsShapeP{}; +    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/llpanelblockedlist.cpp b/indra/newview/llpanelblockedlist.cpp index 3322e8a3df..3a4fc613b7 100644 --- a/indra/newview/llpanelblockedlist.cpp +++ b/indra/newview/llpanelblockedlist.cpp @@ -232,7 +232,7 @@ void LLPanelBlockedList::onFilterEdit(const std::string& search_string)  void LLPanelBlockedList::callbackBlockPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names)  {  	if (names.empty() || ids.empty()) return; -	LLMute mute(ids[0], names[0].getAccountName(), LLMute::AGENT); +    LLMute mute(ids[0], names[0].getUserName(), LLMute::AGENT);  	LLMuteList::getInstance()->add(mute);  	showPanelAndSelect(mute.mID);  } diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index be11a4a9f3..6e897e2c7e 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -1308,7 +1308,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 71657239a6..7fe5b1dd3f 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" @@ -298,7 +305,9 @@ BOOL	LLPanelFace::postBuild()  	{  		mCtrlGlow->setCommitCallback(LLPanelFace::onCommitGlow, this);  	} -	 + +    mMenuClipboardColor = getChild<LLMenuButton>("clipboard_color_params_btn"); +    mMenuClipboardTexture = getChild<LLMenuButton>("clipboard_texture_params_btn");  	clearCtrls(); @@ -309,7 +318,9 @@ LLPanelFace::LLPanelFace()  :	LLPanel(),  	mIsAlpha(false)  { -	USE_TEXTURE = LLTrans::getString("use_texture"); +    USE_TEXTURE = LLTrans::getString("use_texture"); +    mCommitCallbackRegistrar.add("PanelFace.menuDoToSelected", boost::bind(&LLPanelFace::menuDoToSelected, this, _2)); +    mEnableCallbackRegistrar.add("PanelFace.menuEnable", boost::bind(&LLPanelFace::menuEnableItem, this, _2));  } @@ -319,6 +330,13 @@ LLPanelFace::~LLPanelFace()  } +void LLPanelFace::draw() +{ +    updateCopyTexButton(); + +    LLPanel::draw(); +} +  void LLPanelFace::sendTexture()  {  	LLTextureCtrl* mTextureCtrl = getChild<LLTextureCtrl>("texture control"); @@ -1508,6 +1526,9 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)  				}  			}  		} +        S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); +        BOOL single_volume = (selected_count == 1); +        mMenuClipboardColor->setEnabled(editable && single_volume);  		// Set variable values for numeric expressions  		LLCalc* calcp = LLCalc::getInstance(); @@ -1568,6 +1589,17 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)  } +void LLPanelFace::updateCopyTexButton() +{ +    LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); +    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); + +} +  void LLPanelFace::refresh()  {  	LL_DEBUGS("Materials") << LL_ENDL; @@ -2538,6 +2570,810 @@ 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<LLVOVolume*>(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<LLUUID, LLUUID> 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<LLUUID, LLUUID> 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<LLUUID, LLUUID>::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<LLVOVolume*>(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()); +            } +        } +    } +} + +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; +} +  // TODO: I don't know who put these in or what these are for???  void LLPanelFace::setMediaURL(const std::string& url) diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h index 2d57d89a44..e3b925c1d4 100644 --- a/indra/newview/llpanelface.h +++ b/indra/newview/llpanelface.h @@ -47,6 +47,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.  // @@ -96,6 +97,8 @@ public:  	LLPanelFace();  	virtual ~LLPanelFace(); +    void draw(); +  	void			refresh();  	void			setMediaURL(const std::string& url);  	void			setMediaType(const std::string& mime_type); @@ -128,6 +131,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); @@ -205,6 +210,18 @@ protected:  	static void		onClickAutoFix(void*);      static void		onAlignTexture(void*); +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: +    void            menuDoToSelected(const LLSD& userdata); +    bool            menuEnableItem(const LLSD& userdata); +  	static F32     valueGlow(LLViewerObject* object, S32 face); @@ -400,7 +417,10 @@ 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); + +    LLMenuButton*   mMenuClipboardColor; +    LLMenuButton*   mMenuClipboardTexture;  	bool mIsAlpha; @@ -415,7 +435,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 64f1fc9b90..0bfc1297d3 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" @@ -117,8 +118,9 @@ BOOL	LLPanelObject::postBuild()  	// Phantom checkbox  	mCheckPhantom = getChild<LLCheckBoxCtrl>("Phantom Checkbox Ctrl");  	childSetCommitCallback("Phantom Checkbox Ctrl",onCommitPhantom,this); -        +  	// Position +	mMenuClipboardPos = getChild<LLMenuButton>("clipboard_pos_btn");  	mLabelPosition = getChild<LLTextBox>("label position");  	mCtrlPosX = getChild<LLSpinCtrl>("Pos X");  	childSetCommitCallback("Pos X",onCommitPosition,this); @@ -128,6 +130,7 @@ BOOL	LLPanelObject::postBuild()  	childSetCommitCallback("Pos Z",onCommitPosition,this);  	// Scale +	mMenuClipboardSize = getChild<LLMenuButton>("clipboard_size_btn");  	mLabelSize = getChild<LLTextBox>("label size");  	mCtrlScaleX = getChild<LLSpinCtrl>("Scale X");  	childSetCommitCallback("Scale X",onCommitScale,this); @@ -141,6 +144,7 @@ BOOL	LLPanelObject::postBuild()  	childSetCommitCallback("Scale Z",onCommitScale,this);  	// Rotation +	mMenuClipboardRot = getChild<LLMenuButton>("clipboard_rot_btn");  	mLabelRotation = getChild<LLTextBox>("label rotation");  	mCtrlRotX = getChild<LLSpinCtrl>("Rot X");  	childSetCommitCallback("Rot X",onCommitRotation,this); @@ -155,6 +159,8 @@ BOOL	LLPanelObject::postBuild()  	mComboBaseType = getChild<LLComboBox>("comboBaseType");  	childSetCommitCallback("comboBaseType",onCommitParametric,this); +	mMenuClipboardParams = getChild<LLMenuButton>("clipboard_obj_params_btn"); +  	// Cut  	mLabelCut = getChild<LLTextBox>("text cut");  	mSpinCutBegin = getChild<LLSpinCtrl>("cut begin"); @@ -285,8 +291,13 @@ LLPanelObject::LLPanelObject()  	mSelectedType(MI_BOX),  	mSculptTextureRevert(LLUUID::null),  	mSculptTypeRevert(0), +    mHasClipboardPos(false), +    mHasClipboardSize(false), +    mHasClipboardRot(false),  	mSizeChanged(FALSE)  { +    mCommitCallbackRegistrar.add("PanelObject.menuDoToSelected", boost::bind(&LLPanelObject::menuDoToSelected, this, _2)); +    mEnableCallbackRegistrar.add("PanelObject.menuEnable", boost::bind(&LLPanelObject::menuEnableItem, this, _2));  } @@ -373,7 +384,7 @@ void LLPanelObject::getState( )  		calcp->clearVar(LLCalc::Z_POS);  	} - +	mMenuClipboardPos->setEnabled(enable_move);  	mLabelPosition->setEnabled( enable_move );  	mCtrlPosX->setEnabled(enable_move);  	mCtrlPosY->setEnabled(enable_move); @@ -399,6 +410,7 @@ void LLPanelObject::getState( )  		calcp->setVar(LLCalc::Z_SCALE, 0.f);  	} +	mMenuClipboardSize->setEnabled(enable_scale);  	mLabelSize->setEnabled( enable_scale );  	mCtrlScaleX->setEnabled( enable_scale );  	mCtrlScaleY->setEnabled( enable_scale ); @@ -430,6 +442,7 @@ void LLPanelObject::getState( )  		calcp->clearVar(LLCalc::Z_ROT);  	} +	mMenuClipboardRot->setEnabled(enable_rotate);  	mLabelRotation->setEnabled( enable_rotate );  	mCtrlRotX->setEnabled( enable_rotate );  	mCtrlRotY->setEnabled( enable_rotate ); @@ -607,7 +620,7 @@ void LLPanelObject::getState( )  		}  		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;  		} @@ -933,6 +946,7 @@ void LLPanelObject::getState( )  	// Update field enablement  	mComboBaseType	->setEnabled( enabled ); +	mMenuClipboardParams->setEnabled(enabled);  	mLabelCut		->setEnabled( enabled );  	mSpinCutBegin	->setEnabled( enabled ); @@ -1093,7 +1107,8 @@ void LLPanelObject::getState( )  			}  			mComboBaseType->setEnabled(!isMesh); -			 +			mMenuClipboardParams->setEnabled(!isMesh); +  			if (mCtrlSculptType)  			{  				if (sculpt_stitching == LL_SCULPT_TYPE_NONE) @@ -1157,11 +1172,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;  	}  } @@ -1173,11 +1188,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;  	}  } @@ -1190,11 +1205,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;  	}  } @@ -1304,7 +1319,7 @@ void LLPanelObject::getVolumeParams(LLVolumeParams& volume_params)  		break;  	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; @@ -1644,13 +1659,15 @@ void LLPanelObject::sendPosition(BOOL btn_down)  	LLVector3 newpos(mCtrlPosX->get(), mCtrlPosY->get(), mCtrlPosZ->get());  	LLViewerRegion* regionp = mObject->getRegion(); -	// Clamp the Z height -	const F32 height = newpos.mV[VZ]; -	const F32 min_height = LLWorld::getInstance()->getMinAllowedZ(mObject, mObject->getPositionGlobal()); -	const F32 max_height = LLWorld::getInstance()->getRegionMaxHeight(); +	if (!regionp) return;  	if (!mObject->isAttachment())  	{ +        // Clamp the Z height +        const F32 height = newpos.mV[VZ]; +        const F32 min_height = LLWorld::getInstance()->getMinAllowedZ(mObject, mObject->getPositionGlobal()); +        const F32 max_height = LLWorld::getInstance()->getRegionMaxHeight(); +  		if ( height < min_height)  		{  			newpos.mV[VZ] = min_height; @@ -2011,3 +2028,283 @@ void LLPanelObject::onCommitSculptType(LLUICtrl *ctrl, void* userdata)  	self->sendSculpt();  } + +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 mClipboardParams.isMap() && !mClipboardParams.emptyMap(); +    } +    // 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 LLPanelObject::onCopyPos() +{ +    mClipboardPos = LLVector3(mCtrlPosX->get(), mCtrlPosY->get(), mCtrlPosZ->get()); + +    std::string stringVec = llformat("<%g, %g, %g>", mClipboardPos.mV[VX], mClipboardPos.mV[VY], mClipboardPos.mV[VZ]); +    LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(stringVec)); + +    mHasClipboardPos = true; +} + +void LLPanelObject::onCopySize() +{ +    mClipboardSize = LLVector3(mCtrlScaleX->get(), mCtrlScaleY->get(), mCtrlScaleZ->get()); + +    std::string stringVec = llformat("<%g, %g, %g>", mClipboardSize.mV[VX], mClipboardSize.mV[VY], mClipboardSize.mV[VZ]); +    LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(stringVec)); + +    mHasClipboardSize = true; +} + +void LLPanelObject::onCopyRot() +{ +    mClipboardRot = LLVector3(mCtrlRotX->get(), mCtrlRotY->get(), mCtrlRotZ->get()); + +    std::string stringVec = llformat("<%g, %g, %g>", mClipboardRot.mV[VX], mClipboardRot.mV[VY], mClipboardRot.mV[VZ]); +    LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(stringVec)); + +    mHasClipboardRot = true; +} + +void LLPanelObject::onPastePos() +{ +    if (!mHasClipboardPos) return; +    if (mObject.isNull()) return; + +    LLViewerRegion* regionp = mObject->getRegion(); +    if (!regionp) return; + + +    // 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 clamped by sendPosition +    } + +    mCtrlPosX->set( mClipboardPos.mV[VX] ); +    mCtrlPosY->set( mClipboardPos.mV[VY] ); +    mCtrlPosZ->set( mClipboardPos.mV[VZ] ); + +    sendPosition(FALSE); +} + +void LLPanelObject::onPasteSize() +{ +    if (!mHasClipboardSize) return; + +    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); + +    mCtrlScaleX->set(mClipboardSize.mV[VX]); +    mCtrlScaleY->set(mClipboardSize.mV[VY]); +    mCtrlScaleZ->set(mClipboardSize.mV[VZ]); + +    sendScale(FALSE); +} + +void LLPanelObject::onPasteRot() +{ +    if (!mHasClipboardRot) return; + +    mCtrlRotX->set(mClipboardRot.mV[VX]); +    mCtrlRotY->set(mClipboardRot.mV[VY]); +    mCtrlRotZ->set(mClipboardRot.mV[VZ]); + +    sendRotation(FALSE); +} + +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(); +    } +} + +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); +    } +} diff --git a/indra/newview/llpanelobject.h b/indra/newview/llpanelobject.h index 8829f493fa..515dd27c0a 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; @@ -66,6 +67,14 @@ public:  	static void 	onCommitPhantom(		LLUICtrl* ctrl, void* userdata);  	static void 	onCommitPhysics(		LLUICtrl* ctrl, void* userdata); +    void            onCopyPos(); +    void            onPastePos(); +    void            onCopySize(); +    void            onPasteSize(); +    void            onCopyRot(); +    void            onPasteRot(); +    void            onCopyParams(); +    void            onPasteParams();  	static void 	onCommitParametric(LLUICtrl* ctrl, void* userdata); @@ -75,6 +84,9 @@ public:  	BOOL     		onDropSculpt(LLInventoryItem* item);  	static void     onCommitSculptType(    LLUICtrl *ctrl, void* userdata); +    void            menuDoToSelected(const LLSD& userdata); +    bool            menuEnableItem(const LLSD& userdata); +  protected:  	void			getState(); @@ -92,6 +104,7 @@ protected:  protected:  	// Per-object options  	LLComboBox*		mComboBaseType; +	LLMenuButton*	mMenuClipboardParams;  	LLTextBox*		mLabelCut;  	LLSpinCtrl*		mSpinCutBegin; @@ -131,17 +144,20 @@ protected:  	LLTextBox*		mLabelRevolutions;  	LLSpinCtrl*		mSpinRevolutions; +	LLMenuButton*   mMenuClipboardPos;  	LLTextBox*		mLabelPosition;  	LLSpinCtrl*		mCtrlPosX;  	LLSpinCtrl*		mCtrlPosY;  	LLSpinCtrl*		mCtrlPosZ; +	LLMenuButton*   mMenuClipboardSize;  	LLTextBox*		mLabelSize;  	LLSpinCtrl*		mCtrlScaleX;  	LLSpinCtrl*		mCtrlScaleY;  	LLSpinCtrl*		mCtrlScaleZ;  	BOOL			mSizeChanged; +	LLMenuButton*   mMenuClipboardRot;  	LLTextBox*		mLabelRotation;  	LLSpinCtrl*		mCtrlRotX;  	LLSpinCtrl*		mCtrlRotY; @@ -157,7 +173,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 @@ -167,6 +183,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<LLViewerObject> mObject;  	LLPointer<LLViewerObject> mRootObject;  }; diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index 0d987df6ca..cfaa9456be 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -618,7 +618,18 @@ const std::string& LLTaskCategoryBridge::getDisplayName() const  	if (cat)  	{ -		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);  	}  	return mDisplayName; @@ -1522,6 +1533,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 89c558e4f8..5b02609a53 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"  #include "llpreviewscript.h"  #include "llresmgr.h"  #include "llselectmgr.h" @@ -168,6 +169,9 @@ BOOL	LLPanelVolume::postBuild()  		mSpinPhysicsRestitution->setCommitCallback(boost::bind(&LLPanelVolume::sendPhysicsRestitution, this, _1, mSpinPhysicsRestitution));  	} +    mMenuClipboardFeatures = getChild<LLMenuButton>("clipboard_features_params_btn"); +    mMenuClipboardLight = getChild<LLMenuButton>("clipboard_light_params_btn"); +  	std::map<std::string, std::string> material_name_map;  	material_name_map["Stone"]= LLTrans::getString("Stone");  	material_name_map["Metal"]= LLTrans::getString("Metal");	 @@ -208,6 +212,8 @@ LLPanelVolume::LLPanelVolume()  {  	setMouseOpaque(FALSE); +    mCommitCallbackRegistrar.add("PanelVolume.menuDoToSelected", boost::bind(&LLPanelVolume::menuDoToSelected, this, _2)); +    mEnableCallbackRegistrar.add("PanelVolume.menuEnable", boost::bind(&LLPanelVolume::menuEnableItem, this, _2));  } @@ -409,7 +415,6 @@ void LLPanelVolume::getState( )  			gAgentAvatarp->updateMeshVisibility();  		}  	} -	  	// Flexible properties  	BOOL is_flexible = volobjp && volobjp->isFlexible(); @@ -564,6 +569,9 @@ void LLPanelVolume::getState( )  	mObject = objectp;  	mRootObject = root_objectp; + +    mMenuClipboardFeatures->setEnabled(editable && single_volume && volobjp); // Note: physics doesn't need to be limited by single volume +    mMenuClipboardLight->setEnabled(editable && single_volume && volobjp);  }  // static @@ -831,6 +839,290 @@ 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> +        { +            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); +        } +    } +} + +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; +} +  // static  void LLPanelVolume::onCommitMaterial( LLUICtrl* ctrl, void* userdata )  { diff --git a/indra/newview/llpanelvolume.h b/indra/newview/llpanelvolume.h index 6e49ccb742..d9198f3693 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;  class LLViewerObject;  class LLComboBox;  class LLColorSwatchCtrl; @@ -76,6 +77,13 @@ public:      static void    setLightTextureID(const LLUUID &asset_id, const LLUUID &item_id, LLVOVolume* volobjp); +    void            onCopyFeatures(); +    void            onPasteFeatures(); +    void            onCopyLight(); +    void            onPasteLight(); +  +    void        menuDoToSelected(const LLSD& userdata); +    bool        menuEnableItem(const LLSD& userdata);  protected:  	void			getState(); @@ -123,6 +131,11 @@ protected:  	LLSpinCtrl*     mSpinPhysicsFriction;  	LLSpinCtrl*     mSpinPhysicsDensity;  	LLSpinCtrl*     mSpinPhysicsRestitution; + +    LLMenuButton*   mMenuClipboardFeatures; +    LLMenuButton*   mMenuClipboardLight; + +    LLSD            mClipboardParams;  };  #endif diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 1c4a56b549..51ee5b6157 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -80,6 +80,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 92f6f89af6..3769f43737 100644 --- a/indra/newview/lltexturectrl.h +++ b/indra/newview/lltexturectrl.h @@ -52,6 +52,16 @@ class LLViewerFetchedTexture;  typedef boost::function<BOOL (LLUICtrl*, LLInventoryItem*)> drag_n_drop_callback;  typedef boost::function<void (LLInventoryItem*)> 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 8baad30e8f..692e8d91a9 100644 --- a/indra/newview/lltoastalertpanel.cpp +++ b/indra/newview/lltoastalertpanel.cpp @@ -291,7 +291,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 50868d0fa5..f76a2f7f90 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -1100,7 +1100,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) @@ -1124,7 +1125,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 59a213d010..765b3f9d26 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -2740,6 +2740,32 @@ 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()); +}  static void init_default_item_label(const std::string& item_name) @@ -9527,6 +9553,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"); diff --git a/indra/newview/llvieweroctree.cpp b/indra/newview/llvieweroctree.cpp index 12624ec3a2..36b2bd4c32 100644 --- a/indra/newview/llvieweroctree.cpp +++ b/indra/newview/llvieweroctree.cpp @@ -948,6 +948,7 @@ void LLOcclusionCullingGroup::setOcclusionState(U32 state, S32 mode /* = STATE_M          break;      case STATE_MODE_DIFF: +        if (mOctreeNode)          {              LLSpatialSetOcclusionStateDiff setter(state);              setter.traverse(mOctreeNode); @@ -955,6 +956,7 @@ void LLOcclusionCullingGroup::setOcclusionState(U32 state, S32 mode /* = STATE_M          break;      case STATE_MODE_BRANCH: +        if (mOctreeNode)          {              LLSpatialSetOcclusionState setter(state);              setter.traverse(mOctreeNode); @@ -1024,6 +1026,7 @@ void LLOcclusionCullingGroup::clearOcclusionState(U32 state, S32 mode /* = STATE              break;          case STATE_MODE_DIFF: +            if (mOctreeNode)              {                  LLSpatialClearOcclusionStateDiff clearer(state);                  clearer.traverse(mOctreeNode); @@ -1031,6 +1034,7 @@ void LLOcclusionCullingGroup::clearOcclusionState(U32 state, S32 mode /* = STATE              break;          case STATE_MODE_BRANCH: +            if (mOctreeNode)              {                  LLSpatialClearOcclusionState clearer(state);                  clearer.traverse(mOctreeNode); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 2deb2928b9..cd8021457b 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -103,7 +103,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/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 5dfcb68562..1be0d91d19 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -4620,7 +4620,12 @@ bool LLVOAvatar::updateCharacter(LLAgent &agent)  	}  	else if (!getParent() && isSitting() && !isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED))  	{ -		getOffObject(); +        // If we are starting up, motion might be loading +        LLMotion *motionp = mMotionController.findMotion(ANIM_AGENT_SIT_GROUND_CONSTRAINED); +        if (!motionp || !mMotionController.isMotionLoading(motionp)) +        { +            getOffObject(); +        }  	}  	//-------------------------------------------------------------------- diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 8bf9d12b33..56028e051f 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -230,6 +230,7 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re  	mMediaImplList.resize(getNumTEs());  	mLastFetchedMediaVersion = -1; +    mServerDrawableUpdateCount = 0;  	memset(&mIndexInTex, 0, sizeof(S32) * LLRender::NUM_VOLUME_TEXTURE_CHANNELS);  	mMDCImplCount = 0;  	mLastRiggingInfoLOD = -1; @@ -325,6 +326,9 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,  	LLColor4U color;  	const S32 teDirtyBits = (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR|TEM_CHANGE_MEDIA); +    const bool previously_volume_changed = mVolumeChanged; +    const bool previously_face_mapping_changed = mFaceMappingChanged; +    const bool previously_color_changed = mColorChanged;  	// Do base class updates...  	U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp); @@ -548,9 +552,31 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,  	// ...and clean up any media impls  	cleanUpMediaImpls(); +    if (( +            (mVolumeChanged && !previously_volume_changed) || +            (mFaceMappingChanged && !previously_face_mapping_changed) || +            (mColorChanged && !previously_color_changed) +        ) +        && !mLODChanged) { +        onDrawableUpdateFromServer(); +    } +  	return retval;  } +// Called when a volume, material, etc is updated by the server, possibly by a +// script. If this occurs too often for this object, mark it as active so that +// it doesn't disrupt the octree/render batches, thereby potentially causing a +// big performance penalty. +void LLVOVolume::onDrawableUpdateFromServer() +{ +    constexpr U32 UPDATES_UNTIL_ACTIVE = 8; +    ++mServerDrawableUpdateCount; +    if (mDrawable && !mDrawable->isActive() && mServerDrawableUpdateCount > UPDATES_UNTIL_ACTIVE) +    { +        mDrawable->makeActive(); +    } +}  void LLVOVolume::animateTextures()  { @@ -1670,7 +1696,7 @@ void LLVOVolume::regenFaces()  	}  } -BOOL LLVOVolume::genBBoxes(BOOL force_global) +BOOL LLVOVolume::genBBoxes(BOOL force_global, BOOL should_update_octree_bounds)  {      LL_PROFILE_ZONE_SCOPED;      BOOL res = TRUE; @@ -1746,10 +1772,9 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global)          }      } -      if (any_valid_boxes)      { -        if (rebuild) +        if (rebuild && should_update_octree_bounds)          {              //get the Avatar associated with this object if it's rigged              LLVOAvatar* avatar = nullptr; @@ -1911,7 +1936,7 @@ void LLVOVolume::updateRelativeXform(bool force_identity)  	}  } -bool LLVOVolume::lodOrSculptChanged(LLDrawable *drawable, BOOL &compiled) +bool LLVOVolume::lodOrSculptChanged(LLDrawable *drawable, BOOL &compiled, BOOL &should_update_octree_bounds)  {      LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;  	bool regen_faces = false; @@ -1943,6 +1968,9 @@ bool LLVOVolume::lodOrSculptChanged(LLDrawable *drawable, BOOL &compiled)          }  		compiled = TRUE; +        // new_lod > old_lod breaks a feedback loop between LOD updates and +        // bounding box updates. +        should_update_octree_bounds = should_update_octree_bounds || mSculptChanged || new_lod > old_lod;  		sNumLODChanges += new_num_faces;  		if ((S32)getNumTEs() != getVolume()->getNumFaces()) @@ -2002,8 +2030,6 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)  		group->dirtyMesh();  	} -	BOOL compiled = FALSE; -			  	updateRelativeXform();  	if (mDrawable.isNull()) // Not sure why this is happening, but it is... @@ -2011,49 +2037,55 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)  		return TRUE; // No update to complete  	} +	BOOL compiled = FALSE; +    // This should be true in most cases, unless we're sure no octree update is +    // needed. +    BOOL should_update_octree_bounds = bool(getRiggedVolume()) || mDrawable->isState(LLDrawable::REBUILD_POSITION) || !mDrawable->getSpatialExtents()->isFinite3(); +  	if (mVolumeChanged || mFaceMappingChanged)  	{  		dirtySpatialGroup(drawable->isState(LLDrawable::IN_REBUILD_Q1));  		bool was_regen_faces = false; +        should_update_octree_bounds = true;  		if (mVolumeChanged)  		{ -			was_regen_faces = lodOrSculptChanged(drawable, compiled); +            was_regen_faces = lodOrSculptChanged(drawable, compiled, should_update_octree_bounds);  			drawable->setState(LLDrawable::REBUILD_VOLUME);  		}  		else if (mSculptChanged || mLODChanged || mColorChanged)  		{  			compiled = TRUE; -			was_regen_faces = lodOrSculptChanged(drawable, compiled); +            was_regen_faces = lodOrSculptChanged(drawable, compiled, should_update_octree_bounds);  		}  		if (!was_regen_faces) {  			regenFaces();  		} - -		genBBoxes(FALSE);  	}  	else if (mLODChanged || mSculptChanged || mColorChanged)  	{  		dirtySpatialGroup(drawable->isState(LLDrawable::IN_REBUILD_Q1));  		compiled = TRUE; -		lodOrSculptChanged(drawable, compiled); +        lodOrSculptChanged(drawable, compiled, should_update_octree_bounds);  		if(drawable->isState(LLDrawable::REBUILD_RIGGED | LLDrawable::RIGGED))   		{  			updateRiggedVolume(false);  		} -		genBBoxes(FALSE);  	}  	// it has its own drawable (it's moved) or it has changed UVs or it has changed xforms from global<->local  	else  	{  		compiled = TRUE;  		// All it did was move or we changed the texture coordinate offset -		genBBoxes(FALSE);  	} +    // Generate bounding boxes if needed, and update the object's size in the +    // octree +    genBBoxes(FALSE, should_update_octree_bounds); +  	// Update face flags  	updateFaceFlags(); diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index 64514c51b0..01ad40274b 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -235,7 +235,7 @@ public:  				void	updateFaceFlags();  				void	regenFaces(); -				BOOL	genBBoxes(BOOL force_global); +                BOOL    genBBoxes(BOOL force_global, BOOL should_update_octree_bounds = TRUE);  				void	preRebuild();  	virtual		void	updateSpatialExtents(LLVector4a& min, LLVector4a& max);  	virtual		F32		getBinRadius(); @@ -390,13 +390,14 @@ protected:  	static S32 mRenderComplexity_last;  	static S32 mRenderComplexity_current; +    void onDrawableUpdateFromServer();  	void requestMediaDataUpdate(bool isNew);  	void cleanUpMediaImpls();  	void addMediaImpl(LLViewerMediaImpl* media_impl, S32 texture_index) ;  	void removeMediaImpl(S32 texture_index) ;  private: -	bool lodOrSculptChanged(LLDrawable *drawable, BOOL &compiled); +    bool lodOrSculptChanged(LLDrawable *drawable, BOOL &compiled, BOOL &shouldUpdateOctreeBounds);  public: @@ -428,6 +429,7 @@ private:  	LLPointer<LLViewerFetchedTexture> mLightTexture;  	media_list_t mMediaImplList;  	S32			mLastFetchedMediaVersion; // as fetched from the server, starts as -1 +    U32         mServerDrawableUpdateCount;  	S32 mIndexInTex[LLRender::NUM_VOLUME_TEXTURE_CHANNELS];  	S32 mMDCImplCount; diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 95e2f98251..96ba80dacc 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -3939,7 +3939,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.pngBinary files differ new file mode 100644 index 0000000000..9a81c5f94b --- /dev/null +++ b/indra/newview/skins/default/textures/icons/ClipboardMenu_Disabled.png diff --git a/indra/newview/skins/default/textures/icons/ClipboardMenu_Off.png b/indra/newview/skins/default/textures/icons/ClipboardMenu_Off.pngBinary files differ new file mode 100644 index 0000000000..88012cf8d1 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/ClipboardMenu_Off.png diff --git a/indra/newview/skins/default/textures/icons/ClipboardMenu_Press.png b/indra/newview/skins/default/textures/icons/ClipboardMenu_Press.pngBinary files differ new file mode 100644 index 0000000000..ab02e7d42d --- /dev/null +++ b/indra/newview/skins/default/textures/icons/ClipboardMenu_Press.png diff --git a/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Disabled.png b/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Disabled.pngBinary files differ new file mode 100644 index 0000000000..63b4bd2127 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Disabled.png diff --git a/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Off.png b/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Off.pngBinary files differ new file mode 100644 index 0000000000..4200182b0c --- /dev/null +++ b/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Off.png diff --git a/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Press.png b/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Press.pngBinary files differ new file mode 100644 index 0000000000..e12887f489 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Press.png diff --git a/indra/newview/skins/default/textures/map_ui_collapse_icon.png b/indra/newview/skins/default/textures/map_ui_collapse_icon.pngBinary files differ new file mode 100644 index 0000000000..e4de49d4af --- /dev/null +++ b/indra/newview/skins/default/textures/map_ui_collapse_icon.png diff --git a/indra/newview/skins/default/textures/map_ui_expand_icon.png b/indra/newview/skins/default/textures/map_ui_expand_icon.pngBinary files differ new file mode 100644 index 0000000000..08734b4cc0 --- /dev/null +++ b/indra/newview/skins/default/textures/map_ui_expand_icon.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 03e23b5ba3..e9ef888a7f 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -447,6 +447,13 @@ with the same filename but different name    <texture name="OptionsMenu_Off" file_name="icons/OptionsMenu_Off.png" preload="false" />    <texture name="OptionsMenu_Press" file_name="icons/OptionsMenu_Press.png" preload="false" /> +  <texture name="ClipboardSmallMenu_Disabled" file_name="icons/ClipboardSmallMenu_Disabled.png" preload="false" /> +  <texture name="ClipboardSmallMenu_Off" file_name="icons/ClipboardSmallMenu_Off.png" preload="false" /> +  <texture name="ClipboardSmallMenu_Press" file_name="icons/ClipboardSmallMenu_Press.png" preload="false" /> +  <texture name="ClipboardMenu_Disabled" file_name="icons/ClipboardMenu_Disabled.png" preload="false" /> +  <texture name="ClipboardMenu_Off" file_name="icons/ClipboardMenu_Off.png" preload="false" /> +  <texture name="ClipboardMenu_Press" file_name="icons/ClipboardMenu_Press.png" preload="false" /> +    <texture name="OutboxStatus_Success" file_name="green_checkmark.png" preload="false" />    <texture name="OutboxStatus_Warning" file_name="icons/pop_up_caution.png" preload="false" />    <texture name="OutboxStatus_Error" file_name="red_x.png" preload="false" /> @@ -803,6 +810,8 @@ with the same filename but different name    <texture name="map_infohub.tga" />    <texture name="map_telehub.tga" />    <texture name="map_track_16.tga" /> +  <texture name="map_ui_collapse_icon.png" /> +  <texture name="map_ui_expand_icon.png" />    <texture name="notify_caution_icon.tga" /> diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml index 0abee2ff80..ade79b8884 100644 --- a/indra/newview/skins/default/xui/en/floater_tools.xml +++ b/indra/newview/skins/default/xui/en/floater_tools.xml @@ -2,7 +2,7 @@  <floater   positioning="cascading"   legacy_header_height="18" - height="600" + height="609"   layout="topleft"   bg_opaque_image="Window_NoTitle_Foreground"   bg_alpha_image="Window_NoTitle_Background" @@ -68,7 +68,7 @@      </floater.string>      <floater.string       name="status_selectcount"> -        [OBJ_COUNT] objects selected, land impact [LAND_IMPACT] +        [OBJ_COUNT] objects selected, land impact [LAND_IMPACT] [secondlife:///app/openfloater/object_weights More info]      </floater.string>      <floater.string       name="status_remaining_capacity"> @@ -763,11 +763,12 @@  	  font="SansSerifSmall"  	  layout="topleft"  	  left="10" -	  name="selection_count" +	  name="selection_faces"  	  top_delta="0"  	  visible="false"  	  width="280"> -	</text> +    Faces selected: [FACES_STRING] +  </text>  	<text  	 text_color="LtGray_50"  	  type="string" @@ -777,11 +778,10 @@  	  font="SansSerifSmall"  	  layout="topleft"  	  left="10" -	  name="remaining_capacity" +	  name="selection_count"  	  top_pad="0"  	  visible="false"  	  width="280"> -	  [CAPACITY_STRING] [secondlife:///app/openfloater/object_weights More info]  	</text>      <!-- <text -->      <!-- text_color="LtGray_50" --> @@ -820,7 +820,7 @@      width="282"/>      <tab_container       follows="left|top" -     height="410" +     height="426"       halign="center"       left="0"       name="Object Info Tabs" @@ -1430,16 +1430,40 @@ even though the user gets a free copy.               tool_tip="Causes object to not collide with other objects or avatars"               top_pad="0"               width="123" /> -        <text +            <view_border +             bevel_style="none" +             follows="top|left" +             height="0" +             layout="topleft" +             left_delta="0" +             name="object_horizontal" +             top_pad="10" +             width="95" /> +            <menu_button +             menu_filename="menu_copy_paste_pos.xml" +             follows="top|left" +             height="11" +             image_disabled="ClipboardSmallMenu_Disabled" +             image_selected="ClipboardSmallMenu_Press" +             image_unselected="ClipboardSmallMenu_Off" +             layout="topleft" +             left_delta="0" +             top_pad="13" +             name="clipboard_pos_btn" +             tool_tip="Paste options" +             width="19"/> +            <text               type="string"               length="1"               follows="left|top"               height="10"               layout="topleft"               name="label position" -             top_pad="10" +             tool_tip="Position (meters)" +             left_pad="8" +             top_delta="0"               width="121"> -                Position (meters) +                Position (m)              </text>              <spinner               follows="left|top" @@ -1449,12 +1473,12 @@ even though the user gets a free copy.               label="X"               label_width="10"               layout="topleft" -             left_delta="0" +             left_delta="-27"               max_val="512"               min_val="-256"               name="Pos X"               text_enabled_color="1 0 0.3 .7" -             top_pad="5" +             top_pad="8"               width="87" />              <spinner               follows="left|top" @@ -1481,21 +1505,36 @@ even though the user gets a free copy.               layout="topleft"               left_delta="0"               max_val="4096" +             min_val="-32"               name="Pos Z"               text_enabled_color="0 0.8 1 .65"               top_pad="3"               width="87" /> +            <menu_button +             menu_filename="menu_copy_paste_size.xml" +             follows="top|left" +             height="11" +             image_disabled="ClipboardSmallMenu_Disabled" +             image_selected="ClipboardSmallMenu_Press" +             image_unselected="ClipboardSmallMenu_Off" +             layout="topleft" +             left_delta="0" +             top_pad="13" +             name="clipboard_size_btn" +             tool_tip="Paste options" +             width="19"/>              <text               type="string"               length="1"               follows="left|top"               height="10"               layout="topleft" -             left_delta="0" +             left_pad="8" +             top_delta="0"               name="label size" -             top_pad="6" +             tool_tip="Size (meters)"               width="121"> -                Size (meters) +                Size (m)              </text>              <spinner               follows="left|top" @@ -1505,12 +1544,12 @@ even though the user gets a free copy.               label="X"               label_width="10"               layout="topleft" -             left_delta="0" +             left_delta="-27"               max_val="64"               min_val="0.01"               name="Scale X"               text_enabled_color="1 1 1 1" -             top_pad="5" +             top_pad="8"               width="87" />              <spinner               follows="left|top" @@ -1542,17 +1581,31 @@ even though the user gets a free copy.               text_enabled_color="1 1 1 1"               top_pad="3"               width="87" /> +            <menu_button +             menu_filename="menu_copy_paste_rot.xml" +             follows="top|left" +             height="11" +             image_disabled="ClipboardSmallMenu_Disabled" +             image_selected="ClipboardSmallMenu_Press" +             image_unselected="ClipboardSmallMenu_Off" +             layout="topleft" +             left_delta="0" +             top_pad="13" +             name="clipboard_rot_btn" +             tool_tip="Paste options" +             width="19"/>              <text               type="string"               length="1"               follows="left|top"               height="10"               layout="topleft" -             left_delta="0" +             left_pad="8" +             top_delta="0"               name="label rotation" -             top_pad="10" +             tool_tip="Rotation (degrees)"               width="121"> -                Rotation (degrees) +                Rotation (°)              </text>              <spinner               decimal_digits="2" @@ -1563,12 +1616,12 @@ even though the user gets a free copy.               label="X"               label_width="10"               layout="topleft" -             left_delta="0" +             left_delta="-27"               max_val="9999"               min_val="-9999"               name="Rot X"               text_enabled_color="1 1 1 1" -             top_pad="5" +             top_pad="8"               width="87" />              <spinner               decimal_digits="2" @@ -1614,13 +1667,23 @@ even though the user gets a free copy.               width="150">                  Prim Type              </text>--> + +            <view_border +             bevel_style="none" +             follows="top|left" +             layout="topleft" +             name="object_vertical" +             left="117" +             top="6" +             height="500" +             width="0"/>              <combo_box               height="19"               layout="topleft"               name="comboBaseType"               top="6"               left="125" -             width="150"> +             width="125">                  <combo_box.item                   label="Box"                   name="Box" @@ -1654,13 +1717,26 @@ even though the user gets a free copy.                   name="Sculpted"                   value="Sculpted" />              </combo_box> +            <menu_button +              menu_filename="menu_copy_paste_object.xml" +              follows="top|left" +              height="15" +              image_disabled="ClipboardMenu_Disabled" +              image_selected="ClipboardMenu_Press" +              image_unselected="ClipboardMenu_Off" +              layout="topleft" +              left_pad="8" +              top_delta="2" +              name="clipboard_obj_params_btn" +              tool_tip="Paste options" +              width="22"/>              <text               type="string"               length="1"               follows="left|top"               height="10"               layout="topleft" -             left_delta="0" +             left="125"               name="text cut"               top_pad="5"               width="150"> @@ -1700,7 +1776,7 @@ even though the user gets a free copy.               layout="topleft"               left="125"               name="text hollow" -             top_pad="6" +             top_pad="7"               width="68">                  Hollow              </text> @@ -1748,7 +1824,7 @@ even though the user gets a free copy.               layout="topleft"               left="125"               name="Hollow Shape" -             top_pad="4" +             top_pad="7"               width="150">                  Hollow Shape              </text> @@ -1784,7 +1860,7 @@ even though the user gets a free copy.               layout="topleft"               left_delta="0"               name="text twist" -             top_pad="5" +             top_pad="7"               width="150">                  Twist (begin/end)              </text> @@ -1826,12 +1902,12 @@ even though the user gets a free copy.               layout="topleft"               left="125"               name="scale_taper" -             top_pad="3" +             top_pad="7"               width="150">                  Taper              </text>              <text -			 visible="false" +             visible="false"               type="string"               length="1"               follows="left|top" @@ -1879,7 +1955,7 @@ even though the user gets a free copy.               layout="topleft"               left="125"               name="text topshear" -             top_pad="3" +             top_pad="5"               width="141">                  Top Shear              </text> @@ -1922,12 +1998,12 @@ even though the user gets a free copy.               layout="topleft"               left="125"               name="advanced_cut" -             top_pad="3" +             top_pad="7"               width="150">                  Profile Cut (begin/end)              </text>              <text -			 visible="false" +             visible="false"               type="string"               length="1"               follows="left|top" @@ -1986,7 +2062,7 @@ even though the user gets a free copy.               layout="topleft"               left="125"               name="text taper2" -             top_pad="3" +             top_pad="7"               width="150">                  Taper              </text> @@ -2029,7 +2105,7 @@ even though the user gets a free copy.               layout="topleft"               left="125"               name="text radius delta" -             top_pad="2" +             top_pad="7"               width="78">                  Radius              </text> @@ -2157,6 +2233,19 @@ even though the user gets a free copy.  	<panel.string name="None">None</panel.string>  	<panel.string name="Prim">Prim</panel.string>  	<panel.string name="Convex Hull">Convex Hull</panel.string> +            <menu_button +              menu_filename="menu_copy_paste_features.xml" +              follows="top|left" +              height="15" +              image_disabled="ClipboardMenu_Disabled" +              image_selected="ClipboardMenu_Press" +              image_unselected="ClipboardMenu_Off" +              layout="topleft" +              left="258" +              top="8" +              name="clipboard_features_params_btn" +              tool_tip="Paste options" +              width="22"/>              <text               type="string"               length="1" @@ -2309,6 +2398,15 @@ even though the user gets a free copy.               name="FlexForceZ"               top_pad="4"               width="128" /> +            <view_border +             bevel_style="none" +             follows="top|left" +             height="0" +             layout="topleft" +             left="8" +             name="object_horizontal" +             top_pad="10" +             width="278" />              <check_box               height="16" @@ -2317,7 +2415,7 @@ even though the user gets a free copy.               left="10"               name="Light Checkbox Ctrl"               tool_tip="Causes object to emit light" -             top_pad="15" +             top_pad="8"               width="60" />              <color_swatch               can_apply_immediately="true" @@ -2344,6 +2442,19 @@ even though the user gets a free copy.              name="light texture control"              tool_tip="Click to choose a projection image (only has effect with deferred rendering enabled)"              width="32" /> +         <menu_button +              menu_filename="menu_copy_paste_light.xml" +              follows="top|left" +              height="15" +              image_disabled="ClipboardMenu_Disabled" +              image_selected="ClipboardMenu_Press" +              image_unselected="ClipboardMenu_Off" +              layout="topleft" +              left="258" +              top_delta="0" +              name="clipboard_light_params_btn" +              tool_tip="Paste options" +              width="22"/>            <spinner               follows="left|top"               height="19" @@ -2353,7 +2464,7 @@ even though the user gets a free copy.               layout="topleft"               left="10"               name="Light Intensity" -             top_pad="3" +             top_pad="26"               width="128" />            <spinner bottom_delta="0"                     decimal_digits="3" @@ -2575,7 +2686,7 @@ even though the user gets a free copy.               border_visible="true"               bevel_style="in"               follows="left|top|right" -             height="325" +             height="335"               layout="topleft"               left="10"               name="contents_inventory" diff --git a/indra/newview/skins/default/xui/en/floater_world_map.xml b/indra/newview/skins/default/xui/en/floater_world_map.xml index eb633e50e7..c965a4427c 100644 --- a/indra/newview/skins/default/xui/en/floater_world_map.xml +++ b/indra/newview/skins/default/xui/en/floater_world_map.xml @@ -14,6 +14,31 @@   single_instance="true"   title="WORLD MAP"   width="650"> +  <string +   name="collapse_icon" +   value="map_ui_collapse_icon.png"/> +  <string +   name="expand_icon" +   value="map_ui_expand_icon.png"/> +  <string +   name="collapse_tooltip" +   value="Hide map controls"/> +  <string +   name="expand_tooltip" +   value="Show map controls"/> +  <layout_stack +   animate="false" +   follows="all" +   name="floater_map_stack" +   tab_group="1" +   top="16" +   left="0" +   right="-1" +   bottom="-1"> +    <layout_panel +     name="map_lp" +     width="385" +     height="575">      <panel       filename="panel_world_map.xml"       follows="all" @@ -21,17 +46,48 @@       layout="topleft"       left="10"       name="objects_mapview" -     top="25" +     top="6"       width="375" /> +      <panel +       follows="top|right" +       height="30" +       layout="topleft" +       left_pad="-29" +       name="expand_btn_panel" +       background_visible="true" +       bg_opaque_color="FloaterFocusBackgroundColor" +       bg_alpha_color="FloaterDefaultBackgroundColor" +       background_opaque="true" +       tool_tip="Hide map controls" +       top="350" +       width="30"> +        <icon +         follows="top|right" +         height="16" +         width="16" +         top="7" +         left="7" +         scale_image="false" +         image_name="map_ui_collapse_icon.png" +         layout="topleft" +         mouse_opaque="true" +         name="expand_collapse_icon" +         tool_tip="Hide map controls" /> +      </panel> +      </layout_panel> +    <layout_panel +      height="575" +      width="265" +      expanded_min_dim="265" +      name="controls_lp">       <panel -     name="layout_panel_1" -     height="22" -     width="238" -     follows="right|top" -     top="25" -     left_pad="5" -  background_visible="true" -  bg_alpha_color="DkGray2"> +      name="layout_panel_1" +      height="22" +      width="238" +      follows="right|top" +      top="6"      +      background_visible="true" +      bg_alpha_color="DkGray2">      <text       text_color="White"       font="SansSerifLarge" @@ -43,17 +99,17 @@       layout="topleft"       left="15"       name="events_label" -     top="3"       width="215">          Legend      </text>      </panel> -<panel - follows="right|top" -  height="126" -  top_pad="0" -  width="238" -  name="layout_panel_2"> +    <panel +     follows="right|top" +     height="126" +     top_pad="4" +     width="238" +     left="1" +     name="layout_panel_2">  <button       follows="right|top"       height="22" @@ -691,4 +747,6 @@       show_text="false"       width="200" />       </panel> +      </layout_panel> +    </layout_stack>  </floater> diff --git a/indra/newview/skins/default/xui/en/menu_attachment_self.xml b/indra/newview/skins/default/xui/en/menu_attachment_self.xml index 26b1c86c53..3b91b9df7a 100644 --- a/indra/newview/skins/default/xui/en/menu_attachment_self.xml +++ b/indra/newview/skins/default/xui/en/menu_attachment_self.xml @@ -33,6 +33,13 @@           function="Object.EnableTouch"           name="EnableTouch"/>      </menu_item_call> +    <menu_item_call +     label="Show in inventory" +     layout="topleft" +     name="Show original"> +      <menu_item_call.on_click +       function="Object.ShowOriginal" /> +    </menu_item_call>      <menu_item_separator       layout="topleft" /> diff --git a/indra/newview/skins/default/xui/en/menu_copy_paste_color.xml b/indra/newview/skins/default/xui/en/menu_copy_paste_color.xml new file mode 100644 index 0000000000..4c12180daf --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_copy_paste_color.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + layout="topleft" + name="Copy Paste Color Menu"> +    <menu_item_call +     label="Copy" +     layout="topleft" +     name="params_copy" +     visible="true"> +       <on_click function="PanelFace.menuDoToSelected" parameter="color_copy" /> +    </menu_item_call> +    <menu_item_call +     label="Paste" +     layout="topleft" +     name="params_paste" +     visible="true"> +       <on_click function="PanelFace.menuDoToSelected" parameter="color_paste" /> +       <on_enable function="PanelFace.menuEnable" parameter="color_paste" /> +    </menu_item_call> +</toggleable_menu> + 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 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + layout="topleft" + name="Copy Paste Features Menu"> +    <menu_item_call +     label="Copy" +     layout="topleft" +     name="params_copy" +     visible="true"> +       <on_click function="PanelVolume.menuDoToSelected" parameter="features_copy" /> +    </menu_item_call> +    <menu_item_call +     label="Paste" +     layout="topleft" +     name="params_paste" +     visible="true"> +       <on_click function="PanelVolume.menuDoToSelected" parameter="features_paste" /> +       <on_enable function="PanelVolume.menuEnable" parameter="features_paste" /> +    </menu_item_call> +</toggleable_menu> + 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 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + layout="topleft" + name="Copy Paste Light Menu"> +    <menu_item_call +     label="Copy" +     layout="topleft" +     name="params_copy" +     visible="true"> +       <on_click function="PanelVolume.menuDoToSelected" parameter="light_copy" /> +    </menu_item_call> +    <menu_item_call +     label="Paste" +     layout="topleft" +     name="params_paste" +     visible="true"> +       <on_click function="PanelVolume.menuDoToSelected" parameter="light_paste" /> +       <on_enable function="PanelVolume.menuEnable" parameter="light_paste" /> +    </menu_item_call> +</toggleable_menu> + 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 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + layout="topleft" + name="Copy Paste Object Menu"> +    <menu_item_call +     label="Copy" +     layout="topleft" +     name="params_copy" +     visible="true"> +       <on_click function="PanelObject.menuDoToSelected" parameter="params_copy" /> +    </menu_item_call> +    <menu_item_call +     label="Paste" +     layout="topleft" +     name="params_paste" +     visible="true"> +       <on_click function="PanelObject.menuDoToSelected" parameter="params_paste" /> +       <on_enable function="PanelObject.menuEnable" parameter="params_paste" /> +    </menu_item_call> +</toggleable_menu> + 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 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + layout="topleft" + name="Copy Paste Position Menu"> +    <menu_item_call +     label="Copy all" +     layout="topleft" +     name="psr_copy" +     visible="true"> +       <on_click function="PanelObject.menuDoToSelected" parameter="psr_copy" /> +       <on_enable function="PanelObject.menuEnable" parameter="psr_copy" /> +    </menu_item_call> +    <menu_item_call +     label="Copy position" +     layout="topleft" +     name="pos_copy" +     visible="true"> +       <on_click function="PanelObject.menuDoToSelected" parameter="pos_copy" /> +    </menu_item_call> +    <menu_item_call +     label="Paste all" +     layout="topleft" +     name="psr_paste" +     visible="true"> +       <on_click function="PanelObject.menuDoToSelected" parameter="psr_paste" /> +       <on_enable function="PanelObject.menuEnable" parameter="psr_paste" /> +    </menu_item_call> +    <menu_item_call +     label="Paste position" +     layout="topleft" +     name="pos_paste" +     visible="true"> +       <on_click function="PanelObject.menuDoToSelected" parameter="pos_paste" /> +       <on_enable function="PanelObject.menuEnable" parameter="pos_paste" /> +    </menu_item_call> +</toggleable_menu> + 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 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + layout="topleft" + name="Copy Paste Rotation Menu"> +    <menu_item_call +     label="Copy all" +     layout="topleft" +     name="psr_copy" +     visible="true"> +       <on_click function="PanelObject.menuDoToSelected" parameter="psr_copy" /> +       <on_enable function="PanelObject.menuEnable" parameter="rot_paste" /> +    </menu_item_call> +    <menu_item_call +     label="Copy rotation" +     layout="topleft" +     name="rot_copy" +     visible="true"> +       <on_click function="PanelObject.menuDoToSelected" parameter="rot_copy" /> +    </menu_item_call> +    <menu_item_call +     label="Paste all" +     layout="topleft" +     name="psr_paste" +     visible="true"> +       <on_click function="PanelObject.menuDoToSelected" parameter="psr_paste" /> +       <on_enable function="PanelObject.menuEnable" parameter="psr_paste" /> +    </menu_item_call> +    <menu_item_call +     label="Paste rotation" +     layout="topleft" +     name="rot_paste" +     visible="true"> +       <on_click function="PanelObject.menuDoToSelected" parameter="rot_paste" /> +       <on_enable function="PanelObject.menuEnable" parameter="rot_paste" /> +    </menu_item_call> +</toggleable_menu> + 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 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + layout="topleft" + name="Copy Paste Size Menu"> +    <menu_item_call +     label="Copy all" +     layout="topleft" +     name="psr_copy" +     visible="true"> +       <on_click function="PanelObject.menuDoToSelected" parameter="psr_copy" /> +       <on_enable function="PanelObject.menuEnable" parameter="psr_copy" /> +    </menu_item_call> +    <menu_item_call +     label="Copy size" +     layout="topleft" +     name="size_copy" +     visible="true"> +       <on_click function="PanelObject.menuDoToSelected" parameter="size_copy" /> +    </menu_item_call> +    <menu_item_call +     label="Paste all" +     layout="topleft" +     name="psr_paste" +     visible="true"> +       <on_click function="PanelObject.menuDoToSelected" parameter="psr_paste" /> +       <on_enable function="PanelObject.menuEnable" parameter="psr_paste" /> +    </menu_item_call> +    <menu_item_call +     label="Paste size" +     layout="topleft" +     name="size_paste" +     visible="true"> +       <on_click function="PanelObject.menuDoToSelected" parameter="size_paste" /> +       <on_enable function="PanelObject.menuEnable" parameter="size_paste" /> +    </menu_item_call> +</toggleable_menu> + 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 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + layout="topleft" + name="Copy Paste Texture Menu"> +    <menu_item_call +     label="Copy" +     layout="topleft" +     name="params_copy" +     visible="true"> +       <on_click function="PanelFace.menuDoToSelected" parameter="texture_copy" /> +    </menu_item_call> +    <menu_item_call +     label="Paste" +     layout="topleft" +     name="params_paste" +     visible="true"> +       <on_click function="PanelFace.menuDoToSelected" parameter="texture_paste" /> +       <on_enable function="PanelFace.menuEnable" parameter="texture_paste" /> +    </menu_item_call> +</toggleable_menu> + diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml index 78ca170813..aa3d0ae071 100644 --- a/indra/newview/skins/default/xui/en/menu_inventory.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory.xml @@ -912,6 +912,25 @@                  function="Inventory.DoToSelected"                  parameter="apply_settings_parcel" />      </menu_item_call> +  <menu_item_separator +   layout="topleft" +   name="Subfolder Separator" /> +  <menu_item_call +   label="Create folder from selected" +   layout="topleft" +   name="New folder from selected"> +    <menu_item_call.on_click +     function="Inventory.DoToSelected" +     parameter="new_folder_from_selected" /> +  </menu_item_call> +  <menu_item_call +   label="Ungroup folder items" +   layout="topleft" +   name="Ungroup folder items"> +    <menu_item_call.on_click +     function="Inventory.DoToSelected" +     parameter="ungroup_folder_items" /> +  </menu_item_call>  	<menu_item_separator  	 layout="topleft"  	 name="Marketplace Separator" /> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index aa93601669..1ee0d9c64a 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -9040,6 +9040,29 @@ We cannot display a preview of this texture because it is no-copy and/or no-tran    <notification     icon="alertmodal.tga" +   name="FacePasteFailed" +   type="alertmodal"> +Paste failed. [REASON] +   <usetemplate +    name="okbutton" +    yestext="OK"/> +  </notification> + +  <notification +   icon="alertmodal.tga" +   name="FacePasteTexturePermissions" +   type="alertmodal"> +    You applied a texture with limited permissions, object will inherit permissions from texture. +    <usetemplate +     ignoretext="Paste: You applied a texture with limited permissions" +     name="notifyignore"/> +    <usetemplate +     name="okbutton" +     yestext="OK"/> +  </notification> + +  <notification +   icon="alertmodal.tga"     name="ConfirmLeaveCall"     type="alertmodal">  Are you sure you want to leave this call? @@ -11640,7 +11663,7 @@ This Region does not support environmental settings.    <notification     icon="alertmodal.tga" -   label="Save Outfit" +   label="Save Environmental Settings"     name="SaveSettingAs"     type="alertmodal">      <unique/> @@ -11819,4 +11842,38 @@ Unpacking: [UNPACK_TIME]s [USIZE]KB      </form>    </notification> +  <notification +   icon="alertmodal.tga" +   label="Create subfolder" +   name="CreateSubfolder" +   type="alertmodal"> +    <unique/> +    Name the new folder: +    <tag>confirm</tag> +    <form name="form"> +      <input name="message" type="text"> +        [DESC] +      </input> +      <button +       default="true" +       index="0" +       name="OK" +       text="OK"/> +      <button +       index="1" +       name="Cancel" +       text="Cancel"/> +    </form> +  </notification> +  <notification +   icon="alertmodal.tga" +   name="SameFolderRequired" +   type="alert"> +    Selected items must be in the same folder. +    <tag>fail</tag> +    <usetemplate +      name="okbutton" +      yestext="OK"/> +  </notification> +  </notifications> diff --git a/indra/newview/skins/default/xui/en/panel_tools_texture.xml b/indra/newview/skins/default/xui/en/panel_tools_texture.xml index 90f32ae452..0cbd7fe2dd 100644 --- a/indra/newview/skins/default/xui/en/panel_tools_texture.xml +++ b/indra/newview/skins/default/xui/en/panel_tools_texture.xml @@ -11,6 +11,36 @@           name="Texture"           top="0"           width="295"> +            <panel.string +             name="paste_error_face_selection_mismatch"> +              When multiple faces are copied, the target object must have the same number of faces selected. +            </panel.string> +            <panel.string +             name="paste_error_object_face_count_mismatch"> +              When all faces of an object are copied, the target object must have the same number of faces. +            </panel.string> +            <panel.string +             name="paste_error_inventory_not_found"> +              One or more texture not found in inventory. +            </panel.string> +            <panel.string +             name="paste_options"> +              Paste options +            </panel.string> + +            <menu_button +             menu_filename="menu_copy_paste_color.xml" +             follows="top|left" +             height="15" +             image_disabled="ClipboardMenu_Disabled" +             image_selected="ClipboardMenu_Press" +             image_unselected="ClipboardMenu_Off" +             layout="topleft" +             left="258" +             top="8" +             name="clipboard_color_params_btn" +             tool_tip="Paste options" +             width="22"/>              <text               type="string"               length="1" @@ -36,7 +66,7 @@               name="colorswatch"               tool_tip="Click to open color picker"               top="20" -             width="64" /> +             width="54" />              <text               type="string"               length="1" @@ -84,7 +114,7 @@               left_delta="0"               name="glow"               top_pad="4" -             width="80" /> +             width="77" />              <check_box               height="19"               label="Full Bright" @@ -93,13 +123,22 @@               name="checkbox fullbright"               top_pad="4"               width="81" /> +            <view_border +             bevel_style="none" +             follows="top|left" +             height="0" +             layout="topleft" +             left="8" +             name="object_horizontal" +             top_pad="4" +             width="278" />              <combo_box               height="23"               layout="topleft"               left="10"               name="combobox matmedia" -             top_pad="5" -             width="100"> +             top_pad="17" +             width="90">                  <combo_box.item                   label="Materials"                   name="Materials" @@ -113,7 +152,7 @@              control_name="ComboMaterialType"              height="50"              layout="topleft" -            left_pad="20" +            left_pad="5"              top_delta="-10"              width="150"              visible = "false" @@ -139,7 +178,20 @@                  layout="topleft"                  top_pad="1"                  value="2"/> -            </radio_group>  +            </radio_group> +            <menu_button +                menu_filename="menu_copy_paste_texture.xml" +                follows="top|left" +                height="15" +                image_disabled="ClipboardMenu_Disabled" +                image_selected="ClipboardMenu_Press" +                image_unselected="ClipboardMenu_Off" +                layout="topleft" +                left="258" +                top_delta="0" +                name="clipboard_texture_params_btn" +                tool_tip="Paste options" +                width="22"/>              <check_box               control_name="SyncMaterialSettings"               follows="top|left" @@ -150,7 +202,7 @@               left="8"               name="checkbox_sync_settings"               tool_tip="Adjust all maps repeats simultaneously" -             top_pad="-16" +             top_pad="19"               width="160" />              <texture_picker               can_apply_immediately="true" @@ -771,14 +823,14 @@               top_delta="16"               width="260" />  			<button -			 left="10" -			 top="222" +       follows="left|top" +       layout="topleft" +			 left="9" +			 top="204"  			 height="20"  			 label="Align"  			 label_selected="Align current texture layers" -			 layout="topleft"  			 name="button align textures" -			 top_delta="0"  			 tool_tip="Align current texture layers"  			 width="66" />              <web_browser diff --git a/indra/newview/skins/default/xui/en/sidepanel_task_info.xml b/indra/newview/skins/default/xui/en/sidepanel_task_info.xml index 8a3e18707f..1c9d750aa6 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_task_info.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_task_info.xml @@ -459,7 +459,7 @@          label="Price: L$"          label_width="73"				          width="150" -        min_val="1" +        min_val="0"          height="20"          max_val="999999999"          tool_tip="Object cost." /> | 
