summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/llmath/CMakeLists.txt3
-rw-r--r--indra/llmath/llvolume.cpp45
-rw-r--r--indra/llmath/llvolume.h4
-rw-r--r--indra/llmeshoptimizer/llmeshoptimizer.cpp215
-rw-r--r--indra/llmeshoptimizer/llmeshoptimizer.h81
-rw-r--r--indra/llprimitive/lldaeloader.cpp2
-rw-r--r--indra/llprimitive/llmodel.cpp8
-rw-r--r--indra/llprimitive/llmodel.h1
-rw-r--r--indra/llui/llbutton.cpp20
-rw-r--r--indra/llui/llbutton.h2
-rw-r--r--indra/llui/llprogressbar.cpp24
-rw-r--r--indra/llui/llspinctrl.cpp2
-rw-r--r--indra/newview/VIEWER_VERSION.txt2
-rw-r--r--indra/newview/llappviewer.cpp23
-rw-r--r--indra/newview/llconversationview.cpp47
-rw-r--r--indra/newview/llfloaterimsessiontab.cpp14
-rw-r--r--indra/newview/llfloatermodelpreview.cpp4
-rw-r--r--indra/newview/llfloatertools.cpp102
-rw-r--r--indra/newview/llfloaterworldmap.cpp19
-rw-r--r--indra/newview/llfloaterworldmap.h2
-rw-r--r--indra/newview/llinspectavatar.cpp2
-rw-r--r--indra/newview/llinventorybridge.cpp22
-rw-r--r--indra/newview/llinventoryfunctions.cpp155
-rw-r--r--indra/newview/llinventoryfunctions.h4
-rw-r--r--indra/newview/llinventorylistitem.cpp32
-rw-r--r--indra/newview/lllocalbitmaps.h2
-rw-r--r--indra/newview/llmodelpreview.cpp248
-rw-r--r--indra/newview/llmodelpreview.h14
-rw-r--r--indra/newview/llpanelblockedlist.cpp2
-rw-r--r--indra/newview/llpaneleditwearable.cpp3
-rw-r--r--indra/newview/llpanelface.cpp1159
-rw-r--r--indra/newview/llpanelface.h26
-rw-r--r--indra/newview/llpanelobject.cpp327
-rw-r--r--indra/newview/llpanelobject.h27
-rw-r--r--indra/newview/llpanelobjectinventory.cpp15
-rw-r--r--indra/newview/llpanelvolume.cpp294
-rw-r--r--indra/newview/llpanelvolume.h13
-rw-r--r--indra/newview/lltexturectrl.cpp61
-rw-r--r--indra/newview/lltexturectrl.h10
-rw-r--r--indra/newview/lltoastalertpanel.cpp2
-rw-r--r--indra/newview/lltooldraganddrop.cpp6
-rw-r--r--indra/newview/lltooldraganddrop.h3
-rw-r--r--indra/newview/llviewermenu.cpp27
-rw-r--r--indra/newview/llvieweroctree.cpp4
-rw-r--r--indra/newview/llviewerwindow.cpp1
-rw-r--r--indra/newview/llvoavatar.cpp7
-rw-r--r--indra/newview/llvovolume.cpp68
-rw-r--r--indra/newview/llvovolume.h6
-rw-r--r--indra/newview/pipeline.cpp2
-rw-r--r--indra/newview/skins/default/textures/icons/ClipboardMenu_Disabled.pngbin0 -> 231 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/ClipboardMenu_Off.pngbin0 -> 231 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/ClipboardMenu_Press.pngbin0 -> 224 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Disabled.pngbin0 -> 218 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Off.pngbin0 -> 217 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Press.pngbin0 -> 215 bytes
-rw-r--r--indra/newview/skins/default/textures/map_ui_collapse_icon.pngbin0 -> 300 bytes
-rw-r--r--indra/newview/skins/default/textures/map_ui_expand_icon.pngbin0 -> 284 bytes
-rw-r--r--indra/newview/skins/default/textures/textures.xml9
-rw-r--r--indra/newview/skins/default/xui/en/floater_tools.xml185
-rw-r--r--indra/newview/skins/default/xui/en/floater_world_map.xml90
-rw-r--r--indra/newview/skins/default/xui/en/menu_attachment_self.xml7
-rw-r--r--indra/newview/skins/default/xui/en/menu_copy_paste_color.xml21
-rw-r--r--indra/newview/skins/default/xui/en/menu_copy_paste_features.xml21
-rw-r--r--indra/newview/skins/default/xui/en/menu_copy_paste_light.xml21
-rw-r--r--indra/newview/skins/default/xui/en/menu_copy_paste_object.xml21
-rw-r--r--indra/newview/skins/default/xui/en/menu_copy_paste_pos.xml37
-rw-r--r--indra/newview/skins/default/xui/en/menu_copy_paste_rot.xml37
-rw-r--r--indra/newview/skins/default/xui/en/menu_copy_paste_size.xml37
-rw-r--r--indra/newview/skins/default/xui/en/menu_copy_paste_texture.xml21
-rw-r--r--indra/newview/skins/default/xui/en/menu_inventory.xml19
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml59
-rw-r--r--indra/newview/skins/default/xui/en/panel_tools_texture.xml74
-rw-r--r--indra/newview/skins/default/xui/en/sidepanel_task_info.xml2
73 files changed, 3353 insertions, 470 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 5099920f32..4a069b0f63 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
@@ -4952,6 +4953,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 c0b224b1ff..9697952f5b 100644
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -902,6 +902,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 68b29f01da..50f4a4306e 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -2563,7 +2563,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 b1d371a399..285c5f656b 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 1dfd7bfc86..bf67807aea 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -2717,19 +2717,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");
@@ -3087,7 +3082,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 b77341f806..b7c98c915c 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -741,7 +741,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:
@@ -1745,7 +1745,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 2c84cd1f93..09235d8fb2 100644
--- a/indra/newview/llfloaterworldmap.cpp
+++ b/indra/newview/llfloaterworldmap.cpp
@@ -45,6 +45,7 @@
//#include "llfirstuse.h"
#include "llfloaterreg.h" // getTypedInstance()
#include "llfocusmgr.h"
+#include "lliconctrl.h"
#include "llinventoryfunctions.h"
#include "llinventorymodel.h"
#include "llinventorymodelbackgroundfetch.h"
@@ -307,6 +308,8 @@ BOOL LLFloaterWorldMap::postBuild()
mCurZoomVal = log(LLWorldMapView::sMapScale/256.f)/log(2.f);
getChild<LLUICtrl>("zoom slider")->setValue(mCurZoomVal);
+
+ getChild<LLPanel>("expand_btn_panel")->setMouseDownCallback(boost::bind(&LLFloaterWorldMap::onExpandCollapseBtn, this));
setDefaultBtn(NULL);
@@ -1315,6 +1318,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 14a9c26fb9..fcb55e9666 100644
--- a/indra/newview/llfloaterworldmap.h
+++ b/indra/newview/llfloaterworldmap.h
@@ -130,6 +130,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 03c6996de6..7abe48709b 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -795,6 +795,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"));
@@ -4291,7 +4304,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 e67bd6468e..c3fbada9db 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -838,8 +838,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)
@@ -853,9 +855,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);
+ }
}
}
@@ -1243,8 +1283,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;
@@ -1279,20 +1320,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];
@@ -1303,10 +1345,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)
{
@@ -1316,38 +1390,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;
@@ -1378,7 +1457,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];
@@ -1401,19 +1480,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++;
@@ -1421,7 +1500,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
{
@@ -1491,18 +1570,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;
@@ -1510,14 +1588,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)
{
@@ -1527,25 +1631,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;
@@ -1556,10 +1660,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
@@ -1583,23 +1686,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)
@@ -1733,16 +1837,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;
+ }
}
}
@@ -1751,19 +1858,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))
{
@@ -1771,10 +1888,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)
{
@@ -1797,13 +1915,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)
@@ -1836,7 +1954,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;
}
}
@@ -1856,7 +1974,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()
@@ -3767,7 +3885,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 9e32215e6a..215f44357f 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;
@@ -225,13 +225,21 @@ private:
// Count amount of original models, excluding sub-models
static U32 countRootModels(LLModelLoader::model_list models);
+ typedef enum
+ {
+ MESH_OPTIMIZER_FULL,
+ MESH_OPTIMIZER_NO_NORMALS,
+ MESH_OPTIMIZER_NO_UVS,
+ MESH_OPTIMIZER_NO_TOPOLOGY,
+ } eSimplificationMode;
+
// Merges faces into single mesh, simplifies using mesh optimizer,
// then splits back into faces.
// Returns reached simplification ratio. -1 in case of a failure.
- F32 genMeshOptimizerPerModel(LLModel *base_model, LLModel *target_model, F32 indices_ratio, F32 error_threshold, bool sloppy);
+ F32 genMeshOptimizerPerModel(LLModel *base_model, LLModel *target_model, F32 indices_ratio, F32 error_threshold, eSimplificationMode simplification_mode);
// Simplifies specified face using mesh optimizer.
// Returns reached simplification ratio. -1 in case of a failure.
- F32 genMeshOptimizerPerFace(LLModel *base_model, LLModel *target_model, U32 face_idx, F32 indices_ratio, F32 error_threshold, bool sloppy);
+ F32 genMeshOptimizerPerFace(LLModel *base_model, LLModel *target_model, U32 face_idx, F32 indices_ratio, F32 error_threshold, eSimplificationMode simplification_mode);
protected:
friend class LLModelLoader;
diff --git a/indra/newview/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 ad3742157f..ec67a9cdc6 100644
--- a/indra/newview/llpanelface.cpp
+++ b/indra/newview/llpanelface.cpp
@@ -38,6 +38,7 @@
#include "llfontgl.h"
// project includes
+#include "llagent.h"
#include "llagentdata.h"
#include "llbutton.h"
#include "llcheckboxctrl.h"
@@ -45,9 +46,13 @@
#include "llcombobox.h"
#include "lldrawpoolbump.h"
#include "llface.h"
+#include "llinventoryfunctions.h"
+#include "llinventorymodel.h" // gInventory
+#include "llinventorymodelbackgroundfetch.h"
#include "lllineeditor.h"
#include "llmaterialmgr.h"
#include "llmediaentry.h"
+#include "llmenubutton.h"
#include "llnotificationsutil.h"
#include "llradiogroup.h"
#include "llresmgr.h"
@@ -57,6 +62,8 @@
#include "lltexturectrl.h"
#include "lltextureentry.h"
#include "lltooldraganddrop.h"
+#include "lltoolface.h"
+#include "lltoolmgr.h"
#include "lltrans.h"
#include "llui.h"
#include "llviewercontrol.h"
@@ -69,6 +76,18 @@
#include "llpluginclassmedia.h"
#include "llviewertexturelist.h"// Update sel manager as to which channel we're editing so it can reflect the correct overlay UI
+
+
+#include "llagent.h"
+#include "llfilesystem.h"
+#include "llviewerassetupload.h"
+#include "llviewermenufile.h"
+#include "llsd.h"
+#include "llsdutil.h"
+#include "llsdserialize.h"
+#include "llinventorymodel.h"
+
+
//
// Constant definitions for comboboxes
// Must match the commbobox definitions in panel_tools_texture.xml
@@ -302,6 +321,8 @@ BOOL LLPanelFace::postBuild()
mCtrlGlow->setCommitCallback(LLPanelFace::onCommitGlow, this);
}
+ mMenuClipboardColor = getChild<LLMenuButton>("clipboard_color_params_btn");
+ mMenuClipboardTexture = getChild<LLMenuButton>("clipboard_texture_params_btn");
clearCtrls();
@@ -312,7 +333,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));
}
@@ -322,6 +345,13 @@ LLPanelFace::~LLPanelFace()
}
+void LLPanelFace::draw()
+{
+ updateCopyTexButton();
+
+ LLPanel::draw();
+}
+
void LLPanelFace::sendTexture()
{
LLTextureCtrl* mTextureCtrl = getChild<LLTextureCtrl>("texture control");
@@ -1519,6 +1549,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();
@@ -1581,6 +1614,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;
@@ -2562,205 +2606,808 @@ void LLPanelFace::onAlignTexture(void* userdata)
self->alignTestureLayer();
}
-#include "llagent.h"
-#include "llfilesystem.h"
-#include "llviewerassetupload.h"
-#include "llviewermenufile.h"
-#include "llsd.h"
-#include "llsdutil.h"
-#include "llsdserialize.h"
-#include "llinventorymodel.h"
+enum EPasteMode
+{
+ PASTE_COLOR,
+ PASTE_TEXTURE
+};
-void LLPanelFace::onSaveMaterial(void* userdata)
+struct LLPanelFacePasteTexFunctor : public LLSelectedTEFunctor
{
- // DRTVWR-559, Q&D material picker - save to inventory goes here
- LL_DEBUGS("Material") << "saving render material to inventory" << LL_ENDL;
+ LLPanelFacePasteTexFunctor(LLPanelFace* panel, EPasteMode mode) :
+ mPanelFace(panel), mMode(mode) {}
- std::string name = "New Material";
+ 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;
+};
- LLSD material_data = llsd::map(
- "version", "1",
- "material", LLSD::emptyMap()
- );
+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;
+};
- // gen a new uuid for this asset
- LLTransactionID tid;
- tid.generate(); // timestamp-based randomization + uniquification
- LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+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;
+ }
+};
- material_data["material"] = renderMaterialToLLSD(new_asset_id, userdata);
- std::stringstream output;
- LLSDSerialize::toNotation(material_data, output);
+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;
+ }
- //S32 expected_upload_cost = 0;// LLAgentBenefitsMgr::current().getTextureUploadCost();
-
- std::string res_name = name;
- std::string res_desc = "Saved Material";
- //LLFolderType::EType folder_type = LLFolderType::FT_MATERIAL;
- //LLInventoryType::EType inv_type = LLInventoryType::IT_MATERIAL;
- U32 next_owner_perm = LLPermissions::DEFAULT.getMaskNextOwner();
+ if (mClipboardParams.has("color"))
+ {
+ mClipboardParams["color"].clear();
+ }
+ else
+ {
+ mClipboardParams["color"] = LLSD::emptyArray();
+ }
- LLUUID parent = gInventory.findCategoryUUIDForType(LLFolderType::FT_MATERIAL);
- const U8 subtype = NO_INV_SUBTYPE; // TODO maybe use AT_SETTINGS and LLSettingsType::ST_MATERIAL ?
+ std::map<LLUUID, LLUUID> asset_item_map;
- create_inventory_item(gAgent.getID(), gAgent.getSessionID(), parent, tid, res_name, res_desc,
- LLAssetType::AT_MATERIAL, LLInventoryType::IT_MATERIAL, subtype, next_owner_perm,
- new LLBoostFuncInventoryCallback([output=output.str()](LLUUID const & inv_item_id){
- // from reference in LLSettingsVOBase::createInventoryItem()/updateInventoryItem()
- LLResourceUploadInfo::ptr_t uploadInfo =
- std::make_shared<LLBufferedAssetUploadInfo>(
- inv_item_id,
- LLAssetType::AT_SETTINGS, // TODO switch to AT_MATERIAL
- output,
- [](LLUUID item_id, LLUUID new_asset_id, LLUUID new_item_id, LLSD response) {
- LL_INFOS("Material") << "inventory item uploaded. item: " << item_id << " asset: " << new_asset_id << " new_item_id: " << new_item_id << " response: " << response << LL_ENDL;
- LLSD params = llsd::map("ASSET_ID", new_asset_id);
- LLNotificationsUtil::add("MaterialCreated", params);
- });
+ // 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;
- const LLViewerRegion* region = gAgent.getRegion();
- if (region)
+ // 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))
{
- std::string agent_url(region->getCapability("UpdateSettingsAgentInventory"));
- if (agent_url.empty())
- {
- LL_ERRS() << "missing required agent inventory cap url" << LL_ENDL;
- }
- LLViewerAssetUpload::EnqueueInventoryUpload(agent_url, uploadInfo);
+ 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);
}
-// Fill an LLSD with data describing the current face's texture settings
-// TODO 2022-05 FUBAR there are both colliding and different data in LLPanelFace vs the TE. Also, neither one has the diffuse tex settings.
-//
-LLSD LLPanelFace::renderMaterialToLLSD(LLUUID uuid, void* userdata)
+void LLPanelFace::onPasteColor(LLViewerObject* objectp, S32 te)
{
- llassert(userdata != nullptr);
+ 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;
+ }
- LLSD sd;
+ LLTextureEntry* tep = objectp->getTE(te);
+ if (tep)
+ {
+ if (te_data.has("te"))
+ {
+ // Color / Alpha
+ if (te_data["te"].has("colors"))
+ {
+ LLColor4 color = tep->getColor();
- sd.insert("RenderMaterialUUID", LLSD(uuid));
+ LLColor4 clip_color;
+ clip_color.setValue(te_data["te"]["colors"]);
- // now pull same data from the selected TE (same but different. W T F?)
- LLMaterialPtr mat = nullptr;
- bool ident; // ?
- LLSelectedTEMaterial::getCurrent(mat, ident);
+ // Color
+ color.mV[VRED] = clip_color.mV[VRED];
+ color.mV[VGREEN] = clip_color.mV[VGREEN];
+ color.mV[VBLUE] = clip_color.mV[VBLUE];
- if (mat)
- {
- sd.insert("teMaterialID", LLSD(mat->getMaterialID()));
+ // Alpha
+ color.mV[VALPHA] = clip_color.mV[VALPHA];
- sd.insert("teNormalMap", LLSD(mat->getNormalID()));
- sd.insert("teNormalOffsetX", LLSD(mat->getNormalOffsetX()));
- sd.insert("teNormalOffsetY", LLSD(mat->getNormalOffsetY()));
- sd.insert("teNormalRepeatX", LLSD(mat->getNormalRepeatX()));
- sd.insert("teNormalRepeatY", LLSD(mat->getNormalRepeatY()));
- sd.insert("teNormalRotation", LLSD(mat->getNormalRotation()));
+ objectp->setTEColor(te, color);
+ }
- sd.insert("teSpecularMap", LLSD(mat->getSpecularID()));
- LLColor4U color = mat->getSpecularLightColor();
+ // Color/fullbright
+ if (te_data["te"].has("fullbright"))
+ {
+ objectp->setTEFullbright(te, te_data["te"]["fullbright"].asInteger());
+ }
- sd.insert("teSpecularColorR", LLSD(static_cast<S32>(color.mV[0])));
- sd.insert("teSpecularColorG", LLSD(static_cast<S32>(color.mV[1])));
- sd.insert("teSpecularColorB", LLSD(static_cast<S32>(color.mV[2])));
- sd.insert("teSpecularColorA", LLSD(static_cast<S32>(color.mV[3])));
- sd.insert("teSpecularExponent", LLSD(static_cast<S32>(mat->getSpecularLightExponent())));
- sd.insert("teSpecularOffsetX", LLSD(mat->getSpecularOffsetX()));
- sd.insert("teSpecularOffsetY", LLSD(mat->getSpecularOffsetY()));
- sd.insert("teSpecularRepeatX", LLSD(mat->getSpecularRepeatX()));
- sd.insert("teSpecularRepeatY", LLSD(mat->getSpecularRepeatY()));
- sd.insert("teSpecularRotation", LLSD(mat->getSpecularRotation()));
+ // Glow
+ if (te_data["te"].has("glow"))
+ {
+ objectp->setTEGlow(te, (F32)te_data["te"]["glow"].asReal());
+ }
+ }
+ }
+}
- sd.insert("teAlphaMode", LLSD(static_cast<S32>(mat->getDiffuseAlphaMode())));
- sd.insert("teAlphaCutoff", LLSD(static_cast<S32>(mat->getAlphaMaskCutoff())));
- sd.insert("teEnvIntensity", LLSD(static_cast<S32>(mat->getEnvironmentIntensity())));
- sd.insert("teShaderMask", LLSD(static_cast<S32>(mat->getShaderMask())));
+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
{
- // pull data from the LLPanelFace
- LLPanelFace* instance = static_cast<LLPanelFace*>(userdata);
- sd.insert("pfNormalMap", LLSD(instance->getCurrentNormalMap()));
- sd.insert("pfSpecularMap", LLSD(instance->getCurrentSpecularMap()));
- sd.insert("pfShininess", LLSD(static_cast<S32>(instance->getCurrentShininess())));
- sd.insert("pfBumpiness", LLSD(static_cast<S32>(instance->getCurrentBumpiness())));
- sd.insert("pfAlphaMode", LLSD(static_cast<S32>(instance->getCurrentDiffuseAlphaMode())));
- sd.insert("pfAlphaCutoff", LLSD(static_cast<S32>(instance->getCurrentAlphaMaskCutoff())));
- sd.insert("pfEnvIntensity", LLSD(static_cast<S32>(instance->getCurrentEnvIntensity())));
- sd.insert("pfGlossiness", LLSD(static_cast<S32>(instance->getCurrentGlossiness())));
- sd.insert("pfNormalRotation", LLSD(instance->getCurrentBumpyRot()));
- sd.insert("pfNormalScaleU", LLSD(instance->getCurrentBumpyScaleU()));
- sd.insert("pfNormalScaleV", LLSD(instance->getCurrentBumpyScaleV()));
- sd.insert("pfNormalOffsetU", LLSD(instance->getCurrentBumpyOffsetU()));
- sd.insert("pfNormalOffsetV", LLSD(instance->getCurrentBumpyOffsetV()));
- sd.insert("pfSpecularRotation", LLSD(instance->getCurrentShinyRot()));
- sd.insert("pfSpecularScaleU", LLSD(instance->getCurrentShinyScaleU()));
- sd.insert("pfSpecularScaleV", LLSD(instance->getCurrentShinyScaleV()));
- sd.insert("pfSpecularOffsetU", LLSD(instance->getCurrentShinyOffsetU()));
- sd.insert("pfSpecularOffsetV", LLSD(instance->getCurrentShinyOffsetV()));
- sd.insert("pfMaterialID", LLSD(instance->getCurrentMaterialID()));
+ mClipboardParams["texture"] = LLSD::emptyArray();
}
- return sd;
+ 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);
+ }
+ }
+ }
}
-// Take the individual texture settings from the material and apply to current face & TE
-void LLPanelFace::applyMaterialUUID(LLUUID uuid, void* userdata)
+void LLPanelFace::onPasteTexture()
{
- llassert(userdata != nullptr);
- //LLPanelFace* instance = static_cast<LLPanelFace*>(userdata);
-
- LLFileSystem material_file(uuid, LLAssetType::AT_MATERIAL, LLFileSystem::READ);
- S32 bufsize = material_file.getSize();
- llassert(bufsize > 0);
- std::vector<U8> buffer(bufsize);
- material_file.read(&buffer[0], bufsize);
- std::istringstream input(std::string(buffer.begin(), buffer.end())); // TODO - extend LLFileSystem to expose iostream interface
- LLSD matSD;
+ if (!mClipboardParams.has("texture"))
+ {
+ return;
+ }
- LLSDSerialize::fromNotation(matSD, input, bufsize);
+ 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;
+ }
- LL_INFOS() << "dump matSD: " << matSD << LL_ENDL;
+ 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;
- // strip off the versioning wrapper for now
- matSD = matSD["material"];
+ if (face_selection_mode)
+ {
+ compare_tes = 0;
+ for (S32 te = 0; te < num_tes; ++te)
+ {
+ if (node->isTESelected(te))
+ {
+ compare_tes++;
+ }
+ }
+ }
- // wrong, oops. llassert(uuid == matSD.get("RenderMaterialUUID").asUUID()); // if not, whoo boy
+ // 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;
+ }
- LLMaterialPtr mat = nullptr;
- bool ident; // ?
- LLSelectedTEMaterial::getCurrent(mat, ident);
+ 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;
+ }
+ }
+ }
+ }
- mat->setMaterialID(matSD.get("teMaterialID").asUUID());
+ if (!full_perm_object)
+ {
+ LLNotificationsUtil::add("FacePasteTexturePermissions");
+ }
- mat->setNormalID(matSD.get("teNormalMap").asUUID());
- mat->setNormalOffsetX(matSD.get("teNormalOffsetX").asReal());
- mat->setNormalOffsetY(matSD.get("teNormalOffsetY").asReal());
- mat->setNormalRepeatX(matSD.get("teNormalRepeatX").asReal());
- mat->setNormalRepeatY(matSD.get("teNormalRepeatY").asReal());
- mat->setNormalRotation(matSD.get("teNormalRotation").asReal());
+ LLObjectSelectionHandle selected_objects = LLSelectMgr::getInstance()->getSelection();
- mat->setSpecularID(matSD.get("teSpecularMap").asUUID());
- LLColor4U color;
- color.mV[0] = static_cast<U8>(matSD.get("teSecularColorR").asInteger());
- color.mV[1] = static_cast<U8>(matSD.get("teSecularColorG").asInteger());
- color.mV[2] = static_cast<U8>(matSD.get("teSecularColorB").asInteger());
- color.mV[3] = static_cast<U8>(matSD.get("teSecularColorA").asInteger());
- mat->setSpecularLightColor(color);
- mat->setSpecularLightExponent(static_cast<U8>(matSD.get("teSpecularExponent").asInteger()));
- mat->setSpecularOffsetX(matSD.get("teSpecularOffsetX").asReal());
- mat->setSpecularOffsetY(matSD.get("teSpecularOffsetY").asReal());
- mat->setSpecularRepeatX(matSD.get("teSpecularRepeatX").asReal());
- mat->setSpecularRepeatY(matSD.get("teSpecularRepeatY").asReal());
- mat->setSpecularRotation(matSD.get("teSpecularRotation").asReal());
+ LLPanelFacePasteTexFunctor paste_func(this, PASTE_TEXTURE);
+ selected_objects->applyToTEs(&paste_func);
- mat->setDiffuseAlphaMode(static_cast<U8>(matSD.get("teAlphaMode").asInteger()));
- mat->setAlphaMaskCutoff(static_cast<U8>(matSD.get("teAlphaCutoff").asInteger()));
- mat->setEnvironmentIntensity(static_cast<U8>(matSD.get("teEnvIntensity").asInteger()));
- //mat->setShaderMask(static_cast<U32>(matSD.get(teShaderMask").asInteger());
+ 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;
}
@@ -3049,3 +3696,197 @@ void LLPanelFace::LLSelectedTE::getMaxDiffuseRepeats(F32& repeats, bool& identic
} max_diff_repeats_func;
identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &max_diff_repeats_func, repeats );
}
+
+
+void LLPanelFace::onSaveMaterial(void* userdata)
+{
+ // DRTVWR-559, Q&D material picker - save to inventory goes here
+ LL_DEBUGS("Material") << "saving render material to inventory" << LL_ENDL;
+
+ std::string name = "New Material";
+
+ LLSD material_data = llsd::map(
+ "version", "1",
+ "material", LLSD::emptyMap()
+ );
+
+ // gen a new uuid for this asset
+ LLTransactionID tid;
+ tid.generate(); // timestamp-based randomization + uniquification
+ LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ material_data["material"] = renderMaterialToLLSD(new_asset_id, userdata);
+ std::stringstream output;
+ LLSDSerialize::toNotation(material_data, output);
+
+ //S32 expected_upload_cost = 0;// LLAgentBenefitsMgr::current().getTextureUploadCost();
+
+ std::string res_name = name;
+ std::string res_desc = "Saved Material";
+ //LLFolderType::EType folder_type = LLFolderType::FT_MATERIAL;
+ //LLInventoryType::EType inv_type = LLInventoryType::IT_MATERIAL;
+ U32 next_owner_perm = LLPermissions::DEFAULT.getMaskNextOwner();
+
+ LLUUID parent = gInventory.findCategoryUUIDForType(LLFolderType::FT_MATERIAL);
+ const U8 subtype = NO_INV_SUBTYPE; // TODO maybe use AT_SETTINGS and LLSettingsType::ST_MATERIAL ?
+
+ create_inventory_item(gAgent.getID(), gAgent.getSessionID(), parent, tid, res_name, res_desc,
+ LLAssetType::AT_MATERIAL, LLInventoryType::IT_MATERIAL, subtype, next_owner_perm,
+ new LLBoostFuncInventoryCallback([output=output.str()](LLUUID const & inv_item_id){
+ // from reference in LLSettingsVOBase::createInventoryItem()/updateInventoryItem()
+ LLResourceUploadInfo::ptr_t uploadInfo =
+ std::make_shared<LLBufferedAssetUploadInfo>(
+ inv_item_id,
+ LLAssetType::AT_SETTINGS, // TODO switch to AT_MATERIAL
+ output,
+ [](LLUUID item_id, LLUUID new_asset_id, LLUUID new_item_id, LLSD response) {
+ LL_INFOS("Material") << "inventory item uploaded. item: " << item_id << " asset: " << new_asset_id << " new_item_id: " << new_item_id << " response: " << response << LL_ENDL;
+ LLSD params = llsd::map("ASSET_ID", new_asset_id);
+ LLNotificationsUtil::add("MaterialCreated", params);
+ });
+
+ const LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ std::string agent_url(region->getCapability("UpdateSettingsAgentInventory"));
+ if (agent_url.empty())
+ {
+ LL_ERRS() << "missing required agent inventory cap url" << LL_ENDL;
+ }
+ LLViewerAssetUpload::EnqueueInventoryUpload(agent_url, uploadInfo);
+ }
+ })
+ );
+
+}
+
+// Fill an LLSD with data describing the current face's texture settings
+// TODO 2022-05 FUBAR there are both colliding and different data in LLPanelFace vs the TE. Also, neither one has the diffuse tex settings.
+//
+LLSD LLPanelFace::renderMaterialToLLSD(LLUUID uuid, void* userdata)
+{
+ llassert(userdata != nullptr);
+
+ LLSD sd;
+
+ sd.insert("RenderMaterialUUID", LLSD(uuid));
+
+ // now pull same data from the selected TE (same but different. W T F?)
+ LLMaterialPtr mat = nullptr;
+ bool ident; // ?
+ LLSelectedTEMaterial::getCurrent(mat, ident);
+
+ if (mat)
+ {
+ sd.insert("teMaterialID", LLSD(mat->getMaterialID()));
+
+ sd.insert("teNormalMap", LLSD(mat->getNormalID()));
+ sd.insert("teNormalOffsetX", LLSD(mat->getNormalOffsetX()));
+ sd.insert("teNormalOffsetY", LLSD(mat->getNormalOffsetY()));
+ sd.insert("teNormalRepeatX", LLSD(mat->getNormalRepeatX()));
+ sd.insert("teNormalRepeatY", LLSD(mat->getNormalRepeatY()));
+ sd.insert("teNormalRotation", LLSD(mat->getNormalRotation()));
+
+ sd.insert("teSpecularMap", LLSD(mat->getSpecularID()));
+ LLColor4U color = mat->getSpecularLightColor();
+
+ sd.insert("teSpecularColorR", LLSD(static_cast<S32>(color.mV[0])));
+ sd.insert("teSpecularColorG", LLSD(static_cast<S32>(color.mV[1])));
+ sd.insert("teSpecularColorB", LLSD(static_cast<S32>(color.mV[2])));
+ sd.insert("teSpecularColorA", LLSD(static_cast<S32>(color.mV[3])));
+ sd.insert("teSpecularExponent", LLSD(static_cast<S32>(mat->getSpecularLightExponent())));
+ sd.insert("teSpecularOffsetX", LLSD(mat->getSpecularOffsetX()));
+ sd.insert("teSpecularOffsetY", LLSD(mat->getSpecularOffsetY()));
+ sd.insert("teSpecularRepeatX", LLSD(mat->getSpecularRepeatX()));
+ sd.insert("teSpecularRepeatY", LLSD(mat->getSpecularRepeatY()));
+ sd.insert("teSpecularRotation", LLSD(mat->getSpecularRotation()));
+
+ sd.insert("teAlphaMode", LLSD(static_cast<S32>(mat->getDiffuseAlphaMode())));
+ sd.insert("teAlphaCutoff", LLSD(static_cast<S32>(mat->getAlphaMaskCutoff())));
+ sd.insert("teEnvIntensity", LLSD(static_cast<S32>(mat->getEnvironmentIntensity())));
+ sd.insert("teShaderMask", LLSD(static_cast<S32>(mat->getShaderMask())));
+ }
+ else
+ {
+ // pull data from the LLPanelFace
+ LLPanelFace* instance = static_cast<LLPanelFace*>(userdata);
+ sd.insert("pfNormalMap", LLSD(instance->getCurrentNormalMap()));
+ sd.insert("pfSpecularMap", LLSD(instance->getCurrentSpecularMap()));
+ sd.insert("pfShininess", LLSD(static_cast<S32>(instance->getCurrentShininess())));
+ sd.insert("pfBumpiness", LLSD(static_cast<S32>(instance->getCurrentBumpiness())));
+ sd.insert("pfAlphaMode", LLSD(static_cast<S32>(instance->getCurrentDiffuseAlphaMode())));
+ sd.insert("pfAlphaCutoff", LLSD(static_cast<S32>(instance->getCurrentAlphaMaskCutoff())));
+ sd.insert("pfEnvIntensity", LLSD(static_cast<S32>(instance->getCurrentEnvIntensity())));
+ sd.insert("pfGlossiness", LLSD(static_cast<S32>(instance->getCurrentGlossiness())));
+ sd.insert("pfNormalRotation", LLSD(instance->getCurrentBumpyRot()));
+ sd.insert("pfNormalScaleU", LLSD(instance->getCurrentBumpyScaleU()));
+ sd.insert("pfNormalScaleV", LLSD(instance->getCurrentBumpyScaleV()));
+ sd.insert("pfNormalOffsetU", LLSD(instance->getCurrentBumpyOffsetU()));
+ sd.insert("pfNormalOffsetV", LLSD(instance->getCurrentBumpyOffsetV()));
+ sd.insert("pfSpecularRotation", LLSD(instance->getCurrentShinyRot()));
+ sd.insert("pfSpecularScaleU", LLSD(instance->getCurrentShinyScaleU()));
+ sd.insert("pfSpecularScaleV", LLSD(instance->getCurrentShinyScaleV()));
+ sd.insert("pfSpecularOffsetU", LLSD(instance->getCurrentShinyOffsetU()));
+ sd.insert("pfSpecularOffsetV", LLSD(instance->getCurrentShinyOffsetV()));
+ sd.insert("pfMaterialID", LLSD(instance->getCurrentMaterialID()));
+ }
+
+ return sd;
+}
+
+// Take the individual texture settings from the material and apply to current face & TE
+void LLPanelFace::applyMaterialUUID(LLUUID uuid, void* userdata)
+{
+ llassert(userdata != nullptr);
+ //LLPanelFace* instance = static_cast<LLPanelFace*>(userdata);
+
+ LLFileSystem material_file(uuid, LLAssetType::AT_MATERIAL, LLFileSystem::READ);
+ S32 bufsize = material_file.getSize();
+ llassert(bufsize > 0);
+ std::vector<U8> buffer(bufsize);
+ material_file.read(&buffer[0], bufsize);
+ std::istringstream input(std::string(buffer.begin(), buffer.end())); // TODO - extend LLFileSystem to expose iostream interface
+ LLSD matSD;
+
+ LLSDSerialize::fromNotation(matSD, input, bufsize);
+
+ LL_INFOS() << "dump matSD: " << matSD << LL_ENDL;
+
+ // strip off the versioning wrapper for now
+ matSD = matSD["material"];
+
+ // wrong, oops. llassert(uuid == matSD.get("RenderMaterialUUID").asUUID()); // if not, whoo boy
+
+ LLMaterialPtr mat = nullptr;
+ bool ident; // ?
+ LLSelectedTEMaterial::getCurrent(mat, ident);
+
+ mat->setMaterialID(matSD.get("teMaterialID").asUUID());
+
+ mat->setNormalID(matSD.get("teNormalMap").asUUID());
+ mat->setNormalOffsetX(matSD.get("teNormalOffsetX").asReal());
+ mat->setNormalOffsetY(matSD.get("teNormalOffsetY").asReal());
+ mat->setNormalRepeatX(matSD.get("teNormalRepeatX").asReal());
+ mat->setNormalRepeatY(matSD.get("teNormalRepeatY").asReal());
+ mat->setNormalRotation(matSD.get("teNormalRotation").asReal());
+
+ mat->setSpecularID(matSD.get("teSpecularMap").asUUID());
+ LLColor4U color;
+ color.mV[0] = static_cast<U8>(matSD.get("teSecularColorR").asInteger());
+ color.mV[1] = static_cast<U8>(matSD.get("teSecularColorG").asInteger());
+ color.mV[2] = static_cast<U8>(matSD.get("teSecularColorB").asInteger());
+ color.mV[3] = static_cast<U8>(matSD.get("teSecularColorA").asInteger());
+ mat->setSpecularLightColor(color);
+ mat->setSpecularLightExponent(static_cast<U8>(matSD.get("teSpecularExponent").asInteger()));
+ mat->setSpecularOffsetX(matSD.get("teSpecularOffsetX").asReal());
+ mat->setSpecularOffsetY(matSD.get("teSpecularOffsetY").asReal());
+ mat->setSpecularRepeatX(matSD.get("teSpecularRepeatX").asReal());
+ mat->setSpecularRepeatY(matSD.get("teSpecularRepeatY").asReal());
+ mat->setSpecularRotation(matSD.get("teSpecularRotation").asReal());
+
+ mat->setDiffuseAlphaMode(static_cast<U8>(matSD.get("teAlphaMode").asInteger()));
+ mat->setAlphaMaskCutoff(static_cast<U8>(matSD.get("teAlphaCutoff").asInteger()));
+ mat->setEnvironmentIntensity(static_cast<U8>(matSD.get("teEnvIntensity").asInteger()));
+ //mat->setShaderMask(static_cast<U32>(matSD.get(teShaderMask").asInteger());
+}
+
diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h
index a8ecf8fdf2..08dc619402 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);
@@ -210,6 +215,18 @@ protected:
static LLSD renderMaterialToLLSD(LLUUID uuid, void* userdata);
static void applyMaterialUUID(LLUUID uuid, 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);
@@ -406,7 +423,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;
@@ -421,7 +441,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 998cffef4a..2faa3a7137 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;
@@ -1578,6 +1589,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 956539cd98..5c93271446 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"
@@ -177,6 +178,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");
@@ -217,6 +221,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));
}
@@ -455,7 +461,6 @@ void LLPanelVolume::getState( )
gAgentAvatarp->updateMeshVisibility();
}
}
-
// Flexible properties
BOOL is_flexible = volobjp && volobjp->isFlexible();
@@ -610,6 +615,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
@@ -896,6 +904,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 16d9ac292d..62a6d01b21 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;
@@ -79,6 +80,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();
@@ -126,6 +134,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 ee83d9eb57..e8ff14daac 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 1475c8c6fc..a234124c08 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 55e8a3b98b..ab54e2afc6 100644
--- a/indra/newview/lltooldraganddrop.cpp
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -1160,7 +1160,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)
@@ -1184,7 +1185,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 2f6423080e..cfdbd931ce 100644
--- a/indra/newview/lltooldraganddrop.h
+++ b/indra/newview/lltooldraganddrop.h
@@ -246,7 +246,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 34ce35ddeb..69347ae4ce 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -2724,6 +2724,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)
@@ -9530,6 +9556,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 0a44664f17..e102c917ee 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 4ec5c999ac..e55ee21414 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -4621,7 +4621,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 4fa9f05c09..d654c470e2 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -232,6 +232,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;
@@ -327,6 +328,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);
@@ -550,9 +554,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()
{
@@ -1677,7 +1703,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;
@@ -1753,20 +1779,9 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global)
}
}
- bool rigged = false;
-
- if (!isAnimatedObject())
- {
- rigged = isRiggedMesh() && isAttachment();
- }
- else
- {
- rigged = isRiggedMesh() && getControlAvatar() && getControlAvatar()->mPlaying;
- }
-
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;
@@ -1928,7 +1943,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;
@@ -1960,6 +1975,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())
@@ -2019,8 +2037,6 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
group->dirtyMesh();
}
- BOOL compiled = FALSE;
-
updateRelativeXform();
if (mDrawable.isNull()) // Not sure why this is happening, but it is...
@@ -2028,49 +2044,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 db586fd741..391351c13e 100644
--- a/indra/newview/llvovolume.h
+++ b/indra/newview/llvovolume.h
@@ -236,7 +236,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) override;
virtual F32 getBinRadius() override;
@@ -403,13 +403,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:
@@ -441,6 +442,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 b23c1fe741..ee2dc82505 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -3976,7 +3976,7 @@ void LLPipeline::postSort(LLCamera& camera)
}
LL_PUSH_CALLSTACKS();
// If managing your telehub, draw beacons at telehub and currently selected spawnpoint.
- if (LLFloaterTelehub::renderBeacons())
+ if (LLFloaterTelehub::renderBeacons() && !sShadowRender)
{
LLFloaterTelehub::addBeacons();
}
diff --git a/indra/newview/skins/default/textures/icons/ClipboardMenu_Disabled.png b/indra/newview/skins/default/textures/icons/ClipboardMenu_Disabled.png
new file mode 100644
index 0000000000..9a81c5f94b
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/ClipboardMenu_Disabled.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/ClipboardMenu_Off.png b/indra/newview/skins/default/textures/icons/ClipboardMenu_Off.png
new file mode 100644
index 0000000000..88012cf8d1
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/ClipboardMenu_Off.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/ClipboardMenu_Press.png b/indra/newview/skins/default/textures/icons/ClipboardMenu_Press.png
new file mode 100644
index 0000000000..ab02e7d42d
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/ClipboardMenu_Press.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Disabled.png b/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Disabled.png
new file mode 100644
index 0000000000..63b4bd2127
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Disabled.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Off.png b/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Off.png
new file mode 100644
index 0000000000..4200182b0c
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Off.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Press.png b/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Press.png
new file mode 100644
index 0000000000..e12887f489
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/ClipboardSmallMenu_Press.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/map_ui_collapse_icon.png b/indra/newview/skins/default/textures/map_ui_collapse_icon.png
new file mode 100644
index 0000000000..e4de49d4af
--- /dev/null
+++ b/indra/newview/skins/default/textures/map_ui_collapse_icon.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/map_ui_expand_icon.png b/indra/newview/skins/default/textures/map_ui_expand_icon.png
new file mode 100644
index 0000000000..08734b4cc0
--- /dev/null
+++ b/indra/newview/skins/default/textures/map_ui_expand_icon.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index 84ca634600..f18dff5b22 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -448,6 +448,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" />
@@ -804,6 +811,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 449bf8fa3a..3471d4b7b1 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="640"
+ 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="460"
+ 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="10"
+ 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_delta="32"
+ top_pad="26"
width="128" />
<spinner bottom_delta="0"
decimal_digits="3"
@@ -2638,7 +2749,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 83407069d2..45e11fc836 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"
@@ -690,4 +746,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 67bcbe020b..c7c1e1d75a 100644
--- a/indra/newview/skins/default/xui/en/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/en/menu_inventory.xml
@@ -923,6 +923,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 ae4b0538d8..24a934fbb8 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -9122,6 +9122,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?
@@ -11722,7 +11745,7 @@ This Region does not support environmental settings.
<notification
icon="alertmodal.tga"
- label="Save Outfit"
+ label="Save Environmental Settings"
name="SaveSettingAs"
type="alertmodal">
<unique/>
@@ -11901,6 +11924,40 @@ 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>
+
<notification
icon="notifytip.tga"
name="MaterialCreated"
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 85ee736176..c8f5a66661 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="257"
+ 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." />