summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/CMakeLists.txt3
-rw-r--r--indra/newview/VIEWER_VERSION.txt2
-rw-r--r--indra/newview/app_settings/settings.xml59
-rw-r--r--indra/newview/gltf/asset.cpp198
-rw-r--r--indra/newview/gltf/asset.h13
-rw-r--r--indra/newview/gltf/buffer_util.h11
-rw-r--r--indra/newview/gltf/llgltfloader.cpp1819
-rw-r--r--indra/newview/gltf/llgltfloader.h216
-rw-r--r--indra/newview/gltfscenemanager.cpp2
-rw-r--r--indra/newview/llagent.cpp13
-rw-r--r--indra/newview/llagentcamera.cpp10
-rw-r--r--indra/newview/llappviewer.cpp58
-rw-r--r--indra/newview/llappviewer.h7
-rw-r--r--indra/newview/llappviewerwin32.cpp23
-rw-r--r--indra/newview/llappviewerwin32.h1
-rw-r--r--indra/newview/llfilepicker.cpp6
-rw-r--r--indra/newview/llfloaterbvhpreview.cpp4
-rw-r--r--indra/newview/llfloaterbvhpreview.h2
-rw-r--r--indra/newview/llfloatermodelpreview.cpp5
-rw-r--r--indra/newview/llfloateruipreview.cpp5
-rwxr-xr-xindra/newview/llfloaterworldmap.cpp257
-rw-r--r--indra/newview/llfloaterworldmap.h14
-rw-r--r--indra/newview/llhudtext.cpp4
-rw-r--r--indra/newview/llinventoryfilter.cpp6
-rw-r--r--indra/newview/llinventoryfilter.h2
-rw-r--r--indra/newview/llinventoryfunctions.cpp4
-rw-r--r--indra/newview/llmaterialeditor.cpp48
-rw-r--r--indra/newview/llmeshrepository.cpp34
-rw-r--r--indra/newview/llmeshrepository.h17
-rw-r--r--indra/newview/llmodelpreview.cpp111
-rw-r--r--indra/newview/llpanelface.cpp286
-rw-r--r--indra/newview/llpanelface.h6
-rw-r--r--indra/newview/llpanelgroupbulk.cpp62
-rw-r--r--indra/newview/llpanelgroupbulkimpl.h4
-rw-r--r--indra/newview/llpanelmaininventory.cpp248
-rw-r--r--indra/newview/llpanelpermissions.cpp1
-rw-r--r--indra/newview/llpanelprofilepicks.cpp42
-rw-r--r--indra/newview/llpanelprofilepicks.h10
-rw-r--r--indra/newview/llreflectionmap.cpp2
-rw-r--r--indra/newview/llreflectionmap.h2
-rw-r--r--indra/newview/llreflectionmapmanager.cpp2
-rw-r--r--indra/newview/llskinningutil.cpp9
-rw-r--r--indra/newview/llsky.h1
-rw-r--r--indra/newview/llsprite.h2
-rw-r--r--indra/newview/llsurface.h1
-rw-r--r--indra/newview/lltoolpie.cpp141
-rw-r--r--indra/newview/lltoolpie.h16
-rw-r--r--indra/newview/llviewerdisplay.cpp63
-rw-r--r--indra/newview/llviewermenu.cpp16
-rw-r--r--indra/newview/llviewermenufile.cpp2
-rwxr-xr-xindra/newview/llviewerparceloverlay.cpp74
-rw-r--r--indra/newview/llviewerparceloverlay.h13
-rwxr-xr-xindra/newview/llviewerregion.cpp1
-rw-r--r--indra/newview/llviewerregion.h1
-rw-r--r--indra/newview/llviewerstats.cpp106
-rw-r--r--indra/newview/llviewerstats.h11
-rw-r--r--indra/newview/llviewertexturelist.cpp16
-rw-r--r--indra/newview/llviewerwindow.cpp16
-rw-r--r--indra/newview/llvoavatar.cpp8
-rw-r--r--indra/newview/llvoavatar.h2
-rw-r--r--indra/newview/llvoavatarself.cpp6
-rw-r--r--indra/newview/llvoavatarself.h2
-rw-r--r--indra/newview/llvovolume.cpp18
-rw-r--r--indra/newview/llwatchdog.cpp34
-rw-r--r--indra/newview/llwatchdog.h12
-rw-r--r--indra/newview/skins/default/xui/en/floater_model_preview.xml27
-rw-r--r--indra/newview/skins/default/xui/en/floater_stats.xml45
-rw-r--r--indra/newview/skins/default/xui/en/floater_world_map.xml18
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml10
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml5
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_advanced.xml27
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_sound.xml286
-rw-r--r--indra/newview/skins/default/xui/en/panel_profile_pick.xml20
-rw-r--r--indra/newview/skins/default/xui/en/panel_tools_texture.xml13
74 files changed, 3803 insertions, 838 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 40e90a4141..f6c41522c8 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -87,6 +87,7 @@ set(viewer_SOURCE_FILES
gltf/accessor.cpp
gltf/primitive.cpp
gltf/animation.cpp
+ gltf/llgltfloader.cpp
groupchatlistener.cpp
llaccountingcostmanager.cpp
llaisapi.cpp
@@ -768,6 +769,7 @@ set(viewer_HEADER_FILES
gltf/buffer_util.h
gltf/primitive.h
gltf/animation.h
+ gltf/llgltfloader.h
llaccountingcost.h
llaccountingcostmanager.h
llaisapi.h
@@ -2183,6 +2185,7 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES GNU)
set_source_files_properties(llviewerstats.cpp PROPERTIES
COMPILE_FLAGS -Wno-unused-value)
set_source_files_properties(
+ gltf/llgltfloader.cpp
llaisapi.cpp
llconversationlog.cpp
llconversationmodel.cpp
diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index 6329380f96..0ee843cc60 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-7.1.15
+7.2.0
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 315018bbee..13826358b5 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -2,7 +2,7 @@
<llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="llsd.xsd">
<map>
- <key>ImporterDebug</key>
+ <key>ImporterDebugVerboseLogging</key>
<map>
<key>Comment</key>
<string>Enable debug output to more precisely identify sources of import errors. Warning: the output can slow down import on many machines.</string>
@@ -27,7 +27,7 @@
<key>ImporterModelLimit</key>
<map>
<key>Comment</key>
- <string>Limits amount of importer generated models for dae files</string>
+ <string>Limits amount of importer generated (when over 8 faces) models for dae and gltf files</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@@ -35,6 +35,17 @@
<key>Value</key>
<integer>768</integer>
</map>
+ <key>ImporterDebugMode</key>
+ <map>
+ <key>Comment</key>
+ <string>At 0 does nothing, at 1 dumps skinning data near orifinal file, at 2 dumps skining data and positions/weights of first 5 models, at 3 dumps skinning data and models as llsd</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>ImporterPreprocessDAE</key>
<map>
<key>Comment</key>
@@ -11631,6 +11642,28 @@
<key>Value</key>
<string>fss.txt</string>
</map>
+ <key>StatsFrametimeSampleSeconds</key>
+ <map>
+ <key>Comment</key>
+ <string>The number of seconds to sample extended frametime data (percentiles, stddev).</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>5</integer>
+ </map>
+ <key>StatsFrametimeEventThreshold</key>
+ <map>
+ <key>Comment</key>
+ <string>The percentage that the frametime difference must exceed in order to register a frametime event. 0.1 = 10%, 0.25 = 25%, etc.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.1</real>
+ </map>
<key>SystemLanguage</key>
<map>
<key>Comment</key>
@@ -16683,5 +16716,27 @@
<key>Value</key>
<integer>1</integer>
</map>
+ <key>MediaAutoPlayHuds</key>
+ <map>
+ <key>Comment</key>
+ <string>Automatically play HUD media</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
+ <key>MediaFirstClickInteract</key>
+ <map>
+ <key>Comment</key>
+ <string>This setting controls which media (once loaded) does not require a first click to focus before interaction can begin. This allows clicks to be passed directly to media bypassing the focus click requirement. This setting is a bitfield, precomputed values are as follows: Disabled=0; Worn HUDs only=1; Owned objects=3; Friend objects=7; Group objects=15; Landowner objects=31; Any object=31; All MOAP=1073741824. For complete details see lltoolpie.h enum MediaFirstClickTypes.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
</map>
</llsd>
diff --git a/indra/newview/gltf/asset.cpp b/indra/newview/gltf/asset.cpp
index a399a59f40..f40cc58440 100644
--- a/indra/newview/gltf/asset.cpp
+++ b/indra/newview/gltf/asset.cpp
@@ -50,6 +50,10 @@ namespace LL
"KHR_texture_transform"
};
+ static std::unordered_set<std::string> ExtensionsIgnored = {
+ "KHR_materials_pbrSpecularGlossiness"
+ };
+
Material::AlphaMode gltf_alpha_mode_to_enum(const std::string& alpha_mode)
{
if (alpha_mode == "OPAQUE")
@@ -472,11 +476,14 @@ void Asset::update()
for (auto& image : mImages)
{
- if (image.mTexture.notNull())
- { // HACK - force texture to be loaded full rez
- // TODO: calculate actual vsize
- image.mTexture->addTextureStats(2048.f * 2048.f);
- image.mTexture->setBoostLevel(LLViewerTexture::BOOST_HIGH);
+ if (image.mLoadIntoTexturePipe)
+ {
+ if (image.mTexture.notNull())
+ { // HACK - force texture to be loaded full rez
+ // TODO: calculate actual vsize
+ image.mTexture->addTextureStats(2048.f * 2048.f);
+ image.mTexture->setBoostLevel(LLViewerTexture::BOOST_HIGH);
+ }
}
}
}
@@ -486,18 +493,23 @@ void Asset::update()
bool Asset::prep()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
- // check required extensions and fail if not supported
- bool unsupported = false;
+ // check required extensions
for (auto& extension : mExtensionsRequired)
{
if (ExtensionsSupported.find(extension) == ExtensionsSupported.end())
{
- LL_WARNS() << "Unsupported extension: " << extension << LL_ENDL;
- unsupported = true;
+ if (ExtensionsIgnored.find(extension) == ExtensionsIgnored.end())
+ {
+ LL_WARNS() << "Unsupported extension: " << extension << LL_ENDL;
+ mUnsupportedExtensions.push_back(extension);
+ }
+ else
+ {
+ mIgnoredExtensions.push_back(extension);
+ }
}
}
-
- if (unsupported)
+ if (mUnsupportedExtensions.size() > 0)
{
return false;
}
@@ -513,7 +525,7 @@ bool Asset::prep()
for (auto& image : mImages)
{
- if (!image.prep(*this))
+ if (!image.prep(*this, mLoadIntoVRAM))
{
return false;
}
@@ -542,102 +554,106 @@ bool Asset::prep()
return false;
}
}
+ if (mLoadIntoVRAM)
+ {
+ // prepare vertex buffers
- // prepare vertex buffers
-
- // material count is number of materials + 1 for default material
- U32 mat_count = (U32) mMaterials.size() + 1;
-
- if (LLGLSLShader::sCurBoundShaderPtr == nullptr)
- { // make sure a shader is bound to satisfy mVertexBuffer->setBuffer
- gDebugProgram.bind();
- }
+ // material count is number of materials + 1 for default material
+ U32 mat_count = (U32) mMaterials.size() + 1;
- for (S32 double_sided = 0; double_sided < 2; ++double_sided)
- {
- RenderData& rd = mRenderData[double_sided];
- for (U32 i = 0; i < LLGLSLShader::NUM_GLTF_VARIANTS; ++i)
- {
- rd.mBatches[i].resize(mat_count);
+ if (LLGLSLShader::sCurBoundShaderPtr == nullptr)
+ { // make sure a shader is bound to satisfy mVertexBuffer->setBuffer
+ gDebugProgram.bind();
}
- // for each material
- for (S32 mat_id = -1; mat_id < (S32)mMaterials.size(); ++mat_id)
+ for (S32 double_sided = 0; double_sided < 2; ++double_sided)
{
- // for each shader variant
- U32 vertex_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 };
- U32 index_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 };
-
- S32 ds_mat = mat_id == -1 ? 0 : mMaterials[mat_id].mDoubleSided;
- if (ds_mat != double_sided)
+ RenderData& rd = mRenderData[double_sided];
+ for (U32 i = 0; i < LLGLSLShader::NUM_GLTF_VARIANTS; ++i)
{
- continue;
+ rd.mBatches[i].resize(mat_count);
}
- for (U32 variant = 0; variant < LLGLSLShader::NUM_GLTF_VARIANTS; ++variant)
+ // for each material
+ for (S32 mat_id = -1; mat_id < (S32)mMaterials.size(); ++mat_id)
{
- U32 attribute_mask = 0;
- // for each mesh
- for (auto& mesh : mMeshes)
+ // for each shader variant
+ U32 vertex_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 };
+ U32 index_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 };
+
+ S32 ds_mat = mat_id == -1 ? 0 : mMaterials[mat_id].mDoubleSided;
+ if (ds_mat != double_sided)
{
- // for each primitive
- for (auto& primitive : mesh.mPrimitives)
+ continue;
+ }
+
+ for (U32 variant = 0; variant < LLGLSLShader::NUM_GLTF_VARIANTS; ++variant)
+ {
+ U32 attribute_mask = 0;
+ // for each mesh
+ for (auto& mesh : mMeshes)
{
- if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant)
+ // for each primitive
+ for (auto& primitive : mesh.mPrimitives)
{
- // accumulate vertex and index counts
- primitive.mVertexOffset = vertex_count[variant];
- primitive.mIndexOffset = index_count[variant];
+ if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant)
+ {
+ // accumulate vertex and index counts
+ primitive.mVertexOffset = vertex_count[variant];
+ primitive.mIndexOffset = index_count[variant];
- vertex_count[variant] += primitive.getVertexCount();
- index_count[variant] += primitive.getIndexCount();
+ vertex_count[variant] += primitive.getVertexCount();
+ index_count[variant] += primitive.getIndexCount();
- // all primitives of a given variant and material should all have the same attribute mask
- llassert(attribute_mask == 0 || primitive.mAttributeMask == attribute_mask);
- attribute_mask |= primitive.mAttributeMask;
+ // all primitives of a given variant and material should all have the same attribute mask
+ llassert(attribute_mask == 0 || primitive.mAttributeMask == attribute_mask);
+ attribute_mask |= primitive.mAttributeMask;
+ }
}
}
- }
- // allocate vertex buffer and pack it
- if (vertex_count[variant] > 0)
- {
- U32 mat_idx = mat_id + 1;
- LLVertexBuffer* vb = new LLVertexBuffer(attribute_mask);
+ // allocate vertex buffer and pack it
+ if (vertex_count[variant] > 0)
+ {
+ U32 mat_idx = mat_id + 1;
+ #if 0
+ LLVertexBuffer* vb = new LLVertexBuffer(attribute_mask);
- rd.mBatches[variant][mat_idx].mVertexBuffer = vb;
- vb->allocateBuffer(vertex_count[variant],
- index_count[variant] * 2); // hack double index count... TODO: find a better way to indicate 32-bit indices will be used
- vb->setBuffer();
+ rd.mBatches[variant][mat_idx].mVertexBuffer = vb;
+ vb->allocateBuffer(vertex_count[variant],
+ index_count[variant] * 2); // hack double index count... TODO: find a better way to indicate 32-bit indices will be used
+ vb->setBuffer();
- for (auto& mesh : mMeshes)
- {
- for (auto& primitive : mesh.mPrimitives)
+ for (auto& mesh : mMeshes)
{
- if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant)
+ for (auto& primitive : mesh.mPrimitives)
{
- primitive.upload(vb);
+ if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant)
+ {
+ primitive.upload(vb);
+ }
}
}
- }
- vb->unmapBuffer();
+ vb->unmapBuffer();
- vb->unbind();
+ vb->unbind();
+ #endif
+ }
}
}
}
- }
- // sanity check that all primitives have a vertex buffer
- for (auto& mesh : mMeshes)
- {
- for (auto& primitive : mesh.mPrimitives)
+ // sanity check that all primitives have a vertex buffer
+ for (auto& mesh : mMeshes)
{
- llassert(primitive.mVertexBuffer.notNull());
+ for (auto& primitive : mesh.mPrimitives)
+ {
+ //llassert(primitive.mVertexBuffer.notNull());
+ }
}
}
-
+ #if 0
// build render batches
for (S32 node_id = 0; node_id < mNodes.size(); ++node_id)
{
@@ -664,6 +680,7 @@ bool Asset::prep()
}
}
}
+ #endif
return true;
}
@@ -672,9 +689,10 @@ Asset::Asset(const Value& src)
*this = src;
}
-bool Asset::load(std::string_view filename)
+bool Asset::load(std::string_view filename, bool loadIntoVRAM)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
+ mLoadIntoVRAM = loadIntoVRAM;
mFilename = filename;
std::string ext = gDirUtilp->getExtension(mFilename);
@@ -692,7 +710,7 @@ bool Asset::load(std::string_view filename)
}
else if (ext == "glb")
{
- return loadBinary(str);
+ return loadBinary(str, mLoadIntoVRAM);
}
else
{
@@ -709,8 +727,9 @@ bool Asset::load(std::string_view filename)
return false;
}
-bool Asset::loadBinary(const std::string& data)
+bool Asset::loadBinary(const std::string& data, bool loadIntoVRAM)
{
+ mLoadIntoVRAM = loadIntoVRAM;
// load from binary gltf
const U8* ptr = (const U8*)data.data();
const U8* end = ptr + data.size();
@@ -935,8 +954,9 @@ void Asset::eraseBufferView(S32 bufferView)
LLViewerFetchedTexture* fetch_texture(const LLUUID& id);
-bool Image::prep(Asset& asset)
+bool Image::prep(Asset& asset, bool loadIntoVRAM)
{
+ mLoadIntoTexturePipe = loadIntoVRAM;
LLUUID id;
if (mUri.size() == UUID_STR_SIZE && LLUUID::parseUUID(mUri, &id) && id.notNull())
{ // loaded from an asset, fetch the texture from the asset system
@@ -951,12 +971,12 @@ bool Image::prep(Asset& asset)
{ // embedded in a buffer, load the texture from the buffer
BufferView& bufferView = asset.mBufferViews[mBufferView];
Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
-
- U8* data = buffer.mData.data() + bufferView.mByteOffset;
-
- mTexture = LLViewerTextureManager::getFetchedTextureFromMemory(data, bufferView.mByteLength, mMimeType);
-
- if (mTexture.isNull())
+ if (mLoadIntoTexturePipe)
+ {
+ U8* data = buffer.mData.data() + bufferView.mByteOffset;
+ mTexture = LLViewerTextureManager::getFetchedTextureFromMemory(data, bufferView.mByteLength, mMimeType);
+ }
+ else if (mTexture.isNull() && mLoadIntoTexturePipe)
{
LL_WARNS("GLTF") << "Failed to load image from buffer:" << LL_ENDL;
LL_WARNS("GLTF") << " image: " << mName << LL_ENDL;
@@ -971,12 +991,12 @@ bool Image::prep(Asset& asset)
std::string img_file = dir + gDirUtilp->getDirDelimiter() + mUri;
LLUUID tracking_id = LLLocalBitmapMgr::getInstance()->addUnit(img_file);
- if (tracking_id.notNull())
+ if (tracking_id.notNull() && mLoadIntoTexturePipe)
{
LLUUID world_id = LLLocalBitmapMgr::getInstance()->getWorldID(tracking_id);
mTexture = LLViewerTextureManager::getFetchedTexture(world_id);
}
- else
+ else if (mLoadIntoTexturePipe)
{
LL_WARNS("GLTF") << "Failed to load image from file:" << LL_ENDL;
LL_WARNS("GLTF") << " image: " << mName << LL_ENDL;
@@ -991,7 +1011,7 @@ bool Image::prep(Asset& asset)
return false;
}
- if (!asset.mFilename.empty())
+ if (!asset.mFilename.empty() && mLoadIntoTexturePipe)
{ // local preview, boost image so it doesn't discard and force to save raw image in case we save out or upload
mTexture->setBoostLevel(LLViewerTexture::BOOST_PREVIEW);
mTexture->forceToSaveRawImage(0, F32_MAX);
diff --git a/indra/newview/gltf/asset.h b/indra/newview/gltf/asset.h
index 27821659db..b9554d753c 100644
--- a/indra/newview/gltf/asset.h
+++ b/indra/newview/gltf/asset.h
@@ -286,6 +286,7 @@ namespace LL
void serialize(boost::json::object& dst) const;
};
+ // Image is for images that we want to load for the given asset. This acts as an interface into the viewer's texture pipe.
class Image
{
public:
@@ -301,6 +302,8 @@ namespace LL
S32 mBits = -1;
S32 mPixelType = -1;
+ bool mLoadIntoTexturePipe = false;
+
LLPointer<LLViewerFetchedTexture> mTexture;
const Image& operator=(const Value& src);
@@ -316,7 +319,7 @@ namespace LL
// preserve only uri and name
void clearData(Asset& asset);
- bool prep(Asset& asset);
+ bool prep(Asset& asset, bool loadIntoVRAM);
};
// Render Batch -- vertex buffer and list of primitives to render using
@@ -391,6 +394,10 @@ namespace LL
// UBO for storing material data
U32 mMaterialsUBO = 0;
+ bool mLoadIntoVRAM = false;
+
+ std::vector<std::string> mUnsupportedExtensions;
+ std::vector<std::string> mIgnoredExtensions;
// prepare for first time use
bool prep();
@@ -428,12 +435,12 @@ namespace LL
// accepts .gltf and .glb files
// Any existing data will be lost
// returns result of prep() on success
- bool load(std::string_view filename);
+ bool load(std::string_view filename, bool loadIntoVRAM);
// load .glb contents from memory
// data - binary contents of .glb file
// returns result of prep() on success
- bool loadBinary(const std::string& data);
+ bool loadBinary(const std::string& data, bool loadIntoVRAM);
const Asset& operator=(const Value& src);
void serialize(boost::json::object& dst) const;
diff --git a/indra/newview/gltf/buffer_util.h b/indra/newview/gltf/buffer_util.h
index ef9bba8128..be36c5e90b 100644
--- a/indra/newview/gltf/buffer_util.h
+++ b/indra/newview/gltf/buffer_util.h
@@ -159,6 +159,12 @@ namespace LL
}
template<>
+ inline void copyVec3<F32, LLColor4U>(F32* src, LLColor4U& dst)
+ {
+ dst.set((U8)(src[0] * 255.f), (U8)(src[1] * 255.f), (U8)(src[2] * 255.f), 255);
+ }
+
+ template<>
inline void copyVec3<U16, LLColor4U>(U16* src, LLColor4U& dst)
{
dst.set((U8)(src[0]), (U8)(src[1]), (U8)(src[2]), 255);
@@ -369,6 +375,11 @@ namespace LL
template<class T>
inline void copy(Asset& asset, Accessor& accessor, LLStrider<T>& dst)
{
+ if (accessor.mBufferView == INVALID_INDEX)
+ {
+ LL_WARNS("GLTF") << "Invalid buffer" << LL_ENDL;
+ return;
+ }
const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView];
const Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset;
diff --git a/indra/newview/gltf/llgltfloader.cpp b/indra/newview/gltf/llgltfloader.cpp
new file mode 100644
index 0000000000..3019a12446
--- /dev/null
+++ b/indra/newview/gltf/llgltfloader.cpp
@@ -0,0 +1,1819 @@
+/**
+ * @file LLGLTFLoader.cpp
+ * @brief LLGLTFLoader class implementation
+ *
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2022, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llgltfloader.h"
+#include "meshoptimizer.h"
+#include <glm/gtc/packing.hpp>
+
+// Import & define single-header gltf import/export lib
+#define TINYGLTF_IMPLEMENTATION
+#define TINYGLTF_USE_CPP14 // default is C++ 11
+
+// tinygltf by default loads image files using STB
+#define STB_IMAGE_IMPLEMENTATION
+// to use our own image loading:
+// 1. replace this definition with TINYGLTF_NO_STB_IMAGE
+// 2. provide image loader callback with TinyGLTF::SetImageLoader(LoadimageDataFunction LoadImageData, void *user_data)
+
+// tinygltf saves image files using STB
+#define STB_IMAGE_WRITE_IMPLEMENTATION
+// similarly, can override with TINYGLTF_NO_STB_IMAGE_WRITE and TinyGLTF::SetImageWriter(fxn, data)
+
+// Additionally, disable inclusion of STB header files entirely with
+// TINYGLTF_NO_INCLUDE_STB_IMAGE
+// TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
+#include "tinygltf/tiny_gltf.h"
+
+
+// TODO: includes inherited from dae loader. Validate / prune
+
+#include "llsdserialize.h"
+#include "lljoint.h"
+#include "llbase64.h"
+#include "lldir.h"
+
+#include "llmatrix4a.h"
+
+#include <boost/regex.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <fstream>
+
+static const std::string lod_suffix[LLModel::NUM_LODS] =
+{
+ "_LOD0",
+ "_LOD1",
+ "_LOD2",
+ "",
+ "_PHYS",
+};
+
+// Premade rotation matrix, GLTF is Y-up while SL is Z-up
+static const glm::mat4 coord_system_rotation(
+ 1.f, 0.f, 0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 0.f, -1.f, 0.f, 0.f,
+ 0.f, 0.f, 0.f, 1.f
+);
+
+
+static const glm::mat4 coord_system_rotationxy(
+ 0.f, 1.f, 0.f, 0.f,
+ -1.f, 0.f, 0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 0.f, 0.f, 0.f, 1.f
+);
+
+static const S32 VERTICIES_LIMIT = USHRT_MAX - 2;
+
+LLGLTFLoader::LLGLTFLoader(std::string filename,
+ S32 lod,
+ LLModelLoader::load_callback_t load_cb,
+ LLModelLoader::joint_lookup_func_t joint_lookup_func,
+ LLModelLoader::texture_load_func_t texture_load_func,
+ LLModelLoader::state_callback_t state_cb,
+ void * opaque_userdata,
+ JointTransformMap & jointTransformMap,
+ JointNameSet & jointsFromNodes,
+ std::map<std::string, std::string, std::less<>> & jointAliasMap,
+ U32 maxJointsPerMesh,
+ U32 modelLimit,
+ U32 debugMode,
+ std::vector<LLJointData> viewer_skeleton) //,
+ //bool preprocess)
+ : LLModelLoader( filename,
+ lod,
+ load_cb,
+ joint_lookup_func,
+ texture_load_func,
+ state_cb,
+ opaque_userdata,
+ jointTransformMap,
+ jointsFromNodes,
+ jointAliasMap,
+ maxJointsPerMesh,
+ modelLimit,
+ debugMode)
+ , mViewerJointData(viewer_skeleton)
+ , mGltfLoaded(false)
+ , mApplyXYRotation(false)
+{
+}
+
+LLGLTFLoader::~LLGLTFLoader() {}
+
+bool LLGLTFLoader::OpenFile(const std::string &filename)
+{
+ // Clear the material cache for new file
+ mMaterialCache.clear();
+
+ tinygltf::TinyGLTF loader;
+ std::string filename_lc(filename);
+ LLStringUtil::toLower(filename_lc);
+
+ try
+ {
+ mGltfLoaded = mGLTFAsset.load(filename, false);
+ }
+ catch (const std::exception& e)
+ {
+ LL_WARNS() << "Exception in LLModelLoader::run: " << e.what() << LL_ENDL;
+ LLSD args;
+ args["Message"] = "ParsingErrorException";
+ args["FILENAME"] = filename;
+ args["EXCEPTION"] = e.what();
+ mWarningsArray.append(args);
+ setLoadState(ERROR_PARSING);
+ return false;
+ }
+ catch (...)
+ {
+ LOG_UNHANDLED_EXCEPTION("LLGLTFLoader");
+ LLSD args;
+ args["Message"] = "ParsingErrorException";
+ args["FILENAME"] = filename;
+ args["EXCEPTION"] = "Unknown exception";
+ mWarningsArray.append(args);
+ setLoadState(ERROR_PARSING);
+ return false;
+ }
+
+ if (!mGltfLoaded)
+ {
+ notifyUnsupportedExtension(true);
+
+ for (const auto& buffer : mGLTFAsset.mBuffers)
+ {
+ if (buffer.mByteLength > 0 && buffer.mData.empty())
+ {
+ bool bin_file = buffer.mUri.ends_with(".bin");
+ LLSD args;
+ args["Message"] = bin_file ? "ParsingErrorMissingBufferBin" : "ParsingErrorMissingBuffer";
+ args["BUFFER_NAME"] = buffer.mName;
+ args["BUFFER_URI"] = buffer.mUri;
+ mWarningsArray.append(args);
+ }
+ }
+ setLoadState(ERROR_PARSING);
+ return false;
+ }
+
+ notifyUnsupportedExtension(false);
+
+ bool meshesLoaded = parseMeshes();
+
+ setLoadState(DONE);
+
+ return meshesLoaded;
+}
+
+void LLGLTFLoader::addModelToScene(
+ LLModel* pModel,
+ const std::string& model_name,
+ U32 submodel_limit,
+ const LLMatrix4& transformation,
+ const LLVolumeParams& volume_params,
+ const material_map& mats)
+{
+ U32 volume_faces = pModel->getNumVolumeFaces();
+
+ // Side-steps all manner of issues when splitting models
+ // and matching lower LOD materials to base models
+ //
+ pModel->sortVolumeFacesByMaterialName();
+
+ int submodelID = 0;
+
+ // remove all faces that definitely won't fit into one model and submodel limit
+ U32 face_limit = (submodel_limit + 1) * LL_SCULPT_MESH_MAX_FACES;
+ if (face_limit < volume_faces)
+ {
+ LL_WARNS("GLTF_IMPORT") << "Model contains " << volume_faces
+ << " faces, exceeding the limit of " << face_limit << LL_ENDL;
+
+ LLSD args;
+ args["Message"] = "ModelTooManySubmodels";
+ args["MODEL_NAME"] = pModel->mLabel;
+ args["SUBMODEL_COUNT"] = static_cast<S32>(llfloor((F32)volume_faces / LL_SCULPT_MESH_MAX_FACES));
+ args["SUBMODEL_LIMIT"] = static_cast<S32>(submodel_limit);
+ mWarningsArray.append(args);
+
+ pModel->setNumVolumeFaces(face_limit);
+ }
+
+ LLVolume::face_list_t remainder;
+ std::vector<LLModel*> ready_models;
+ LLModel* current_model = pModel;
+
+ do
+ {
+ current_model->trimVolumeFacesToSize(LL_SCULPT_MESH_MAX_FACES, &remainder);
+
+ volume_faces = static_cast<U32>(remainder.size());
+
+ // Don't add to scene yet because weights and materials aren't ready.
+ // Just save it
+ ready_models.push_back(current_model);
+
+ // If we have left-over volume faces, create another model
+ // to absorb them.
+ if (volume_faces)
+ {
+ LLModel* next = new LLModel(volume_params, 0.f);
+ next->ClearFacesAndMaterials();
+ next->mSubmodelID = ++submodelID;
+
+ std::string instance_name = model_name;
+ if (next->mSubmodelID > 0)
+ {
+ instance_name += (char)((int)'a' + next->mSubmodelID);
+ }
+ // Check for duplicates and add copy suffix if needed
+ int duplicate_count = 0;
+ for (const auto& inst : mScene[transformation])
+ {
+ if (inst.mLabel == instance_name)
+ {
+ ++duplicate_count;
+ }
+ }
+ if (duplicate_count > 0) {
+ instance_name += "_copy_" + std::to_string(duplicate_count);
+ }
+ next->mLabel = instance_name;
+
+ next->getVolumeFaces() = remainder;
+ next->mNormalizedScale = current_model->mNormalizedScale;
+ next->mNormalizedTranslation = current_model->mNormalizedTranslation;
+ next->mSkinWeights = current_model->mSkinWeights;
+ next->mPosition = current_model->mPosition;
+
+ const LLMeshSkinInfo& current_skin_info = current_model->mSkinInfo;
+ LLMeshSkinInfo& next_skin_info = next->mSkinInfo;
+ next_skin_info.mJointNames = current_skin_info.mJointNames;
+ next_skin_info.mJointNums = current_skin_info.mJointNums;
+ next_skin_info.mBindShapeMatrix = current_skin_info.mBindShapeMatrix;
+ next_skin_info.mInvBindMatrix = current_skin_info.mInvBindMatrix;
+ next_skin_info.mAlternateBindMatrix = current_skin_info.mAlternateBindMatrix;
+ next_skin_info.mPelvisOffset = current_skin_info.mPelvisOffset;
+
+
+ if (current_model->mMaterialList.size() > LL_SCULPT_MESH_MAX_FACES)
+ {
+ next->mMaterialList.assign(current_model->mMaterialList.begin() + LL_SCULPT_MESH_MAX_FACES, current_model->mMaterialList.end());
+ current_model->mMaterialList.resize(LL_SCULPT_MESH_MAX_FACES);
+ }
+
+ current_model = next;
+ }
+
+ remainder.clear();
+
+ } while (volume_faces);
+
+ for (auto model : ready_models)
+ {
+ // remove unused/redundant vertices
+ model->remapVolumeFaces();
+
+ mModelList.push_back(model);
+
+ std::map<std::string, LLImportMaterial> materials;
+ for (U32 i = 0; i < (U32)model->mMaterialList.size(); ++i)
+ {
+ material_map::const_iterator found = mats.find(model->mMaterialList[i]);
+ if (found != mats.end())
+ {
+ materials[model->mMaterialList[i]] = found->second;
+ }
+ else
+ {
+ materials[model->mMaterialList[i]] = LLImportMaterial();
+ }
+ }
+ // Keep base name for scene instance.
+ std::string instance_name = model->mLabel;
+ // Add suffix. Suffix is nessesary for model matching logic
+ // because sometimes higher lod can be used as a lower one, so models
+ // need unique names not just in scope of one lod, but across lods.
+ model->mLabel += lod_suffix[mLod];
+ mScene[transformation].push_back(LLModelInstance(model, instance_name, transformation, materials));
+ stretch_extents(model, transformation);
+ }
+}
+
+bool LLGLTFLoader::parseMeshes()
+{
+ if (!mGltfLoaded) return false;
+
+ // 2022-04 DJH Volume params from dae example. TODO understand PCODE
+ LLVolumeParams volume_params;
+ volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+
+ mTransform.setIdentity();
+
+ for (auto& node : mGLTFAsset.mNodes)
+ {
+ // Make node matrix valid for correct transformation
+ node.makeMatrixValid();
+ }
+
+ if (mGLTFAsset.mSkins.size() > 0)
+ {
+ checkForXYrotation(mGLTFAsset.mSkins[0]);
+ populateJointGroups();
+ }
+
+ // Populate the joints from skins first.
+ // Multiple meshes can share the same skin, so preparing skins beforehand.
+ for (S32 i = 0; i < mGLTFAsset.mSkins.size(); i++)
+ {
+ populateJointsFromSkin(i);
+ }
+
+ // Track how many times each mesh name has been used
+ std::map<std::string, S32> mesh_name_counts;
+
+ // For now use mesh count, but might be better to do 'mNodes.size() - joints count'.
+ U32 submodel_limit = mGLTFAsset.mMeshes.size() > 0 ? mGeneratedModelLimit / (U32)mGLTFAsset.mMeshes.size() : 0;
+
+ // Check if we have scenes defined
+ if (!mGLTFAsset.mScenes.empty())
+ {
+ // Process the default scene (or first scene if no default)
+ S32 scene_idx = mGLTFAsset.mScene >= 0 ? mGLTFAsset.mScene : 0;
+
+ if (scene_idx < mGLTFAsset.mScenes.size())
+ {
+ const LL::GLTF::Scene& scene = mGLTFAsset.mScenes[scene_idx];
+
+ LL_INFOS("GLTF_IMPORT") << "Processing scene " << scene_idx << " with " << scene.mNodes.size() << " root nodes" << LL_ENDL;
+
+ // Process all root nodes defined in the scene
+ for (S32 root_idx : scene.mNodes)
+ {
+ if (root_idx >= 0 && root_idx < static_cast<S32>(mGLTFAsset.mNodes.size()))
+ {
+ processNodeHierarchy(root_idx, mesh_name_counts, submodel_limit, volume_params);
+ }
+ }
+ }
+ }
+ else
+ {
+ LL_WARNS("GLTF_IMPORT") << "No scenes defined in GLTF file" << LL_ENDL;
+
+ LLSD args;
+ args["Message"] = "NoScenesFound";
+ mWarningsArray.append(args);
+ return false;
+ }
+
+ checkGlobalJointUsage();
+
+ return true;
+}
+
+void LLGLTFLoader::processNodeHierarchy(S32 node_idx, std::map<std::string, S32>& mesh_name_counts, U32 submodel_limit, const LLVolumeParams& volume_params)
+{
+ if (node_idx < 0 || node_idx >= static_cast<S32>(mGLTFAsset.mNodes.size()))
+ return;
+
+ const LL::GLTF::Node& node = mGLTFAsset.mNodes[node_idx];
+
+ LL_DEBUGS("GLTF_IMPORT") << "Processing node " << node_idx << " (" << node.mName << ")"
+ << " - has mesh: " << (node.mMesh >= 0 ? "yes" : "no")
+ << " - children: " << node.mChildren.size() << LL_ENDL;
+
+ // Process this node's mesh if it has one
+ if (node.mMesh >= 0 && node.mMesh < mGLTFAsset.mMeshes.size())
+ {
+ LLMatrix4 transformation;
+ material_map mats;
+
+ LLModel* pModel = new LLModel(volume_params, 0.f);
+ const LL::GLTF::Mesh& mesh = mGLTFAsset.mMeshes[node.mMesh];
+
+ // Get base mesh name and track usage
+ std::string base_name = getLodlessLabel(mesh);
+ if (base_name.empty())
+ {
+ base_name = "mesh_" + std::to_string(node.mMesh);
+ }
+
+ S32 instance_count = mesh_name_counts[base_name]++;
+
+ // make name unique
+ if (instance_count > 0)
+ {
+ base_name = base_name + "_copy_" + std::to_string(instance_count);
+ }
+
+ if (populateModelFromMesh(pModel, base_name, mesh, node, mats) &&
+ (LLModel::NO_ERRORS == pModel->getStatus()) &&
+ validate_model(pModel))
+ {
+ mTransform.setIdentity();
+ transformation = mTransform;
+
+ // adjust the transformation to compensate for mesh normalization
+ LLVector3 mesh_scale_vector;
+ LLVector3 mesh_translation_vector;
+ pModel->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector);
+
+ LLMatrix4 mesh_translation;
+ mesh_translation.setTranslation(mesh_translation_vector);
+ mesh_translation *= transformation;
+ transformation = mesh_translation;
+
+ LLMatrix4 mesh_scale;
+ mesh_scale.initScale(mesh_scale_vector);
+ mesh_scale *= transformation;
+ transformation = mesh_scale;
+
+ if (node.mSkin >= 0)
+ {
+ // "Bind Shape Matrix" is supposed to transform the geometry of the skinned mesh
+ // into the coordinate space of the joints.
+ // In GLTF, this matrix is omitted, and it is assumed that this transform is either
+ // premultiplied with the mesh data, or postmultiplied to the inverse bind matrices.
+ //
+ // TODO: There appears to be missing rotation when joints rotate the model
+ // or inverted bind matrices are missing inherited rotation
+ // (based of values the 'bento shoes' mesh might be missing 90 degrees horizontaly
+ // prior to skinning)
+
+ pModel->mSkinInfo.mBindShapeMatrix.loadu(mesh_scale);
+ LL_INFOS("GLTF_DEBUG") << "Model: " << pModel->mLabel << " mBindShapeMatrix: " << pModel->mSkinInfo.mBindShapeMatrix << LL_ENDL;
+ }
+
+ if (transformation.determinant() < 0)
+ { // negative scales are not supported
+ LL_INFOS("GLTF_IMPORT") << "Negative scale detected, unsupported post-normalization transform. domInstance_geometry: "
+ << pModel->mLabel << LL_ENDL;
+ LLSD args;
+ args["Message"] = "NegativeScaleNormTrans";
+ args["LABEL"] = pModel->mLabel;
+ mWarningsArray.append(args);
+ }
+
+ addModelToScene(pModel, base_name, submodel_limit, transformation, volume_params, mats);
+ mats.clear();
+ }
+ else
+ {
+ setLoadState(ERROR_MODEL + pModel->getStatus());
+ delete pModel;
+ return;
+ }
+ }
+ else if (node.mMesh >= 0)
+ {
+ // Log invalid mesh reference
+ LL_WARNS("GLTF_IMPORT") << "Node " << node_idx << " (" << node.mName
+ << ") references invalid mesh " << node.mMesh
+ << " (total meshes: " << mGLTFAsset.mMeshes.size() << ")" << LL_ENDL;
+
+ LLSD args;
+ args["Message"] = "InvalidMeshReference";
+ args["NODE_NAME"] = node.mName;
+ args["MESH_INDEX"] = node.mMesh;
+ args["TOTAL_MESHES"] = static_cast<S32>(mGLTFAsset.mMeshes.size());
+ mWarningsArray.append(args);
+ }
+
+ // Process all children recursively
+ for (S32 child_idx : node.mChildren)
+ {
+ processNodeHierarchy(child_idx, mesh_name_counts, submodel_limit, volume_params);
+ }
+}
+
+void LLGLTFLoader::computeCombinedNodeTransform(const LL::GLTF::Asset& asset, S32 node_index, glm::mat4& combined_transform) const
+{
+ if (node_index < 0 || node_index >= static_cast<S32>(asset.mNodes.size()))
+ {
+ combined_transform = glm::mat4(1.0f);
+ return;
+ }
+
+ const auto& node = asset.mNodes[node_index];
+
+ // Ensure the node's matrix is valid
+ const_cast<LL::GLTF::Node&>(node).makeMatrixValid();
+
+ // Start with this node's transform
+ combined_transform = node.mMatrix;
+
+ // Find and apply parent transform if it exists
+ for (size_t i = 0; i < asset.mNodes.size(); ++i)
+ {
+ const auto& potential_parent = asset.mNodes[i];
+ auto it = std::find(potential_parent.mChildren.begin(), potential_parent.mChildren.end(), node_index);
+
+ if (it != potential_parent.mChildren.end())
+ {
+ // Found parent - recursively get its combined transform and apply it
+ glm::mat4 parent_transform;
+ computeCombinedNodeTransform(asset, static_cast<S32>(i), parent_transform);
+ combined_transform = parent_transform * combined_transform;
+ return; // Early exit - a node can only have one parent
+ }
+ }
+}
+
+bool LLGLTFLoader::addJointToModelSkin(LLMeshSkinInfo& skin_info, S32 gltf_skin_idx, size_t gltf_joint_idx)
+{
+ const std::string& legal_name = mJointNames[gltf_skin_idx][gltf_joint_idx];
+ if (legal_name.empty())
+ {
+ llassert(false); // should have been stopped by gltf_joint_index_use[i] == -1
+ return false;
+ }
+ skin_info.mJointNames.push_back(legal_name);
+ skin_info.mJointNums.push_back(-1);
+
+ // In scope of same skin multiple meshes reuse same bind matrices
+ skin_info.mInvBindMatrix.push_back(mInverseBindMatrices[gltf_skin_idx][gltf_joint_idx]);
+ skin_info.mAlternateBindMatrix.push_back(mAlternateBindMatrices[gltf_skin_idx][gltf_joint_idx]);
+
+ // Track joint usage for this skin, for the sake of unused joints detection
+ mJointUsage[gltf_skin_idx][gltf_joint_idx]++;
+
+ return true;
+}
+
+LLGLTFLoader::LLGLTFImportMaterial LLGLTFLoader::processMaterial(S32 material_index, S32 fallback_index)
+{
+ // Check cache first
+ auto cached = mMaterialCache.find(material_index);
+ if (cached != mMaterialCache.end())
+ {
+ return cached->second;
+ }
+
+ LLImportMaterial impMat;
+ impMat.mDiffuseColor = LLColor4::white; // Default color
+
+ // Generate material name
+ std::string materialName = generateMaterialName(material_index, fallback_index);
+
+ // Process material if available
+ if (material_index >= 0 && material_index < mGLTFAsset.mMaterials.size())
+ {
+ LL::GLTF::Material* material = &mGLTFAsset.mMaterials[material_index];
+
+ // Set diffuse color from base color factor
+ impMat.mDiffuseColor = LLColor4(
+ material->mPbrMetallicRoughness.mBaseColorFactor[0],
+ material->mPbrMetallicRoughness.mBaseColorFactor[1],
+ material->mPbrMetallicRoughness.mBaseColorFactor[2],
+ material->mPbrMetallicRoughness.mBaseColorFactor[3]
+ );
+
+ // Process base color texture if it exists
+ if (material->mPbrMetallicRoughness.mBaseColorTexture.mIndex >= 0)
+ {
+ S32 texIndex = material->mPbrMetallicRoughness.mBaseColorTexture.mIndex;
+ std::string filename = processTexture(texIndex, "base_color", material->mName);
+
+ if (!filename.empty())
+ {
+ impMat.mDiffuseMapFilename = filename;
+ impMat.mDiffuseMapLabel = material->mName.empty() ? filename : material->mName;
+
+ // Check if the texture is already loaded
+ S32 sourceIndex;
+ if (validateTextureIndex(texIndex, sourceIndex))
+ {
+ LL::GLTF::Image& image = mGLTFAsset.mImages[sourceIndex];
+ if (image.mTexture.notNull())
+ {
+ impMat.setDiffuseMap(image.mTexture->getID());
+ LL_INFOS("GLTF_IMPORT") << "Using existing texture ID: " << image.mTexture->getID().asString() << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS("GLTF_IMPORT") << "Texture needs loading: " << impMat.mDiffuseMapFilename << LL_ENDL;
+ }
+ }
+ }
+ }
+ }
+
+ // Create cached material with both material and name
+ LLGLTFImportMaterial cachedMat(impMat, materialName);
+
+ // Cache the processed material
+ mMaterialCache[material_index] = cachedMat;
+ return cachedMat;
+}
+
+std::string LLGLTFLoader::processTexture(S32 texture_index, const std::string& texture_type, const std::string& material_name)
+{
+ S32 sourceIndex;
+ if (!validateTextureIndex(texture_index, sourceIndex))
+ return "";
+
+ LL::GLTF::Image& image = mGLTFAsset.mImages[sourceIndex];
+
+ // Process URI-based textures
+ if (!image.mUri.empty())
+ {
+ std::string filename = image.mUri;
+ size_t pos = filename.find_last_of("/\\");
+ if (pos != std::string::npos)
+ {
+ filename = filename.substr(pos + 1);
+ }
+
+ LL_INFOS("GLTF_IMPORT") << "Found texture: " << filename << " for material: " << material_name << LL_ENDL;
+
+ LLSD args;
+ args["Message"] = "TextureFound";
+ args["TEXTURE_NAME"] = filename;
+ args["MATERIAL_NAME"] = material_name;
+ mWarningsArray.append(args);
+
+ return filename;
+ }
+
+ // Process embedded textures
+ if (image.mBufferView >= 0)
+ {
+ return extractTextureToTempFile(texture_index, texture_type);
+ }
+
+ return "";
+}
+
+bool LLGLTFLoader::validateTextureIndex(S32 texture_index, S32& source_index)
+{
+ if (texture_index < 0 || texture_index >= mGLTFAsset.mTextures.size())
+ return false;
+
+ source_index = mGLTFAsset.mTextures[texture_index].mSource;
+ if (source_index < 0 || source_index >= mGLTFAsset.mImages.size())
+ return false;
+
+ return true;
+}
+
+std::string LLGLTFLoader::generateMaterialName(S32 material_index, S32 fallback_index)
+{
+ if (material_index >= 0 && material_index < mGLTFAsset.mMaterials.size())
+ {
+ LL::GLTF::Material* material = &mGLTFAsset.mMaterials[material_index];
+ std::string materialName = material->mName;
+
+ if (materialName.empty())
+ {
+ materialName = "mat" + std::to_string(material_index);
+ }
+ return materialName;
+ }
+ else
+ {
+ return fallback_index >= 0 ? "mat_default" + std::to_string(fallback_index) : "mat_default";
+ }
+}
+
+bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const std::string& base_name, const LL::GLTF::Mesh& mesh, const LL::GLTF::Node& nodeno, material_map& mats)
+{
+ // Set the requested label for the floater display and uploading
+ pModel->mRequestedLabel = gDirUtilp->getBaseFileName(mFilename, true);
+ // Set only name, suffix will be added later
+ pModel->mLabel = base_name;
+
+ LL_DEBUGS("GLTF_DEBUG") << "Processing model " << pModel->mLabel << LL_ENDL;
+
+ pModel->ClearFacesAndMaterials();
+
+ S32 skinIdx = nodeno.mSkin;
+
+ // Compute final combined transform matrix (hierarchy + coordinate rotation)
+ S32 node_index = static_cast<S32>(&nodeno - &mGLTFAsset.mNodes[0]);
+ glm::mat4 hierarchy_transform;
+ computeCombinedNodeTransform(mGLTFAsset, node_index, hierarchy_transform);
+
+ // Combine transforms: coordinate rotation applied to hierarchy transform
+ glm::mat4 final_transform = coord_system_rotation * hierarchy_transform;
+ if (mApplyXYRotation)
+ {
+ final_transform = coord_system_rotationxy * final_transform;
+ }
+
+ // Check if we have a negative scale (flipped coordinate system)
+ bool hasNegativeScale = glm::determinant(final_transform) < 0.0f;
+
+ // Pre-compute normal transform matrix (transpose of inverse of upper-left 3x3)
+ const glm::mat3 normal_transform = glm::transpose(glm::inverse(glm::mat3(final_transform)));
+
+ // Mark unsuported joints with '-1' so that they won't get added into weights
+ // GLTF maps all joints onto all meshes. Gather use count per mesh to cut unused ones.
+ std::vector<S32> gltf_joint_index_use;
+ if (skinIdx >= 0 && mGLTFAsset.mSkins.size() > skinIdx)
+ {
+ LL::GLTF::Skin& gltf_skin = mGLTFAsset.mSkins[skinIdx];
+
+ size_t jointCnt = gltf_skin.mJoints.size();
+ gltf_joint_index_use.resize(jointCnt, 0);
+
+ for (size_t i = 0; i < jointCnt; ++i)
+ {
+ if (mJointNames[skinIdx][i].empty())
+ {
+ // This might need to hold a substitute index
+ gltf_joint_index_use[i] = -1; // mark as unsupported
+ }
+ }
+ }
+
+ for (size_t prim_idx = 0; prim_idx < mesh.mPrimitives.size(); ++prim_idx)
+ {
+ const LL::GLTF::Primitive& prim = mesh.mPrimitives[prim_idx];
+
+ // So primitives already have all of the data we need for a given face in SL land.
+ // Primitives may only ever have a single material assigned to them - as the relation is 1:1 in terms of intended draw call
+ // count. Just go ahead and populate faces direct from the GLTF primitives here. -Geenz 2025-04-07
+ LLVolumeFace face;
+ std::vector<GLTFVertex> vertices;
+
+ // Use cached material processing
+ LLGLTFImportMaterial cachedMat = processMaterial(prim.mMaterial, pModel->getNumVolumeFaces() - 1);
+ LLImportMaterial impMat = cachedMat;
+ std::string materialName = cachedMat.name;
+ mats[materialName] = impMat;
+
+ if (prim.getIndexCount() % 3 != 0)
+ {
+ LL_WARNS("GLTF_IMPORT") << "Mesh '" << mesh.mName << "' primitive " << prim_idx
+ << ": Invalid index count " << prim.getIndexCount()
+ << " (not divisible by 3). GLTF files must contain triangulated geometry." << LL_ENDL;
+
+ LLSD args;
+ args["Message"] = "InvalidGeometryNonTriangulated";
+ args["MESH_NAME"] = mesh.mName;
+ args["PRIMITIVE_INDEX"] = static_cast<S32>(prim_idx);
+ args["INDEX_COUNT"] = static_cast<S32>(prim.getIndexCount());
+ mWarningsArray.append(args);
+ return false; // Skip this primitive
+ }
+
+ // Apply the global scale and center offset to all vertices
+ for (U32 i = 0; i < prim.getVertexCount(); i++)
+ {
+ // Use pre-computed final_transform
+ glm::vec4 pos(prim.mPositions[i][0], prim.mPositions[i][1], prim.mPositions[i][2], 1.0f);
+ glm::vec4 transformed_pos = final_transform * pos;
+
+ GLTFVertex vert;
+ vert.position = glm::vec3(transformed_pos);
+
+ if (!prim.mNormals.empty())
+ {
+ // Use pre-computed normal_transform
+ glm::vec3 normal_vec(prim.mNormals[i][0], prim.mNormals[i][1], prim.mNormals[i][2]);
+ vert.normal = glm::normalize(normal_transform * normal_vec);
+ }
+ else
+ {
+ // Use default normal (pointing up in model space)
+ vert.normal = glm::normalize(normal_transform * glm::vec3(0.0f, 0.0f, 1.0f));
+ LL_DEBUGS("GLTF_IMPORT") << "No normals found for primitive, using default normal." << LL_ENDL;
+ }
+
+ vert.uv0 = glm::vec2(prim.mTexCoords0[i][0], -prim.mTexCoords0[i][1]);
+
+ if (skinIdx >= 0)
+ {
+ vert.weights = glm::vec4(prim.mWeights[i]);
+
+ auto accessorIdx = prim.mAttributes.at("JOINTS_0");
+ LL::GLTF::Accessor::ComponentType componentType = LL::GLTF::Accessor::ComponentType::UNSIGNED_BYTE;
+ if (accessorIdx >= 0)
+ {
+ auto accessor = mGLTFAsset.mAccessors[accessorIdx];
+ componentType = accessor.mComponentType;
+ }
+
+ // The GLTF spec allows for either an unsigned byte for joint indices, or an unsigned short.
+ // Detect and unpack accordingly.
+ if (componentType == LL::GLTF::Accessor::ComponentType::UNSIGNED_BYTE)
+ {
+ auto ujoint = glm::unpackUint4x8((U32)(prim.mJoints[i] & 0xFFFFFFFF));
+ vert.joints = glm::u16vec4(ujoint.x, ujoint.y, ujoint.z, ujoint.w);
+ }
+ else if (componentType == LL::GLTF::Accessor::ComponentType::UNSIGNED_SHORT)
+ {
+ vert.joints = glm::unpackUint4x16(prim.mJoints[i]);
+ }
+ else
+ {
+ vert.joints = glm::zero<glm::u16vec4>();
+ vert.weights = glm::zero<glm::vec4>();
+ }
+ }
+ vertices.push_back(vert);
+ }
+
+ // Check for empty vertex array before processing
+ if (vertices.empty())
+ {
+ LL_WARNS("GLTF_IMPORT") << "Empty vertex array for primitive " << prim_idx << " in model " << mesh.mName << LL_ENDL;
+ LLSD args;
+ args["Message"] = "EmptyVertexArray";
+ args["MESH_NAME"] = mesh.mName;
+ args["PRIMITIVE_INDEX"] = static_cast<S32>(prim_idx);
+ args["INDEX_COUNT"] = static_cast<S32>(prim.getIndexCount());
+ mWarningsArray.append(args);
+ return false; // Skip this primitive
+ }
+
+ std::vector<LLVolumeFace::VertexData> faceVertices;
+ glm::vec3 min = glm::vec3(FLT_MAX);
+ glm::vec3 max = glm::vec3(-FLT_MAX);
+
+ for (U32 i = 0; i < vertices.size(); i++)
+ {
+ LLVolumeFace::VertexData vert;
+
+ // Update min/max bounds
+ if (i == 0)
+ {
+ min = max = vertices[i].position;
+ }
+ else
+ {
+ min.x = std::min(min.x, vertices[i].position.x);
+ min.y = std::min(min.y, vertices[i].position.y);
+ min.z = std::min(min.z, vertices[i].position.z);
+ max.x = std::max(max.x, vertices[i].position.x);
+ max.y = std::max(max.y, vertices[i].position.y);
+ max.z = std::max(max.z, vertices[i].position.z);
+ }
+
+ LLVector4a position = LLVector4a(vertices[i].position.x, vertices[i].position.y, vertices[i].position.z);
+ LLVector4a normal = LLVector4a(vertices[i].normal.x, vertices[i].normal.y, vertices[i].normal.z);
+ vert.setPosition(position);
+ vert.setNormal(normal);
+ vert.mTexCoord = LLVector2(vertices[i].uv0.x, vertices[i].uv0.y);
+ faceVertices.push_back(vert);
+
+ if (skinIdx >= 0)
+ {
+ // create list of weights that influence this vertex
+ LLModel::weight_list weight_list;
+
+ // Drop joints that viewer doesn't support (negative in gltf_joint_index_use_count)
+ // don't reindex them yet, more indexes will be removed
+ // Also drop joints that have no weight. GLTF stores 4 per vertex, so there might be
+ // 'empty' ones
+ if (gltf_joint_index_use[vertices[i].joints.x] >= 0
+ && vertices[i].weights.x > 0.f)
+ {
+ weight_list.push_back(LLModel::JointWeight(vertices[i].joints.x, vertices[i].weights.x));
+ gltf_joint_index_use[vertices[i].joints.x]++;
+ }
+ if (gltf_joint_index_use[vertices[i].joints.y] >= 0
+ && vertices[i].weights.y > 0.f)
+ {
+ weight_list.push_back(LLModel::JointWeight(vertices[i].joints.y, vertices[i].weights.y));
+ gltf_joint_index_use[vertices[i].joints.y]++;
+ }
+ if (gltf_joint_index_use[vertices[i].joints.z] >= 0
+ && vertices[i].weights.z > 0.f)
+ {
+ weight_list.push_back(LLModel::JointWeight(vertices[i].joints.z, vertices[i].weights.z));
+ gltf_joint_index_use[vertices[i].joints.z]++;
+ }
+ if (gltf_joint_index_use[vertices[i].joints.w] >= 0
+ && vertices[i].weights.w > 0.f)
+ {
+ weight_list.push_back(LLModel::JointWeight(vertices[i].joints.w, vertices[i].weights.w));
+ gltf_joint_index_use[vertices[i].joints.w]++;
+ }
+
+ std::sort(weight_list.begin(), weight_list.end(), LLModel::CompareWeightGreater());
+
+ std::vector<LLModel::JointWeight> wght;
+ F32 total = 0.f;
+
+ for (U32 j = 0; j < llmin((U32)4, (U32)weight_list.size()); ++j)
+ {
+ // take up to 4 most significant weights
+ // Ported from the DAE loader - however, GLTF right now only supports up to four weights per vertex.
+ wght.push_back(weight_list[j]);
+ total += weight_list[j].mWeight;
+ }
+
+ if (total != 0.f)
+ {
+ F32 scale = 1.f / total;
+ if (scale != 1.f)
+ { // normalize weights
+ for (U32 j = 0; j < wght.size(); ++j)
+ {
+ wght[j].mWeight *= scale;
+ }
+ }
+ }
+
+ if (wght.size() > 0)
+ {
+ pModel->mSkinWeights[LLVector3(vertices[i].position)] = wght;
+ }
+ }
+ }
+
+ // Indices handling
+ if (faceVertices.size() >= VERTICIES_LIMIT)
+ {
+ // Will have to remap 32 bit indices into 16 bit indices
+ // For the sake of simplicity build vector of 32 bit indices first
+ std::vector<U32> indices_32;
+ for (U32 i = 0; i < prim.getIndexCount(); i += 3)
+ {
+ // When processing indices, flip winding order if needed
+ if (hasNegativeScale)
+ {
+ // Flip winding order for negative scale
+ indices_32.push_back(prim.mIndexArray[i]);
+ indices_32.push_back(prim.mIndexArray[i + 2]); // Swap these two
+ indices_32.push_back(prim.mIndexArray[i + 1]);
+ }
+ else
+ {
+ indices_32.push_back(prim.mIndexArray[i]);
+ indices_32.push_back(prim.mIndexArray[i + 1]);
+ indices_32.push_back(prim.mIndexArray[i + 2]);
+ }
+ }
+
+ // Generates a vertex remap table with no gaps in the resulting sequence
+ std::vector<U32> remap(faceVertices.size());
+ size_t vertex_count = meshopt_generateVertexRemap(&remap[0], &indices_32[0], indices_32.size(), &faceVertices[0], faceVertices.size(), sizeof(LLVolumeFace::VertexData));
+
+ // Manually remap vertices
+ std::vector<LLVolumeFace::VertexData> optimized_vertices(vertex_count);
+ for (size_t i = 0; i < vertex_count; ++i)
+ {
+ optimized_vertices[i] = faceVertices[remap[i]];
+ }
+
+ std::vector<U32> optimized_indices(indices_32.size());
+ meshopt_remapIndexBuffer(&optimized_indices[0], &indices_32[0], indices_32.size(), &remap[0]);
+
+ // Sort indices to improve mesh splits (reducing amount of duplicated indices)
+ meshopt_optimizeVertexCache(&optimized_indices[0], &optimized_indices[0], indices_32.size(), vertex_count);
+
+ std::vector<U16> indices_16;
+ std::vector<S64> vertices_remap;
+ vertices_remap.resize(vertex_count, -1);
+ S32 created_faces = 0;
+ std::vector<LLVolumeFace::VertexData> face_verts;
+ min = glm::vec3(FLT_MAX);
+ max = glm::vec3(-FLT_MAX);
+
+ for (size_t idx = 0; idx < optimized_indices.size(); idx++)
+ {
+ size_t vert_index = optimized_indices[idx];
+ if (vertices_remap[vert_index] == -1)
+ {
+ // First encounter, add it
+ size_t new_vert_idx = face_verts.size();
+ vertices_remap[vert_index] = (S64)new_vert_idx;
+ face_verts.push_back(optimized_vertices[vert_index]);
+ vert_index = new_vert_idx;
+
+ // Update min/max bounds
+ const LLVector4a& vec = face_verts[new_vert_idx].getPosition();
+ if (new_vert_idx == 0)
+ {
+ min.x = vec[0];
+ min.y = vec[1];
+ min.z = vec[2];
+ max = min;
+ }
+ else
+ {
+ min.x = std::min(min.x, vec[0]);
+ min.y = std::min(min.y, vec[1]);
+ min.z = std::min(min.z, vec[2]);
+ max.x = std::max(max.x, vec[0]);
+ max.y = std::max(max.y, vec[1]);
+ max.z = std::max(max.z, vec[2]);
+ }
+ }
+ else
+ {
+ // already in vector, get position
+ vert_index = (size_t)vertices_remap[vert_index];
+ }
+ indices_16.push_back((U16)vert_index);
+
+ if (indices_16.size() % 3 == 0 && face_verts.size() >= VERTICIES_LIMIT - 1)
+ {
+ LLVolumeFace face;
+ face.fillFromLegacyData(face_verts, indices_16);
+ face.mExtents[0] = LLVector4a(min.x, min.y, min.z, 0);
+ face.mExtents[1] = LLVector4a(max.x, max.y, max.z, 0);
+ pModel->getVolumeFaces().push_back(face);
+ pModel->getMaterialList().push_back(materialName);
+ created_faces++;
+
+ std::fill(vertices_remap.begin(), vertices_remap.end(), -1);
+ indices_16.clear();
+ face_verts.clear();
+
+ min = glm::vec3(FLT_MAX);
+ max = glm::vec3(-FLT_MAX);
+ }
+ }
+ if (indices_16.size() > 0 && face_verts.size() > 0)
+ {
+ LLVolumeFace face;
+ face.fillFromLegacyData(face_verts, indices_16);
+ face.mExtents[0] = LLVector4a(min.x, min.y, min.z, 0);
+ face.mExtents[1] = LLVector4a(max.x, max.y, max.z, 0);
+ pModel->getVolumeFaces().push_back(face);
+ pModel->getMaterialList().push_back(materialName);
+ created_faces++;
+ }
+
+ LL_INFOS("GLTF_IMPORT") << "Primitive " << (S32)prim_idx << " from model " << pModel->mLabel
+ << " is over vertices limit, it was split into " << created_faces
+ << " faces" << LL_ENDL;
+ LLSD args;
+ args["Message"] = "ModelSplitPrimitive";
+ args["MODEL_NAME"] = pModel->mLabel;
+ args["FACE_COUNT"] = created_faces;
+ mWarningsArray.append(args);
+ }
+ else
+ {
+ // can use indices directly
+ std::vector<U16> indices;
+ for (U32 i = 0; i < prim.getIndexCount(); i += 3)
+ {
+ // When processing indices, flip winding order if needed
+ if (hasNegativeScale)
+ {
+ // Flip winding order for negative scale
+ indices.push_back(prim.mIndexArray[i]);
+ indices.push_back(prim.mIndexArray[i + 2]); // Swap these two
+ indices.push_back(prim.mIndexArray[i + 1]);
+ }
+ else
+ {
+ indices.push_back(prim.mIndexArray[i]);
+ indices.push_back(prim.mIndexArray[i + 1]);
+ indices.push_back(prim.mIndexArray[i + 2]);
+ }
+ }
+
+ face.fillFromLegacyData(faceVertices, indices);
+ face.mExtents[0] = LLVector4a(min.x, min.y, min.z, 0);
+ face.mExtents[1] = LLVector4a(max.x, max.y, max.z, 0);
+
+ pModel->getVolumeFaces().push_back(face);
+ pModel->getMaterialList().push_back(materialName);
+ }
+ }
+
+ // Call normalizeVolumeFacesAndWeights to compute proper extents
+ pModel->normalizeVolumeFacesAndWeights();
+
+ // Fill joint names, bind matrices and remap weight indices
+ if (skinIdx >= 0)
+ {
+ LL::GLTF::Skin& gltf_skin = mGLTFAsset.mSkins[skinIdx];
+ LLMeshSkinInfo& skin_info = pModel->mSkinInfo;
+ S32 valid_joints_count = mValidJointsCount[skinIdx];
+
+ S32 replacement_index = 0;
+ std::vector<S32> gltfindex_to_joitindex_map;
+ size_t jointCnt = gltf_skin.mJoints.size();
+ gltfindex_to_joitindex_map.resize(jointCnt, -1);
+
+ if (valid_joints_count > (S32)mMaxJointsPerMesh)
+ {
+ std::map<std::string, S32> goup_use_count;
+
+ for (const auto& elem : mJointGroups)
+ {
+ goup_use_count[elem.second.mGroup] = 0;
+ goup_use_count[elem.second.mParentGroup] = 0;
+ }
+
+ // Assume that 'Torso' group is always in use since that's what everything else is attached to
+ goup_use_count["Torso"] = 1;
+ // Note that Collisions and Extra groups are all over the place, might want to include them from the start
+ // or add individual when parents are added
+
+ // Check which groups are in use
+ for (size_t i = 0; i < jointCnt; ++i)
+ {
+ std::string& joint_name = mJointNames[skinIdx][i];
+ if (!joint_name.empty())
+ {
+ if (gltf_joint_index_use[i] > 0)
+ {
+ const JointGroups &group = mJointGroups[joint_name];
+ // Joint in use, increment it's groups
+ goup_use_count[group.mGroup]++;
+ goup_use_count[group.mParentGroup]++;
+ }
+ }
+ }
+
+ // 1. add joints that are in use directly
+ for (size_t i = 0; i < jointCnt; ++i)
+ {
+ // Process joint name and idnex
+ S32 joint = gltf_skin.mJoints[i];
+ if (gltf_joint_index_use[i] <= 0)
+ {
+ // unsupported (-1) joint, drop it
+ // unused (0) joint, drop it
+ continue;
+ }
+
+ if (addJointToModelSkin(skin_info, skinIdx, i))
+ {
+ gltfindex_to_joitindex_map[i] = replacement_index++;
+ }
+ }
+
+ // 2. add joints from groups that this model's joints belong to
+ // It's perfectly valid to have more joints than is in use
+ // Ex: sandals that make your legs digitigrade despite not skining to
+ // knees or the like.
+ // Todo: sort and add by usecount
+ for (size_t i = 0; i < jointCnt; ++i)
+ {
+ S32 joint = gltf_skin.mJoints[i];
+ if (gltf_joint_index_use[i] != 0)
+ {
+ // this step needs only joints that have zero uses
+ continue;
+ }
+ if (skin_info.mInvBindMatrix.size() > mMaxJointsPerMesh)
+ {
+ break;
+ }
+ const std::string& legal_name = mJointNames[skinIdx][i];
+ std::string group_name = mJointGroups[legal_name].mGroup;
+ if (goup_use_count[group_name] > 0)
+ {
+ if (addJointToModelSkin(skin_info, skinIdx, i))
+ {
+ gltfindex_to_joitindex_map[i] = replacement_index++;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Less than 110, just add every valid joint
+ for (size_t i = 0; i < jointCnt; ++i)
+ {
+ // Process joint name and idnex
+ S32 joint = gltf_skin.mJoints[i];
+ if (gltf_joint_index_use[i] < 0)
+ {
+ // unsupported (-1) joint, drop it
+ continue;
+ }
+
+ if (addJointToModelSkin(skin_info, skinIdx, i))
+ {
+ gltfindex_to_joitindex_map[i] = replacement_index++;
+ }
+ }
+ }
+
+ if (skin_info.mInvBindMatrix.size() > mMaxJointsPerMesh)
+ {
+ // mMaxJointsPerMesh ususlly is equal to LL_MAX_JOINTS_PER_MESH_OBJECT
+ // and is 110.
+ LL_WARNS("GLTF_IMPORT") << "Too many jonts in " << pModel->mLabel
+ << " Count: " << (S32)skin_info.mInvBindMatrix.size()
+ << " Limit:" << (S32)mMaxJointsPerMesh << LL_ENDL;
+ LLSD args;
+ args["Message"] = "ModelTooManyJoints";
+ args["MODEL_NAME"] = pModel->mLabel;
+ args["JOINT_COUNT"] = (S32)skin_info.mInvBindMatrix.size();
+ args["MAX"] = (S32)mMaxJointsPerMesh;
+ mWarningsArray.append(args);
+ }
+
+ // Remap indices for pModel->mSkinWeights
+ for (auto& weights : pModel->mSkinWeights)
+ {
+ for (auto& weight : weights.second)
+ {
+ weight.mJointIdx = gltfindex_to_joitindex_map[weight.mJointIdx];
+ }
+ }
+ }
+
+ return true;
+}
+
+void LLGLTFLoader::populateJointsFromSkin(S32 skin_idx)
+{
+ const LL::GLTF::Skin& skin = mGLTFAsset.mSkins[skin_idx];
+
+ LL_INFOS("GLTF_DEBUG") << "populateJointFromSkin: Processing skin " << skin_idx << " with " << skin.mJoints.size() << " joints" << LL_ENDL;
+
+ if (skin.mInverseBindMatrices > 0 && skin.mJoints.size() != skin.mInverseBindMatricesData.size())
+ {
+ LL_INFOS("GLTF_IMPORT") << "Bind matrices count mismatch joints count" << LL_ENDL;
+ LLSD args;
+ args["Message"] = "InvBindCountMismatch";
+ mWarningsArray.append(args);
+ }
+
+ S32 joint_count = (S32)skin.mJoints.size();
+ S32 inverse_count = (S32)skin.mInverseBindMatricesData.size();
+ if (mInverseBindMatrices.size() <= skin_idx)
+ {
+ mInverseBindMatrices.resize(skin_idx + 1);
+ mAlternateBindMatrices.resize(skin_idx + 1);
+ mJointNames.resize(skin_idx + 1);
+ mJointUsage.resize(skin_idx + 1);
+ mValidJointsCount.resize(skin_idx + 1, 0);
+ }
+
+ // fill up joints related data
+ joints_data_map_t joints_data;
+ joints_name_to_node_map_t names_to_nodes;
+ for (S32 i = 0; i < joint_count; i++)
+ {
+ S32 joint = skin.mJoints[i];
+ const LL::GLTF::Node &jointNode = mGLTFAsset.mNodes[joint];
+ JointNodeData& data = joints_data[joint];
+ data.mNodeIdx = joint;
+ data.mJointListIdx = i;
+ data.mGltfRestMatrix = buildGltfRestMatrix(joint, skin);
+ data.mGltfMatrix = jointNode.mMatrix;
+ data.mOverrideMatrix = glm::mat4(1.f);
+
+ if (mJointMap.find(jointNode.mName) != mJointMap.end())
+ {
+ data.mName = mJointMap[jointNode.mName];
+ data.mIsValidViewerJoint = true;
+ mValidJointsCount[skin_idx]++;
+ }
+ else
+ {
+ data.mName = jointNode.mName;
+ data.mIsValidViewerJoint = false;
+ }
+ names_to_nodes[data.mName] = joint;
+
+ for (S32 child : jointNode.mChildren)
+ {
+ JointNodeData& child_data = joints_data[child];
+ child_data.mParentNodeIdx = joint;
+ child_data.mIsParentValidViewerJoint = data.mIsValidViewerJoint;
+ }
+ }
+
+ // Go over viewer joints and build overrides
+ // This is needed because gltf skeleton doesn't necessarily match viewer's skeleton.
+ glm::mat4 ident(1.0);
+ for (auto &viewer_data : mViewerJointData)
+ {
+ buildOverrideMatrix(viewer_data, joints_data, names_to_nodes, ident, ident);
+ }
+
+ for (S32 i = 0; i < joint_count; i++)
+ {
+ S32 joint = skin.mJoints[i];
+ const LL::GLTF::Node &jointNode = mGLTFAsset.mNodes[joint];
+ std::string legal_name(jointNode.mName);
+
+ // Viewer supports a limited set of joints, mark them as legal
+ bool legal_joint = false;
+ if (mJointMap.find(legal_name) != mJointMap.end())
+ {
+ legal_name = mJointMap[legal_name];
+ legal_joint = true;
+ mJointNames[skin_idx].push_back(legal_name);
+ }
+ else
+ {
+ mJointNames[skin_idx].emplace_back();
+ }
+ mJointUsage[skin_idx].push_back(0);
+
+ // Compute bind matrices
+
+ if (!legal_joint)
+ {
+ // Add placeholder to not break index.
+ // Not going to be used by viewer, will be stripped from skin_info.
+ LLMatrix4 gltf_transform;
+ gltf_transform.setIdentity();
+ mInverseBindMatrices[skin_idx].push_back(LLMatrix4a(gltf_transform));
+ }
+ else if (inverse_count > i)
+ {
+ // Transalte existing bind matrix to viewer's overriden skeleton
+ glm::mat4 original_bind_matrix = glm::inverse(skin.mInverseBindMatricesData[i]);
+ glm::mat4 rotated_original = coord_system_rotation * original_bind_matrix;
+ glm::mat4 skeleton_transform = computeGltfToViewerSkeletonTransform(joints_data, joint, legal_name);
+ glm::mat4 tranlated_original = skeleton_transform * rotated_original;
+ glm::mat4 final_inverse_bind_matrix = glm::inverse(tranlated_original);
+
+ LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(final_inverse_bind_matrix));
+ LL_DEBUGS("GLTF_DEBUG") << "mInvBindMatrix name: " << legal_name << " Translated val: " << gltf_transform << LL_ENDL;
+ mInverseBindMatrices[skin_idx].push_back(LLMatrix4a(gltf_transform));
+ }
+ else
+ {
+ // If bind matrices aren't present (they are optional in gltf),
+ // assume an identy matrix
+ // todo: find a model with this, might need to use YZ rotated matrix
+ glm::mat4 inv_bind(1.0f);
+ glm::mat4 skeleton_transform = computeGltfToViewerSkeletonTransform(joints_data, joint, legal_name);
+ inv_bind = glm::inverse(skeleton_transform * inv_bind);
+
+ LLMatrix4 gltf_transform = LLMatrix4(glm::value_ptr(inv_bind));
+ LL_DEBUGS("GLTF_DEBUG") << "mInvBindMatrix name: " << legal_name << " Generated val: " << gltf_transform << LL_ENDL;
+ mInverseBindMatrices[skin_idx].push_back(LLMatrix4a(gltf_transform));
+ }
+
+ // Compute Alternative matrices also known as overrides
+ LLMatrix4 original_joint_transform(glm::value_ptr(joints_data[joint].mOverrideMatrix));
+
+ // Viewer seems to care only about translation part,
+ // but for parity with collada taking original value
+ LLMatrix4 newInverse = LLMatrix4(mInverseBindMatrices[skin_idx].back().getF32ptr());
+ newInverse.setTranslation(original_joint_transform.getTranslation());
+
+ LL_DEBUGS("GLTF_DEBUG") << "mAlternateBindMatrix name: " << legal_name << " val: " << newInverse << LL_ENDL;
+ mAlternateBindMatrices[skin_idx].push_back(LLMatrix4a(newInverse));
+
+ if (legal_joint)
+ {
+ // Might be needed for uploader UI to correctly identify overriden joints
+ // but going to be incorrect if multiple skins are present
+ mJointList[legal_name] = newInverse;
+ mJointsFromNode.push_front(legal_name);
+ }
+ }
+
+ S32 valid_joints = mValidJointsCount[skin_idx];
+ if (valid_joints < joint_count)
+ {
+ LL_INFOS("GLTF_IMPORT") << "Skin " << skin_idx
+ << " defines " << joint_count
+ << " joints, but only " << valid_joints
+ << " were recognized and are compatible." << LL_ENDL;
+ LLSD args;
+ args["Message"] = "SkinUsupportedJoints";
+ args["SKIN_INDEX"] = skin_idx;
+ args["JOINT_COUNT"] = joint_count;
+ args["LEGAL_COUNT"] = valid_joints;
+ mWarningsArray.append(args);
+ }
+}
+
+void LLGLTFLoader::populateJointGroups()
+{
+ std::string parent;
+ for (auto& viewer_data : mViewerJointData)
+ {
+ buildJointGroup(viewer_data, parent);
+ }
+}
+
+void LLGLTFLoader::buildJointGroup(LLJointData& viewer_data, const std::string &parent_group)
+{
+ JointGroups& jount_group_data = mJointGroups[viewer_data.mName];
+ jount_group_data.mGroup = viewer_data.mGroup;
+ jount_group_data.mParentGroup = parent_group;
+
+ for (LLJointData& child_data : viewer_data.mChildren)
+ {
+ buildJointGroup(child_data, viewer_data.mGroup);
+ }
+}
+
+void LLGLTFLoader::buildOverrideMatrix(LLJointData& viewer_data, joints_data_map_t &gltf_nodes, joints_name_to_node_map_t &names_to_nodes, glm::mat4& parent_rest, glm::mat4& parent_support_rest) const
+{
+ glm::mat4 rest(1.f);
+ joints_name_to_node_map_t::iterator found_node = names_to_nodes.find(viewer_data.mName);
+ if (found_node != names_to_nodes.end())
+ {
+ S32 gltf_node_idx = found_node->second;
+ JointNodeData& node = gltf_nodes[gltf_node_idx];
+ node.mIsOverrideValid = true;
+ node.mViewerRestMatrix = viewer_data.mRestMatrix;
+
+ glm::mat4 gltf_joint_rest_pose = coord_system_rotation * node.mGltfRestMatrix;
+ if (mApplyXYRotation)
+ {
+ gltf_joint_rest_pose = coord_system_rotationxy * gltf_joint_rest_pose;
+ }
+
+ glm::mat4 translated_joint;
+ // Example:
+ // Viewer has pelvis->spine1->spine2->torso.
+ // gltf example model has pelvis->torso
+ // By doing glm::inverse(transalted_rest_spine2) * gltf_rest_torso
+ // We get what torso would have looked like if gltf had a spine2
+ if (viewer_data.mIsJoint)
+ {
+ translated_joint = glm::inverse(parent_rest) * gltf_joint_rest_pose;
+ }
+ else
+ {
+ translated_joint = glm::inverse(parent_support_rest) * gltf_joint_rest_pose;
+ }
+
+ glm::vec3 translation_override;
+ glm::vec3 skew;
+ glm::vec3 scale;
+ glm::vec4 perspective;
+ glm::quat rotation;
+ glm::decompose(translated_joint, scale, rotation, translation_override, skew, perspective);
+
+ // Viewer allows overrides, which are base joint with applied translation override.
+ // fortunately normal bones use only translation, without rotation or scale
+ node.mOverrideMatrix = glm::recompose(glm::vec3(1, 1, 1), glm::identity<glm::quat>(), translation_override, glm::vec3(0, 0, 0), glm::vec4(0, 0, 0, 1));
+
+ glm::mat4 overriden_joint = node.mOverrideMatrix;
+
+ // todo: if gltf bone had rotation or scale, they probably should be saved here
+ // then applied to bind matrix
+ rest = parent_rest * overriden_joint;
+ if (viewer_data.mIsJoint)
+ {
+ node.mOverrideRestMatrix = rest;
+ }
+ else
+ {
+ // This is likely incomplete or even wrong.
+ // Viewer Collision bones specify rotation and scale.
+ // Importer should apply rotation and scale to this matrix and save as needed
+ // then subsctruct them from bind matrix
+ // Todo: get models that use collision bones, made by different programs
+
+ overriden_joint = glm::scale(overriden_joint, viewer_data.mScale);
+ node.mOverrideRestMatrix = parent_support_rest * overriden_joint;
+ }
+ }
+ else
+ {
+ // No override for this joint
+ rest = parent_rest * viewer_data.mJointMatrix;
+ }
+
+ glm::mat4 support_rest(1.f);
+ if (viewer_data.mSupport == LLJointData::SUPPORT_BASE)
+ {
+ support_rest = rest;
+ }
+ else
+ {
+ support_rest = parent_support_rest;
+ }
+
+ for (LLJointData& child_data : viewer_data.mChildren)
+ {
+ buildOverrideMatrix(child_data, gltf_nodes, names_to_nodes, rest, support_rest);
+ }
+}
+
+glm::mat4 LLGLTFLoader::buildGltfRestMatrix(S32 joint_node_index, const LL::GLTF::Skin& gltf_skin) const
+{
+ // This is inefficient since we are recalculating some joints multiple times over
+ // Todo: cache it?
+
+ if (joint_node_index < 0 || joint_node_index >= static_cast<S32>(mGLTFAsset.mNodes.size()))
+ {
+ return glm::mat4(1.0f);
+ }
+
+ const auto& node = mGLTFAsset.mNodes[joint_node_index];
+
+ // Find and apply parent transform if it exists
+ for (size_t i = 0; i < mGLTFAsset.mNodes.size(); ++i)
+ {
+ const auto& potential_parent = mGLTFAsset.mNodes[i];
+ auto it = std::find(potential_parent.mChildren.begin(), potential_parent.mChildren.end(), joint_node_index);
+
+ if (it != potential_parent.mChildren.end())
+ {
+ // Found parent
+ if (std::find(gltf_skin.mJoints.begin(), gltf_skin.mJoints.end(), joint_node_index) != gltf_skin.mJoints.end())
+ {
+ // parent is a joint - recursively combine transform
+ // assumes that matrix is already valid
+ return buildGltfRestMatrix(static_cast<S32>(i), gltf_skin) * node.mMatrix;
+ }
+ }
+ }
+ // Should we return armature or stop earlier?
+ return node.mMatrix;
+}
+
+glm::mat4 LLGLTFLoader::buildGltfRestMatrix(S32 joint_node_index, const joints_data_map_t& joint_data) const
+{
+ // This is inefficient since we are recalculating some joints multiple times over
+ // Todo: cache it?
+
+ if (joint_node_index < 0 || joint_node_index >= static_cast<S32>(mGLTFAsset.mNodes.size()))
+ {
+ return glm::mat4(1.0f);
+ }
+
+ auto& data = joint_data.at(joint_node_index);
+
+ if (data.mParentNodeIdx >=0)
+ {
+ return buildGltfRestMatrix(data.mParentNodeIdx, joint_data) * data.mGltfMatrix;
+ }
+ // Should we return armature or stop earlier?
+ return data.mGltfMatrix;
+}
+
+// This function computes the transformation matrix needed to convert from GLTF skeleton space
+// to viewer skeleton space for a specific joint
+
+glm::mat4 LLGLTFLoader::computeGltfToViewerSkeletonTransform(const joints_data_map_t& joints_data_map, S32 gltf_node_index, const std::string& joint_name) const
+{
+ const JointNodeData& node_data = joints_data_map.at(gltf_node_index);
+ if (!node_data.mIsOverrideValid)
+ {
+ // For now assume they are identical and return an identity (for ease of debuging)
+ return glm::mat4(1.0f);
+ }
+
+ // Get the GLTF joint's rest pose (in GLTF coordinate system)
+ const glm::mat4 &gltf_joint_rest_pose = node_data.mGltfRestMatrix;
+ glm::mat4 rest_pose = coord_system_rotation * gltf_joint_rest_pose;
+
+ LL_INFOS("GLTF_DEBUG") << "rest matrix for joint " << joint_name << ": ";
+
+ LLMatrix4 transform(glm::value_ptr(rest_pose));
+
+ LL_CONT << transform << LL_ENDL;
+
+ // Compute transformation from GLTF space to viewer space
+ // This assumes both skeletons are in rest pose initially
+ return node_data.mOverrideRestMatrix * glm::inverse(rest_pose);
+}
+
+bool LLGLTFLoader::checkForXYrotation(const LL::GLTF::Skin& gltf_skin, S32 joint_idx, S32 bind_indx)
+{
+ glm::mat4 gltf_joint_rest = buildGltfRestMatrix(joint_idx, gltf_skin);
+ glm::mat4 test_mat = glm::inverse(gltf_joint_rest) * gltf_skin.mInverseBindMatricesData[bind_indx];
+ // Normally for shoulders it should be something close to
+ // {1,0,0,0;0,-1,0,0;0,0,-1,0;0,0,0,1}
+ // rotated one will look like
+ // {0,0,0,-1;1,0,0,0;0,-1,0,0;0,0,0,1}
+ // Todo: This is a cheap hack,
+ // figure out how rotation is supposed to work
+ return abs(test_mat[0][0]) < 0.5 && abs(test_mat[1][1]) < 0.5 && abs(test_mat[2][2]) < 0.5;
+}
+
+void LLGLTFLoader::checkForXYrotation(const LL::GLTF::Skin& gltf_skin)
+{
+ // HACK: figure out model's rotation from shoulders' matrix.
+ // This is wrong on many levels:
+ // Too limited (only models that have shoulders),
+ // Will not work well with things that emulate 3 hands in some manner
+ // Only supports xy 90 degree rotation
+ // Todo: figure out how to find skeleton's orientation Correctly
+ // when model is rotated at a triangle level
+ constexpr char right_shoulder_str[] = "mShoulderRight";
+ constexpr char left_shoulder_str[] = "mShoulderLeft";
+
+ S32 size = (S32)gltf_skin.mJoints.size();
+ S32 joints_found = 0;
+ for (S32 i= 0; i < size; i++)
+ {
+ S32 joint = gltf_skin.mJoints[i];
+ const LL::GLTF::Node &joint_node = mGLTFAsset.mNodes[joint];
+
+ // todo: we are doing this search thing everywhere,
+ // just pre-translate every joint
+ JointMap::iterator found = mJointMap.find(joint_node.mName);
+ if (found == mJointMap.end())
+ {
+ // unsupported joint
+ continue;
+ }
+ if (found->second == right_shoulder_str || found->second == left_shoulder_str)
+ {
+ if (checkForXYrotation(gltf_skin, joint, i))
+ {
+ joints_found++;
+ }
+ else
+ {
+ return;
+ }
+ }
+ }
+
+ if (joints_found == 2)
+ {
+ // Both joints in a weird position/rotation, assume rotated model
+ mApplyXYRotation = true;
+ }
+}
+
+void LLGLTFLoader::checkGlobalJointUsage()
+{
+ // Check if some joints remained unused
+ for (S32 skin_idx = 0; skin_idx < (S32)mGLTFAsset.mSkins.size(); ++skin_idx)
+ {
+ const LL::GLTF::Skin& gltf_skin = mGLTFAsset.mSkins[skin_idx];
+ S32 joint_count = (S32)gltf_skin.mJoints.size();
+ S32 used_joints = 0;
+ for (S32 i = 0; i < joint_count; ++i)
+ {
+ S32 joint = gltf_skin.mJoints[i];
+ if (mJointUsage[skin_idx][i] == 0)
+ {
+ // Joint is unused, log it
+ LL_INFOS("GLTF_DEBUG") << "Joint " << mJointNames[skin_idx][i]
+ << " in skin " << skin_idx << " is unused." << LL_ENDL;
+ }
+ else
+ {
+ used_joints++;
+ }
+ }
+
+ S32 valid_joints = mValidJointsCount[skin_idx];
+ if (valid_joints > used_joints)
+ {
+ S32 unsed_joints = valid_joints - used_joints;
+ LL_INFOS("GLTF_IMPORT") << "Skin " << skin_idx
+ << " declares " << valid_joints
+ << " valid joints, of them " << unsed_joints
+ << " remained unused" << LL_ENDL;
+ LLSD args;
+ args["Message"] = "SkinUnusedJoints";
+ args["SKIN_INDEX"] = (S32)skin_idx;
+ args["JOINT_COUNT"] = valid_joints;
+ args["USED_COUNT"] = used_joints;
+ mWarningsArray.append(args);
+ }
+ }
+}
+
+std::string LLGLTFLoader::extractTextureToTempFile(S32 textureIndex, const std::string& texture_type)
+{
+ if (textureIndex < 0 || textureIndex >= mGLTFAsset.mTextures.size())
+ return "";
+
+ S32 sourceIndex = mGLTFAsset.mTextures[textureIndex].mSource;
+ if (sourceIndex < 0 || sourceIndex >= mGLTFAsset.mImages.size())
+ return "";
+
+ LL::GLTF::Image& image = mGLTFAsset.mImages[sourceIndex];
+
+ // Handle URI-based textures
+ if (!image.mUri.empty())
+ {
+ return image.mUri; // Return URI directly
+ }
+
+ // Handle embedded textures
+ if (image.mBufferView >= 0)
+ {
+ if (image.mBufferView < mGLTFAsset.mBufferViews.size())
+ {
+ const LL::GLTF::BufferView& buffer_view = mGLTFAsset.mBufferViews[image.mBufferView];
+ if (buffer_view.mBuffer < mGLTFAsset.mBuffers.size())
+ {
+ const LL::GLTF::Buffer& buffer = mGLTFAsset.mBuffers[buffer_view.mBuffer];
+
+ if (buffer_view.mByteOffset + buffer_view.mByteLength <= buffer.mData.size())
+ {
+ // Extract image data
+ const U8* data_ptr = &buffer.mData[buffer_view.mByteOffset];
+ U32 data_size = buffer_view.mByteLength;
+
+ // Determine the file extension
+ std::string extension = ".png"; // Default
+ if (!image.mMimeType.empty())
+ {
+ if (image.mMimeType == "image/jpeg")
+ extension = ".jpg";
+ else if (image.mMimeType == "image/png")
+ extension = ".png";
+ }
+ else if (data_size >= 4)
+ {
+ if (data_ptr[0] == 0xFF && data_ptr[1] == 0xD8)
+ extension = ".jpg"; // JPEG magic bytes
+ else if (data_ptr[0] == 0x89 && data_ptr[1] == 0x50 && data_ptr[2] == 0x4E && data_ptr[3] == 0x47)
+ extension = ".png"; // PNG magic bytes
+ }
+
+ // Create a temporary file
+ std::string temp_dir = gDirUtilp->getTempDir();
+ std::string temp_filename = temp_dir + gDirUtilp->getDirDelimiter() +
+ "gltf_embedded_" + texture_type + "_" + std::to_string(sourceIndex) + extension;
+
+ // Write the image data to the temporary file
+ std::ofstream temp_file(temp_filename, std::ios::binary);
+ if (temp_file.is_open())
+ {
+ temp_file.write(reinterpret_cast<const char*>(data_ptr), data_size);
+ temp_file.close();
+
+ LL_INFOS("GLTF_IMPORT") << "Extracted embedded " << texture_type << " texture to: " << temp_filename << LL_ENDL;
+ return temp_filename;
+ }
+ else
+ {
+ LL_WARNS("GLTF_IMPORT") << "Failed to create temporary file for " << texture_type << " texture: " << temp_filename << LL_ENDL;
+
+ LLSD args;
+ args["Message"] = "FailedToCreateTempFile";
+ args["TEXTURE_INDEX"] = sourceIndex;
+ args["TEXTURE_TYPE"] = texture_type;
+ args["TEMP_FILE"] = temp_filename;
+ mWarningsArray.append(args);
+ }
+ }
+ }
+ }
+ }
+
+ return "";
+}
+
+void LLGLTFLoader::notifyUnsupportedExtension(bool unsupported)
+{
+ std::vector<std::string> extensions = unsupported ? mGLTFAsset.mUnsupportedExtensions : mGLTFAsset.mIgnoredExtensions;
+ if (extensions.size() > 0)
+ {
+ LLSD args;
+ args["Message"] = unsupported ? "UnsupportedExtension" : "IgnoredExtension";
+ std::string del;
+ std::string ext;
+ for (auto& extension : extensions)
+ {
+ ext += del;
+ ext += extension;
+ del = ",";
+ }
+ args["EXT"] = ext;
+ mWarningsArray.append(args);
+
+ LL_WARNS("GLTF_IMPORT") << "Model uses unsupported extension: " << ext << LL_ENDL;
+ }
+}
+
+size_t LLGLTFLoader::getSuffixPosition(const std::string &label)
+{
+ if ((label.find("_LOD") != -1) || (label.find("_PHYS") != -1))
+ {
+ return label.rfind('_');
+ }
+ return -1;
+}
+
+std::string LLGLTFLoader::getLodlessLabel(const LL::GLTF::Mesh& mesh)
+{
+ size_t ext_pos = getSuffixPosition(mesh.mName);
+ if (ext_pos != -1)
+ {
+ return mesh.mName.substr(0, ext_pos);
+ }
+ return mesh.mName;
+}
+
diff --git a/indra/newview/gltf/llgltfloader.h b/indra/newview/gltf/llgltfloader.h
new file mode 100644
index 0000000000..e8b91996c7
--- /dev/null
+++ b/indra/newview/gltf/llgltfloader.h
@@ -0,0 +1,216 @@
+/**
+ * @file LLGLTFLoader.h
+ * @brief LLGLTFLoader class definition
+ *
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2022, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLGLTFLoader_H
+#define LL_LLGLTFLoader_H
+
+#include "tinygltf/tiny_gltf.h"
+
+#include "asset.h"
+
+#include "llglheaders.h"
+#include "lljointdata.h"
+#include "llmodelloader.h"
+
+class LLGLTFLoader : public LLModelLoader
+{
+ public:
+ typedef std::map<std::string, LLImportMaterial> material_map;
+ typedef std::map<std::string, std::string> joint_viewer_parent_map_t;
+ typedef std::map<std::string, glm::mat4> joint_viewer_rest_map_t;
+ typedef std::map<S32, glm::mat4> joint_node_mat4_map_t;
+
+ struct JointNodeData
+ {
+ JointNodeData()
+ : mJointListIdx(-1)
+ , mNodeIdx(-1)
+ , mParentNodeIdx(-1)
+ , mIsValidViewerJoint(false)
+ , mIsParentValidViewerJoint(false)
+ , mIsOverrideValid(false)
+ {
+
+ }
+ S32 mJointListIdx;
+ S32 mNodeIdx;
+ S32 mParentNodeIdx;
+ glm::mat4 mGltfRestMatrix;
+ glm::mat4 mViewerRestMatrix;
+ glm::mat4 mOverrideRestMatrix;
+ glm::mat4 mGltfMatrix;
+ glm::mat4 mOverrideMatrix;
+ std::string mName;
+ bool mIsValidViewerJoint;
+ bool mIsParentValidViewerJoint;
+ bool mIsOverrideValid;
+ };
+ typedef std::map <S32, JointNodeData> joints_data_map_t;
+ typedef std::map <std::string, S32> joints_name_to_node_map_t;
+
+ class LLGLTFImportMaterial : public LLImportMaterial
+ {
+ public:
+ std::string name;
+ LLGLTFImportMaterial() = default;
+ LLGLTFImportMaterial(const LLImportMaterial& mat, const std::string& n) : LLImportMaterial(mat), name(n) {}
+ };
+
+ LLGLTFLoader(std::string filename,
+ S32 lod,
+ LLModelLoader::load_callback_t load_cb,
+ LLModelLoader::joint_lookup_func_t joint_lookup_func,
+ LLModelLoader::texture_load_func_t texture_load_func,
+ LLModelLoader::state_callback_t state_cb,
+ void * opaque_userdata,
+ JointTransformMap & jointTransformMap,
+ JointNameSet & jointsFromNodes,
+ std::map<std::string, std::string, std::less<>> & jointAliasMap,
+ U32 maxJointsPerMesh,
+ U32 modelLimit,
+ U32 debugMode,
+ std::vector<LLJointData> viewer_skeleton); //,
+ //bool preprocess );
+ virtual ~LLGLTFLoader();
+
+ virtual bool OpenFile(const std::string &filename);
+
+ struct GLTFVertex
+ {
+ glm::vec3 position;
+ glm::vec3 normal;
+ glm::vec2 uv0;
+ glm::u16vec4 joints;
+ glm::vec4 weights;
+ };
+
+protected:
+ LL::GLTF::Asset mGLTFAsset;
+ tinygltf::Model mGltfModel;
+ bool mGltfLoaded = false;
+ bool mApplyXYRotation = false;
+
+ // GLTF isn't aware of viewer's skeleton and uses it's own,
+ // so need to take viewer's joints and use them to
+ // recalculate iverse bind matrices
+ std::vector<LLJointData> mViewerJointData;
+
+ // vector of vectors because of a posibility of having more than one skin
+ typedef std::vector<LLMeshSkinInfo::matrix_list_t> bind_matrices_t;
+ typedef std::vector<std::vector<std::string> > joint_names_t;
+ bind_matrices_t mInverseBindMatrices;
+ bind_matrices_t mAlternateBindMatrices;
+ joint_names_t mJointNames; // empty string when no legal name for a given idx
+ std::vector<std::vector<S32>> mJointUsage; // detect and warn about unsed joints
+
+ // what group a joint belongs to.
+ // For purpose of stripping unused groups when joints are over limit.
+ struct JointGroups
+ {
+ std::string mGroup;
+ std::string mParentGroup;
+ };
+ typedef std::map<std::string, JointGroups, std::less<> > joint_to_group_map_t;
+ joint_to_group_map_t mJointGroups;
+
+ // per skin joint count, needs to be tracked for the sake of limits check.
+ std::vector<S32> mValidJointsCount;
+
+ // Cached material information
+ typedef std::map<S32, LLGLTFImportMaterial> MaterialCache;
+ MaterialCache mMaterialCache;
+
+private:
+ bool parseMeshes();
+ void computeCombinedNodeTransform(const LL::GLTF::Asset& asset, S32 node_index, glm::mat4& combined_transform) const;
+ void processNodeHierarchy(S32 node_idx, std::map<std::string, S32>& mesh_name_counts, U32 submodel_limit, const LLVolumeParams& volume_params);
+ bool addJointToModelSkin(LLMeshSkinInfo& skin_info, S32 gltf_skin_idx, size_t gltf_joint_idx);
+ LLGLTFImportMaterial processMaterial(S32 material_index, S32 fallback_index);
+ std::string processTexture(S32 texture_index, const std::string& texture_type, const std::string& material_name);
+ bool validateTextureIndex(S32 texture_index, S32& source_index);
+ std::string generateMaterialName(S32 material_index, S32 fallback_index = -1);
+ bool populateModelFromMesh(LLModel* pModel, const std::string& base_name, const LL::GLTF::Mesh &mesh, const LL::GLTF::Node &node, material_map& mats);
+ void populateJointsFromSkin(S32 skin_idx);
+ void populateJointGroups();
+ void addModelToScene(LLModel* pModel, const std::string& model_name, U32 submodel_limit, const LLMatrix4& transformation, const LLVolumeParams& volume_params, const material_map& mats);
+ void buildJointGroup(LLJointData& viewer_data, const std::string& parent_group);
+ void buildOverrideMatrix(LLJointData& data, joints_data_map_t &gltf_nodes, joints_name_to_node_map_t &names_to_nodes, glm::mat4& parent_rest, glm::mat4& support_rest) const;
+ glm::mat4 buildGltfRestMatrix(S32 joint_node_index, const LL::GLTF::Skin& gltf_skin) const;
+ glm::mat4 buildGltfRestMatrix(S32 joint_node_index, const joints_data_map_t& joint_data) const;
+ glm::mat4 computeGltfToViewerSkeletonTransform(const joints_data_map_t& joints_data_map, S32 gltf_node_index, const std::string& joint_name) const;
+ bool checkForXYrotation(const LL::GLTF::Skin& gltf_skin, S32 joint_idx, S32 bind_indx);
+ void checkForXYrotation(const LL::GLTF::Skin& gltf_skin);
+ void checkGlobalJointUsage();
+
+ std::string extractTextureToTempFile(S32 textureIndex, const std::string& texture_type);
+
+ void notifyUnsupportedExtension(bool unsupported);
+
+ static size_t getSuffixPosition(const std::string& label);
+ static std::string getLodlessLabel(const LL::GLTF::Mesh& mesh);
+
+ // bool mPreprocessGLTF;
+
+ /* Below inherited from dae loader - unknown if/how useful here
+
+ void processElement(gltfElement *element, bool &badElement, GLTF *gltf);
+ void processGltfModel(LLModel *model, GLTF *gltf, gltfElement *pRoot, gltfMesh *mesh, gltfSkin *skin);
+
+ material_map getMaterials(LLModel *model, gltfInstance_geometry *instance_geo, GLTF *gltf);
+ LLImportMaterial profileToMaterial(gltfProfile_COMMON *material, GLTF *gltf);
+ LLColor4 getGltfColor(gltfElement *element);
+
+ gltfElement *getChildFromElement(gltfElement *pElement, std::string const &name);
+
+ bool isNodeAJoint(gltfNode *pNode);
+ void processJointNode(gltfNode *pNode, std::map<std::string, LLMatrix4> &jointTransforms);
+ void extractTranslation(gltfTranslate *pTranslate, LLMatrix4 &transform);
+ void extractTranslationViaElement(gltfElement *pTranslateElement, LLMatrix4 &transform);
+ void extractTranslationViaSID(gltfElement *pElement, LLMatrix4 &transform);
+ void buildJointToNodeMappingFromScene(gltfElement *pRoot);
+ void processJointToNodeMapping(gltfNode *pNode);
+ void processChildJoints(gltfNode *pParentNode);
+
+ bool verifyCount(int expected, int result);
+
+ // Verify that a controller matches vertex counts
+ bool verifyController(gltfController *pController);
+
+ static bool addVolumeFacesFromGltfMesh(LLModel *model, gltfMesh *mesh, LLSD &log_msg);
+ static bool createVolumeFacesFromGltfMesh(LLModel *model, gltfMesh *mesh);
+
+ static LLModel *loadModelFromGltfMesh(gltfMesh *mesh);
+
+ // Loads a mesh breaking it into one or more models as necessary
+ // to get around volume face limitations while retaining >8 materials
+ //
+ bool loadModelsFromGltfMesh(gltfMesh *mesh, std::vector<LLModel *> &models_out, U32 submodel_limit);
+
+ static std::string preprocessGLTF(std::string filename);
+ */
+
+};
+#endif // LL_LLGLTFLLOADER_H
diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp
index 9a381f9ba6..5dec4dd62f 100644
--- a/indra/newview/gltfscenemanager.cpp
+++ b/indra/newview/gltfscenemanager.cpp
@@ -317,7 +317,7 @@ void GLTFSceneManager::load(const std::string& filename)
{
std::shared_ptr<Asset> asset = std::make_shared<Asset>();
- if (asset->load(filename))
+ if (asset->load(filename, true))
{
gDebugProgram.bind(); // bind a shader to satisfy LLVertexBuffer assertions
asset->updateTransforms();
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index f7b48d25f2..9ce14eb0ab 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -3477,11 +3477,14 @@ void LLAgent::initOriginGlobal(const LLVector3d &origin_global)
bool LLAgent::leftButtonGrabbed() const
{
- const bool camera_mouse_look = gAgentCamera.cameraMouselook();
- return (!camera_mouse_look && mControlsTakenCount[CONTROL_LBUTTON_DOWN_INDEX] > 0)
- || (camera_mouse_look && mControlsTakenCount[CONTROL_ML_LBUTTON_DOWN_INDEX] > 0)
- || (!camera_mouse_look && mControlsTakenPassedOnCount[CONTROL_LBUTTON_DOWN_INDEX] > 0)
- || (camera_mouse_look && mControlsTakenPassedOnCount[CONTROL_ML_LBUTTON_DOWN_INDEX] > 0);
+ if (gAgentCamera.cameraMouselook())
+ {
+ return mControlsTakenCount[CONTROL_ML_LBUTTON_DOWN_INDEX] > 0;
+ }
+ else
+ {
+ return mControlsTakenCount[CONTROL_LBUTTON_DOWN_INDEX] > 0;
+ }
}
bool LLAgent::rotateGrabbed() const
diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp
index 4856eb1199..3a0d55eeba 100644
--- a/indra/newview/llagentcamera.cpp
+++ b/indra/newview/llagentcamera.cpp
@@ -1990,16 +1990,6 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(bool *hit_limit)
isConstrained = true;
}
}
-
-// JC - Could constrain camera based on parcel stuff here.
-// LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromPosGlobal(camera_position_global);
-//
-// if (regionp && !regionp->mParcelOverlay->isBuildCameraAllowed(regionp->getPosRegionFromGlobal(camera_position_global)))
-// {
-// camera_position_global = last_position_global;
-//
-// isConstrained = true;
-// }
}
// Don't let camera go underground
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 419d2cb842..91232c0bed 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -2302,10 +2302,7 @@ void errorCallback(LLError::ELevel level, const std::string &error_string)
// Callback for LLError::LLUserWarningMsg
void errorHandler(const std::string& title_string, const std::string& message_string, S32 code)
{
- if (!message_string.empty())
- {
- OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK);
- }
+ // message is going to hang viewer, create marker first
switch (code)
{
case LLError::LLUserWarningMsg::ERROR_OTHER:
@@ -2313,6 +2310,10 @@ void errorHandler(const std::string& title_string, const std::string& message_st
break;
case LLError::LLUserWarningMsg::ERROR_BAD_ALLOC:
LLAppViewer::instance()->createErrorMarker(LAST_EXEC_BAD_ALLOC);
+ // When system run out of memory and errorHandler gets called from a thread,
+ // main thread might keep going while OSMessageBox freezes the caller.
+ // Todo: handle it better, but for now disconnect to avoid making things worse
+ gDisconnected = true;
break;
case LLError::LLUserWarningMsg::ERROR_MISSING_FILES:
LLAppViewer::instance()->createErrorMarker(LAST_EXEC_MISSING_FILES);
@@ -2320,6 +2321,10 @@ void errorHandler(const std::string& title_string, const std::string& message_st
default:
break;
}
+ if (!message_string.empty())
+ {
+ OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK);
+ }
}
void LLAppViewer::initLoggingAndGetLastDuration()
@@ -5737,9 +5742,30 @@ void LLAppViewer::forceErrorThreadCrash()
thread->start();
}
-void LLAppViewer::initMainloopTimeout(const std::string& state, F32 secs)
+void LLAppViewer::forceExceptionThreadCrash()
+{
+ class LLCrashTestThread : public LLThread
+ {
+ public:
+
+ LLCrashTestThread() : LLThread("Crash logging test thread")
+ {
+ }
+
+ void run()
+ {
+ throw std::exception();
+ }
+ };
+
+ LL_WARNS() << "This is a deliberate exception in a thread" << LL_ENDL;
+ LLCrashTestThread* thread = new LLCrashTestThread();
+ thread->start();
+}
+
+void LLAppViewer::initMainloopTimeout(std::string_view state, F32 secs)
{
- if(!mMainloopTimeout)
+ if (!mMainloopTimeout)
{
mMainloopTimeout = new LLWatchdogTimeout();
resumeMainloopTimeout(state, secs);
@@ -5748,20 +5774,20 @@ void LLAppViewer::initMainloopTimeout(const std::string& state, F32 secs)
void LLAppViewer::destroyMainloopTimeout()
{
- if(mMainloopTimeout)
+ if (mMainloopTimeout)
{
delete mMainloopTimeout;
- mMainloopTimeout = NULL;
+ mMainloopTimeout = nullptr;
}
}
-void LLAppViewer::resumeMainloopTimeout(const std::string& state, F32 secs)
+void LLAppViewer::resumeMainloopTimeout(std::string_view state, F32 secs)
{
- if(mMainloopTimeout)
+ if (mMainloopTimeout)
{
- if(secs < 0.0f)
+ if (secs < 0.0f)
{
- static LLCachedControl<F32> mainloop_timeout(gSavedSettings, "MainloopTimeoutDefault", 60);
+ static LLCachedControl<F32> mainloop_timeout(gSavedSettings, "MainloopTimeoutDefault", 60.f);
secs = mainloop_timeout;
}
@@ -5772,19 +5798,19 @@ void LLAppViewer::resumeMainloopTimeout(const std::string& state, F32 secs)
void LLAppViewer::pauseMainloopTimeout()
{
- if(mMainloopTimeout)
+ if (mMainloopTimeout)
{
mMainloopTimeout->stop();
}
}
-void LLAppViewer::pingMainloopTimeout(const std::string& state, F32 secs)
+void LLAppViewer::pingMainloopTimeout(std::string_view state, F32 secs)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_APP;
- if(mMainloopTimeout)
+ if (mMainloopTimeout)
{
- if(secs < 0.0f)
+ if (secs < 0.0f)
{
static LLCachedControl<F32> mainloop_timeout(gSavedSettings, "MainloopTimeoutDefault", 60);
secs = mainloop_timeout;
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index 3da0246ccf..bafe952659 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -175,6 +175,7 @@ public:
virtual void forceErrorCoroprocedureCrash();
virtual void forceErrorWorkQueueCrash();
virtual void forceErrorThreadCrash();
+ virtual void forceExceptionThreadCrash();
// The list is found in app_settings/settings_files.xml
// but since they are used explicitly in code,
@@ -197,11 +198,11 @@ public:
// For thread debugging.
// llstartup needs to control init.
// llworld, send_agent_pause() also controls pause/resume.
- void initMainloopTimeout(const std::string& state, F32 secs = -1.0f);
+ void initMainloopTimeout(std::string_view state, F32 secs = -1.0f);
void destroyMainloopTimeout();
void pauseMainloopTimeout();
- void resumeMainloopTimeout(const std::string& state = "", F32 secs = -1.0f);
- void pingMainloopTimeout(const std::string& state, F32 secs = -1.0f);
+ void resumeMainloopTimeout(std::string_view state = "", F32 secs = -1.0f);
+ void pingMainloopTimeout(std::string_view state, F32 secs = -1.0f);
// Handle the 'login completed' event.
// *NOTE:Mani Fix this for login abstraction!!
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 67a44cfb27..f3214e2a20 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -832,6 +832,29 @@ bool LLAppViewerWin32::reportCrashToBugsplat(void* pExcepInfo)
return false;
}
+bool LLAppViewerWin32::initWindow()
+{
+ // This is a workaround/hotfix for a change in Windows 11 24H2 (and possibly later)
+ // Where the window width and height need to correctly reflect an available FullScreen size
+ if (gSavedSettings.getBOOL("FullScreen"))
+ {
+ DEVMODE dev_mode;
+ ::ZeroMemory(&dev_mode, sizeof(DEVMODE));
+ dev_mode.dmSize = sizeof(DEVMODE);
+ if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode))
+ {
+ gSavedSettings.setU32("WindowWidth", dev_mode.dmPelsWidth);
+ gSavedSettings.setU32("WindowHeight", dev_mode.dmPelsHeight);
+ }
+ else
+ {
+ LL_WARNS("AppInit") << "Unable to set WindowWidth and WindowHeight for FullScreen mode" << LL_ENDL;
+ }
+ }
+
+ return LLAppViewer::initWindow();
+}
+
void LLAppViewerWin32::initLoggingAndGetLastDuration()
{
LLAppViewer::initLoggingAndGetLastDuration();
diff --git a/indra/newview/llappviewerwin32.h b/indra/newview/llappviewerwin32.h
index 250e72edf3..3fad53ec72 100644
--- a/indra/newview/llappviewerwin32.h
+++ b/indra/newview/llappviewerwin32.h
@@ -46,6 +46,7 @@ public:
bool reportCrashToBugsplat(void* pExcepInfo) override;
protected:
+ bool initWindow() override; // Override to initialize the viewer's window.
void initLoggingAndGetLastDuration() override; // Override to clean stack_trace info.
void initConsole() override; // Initialize OS level debugging console.
bool initHardwareTest() override; // Win32 uses DX9 to test hardware.
diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp
index 2516bece8d..9e19d16957 100644
--- a/indra/newview/llfilepicker.cpp
+++ b/indra/newview/llfilepicker.cpp
@@ -64,7 +64,7 @@ LLFilePicker LLFilePicker::sInstance;
#define XML_FILTER L"XML files (*.xml)\0*.xml\0"
#define SLOBJECT_FILTER L"Objects (*.slobject)\0*.slobject\0"
#define RAW_FILTER L"RAW files (*.raw)\0*.raw\0"
-#define MODEL_FILTER L"Model files (*.dae)\0*.dae\0"
+#define MODEL_FILTER L"Model files (*.dae, *.gltf, *.glb)\0*.dae;*.gltf;*.glb\0"
#define MATERIAL_FILTER L"GLTF Files (*.gltf; *.glb)\0*.gltf;*.glb\0"
#define HDRI_FILTER L"HDRI Files (*.exr)\0*.exr\0"
#define MATERIAL_TEXTURES_FILTER L"GLTF Import (*.gltf; *.glb; *.tga; *.bmp; *.jpg; *.jpeg; *.png)\0*.gltf;*.glb;*.tga;*.bmp;*.jpg;*.jpeg;*.png\0"
@@ -222,6 +222,8 @@ bool LLFilePicker::setupFilter(ELoadFilter filter)
break;
case FFLOAD_MODEL:
mOFN.lpstrFilter = MODEL_FILTER \
+ COLLADA_FILTER \
+ MATERIAL_FILTER \
L"\0";
break;
case FFLOAD_MATERIAL:
@@ -676,6 +678,8 @@ std::unique_ptr<std::vector<std::string>> LLFilePicker::navOpenFilterProc(ELoadF
case FFLOAD_HDRI:
allowedv->push_back("exr");
case FFLOAD_MODEL:
+ allowedv->push_back("gltf");
+ allowedv->push_back("glb");
case FFLOAD_COLLADA:
allowedv->push_back("dae");
break;
diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp
index b94c31ec04..5ee93be061 100644
--- a/indra/newview/llfloaterbvhpreview.cpp
+++ b/indra/newview/llfloaterbvhpreview.cpp
@@ -179,7 +179,7 @@ void LLFloaterBvhPreview::setAnimCallbacks()
getChild<LLUICtrl>("ease_out_time")->setValidateBeforeCommit( boost::bind(&LLFloaterBvhPreview::validateEaseOut, this, _1));
}
-std::map <std::string, std::string> LLFloaterBvhPreview::getJointAliases()
+std::map<std::string, std::string, std::less<>> LLFloaterBvhPreview::getJointAliases()
{
LLPointer<LLVOAvatar> av = (LLVOAvatar*)mAnimPreview->getDummyAvatar();
return av->getJointAliases();
@@ -252,7 +252,7 @@ bool LLFloaterBvhPreview::postBuild()
ELoadStatus load_status = E_ST_OK;
S32 line_number = 0;
- std::map<std::string, std::string> joint_alias_map = getJointAliases();
+ auto joint_alias_map = getJointAliases();
loaderp = new LLBVHLoader(file_buffer, load_status, line_number, joint_alias_map);
std::string status = getString(STATUS[load_status]);
diff --git a/indra/newview/llfloaterbvhpreview.h b/indra/newview/llfloaterbvhpreview.h
index ae64521492..bb69ab65ef 100644
--- a/indra/newview/llfloaterbvhpreview.h
+++ b/indra/newview/llfloaterbvhpreview.h
@@ -108,7 +108,7 @@ public:
S32 status, LLExtStat ext_status);
private:
void setAnimCallbacks() ;
- std::map <std::string, std::string> getJointAliases();
+ std::map<std::string, std::string, std::less<>> getJointAliases();
protected:
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 8332a430e6..bd33ab2dbe 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -64,6 +64,7 @@
#include "llcallbacklist.h"
#include "llviewertexteditor.h"
#include "llviewernetwork.h"
+#include "llmaterialeditor.h"
//static
@@ -619,11 +620,9 @@ void LLFloaterModelPreview::onJointListSelection()
LLPanel *panel = mTabContainer->getPanelByName("rigging_panel");
LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
LLScrollListCtrl *joints_pos = panel->getChild<LLScrollListCtrl>("pos_overrides_list");
- LLScrollListCtrl *joints_scale = panel->getChild<LLScrollListCtrl>("scale_overrides_list");
LLTextBox *joint_pos_descr = panel->getChild<LLTextBox>("pos_overrides_descr");
joints_pos->deleteAllItems();
- joints_scale->deleteAllItems();
LLScrollListItem *selected = joints_list->getFirstSelected();
if (selected)
@@ -1488,7 +1487,7 @@ void LLFloaterModelPreview::updateAvatarTab(bool highlight_overrides)
{
// Populate table
- std::map<std::string, std::string> joint_alias_map;
+ std::map<std::string, std::string, std::less<>> joint_alias_map;
mModelPreview->getJointAliases(joint_alias_map);
S32 conflicts = 0;
diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp
index 9bc075d03f..af58c6316d 100644
--- a/indra/newview/llfloateruipreview.cpp
+++ b/indra/newview/llfloateruipreview.cpp
@@ -1043,7 +1043,9 @@ void LLFloaterUIPreview::getExecutablePath(const std::vector<std::string>& filen
{
CFStringRef executable_cfstr = (CFStringRef)CFDictionaryGetValue(bundleInfoDict, CFSTR("CFBundleExecutable")); // get the name of the actual executable (e.g. TextEdit or firefox-bin)
int max_file_length = 256; // (max file name length is 255 in OSX)
- char executable_buf[max_file_length];
+
+ // Xcode 26: VLAs are a clang extension. Just create the buffer and delete it after.
+ char *executable_buf = new char [max_file_length];
if(CFStringGetCString(executable_cfstr, executable_buf, max_file_length, kCFStringEncodingMacRoman)) // convert CFStringRef to char*
{
executable_path += std::string("/Contents/MacOS/") + std::string(executable_buf); // append path to executable directory and then executable name to exec path
@@ -1053,6 +1055,7 @@ void LLFloaterUIPreview::getExecutablePath(const std::vector<std::string>& filen
std::string warning = "Unable to get CString from CFString for executable path";
popupAndPrintWarning(warning);
}
+ delete [] executable_buf;
}
else
{
diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp
index a798ba31ee..565642e683 100755
--- a/indra/newview/llfloaterworldmap.cpp
+++ b/indra/newview/llfloaterworldmap.cpp
@@ -327,9 +327,6 @@ LLFloaterWorldMap::LLFloaterWorldMap(const LLSD& key)
mSetToUserPosition(true),
mTrackedLocation(0.0,0.0,0.0),
mTrackedStatus(LLTracker::TRACKING_NOTHING),
- mListFriendCombo(nullptr),
- mListLandmarkCombo(nullptr),
- mListSearchResults(nullptr),
mParcelInfoObserver(nullptr),
mShowParcelInfo(false)
{
@@ -383,32 +380,33 @@ bool LLFloaterWorldMap::postBuild()
mTeleportCoordSpinY = getChild<LLUICtrl>("teleport_coordinate_y");
mTeleportCoordSpinZ = getChild<LLUICtrl>("teleport_coordinate_z");
- LLComboBox *avatar_combo = getChild<LLComboBox>("friend combo");
- avatar_combo->selectFirstItem();
- avatar_combo->setPrearrangeCallback( boost::bind(&LLFloaterWorldMap::onAvatarComboPrearrange, this) );
- avatar_combo->setTextChangedCallback( boost::bind(&LLFloaterWorldMap::onComboTextEntry, this) );
- mListFriendCombo = dynamic_cast<LLCtrlListInterface *>(avatar_combo);
+ mFriendCombo = getChild<LLComboBox>("friend combo");
+ mFriendCombo->selectFirstItem();
+ mFriendCombo->setPrearrangeCallback(boost::bind(&LLFloaterWorldMap::onAvatarComboPrearrange, this));
+ mFriendCombo->setTextChangedCallback(boost::bind(&LLFloaterWorldMap::onComboTextEntry, this));
mLocationEditor = getChild<LLSearchEditor>("location");
mLocationEditor->setFocusChangedCallback(boost::bind(&LLFloaterWorldMap::onLocationFocusChanged, this, _1));
- mLocationEditor->setTextChangedCallback( boost::bind(&LLFloaterWorldMap::onSearchTextEntry, this));
+ mLocationEditor->setTextChangedCallback(boost::bind(&LLFloaterWorldMap::onSearchTextEntry, this));
- getChild<LLScrollListCtrl>("search_results")->setDoubleClickCallback( boost::bind(&LLFloaterWorldMap::onClickTeleportBtn, this));
- mListSearchResults = childGetListInterface("search_results");
+ mSearchResults = getChild<LLScrollListCtrl>("search_results");
+ mSearchResults->setDoubleClickCallback(boost::bind(&LLFloaterWorldMap::onClickTeleportBtn, this));
- LLComboBox *landmark_combo = getChild<LLComboBox>( "landmark combo");
- landmark_combo->selectFirstItem();
- landmark_combo->setPrearrangeCallback( boost::bind(&LLFloaterWorldMap::onLandmarkComboPrearrange, this) );
- landmark_combo->setTextChangedCallback( boost::bind(&LLFloaterWorldMap::onComboTextEntry, this) );
- mListLandmarkCombo = dynamic_cast<LLCtrlListInterface *>(landmark_combo);
+ mLandmarkCombo = getChild<LLComboBox>("landmark combo");
+ mLandmarkCombo->selectFirstItem();
+ mLandmarkCombo->setPrearrangeCallback(boost::bind(&LLFloaterWorldMap::onLandmarkComboPrearrange, this));
+ mLandmarkCombo->setTextChangedCallback(boost::bind(&LLFloaterWorldMap::onComboTextEntry, this));
mZoomSlider = getChild<LLSliderCtrl>("zoom slider");
F32 slider_zoom = mMapView->getZoom();
mZoomSlider->setValue(slider_zoom);
+ mTrackCtrlsPanel = getChild<LLPanel>("layout_panel_4");
+ mSearchButton = getChild<LLButton>("DoSearch");
+
getChild<LLPanel>("expand_btn_panel")->setMouseDownCallback(boost::bind(&LLFloaterWorldMap::onExpandCollapseBtn, this));
- setDefaultBtn(NULL);
+ mTrackCtrlsPanel->setDefaultBtn(nullptr);
onChangeMaturity();
@@ -608,7 +606,6 @@ void LLFloaterWorldMap::draw()
}
mTeleportButton->setEnabled((bool)tracking_status);
- // getChildView("Clear")->setEnabled((bool)tracking_status);
mShowDestinationButton->setEnabled((bool)tracking_status || LLWorldMap::getInstance()->isTracking());
mCopySlurlButton->setEnabled((mSLURL.isValid()) );
@@ -700,26 +697,24 @@ void LLFloaterWorldMap::requestParcelInfo(const LLVector3d& pos_global, const LL
}
}
-void LLFloaterWorldMap::trackAvatar( const LLUUID& avatar_id, const std::string& name )
+void LLFloaterWorldMap::trackAvatar(const LLUUID& avatar_id, const std::string& name)
{
mShowParcelInfo = false;
- LLCtrlSelectionInterface *iface = childGetSelectionInterface("friend combo");
- if (!iface) return;
buildAvatarIDList();
- if(iface->setCurrentByID(avatar_id) || gAgent.isGodlike())
+ if (mFriendCombo->setCurrentByID(avatar_id) || gAgent.isGodlike())
{
// *HACK: Adjust Z values automatically for liaisons & gods so
// they swoop down when they click on the map. Requested
// convenience.
- if(gAgent.isGodlike())
+ if (gAgent.isGodlike())
{
mTeleportCoordSpinZ->setValue(LLSD(200.f));
}
// Don't re-request info if we already have it or we won't have it in time to teleport
if (mTrackedStatus != LLTracker::TRACKING_AVATAR || avatar_id != mTrackedAvatarID)
{
- mTrackedStatus = LLTracker::TRACKING_AVATAR;
+ mTrackedStatus = LLTracker::TRACKING_AVATAR;
mTrackedAvatarID = avatar_id;
LLTracker::trackAvatar(avatar_id, name);
}
@@ -728,52 +723,45 @@ void LLFloaterWorldMap::trackAvatar( const LLUUID& avatar_id, const std::string&
{
LLTracker::stopTracking(false);
}
- setDefaultBtn("Teleport");
+ mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
}
-void LLFloaterWorldMap::trackLandmark( const LLUUID& landmark_item_id )
+void LLFloaterWorldMap::trackLandmark(const LLUUID& landmark_item_id)
{
mShowParcelInfo = false;
- LLCtrlSelectionInterface *iface = childGetSelectionInterface("landmark combo");
- if (!iface) return;
buildLandmarkIDLists();
bool found = false;
- S32 idx;
+ S32 idx;
for (idx = 0; idx < mLandmarkItemIDList.size(); idx++)
{
- if ( mLandmarkItemIDList.at(idx) == landmark_item_id)
+ if (mLandmarkItemIDList.at(idx) == landmark_item_id)
{
found = true;
break;
}
}
- if (found && iface->setCurrentByID( landmark_item_id ) )
+ if (found && mLandmarkCombo->setCurrentByID(landmark_item_id))
{
- LLUUID asset_id = mLandmarkAssetIDList.at( idx );
- std::string name;
- LLComboBox* combo = getChild<LLComboBox>( "landmark combo");
- if (combo) name = combo->getSimple();
- mTrackedStatus = LLTracker::TRACKING_LANDMARK;
- LLTracker::trackLandmark(mLandmarkAssetIDList.at( idx ), // assetID
- mLandmarkItemIDList.at( idx ), // itemID
- name); // name
+ LLUUID asset_id = mLandmarkAssetIDList.at(idx);
+ std::string name = mLandmarkCombo->getSimple();
+ mTrackedStatus = LLTracker::TRACKING_LANDMARK;
+ LLTracker::trackLandmark(mLandmarkAssetIDList.at(idx), // assetID
+ mLandmarkItemIDList.at(idx), // itemID
+ name); // name
- if( asset_id != sHomeID )
+ if (asset_id != sHomeID)
{
// start the download process
- gLandmarkList.getAsset( asset_id);
+ gLandmarkList.getAsset(asset_id);
}
-
- // We have to download both region info and landmark data, so set busy. JC
- // getWindow()->incBusyCount();
}
else
{
LLTracker::stopTracking(false);
}
- setDefaultBtn("Teleport");
+ mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
}
@@ -782,7 +770,7 @@ void LLFloaterWorldMap::trackEvent(const LLItemInfo &event_info)
mShowParcelInfo = false;
mTrackedStatus = LLTracker::TRACKING_LOCATION;
LLTracker::trackLocation(event_info.getGlobalPosition(), event_info.getName(), event_info.getToolTip(), LLTracker::LOCATION_EVENT);
- setDefaultBtn("Teleport");
+ mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
}
void LLFloaterWorldMap::trackGenericItem(const LLItemInfo &item)
@@ -790,7 +778,7 @@ void LLFloaterWorldMap::trackGenericItem(const LLItemInfo &item)
mShowParcelInfo = false;
mTrackedStatus = LLTracker::TRACKING_LOCATION;
LLTracker::trackLocation(item.getGlobalPosition(), item.getName(), item.getToolTip(), LLTracker::LOCATION_ITEM);
- setDefaultBtn("Teleport");
+ mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
}
void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
@@ -804,7 +792,7 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
S32 world_x = S32(pos_global.mdV[0] / 256);
S32 world_y = S32(pos_global.mdV[1] / 256);
LLWorldMapMessage::getInstance()->sendMapBlockRequest(world_x, world_y, world_x, world_y, true);
- setDefaultBtn("");
+ mTrackCtrlsPanel->setDefaultBtn(nullptr);
// clicked on a non-region - turn off coord display
enableTeleportCoordsDisplay( false );
@@ -818,7 +806,7 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
LLTracker::stopTracking(false);
LLWorldMap::getInstance()->setTracking(pos_global);
LLWorldMap::getInstance()->setTrackingInvalid();
- setDefaultBtn("");
+ mTrackCtrlsPanel->setDefaultBtn(nullptr);
// clicked on a down region - turn off coord display
enableTeleportCoordsDisplay( false );
@@ -849,7 +837,7 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
// we have a valid region - turn on coord display
enableTeleportCoordsDisplay( true );
- setDefaultBtn("Teleport");
+ mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
}
// enable/disable teleport destination coordinates
@@ -964,7 +952,7 @@ void LLFloaterWorldMap::trackURL(const std::string& region_name, S32 x_coord, S3
local_pos.mV[VZ] = (F32)z_coord;
LLVector3d global_pos = sim_info->getGlobalPos(local_pos);
trackLocation(global_pos);
- setDefaultBtn("Teleport");
+ mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
}
else
{
@@ -1025,17 +1013,14 @@ void LLFloaterWorldMap::observeFriends()
void LLFloaterWorldMap::friendsChanged()
{
- LLAvatarTracker& t = LLAvatarTracker::instance();
- const LLUUID& avatar_id = t.getAvatarID();
+ LLAvatarTracker& t = LLAvatarTracker::instance();
+ const LLUUID& avatar_id = t.getAvatarID();
buildAvatarIDList();
- if(avatar_id.notNull())
+ if (avatar_id.notNull())
{
- LLCtrlSelectionInterface *iface = childGetSelectionInterface("friend combo");
const LLRelationship* buddy_info = t.getBuddyInfo(avatar_id);
- if(!iface ||
- !iface->setCurrentByID(avatar_id) ||
- (buddy_info && !buddy_info->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION)) ||
- gAgent.isGodlike())
+ if (!mFriendCombo->setCurrentByID(avatar_id) ||
+ (buddy_info && !buddy_info->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION)) || gAgent.isGodlike())
{
LLTracker::stopTracking(false);
}
@@ -1045,15 +1030,12 @@ void LLFloaterWorldMap::friendsChanged()
// No longer really builds a list. Instead, just updates mAvatarCombo.
void LLFloaterWorldMap::buildAvatarIDList()
{
- LLCtrlListInterface *list = mListFriendCombo;
- if (!list) return;
-
// Delete all but the "None" entry
- S32 list_size = list->getItemCount();
+ S32 list_size = mFriendCombo->getItemCount();
if (list_size > 1)
{
- list->selectItemRange(1, -1);
- list->operateOnSelection(LLCtrlListInterface::OP_DELETE);
+ mFriendCombo->selectItemRange(1, -1);
+ mFriendCombo->operateOnSelection(LLCtrlListInterface::OP_DELETE);
}
// Get all of the calling cards for avatar that are currently online
@@ -1061,29 +1043,26 @@ void LLFloaterWorldMap::buildAvatarIDList()
LLAvatarTracker::instance().applyFunctor(collector);
LLCollectMappableBuddies::buddy_map_t::iterator it;
LLCollectMappableBuddies::buddy_map_t::iterator end;
- it = collector.mMappable.begin();
+ it = collector.mMappable.begin();
end = collector.mMappable.end();
- for( ; it != end; ++it)
+ for (; it != end; ++it)
{
- list->addSimpleElement((*it).second, ADD_BOTTOM, (*it).first);
+ mFriendCombo->addSimpleElement((*it).second, ADD_BOTTOM, (*it).first);
}
- list->setCurrentByID( LLAvatarTracker::instance().getAvatarID() );
- list->selectFirstItem();
+ mFriendCombo->setCurrentByID(LLAvatarTracker::instance().getAvatarID());
+ mFriendCombo->selectFirstItem();
}
void LLFloaterWorldMap::buildLandmarkIDLists()
{
- LLCtrlListInterface *list = mListLandmarkCombo;
- if (!list) return;
-
// Delete all but the "None" entry
- S32 list_size = list->getItemCount();
+ S32 list_size = mLandmarkCombo->getItemCount();
if (list_size > 1)
{
- list->selectItemRange(1, -1);
- list->operateOnSelection(LLCtrlListInterface::OP_DELETE);
+ mLandmarkCombo->selectItemRange(1, -1);
+ mLandmarkCombo->operateOnSelection(LLCtrlListInterface::OP_DELETE);
}
mLandmarkItemIDList.clear();
@@ -1115,13 +1094,13 @@ void LLFloaterWorldMap::buildLandmarkIDLists()
{
LLInventoryItem* item = items.at(i);
- list->addSimpleElement(item->getName(), ADD_BOTTOM, item->getUUID());
+ mLandmarkCombo->addSimpleElement(item->getName(), ADD_BOTTOM, item->getUUID());
mLandmarkAssetIDList.push_back( item->getAssetUUID() );
mLandmarkItemIDList.push_back( item->getUUID() );
}
- list->selectFirstItem();
+ mLandmarkCombo->selectFirstItem();
}
@@ -1139,10 +1118,9 @@ F32 LLFloaterWorldMap::getDistanceToDestination(const LLVector3d &destination,
void LLFloaterWorldMap::clearLocationSelection(bool clear_ui, bool dest_reached)
{
- LLCtrlListInterface *list = mListSearchResults;
- if (list && (!dest_reached || (list->getItemCount() == 1)))
+ if (!dest_reached || (mSearchResults->getItemCount() == 1))
{
- list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+ mSearchResults->operateOnAll(LLCtrlListInterface::OP_DELETE);
}
LLWorldMap::getInstance()->cancelTracking();
mCompletingRegionName = "";
@@ -1153,11 +1131,7 @@ void LLFloaterWorldMap::clearLandmarkSelection(bool clear_ui)
{
if (clear_ui || !childHasKeyboardFocus("landmark combo"))
{
- LLCtrlListInterface *list = mListLandmarkCombo;
- if (list)
- {
- list->selectByValue( "None" );
- }
+ mLandmarkCombo->selectByValue("None");
}
}
@@ -1167,10 +1141,9 @@ void LLFloaterWorldMap::clearAvatarSelection(bool clear_ui)
if (clear_ui || !childHasKeyboardFocus("friend combo"))
{
mTrackedStatus = LLTracker::TRACKING_NOTHING;
- LLCtrlListInterface *list = mListFriendCombo;
- if (list && list->getSelectedValue().asString() != "None")
+ if (mFriendCombo->getSelectedValue().asString() != "None")
{
- list->selectByValue( "None" );
+ mFriendCombo->selectByValue("None");
}
}
}
@@ -1226,25 +1199,21 @@ void LLFloaterWorldMap::onGoHome()
}
-void LLFloaterWorldMap::onLandmarkComboPrearrange( )
+void LLFloaterWorldMap::onLandmarkComboPrearrange()
{
- if( mIsClosing )
+ if (mIsClosing)
{
return;
}
- LLCtrlListInterface *list = mListLandmarkCombo;
- if (!list) return;
-
- LLUUID current_choice = list->getCurrentID();
+ LLUUID current_choice = mLandmarkCombo->getCurrentID();
buildLandmarkIDLists();
- if( current_choice.isNull() || !list->setCurrentByID( current_choice ) )
+ if (current_choice.isNull() || !mLandmarkCombo->setCurrentByID(current_choice))
{
LLTracker::stopTracking(false);
}
-
}
void LLFloaterWorldMap::onComboTextEntry()
@@ -1264,33 +1233,28 @@ void LLFloaterWorldMap::onSearchTextEntry( )
void LLFloaterWorldMap::onLandmarkComboCommit()
{
- if( mIsClosing )
+ if (mIsClosing)
{
return;
}
- LLCtrlListInterface *list = mListLandmarkCombo;
- if (!list) return;
-
LLUUID asset_id;
- LLUUID item_id = list->getCurrentID();
+ LLUUID item_id = mLandmarkCombo->getCurrentID();
LLTracker::stopTracking(false);
- //RN: stopTracking() clears current combobox selection, need to reassert it here
- list->setCurrentByID(item_id);
+ // RN: stopTracking() clears current combobox selection, need to reassert it here
+ mLandmarkCombo->setCurrentByID(item_id);
- if( item_id.isNull() )
- {
- }
- else if( item_id == sHomeID )
+ if (item_id.isNull()) {}
+ else if (item_id == sHomeID)
{
asset_id = sHomeID;
}
else
{
- LLInventoryItem* item = gInventory.getItem( item_id );
- if( item )
+ LLInventoryItem* item = gInventory.getItem(item_id);
+ if (item)
{
asset_id = item->getAssetUUID();
}
@@ -1301,34 +1265,31 @@ void LLFloaterWorldMap::onLandmarkComboCommit()
}
}
- trackLandmark( item_id);
+ trackLandmark(item_id);
onShowTargetBtn();
// Reset to user postion if nothing is tracked
- mSetToUserPosition = ( LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING );
+ mSetToUserPosition = (LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING);
}
// static
-void LLFloaterWorldMap::onAvatarComboPrearrange( )
+void LLFloaterWorldMap::onAvatarComboPrearrange()
{
- if( mIsClosing )
+ if (mIsClosing)
{
return;
}
- LLCtrlListInterface *list = mListFriendCombo;
- if (!list) return;
-
LLUUID current_choice;
- if( LLAvatarTracker::instance().haveTrackingInfo() )
+ if (LLAvatarTracker::instance().haveTrackingInfo())
{
current_choice = LLAvatarTracker::instance().getAvatarID();
}
buildAvatarIDList();
- if( !list->setCurrentByID( current_choice ) || current_choice.isNull() )
+ if (!mFriendCombo->setCurrentByID(current_choice) || current_choice.isNull())
{
LLTracker::stopTracking(false);
}
@@ -1336,26 +1297,21 @@ void LLFloaterWorldMap::onAvatarComboPrearrange( )
void LLFloaterWorldMap::onAvatarComboCommit()
{
- if( mIsClosing )
+ if (mIsClosing)
{
return;
}
- LLCtrlListInterface *list = mListFriendCombo;
- if (!list) return;
-
- const LLUUID& new_avatar_id = list->getCurrentID();
+ const LLUUID& new_avatar_id = mFriendCombo->getCurrentID();
if (new_avatar_id.notNull())
{
- std::string name;
- LLComboBox* combo = getChild<LLComboBox>("friend combo");
- if (combo) name = combo->getSimple();
+ std::string name = mFriendCombo->getSimple();
trackAvatar(new_avatar_id, name);
onShowTargetBtn();
}
else
- { // Reset to user postion if nothing is tracked
- mSetToUserPosition = ( LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING );
+ { // Reset to user postion if nothing is tracked
+ mSetToUserPosition = (LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING);
}
}
@@ -1375,11 +1331,11 @@ void LLFloaterWorldMap::updateSearchEnabled()
if (childHasKeyboardFocus("location") &&
mLocationEditor->getValue().asString().length() > 0)
{
- setDefaultBtn("DoSearch");
+ mTrackCtrlsPanel->setDefaultBtn(mSearchButton);
}
else
{
- setDefaultBtn(NULL);
+ mTrackCtrlsPanel->setDefaultBtn(nullptr);
}
}
@@ -1487,8 +1443,9 @@ void LLFloaterWorldMap::onExpandCollapseBtn()
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);
+ LLIconCtrl* expandCollapseIcon = getChild<LLIconCtrl>("expand_collapse_icon");
+ expandCollapseIcon->setImage(LLUI::getUIImage(image_name));
+ expandCollapseIcon->setToolTip(tooltip);
getChild<LLPanel>("expand_btn_panel")->setToolTip(tooltip);
}
@@ -1680,9 +1637,9 @@ void LLFloaterWorldMap::teleportToAvatar()
void LLFloaterWorldMap::flyToAvatar()
{
- if( LLAvatarTracker::instance().haveTrackingInfo() )
+ if (LLAvatarTracker::instance().haveTrackingInfo())
{
- gAgent.startAutoPilotGlobal( LLAvatarTracker::instance().getGlobalPos() );
+ gAgent.startAutoPilotGlobal(LLAvatarTracker::instance().getGlobalPos());
}
}
@@ -1693,8 +1650,7 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim)
return;
}
- LLScrollListCtrl *list = getChild<LLScrollListCtrl>("search_results");
- list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+ mSearchResults->operateOnAll(LLCtrlListInterface::OP_DELETE);
auto name_length = mCompletingRegionName.length();
@@ -1722,7 +1678,7 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim)
value["id"] = info->getName();
value["columns"][0]["column"] = "sim_name";
value["columns"][0]["value"] = info->getName();
- list->addElement(value);
+ mSearchResults->addElement(value);
num_results++;
}
}
@@ -1737,21 +1693,22 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim)
// if match found, highlight it and go
if (!match.isUndefined())
{
- list->selectByValue(match);
+ mSearchResults->selectByValue(match);
+ mSearchResults->setFocus(true);
+ onCommitSearchResult();
}
- // else select first found item
+ // else let user decide
else
{
- list->selectFirstItem();
+ mSearchResults->operateOnAll(LLCtrlListInterface::OP_DESELECT);
+ mSearchResults->setFocus(true);
}
- getChild<LLUICtrl>("search_results")->setFocus(true);
- onCommitSearchResult();
}
else
{
// if we found nothing, say "none"
- list->setCommentText(LLTrans::getString("worldmap_results_none_found"));
- list->operateOnAll(LLCtrlListInterface::OP_DESELECT);
+ mSearchResults->setCommentText(LLTrans::getString("worldmap_results_none_found"));
+ mSearchResults->operateOnAll(LLCtrlListInterface::OP_DESELECT);
}
}
@@ -1765,11 +1722,7 @@ void LLFloaterWorldMap::onTeleportFinished()
void LLFloaterWorldMap::onCommitSearchResult()
{
- LLCtrlListInterface *list = mListSearchResults;
- if (!list) return;
-
- LLSD selected_value = list->getSelectedValue();
- std::string sim_name = selected_value.asString();
+ std::string sim_name = mSearchResults->getSelectedValue().asString();
if (sim_name.empty())
{
return;
@@ -1785,7 +1738,7 @@ void LLFloaterWorldMap::onCommitSearchResult()
{
LLVector3d pos_global = info->getGlobalOrigin();
- const F64 SIM_COORD_DEFAULT = 128.0;
+ constexpr F64 SIM_COORD_DEFAULT = 128.0;
LLVector3 pos_local(SIM_COORD_DEFAULT, SIM_COORD_DEFAULT, 0.0f);
// Did this value come from a trackURL() request?
@@ -1800,7 +1753,7 @@ void LLFloaterWorldMap::onCommitSearchResult()
mLocationEditor->setValue(sim_name);
trackLocation(pos_global);
- setDefaultBtn("Teleport");
+ mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
break;
}
}
diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h
index 2f2b2b7a0d..bf1650eb7c 100644
--- a/indra/newview/llfloaterworldmap.h
+++ b/indra/newview/llfloaterworldmap.h
@@ -51,6 +51,8 @@ class LLCheckBoxCtrl;
class LLSliderCtrl;
class LLSpinCtrl;
class LLSearchEditor;
+class LLComboBox;
+class LLScrollListCtrl;
class LLWorldMapParcelInfoObserver : public LLRemoteParcelInfoObserver
{
@@ -218,14 +220,11 @@ private:
LLUUID mTrackedAvatarID;
LLSLURL mSLURL;
- LLCtrlListInterface * mListFriendCombo;
- LLCtrlListInterface * mListLandmarkCombo;
- LLCtrlListInterface * mListSearchResults;
-
LLButton* mTeleportButton = nullptr;
LLButton* mShowDestinationButton = nullptr;
LLButton* mCopySlurlButton = nullptr;
LLButton* mGoHomeButton = nullptr;
+ LLButton* mSearchButton = nullptr;
LLCheckBoxCtrl* mPeopleCheck = nullptr;
LLCheckBoxCtrl* mInfohubCheck = nullptr;
@@ -245,6 +244,13 @@ private:
LLSliderCtrl* mZoomSlider = nullptr;
+ LLComboBox* mLandmarkCombo = nullptr;
+ LLComboBox* mFriendCombo = nullptr;
+
+ LLScrollListCtrl* mSearchResults = nullptr;
+
+ LLPanel* mTrackCtrlsPanel = nullptr;
+
boost::signals2::connection mTeleportFinishConnection;
};
diff --git a/indra/newview/llhudtext.cpp b/indra/newview/llhudtext.cpp
index fd0d8b696f..c092b4c91a 100644
--- a/indra/newview/llhudtext.cpp
+++ b/indra/newview/llhudtext.cpp
@@ -225,10 +225,6 @@ void LLHUDText::renderText()
}
text_color = segment_iter->mColor;
- if (mOnHUDAttachment)
- {
- text_color = linearColor4(text_color);
- }
text_color.mV[VALPHA] *= alpha_factor;
hud_render_text(segment_iter->getText(), render_position, *fontp, style, shadow, x_offset, y_offset, text_color, mOnHUDAttachment);
diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp
index 5c0905af3c..01f2c6c525 100644
--- a/indra/newview/llinventoryfilter.cpp
+++ b/indra/newview/llinventoryfilter.cpp
@@ -959,7 +959,7 @@ void LLInventoryFilter::setFilterSubString(const std::string& string)
boost::char_separator<char> sep("+");
tokenizer tokens(filter_sub_string_new, sep);
- for (auto token_iter : tokens)
+ for (const auto& token_iter : tokens)
{
mFilterTokens.push_back(token_iter);
}
@@ -1025,7 +1025,7 @@ void LLInventoryFilter::setFilterSubString(const std::string& string)
}
// Cancel out UUID once the search string is modified
- if (mFilterOps.mFilterTypes == FILTERTYPE_UUID)
+ if (mFilterOps.mFilterTypes & FILTERTYPE_UUID)
{
mFilterOps.mFilterTypes &= ~FILTERTYPE_UUID;
mFilterOps.mFilterUUID = LLUUID::null;
@@ -1707,7 +1707,7 @@ std::string LLInventoryFilter::getEmptyLookupMessage(bool is_empty_folder) const
}
}
-bool LLInventoryFilter::areDateLimitsSet()
+bool LLInventoryFilter::areDateLimitsSet() const
{
return mFilterOps.mMinDate != time_min()
|| mFilterOps.mMaxDate != time_max()
diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h
index 7e64a03e73..612a161ba2 100644
--- a/indra/newview/llinventoryfilter.h
+++ b/indra/newview/llinventoryfilter.h
@@ -341,7 +341,7 @@ public:
bool checkAgainstFilterThumbnails(const LLUUID& object_id) const;
private:
- bool areDateLimitsSet();
+ bool areDateLimitsSet() const;
bool checkAgainstFilterSubString(const std::string& desc) const;
bool checkAgainstFilterType(const class LLFolderViewModelItemInventory* listener) const;
bool checkAgainstFilterType(const LLInventoryItem* item) const;
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp
index 1077ce74ae..d2ed7168c5 100644
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -2350,9 +2350,9 @@ bool can_move_to_my_outfits_as_outfit(LLInventoryModel* model, LLInventoryCatego
return false;
}
- if (items->size() == 0)
+ if (items->size() == 0 && inv_cat->getPreferredType() != LLFolderType::FT_OUTFIT)
{
- // Nothing to move(create)
+ // Nothing to create an outfit folder from
return false;
}
diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp
index 28160177f6..5b3ac53d51 100644
--- a/indra/newview/llmaterialeditor.cpp
+++ b/indra/newview/llmaterialeditor.cpp
@@ -137,7 +137,8 @@ LLFloaterComboOptions* LLFloaterComboOptions::showUI(
{
combo_picker->mComboOptions->addSimpleElement(*iter);
}
- combo_picker->mComboOptions->selectFirstItem();
+ // select 'Bulk Upload All' option
+ combo_picker->mComboOptions->selectNthItem((S32)options.size() - 1);
combo_picker->openFloater(LLSD(title));
combo_picker->setFocus(true);
@@ -1332,15 +1333,6 @@ const std::string LLMaterialEditor::buildMaterialDescription()
desc << mNormalName;
}
- // trim last char if it's a ',' in case there is no normal texture
- // present and the code above inserts one
- // (no need to check for string length - always has initial string)
- std::string::iterator iter = desc.str().end() - 1;
- if (*iter == ',')
- {
- desc.str().erase(iter);
- }
-
// sanitize the material description so that it's compatible with the inventory
// note: split this up because clang doesn't like operating directly on the
// str() - error: lvalue reference to type 'basic_string<...>' cannot bind to a
@@ -1348,6 +1340,15 @@ const std::string LLMaterialEditor::buildMaterialDescription()
std::string inv_desc = desc.str();
LLInventoryObject::correctInventoryName(inv_desc);
+ // trim last char if it's a ',' in case there is no normal texture
+ // present and the code above inserts one
+ // (no need to check for string length - always has initial string)
+ std::string::iterator iter = inv_desc.end() - 1;
+ if (*iter == ',')
+ {
+ inv_desc.erase(iter);
+ }
+
return inv_desc;
}
@@ -2684,10 +2685,8 @@ const std::string LLMaterialEditor::getImageNameFromUri(std::string image_uri, c
// so we can include everything
if (stripped_uri.length() > 0)
{
- // example "DamagedHelmet: base layer"
+ // example "base layer"
return STRINGIZE(
- mMaterialNameShort <<
- ": " <<
stripped_uri <<
" (" <<
texture_type <<
@@ -2696,28 +2695,17 @@ const std::string LLMaterialEditor::getImageNameFromUri(std::string image_uri, c
}
else
// uri doesn't include the type (because the uri is empty)
- // so we must reorganize the string a bit to include the name
- // and an explicit name type
+ // include an explicit name type
{
- // example "DamagedHelmet: (Emissive)"
- return STRINGIZE(
- mMaterialNameShort <<
- " (" <<
- texture_type <<
- ")"
- );
+ // example "Emissive"
+ return texture_type;
}
}
else
- // uri includes the type so just use it directly with the
- // name of the material
+ // uri includes the type so just use it directly
{
- return STRINGIZE(
- // example: AlienBust: normal_layer
- mMaterialNameShort <<
- ": " <<
- stripped_uri
- );
+ // example: "normal_layer"
+ return stripped_uri;
}
}
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index e7e95034b2..142a3dac39 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -2395,6 +2395,11 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p
// might be good idea to turn mesh into pointer to avoid making a copy
mesh.mVolume = NULL;
}
+ {
+ // make sure skin info is not removed from list while we are decreasing reference count
+ LLMutexLock lock(mSkinMapMutex);
+ skin_info = nullptr;
+ }
return MESH_OK;
}
}
@@ -2694,10 +2699,14 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
S32 instance_num = 0;
- for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
+ // Handle models, ignore submodels for now.
+ // Probably should pre-sort by mSubmodelID instead of running twice.
+ // Note: mInstance should be sorted by model name for the sake of
+ // deterministic order.
+ for (auto& iter : mInstance)
{
LLMeshUploadData data;
- data.mBaseModel = iter->first;
+ data.mBaseModel = iter.first;
if (data.mBaseModel->mSubmodelID)
{
@@ -2706,7 +2715,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
continue;
}
- LLModelInstance& first_instance = *(iter->second.begin());
+ LLModelInstance& first_instance = *(iter.second.begin());
for (S32 i = 0; i < 5; i++)
{
data.mModel[i] = first_instance.mLOD[i];
@@ -2740,7 +2749,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
mUploadSkin,
mUploadJoints,
mLockScaleIfJointPosition,
- false,
+ LLModel::WRITE_BINARY,
false,
data.mBaseModel->mSubmodelID);
@@ -2753,8 +2762,8 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
}
// For all instances that use this model
- for (instance_list::iterator instance_iter = iter->second.begin();
- instance_iter != iter->second.end();
+ for (instance_list::iterator instance_iter = iter.second.begin();
+ instance_iter != iter.second.end();
++instance_iter)
{
@@ -2852,10 +2861,11 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
}
}
- for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
+ // Now handle the submodels.
+ for (auto& iter : mInstance)
{
LLMeshUploadData data;
- data.mBaseModel = iter->first;
+ data.mBaseModel = iter.first;
if (!data.mBaseModel->mSubmodelID)
{
@@ -2864,7 +2874,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
continue;
}
- LLModelInstance& first_instance = *(iter->second.begin());
+ LLModelInstance& first_instance = *(iter.second.begin());
for (S32 i = 0; i < 5; i++)
{
data.mModel[i] = first_instance.mLOD[i];
@@ -2898,7 +2908,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
mUploadSkin,
mUploadJoints,
mLockScaleIfJointPosition,
- false,
+ LLModel::WRITE_BINARY,
false,
data.mBaseModel->mSubmodelID);
@@ -2911,8 +2921,8 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
}
// For all instances that use this model
- for (instance_list::iterator instance_iter = iter->second.begin();
- instance_iter != iter->second.end();
+ for (instance_list::iterator instance_iter = iter.second.begin();
+ instance_iter != iter.second.end();
++instance_iter)
{
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 0847c29d0d..4c3901408f 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -674,7 +674,22 @@ public:
typedef std::vector<LLModelInstance> instance_list;
instance_list mInstanceList;
- typedef std::map<LLPointer<LLModel>, instance_list> instance_map;
+ // Upload should happen in deterministic order, so sort instances by model name.
+ struct LLUploadModelInstanceLess
+ {
+ inline bool operator()(const LLPointer<LLModel>& a, const LLPointer<LLModel>& b) const
+ {
+ if (a.isNull() || b.isNull())
+ {
+ llassert(false); // We are uploading these models, they shouldn't be null.
+ return true;
+ }
+ // Note: probably can sort by mBaseModel->mSubmodelID here as well to avoid
+ // running over the list twice in wholeModelToLLSD.
+ return a->mLabel < b->mLabel;
+ }
+ };
+ typedef std::map<LLPointer<LLModel>, instance_list, LLUploadModelInstanceLess> instance_map;
instance_map mInstance;
LLMutex* mMutex;
diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index c73282dad3..6fe192ac19 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -30,7 +30,7 @@
#include "llmodelloader.h"
#include "lldaeloader.h"
-#include "llgltfloader.h"
+#include "gltf/llgltfloader.h"
#include "llfloatermodelpreview.h"
#include "llagent.h"
@@ -40,6 +40,7 @@
#include "lldrawable.h"
#include "llface.h"
#include "lliconctrl.h"
+#include "lljointdata.h"
#include "llmatrix4a.h"
#include "llmeshrepository.h"
#include "llmeshoptimizer.h"
@@ -163,10 +164,12 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
, mPhysicsSearchLOD(LLModel::LOD_PHYSICS)
, mResetJoints(false)
, mModelNoErrors(true)
+ , mLoading(false)
+ , mModelLoader(nullptr)
, mLastJointUpdate(false)
, mFirstSkinUpdate(true)
, mHasDegenerate(false)
- , mImporterDebug(LLCachedControl<bool>(gSavedSettings, "ImporterDebug", false))
+ , mImporterDebug(LLCachedControl<bool>(gSavedSettings, "ImporterDebugVerboseLogging", false))
{
mNeedsUpdate = true;
mCameraDistance = 0.f;
@@ -175,11 +178,9 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
mCameraZoom = 1.f;
mTextureName = 0;
mPreviewLOD = 0;
- mModelLoader = NULL;
mMaxTriangleLimit = 0;
mDirty = false;
mGenLOD = false;
- mLoading = false;
mLookUpLodFiles = false;
mLoadState = LLModelLoader::STARTING;
mGroup = 0;
@@ -211,6 +212,7 @@ LLModelPreview::~LLModelPreview()
{
mModelLoader->shutdown();
mModelLoader = NULL;
+ mLoading = false;
}
if (mPreviewAvatar)
@@ -691,7 +693,7 @@ void LLModelPreview::saveUploadData(const std::string& filename,
save_skinweights,
save_joint_positions,
lock_scale_if_joint_position,
- false, true, instance.mModel->mSubmodelID);
+ LLModel::WRITE_BINARY, true, instance.mModel->mSubmodelID);
data["mesh"][instance.mModel->mLocalID] = str.str();
}
@@ -753,6 +755,10 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
LL_WARNS() << out.str() << LL_ENDL;
LLFloaterModelPreview::addStringToLog(out, true);
assert(lod >= LLModel::LOD_IMPOSTOR && lod < LLModel::NUM_LODS);
+ if (mModelLoader == nullptr)
+ {
+ mLoading = false;
+ }
return;
}
@@ -780,7 +786,7 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
mLODFile[lod] = filename;
- std::map<std::string, std::string> joint_alias_map;
+ std::map<std::string, std::string, std::less<>> joint_alias_map;
getJointAliases(joint_alias_map);
LLHandle<LLModelPreview> preview_handle = getHandle();
@@ -806,10 +812,14 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
joint_alias_map,
LLSkinningUtil::getMaxJointCount(),
gSavedSettings.getU32("ImporterModelLimit"),
+ gSavedSettings.getU32("ImporterDebugMode"),
gSavedSettings.getBOOL("ImporterPreprocessDAE"));
}
else
{
+ LLVOAvatar* av = getPreviewAvatar();
+ std::vector<LLJointData> viewer_skeleton;
+ av->getJointMatricesAndHierarhy(viewer_skeleton);
mModelLoader = new LLGLTFLoader(
filename,
lod,
@@ -822,7 +832,9 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
mJointsFromNode,
joint_alias_map,
LLSkinningUtil::getMaxJointCount(),
- gSavedSettings.getU32("ImporterModelLimit"));
+ gSavedSettings.getU32("ImporterModelLimit"),
+ gSavedSettings.getU32("ImporterDebugMode"),
+ viewer_skeleton);
}
if (force_disable_slm)
@@ -1807,7 +1819,7 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target
void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 decimation, bool enforce_tri_limit)
{
- LL_INFOS() << "Generating lod " << which_lod << " using meshoptimizer" << LL_ENDL;
+ LL_DEBUGS("Upload") << "Generating lod " << which_lod << " using meshoptimizer" << LL_ENDL;
// Allow LoD from -1 to LLModel::LOD_PHYSICS
if (which_lod < -1 || which_lod > LLModel::NUM_LODS - 1)
{
@@ -1884,6 +1896,12 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
mMaxTriangleLimit = base_triangle_count;
+ // For logging purposes
+ S32 meshes_processed = 0;
+ S32 meshes_simplified = 0;
+ S32 meshes_sloppy_simplified = 0;
+ S32 meshes_fail_count = 0;
+
// Build models
S32 start = LLModel::LOD_HIGH;
@@ -1893,7 +1911,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
{
start = which_lod;
end = which_lod;
- }
+ };
for (S32 lod = start; lod >= end; --lod)
{
@@ -1956,6 +1974,11 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
const LLVolumeFace &face = base->getVolumeFace(face_idx);
LLVolumeFace &new_face = target_model->getVolumeFace(face_idx);
new_face = face;
+ meshes_fail_count++;
+ }
+ else
+ {
+ meshes_simplified++;
}
}
}
@@ -1968,7 +1991,18 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
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, MESH_OPTIMIZER_FULL);
+ if (genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL) < 0)
+ {
+ meshes_fail_count++;
+ }
+ else
+ {
+ meshes_simplified++;
+ }
+ }
+ else
+ {
+ meshes_sloppy_simplified++;
}
}
}
@@ -2068,25 +2102,28 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL);
}
- LL_INFOS() << "Model " << target_model->getName()
+ LL_DEBUGS("Upload") << "Model " << target_model->getName()
<< " lod " << which_lod
<< " resulting ratio " << precise_ratio
<< " simplified using per model method." << LL_ENDL;
+ meshes_simplified++;
}
else
{
- LL_INFOS() << "Model " << target_model->getName()
+ LL_DEBUGS("Upload") << "Model " << target_model->getName()
<< " lod " << which_lod
<< " resulting ratio " << sloppy_ratio
<< " sloppily simplified using per model method." << LL_ENDL;
+ meshes_sloppy_simplified++;
}
}
else
{
- LL_INFOS() << "Model " << target_model->getName()
+ LL_DEBUGS("Upload") << "Model " << target_model->getName()
<< " lod " << which_lod
<< " resulting ratio " << precise_ratio
<< " simplified using per model method." << LL_ENDL;
+ meshes_simplified++;
}
}
@@ -2100,6 +2137,8 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
//copy material list
target_model->mMaterialList = base->mMaterialList;
+ meshes_processed++;
+
if (!validate_model(target_model))
{
LL_ERRS() << "Invalid model generated when creating LODs" << LL_ENDL;
@@ -2129,6 +2168,11 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
}
}
}
+
+ LL_INFOS("Upload") << "LOD " << which_lod << ", Mesh optimizer processed meshes : " << meshes_processed
+ <<" simplified: " << meshes_simplified
+ << ", slopily simplified: " << meshes_sloppy_simplified
+ << ", failures: " << meshes_fail_count << LL_ENDL;
}
void LLModelPreview::updateStatusMessages()
@@ -3090,25 +3134,48 @@ void LLModelPreview::lookupLODModelFiles(S32 lod)
S32 next_lod = (lod - 1 >= LLModel::LOD_IMPOSTOR) ? lod - 1 : LLModel::LOD_PHYSICS;
std::string lod_filename = mLODFile[LLModel::LOD_HIGH];
- std::string ext = ".dae";
std::string lod_filename_lower(lod_filename);
LLStringUtil::toLower(lod_filename_lower);
- std::string::size_type i = lod_filename_lower.rfind(ext);
- if (i != std::string::npos)
+
+ // Check for each supported file extension
+ std::vector<std::string> supported_exts = { ".dae", ".gltf", ".glb" };
+ std::string found_ext;
+ std::string::size_type ext_pos = std::string::npos;
+
+ for (const auto& ext : supported_exts)
{
- lod_filename.replace(i, lod_filename.size() - ext.size(), getLodSuffix(next_lod) + ext);
+ std::string::size_type i = lod_filename_lower.rfind(ext);
+ if (i != std::string::npos)
+ {
+ ext_pos = i;
+ found_ext = ext;
+ break;
+ }
}
- if (gDirUtilp->fileExists(lod_filename))
+
+ if (ext_pos != std::string::npos)
{
- LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
- if (fmp)
+ // Replace extension with LOD suffix + original extension
+ std::string lod_file_to_check = lod_filename;
+ lod_file_to_check.replace(ext_pos, found_ext.size(), getLodSuffix(next_lod) + found_ext);
+
+ if (gDirUtilp->fileExists(lod_file_to_check))
+ {
+ LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
+ if (fmp)
+ {
+ fmp->setCtrlLoadFromFile(next_lod);
+ }
+ loadModel(lod_file_to_check, next_lod);
+ }
+ else
{
- fmp->setCtrlLoadFromFile(next_lod);
+ lookupLODModelFiles(next_lod);
}
- loadModel(lod_filename, next_lod);
}
else
{
+ // No recognized extension found, continue with next LOD
lookupLODModelFiles(next_lod);
}
}
diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp
index 5b059516cd..5c66f34750 100644
--- a/indra/newview/llpanelface.cpp
+++ b/indra/newview/llpanelface.cpp
@@ -162,6 +162,36 @@ void LLPanelFace::updateSelectedGLTFMaterials(std::function<void(LLGLTFMaterial*
LLSelectMgr::getInstance()->getSelection()->applyToTEs(&select_func);
}
+void LLPanelFace::updateSelectedGLTFMaterialsWithScale(std::function<void(LLGLTFMaterial*, const F32, const F32)> func)
+{
+ struct LLSelectedTEGLTFMaterialFunctor : public LLSelectedTEFunctor
+ {
+ LLSelectedTEGLTFMaterialFunctor(std::function<void(LLGLTFMaterial*, const F32, const F32)> func) : mFunc(func) {}
+ virtual ~LLSelectedTEGLTFMaterialFunctor() {};
+ bool apply(LLViewerObject* object, S32 face) override
+ {
+ LLGLTFMaterial new_override;
+ const LLTextureEntry* tep = object->getTE(face);
+ if (tep->getGLTFMaterialOverride())
+ {
+ new_override = *tep->getGLTFMaterialOverride();
+ }
+
+ U32 s_axis = VX;
+ U32 t_axis = VY;
+ LLPrimitive::getTESTAxes(face, &s_axis, &t_axis);
+ mFunc(&new_override, object->getScale().mV[s_axis], object->getScale().mV[t_axis]);
+ LLGLTFMaterialList::queueModify(object, face, &new_override);
+
+ return true;
+ }
+
+ std::function<void(LLGLTFMaterial*, const F32, const F32)> mFunc;
+ } select_func(func);
+
+ LLSelectMgr::getInstance()->getSelection()->applyToTEs(&select_func);
+}
+
template<typename T>
void readSelectedGLTFMaterial(std::function<T(const LLGLTFMaterial*)> func, T& value, bool& identical, bool has_tolerance, T tolerance)
{
@@ -182,6 +212,36 @@ void readSelectedGLTFMaterial(std::function<T(const LLGLTFMaterial*)> func, T& v
identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&select_func, value, has_tolerance, tolerance);
}
+void getSelectedGLTFMaterialMaxRepeats(LLGLTFMaterial::TextureInfo channel, F32& repeats, bool& identical)
+{
+ // The All channel should read base color values
+ if (channel == LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_COUNT)
+ channel = LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_BASE_COLOR;
+
+ struct LLSelectedTEGetGLTFMaterialMaxRepeatsFunctor : public LLSelectedTEGetFunctor<F32>
+ {
+ LLSelectedTEGetGLTFMaterialMaxRepeatsFunctor(LLGLTFMaterial::TextureInfo channel) : mChannel(channel) {}
+ virtual ~LLSelectedTEGetGLTFMaterialMaxRepeatsFunctor() {};
+ F32 get(LLViewerObject* object, S32 face) override
+ {
+ const LLTextureEntry* tep = object->getTE(face);
+ const LLGLTFMaterial* render_material = tep->getGLTFRenderMaterial();
+ if (!render_material)
+ return 0.f;
+
+ U32 s_axis = VX;
+ U32 t_axis = VY;
+ LLPrimitive::getTESTAxes(face, &s_axis, &t_axis);
+ F32 repeats_u = render_material->mTextureTransform[mChannel].mScale[VX] / object->getScale().mV[s_axis];
+ F32 repeats_v = render_material->mTextureTransform[mChannel].mScale[VY] / object->getScale().mV[t_axis];
+ return llmax(repeats_u, repeats_v);
+ }
+
+ LLGLTFMaterial::TextureInfo mChannel;
+ } max_repeats_func(channel);
+ identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&max_repeats_func, repeats);
+}
+
BOOST_STATIC_ASSERT(MATTYPE_DIFFUSE == LLRender::DIFFUSE_MAP && MATTYPE_NORMAL == LLRender::NORMAL_MAP && MATTYPE_SPECULAR == LLRender::SPECULAR_MAP);
//
@@ -216,7 +276,7 @@ LLRender::eTexIndex LLPanelFace::getMatTextureChannel()
return LLRender::NORMAL_MAP;
break;
case MATTYPE_SPECULAR: // "Shininess (specular)"
- if (getCurrentNormalMap().notNull())
+ if (getCurrentSpecularMap().notNull())
return LLRender::SPECULAR_MAP;
break;
}
@@ -322,6 +382,7 @@ bool LLPanelFace::postBuild()
getChildSetCommitCallback(mPBRScaleU, "gltfTextureScaleU", [&](LLUICtrl*, const LLSD&) { onCommitGLTFTextureScaleU(); });
getChildSetCommitCallback(mPBRScaleV, "gltfTextureScaleV", [&](LLUICtrl*, const LLSD&) { onCommitGLTFTextureScaleV(); });
+ getChildSetCommitCallback(mPBRRepeat, "gltfRptctrl", [&](LLUICtrl*, const LLSD&) { onCommitGLTFRepeatsPerMeter(); });
getChildSetCommitCallback(mPBRRotate, "gltfTextureRotation", [&](LLUICtrl*, const LLSD&) { onCommitGLTFRotation(); });
getChildSetCommitCallback(mPBROffsetU, "gltfTextureOffsetU", [&](LLUICtrl*, const LLSD&) { onCommitGLTFTextureOffsetU(); });
getChildSetCommitCallback(mPBROffsetV, "gltfTextureOffsetV", [&](LLUICtrl*, const LLSD&) { onCommitGLTFTextureOffsetV(); });
@@ -1398,9 +1459,18 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
spec_scale_s = editable ? spec_scale_s : 1.0f;
spec_scale_s *= identical_planar_texgen ? 2.0f : 1.0f;
- mTexScaleU->setValue(diff_scale_s);
- mShinyScaleU->setValue(spec_scale_s);
- mBumpyScaleU->setValue(norm_scale_s);
+ if (force_set_values)
+ {
+ mTexScaleU->forceSetValue(diff_scale_s);
+ mShinyScaleU->forceSetValue(spec_scale_s);
+ mBumpyScaleU->forceSetValue(norm_scale_s);
+ }
+ else
+ {
+ mTexScaleU->setValue(diff_scale_s);
+ mShinyScaleU->setValue(spec_scale_s);
+ mBumpyScaleU->setValue(norm_scale_s);
+ }
mTexScaleU->setEnabled(editable && has_material);
mShinyScaleU->setEnabled(editable && has_material && specmap_id.notNull());
@@ -1448,13 +1518,16 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
if (force_set_values)
{
mTexScaleV->forceSetValue(diff_scale_t);
+ mShinyScaleV->forceSetValue(spec_scale_t);
+ mBumpyScaleV->forceSetValue(norm_scale_t);
}
else
{
mTexScaleV->setValue(diff_scale_t);
+ mShinyScaleV->setValue(spec_scale_t);
+ mBumpyScaleV->setValue(norm_scale_t);
}
- mShinyScaleV->setValue(spec_scale_t);
- mBumpyScaleV->setValue(norm_scale_t);
+
mTexScaleV->setTentative(LLSD(diff_scale_tentative));
mShinyScaleV->setTentative(LLSD(spec_scale_tentative));
@@ -1594,36 +1667,57 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
F32 repeats_norm = 1.f;
F32 repeats_spec = 1.f;
+ F32 repeats_pbr_basecolor = 1.f;
+ F32 repeats_pbr_metallic_roughness = 1.f;
+ F32 repeats_pbr_normal = 1.f;
+ F32 repeats_pbr_emissive = 1.f;
+
bool identical_diff_repeats = false;
bool identical_norm_repeats = false;
bool identical_spec_repeats = false;
- LLSelectedTE::getMaxDiffuseRepeats(repeats_diff, identical_diff_repeats);
- LLSelectedTEMaterial::getMaxNormalRepeats(repeats_norm, identical_norm_repeats);
- LLSelectedTEMaterial::getMaxSpecularRepeats(repeats_spec, identical_spec_repeats);
+ bool identical_pbr_basecolor_repeats = false;
+ bool identical_pbr_metallic_roughness_repeats = false;
+ bool identical_pbr_normal_repeats = false;
+ bool identical_pbr_emissive_repeats = false;
{
+ LLSpinCtrl* repeats_spin_ctrl = nullptr;
S32 index = mComboTexGen ? mComboTexGen->getCurrentIndex() : 0;
bool enabled = editable && (index != 1);
bool identical_repeats = true;
S32 material_selection = mComboMatMedia->getCurrentIndex();
F32 repeats = 1.0f;
- U32 material_type = MATTYPE_DIFFUSE;
- if (material_selection == MATMEDIA_MATERIAL)
+ LLRender::eTexIndex material_channel = LLRender::DIFFUSE_MAP;
+ if (material_selection != MATMEDIA_PBR)
{
- material_type = mRadioMaterialType->getSelectedIndex();
+ repeats_spin_ctrl = mTexRepeat;
+ material_channel = getMatTextureChannel();
+ LLSelectedTE::getMaxDiffuseRepeats(repeats_diff, identical_diff_repeats);
+ LLSelectedTEMaterial::getMaxNormalRepeats(repeats_norm, identical_norm_repeats);
+ LLSelectedTEMaterial::getMaxSpecularRepeats(repeats_spec, identical_spec_repeats);
}
else if (material_selection == MATMEDIA_PBR)
{
+ repeats_spin_ctrl = mPBRRepeat;
enabled = editable && has_pbr_material;
- material_type = mRadioPbrType->getSelectedIndex();
+ material_channel = getPBRTextureChannel();
+
+ getSelectedGLTFMaterialMaxRepeats(LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_BASE_COLOR,
+ repeats_pbr_basecolor, identical_pbr_basecolor_repeats);
+ getSelectedGLTFMaterialMaxRepeats(LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS,
+ repeats_pbr_metallic_roughness, identical_pbr_metallic_roughness_repeats);
+ getSelectedGLTFMaterialMaxRepeats(LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_NORMAL,
+ repeats_pbr_normal, identical_pbr_normal_repeats);
+ getSelectedGLTFMaterialMaxRepeats(LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_EMISSIVE,
+ repeats_pbr_emissive, identical_pbr_emissive_repeats);
}
- switch (material_type)
+ switch (material_channel)
{
default:
- case MATTYPE_DIFFUSE:
+ case LLRender::DIFFUSE_MAP:
if (material_selection != MATMEDIA_PBR)
{
enabled = editable && !id.isNull();
@@ -1631,7 +1725,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
identical_repeats = identical_diff_repeats;
repeats = repeats_diff;
break;
- case MATTYPE_SPECULAR:
+ case LLRender::SPECULAR_MAP:
if (material_selection != MATMEDIA_PBR)
{
enabled = (editable && ((shiny == SHINY_TEXTURE) && !specmap_id.isNull()));
@@ -1639,7 +1733,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
identical_repeats = identical_spec_repeats;
repeats = repeats_spec;
break;
- case MATTYPE_NORMAL:
+ case LLRender::NORMAL_MAP:
if (material_selection != MATMEDIA_PBR)
{
enabled = (editable && ((bumpy == BUMPY_TEXTURE) && !normmap_id.isNull()));
@@ -1647,6 +1741,23 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
identical_repeats = identical_norm_repeats;
repeats = repeats_norm;
break;
+ case LLRender::NUM_TEXTURE_CHANNELS:
+ case LLRender::BASECOLOR_MAP:
+ identical_repeats = identical_pbr_basecolor_repeats;
+ repeats = repeats_pbr_basecolor;
+ break;
+ case LLRender::METALLIC_ROUGHNESS_MAP:
+ identical_repeats = identical_pbr_metallic_roughness_repeats;
+ repeats = repeats_pbr_metallic_roughness;
+ break;
+ case LLRender::GLTF_NORMAL_MAP:
+ identical_repeats = identical_pbr_normal_repeats;
+ repeats = repeats_pbr_normal;
+ break;
+ case LLRender::EMISSIVE_MAP:
+ identical_repeats = identical_pbr_emissive_repeats;
+ repeats = repeats_pbr_emissive;
+ break;
}
bool repeats_tentative = !identical_repeats;
@@ -1654,14 +1765,14 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
if (force_set_values)
{
// onCommit, previosly edited element updates related ones
- mTexRepeat->forceSetValue(editable ? repeats : 1.0f);
+ repeats_spin_ctrl->forceSetValue(editable ? repeats : 1.0f);
}
else
{
- mTexRepeat->setValue(editable ? repeats : 1.0f);
+ repeats_spin_ctrl->setValue(editable ? repeats : 1.0f);
}
- mTexRepeat->setTentative(LLSD(repeats_tentative));
- mTexRepeat->setEnabled(has_material && !identical_planar_texgen && enabled);
+ repeats_spin_ctrl->setTentative(LLSD(repeats_tentative));
+ repeats_spin_ctrl->setEnabled(!identical_planar_texgen && enabled);
}
}
@@ -1807,6 +1918,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
}
mLabelColorTransp->setEnabled(false);
mTexRepeat->setEnabled(false);
+ mPBRRepeat->setEnabled(false);
mLabelTexGen->setEnabled(false);
mLabelShininess->setEnabled(false);
mLabelBumpiness->setEnabled(false);
@@ -2002,6 +2114,7 @@ void LLPanelFace::updateVisibilityGLTF(LLViewerObject* objectp /*= nullptr */)
mPBRRotate->setVisible(show_pbr);
mPBROffsetU->setVisible(show_pbr);
mPBROffsetV->setVisible(show_pbr);
+ mPBRRepeat->setVisible(show_pbr);
}
void LLPanelFace::updateCopyTexButton()
@@ -3652,18 +3765,8 @@ void LLPanelFace::onCommitRepeatsPerMeter()
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
{
LLSelectMgr::getInstance()->selectionTexScaleAutofit(repeats_per_meter);
-
- mBumpyScaleU->setValue(obj_scale_s * repeats_per_meter);
- mBumpyScaleV->setValue(obj_scale_t * repeats_per_meter);
-
- LLSelectedTEMaterial::setNormalRepeatX(this, obj_scale_s * repeats_per_meter);
- LLSelectedTEMaterial::setNormalRepeatY(this, obj_scale_t * repeats_per_meter);
-
- mShinyScaleU->setValue(obj_scale_s * repeats_per_meter);
- mShinyScaleV->setValue(obj_scale_t * repeats_per_meter);
-
- LLSelectedTEMaterial::setSpecularRepeatX(this, obj_scale_s * repeats_per_meter);
- LLSelectedTEMaterial::setSpecularRepeatY(this, obj_scale_t * repeats_per_meter);
+ LLSelectedTEMaterial::selectionNormalScaleAutofit(this, repeats_per_meter);
+ LLSelectedTEMaterial::selectionSpecularScaleAutofit(this, repeats_per_meter);
}
else
{
@@ -3674,18 +3777,10 @@ void LLPanelFace::onCommitRepeatsPerMeter()
LLSelectMgr::getInstance()->selectionTexScaleAutofit(repeats_per_meter);
break;
case MATTYPE_NORMAL:
- mBumpyScaleU->setValue(obj_scale_s * repeats_per_meter);
- mBumpyScaleV->setValue(obj_scale_t * repeats_per_meter);
-
- LLSelectedTEMaterial::setNormalRepeatX(this, obj_scale_s * repeats_per_meter);
- LLSelectedTEMaterial::setNormalRepeatY(this, obj_scale_t * repeats_per_meter);
+ LLSelectedTEMaterial::selectionNormalScaleAutofit(this, repeats_per_meter);
break;
case MATTYPE_SPECULAR:
- mBumpyScaleU->setValue(obj_scale_s * repeats_per_meter);
- mBumpyScaleV->setValue(obj_scale_t * repeats_per_meter);
-
- LLSelectedTEMaterial::setSpecularRepeatX(this, obj_scale_s * repeats_per_meter);
- LLSelectedTEMaterial::setSpecularRepeatY(this, obj_scale_t * repeats_per_meter);
+ LLSelectedTEMaterial::selectionSpecularScaleAutofit(this, repeats_per_meter);
break;
default:
llassert(false);
@@ -3696,6 +3791,21 @@ void LLPanelFace::onCommitRepeatsPerMeter()
updateUI(true);
}
+// Commit the number of GLTF repeats per meter
+void LLPanelFace::onCommitGLTFRepeatsPerMeter()
+{
+ F32 repeats_per_meter = (F32)mPBRRepeat->getValue().asReal();
+
+ LLGLTFMaterial::TextureInfo material_type = getPBRTextureInfo();
+ updateGLTFTextureTransformWithScale(material_type, [&](LLGLTFMaterial::TextureTransform* new_transform, F32 scale_s, F32 scale_t)
+ {
+ new_transform->mScale.mV[VX] = scale_s * repeats_per_meter;
+ new_transform->mScale.mV[VY] = scale_t * repeats_per_meter;
+ });
+
+ updateUI(true);
+}
+
struct LLPanelFaceSetMediaFunctor : public LLSelectedTEFunctor
{
virtual bool apply(LLViewerObject* object, S32 te)
@@ -4795,6 +4905,29 @@ void LLPanelFace::updateGLTFTextureTransform(std::function<void(LLGLTFMaterial::
}
}
+void LLPanelFace::updateGLTFTextureTransformWithScale(const LLGLTFMaterial::TextureInfo texture_info, std::function<void(LLGLTFMaterial::TextureTransform*, const F32, const F32)> edit)
+{
+ if (texture_info == LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT)
+ {
+ updateSelectedGLTFMaterialsWithScale([&](LLGLTFMaterial* new_override, const F32 scale_s, const F32 scale_t)
+ {
+ for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
+ {
+ LLGLTFMaterial::TextureTransform& new_transform = new_override->mTextureTransform[(LLGLTFMaterial::TextureInfo)i];
+ edit(&new_transform, scale_s, scale_t);
+ }
+ });
+ }
+ else
+ {
+ updateSelectedGLTFMaterialsWithScale([&](LLGLTFMaterial* new_override, const F32 scale_s, const F32 scale_t)
+ {
+ LLGLTFMaterial::TextureTransform& new_transform = new_override->mTextureTransform[texture_info];
+ edit(&new_transform, scale_s, scale_t);
+ });
+ }
+}
+
void LLPanelFace::setMaterialOverridesFromSelection()
{
const LLGLTFMaterial::TextureInfo texture_info = getPBRTextureInfo();
@@ -4870,8 +5003,9 @@ void LLPanelFace::setMaterialOverridesFromSelection()
}
}
- mPBRScaleU->setValue(transform.mScale[VX]);
- mPBRScaleV->setValue(transform.mScale[VY]);
+ // Force set scales just in case they were set by repeats per meter and their spinner is focused
+ mPBRScaleU->forceSetValue(transform.mScale[VX]);
+ mPBRScaleV->forceSetValue(transform.mScale[VY]);
mPBRRotate->setValue(transform.mRotation * RAD_TO_DEG);
mPBROffsetU->setValue(transform.mOffset[VX]);
mPBROffsetV->setValue(transform.mOffset[VY]);
@@ -4881,6 +5015,12 @@ void LLPanelFace::setMaterialOverridesFromSelection()
mPBRRotate->setTentative(!rotation_same);
mPBROffsetU->setTentative(!offset_u_same);
mPBROffsetV->setTentative(!offset_v_same);
+
+ F32 repeats = 1.f;
+ bool identical = false;
+ getSelectedGLTFMaterialMaxRepeats(getPBRDropChannel(), repeats, identical);
+ mPBRRepeat->forceSetValue(repeats);
+ mPBRRepeat->setTentative(!identical || !scale_u_same || !scale_v_same);
}
void LLPanelFace::Selection::connect()
@@ -5374,6 +5514,62 @@ void LLPanelFace::LLSelectedTEMaterial::getCurrentDiffuseAlphaMode(U8& diffuse_a
identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &get_diff_mode, diffuse_alpha_mode);
}
+void LLPanelFace::LLSelectedTEMaterial::selectionNormalScaleAutofit(LLPanelFace* panel_face, F32 repeats_per_meter)
+{
+ struct f : public LLSelectedTEFunctor
+ {
+ LLPanelFace* mFacePanel;
+ F32 mRepeatsPerMeter;
+ f(LLPanelFace* face_panel, const F32& repeats_per_meter) : mFacePanel(face_panel), mRepeatsPerMeter(repeats_per_meter) {}
+ bool apply(LLViewerObject* object, S32 te)
+ {
+ if (object->permModify())
+ {
+ // Compute S,T to axis mapping
+ U32 s_axis, t_axis;
+ if (!LLPrimitive::getTESTAxes(te, &s_axis, &t_axis))
+ return true;
+
+ F32 new_s = object->getScale().mV[s_axis] * mRepeatsPerMeter;
+ F32 new_t = object->getScale().mV[t_axis] * mRepeatsPerMeter;
+
+ setNormalRepeatX(mFacePanel, new_s, te);
+ setNormalRepeatY(mFacePanel, new_t, te);
+ }
+ return true;
+ }
+ } setfunc(panel_face, repeats_per_meter);
+ LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc);
+}
+
+void LLPanelFace::LLSelectedTEMaterial::selectionSpecularScaleAutofit(LLPanelFace* panel_face, F32 repeats_per_meter)
+{
+ struct f : public LLSelectedTEFunctor
+ {
+ LLPanelFace* mFacePanel;
+ F32 mRepeatsPerMeter;
+ f(LLPanelFace* face_panel, const F32& repeats_per_meter) : mFacePanel(face_panel), mRepeatsPerMeter(repeats_per_meter) {}
+ bool apply(LLViewerObject* object, S32 te)
+ {
+ if (object->permModify())
+ {
+ // Compute S,T to axis mapping
+ U32 s_axis, t_axis;
+ if (!LLPrimitive::getTESTAxes(te, &s_axis, &t_axis))
+ return true;
+
+ F32 new_s = object->getScale().mV[s_axis] * mRepeatsPerMeter;
+ F32 new_t = object->getScale().mV[t_axis] * mRepeatsPerMeter;
+
+ setSpecularRepeatX(mFacePanel, new_s, te);
+ setSpecularRepeatY(mFacePanel, new_t, te);
+ }
+ return true;
+ }
+ } setfunc(panel_face, repeats_per_meter);
+ LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc);
+}
+
void LLPanelFace::LLSelectedTE::getObjectScaleS(F32& scale_s, bool& identical)
{
struct LLSelectedTEGetObjectScaleS : public LLSelectedTEGetFunctor<F32>
diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h
index ce3dd8bdea..0911378582 100644
--- a/indra/newview/llpanelface.h
+++ b/indra/newview/llpanelface.h
@@ -250,6 +250,7 @@ protected:
void onCommitGLTFRotation();
void onCommitGLTFTextureOffsetU();
void onCommitGLTFTextureOffsetV();
+ void onCommitGLTFRepeatsPerMeter();
void onClickAutoFix();
void onAlignTexture();
@@ -361,6 +362,7 @@ private:
LLButton* mDelMedia { nullptr };
LLSpinCtrl* mPBRScaleU { nullptr };
LLSpinCtrl* mPBRScaleV { nullptr };
+ LLSpinCtrl* mPBRRepeat { nullptr };
LLSpinCtrl* mPBRRotate { nullptr };
LLSpinCtrl* mPBROffsetU { nullptr };
LLSpinCtrl* mPBROffsetV { nullptr };
@@ -554,7 +556,9 @@ private:
void updateVisibilityGLTF(LLViewerObject* objectp = nullptr);
void updateSelectedGLTFMaterials(std::function<void(LLGLTFMaterial*)> func);
+ void updateSelectedGLTFMaterialsWithScale(std::function<void(LLGLTFMaterial*, const F32, const F32)> func);
void updateGLTFTextureTransform(std::function<void(LLGLTFMaterial::TextureTransform*)> edit);
+ void updateGLTFTextureTransformWithScale(const LLGLTFMaterial::TextureInfo texture_info, std::function<void(LLGLTFMaterial::TextureTransform*, const F32, const F32)> edit);
void setMaterialOverridesFromSelection();
@@ -649,6 +653,8 @@ public:
static void getMaxSpecularRepeats(F32& repeats, bool& identical);
static void getMaxNormalRepeats(F32& repeats, bool& identical);
static void getCurrentDiffuseAlphaMode(U8& diffuse_alpha_mode, bool& identical, bool diffuse_texture_has_alpha);
+ static void selectionNormalScaleAutofit(LLPanelFace* panel_face, F32 repeats_per_meter);
+ static void selectionSpecularScaleAutofit(LLPanelFace* panel_face, F32 repeats_per_meter);
DEF_GET_MAT_STATE(LLUUID, const LLUUID&, getNormalID, LLUUID::null, false, LLUUID::null);
DEF_GET_MAT_STATE(LLUUID, const LLUUID&, getSpecularID, LLUUID::null, false, LLUUID::null);
diff --git a/indra/newview/llpanelgroupbulk.cpp b/indra/newview/llpanelgroupbulk.cpp
index 8032e207cd..81c0bd97be 100644
--- a/indra/newview/llpanelgroupbulk.cpp
+++ b/indra/newview/llpanelgroupbulk.cpp
@@ -54,17 +54,16 @@
//////////////////////////////////////////////////////////////////////////
LLPanelGroupBulkImpl::LLPanelGroupBulkImpl(const LLUUID& group_id) :
mGroupID(group_id),
- mBulkAgentList(NULL),
- mOKButton(NULL),
+ mBulkAgentList(nullptr),
+ mOKButton(nullptr),
mAddButton(nullptr),
- mRemoveButton(NULL),
- mGroupName(NULL),
+ mRemoveButton(nullptr),
+ mGroupName(nullptr),
mLoadingText(),
mTooManySelected(),
- mCloseCallback(NULL),
- mCloseCallbackUserData(NULL),
- mAvatarNameCacheConnection(),
- mRoleNames(NULL),
+ mCloseCallback(nullptr),
+ mCloseCallbackUserData(nullptr),
+ mRoleNames(nullptr),
mOwnerWarning(),
mAlreadyInGroup(),
mConfirmedOwnerInvite(false),
@@ -74,10 +73,13 @@ LLPanelGroupBulkImpl::LLPanelGroupBulkImpl(const LLUUID& group_id) :
LLPanelGroupBulkImpl::~LLPanelGroupBulkImpl()
{
- if (mAvatarNameCacheConnection.connected())
+ for (auto& [id, connection] : mAvatarNameCacheConnections)
{
- mAvatarNameCacheConnection.disconnect();
+ if (connection.connected())
+ connection.disconnect();
}
+
+ mAvatarNameCacheConnections.clear();
}
void LLPanelGroupBulkImpl::callbackClickAdd(LLPanelGroupBulk* panelp)
@@ -124,43 +126,42 @@ void LLPanelGroupBulkImpl::callbackSelect(LLUICtrl* ctrl, void* userdata)
void LLPanelGroupBulkImpl::addUsers(const uuid_vec_t& agent_ids)
{
- std::vector<std::string> names;
for (const LLUUID& agent_id : agent_ids)
{
- LLAvatarName av_name;
- if (LLAvatarNameCache::get(agent_id, &av_name))
+ if (LLAvatarName av_name; LLAvatarNameCache::get(agent_id, &av_name))
{
onAvatarNameCache(agent_id, av_name);
}
else
{
- if (mAvatarNameCacheConnection.connected())
+ if (auto found = mAvatarNameCacheConnections.find(agent_id); found != mAvatarNameCacheConnections.end())
{
- mAvatarNameCacheConnection.disconnect();
+ if (found->second.connected())
+ found->second.disconnect();
+
+ mAvatarNameCacheConnections.erase(found);
}
- // *TODO : Add a callback per avatar name being fetched.
- mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_id,
+
+ mAvatarNameCacheConnections.try_emplace(agent_id, LLAvatarNameCache::get(agent_id,
[&](const LLUUID& agent_id, const LLAvatarName& av_name)
{
onAvatarNameCache(agent_id, av_name);
- });
+ }));
}
}
}
void LLPanelGroupBulkImpl::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name)
{
- if (mAvatarNameCacheConnection.connected())
+ if (auto found = mAvatarNameCacheConnections.find(agent_id); found != mAvatarNameCacheConnections.end())
{
- mAvatarNameCacheConnection.disconnect();
- }
+ if (found->second.connected())
+ found->second.disconnect();
- std::vector<std::string> names;
- uuid_vec_t agent_ids;
- agent_ids.push_back(agent_id);
- names.push_back(av_name.getCompleteName());
+ mAvatarNameCacheConnections.erase(found);
+ }
- addUsers(names, agent_ids);
+ addUsers({ av_name.getCompleteName() }, { agent_id });
}
void LLPanelGroupBulkImpl::handleRemove()
@@ -232,7 +233,7 @@ void LLPanelGroupBulkImpl::addUsers(const std::vector<std::string>& names, const
}
}
-void LLPanelGroupBulkImpl::setGroupName(std::string name)
+void LLPanelGroupBulkImpl::setGroupName(const std::string& name)
{
if (mGroupName)
{
@@ -337,12 +338,7 @@ void LLPanelGroupBulk::updateGroupData()
void LLPanelGroupBulk::addUserCallback(const LLUUID& id, const LLAvatarName& av_name)
{
- std::vector<std::string> names;
- uuid_vec_t agent_ids;
- agent_ids.push_back(id);
- names.push_back(av_name.getAccountName());
-
- mImplementation->addUsers(names, agent_ids);
+ mImplementation->addUsers({ av_name.getAccountName() }, { id });
}
void LLPanelGroupBulk::setCloseCallback(void (*close_callback)(void*), void* data)
diff --git a/indra/newview/llpanelgroupbulkimpl.h b/indra/newview/llpanelgroupbulkimpl.h
index 5515bd6d9a..f186ae5373 100644
--- a/indra/newview/llpanelgroupbulkimpl.h
+++ b/indra/newview/llpanelgroupbulkimpl.h
@@ -59,7 +59,7 @@ public:
void handleSelection();
void addUsers(const std::vector<std::string>& names, const uuid_vec_t& agent_ids);
- void setGroupName(std::string name);
+ void setGroupName(const std::string& name);
public:
@@ -84,7 +84,7 @@ public:
void (*mCloseCallback)(void* data);
void* mCloseCallbackUserData;
- boost::signals2::connection mAvatarNameCacheConnection;
+ std::map<LLUUID, boost::signals2::connection> mAvatarNameCacheConnections;
// The following are for the LLPanelGroupInvite subclass only.
// These aren't needed for LLPanelGroupBulkBan, but if we have to add another
diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp
index d964fa9170..44465cbc33 100644
--- a/indra/newview/llpanelmaininventory.cpp
+++ b/indra/newview/llpanelmaininventory.cpp
@@ -78,9 +78,9 @@ static LLPanelInjector<LLPanelMainInventory> t_inventory("panel_main_inventory")
class LLFloaterInventoryFinder : public LLFloater
{
public:
- LLFloaterInventoryFinder( LLPanelMainInventory* inventory_view);
- virtual void draw();
- /*virtual*/ bool postBuild();
+ LLFloaterInventoryFinder(LLPanelMainInventory* inventory_view);
+ void draw();
+ bool postBuild();
void changeFilter(LLInventoryFilter* filter);
void updateElementsFromFilter();
bool getCheckShowEmpty();
@@ -90,17 +90,35 @@ public:
void onCreatorSelfFilterCommit();
void onCreatorOtherFilterCommit();
- static void onTimeAgo(LLUICtrl*, void *);
- static void onCloseBtn(void* user_data);
- static void selectAllTypes(void* user_data);
- static void selectNoTypes(void* user_data);
+ void onTimeAgo();
+ void onCloseBtn();
+ void selectAllTypes();
+ void selectNoTypes();
private:
- LLPanelMainInventory* mPanelMainInventory;
- LLSpinCtrl* mSpinSinceDays;
- LLSpinCtrl* mSpinSinceHours;
- LLCheckBoxCtrl* mCreatorSelf;
- LLCheckBoxCtrl* mCreatorOthers;
- LLInventoryFilter* mFilter;
+ LLPanelMainInventory* mPanelMainInventory{ nullptr };
+ LLSpinCtrl* mSpinSinceDays{ nullptr };
+ LLSpinCtrl* mSpinSinceHours{ nullptr };
+ LLCheckBoxCtrl* mCreatorSelf{ nullptr };
+ LLCheckBoxCtrl* mCreatorOthers{ nullptr };
+ LLInventoryFilter* mFilter{ nullptr };
+
+ LLCheckBoxCtrl* mCheckAnimation{ nullptr };
+ LLCheckBoxCtrl* mCheckCallingCard{ nullptr };
+ LLCheckBoxCtrl* mCheckClothing{ nullptr };
+ LLCheckBoxCtrl* mCheckGesture{ nullptr };
+ LLCheckBoxCtrl* mCheckLandmark{ nullptr };
+ LLCheckBoxCtrl* mCheckMaterial{ nullptr };
+ LLCheckBoxCtrl* mCheckNotecard{ nullptr };
+ LLCheckBoxCtrl* mCheckObject{ nullptr };
+ LLCheckBoxCtrl* mCheckScript{ nullptr };
+ LLCheckBoxCtrl* mCheckSounds{ nullptr };
+ LLCheckBoxCtrl* mCheckTexture{ nullptr };
+ LLCheckBoxCtrl* mCheckSnapshot{ nullptr };
+ LLCheckBoxCtrl* mCheckSettings{ nullptr };
+ LLCheckBoxCtrl* mCheckShowEmpty{ nullptr };
+ LLCheckBoxCtrl* mCheckSinceLogoff{ nullptr };
+
+ LLRadioGroup* mRadioDateSearchDirection{ nullptr };
};
///----------------------------------------------------------------------------
@@ -734,7 +752,6 @@ bool LLPanelMainInventory::filtersVisible(void* user_data)
void LLPanelMainInventory::onClearSearch()
{
bool initially_active = false;
- LLFloater *finder = getFinder();
if (mActivePanel && (getActivePanel() != mWornItemsPanel))
{
initially_active = mActivePanel->getFilter().isNotDefault();
@@ -743,9 +760,9 @@ void LLPanelMainInventory::onClearSearch()
mActivePanel->setFilterLinks(LLInventoryFilter::FILTERLINK_INCLUDE_LINKS);
}
- if (finder)
+ if (LLFloaterInventoryFinder* finder = getFinder())
{
- LLFloaterInventoryFinder::selectAllTypes(finder);
+ finder->selectAllTypes();
}
// re-open folders that were initially open in case filter was active
@@ -1145,36 +1162,53 @@ bool LLFloaterInventoryFinder::postBuild()
const LLRect& viewrect = mPanelMainInventory->getRect();
setRect(LLRect(viewrect.mLeft - getRect().getWidth(), viewrect.mTop, viewrect.mLeft, viewrect.mTop - getRect().getHeight()));
- childSetAction("All", selectAllTypes, this);
- childSetAction("None", selectNoTypes, this);
+ childSetAction("All", [this](LLUICtrl*, const LLSD&) { selectAllTypes(); });
+ childSetAction("None", [this](LLUICtrl*, const LLSD&) { selectNoTypes(); });
mSpinSinceHours = getChild<LLSpinCtrl>("spin_hours_ago");
- childSetCommitCallback("spin_hours_ago", onTimeAgo, this);
+ mSpinSinceHours->setCommitCallback([this](LLUICtrl*, const LLSD&) { onTimeAgo(); });
mSpinSinceDays = getChild<LLSpinCtrl>("spin_days_ago");
- childSetCommitCallback("spin_days_ago", onTimeAgo, this);
+ mSpinSinceDays->setCommitCallback([this](LLUICtrl*, const LLSD&) { onTimeAgo(); });
mCreatorSelf = getChild<LLCheckBoxCtrl>("check_created_by_me");
mCreatorOthers = getChild<LLCheckBoxCtrl>("check_created_by_others");
mCreatorSelf->setCommitCallback(boost::bind(&LLFloaterInventoryFinder::onCreatorSelfFilterCommit, this));
mCreatorOthers->setCommitCallback(boost::bind(&LLFloaterInventoryFinder::onCreatorOtherFilterCommit, this));
- childSetAction("Close", onCloseBtn, this);
+ mCheckAnimation = getChild<LLCheckBoxCtrl>("check_animation");
+ mCheckCallingCard = getChild<LLCheckBoxCtrl>("check_calling_card");
+ mCheckClothing = getChild<LLCheckBoxCtrl>("check_clothing");
+ mCheckGesture = getChild<LLCheckBoxCtrl>("check_gesture");
+ mCheckLandmark = getChild<LLCheckBoxCtrl>("check_landmark");
+ mCheckMaterial = getChild<LLCheckBoxCtrl>("check_material");
+ mCheckNotecard = getChild<LLCheckBoxCtrl>("check_notecard");
+ mCheckObject = getChild<LLCheckBoxCtrl>("check_object");
+ mCheckScript = getChild<LLCheckBoxCtrl>("check_script");
+ mCheckSounds = getChild<LLCheckBoxCtrl>("check_sound");
+ mCheckTexture = getChild<LLCheckBoxCtrl>("check_texture");
+ mCheckSnapshot = getChild<LLCheckBoxCtrl>("check_snapshot");
+ mCheckSettings = getChild<LLCheckBoxCtrl>("check_settings");
+ mCheckShowEmpty = getChild<LLCheckBoxCtrl>("check_show_empty");
+ mCheckSinceLogoff = getChild<LLCheckBoxCtrl>("check_since_logoff");
+
+ mRadioDateSearchDirection = getChild<LLRadioGroup>("date_search_direction");
+
+ childSetAction("Close", [this](LLUICtrl*, const LLSD&) { onCloseBtn(); });
updateElementsFromFilter();
+
return true;
}
-void LLFloaterInventoryFinder::onTimeAgo(LLUICtrl *ctrl, void *user_data)
-{
- LLFloaterInventoryFinder *self = (LLFloaterInventoryFinder *)user_data;
- if (!self) return;
- if ( self->mSpinSinceDays->get() || self->mSpinSinceHours->get() )
+void LLFloaterInventoryFinder::onTimeAgo()
+{
+ if (mSpinSinceDays->get() || mSpinSinceHours->get())
{
- self->getChild<LLUICtrl>("check_since_logoff")->setValue(false);
+ mCheckSinceLogoff->setValue(false);
- U32 days = (U32)self->mSpinSinceDays->get();
- U32 hours = (U32)self->mSpinSinceHours->get();
+ U32 days = (U32)mSpinSinceDays->get();
+ U32 hours = (U32)mSpinSinceHours->get();
if (hours >= 24)
{
// Try to handle both cases of spinner clicking and text input in a sensible fashion as best as possible.
@@ -1190,11 +1224,11 @@ void LLFloaterInventoryFinder::onTimeAgo(LLUICtrl *ctrl, void *user_data)
days = hours / 24;
}
hours = (U32)hours % 24;
- self->mSpinSinceHours->setFocus(false);
- self->mSpinSinceDays->setFocus(false);
- self->mSpinSinceDays->set((F32)days);
- self->mSpinSinceHours->set((F32)hours);
- self->mSpinSinceHours->setFocus(true);
+ mSpinSinceHours->setFocus(false);
+ mSpinSinceDays->setFocus(false);
+ mSpinSinceDays->set((F32)days);
+ mSpinSinceHours->set((F32)hours);
+ mSpinSinceHours->setFocus(true);
}
}
}
@@ -1223,29 +1257,28 @@ void LLFloaterInventoryFinder::updateElementsFromFilter()
// update the ui elements
setTitle(mFilter->getName());
- getChild<LLUICtrl>("check_animation")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_ANIMATION));
-
- getChild<LLUICtrl>("check_calling_card")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_CALLINGCARD));
- getChild<LLUICtrl>("check_clothing")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_WEARABLE));
- getChild<LLUICtrl>("check_gesture")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_GESTURE));
- getChild<LLUICtrl>("check_landmark")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_LANDMARK));
- getChild<LLUICtrl>("check_material")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_MATERIAL));
- getChild<LLUICtrl>("check_notecard")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_NOTECARD));
- getChild<LLUICtrl>("check_object")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_OBJECT));
- getChild<LLUICtrl>("check_script")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_LSL));
- getChild<LLUICtrl>("check_sound")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_SOUND));
- getChild<LLUICtrl>("check_texture")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_TEXTURE));
- getChild<LLUICtrl>("check_snapshot")->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_SNAPSHOT));
- getChild<LLUICtrl>("check_settings")->setValue((S32)(filter_types & 0x1 << LLInventoryType::IT_SETTINGS));
- getChild<LLUICtrl>("check_show_empty")->setValue(show_folders == LLInventoryFilter::SHOW_ALL_FOLDERS);
-
- getChild<LLUICtrl>("check_created_by_me")->setValue(show_created_by_me);
- getChild<LLUICtrl>("check_created_by_others")->setValue(show_created_by_others);
-
- getChild<LLUICtrl>("check_since_logoff")->setValue(mFilter->isSinceLogoff());
+ mCheckAnimation->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_ANIMATION));
+ mCheckCallingCard->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_CALLINGCARD));
+ mCheckClothing->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_WEARABLE));
+ mCheckGesture->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_GESTURE));
+ mCheckLandmark->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_LANDMARK));
+ mCheckMaterial->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_MATERIAL));
+ mCheckNotecard->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_NOTECARD));
+ mCheckObject->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_OBJECT));
+ mCheckScript->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_LSL));
+ mCheckSounds->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_SOUND));
+ mCheckTexture->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_TEXTURE));
+ mCheckSnapshot->setValue((S32) (filter_types & 0x1 << LLInventoryType::IT_SNAPSHOT));
+ mCheckSettings->setValue((S32)(filter_types & 0x1 << LLInventoryType::IT_SETTINGS));
+ mCheckShowEmpty->setValue(show_folders == LLInventoryFilter::SHOW_ALL_FOLDERS);
+
+ mCreatorSelf->setValue(show_created_by_me);
+ mCreatorOthers->setValue(show_created_by_others);
+
+ mCheckSinceLogoff->setValue(mFilter->isSinceLogoff());
mSpinSinceHours->set((F32)(hours % 24));
mSpinSinceDays->set((F32)(hours / 24));
- getChild<LLRadioGroup>("date_search_direction")->setSelectedIndex(date_search_direction);
+ mRadioDateSearchDirection->setSelectedIndex(date_search_direction);
}
void LLFloaterInventoryFinder::draw()
@@ -1253,80 +1286,80 @@ void LLFloaterInventoryFinder::draw()
U64 filter = 0xffffffffffffffffULL;
bool filtered_by_all_types = true;
- if (!getChild<LLUICtrl>("check_animation")->getValue())
+ if (!mCheckAnimation->getValue())
{
filter &= ~(0x1 << LLInventoryType::IT_ANIMATION);
filtered_by_all_types = false;
}
- if (!getChild<LLUICtrl>("check_calling_card")->getValue())
+ if (!mCheckCallingCard->getValue())
{
filter &= ~(0x1 << LLInventoryType::IT_CALLINGCARD);
filtered_by_all_types = false;
}
- if (!getChild<LLUICtrl>("check_clothing")->getValue())
+ if (!mCheckClothing->getValue())
{
filter &= ~(0x1 << LLInventoryType::IT_WEARABLE);
filtered_by_all_types = false;
}
- if (!getChild<LLUICtrl>("check_gesture")->getValue())
+ if (!mCheckGesture->getValue())
{
filter &= ~(0x1 << LLInventoryType::IT_GESTURE);
filtered_by_all_types = false;
}
- if (!getChild<LLUICtrl>("check_landmark")->getValue())
+ if (!mCheckLandmark->getValue())
{
filter &= ~(0x1 << LLInventoryType::IT_LANDMARK);
filtered_by_all_types = false;
}
- if (!getChild<LLUICtrl>("check_material")->getValue())
+ if (!mCheckMaterial->getValue())
{
filter &= ~(0x1 << LLInventoryType::IT_MATERIAL);
filtered_by_all_types = false;
}
- if (!getChild<LLUICtrl>("check_notecard")->getValue())
+ if (!mCheckNotecard->getValue())
{
filter &= ~(0x1 << LLInventoryType::IT_NOTECARD);
filtered_by_all_types = false;
}
- if (!getChild<LLUICtrl>("check_object")->getValue())
+ if (!mCheckObject->getValue())
{
filter &= ~(0x1 << LLInventoryType::IT_OBJECT);
filter &= ~(0x1 << LLInventoryType::IT_ATTACHMENT);
filtered_by_all_types = false;
}
- if (!getChild<LLUICtrl>("check_script")->getValue())
+ if (!mCheckScript->getValue())
{
filter &= ~(0x1 << LLInventoryType::IT_LSL);
filtered_by_all_types = false;
}
- if (!getChild<LLUICtrl>("check_sound")->getValue())
+ if (!mCheckSounds->getValue())
{
filter &= ~(0x1 << LLInventoryType::IT_SOUND);
filtered_by_all_types = false;
}
- if (!getChild<LLUICtrl>("check_texture")->getValue())
+ if (!mCheckTexture->getValue())
{
filter &= ~(0x1 << LLInventoryType::IT_TEXTURE);
filtered_by_all_types = false;
}
- if (!getChild<LLUICtrl>("check_snapshot")->getValue())
+ if (!mCheckSnapshot->getValue())
{
filter &= ~(0x1 << LLInventoryType::IT_SNAPSHOT);
filtered_by_all_types = false;
}
- if (!getChild<LLUICtrl>("check_settings")->getValue())
+ if (!mCheckSettings->getValue())
{
filter &= ~(0x1 << LLInventoryType::IT_SETTINGS);
filtered_by_all_types = false;
@@ -1444,65 +1477,56 @@ void LLFloaterInventoryFinder::onCreatorOtherFilterCommit()
bool LLFloaterInventoryFinder::getCheckShowEmpty()
{
- return getChild<LLUICtrl>("check_show_empty")->getValue();
+ return mCheckShowEmpty->getValue();
}
bool LLFloaterInventoryFinder::getCheckSinceLogoff()
{
- return getChild<LLUICtrl>("check_since_logoff")->getValue();
+ return mCheckSinceLogoff->getValue();
}
U32 LLFloaterInventoryFinder::getDateSearchDirection()
{
- return getChild<LLRadioGroup>("date_search_direction")->getSelectedIndex();
+ return mRadioDateSearchDirection->getSelectedIndex();
}
-void LLFloaterInventoryFinder::onCloseBtn(void* user_data)
+void LLFloaterInventoryFinder::onCloseBtn()
{
- LLFloaterInventoryFinder* finderp = (LLFloaterInventoryFinder*)user_data;
- finderp->closeFloater();
+ closeFloater();
}
-// static
-void LLFloaterInventoryFinder::selectAllTypes(void* user_data)
-{
- LLFloaterInventoryFinder* self = (LLFloaterInventoryFinder*)user_data;
- if(!self) return;
-
- self->getChild<LLUICtrl>("check_animation")->setValue(true);
- self->getChild<LLUICtrl>("check_calling_card")->setValue(true);
- self->getChild<LLUICtrl>("check_clothing")->setValue(true);
- self->getChild<LLUICtrl>("check_gesture")->setValue(true);
- self->getChild<LLUICtrl>("check_landmark")->setValue(true);
- self->getChild<LLUICtrl>("check_material")->setValue(true);
- self->getChild<LLUICtrl>("check_notecard")->setValue(true);
- self->getChild<LLUICtrl>("check_object")->setValue(true);
- self->getChild<LLUICtrl>("check_script")->setValue(true);
- self->getChild<LLUICtrl>("check_sound")->setValue(true);
- self->getChild<LLUICtrl>("check_texture")->setValue(true);
- self->getChild<LLUICtrl>("check_snapshot")->setValue(true);
- self->getChild<LLUICtrl>("check_settings")->setValue(true);
+void LLFloaterInventoryFinder::selectAllTypes()
+{
+ mCheckAnimation->setValue(true);
+ mCheckCallingCard->setValue(true);
+ mCheckClothing->setValue(true);
+ mCheckGesture->setValue(true);
+ mCheckLandmark->setValue(true);
+ mCheckMaterial->setValue(true);
+ mCheckNotecard->setValue(true);
+ mCheckObject->setValue(true);
+ mCheckScript->setValue(true);
+ mCheckSounds->setValue(true);
+ mCheckTexture->setValue(true);
+ mCheckSnapshot->setValue(true);
+ mCheckSettings->setValue(true);
}
-//static
-void LLFloaterInventoryFinder::selectNoTypes(void* user_data)
-{
- LLFloaterInventoryFinder* self = (LLFloaterInventoryFinder*)user_data;
- if(!self) return;
-
- self->getChild<LLUICtrl>("check_animation")->setValue(false);
- self->getChild<LLUICtrl>("check_calling_card")->setValue(false);
- self->getChild<LLUICtrl>("check_clothing")->setValue(false);
- self->getChild<LLUICtrl>("check_gesture")->setValue(false);
- self->getChild<LLUICtrl>("check_landmark")->setValue(false);
- self->getChild<LLUICtrl>("check_material")->setValue(false);
- self->getChild<LLUICtrl>("check_notecard")->setValue(false);
- self->getChild<LLUICtrl>("check_object")->setValue(false);
- self->getChild<LLUICtrl>("check_script")->setValue(false);
- self->getChild<LLUICtrl>("check_sound")->setValue(false);
- self->getChild<LLUICtrl>("check_texture")->setValue(false);
- self->getChild<LLUICtrl>("check_snapshot")->setValue(false);
- self->getChild<LLUICtrl>("check_settings")->setValue(false);
+void LLFloaterInventoryFinder::selectNoTypes()
+{
+ mCheckAnimation->setValue(false);
+ mCheckCallingCard->setValue(false);
+ mCheckClothing->setValue(false);
+ mCheckGesture->setValue(false);
+ mCheckLandmark->setValue(false);
+ mCheckMaterial->setValue(false);
+ mCheckNotecard->setValue(false);
+ mCheckObject->setValue(false);
+ mCheckScript->setValue(false);
+ mCheckSounds->setValue(false);
+ mCheckTexture->setValue(false);
+ mCheckSnapshot->setValue(false);
+ mCheckSettings->setValue(false);
}
//////////////////////////////////////////////////////////////////////////////////
diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp
index 0ce1f0f9d3..0ce9ef7a7a 100644
--- a/indra/newview/llpanelpermissions.cpp
+++ b/indra/newview/llpanelpermissions.cpp
@@ -1175,6 +1175,7 @@ void LLPanelPermissions::onCommitName(LLUICtrl*, void* data)
{
LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
new_item->rename(tb->getText());
+ new_item->setComplete(true); // to not err at updateServer
new_item->updateServer(false);
gInventory.updateItem(new_item);
gInventory.notifyObservers();
diff --git a/indra/newview/llpanelprofilepicks.cpp b/indra/newview/llpanelprofilepicks.cpp
index a87ef4f0f9..c9626bf9ea 100644
--- a/indra/newview/llpanelprofilepicks.cpp
+++ b/indra/newview/llpanelprofilepicks.cpp
@@ -248,6 +248,8 @@ void LLPanelProfilePicks::onClickNewBtn()
select_tab(true).
label(pick_panel->getPickName()));
updateButtons();
+
+ pick_panel->addLocationChangedCallbacks();
}
void LLPanelProfilePicks::onClickDelete()
@@ -607,10 +609,12 @@ void LLPanelProfilePick::setAvatarId(const LLUUID& avatar_id)
{
mPickName->setEnabled(true);
mPickDescription->setEnabled(true);
+ mSetCurrentLocationButton->setVisible(true);
}
else
{
mSnapshotCtrl->setEnabled(false);
+ mSetCurrentLocationButton->setVisible(false);
}
}
@@ -621,6 +625,7 @@ bool LLPanelProfilePick::postBuild()
mSaveButton = getChild<LLButton>("save_changes_btn");
mCreateButton = getChild<LLButton>("create_changes_btn");
mCancelButton = getChild<LLButton>("cancel_changes_btn");
+ mSetCurrentLocationButton = getChild<LLButton>("set_to_curr_location_btn");
mSnapshotCtrl = getChild<LLTextureCtrl>("pick_snapshot");
mSnapshotCtrl->setCommitCallback(boost::bind(&LLPanelProfilePick::onSnapshotChanged, this));
@@ -633,6 +638,7 @@ bool LLPanelProfilePick::postBuild()
mSaveButton->setCommitCallback(boost::bind(&LLPanelProfilePick::onClickSave, this));
mCreateButton->setCommitCallback(boost::bind(&LLPanelProfilePick::onClickSave, this));
mCancelButton->setCommitCallback(boost::bind(&LLPanelProfilePick::onClickCancel, this));
+ mSetCurrentLocationButton->setCommitCallback(boost::bind(&LLPanelProfilePick::onClickSetLocation, this));
mPickName->setKeystrokeCallback(boost::bind(&LLPanelProfilePick::onPickChanged, this, _1), NULL);
mPickName->setEnabled(false);
@@ -811,6 +817,32 @@ bool LLPanelProfilePick::isDirty() const
return false;
}
+void LLPanelProfilePick::onClickSetLocation()
+{
+ // Save location for later use.
+ setPosGlobal(gAgent.getPositionGlobal());
+
+ std::string parcel_name, region_name;
+
+ LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
+ if (parcel)
+ {
+ mParcelId = parcel->getID();
+ parcel_name = parcel->getName();
+ }
+
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ region_name = region->getName();
+ }
+
+ setPickLocation(createLocationText(getLocationNotice(), parcel_name, region_name, getPosGlobal()));
+
+ mLocationChanged = true;
+ enableSaveButton(true);
+}
+
void LLPanelProfilePick::onClickSave()
{
if (mRegionCallbackConnection.connected())
@@ -821,6 +853,10 @@ void LLPanelProfilePick::onClickSave()
{
mParcelCallbackConnection.disconnect();
}
+ if (mLocationChanged)
+ {
+ onClickSetLocation();
+ }
sendUpdate();
mLocationChanged = false;
@@ -871,6 +907,12 @@ void LLPanelProfilePick::processParcelInfo(const LLParcelData& parcel_data)
}
}
+void LLPanelProfilePick::addLocationChangedCallbacks()
+{
+ mRegionCallbackConnection = gAgent.addRegionChangedCallback([this]() { onClickSetLocation(); });
+ mParcelCallbackConnection = gAgent.addParcelChangedCallback([this]() { onClickSetLocation(); });
+}
+
void LLPanelProfilePick::setParcelID(const LLUUID& parcel_id)
{
if (mParcelId != parcel_id)
diff --git a/indra/newview/llpanelprofilepicks.h b/indra/newview/llpanelprofilepicks.h
index b4d3eb010e..847ac57cea 100644
--- a/indra/newview/llpanelprofilepicks.h
+++ b/indra/newview/llpanelprofilepicks.h
@@ -141,6 +141,8 @@ public:
LLUUID getParcelID() const { return mParcelId; }
void setErrorStatus(S32 status, const std::string& reason) override {};
+ void addLocationChangedCallbacks();
+
protected:
/**
@@ -203,6 +205,11 @@ public:
void resetDirty() override;
/**
+ * Callback for "Set Location" button click
+ */
+ void onClickSetLocation();
+
+ /**
* Callback for "Save" and "Create" button click
*/
void onClickSave();
@@ -224,6 +231,7 @@ protected:
LLTextureCtrl* mSnapshotCtrl;
LLLineEditor* mPickName;
LLTextEditor* mPickDescription;
+ LLButton* mSetCurrentLocationButton;
LLButton* mSaveButton;
LLButton* mCreateButton;
LLButton* mCancelButton;
@@ -241,7 +249,7 @@ protected:
bool mLocationChanged;
bool mNewPick;
- bool mIsEditing;
+ bool mIsEditing;
void onDescriptionFocusReceived();
};
diff --git a/indra/newview/llreflectionmap.cpp b/indra/newview/llreflectionmap.cpp
index 910509928d..7f5076bd56 100644
--- a/indra/newview/llreflectionmap.cpp
+++ b/indra/newview/llreflectionmap.cpp
@@ -177,7 +177,7 @@ void LLReflectionMap::autoAdjustOrigin()
mPriority = 1;
mOrigin.load3(mViewerObject->getPositionAgent().mV);
- if (mViewerObject->getVolume() && ((LLVOVolume*)mViewerObject)->getReflectionProbeIsBox())
+ if (mViewerObject->getVolume() && ((LLVOVolume*)mViewerObject.get())->getReflectionProbeIsBox())
{
LLVector3 s = mViewerObject->getScale().scaledVec(LLVector3(0.5f, 0.5f, 0.5f));
mRadius = s.magVec();
diff --git a/indra/newview/llreflectionmap.h b/indra/newview/llreflectionmap.h
index d20bba7059..a818793550 100644
--- a/indra/newview/llreflectionmap.h
+++ b/indra/newview/llreflectionmap.h
@@ -124,7 +124,7 @@ public:
LLSpatialGroup* mGroup = nullptr;
// viewer object this probe is tracking (if any)
- LLViewerObject* mViewerObject = nullptr;
+ LLPointer<LLViewerObject> mViewerObject = nullptr;
// what priority should this probe have (higher is higher priority)
// currently only 0 or 1
diff --git a/indra/newview/llreflectionmapmanager.cpp b/indra/newview/llreflectionmapmanager.cpp
index fae6acec3c..c1815ad57e 100644
--- a/indra/newview/llreflectionmapmanager.cpp
+++ b/indra/newview/llreflectionmapmanager.cpp
@@ -1156,7 +1156,7 @@ void LLReflectionMapManager::updateUniforms()
{
if (refmap->mViewerObject && refmap->mViewerObject->getVolume())
{ // have active manual probes live-track the object they're associated with
- LLVOVolume* vobj = (LLVOVolume*)refmap->mViewerObject;
+ LLVOVolume* vobj = (LLVOVolume*)refmap->mViewerObject.get();
refmap->mOrigin.load3(vobj->getPositionAgent().mV);
diff --git a/indra/newview/llskinningutil.cpp b/indra/newview/llskinningutil.cpp
index cee43f3cff..47f58afa00 100644
--- a/indra/newview/llskinningutil.cpp
+++ b/indra/newview/llskinningutil.cpp
@@ -135,6 +135,12 @@ void LLSkinningUtil::initSkinningMatrixPalette(
initJointNums(const_cast<LLMeshSkinInfo*>(skin), avatar);
+ if (skin->mInvBindMatrix.size() < count )
+ {
+ // faulty model? mInvBindMatrix.size() should have matched mJointNames.size()
+ return;
+ }
+
LLMatrix4a world[LL_CHARACTER_MAX_ANIMATED_JOINTS];
for (S32 j = 0; j < count; ++j)
@@ -354,7 +360,8 @@ void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *a
{
rig_info_tab[joint_num].setIsRiggedTo(true);
- const LLMatrix4a& mat = skin->mBindPoseMatrix[joint_index];
+ size_t bind_poses_size = skin->mBindPoseMatrix.size();
+ const LLMatrix4a& mat = bind_poses_size > joint_index ? skin->mBindPoseMatrix[joint_index] : LLMatrix4a::identity();
LLVector4a pos_joint_space;
mat.affineTransform(pos, pos_joint_space);
diff --git a/indra/newview/llsky.h b/indra/newview/llsky.h
index 32599dcee2..d06f181357 100644
--- a/indra/newview/llsky.h
+++ b/indra/newview/llsky.h
@@ -28,7 +28,6 @@
#define LL_LLSKY_H
#include "llmath.h"
-//#include "vmath.h"
#include "v3math.h"
#include "v4math.h"
#include "v4color.h"
diff --git a/indra/newview/llsprite.h b/indra/newview/llsprite.h
index 44439bd30c..d6e8e37ec9 100644
--- a/indra/newview/llsprite.h
+++ b/indra/newview/llsprite.h
@@ -27,8 +27,6 @@
#ifndef LL_LLSPRITE_H
#define LL_LLSPRITE_H
-////#include "vmath.h"
-//#include "llmath.h"
#include "v3math.h"
#include "v4math.h"
#include "v4color.h"
diff --git a/indra/newview/llsurface.h b/indra/newview/llsurface.h
index 10a104730b..fc72ab7db7 100644
--- a/indra/newview/llsurface.h
+++ b/indra/newview/llsurface.h
@@ -27,7 +27,6 @@
#ifndef LL_LLSURFACE_H
#define LL_LLSURFACE_H
-//#include "vmath.h"
#include "v3math.h"
#include "v3dmath.h"
#include "v4math.h"
diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp
index 8cdc2e94f4..618955c83b 100644
--- a/indra/newview/lltoolpie.cpp
+++ b/indra/newview/lltoolpie.cpp
@@ -72,6 +72,7 @@
#include "llweb.h"
#include "pipeline.h" // setHighlightObject
#include "lluiusage.h"
+#include "llcallingcard.h"
extern bool gDebugClicks;
@@ -1501,6 +1502,134 @@ static void handle_click_action_play()
}
}
+bool LLToolPie::shouldAllowFirstMediaInteraction(const LLPickInfo& pick, bool moap_flag)
+{
+ // Early failure cases
+ if(!pick.getObject())
+ {
+ LL_WARNS() << "pick.getObject() is NULL" << LL_ENDL;
+ return false;
+ }
+
+ static LLCachedControl<S32> FirstClickPref(gSavedSettings, "MediaFirstClickInteract", 1);
+
+ // Special / early-exit cases first, then checks get more complex and needy as we go down
+ // Feature disabled
+ if(FirstClickPref == MEDIA_FIRST_CLICK_NONE)
+ {
+ LL_DEBUGS_ONCE() << "FirstClickPref == MEDIA_FIRST_CLICK_NONE" << LL_ENDL;
+ return false;
+ }
+ // Every check beyond this point requires PRIM_MEDIA_FIRST_CLICK_INTERACT to be TRUE
+ if(!moap_flag && !(FirstClickPref & MEDIA_FIRST_CLICK_BYPASS_MOAP_FLAG))
+ {
+ LL_DEBUGS_ONCE() << "PRIM_MEDIA_FIRST_CLICK_INTERACT not set" << LL_ENDL;
+ return false;
+ }
+ // Any object with PRIM_MEDIA_FIRST_CLICK_INTERACT set to TRUE
+ if((FirstClickPref & MEDIA_FIRST_CLICK_ANY) == MEDIA_FIRST_CLICK_ANY)
+ {
+ LL_DEBUGS_ONCE() << "FirstClickPref & MEDIA_FIRST_CLICK_ANY" << LL_ENDL;
+ return true;
+ }
+
+ // The following checks require some object information so we obtain that
+ LLPointer<LLViewerObject> object = pick.getObject();
+ if(object.isNull())
+ {
+ LL_WARNS() << "pick.getObject() is NULL" << LL_ENDL;
+ return false;
+ }
+
+ // Own objects
+ if((FirstClickPref & MEDIA_FIRST_CLICK_OWN) && object->permYouOwner())
+ {
+ LL_DEBUGS_ONCE() << "FirstClickPref & MEDIA_FIRST_CLICK_OWN" << LL_ENDL;
+ return true;
+ }
+ // HUD attachments
+ if((FirstClickPref & MEDIA_FIRST_CLICK_HUD) && object->isHUDAttachment())
+ {
+ LL_DEBUGS_ONCE() << "FirstClickPref & MEDIA_FIRST_CLICK_HUD" << LL_ENDL;
+ return true;
+ }
+
+ // Further object detail required beyond this point
+ LLPermissions* perms = LLSelectMgr::getInstance()->getHoverNode()->mPermissions;
+ if(perms == nullptr)
+ {
+ LL_WARNS() << "LLSelectMgr::getInstance()->getHoverNode()->mPermissions is NULL" << LL_ENDL;
+ return false;
+ }
+ LLUUID owner_id = perms->getOwner();
+ LLUUID group_id = perms->getGroup();
+ if(owner_id.isNull() && group_id.isNull())
+ {
+ LL_WARNS() << "Owner information was not reliably obtained" << LL_ENDL;
+ return false;
+ }
+
+ // Check if the object is owned by a friend of the agent
+ if(FirstClickPref & MEDIA_FIRST_CLICK_FRIEND)
+ {
+ LL_DEBUGS_ONCE() << "FirstClickPref & MEDIA_FIRST_CLICK_FRIEND. id: " << owner_id << LL_ENDL;
+ return LLAvatarTracker::instance().isBuddy(owner_id);
+ }
+
+ // Check for objects set to or owned by the active group
+ if(FirstClickPref & MEDIA_FIRST_CLICK_GROUP)
+ {
+ // Get our active group
+ LLUUID active_group = gAgent.getGroupID();
+ if(active_group.notNull() && (active_group == group_id || active_group == owner_id))
+ {
+ LL_DEBUGS_ONCE() << "FirstClickPref & MEDIA_FIRST_CLICK_GROUP.Active group: " << active_group << ", group_id:" << group_id << ", owner_id: " << owner_id << LL_ENDL;
+ return true;
+ }
+ }
+
+ // This check ensures that the following conditions are met:
+ // 1. The object is located in the same parcel as the agent.
+ // 2. One of the following is true:
+ // a. The object is owned by the same group as the parcel.
+ // b. The object is set to the same group as the parcel.
+ // c. The object is owned by the same owner as the parcel.
+ // Conditions 2a and 2b are mutually exclusive, our check is the same for both.
+ if(FirstClickPref & MEDIA_FIRST_CLICK_LAND)
+ {
+ LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
+ if(parcel == nullptr)
+ {
+ LL_WARNS() << "LLViewerParcelMgr::getInstance()->getAgentParcel() is NULL" << LL_ENDL;
+ return false;
+ }
+
+ // Same parcel as the agent only
+ if(!LLViewerParcelMgr::getInstance()->inAgentParcel(object->getPositionGlobal()))
+ {
+ LL_WARNS_ONCE() << "Object is not in the same parcel as the agent" << LL_ENDL;
+ return false;
+ }
+
+ LLUUID parcel_owner = parcel->getOwnerID();
+ LLUUID parcel_group = parcel->getGroupID();
+
+ // The parcel owner and group can't both be null
+ if(parcel_owner.isNull() && parcel_group.isNull())
+ {
+ LL_WARNS() << "Parcel owner and group are both null" << LL_ENDL;
+ return false;
+ }
+
+ if(owner_id == parcel_owner || group_id == parcel_group)
+ {
+ LL_DEBUGS_ONCE() << "FirstClickPref & MEDIA_FIRST_CLICK_LAND. Parcel owner: " << parcel_owner << ", group_id:" << group_id << ", owner_id: " << owner_id << LL_ENDL;
+ return true;
+ }
+ }
+ return false;
+}
+
bool LLToolPie::handleMediaClick(const LLPickInfo& pick)
{
//FIXME: how do we handle object in different parcel than us?
@@ -1535,6 +1664,16 @@ bool LLToolPie::handleMediaClick(const LLPickInfo& pick)
{
// It's okay to give this a null impl
LLViewerMediaFocus::getInstance()->setFocusFace(pick.getObject(), pick.mObjectFace, media_impl, pick.mNormal);
+ if (shouldAllowFirstMediaInteraction(pick, mep->getFirstClickInteract()))
+ {
+ if (media_impl.notNull())
+ {
+ media_impl->mouseDown(pick.mUVCoords, gKeyboard->currentMask(true));
+ mMediaMouseCaptureID = mep->getMediaID();
+ setMouseCapture(true);
+ return true;
+ }
+ }
}
else
{
@@ -1647,7 +1786,7 @@ bool LLToolPie::handleMediaHover(const LLPickInfo& pick)
}
// If this is the focused media face, send mouse move events.
- if (LLViewerMediaFocus::getInstance()->isFocusedOnFace(objectp, pick.mObjectFace))
+ if (LLViewerMediaFocus::getInstance()->isFocusedOnFace(objectp, pick.mObjectFace) || (shouldAllowFirstMediaInteraction(pick, mep->getFirstClickInteract())))
{
media_impl->mouseMove(pick.mUVCoords, gKeyboard->currentMask(true));
gViewerWindow->setCursor(media_impl->getLastSetCursor());
diff --git a/indra/newview/lltoolpie.h b/indra/newview/lltoolpie.h
index b3884a6bfc..d9daad9515 100644
--- a/indra/newview/lltoolpie.h
+++ b/indra/newview/lltoolpie.h
@@ -89,6 +89,22 @@ private:
void showVisualContextMenuEffect();
ECursorType cursorFromObject(LLViewerObject* object);
+ enum MediaFirstClickTypes
+ {
+ MEDIA_FIRST_CLICK_NONE = 0, // Special case: Feature is disabled
+ MEDIA_FIRST_CLICK_HUD = 1 << 0, // 0b00000001 (1)
+ MEDIA_FIRST_CLICK_OWN = 1 << 1, // 0b00000010 (2)
+ MEDIA_FIRST_CLICK_FRIEND = 1 << 2, // 0b00000100 (4)
+ MEDIA_FIRST_CLICK_GROUP = 1 << 3, // 0b00001000 (8)
+ MEDIA_FIRST_CLICK_LAND = 1 << 4, // 0b00010000 (16)
+
+ // Covers any object with PRIM_MEDIA_FIRST_CLICK_INTERACT (combines all previous flags)
+ MEDIA_FIRST_CLICK_ANY = ~(3<<30), // 0b00111111111111111111111111111111
+
+ // Covers all media regardless of other rules or PRIM_MEDIA_FIRST_CLICK_INTERACT
+ MEDIA_FIRST_CLICK_BYPASS_MOAP_FLAG = 1 << 30 // 0b01000000000000000000000000000000 (1073741824)
+ };
+ bool shouldAllowFirstMediaInteraction(const LLPickInfo& info, bool moap_flag);
bool handleMediaClick(const LLPickInfo& info);
bool handleMediaDblClick(const LLPickInfo& info);
bool handleMediaHover(const LLPickInfo& info);
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index 10fa0fd3cd..6e790e4fca 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -100,13 +100,13 @@
extern LLPointer<LLViewerTexture> gStartTexture;
extern bool gShiftFrame;
-LLPointer<LLViewerTexture> gDisconnectedImagep = NULL;
+LLPointer<LLViewerTexture> gDisconnectedImagep = nullptr;
// used to toggle renderer back on after teleport
bool gTeleportDisplay = false;
LLFrameTimer gTeleportDisplayTimer;
LLFrameTimer gTeleportArrivalTimer;
-const F32 RESTORE_GL_TIME = 5.f; // Wait this long while reloading textures before we raise the curtain
+constexpr F32 RESTORE_GL_TIME = 5.f; // Wait this long while reloading textures before we raise the curtain
bool gForceRenderLandFence = false;
bool gDisplaySwapBuffers = false;
@@ -120,9 +120,9 @@ bool gSnapshotNoPost = false;
bool gShaderProfileFrame = false;
// This is how long the sim will try to teleport you before giving up.
-const F32 TELEPORT_EXPIRY = 15.0f;
+constexpr F32 TELEPORT_EXPIRY = 15.0f;
// Additional time (in seconds) to wait per attachment
-const F32 TELEPORT_EXPIRY_PER_ATTACHMENT = 3.f;
+constexpr F32 TELEPORT_EXPIRY_PER_ATTACHMENT = 3.f;
U32 gRecentFrameCount = 0; // number of 'recent' frames
LLFrameTimer gRecentFPSTime;
@@ -130,8 +130,6 @@ LLFrameTimer gRecentMemoryTime;
LLFrameTimer gAssetStorageLogTime;
// Rendering stuff
-void pre_show_depth_buffer();
-void post_show_depth_buffer();
void render_ui(F32 zoom_factor = 1.f, int subfield = 0);
void swap();
void render_hud_attachments();
@@ -212,7 +210,8 @@ void display_update_camera()
F32 final_far = gAgentCamera.mDrawDistance;
if (gCubeSnapshot)
{
- final_far = gSavedSettings.getF32("RenderReflectionProbeDrawDistance");
+ static LLCachedControl<F32> reflection_probe_draw_distance(gSavedSettings, "RenderReflectionProbeDrawDistance", 64.f);
+ final_far = reflection_probe_draw_distance();
}
else if (CAMERA_MODE_CUSTOMIZE_AVATAR == gAgentCamera.getCameraMode())
{
@@ -237,7 +236,7 @@ void display_update_camera()
void display_stats()
{
LL_PROFILE_ZONE_SCOPED;
- const F32 FPS_LOG_FREQUENCY = 10.f;
+ constexpr F32 FPS_LOG_FREQUENCY = 10.f;
if (gRecentFPSTime.getElapsedTimeF32() >= FPS_LOG_FREQUENCY)
{
LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("DS - FPS");
@@ -246,7 +245,7 @@ void display_stats()
gRecentFrameCount = 0;
gRecentFPSTime.reset();
}
- F32 mem_log_freq = gSavedSettings.getF32("MemoryLogFrequency");
+ static LLCachedControl<F32> mem_log_freq(gSavedSettings, "MemoryLogFrequency", 600.f);
if (mem_log_freq > 0.f && gRecentMemoryTime.getElapsedTimeF32() >= mem_log_freq)
{
LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("DS - Memory");
@@ -256,7 +255,7 @@ void display_stats()
LLMemory::logMemoryInfo(true) ;
gRecentMemoryTime.reset();
}
- const F32 ASSET_STORAGE_LOG_FREQUENCY = 60.f;
+ constexpr F32 ASSET_STORAGE_LOG_FREQUENCY = 60.f;
if (gAssetStorageLogTime.getElapsedTimeF32() >= ASSET_STORAGE_LOG_FREQUENCY)
{
LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("DS - Asset Storage");
@@ -572,8 +571,10 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot)
LLImageGL::updateStats(gFrameTimeSeconds);
- LLVOAvatar::sRenderName = gSavedSettings.getS32("AvatarNameTagMode");
- LLVOAvatar::sRenderGroupTitles = (gSavedSettings.getBOOL("NameTagShowGroupTitles") && gSavedSettings.getS32("AvatarNameTagMode"));
+ static LLCachedControl<S32> avatar_name_tag_mode(gSavedSettings, "AvatarNameTagMode", 1);
+ static LLCachedControl<bool> name_tag_show_group_titles(gSavedSettings, "NameTagShowGroupTitles", true);
+ LLVOAvatar::sRenderName = avatar_name_tag_mode;
+ LLVOAvatar::sRenderGroupTitles = name_tag_show_group_titles && avatar_name_tag_mode > 0;
gPipeline.mBackfaceCull = true;
gFrameCount++;
@@ -796,7 +797,7 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot)
}
gGL.setColorMask(true, true);
- glClearColor(0,0,0,0);
+ glClearColor(0.f, 0.f, 0.f, 0.f);
LLGLState::checkStates();
@@ -964,7 +965,7 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot)
gPipeline.mRT->deferredScreen.bindTarget();
if (gUseWireframe)
{
- F32 g = 0.5f;
+ constexpr F32 g = 0.5f;
glClearColor(g, g, g, 1.f);
}
else
@@ -983,11 +984,12 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot)
LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("display - 5")
LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_WORLD;
- if (gSavedSettings.getBOOL("RenderDepthPrePass"))
+ static LLCachedControl<bool> render_depth_pre_pass(gSavedSettings, "RenderDepthPrePass", false);
+ if (render_depth_pre_pass)
{
gGL.setColorMask(false, false);
- static const U32 types[] = {
+ constexpr U32 types[] = {
LLRenderPass::PASS_SIMPLE,
LLRenderPass::PASS_FULLBRIGHT,
LLRenderPass::PASS_SHINY
@@ -1201,7 +1203,7 @@ void display_cube_face()
gGL.setColorMask(true, true);
- glClearColor(0, 0, 0, 0);
+ glClearColor(0.f, 0.f, 0.f, 0.f);
gPipeline.generateSunShadow(*LLViewerCamera::getInstance());
glClear(GL_DEPTH_BUFFER_BIT); // | GL_STENCIL_BUFFER_BIT);
@@ -1237,7 +1239,7 @@ void display_cube_face()
}
else
{
- glClearColor(1, 0, 1, 1);
+ glClearColor(1.f, 0.f, 1.f, 1.f);
}
gPipeline.mRT->deferredScreen.clear();
@@ -1278,11 +1280,12 @@ void render_hud_attachments()
{
LLPipeline::sRenderingHUDs = true;
LLCamera hud_cam = *LLViewerCamera::getInstance();
- hud_cam.setOrigin(-1.f,0,0);
- hud_cam.setAxes(LLVector3(1,0,0), LLVector3(0,1,0), LLVector3(0,0,1));
+ hud_cam.setOrigin(-1.f, 0.f, 0.f);
+ hud_cam.setAxes(LLVector3(1.f, 0.f, 0.f), LLVector3(0.f, 1.f, 0.f), LLVector3(0.f, 0.f, 1.f));
LLViewerCamera::updateFrustumPlanes(hud_cam, true);
- bool render_particles = gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES) && gSavedSettings.getBOOL("RenderHUDParticles");
+ static LLCachedControl<bool> render_hud_particles(gSavedSettings, "RenderHUDParticles", false);
+ bool render_particles = gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES) && render_hud_particles;
//only render hud objects
gPipeline.pushRenderTypeMask();
@@ -1642,10 +1645,11 @@ void render_ui_3d()
stop_glerror();
gUIProgram.bind();
- gGL.color4f(1, 1, 1, 1);
+ gGL.color4f(1.f, 1.f, 1.f, 1.f);
// Coordinate axes
- if (gSavedSettings.getBOOL("ShowAxes"))
+ static LLCachedControl<bool> show_axes(gSavedSettings, "ShowAxes");
+ if (show_axes())
{
draw_axes();
}
@@ -1707,7 +1711,7 @@ void render_ui_2d()
gGL.pushMatrix();
S32 half_width = (gViewerWindow->getWorldViewWidthScaled() / 2);
S32 half_height = (gViewerWindow->getWorldViewHeightScaled() / 2);
- gGL.scalef(LLUI::getScaleFactor().mV[0], LLUI::getScaleFactor().mV[1], 1.f);
+ gGL.scalef(LLUI::getScaleFactor().mV[VX], LLUI::getScaleFactor().mV[VY], 1.f);
gGL.translatef((F32)half_width, (F32)half_height, 0.f);
F32 zoom = gAgentCamera.mHUDCurZoom;
gGL.scalef(zoom,zoom,1.f);
@@ -1729,7 +1733,7 @@ void render_ui_2d()
gPipeline.mUIScreen.bindTarget();
gGL.setColorMask(true, true);
{
- static const S32 pad = 8;
+ constexpr S32 pad = 8;
LLView::sDirtyRect.mLeft -= pad;
LLView::sDirtyRect.mRight += pad;
@@ -1782,8 +1786,6 @@ void render_ui_2d()
gViewerWindow->draw();
}
-
-
// reset current origin for font rendering, in case of tiling render
LLFontGL::sCurOrigin.set(0, 0);
}
@@ -1792,7 +1794,7 @@ void render_disconnected_background()
{
gUIProgram.bind();
- gGL.color4f(1,1,1,1);
+ gGL.color4f(1.f, 1.f, 1.f, 1.f);
if (!gDisconnectedImagep && gDisconnected)
{
LL_INFOS() << "Loading last bitmap..." << LL_ENDL;
@@ -1832,7 +1834,7 @@ void render_disconnected_background()
raw->expandToPowerOfTwo();
- gDisconnectedImagep = LLViewerTextureManager::getLocalTexture(raw.get(), false );
+ gDisconnectedImagep = LLViewerTextureManager::getLocalTexture(raw.get(), false);
gStartTexture = gDisconnectedImagep;
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
}
@@ -1867,6 +1869,5 @@ void render_disconnected_background()
void display_cleanup()
{
- gDisconnectedImagep = NULL;
+ gDisconnectedImagep = nullptr;
}
-
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 4383fc87e5..73fda339b5 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -295,6 +295,7 @@ void force_error_coroutine_crash();
void force_error_coroprocedure_crash();
void force_error_work_queue_crash();
void force_error_thread_crash();
+void force_exception_thread_crash();
void handle_force_delete();
void print_object_info();
@@ -2676,6 +2677,15 @@ class LLAdvancedForceErrorThreadCrash : public view_listener_t
}
};
+class LLAdvancedForceExceptionThreadCrash : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ force_exception_thread_crash();
+ return true;
+ }
+};
+
class LLAdvancedForceErrorDisconnectViewer : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
@@ -8814,6 +8824,11 @@ void force_error_thread_crash()
LLAppViewer::instance()->forceErrorThreadCrash();
}
+void force_exception_thread_crash()
+{
+ LLAppViewer::instance()->forceExceptionThreadCrash();
+}
+
class LLToolsUseSelectionForGrid : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
@@ -10029,6 +10044,7 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedForceErrorCoroprocedureCrash(), "Advanced.ForceErrorCoroprocedureCrash");
view_listener_t::addMenu(new LLAdvancedForceErrorWorkQueueCrash(), "Advanced.ForceErrorWorkQueueCrash");
view_listener_t::addMenu(new LLAdvancedForceErrorThreadCrash(), "Advanced.ForceErrorThreadCrash");
+ view_listener_t::addMenu(new LLAdvancedForceExceptionThreadCrash(), "Advanced.ForceExceptionThreadCrash");
view_listener_t::addMenu(new LLAdvancedForceErrorDisconnectViewer(), "Advanced.ForceErrorDisconnectViewer");
// Advanced (toplevel)
diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp
index 9743ec0c59..84195997c3 100644
--- a/indra/newview/llviewermenufile.cpp
+++ b/indra/newview/llviewermenufile.cpp
@@ -94,7 +94,7 @@ class LLFileEnableUploadModel : public view_listener_t
bool handleEvent(const LLSD& userdata)
{
LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) LLFloaterReg::findInstance("upload_model");
- if (fmp && fmp->isModelLoading())
+ if (fmp && !fmp->isDead() && fmp->isModelLoading())
{
return false;
}
diff --git a/indra/newview/llviewerparceloverlay.cpp b/indra/newview/llviewerparceloverlay.cpp
index da03d3b015..e36ad0e722 100755
--- a/indra/newview/llviewerparceloverlay.cpp
+++ b/indra/newview/llviewerparceloverlay.cpp
@@ -33,6 +33,7 @@
#include "llfloaterreg.h"
#include "llgl.h"
#include "llrender.h"
+#include "lluicolor.h"
#include "v4color.h"
#include "v2math.h"
@@ -50,8 +51,8 @@
#include "pipeline.h"
-static const U8 OVERLAY_IMG_COMPONENTS = 4;
-static const F32 LINE_WIDTH = 0.0625f;
+static constexpr U8 OVERLAY_IMG_COMPONENTS = 4;
+static constexpr F32 LINE_WIDTH = 0.0625f;
bool LLViewerParcelOverlay::sColorSetInitialized = false;
LLUIColor LLViewerParcelOverlay::sAvailColor;
@@ -91,7 +92,7 @@ LLViewerParcelOverlay::LLViewerParcelOverlay(LLViewerRegion* region, F32 region_
// Initialize the GL texture with empty data.
//
// Create the base texture.
- U8 *raw = mImageRaw->getData();
+ U8* raw = mImageRaw->getData();
const S32 COUNT = mParcelGridsPerEdge * mParcelGridsPerEdge * OVERLAY_IMG_COMPONENTS;
for (S32 i = 0; i < COUNT; i++)
{
@@ -158,10 +159,10 @@ bool LLViewerParcelOverlay::encroachesOwned(const std::vector<LLBBox>& boxes) co
LLVector3 min = boxes[i].getMinAgent();
LLVector3 max = boxes[i].getMaxAgent();
- S32 left = S32(llclamp((min.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1));
- S32 right = S32(llclamp((max.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1));
- S32 top = S32(llclamp((min.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1));
- S32 bottom = S32(llclamp((max.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1));
+ S32 left = S32(llclamp((min.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f));
+ S32 right = S32(llclamp((max.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f));
+ S32 top = S32(llclamp((min.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f));
+ S32 bottom = S32(llclamp((max.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f));
for (S32 row = top; row <= bottom; row++)
{
@@ -186,10 +187,10 @@ bool LLViewerParcelOverlay::encroachesOnUnowned(const std::vector<LLBBox>& boxes
LLVector3 min = boxes[i].getMinAgent();
LLVector3 max = boxes[i].getMaxAgent();
- S32 left = S32(llclamp((min.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1));
- S32 right = S32(llclamp((max.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1));
- S32 top = S32(llclamp((min.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1));
- S32 bottom = S32(llclamp((max.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1));
+ S32 left = S32(llclamp((min.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f));
+ S32 right = S32(llclamp((max.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f));
+ S32 top = S32(llclamp((min.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f));
+ S32 bottom = S32(llclamp((max.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f));
for (S32 row = top; row <= bottom; row++)
{
@@ -223,10 +224,10 @@ bool LLViewerParcelOverlay::encroachesOnNearbyParcel(const std::vector<LLBBox>&
return true;
}
- S32 left = S32(llclamp((min.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1));
- S32 right = S32(llclamp((max.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1));
- S32 bottom = S32(llclamp((min.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1));
- S32 top = S32(llclamp((max.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1));
+ S32 left = S32(llclamp((min.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f));
+ S32 right = S32(llclamp((max.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f));
+ S32 bottom = S32(llclamp((min.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f));
+ S32 top = S32(llclamp((max.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1.f));
const S32 GRIDS_PER_EDGE = mParcelGridsPerEdge;
@@ -348,11 +349,11 @@ void LLViewerParcelOverlay::updateOverlayTexture()
const LLColor4U auction = sAuctionColor.get();
// Create the base texture.
- U8 *raw = mImageRaw->getData();
+ U8* raw = mImageRaw->getData();
const S32 COUNT = mParcelGridsPerEdge * mParcelGridsPerEdge;
S32 max = mOverlayTextureIdx + mParcelGridsPerEdge;
if (max > COUNT) max = COUNT;
- S32 pixel_index = mOverlayTextureIdx*OVERLAY_IMG_COMPONENTS;
+ S32 pixel_index = mOverlayTextureIdx * OVERLAY_IMG_COMPONENTS;
S32 i;
for (i = mOverlayTextureIdx; i < max; i++)
{
@@ -361,7 +362,7 @@ void LLViewerParcelOverlay::updateOverlayTexture()
U8 r,g,b,a;
// Color stored in low three bits
- switch( ownership & 0x7 )
+ switch (ownership & 0x7)
{
case PARCEL_PUBLIC:
r = avail.mV[VRED];
@@ -407,10 +408,10 @@ void LLViewerParcelOverlay::updateOverlayTexture()
break;
}
- raw[pixel_index + 0] = (U8)r;
- raw[pixel_index + 1] = (U8)g;
- raw[pixel_index + 2] = (U8)b;
- raw[pixel_index + 3] = (U8)a;
+ raw[pixel_index + VRED] = (U8)r;
+ raw[pixel_index + VGREEN] = (U8)g;
+ raw[pixel_index + VBLUE] = (U8)b;
+ raw[pixel_index + VALPHA] = (U8)a;
pixel_index += OVERLAY_IMG_COMPONENTS;
}
@@ -431,11 +432,10 @@ void LLViewerParcelOverlay::updateOverlayTexture()
}
}
-
-void LLViewerParcelOverlay::uncompressLandOverlay(S32 chunk, U8 *packed_overlay)
+void LLViewerParcelOverlay::uncompressLandOverlay(S32 chunk, U8* packed_overlay)
{
// Unpack the message data into the ownership array
- S32 size = mParcelGridsPerEdge * mParcelGridsPerEdge;
+ S32 size = mParcelGridsPerEdge * mParcelGridsPerEdge;
S32 chunk_size = size / PARCEL_OVERLAY_CHUNKS;
memcpy(mOwnership + chunk*chunk_size, packed_overlay, chunk_size); /*Flawfinder: ignore*/
@@ -460,7 +460,7 @@ void LLViewerParcelOverlay::updatePropertyLines()
mEdges.clear();
- const F32 GRID_STEP = PARCEL_GRID_STEP_METERS;
+ constexpr F32 GRID_STEP = PARCEL_GRID_STEP_METERS;
const S32 GRIDS_PER_EDGE = mParcelGridsPerEdge;
for (S32 row = 0; row < GRIDS_PER_EDGE; row++)
@@ -537,16 +537,16 @@ void LLViewerParcelOverlay::addPropertyLine(F32 start_x, F32 start_y, F32 dx, F3
auto split = [&](const LLVector3& start, F32 x, F32 y, F32 z, F32 part)
{
- F32 new_x = start.mV[0] + (x - start.mV[0]) * part;
- F32 new_y = start.mV[1] + (y - start.mV[1]) * part;
- F32 new_z = start.mV[2] + (z - start.mV[2]) * part;
+ F32 new_x = start.mV[VX] + (x - start.mV[VX]) * part;
+ F32 new_y = start.mV[VY] + (y - start.mV[VY]) * part;
+ F32 new_z = start.mV[VZ] + (z - start.mV[VZ]) * part;
edge.vertices.emplace_back(new_x, new_y, new_z);
};
auto checkForSplit = [&]()
{
const LLVector3& last_outside = edge.vertices.back();
- F32 z0 = last_outside.mV[2];
+ F32 z0 = last_outside.mV[VZ];
F32 z1 = outside_z;
if ((z0 >= water_z && z1 >= water_z) || (z0 < water_z && z1 < water_z))
return;
@@ -581,7 +581,7 @@ void LLViewerParcelOverlay::addPropertyLine(F32 start_x, F32 start_y, F32 dx, F3
outside_y += dy * (dy - LINE_WIDTH);
// Middle part, full width
- const S32 GRID_STEP = (S32)PARCEL_GRID_STEP_METERS;
+ constexpr S32 GRID_STEP = (S32)PARCEL_GRID_STEP_METERS;
for (S32 i = 1; i < GRID_STEP; i++)
{
inside_z = land.resolveHeightRegion( inside_x, inside_y );
@@ -711,7 +711,7 @@ void LLViewerParcelOverlay::renderPropertyLines()
bool render_hidden = LLSelectMgr::sRenderHiddenSelections && LLFloaterReg::instanceVisible("build");
- const F32 PROPERTY_LINE_CLIP_DIST_SQUARED = 256.f * 256.f;
+ constexpr F32 PROPERTY_LINE_CLIP_DIST_SQUARED = 256.f * 256.f;
for (const Edge& edge : mEdges)
{
@@ -744,7 +744,7 @@ void LLViewerParcelOverlay::renderPropertyLines()
else
{
LLVector3 visible = vertex;
- visible.mV[2] = water_z;
+ visible.mV[VZ] = water_z;
gGL.vertex3fv(visible.mV);
}
}
@@ -758,7 +758,7 @@ void LLViewerParcelOverlay::renderPropertyLines()
gGL.begin(LLRender::TRIANGLE_STRIP);
LLColor4U color = edge.color;
- color.mV[3] /= 4;
+ color.mV[VALPHA] /= 4;
gGL.color4ubv(color.mV);
for (const LLVector3& vertex : edge.vertices)
@@ -792,7 +792,7 @@ void grid_2d_part_lines(const F32 left, const F32 top, const F32 right, const F3
gGL.end();
}
-void LLViewerParcelOverlay::renderPropertyLinesOnMinimap(F32 scale_pixels_per_meter, const F32 *parcel_outline_color)
+void LLViewerParcelOverlay::renderPropertyLinesOnMinimap(F32 scale_pixels_per_meter, const F32* parcel_outline_color)
{
static LLCachedControl<bool> show(gSavedSettings, "MiniMapShowPropertyLines");
@@ -803,8 +803,8 @@ void LLViewerParcelOverlay::renderPropertyLinesOnMinimap(F32 scale_pixels_per_me
LLVector3 origin_agent = mRegion->getOriginAgent();
LLVector3 rel_region_pos = origin_agent - gAgentCamera.getCameraPositionAgent();
- F32 region_left = rel_region_pos.mV[0] * scale_pixels_per_meter;
- F32 region_bottom = rel_region_pos.mV[1] * scale_pixels_per_meter;
+ F32 region_left = rel_region_pos.mV[VX] * scale_pixels_per_meter;
+ F32 region_bottom = rel_region_pos.mV[VY] * scale_pixels_per_meter;
F32 map_parcel_width = PARCEL_GRID_STEP_METERS * scale_pixels_per_meter;
const S32 GRIDS_PER_EDGE = mParcelGridsPerEdge;
diff --git a/indra/newview/llviewerparceloverlay.h b/indra/newview/llviewerparceloverlay.h
index 03ae464cb8..50bef02ddf 100644
--- a/indra/newview/llviewerparceloverlay.h
+++ b/indra/newview/llviewerparceloverlay.h
@@ -34,12 +34,11 @@
#include "llframetimer.h"
#include "lluuid.h"
#include "llviewertexture.h"
-#include "llgl.h"
-#include "lluicolor.h"
class LLViewerRegion;
class LLVector3;
class LLColor4U;
+class LLUIColor;
class LLVector2;
class LLViewerParcelOverlay : public LLGLUpdate
@@ -65,19 +64,18 @@ public:
bool isSoundLocal(const LLVector3& pos) const;
- bool isBuildCameraAllowed(const LLVector3& pos) const;
F32 getOwnedRatio() const;
// Returns the number of vertices drawn
void renderPropertyLines();
void renderPropertyLinesOnMinimap(F32 scale_pixels_per_meter, const F32* parcel_outline_color);
- U8 ownership( const LLVector3& pos) const;
- U8 parcelLineFlags( const LLVector3& pos) const;
+ U8 ownership(const LLVector3& pos) const;
+ U8 parcelLineFlags(const LLVector3& pos) const;
U8 parcelLineFlags(S32 row, S32 col) const;
// MANIPULATE
- void uncompressLandOverlay(S32 chunk, U8 *compressed_overlay);
+ void uncompressLandOverlay(S32 chunk, U8* compressed_overlay);
// Indicate property lines and overlay texture need to be rebuilt.
void setDirty();
@@ -88,8 +86,7 @@ public:
private:
// This is in parcel rows and columns, not grid rows and columns
// Stored in bottom three bits.
- U8 ownership(S32 row, S32 col) const
- { return parcelFlags(row, col, (U8)0x7); }
+ U8 ownership(S32 row, S32 col) const { return parcelFlags(row, col, (U8)0x7); }
U8 parcelFlags(S32 row, S32 col, U8 flags) const;
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index b9f52e11aa..a085bc4d91 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -38,7 +38,6 @@
#include "llregionhandle.h"
#include "llsurface.h"
#include "message.h"
-//#include "vmath.h"
#include "v3math.h"
#include "v4math.h"
diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h
index 244e2b7835..ca994c6b06 100644
--- a/indra/newview/llviewerregion.h
+++ b/indra/newview/llviewerregion.h
@@ -43,6 +43,7 @@
#include "m4math.h" // LLMatrix4
#include "llframetimer.h"
#include "llreflectionmap.h"
+#include "llviewerobject.h"
#include "llpointer.h"
// Surface id's
diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp
index ad503cbac7..cf0cf2a6a9 100644
--- a/indra/newview/llviewerstats.cpp
+++ b/indra/newview/llviewerstats.cpp
@@ -220,7 +220,20 @@ SimMeasurement<F64Megabytes > SIM_PHYSICS_MEM("physicsmemoryallocated", "", LL
LLTrace::SampleStatHandle<F64Milliseconds > FRAMETIME_JITTER("frametimejitter", "Average delta between successive frame times"),
FRAMETIME("frametime", "Measured frame time"),
- SIM_PING("simpingstat");
+ SIM_PING("simpingstat"),
+ FRAMETIME_JITTER_99TH("frametimejitter99", "99th percentile of frametime jitter over the last 5 seconds."),
+ FRAMETIME_JITTER_95TH("frametimejitter95", "99th percentile of frametime jitter over the last 5 seconds."),
+ FRAMETIME_99TH("frametime99", "99th percentile of frametime over the last 5 seconds."),
+ FRAMETIME_95TH("frametime95", "99th percentile of frametime over the last 5 seconds."),
+ FRAMETIME_JITTER_CUMULATIVE("frametimejitcumulative", "Cumulative frametime jitter over the session."),
+ FRAMETIME_JITTER_STDDEV("frametimejitterstddev", "Standard deviation of frametime jitter in a 5 second period."),
+ FRAMETIME_STDDEV("frametimestddev", "Standard deviation of frametime in a 5 second period.");
+
+LLTrace::SampleStatHandle<U32> FRAMETIME_JITTER_EVENTS("frametimeevents", "Number of frametime events in the session. Applies when jitter exceeds 10% of the previous frame."),
+ FRAMETIME_JITTER_EVENTS_PER_MINUTE("frametimeeventspm", "Average number of frametime events per minute."),
+ FRAMETIME_JITTER_EVENTS_LAST_MINUTE("frametimeeventslastmin", "Number of frametime events in the last minute.");
+
+LLTrace::SampleStatHandle<F64> NOTRMALIZED_FRAMETIME_JITTER_SESSION("normalizedframetimejitter", "Normalized frametime jitter over the session.");
LLTrace::EventStatHandle<LLUnit<F64, LLUnits::Meters> > AGENT_POSITION_SNAP("agentpositionsnap", "agent position corrections");
@@ -264,16 +277,105 @@ void LLViewerStats::resetStats()
getRecording().reset();
}
+// Helper for calculating Nth percentile with linear interpolation
+template<typename T>
+T calcPercentile(const std::vector<T>& sorted, double percent)
+{
+ if (sorted.empty())
+ return T(0);
+ double idx = percent * (sorted.size() - 1);
+ size_t idx_below = static_cast<size_t>(std::floor(idx));
+ size_t idx_above = static_cast<size_t>(std::ceil(idx));
+ if (idx_below == idx_above)
+ return sorted[idx_below];
+ double weight_above = idx - idx_below;
+ return sorted[idx_below] * (1.0 - weight_above) + sorted[idx_above] * weight_above;
+}
+
+template<typename T>
+T calcStddev(const std::vector<T>& values)
+{
+ if (values.size() < 2)
+ return T(0);
+ double sum = 0, sq_sum = 0;
+ for (const auto& v : values)
+ {
+ double d = v.value();
+ sum += d;
+ sq_sum += d * d;
+ }
+ double mean = sum / values.size();
+ double variance = (sq_sum / values.size()) - (mean * mean);
+ return T(std::sqrt(variance));
+}
+
void LLViewerStats::updateFrameStats(const F64Seconds time_diff)
{
if (gFrameCount && mLastTimeDiff > (F64Seconds)0.0)
{
+ mTotalTime += time_diff;
sample(LLStatViewer::FRAMETIME, time_diff);
// old stats that were never really used
F64Seconds jit = (F64Seconds)std::fabs((mLastTimeDiff - time_diff));
sample(LLStatViewer::FRAMETIME_JITTER, jit);
- }
+ mTotalFrametimeJitter += jit;
+ sample(LLStatViewer::FRAMETIME_JITTER_CUMULATIVE, mTotalFrametimeJitter);
+ sample(LLStatViewer::NOTRMALIZED_FRAMETIME_JITTER_SESSION, mTotalFrametimeJitter / mTotalTime);
+
+ static LLCachedControl<F32> frameTimeEventThreshold(gSavedSettings, "StatsFrametimeEventThreshold", 0.1f);
+
+ if (time_diff - mLastTimeDiff > mLastTimeDiff * frameTimeEventThreshold())
+ {
+ sample(LLStatViewer::FRAMETIME_JITTER_EVENTS, mFrameJitterEvents++);
+ mFrameJitterEventsLastMinute++;
+ }
+ mFrameTimes.push_back(time_diff);
+ mFrameTimesJitter.push_back(jit);
+
+ mLastFrameTimeSample += time_diff;
+ mTimeSinceLastEventSample += time_diff;
+
+ static LLCachedControl<S32> frameTimeSampleSeconds(gSavedSettings, "StatsFrametimeSampleSeconds", 5);
+
+ if (mLastFrameTimeSample >= frameTimeSampleSeconds())
+ {
+ std::sort(mFrameTimes.begin(), mFrameTimes.end());
+ std::sort(mFrameTimesJitter.begin(), mFrameTimesJitter.end());
+
+ // Use new helpers for calculations
+ F64Seconds frame_time_stddev = calcStddev(mFrameTimes);
+ sample(LLStatViewer::FRAMETIME_STDDEV, frame_time_stddev);
+
+ F64Seconds ninety_ninth_percentile = calcPercentile(mFrameTimes, 0.99);
+ F64Seconds ninety_fifth_percentile = calcPercentile(mFrameTimes, 0.95);
+ sample(LLStatViewer::FRAMETIME_99TH, ninety_ninth_percentile);
+ sample(LLStatViewer::FRAMETIME_95TH, ninety_fifth_percentile);
+
+ frame_time_stddev = calcStddev(mFrameTimesJitter);
+ sample(LLStatViewer::FRAMETIME_JITTER_STDDEV, frame_time_stddev);
+
+ ninety_ninth_percentile = calcPercentile(mFrameTimesJitter, 0.99);
+ ninety_fifth_percentile = calcPercentile(mFrameTimesJitter, 0.95);
+ sample(LLStatViewer::FRAMETIME_JITTER_99TH, ninety_ninth_percentile);
+ sample(LLStatViewer::FRAMETIME_JITTER_95TH, ninety_fifth_percentile);
+
+ mFrameTimes.clear();
+ mFrameTimesJitter.clear();
+ mLastFrameTimeSample = F64Seconds(0);
+ }
+
+ if (mTimeSinceLastEventSample >= 60)
+ {
+ mEventMinutes++;
+ // Calculate average events per minute
+ U64 frame_time_events_per_minute = (U64)mFrameJitterEvents / mEventMinutes;
+ sample(LLStatViewer::FRAMETIME_JITTER_EVENTS_PER_MINUTE, frame_time_events_per_minute);
+ sample(LLStatViewer::FRAMETIME_JITTER_EVENTS_LAST_MINUTE, mFrameJitterEventsLastMinute);
+ mFrameJitterEventsLastMinute = 0;
+ mTimeSinceLastEventSample = F64Seconds(0);
+ }
+ }
mLastTimeDiff = time_diff;
}
diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h
index 8aed1c537e..4ec7c6dfac 100644
--- a/indra/newview/llviewerstats.h
+++ b/indra/newview/llviewerstats.h
@@ -275,6 +275,17 @@ private:
LLTrace::Recording mRecording;
F64Seconds mLastTimeDiff; // used for time stat updates
+ F64Seconds mTotalFrametimeJitter;
+
+ U32 mFrameJitterEvents;
+ U32 mFrameJitterEventsLastMinute;
+ U32 mEventMinutes;
+ F64Seconds mTotalTime;
+
+ F64Seconds mLastFrameTimeSample; // used for frame time stats
+ F64Seconds mTimeSinceLastEventSample;
+ std::vector<F64Seconds> mFrameTimes; // used for frame time stats
+ std::vector<F64Seconds> mFrameTimesJitter; // used for frame time jitter stats
};
static const F32 SEND_STATS_PERIOD = 300.0f;
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index f174e16624..7e14c621ad 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -921,6 +921,7 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag
bool on_screen = false;
U32 face_count = 0;
+ U32 max_faces_to_check = 1024;
// get adjusted bias based on image resolution
LLImageGL* img = imagep->getGLTexture();
@@ -933,13 +934,15 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)
{
- for (S32 fi = 0; fi < imagep->getNumFaces(i); ++fi)
+ face_count += imagep->getNumFaces(i);
+ S32 faces_to_check = (face_count > max_faces_to_check) ? 0 : imagep->getNumFaces(i);
+
+ for (S32 fi = 0; fi < faces_to_check; ++fi)
{
LLFace* face = (*(imagep->getFaceList(i)))[fi];
if (face && face->getViewerObject())
{
- ++face_count;
F32 radius;
F32 cos_angle_to_view_dir;
@@ -1002,11 +1005,10 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag
}
}
- if (face_count > 1024)
+ if (face_count > max_faces_to_check)
{ // this texture is used in so many places we should just boost it and not bother checking its vsize
// this is especially important because the above is not time sliced and can hit multiple ms for a single texture
- imagep->setBoostLevel(LLViewerFetchedTexture::BOOST_HIGH);
- // Do we ever remove it? This also sets texture nodelete!
+ max_vsize = MAX_IMAGE_AREA;
}
if (imagep->getType() == LLViewerTexture::LOD_TEXTURE && imagep->getBoostLevel() == LLViewerTexture::BOOST_NONE)
@@ -1205,6 +1207,8 @@ F32 LLViewerTextureList::updateImagesLoadingFastCache(F32 max_time)
enditer = iter;
LLViewerFetchedTexture *imagep = *curiter;
imagep->loadFromFastCache();
+ if (timer.getElapsedTimeF32() > max_time)
+ break;
}
mFastCacheList.erase(mFastCacheList.begin(), enditer);
return timer.getElapsedTimeF32();
@@ -1316,7 +1320,7 @@ void LLViewerTextureList::decodeAllImages(F32 max_time)
LLTimer timer;
//loading from fast cache
- updateImagesLoadingFastCache(max_time);
+ max_time -= updateImagesLoadingFastCache(max_time);
// Update texture stats and priorities
std::vector<LLPointer<LLViewerFetchedTexture> > image_list;
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 2b02d5165f..a2e77e9257 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -4782,7 +4782,18 @@ void LLViewerWindow::saveImageLocal(LLImageFormatted *image, const snapshot_save
#else
boost::filesystem::path b_path(lastSnapshotDir);
#endif
- if (!boost::filesystem::is_directory(b_path))
+ boost::system::error_code ec;
+ if (!boost::filesystem::is_directory(b_path, ec) || ec.failed())
+ {
+ LLSD args;
+ args["PATH"] = lastSnapshotDir;
+ LLNotificationsUtil::add("SnapshotToLocalDirNotExist", args);
+ resetSnapshotLoc();
+ failure_cb();
+ return;
+ }
+ boost::filesystem::space_info b_space = boost::filesystem::space(b_path, ec);
+ if (ec.failed())
{
LLSD args;
args["PATH"] = lastSnapshotDir;
@@ -4791,7 +4802,6 @@ void LLViewerWindow::saveImageLocal(LLImageFormatted *image, const snapshot_save
failure_cb();
return;
}
- boost::filesystem::space_info b_space = boost::filesystem::space(b_path);
if (b_space.free < image->getDataSize())
{
LLSD args;
@@ -4808,6 +4818,8 @@ void LLViewerWindow::saveImageLocal(LLImageFormatted *image, const snapshot_save
LLNotificationsUtil::add("SnapshotToComputerFailed", args);
failure_cb();
+
+ // Shouldn't there be a return here?
}
// Look for an unused file name
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 53832422d8..8c3f219182 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -6349,13 +6349,13 @@ const LLUUID& LLVOAvatar::getID() const
// getJoint()
//-----------------------------------------------------------------------------
// RN: avatar joints are multi-rooted to include screen-based attachments
-LLJoint *LLVOAvatar::getJoint( const std::string &name )
+LLJoint* LLVOAvatar::getJoint(std::string_view name)
{
joint_map_t::iterator iter = mJointMap.find(name);
- LLJoint* jointp = NULL;
+ LLJoint* jointp = nullptr;
- if (iter == mJointMap.end() || iter->second == NULL)
+ if (iter == mJointMap.end() || iter->second == nullptr)
{ //search for joint and cache found joint in lookup table
if (mJointAliasMap.empty())
{
@@ -6372,7 +6372,7 @@ LLJoint *LLVOAvatar::getJoint( const std::string &name )
canonical_name = name;
}
jointp = mRoot->findJoint(canonical_name);
- mJointMap[name] = jointp;
+ mJointMap[std::string(name)] = jointp;
}
else
{ //return cached pointer
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index ab27c5752d..9eb8d3f880 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -202,7 +202,7 @@ public:
void startDefaultMotions();
void dumpAnimationState();
- virtual LLJoint* getJoint(const std::string &name);
+ virtual LLJoint* getJoint(std::string_view name);
LLJoint* getJoint(S32 num);
void initAllJoints();
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index 90ff4067f2..ebba9ba291 100644
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -697,17 +697,17 @@ void LLVOAvatarSelf::idleUpdate(LLAgent &agent, const F64 &time)
}
// virtual
-LLJoint *LLVOAvatarSelf::getJoint(const std::string &name)
+LLJoint* LLVOAvatarSelf::getJoint(std::string_view name)
{
std::lock_guard lock(mJointMapMutex);
- LLJoint *jointp = NULL;
+ LLJoint* jointp = nullptr;
jointp = LLVOAvatar::getJoint(name);
if (!jointp && mScreenp)
{
jointp = mScreenp->findJoint(name);
if (jointp)
{
- mJointMap[name] = jointp;
+ mJointMap[std::string(name)] = jointp;
}
}
if (jointp && jointp != mScreenp && jointp != mRoot)
diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h
index f9bea41b1d..f7cd974ab0 100644
--- a/indra/newview/llvoavatarself.h
+++ b/indra/newview/llvoavatarself.h
@@ -90,7 +90,7 @@ public:
/*virtual*/ bool hasMotionFromSource(const LLUUID& source_id);
/*virtual*/ void stopMotionFromSource(const LLUUID& source_id);
/*virtual*/ void requestStopMotion(LLMotion* motion);
- /*virtual*/ LLJoint* getJoint(const std::string &name);
+ /*virtual*/ LLJoint* getJoint(std::string_view name);
/*virtual*/ void renderJoints();
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 6b3dccf89c..78e2257e56 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -2643,6 +2643,17 @@ void LLVOVolume::syncMediaData(S32 texture_index, const LLSD &media_data, bool m
}
viewer_media_t media_impl = LLViewerMedia::getInstance()->updateMediaImpl(mep, previous_url, update_from_self);
+ static LLCachedControl<bool> media_autoplay_huds(gSavedSettings, "MediaAutoPlayHuds", true);
+ bool was_loaded = media_impl->hasMedia();
+ if (isHUDAttachment() && media_autoplay_huds && !was_loaded)
+ {
+ std::string url = mep->getCurrentURL();
+ if (media_impl->getCurrentMediaURL() != url)
+ {
+ media_impl->navigateTo(url, "", false, true);
+ }
+ }
+
addMediaImpl(media_impl, texture_index) ;
}
else
@@ -3783,7 +3794,12 @@ bool LLVOVolume::canBeAnimatedObject() const
bool LLVOVolume::isAnimatedObject() const
{
- LLVOVolume *root_vol = (LLVOVolume*)getRootEdit();
+ LLViewerObject *root_obj = getRootEdit();
+ if (root_obj->getPCode() != LL_PCODE_VOLUME)
+ {
+ return false; // at the moment only volumes can be animated
+ }
+ LLVOVolume* root_vol = (LLVOVolume*)root_obj;
mIsAnimatedObject = root_vol->getExtendedMeshFlags() & LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG;
return mIsAnimatedObject;
}
diff --git a/indra/newview/llwatchdog.cpp b/indra/newview/llwatchdog.cpp
index 0a1d346266..bf171fe954 100644
--- a/indra/newview/llwatchdog.cpp
+++ b/indra/newview/llwatchdog.cpp
@@ -29,7 +29,7 @@
#include "llwatchdog.h"
#include "llthread.h"
-const U32 WATCHDOG_SLEEP_TIME_USEC = 1000000;
+constexpr U32 WATCHDOG_SLEEP_TIME_USEC = 1000000U;
// This class runs the watchdog timing thread.
class LLWatchdogTimerThread : public LLThread
@@ -51,7 +51,7 @@ public:
mSleepMsecs = 1;
}
- /* virtual */ void run()
+ void run() override
{
while(!mStopping)
{
@@ -83,7 +83,7 @@ void LLWatchdogEntry::start()
void LLWatchdogEntry::stop()
{
// this can happen very late in the shutdown sequence
- if (! LLWatchdog::wasDeleted())
+ if (!LLWatchdog::wasDeleted())
{
LLWatchdog::getInstance()->remove(this);
}
@@ -117,7 +117,7 @@ void LLWatchdogTimeout::setTimeout(F32 d)
mTimeout = d;
}
-void LLWatchdogTimeout::start(const std::string& state)
+void LLWatchdogTimeout::start(std::string_view state)
{
if (mTimeout == 0)
{
@@ -139,9 +139,9 @@ void LLWatchdogTimeout::stop()
mTimer.stop();
}
-void LLWatchdogTimeout::ping(const std::string& state)
+void LLWatchdogTimeout::ping(std::string_view state)
{
- if(!state.empty())
+ if (!state.empty())
{
mPingState = state;
}
@@ -151,7 +151,7 @@ void LLWatchdogTimeout::ping(const std::string& state)
// LLWatchdog
LLWatchdog::LLWatchdog()
:mSuspectsAccessMutex()
- ,mTimer(NULL)
+ ,mTimer(nullptr)
,mLastClockCount(0)
{
}
@@ -176,7 +176,7 @@ void LLWatchdog::remove(LLWatchdogEntry* e)
void LLWatchdog::init()
{
- if(!mSuspectsAccessMutex && !mTimer)
+ if (!mSuspectsAccessMutex && !mTimer)
{
mSuspectsAccessMutex = new LLMutex();
mTimer = new LLWatchdogTimerThread();
@@ -191,17 +191,17 @@ void LLWatchdog::init()
void LLWatchdog::cleanup()
{
- if(mTimer)
+ if (mTimer)
{
mTimer->stop();
delete mTimer;
- mTimer = NULL;
+ mTimer = nullptr;
}
- if(mSuspectsAccessMutex)
+ if (mSuspectsAccessMutex)
{
delete mSuspectsAccessMutex;
- mSuspectsAccessMutex = NULL;
+ mSuspectsAccessMutex = nullptr;
}
mLastClockCount = 0;
@@ -214,12 +214,12 @@ void LLWatchdog::run()
// Check the time since the last call to run...
// If the time elapsed is two times greater than the regualr sleep time
// reset the active timeouts.
- const U32 TIME_ELAPSED_MULTIPLIER = 2;
+ constexpr U32 TIME_ELAPSED_MULTIPLIER = 2;
U64 current_time = LLTimer::getTotalTime();
U64 current_run_delta = current_time - mLastClockCount;
mLastClockCount = current_time;
- if(current_run_delta > (WATCHDOG_SLEEP_TIME_USEC * TIME_ELAPSED_MULTIPLIER))
+ if (current_run_delta > (WATCHDOG_SLEEP_TIME_USEC * TIME_ELAPSED_MULTIPLIER))
{
LL_INFOS() << "Watchdog thread delayed: resetting entries." << LL_ENDL;
for (const auto& suspect : mSuspects)
@@ -233,7 +233,7 @@ void LLWatchdog::run()
std::find_if(mSuspects.begin(),
mSuspects.end(),
[](const LLWatchdogEntry* suspect){ return ! suspect->isAlive(); });
- if(result != mSuspects.end())
+ if (result != mSuspects.end())
{
// error!!!
if(mTimer)
@@ -251,7 +251,7 @@ void LLWatchdog::run()
void LLWatchdog::lockThread()
{
- if(mSuspectsAccessMutex != NULL)
+ if (mSuspectsAccessMutex)
{
mSuspectsAccessMutex->lock();
}
@@ -259,7 +259,7 @@ void LLWatchdog::lockThread()
void LLWatchdog::unlockThread()
{
- if(mSuspectsAccessMutex != NULL)
+ if (mSuspectsAccessMutex)
{
mSuspectsAccessMutex->unlock();
}
diff --git a/indra/newview/llwatchdog.h b/indra/newview/llwatchdog.h
index fe8932e298..1931c582b0 100644
--- a/indra/newview/llwatchdog.h
+++ b/indra/newview/llwatchdog.h
@@ -56,14 +56,14 @@ public:
LLWatchdogTimeout();
virtual ~LLWatchdogTimeout();
- /* virtual */ bool isAlive() const;
- /* virtual */ void reset();
- /* virtual */ void start() { start(""); }
- /* virtual */ void stop();
+ bool isAlive() const override;
+ void reset() override;
+ void start() override { start(""); }
+ void stop() override;
- void start(const std::string& state);
+ void start(std::string_view state);
void setTimeout(F32 d);
- void ping(const std::string& state);
+ void ping(std::string_view state);
const std::string& getState() {return mPingState; }
private:
diff --git a/indra/newview/skins/default/xui/en/floater_model_preview.xml b/indra/newview/skins/default/xui/en/floater_model_preview.xml
index 90223fcda8..a86b9c7da2 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -14,7 +14,7 @@
legacy_header_height="25">
<string name="status_idle"></string>
- <string name="status_parse_error">Error: Dae parsing issue - see log for details.</string>
+ <string name="status_parse_error">Error: Model parsing issue - see log for details.</string>
<string name="status_bind_shape_orientation">Warning: bind shape matrix is not in standard X-forward orientation.</string>
<string name="status_material_mismatch">Error: Material of model is not a subset of reference model.</string>
<string name="status_reading_file">Loading...</string>
@@ -45,6 +45,7 @@
<string name="UnrecognizedJoint">Rigged to unrecognized joint name [NAME]</string>
<string name="UnknownJoints">Skinning disabled due to [COUNT] unknown joints</string>
<string name="ModelLoaded">Model [MODEL_NAME] loaded</string>
+ <string name="InvBindCountMismatch">Bind matrices count mismatch joints count</string>
<string name="IncompleteTC">Texture coordinates data is not complete.</string>
<string name="PositionNaN">Found NaN while loading position data from DAE-Model, invalid model.</string>
@@ -60,6 +61,27 @@
<string name="ParsingErrorNoRoot">Document has no root</string>
<string name="ParsingErrorNoScene">Document has no visual_scene</string>
<string name="ParsingErrorPositionInvalidModel">Unable to process mesh without position data. Invalid model.</string>
+ <string name="UnknownException">Importer crashed while processing [FILENAME], if you encounter this and file is valid, please report the issue to Second Life Support. Exception: [EXCEPTION].</string>
+
+ <!-- GLTF specific messages -->
+ <string name="NoScenesFound">No scenes defined in GLTF file</string>
+ <string name="InvalidMeshReference">Node [NODE_NAME] references invalid mesh [MESH_INDEX] (total meshes: [TOTAL_MESHES])</string>
+ <string name="InvalidGeometryNonTriangulated">Mesh [MESH_NAME] primitive [PRIMITIVE_INDEX]: Invalid geometry with [INDEX_COUNT] indices (must be triangulated)</string>
+ <string name="EmptyVertexArray">Mesh [MESH_NAME] primitive [PRIMITIVE_INDEX]: Empty vertex array</string>
+ <string name="ErrorIndexLimit">Unable to process mesh [MESH_NAME] due to 65,534 vertex limit. Vertex count: [VERTEX_COUNT]</string>
+ <string name="TextureFound">Found texture: [TEXTURE_NAME] for material: [MATERIAL_NAME]</string>
+ <string name="IgnoredExtension">Model uses unsupported extension: [EXT], related material properties are ignored</string>
+ <string name="UnsupportedExtension">Unable to load model, unsupported extension: [EXT]</string>
+ <string name="FailedToCreateTempFile">Failed to create temporary file for embedded [TEXTURE_TYPE] texture [TEXTURE_INDEX]: [TEMP_FILE]</string>
+ <string name="SkinJointsOverLimit">Skin [SKIN_INDEX] defines [JOINT_COUNT] compatible joints, maximum is: [MAX]. Unused joints will be stripped on per model basis.</string>
+ <string name="SkinUsupportedJoints">Skin [SKIN_INDEX] defines [JOINT_COUNT] joints, but only [LEGAL_COUNT] were recognized and are compatible</string>
+ <string name="SkinUnusedJoints">Skin [SKIN_INDEX] defines [JOINT_COUNT] compatible joints, of them only [USED_COUNT] were used</string>
+ <string name="ModelTooManyJoints">Model [MODEL_NAME] uses [JOINT_COUNT], maximum: [MAX], upload might fail</string>
+ <string name="ModelSplitPrimitive">Too many vertices in primitive [MODEL_NAME], it was split into [FACE_COUNT] faces</string>
+ <string name="ModelTooManySubmodels">Model [MODEL_NAME] contains [SUBMODEL_COUNT] generated mesh parts, parts were trimmed to [SUBMODEL_LIMIT]</string>
+ <string name="ParsingErrorMissingBuffer">Buffer is either missing or empty [BUFFER_NAME].</string>
+ <string name="ParsingErrorMissingBufferBin">Buffer is either missing or empty. Check presence of [BUFFER_URI] file.</string>
+ <string name="ParsingErrorException">Parser failed to process [FILENAME], file might be corrupt, incomplete or protected from reading. Exception: [EXCEPTION].</string>
<panel
follows="top|left"
@@ -1404,7 +1426,7 @@
word_wrap="true">
</text_editor>
<check_box
- control_name="ImporterDebug"
+ control_name="ImporterDebugVerboseLogging"
follows="top|left"
top_pad="9"
left="6"
@@ -1706,7 +1728,6 @@ Analysed:
height="408"/>
<panel
follows="right|bottom"
- can_resize="false"
height="140"
layout="topleft"
name="right_panel"
diff --git a/indra/newview/skins/default/xui/en/floater_stats.xml b/indra/newview/skins/default/xui/en/floater_stats.xml
index 6633e25099..f2309eb817 100644
--- a/indra/newview/skins/default/xui/en/floater_stats.xml
+++ b/indra/newview/skins/default/xui/en/floater_stats.xml
@@ -54,6 +54,19 @@
label="jitter"
decimal_digits="1"
stat="frametimejitter"/>
+ <stat_bar name="normalized_cumulative_frametime"
+ label="normalized sess. jitter"
+ decimal_digits="4"
+ stat="normalizedframetimejitter"/>
+ <stat_bar name="frame_events_per_minute"
+ label="frame events/minute"
+ decimal_digits="2"
+ stat="frametimeeventspm"/>
+ <stat_bar name="frame_events_last_minute"
+ label="frame events last min."
+ decimal_digits="0"
+ stat="frametimeeventslastmin"/>
+
<stat_bar name="bandwidth"
label="UDP Data Received"
stat="activemessagedatareceived"
@@ -74,6 +87,38 @@
<stat_view name="render"
label="Render"
setting="OpenDebugStatRender">
+ <stat_bar name="framet_cumulative"
+ label="jitter cumulative"
+ decimal_digits="1"
+ stat="frametimejitcumulative"/>
+ <stat_bar name="framet_jitter_99th"
+ label="jitter 99th percentile"
+ decimal_digits="1"
+ stat="frametimejitter99"/>
+ <stat_bar name="framet_jitter_95th"
+ label="jitter 95th percentile"
+ decimal_digits="1"
+ stat="frametimejitter95"/>
+ <stat_bar name="framet_jitter_stddev"
+ label="frametime jitter std dev"
+ decimal_digits="1"
+ stat="frametimejitterstddev"/>
+ <stat_bar name="framet_99th"
+ label="frametime 99th percentile"
+ decimal_digits="1"
+ stat="frametime99"/>
+ <stat_bar name="framet_95th"
+ label="frametime 95th percentile"
+ decimal_digits="1"
+ stat="frametime95"/>
+ <stat_bar name="framet_stddev"
+ label="frametime std dev"
+ decimal_digits="1"
+ stat="frametimestddev"/>
+ <stat_bar name="framet_events"
+ label="frametime events"
+ decimal_digits="0"
+ stat="frametimeevents"/>
<stat_bar name="ktrisframe"
label="KTris per Frame"
unit_label="ktris/fr"
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 08cc9ae19e..7d094ec613 100644
--- a/indra/newview/skins/default/xui/en/floater_world_map.xml
+++ b/indra/newview/skins/default/xui/en/floater_world_map.xml
@@ -455,7 +455,7 @@
<panel
follows="right|top|bottom"
- height="330"
+ height="235"
top_pad="0"
width="238"
name="layout_panel_4">
@@ -532,9 +532,9 @@
width="16" />
<search_editor
follows="top|right"
- search_button_visible="false"
+ search_button_visible="false"
height="22"
- text_readonly_color="DkGray"
+ text_readonly_color="DkGray"
label="Regions by Name"
layout="topleft"
top_delta="-2"
@@ -542,10 +542,7 @@
name="location"
select_on_focus="true"
tool_tip="Type the name of a region"
- width="152">
- <search_editor.commit_callback
- function="WMap.Location" />
- </search_editor>
+ width="152"/>
<button
follows="top|right"
height="23"
@@ -594,6 +591,13 @@
<scroll_list.commit_callback
function="WMap.SearchResult" />
</scroll_list>
+ </panel>
+ <panel
+ follows="right|bottom"
+ height="95"
+ top_pad="0"
+ width="238"
+ name="layout_panel_7">
<text
type="string"
length="1"
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 56261ce874..f83876fe7d 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -2908,12 +2908,18 @@ function="World.EnvPreset"
function="Advanced.ForceErrorWorkQueueCrash" />
</menu_item_call>
<menu_item_call
- label="Force a Crash in a Thread"
- name="Force a Crash in a Thread">
+ label="Force an LLError Crash in a Thread"
+ name="Force an LLError Crash in a Thread">
<menu_item_call.on_click
function="Advanced.ForceErrorThreadCrash" />
</menu_item_call>
<menu_item_call
+ label="Force an Exception Crash in a Thread"
+ name="Force an Exception Crash in a Thread">
+ <menu_item_call.on_click
+ function="Advanced.ForceExceptionThreadCrash" />
+ </menu_item_call>
+ <menu_item_call
label="Force Disconnect Viewer"
name="Force Disconnect Viewer">
<menu_item_call.on_click
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 8a41405e4c..6bb9f7b1dc 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -9453,8 +9453,11 @@ Unable to upload texture: &apos;[NAME]&apos;
icon="alertmodal.tga"
name="CannotUploadMaterial"
type="alertmodal">
-There was a problem uploading the file
+Unable to upload material file. The file may be corrupted, in an unsupported format, or contain invalid data. Please check that you're using a valid GLTF/GLB file with proper material definitions.
<tag>fail</tag>
+ <usetemplate
+ name="okbutton"
+ yestext="OK"/>
</notification>
<notification
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml
index 8051ffa8ec..c38af9d4fa 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml
@@ -206,6 +206,33 @@
top_delta="0"
width="315" />
</radio_group>
+ <check_box
+ control_name="StreamNotificationChannelEnabled"
+ height="16"
+ label="Custom stream notification channel"
+ layout="topleft"
+ left="30"
+ name="stream_notification_channel_enabled"
+ top_pad="10"
+ width="256">
+ </check_box>
+ <spinner
+ control_name="StreamNotificationChannel"
+ enabled_control="StreamNotificationChannelEnabled"
+ decimal_digits="0"
+ follows="left|top"
+ height="23"
+ increment="1"
+ initial_value="751751"
+ label="Channel number:"
+ label_width="105"
+ layout="topleft"
+ left_pad="-25"
+ max_val="2147483647"
+ min_val="-2147483648"
+ name="stream_notification_channel"
+ top_pad="-15"
+ width="170" />
<check_box
control_name="AllowMultipleViewers"
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml
index 53ae764ba6..de6132aec6 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml
@@ -62,20 +62,9 @@
name="mute_when_minimized"
top_delta="3"
left_pad="5"
- width="20" />
- <!-- *HACK
- After storm-1109 will be fixed: instead of using this text_box, word_wrap should be applied for "mute_when_minimized" check_box's label.-->
- <text
- follows="top|left"
- height="15"
- layout="topleft"
- left_pad="0"
- name="mute_chb_label"
- top_delta="-1"
- width="150"
- wrap="true">
- Mute when minimized
- </text>
+ width="20"
+ label="Mute when minimized"
+ word_wrap="true"/>
<slider
control_name="AudioLevelUI"
disabled_control="MuteAudio"
@@ -321,104 +310,45 @@
left_pad="5"
name="enable_voice_check"
width="110"/>
- <!-- -->
<text
type="string"
length="1"
follows="left|top"
layout="topleft"
left="23"
- top_delta="22"
+ top_delta="25"
name="Listen media from"
height="15"
- word_wrap="true"
- width="112">
- Hear media and sounds from:
+ width="165"
+ halign="right">
+ Hear media and sounds from
</text>
- <radio_group
+ <combo_box
control_name="MediaSoundsEarLocation"
follows="left|top"
- top_delta="-6"
layout="topleft"
left_pad="5"
- width="360"
- height="40"
- name="media_ear_location">
- <radio_item
- height="19"
- label="Camera position"
- follows="left|top"
- layout="topleft"
- name="0"
- width="200"/>
- <radio_item
- height="19"
- follows="left|top"
- label="Avatar position"
- layout="topleft"
- left_delta="0"
- name="1"
- top_delta ="18"
- width="200" />
- </radio_group>
- <text
- type="string"
- length="1"
- follows="left|top"
- layout="topleft"
- left="270"
- top_delta="6"
- name="Listen from"
- width="112">
- Hear voice from:
- </text>
- <radio_group
- enabled_control="EnableVoiceChat"
- control_name="VoiceEarLocation"
- follows="left|top"
- layout="topleft"
- left_pad="2"
- top_delta="-6"
- width="360"
- height="40"
- name="ear_location">
- <radio_item
- height="19"
+ width="130"
+ height="23"
+ top_delta="-4"
+ name="media_ear_location_combo">
+ <item
label="Camera position"
- follows="left|top"
- layout="topleft"
- name="0"
- width="200"/>
- <radio_item
- height="19"
- follows="left|top"
+ name="camera_position"
+ value="0" />
+ <item
label="Avatar position"
- layout="topleft"
- left_delta="0"
- name="1"
- top_delta ="18"
- width="200" />
- </radio_group>
- <check_box
- name="media_show_on_others_btn"
- control_name="MediaShowOnOthers"
- value="true"
- follows="left|top"
- layout="topleft"
- height="15"
- top_pad="8"
- tool_tip="Uncheck this to hide media attached to other avatars nearby"
- label="Play media attached to other avatars"
- left="20"
- width="230"/>
+ name="avatar_position"
+ value="1" />
+</combo_box>
<text
follows="left|top"
layout="topleft"
height="15"
left="23"
- top_pad="8"
- width="120"
- name="media_autoplay_label">
+ width="165"
+ name="media_autoplay_label"
+ halign="right">
Auto-play media
</text>
<combo_box
@@ -427,10 +357,10 @@
follows="left|top"
layout="topleft"
height="23"
- left_pad="-15"
+ left_delta="170"
top_delta="-4"
name="media_auto_play_combo"
- width="115">
+ width="130">
<item
label="Never"
name="autoplay_disabled"
@@ -444,50 +374,106 @@
name="autoplay_ask"
value="2"/>
</combo_box>
+ <text
+ follows="left|top"
+ layout="topleft"
+ height="15"
+ left="23"
+ width="165"
+ name="media_firstinteract_label"
+ halign="right">
+ Media first-interact
+ </text>
+ <combo_box
+ control_name="MediaFirstClickInteract"
+ enabled_control="AudioStreamingMedia"
+ follows="left|top"
+ layout="topleft"
+ height="23"
+ left_delta="170"
+ top_delta="-4"
+ width="130"
+ name="media_first_interact_combo"
+ tool_tip="This setting controls which media (once loaded) does not require a first click to focus before interaction can begin. This allows clicks to be passed directly to media bypassing the focus click requirement. Each option also inherits the previous ones.">
+ <item
+ label="Disabled"
+ name="media_first_click_none"
+ value="0"/>
+ <item
+ label="Worn HUDs only"
+ name="media_first_click_hud"
+ value="1"/>
+ <item
+ label="Owned objects"
+ name="media_first_click_own"
+ value="3"/>
+ <item
+ label="Friends' objects"
+ name="media_first_click_friend"
+ value="7"/>
+ <item
+ label="Group objects"
+ name="media_first_click_group"
+ value="15"/>
+ <item
+ label="Landowner objects"
+ name="media_first_click_land"
+ value="31"/>
+ <item
+ label="Anyone's objects"
+ name="media_first_interact_any"
+ value="1073741823"/>
+ <item
+ label="All MOAP"
+ name="media_first_click_all"
+ value="2147483647"/>
+ </combo_box>
+ <check_box
+ name="media_show_on_others_btn"
+ control_name="MediaShowOnOthers"
+ enabled_control="AudioStreamingMedia"
+ value="true"
+ follows="left|top"
+ tool_tip="Uncheck this to hide media attached to other avatars nearby"
+ label="Play media attached to other avatars"
+ left="23"
+ width="15"
+ top_delta="30"
+ height="15"/>
<check_box
- control_name="StreamNotificationChannelEnabled"
- height="30"
- label="Custom stream notification channel"
- layout="topleft"
- left="20"
- name="stream_notification_channel_enabled"
- top_pad="-10"
- width="256">
- </check_box>
- <spinner
- control_name="StreamNotificationChannel"
- enabled_control="StreamNotificationChannelEnabled"
- decimal_digits="0"
- follows="left|top"
- height="30"
- increment="1"
- initial_value="751751"
- label="Channel number:"
- label_width="105"
- layout="topleft"
- left_pad="0"
- max_val="2147483647"
- min_val="-2147483648"
- name="stream_notification_channel"
- top_pad="-20"
- width="170" />
+ name="media_huds_autoplay"
+ control_name="MediaAutoPlayHuds"
+ enabled_control="AudioStreamingMedia"
+ value="true"
+ follows="left|top"
+ layout="topleft"
+ tool_tip="Uncheck this to make HUDs follow the standard media auto-play setting"
+ label="Auto-play media attached to your HUD"
+ left="260"
+ top_pad="-15"
+ width="15"
+ height="15"/>
<text
layout="topleft"
+ follows="left"
height="15"
- left="260"
- top_pad="-48"
- width="100"
- name="noise_suppression_label">
- Noise suppression
+ width="165"
+ name="noise_suppression_label"
+ left="23"
+ top_delta="22"
+ halign="right">
+ Microphone Noise Suppression
</text>
<combo_box
control_name="VoiceNoiseSuppressionLevel"
+ enabled_control="EnableVoiceChat"
+ follows="left|top"
layout="topleft"
+ left_delta="170"
+ top_delta="-6"
+ width="130"
height="23"
- left_pad="10"
- top_pad="-20"
- name="noise_suppression_combo"
- width="80">
+ name="noise_suppression_combo">
<item
label="Off"
name="noise_suppression_none"
@@ -509,21 +495,52 @@
name="noise_suppression_max"
value="4"/>
</combo_box>
- <text>
-<!-- spacer -->
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ layout="topleft"
+ left="23"
+ top_delta="30"
+ name="Listen from"
+ width="165"
+ height="15"
+ halign="right">
+ Hear voice from
</text>
+ <combo_box
+ enabled_control="EnableVoiceChat"
+ control_name="VoiceEarLocation"
+ follows="left|top"
+ layout="topleft"
+ left_delta="170"
+ top_delta="-6"
+ width="130"
+ height="23"
+ name="ear_location_combo">
+ <item
+ label="Camera position"
+ name="camera_position"
+ value="0" />
+ <item
+ label="Avatar position"
+ name="avatar_position"
+ value="1" />
+</combo_box>
<check_box
control_name="LipSyncEnabled"
- follows="top|left"
+ enabled_control="EnableVoiceChat"
+ follows="left|top"
height="15"
label="Move avatar lips when speaking"
layout="topleft"
left="20"
name="enable_lip_sync"
- top_pad="10"
+ top_pad="8"
width="237"/>
<check_box
control_name="VoiceEchoCancellation"
+ enabled_control="EnableVoiceChat"
height="15"
tool_tip="Check to enable voice echo cancellation"
label="Echo Cancellation"
@@ -546,6 +563,7 @@
top_pad="5"/>
<check_box
control_name="VoiceAutomaticGainControl"
+ enabled_control="EnableVoiceChat"
height="15"
tool_tip="Check to enable automatic gain control"
label="Automatic Gain Control"
@@ -567,6 +585,7 @@
left="20"/>
<check_box
control_name="VoiceVisualizerEnabled"
+ enabled_control="EnableVoiceChat"
height="15"
tool_tip="Check to show voice dot indicator above avatars"
label="Show voice dot above avatars"
@@ -577,6 +596,7 @@
width="200"/>
<button
control_name="ShowDeviceSettings"
+ enabled_control="EnableVoiceChat"
follows="left|top"
height="23"
is_toggle="true"
diff --git a/indra/newview/skins/default/xui/en/panel_profile_pick.xml b/indra/newview/skins/default/xui/en/panel_profile_pick.xml
index 024120931f..4f441b9b49 100644
--- a/indra/newview/skins/default/xui/en/panel_profile_pick.xml
+++ b/indra/newview/skins/default/xui/en/panel_profile_pick.xml
@@ -200,6 +200,26 @@
<layout_panel
follows="all"
+ layout="bottomleft"
+ left_pad="2"
+ name="set_to_curr_location_btn_lp"
+ auto_resize="false"
+ width="100">
+ <button
+ name="set_to_curr_location_btn"
+ label="Set Location"
+ tool_tip="Set to Current Location"
+ left="0"
+ top="0"
+ height="23"
+ width="100"
+ follows="left|top"
+ layout="topleft"
+ />
+ </layout_panel>
+
+ <layout_panel
+ follows="all"
layout="topleft"
name="util_resizer_right"
auto_resize="true"
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 b7db9dec96..1c70383bf9 100644
--- a/indra/newview/skins/default/xui/en/panel_tools_texture.xml
+++ b/indra/newview/skins/default/xui/en/panel_tools_texture.xml
@@ -986,6 +986,19 @@
name="gltfTextureScaleV"
width="265" />
<spinner
+ decimal_digits="1"
+ follows="left|top"
+ height="19"
+ initial_value=""
+ label="Repeats per meter"
+ layout="topleft"
+ label_width="205"
+ left="10"
+ max_val="100"
+ min_val="-100"
+ name="gltfRptctrl"
+ width="265" />
+ <spinner
follows="left|top"
height="19"
initial_value="0"