From 647b26d7b75644f97b2d9afcb1a11f677ae107f5 Mon Sep 17 00:00:00 2001 From: Rye Date: Fri, 2 Jan 2026 09:50:59 -0500 Subject: Optimize away repeated map finds for getParameterEntry* functions via pointers Signed-off-by: Rye --- indra/llprimitive/llprimitive.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'indra/llprimitive') diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h index 31bc76344c..c3e3e19ee9 100644 --- a/indra/llprimitive/llprimitive.h +++ b/indra/llprimitive/llprimitive.h @@ -109,11 +109,12 @@ public: PARAMS_EXTENDED_MESH = 0x70, PARAMS_RENDER_MATERIAL = 0x80, PARAMS_REFLECTION_PROBE = 0x90, + PARAMS_MAX = PARAMS_REFLECTION_PROBE, }; public: U16 mType; - virtual ~LLNetworkData() {}; + virtual ~LLNetworkData() = default; virtual bool pack(LLDataPacker &dp) const = 0; virtual bool unpack(LLDataPacker &dp) = 0; virtual bool operator==(const LLNetworkData& data) const = 0; @@ -319,7 +320,7 @@ public: bool fromLLSD(LLSD& sd); void setSculptTexture(const LLUUID& texture_id, U8 sculpt_type); - LLUUID getSculptTexture() const { return mSculptTexture; } + const LLUUID& getSculptTexture() const { return mSculptTexture; } U8 getSculptType() const { return mSculptType; } }; @@ -340,10 +341,10 @@ public: bool fromLLSD(LLSD& sd); void setLightTexture(const LLUUID& id) { mLightTexture = id; } - LLUUID getLightTexture() const { return mLightTexture; } + const LLUUID& getLightTexture() const { return mLightTexture; } bool isLightSpotlight() const { return mLightTexture.notNull(); } void setParams(const LLVector3& params) { mParams = params; } - LLVector3 getParams() const { return mParams; } + const LLVector3& getParams() const { return mParams; } }; -- cgit v1.3 From 76a80b6787290dc8a950b43b67e5b4cd6238014f Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 10 Jan 2026 04:54:16 -0500 Subject: Replace usage of remaining boost::unordered containers with std Replace LLUUID and LLMaterialID container hashing functions with more collision resistant versions Utilize boost::hash_combine for TEMaterialPair to generate good hash distribution Generalize is_in_map and get_if_there for usage with all mapped types --- indra/llcommon/llpounceable.h | 4 +- indra/llcommon/llsingleton.h | 4 +- indra/llcommon/llstaticstringtable.h | 5 +- indra/llcommon/llstl.h | 18 +++---- indra/llcommon/lluuid.h | 19 ++++++-- indra/llprimitive/llmaterialid.h | 30 +++++++----- indra/llrender/llfontfreetype.h | 5 +- indra/llrender/llgl.cpp | 6 +-- indra/llrender/llgl.h | 4 +- indra/newview/llmaterialmgr.h | 76 +++++++++++++----------------- indra/newview/llmeshrepository.h | 2 +- indra/newview/llperfstats.h | 2 +- indra/newview/llviewermenu.cpp | 8 ++-- indra/newview/llviewerprecompiledheaders.h | 2 - 14 files changed, 97 insertions(+), 88 deletions(-) (limited to 'indra/llprimitive') diff --git a/indra/llcommon/llpounceable.h b/indra/llcommon/llpounceable.h index e86098f20b..20561b0c65 100644 --- a/indra/llcommon/llpounceable.h +++ b/indra/llcommon/llpounceable.h @@ -38,9 +38,9 @@ #include "llsingleton.h" #include #include -#include #include +#include #include // Forward declare the user template, since we want to be able to point to it @@ -86,7 +86,7 @@ class LLPounceableQueueSingleton: // instance will call on the SAME LLPounceableQueueSingleton instance -- // given how class statics work. We must keep a separate queue for each // LLPounceable instance. Use a hash map for that. - typedef boost::unordered_map map_t; + typedef std::unordered_map map_t; public: // Disambiguate queues belonging to different LLPounceables. diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 3fba8602ee..e6989211ae 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -25,10 +25,10 @@ #ifndef LLSINGLETON_H #define LLSINGLETON_H -#include #include #include #include +#include #include #include "mutex.h" #include "lockstatic.h" @@ -61,7 +61,7 @@ private: static vec_t dep_sort(); // we directly depend on these other LLSingletons - typedef boost::unordered_set set_t; + typedef std::unordered_set set_t; set_t mDepends; protected: diff --git a/indra/llcommon/llstaticstringtable.h b/indra/llcommon/llstaticstringtable.h index 66ba3487c4..edff955ee7 100644 --- a/indra/llcommon/llstaticstringtable.h +++ b/indra/llcommon/llstaticstringtable.h @@ -29,9 +29,10 @@ #define LL_STATIC_STRING_TABLE_H #include "lldefs.h" -#include #include "llstl.h" +#include + class LLStaticHashedString { public: @@ -74,7 +75,7 @@ struct LLStaticStringHasher template< typename MappedObject > class LL_COMMON_API LLStaticStringTable - : public boost::unordered_map< LLStaticHashedString, MappedObject, LLStaticStringHasher > + : public std::unordered_map< LLStaticHashedString, MappedObject, LLStaticStringHasher > { }; diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h index 7d41c42ba7..9f3587886c 100644 --- a/indra/llcommon/llstl.h +++ b/indra/llcommon/llstl.h @@ -229,12 +229,10 @@ void delete_and_clear_array(T*& ptr) template inline typename T::mapped_type get_ptr_in_map(const T& inmap, typename T::key_type const& key) { - // Typedef here avoids warnings because of new c++ naming rules. - typedef typename T::const_iterator map_iter; - map_iter iter = inmap.find(key); + auto iter = inmap.find(key); if(iter == inmap.end()) { - return NULL; + return nullptr; } else { @@ -243,8 +241,8 @@ inline typename T::mapped_type get_ptr_in_map(const T& inmap, typename T::key_ty }; // helper function which returns true if key is in inmap. -template -inline bool is_in_map(const std::map& inmap, const K& key) +template +inline bool is_in_map(const T& inmap, typename T::key_type const& key) { if(inmap.find(key) == inmap.end()) { @@ -260,12 +258,10 @@ inline bool is_in_map(const std::map& inmap, const K& key) // To replace LLSkipMap getIfThere, use: // get_if_there(map, key, 0) // WARNING: Make sure default_value (generally 0) is not a valid map entry! -template -inline T get_if_there(const std::map& inmap, const K& key, T default_value) +template +inline typename T::mapped_type get_if_there(const T& inmap, typename T::key_type const& key, typename T::mapped_type default_value) { - // Typedef here avoids warnings because of new c++ naming rules. - typedef typename std::map::const_iterator map_iter; - map_iter iter = inmap.find(key); + auto iter = inmap.find(key); if(iter == inmap.end()) { return default_value; diff --git a/indra/llcommon/lluuid.h b/indra/llcommon/lluuid.h index ca1cf03c4d..f91aadccc0 100644 --- a/indra/llcommon/lluuid.h +++ b/indra/llcommon/lluuid.h @@ -26,6 +26,7 @@ #ifndef LL_LLUUID_H #define LL_LLUUID_H +#include #include #include #include @@ -176,15 +177,27 @@ namespace std { inline size_t operator()(const LLUUID& id) const noexcept { - return (size_t)id.getDigest64(); + size_t h = 0; + // Golden ratio hash with avalanche mixing + // Process 8 bytes at a time by manually constructing 64-bit values + // Shift by 31: mixes upper half into lower half for better bit distribution + // Shift by 47: ensures highest bits influence final hash output + for (int i = 0; i < UUID_BYTES; i += 8) { + size_t chunk = (size_t)id.mData[i] | ((size_t)id.mData[i+1] << 8) | + ((size_t)id.mData[i+2] << 16) | ((size_t)id.mData[i+3] << 24) | + ((size_t)id.mData[i+4] << 32) | ((size_t)id.mData[i+5] << 40) | + ((size_t)id.mData[i+6] << 48) | ((size_t)id.mData[i+7] << 56); + h ^= (chunk * 0x9e3779b97f4a7c15ULL) ^ (h >> 31) ^ (h >> 47); + } + return h; } }; } -// For use with boost containers. +// For use with boost::container_hash inline size_t hash_value(const LLUUID& id) noexcept { - return (size_t)id.getDigest64(); + return std::hash{}(id); } #endif // LL_LLUUID_H diff --git a/indra/llprimitive/llmaterialid.h b/indra/llprimitive/llmaterialid.h index bd6256d961..41dd5a8710 100644 --- a/indra/llprimitive/llmaterialid.h +++ b/indra/llprimitive/llmaterialid.h @@ -67,15 +67,11 @@ public: static const LLMaterialID null; - // Returns a 64 bits digest of the material Id, by XORing its two 64 bits - // long words. HB - inline U64 getDigest64() const - { - U64* tmp = (U64*)mID; - return tmp[0] ^ tmp[1]; - } - private: + // definitions follow class + friend std::hash; + friend size_t hash_value(const LLMaterialID&) noexcept; + void parseFromBinary(const LLSD::Binary& pMaterialID); void copyFromOtherMaterialID(const LLMaterialID& pOtherMaterialID); int compareToOtherMaterialID(const LLMaterialID& pOtherMaterialID) const; @@ -90,15 +86,27 @@ namespace std { inline size_t operator()(const LLMaterialID& id) const noexcept { - return (size_t)id.getDigest64(); + size_t h = 0; + // Golden ratio hash with avalanche mixing + // Process 8 bytes at a time by manually constructing 64-bit values + // Shift by 31: mixes upper half into lower half for better bit distribution + // Shift by 47: ensures highest bits influence final hash output + for (int i = 0; i < MATERIAL_ID_SIZE; i += 8) { + size_t chunk = (size_t)id.mID[i] | ((size_t)id.mID[i + 1] << 8) | + ((size_t)id.mID[i+2] << 16) | ((size_t)id.mID[i+3] << 24) | + ((size_t)id.mID[i+4] << 32) | ((size_t)id.mID[i+5] << 40) | + ((size_t)id.mID[i + 6] << 48) | ((size_t)id.mID[i + 7] << 56); + h ^= (chunk * 0x9e3779b97f4a7c15ULL) ^ (h >> 31) ^ (h >> 47); + } + return h; } }; } -// For use with boost containers. +// For use with boost::container_hash inline size_t hash_value(const LLMaterialID& id) noexcept { - return (size_t)id.getDigest64(); + return std::hash{}(id); } #endif // LL_LLMATERIALID_H diff --git a/indra/llrender/llfontfreetype.h b/indra/llrender/llfontfreetype.h index a9b3a944ee..f7600e40a3 100644 --- a/indra/llrender/llfontfreetype.h +++ b/indra/llrender/llfontfreetype.h @@ -27,13 +27,14 @@ #ifndef LL_LLFONTFREETYPE_H #define LL_LLFONTFREETYPE_H -#include #include "llpointer.h" #include "llstl.h" #include "llimagegl.h" #include "llfontbitmapcache.h" +#include + // Hack. FT_Face is just a typedef for a pointer to a struct, // but there's no simple forward declarations file for FreeType, // and the main include file is 200K. @@ -184,7 +185,7 @@ private: fallback_font_vector_t mFallbackFonts; // A list of fallback fonts to look for glyphs in (for Unicode chars) // *NOTE: the same glyph can be present with multiple representations (but the pointer is always unique) - typedef boost::unordered_multimap char_glyph_info_map_t; + typedef std::unordered_multimap char_glyph_info_map_t; mutable char_glyph_info_map_t mCharGlyphInfoMap; // Information about glyph location in bitmap mutable LLFontBitmapCache* mFontBitmapCachep; diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index d13b98e274..4584ed1d86 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -2376,7 +2376,7 @@ void clear_glerror() // // Static members -boost::unordered_map LLGLState::sStateMap; +std::unordered_map LLGLState::sStateMap; GLboolean LLGLDepthTest::sDepthEnabled = GL_FALSE; // OpenGL default GLenum LLGLDepthTest::sDepthFunc = GL_LESS; // OpenGL default @@ -2419,7 +2419,7 @@ void LLGLState::resetTextureStates() void LLGLState::dumpStates() { LL_INFOS("RenderState") << "GL States:" << LL_ENDL; - for (boost::unordered_map::iterator iter = sStateMap.begin(); + for (std::unordered_map::iterator iter = sStateMap.begin(); iter != sStateMap.end(); ++iter) { LL_INFOS("RenderState") << llformat(" 0x%04x : %s",(S32)iter->first,iter->second?"true":"false") << LL_ENDL; @@ -2451,7 +2451,7 @@ void LLGLState::checkStates(GLboolean writeAlpha) //llassert_always(colorMask[2]); // llassert_always(colorMask[3] == writeAlpha); - for (boost::unordered_map::iterator iter = sStateMap.begin(); + for (std::unordered_map::iterator iter = sStateMap.begin(); iter != sStateMap.end(); ++iter) { LLGLenum state = iter->first; diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h index d19825d9ca..e1ab2a49e6 100644 --- a/indra/llrender/llgl.h +++ b/indra/llrender/llgl.h @@ -31,7 +31,7 @@ #include #include -#include +#include #include #include "llerror.h" @@ -246,7 +246,7 @@ public: static void checkStates(GLboolean writeAlpha = GL_TRUE); protected: - static boost::unordered_map sStateMap; + static std::unordered_map sStateMap; public: enum { CURRENT_STATE = -2, DISABLED_STATE = 0, ENABLED_STATE = 1 }; diff --git a/indra/newview/llmaterialmgr.h b/indra/newview/llmaterialmgr.h index 1279b77ad4..c04f874923 100644 --- a/indra/newview/llmaterialmgr.h +++ b/indra/newview/llmaterialmgr.h @@ -33,9 +33,42 @@ #include "httprequest.h" #include "httpheaders.h" #include "httpoptions.h" +#include class LLViewerRegion; +// struct for TE-specific material ID query +class TEMaterialPair +{ +public: + U32 te; + LLMaterialID materialID; + + bool operator==(const TEMaterialPair& b) const { return (materialID == b.materialID) && (te == b.te); } +}; + +inline bool operator<(const TEMaterialPair& lhs, const TEMaterialPair& rhs) +{ + return (lhs.te < rhs.te) ? true : (lhs.materialID < rhs.materialID); +} + +// std::hash implementation for TEMaterialPair +namespace std +{ + template<> + struct hash + { + inline size_t operator()(const TEMaterialPair& p) const noexcept + { + // Utilize boost::hash_combine to generate a good hash + size_t seed = 0; + boost::hash_combine(seed, p.te + 1); + boost::hash_combine(seed, p.materialID); + return seed; + } + }; +} // namespace std + class LLMaterialMgr : public LLSingleton { LLSINGLETON(LLMaterialMgr); @@ -83,29 +116,6 @@ private: void onRegionRemoved(LLViewerRegion* regionp); private: - // struct for TE-specific material ID query - class TEMaterialPair - { - public: - - U32 te; - LLMaterialID materialID; - - bool operator==(const TEMaterialPair& b) const { return (materialID == b.materialID) && (te == b.te); } - }; - - // definitions follow class - friend std::hash; - friend size_t hash_value(const TEMaterialPair&) noexcept; - - friend inline bool operator<( - const LLMaterialMgr::TEMaterialPair& lhs, - const LLMaterialMgr::TEMaterialPair& rhs) - { - return (lhs.te < rhs.te) ? true : - (lhs.materialID < rhs.materialID); - } - typedef std::set material_queue_t; typedef std::map get_queue_t; typedef std::pair pending_material_t; @@ -113,7 +123,7 @@ private: typedef std::map get_callback_map_t; - typedef boost::unordered_map get_callback_te_map_t; + typedef std::unordered_map get_callback_te_map_t; typedef std::set getall_queue_t; typedef std::map getall_pending_map_t; typedef std::map getall_callback_map_t; @@ -142,23 +152,5 @@ private: U32 getMaxEntries(const LLViewerRegion* regionp); }; -// std::hash implementation for TEMaterialPair -namespace std -{ - template<> struct hash - { - inline size_t operator()(const LLMaterialMgr::TEMaterialPair& p) const noexcept - { - return size_t((p.te + 1) * p.materialID.getDigest64()); - } - }; -} - -// For use with boost containers. -inline size_t hash_value(const LLMaterialMgr::TEMaterialPair& p) noexcept -{ - return size_t((p.te + 1) * p.materialID.getDigest64()); -} - #endif // LL_LLMATERIALMGR_H diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 56ad8297c7..061b4b5428 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -442,7 +442,7 @@ public: LLCondition* mSignal; //map of known mesh headers - typedef boost::unordered_map mesh_header_map; // pair is header_size and data + typedef std::unordered_map mesh_header_map; // pair is header_size and data mesh_header_map mMeshHeader; class HeaderRequest : public RequestStats diff --git a/indra/newview/llperfstats.h b/indra/newview/llperfstats.h index 1a2098ec7e..38deb87237 100644 --- a/indra/newview/llperfstats.h +++ b/indra/newview/llperfstats.h @@ -223,7 +223,7 @@ namespace LLPerfStats static void updateMeanFrameTime(U64 tot_frame_time_raw); // StatsArray is a uint64_t for each possible statistic type. using StatsArray = std::array(LLPerfStats::StatType_t::STATS_COUNT)>; - using StatsMap = std::unordered_map>; + using StatsMap = std::unordered_map; using StatsTypeMatrix = std::array(LLPerfStats::ObjType_t::OT_COUNT)>; using StatsSummaryArray = std::array(LLPerfStats::ObjType_t::OT_COUNT)>; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 5af6a41c11..f88f49d8d7 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -140,7 +140,7 @@ #include "llwindow.h" #include "llpathfindingmanager.h" #include "llstartup.h" -#include "boost/unordered_map.hpp" +#include #include #include #include @@ -153,7 +153,7 @@ using namespace LLAvatarAppearanceDefines; typedef LLPointer LLViewerObjectPtr; -static boost::unordered_map sDefaultItemLabels; +static std::unordered_map sDefaultItemLabels; LLVOAvatar* find_avatar_from_object(LLViewerObject* object); LLVOAvatar* find_avatar_from_object(const LLUUID& object_id); @@ -2965,7 +2965,7 @@ void handle_object_show_original() static void init_default_item_label(LLUICtrl* ctrl) { const std::string& item_name = ctrl->getName(); - boost::unordered_map::iterator it = sDefaultItemLabels.find(item_name); + std::unordered_map::iterator it = sDefaultItemLabels.find(item_name); if (it == sDefaultItemLabels.end()) { // *NOTE: This will not work for items of type LLMenuItemCheckGL because they return boolean value @@ -2981,7 +2981,7 @@ static void init_default_item_label(LLUICtrl* ctrl) static LLStringExplicit get_default_item_label(const std::string& item_name) { LLStringExplicit res(""); - boost::unordered_map::iterator it = sDefaultItemLabels.find(item_name); + std::unordered_map::iterator it = sDefaultItemLabels.find(item_name); if (it != sDefaultItemLabels.end()) { res = it->second; diff --git a/indra/newview/llviewerprecompiledheaders.h b/indra/newview/llviewerprecompiledheaders.h index f6ee00cb25..d33d426769 100644 --- a/indra/newview/llviewerprecompiledheaders.h +++ b/indra/newview/llviewerprecompiledheaders.h @@ -126,8 +126,6 @@ #include "llfloater.h" #include -#include -#include #include #include "glm/glm.hpp" -- cgit v1.3 From 2f8d6420f346b6ee8c98860030b03d79ff88a9c4 Mon Sep 17 00:00:00 2001 From: Rye Date: Sun, 11 Jan 2026 13:22:32 -0500 Subject: Reduce temporary istringstream allocations during LLDate and GLTF override parsing --- indra/llcommon/lldate.cpp | 7 +++++-- indra/llprimitive/llgltfmaterial.cpp | 2 +- indra/newview/llgltfmateriallist.cpp | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) (limited to 'indra/llprimitive') diff --git a/indra/llcommon/lldate.cpp b/indra/llcommon/lldate.cpp index 5205699b92..5545bb71af 100644 --- a/indra/llcommon/lldate.cpp +++ b/indra/llcommon/lldate.cpp @@ -41,6 +41,9 @@ #include "llstring.h" #include "llfasttimer.h" +#include +#include + static const F64 LL_APR_USEC_PER_SEC = 1000000.0; // should be APR_USEC_PER_SEC, but that relies on INT64_C which // isn't defined in glib under our build set up for some reason @@ -64,7 +67,7 @@ std::string LLDate::asString() const { std::ostringstream stream; toStream(stream); - return stream.str(); + return std::move(stream).str(); } //@ brief Converts time in seconds since EPOCH @@ -184,7 +187,7 @@ bool LLDate::split(S32 *year, S32 *month, S32 *day, S32 *hour, S32 *min, S32 *se bool LLDate::fromString(const std::string& iso8601_date) { - std::istringstream stream(iso8601_date); + boost::iostreams::stream stream(iso8601_date.data(), iso8601_date.size()); return fromStream(stream); } diff --git a/indra/llprimitive/llgltfmaterial.cpp b/indra/llprimitive/llgltfmaterial.cpp index 930222e3db..b2cd1dc597 100644 --- a/indra/llprimitive/llgltfmaterial.cpp +++ b/indra/llprimitive/llgltfmaterial.cpp @@ -214,7 +214,7 @@ std::string LLGLTFMaterial::asJSON(bool prettyprint) const // to WriteGltfSceneToStream in the viewer. gltf.WriteGltfSceneToStream(&model_out, str, prettyprint, false); - return str.str(); + return std::move(str).str(); } void LLGLTFMaterial::setFromModel(const tinygltf::Model& model, S32 mat_index) diff --git a/indra/newview/llgltfmateriallist.cpp b/indra/newview/llgltfmateriallist.cpp index ac8477a615..b784419780 100644 --- a/indra/newview/llgltfmateriallist.cpp +++ b/indra/newview/llgltfmateriallist.cpp @@ -170,7 +170,7 @@ namespace void LLGLTFMaterialList::applyOverrideMessage(LLMessageSystem* msg, const std::string& data_in) { - std::istringstream str(data_in); + boost::iostreams::stream str(data_in.data(), data_in.size()); LLSD data; -- cgit v1.3 From 98d7f697ce5d7509a4dabde877de2856e27892dd Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Wed, 28 Jan 2026 03:43:43 +0200 Subject: #2975 PBR support for planar aligment Since checkbox is shared between pbr and normal textures, I'm making it affect both for now. --- indra/llprimitive/llgltfmaterial.cpp | 90 ++++++++++++++++++++++++++++++------ indra/llprimitive/llgltfmaterial.h | 8 ++++ indra/newview/llface.cpp | 85 ++++++++++++++++++++++++++++++++++ indra/newview/llface.h | 2 + indra/newview/llpanelface.cpp | 21 +++++++++ 5 files changed, 192 insertions(+), 14 deletions(-) (limited to 'indra/llprimitive') diff --git a/indra/llprimitive/llgltfmaterial.cpp b/indra/llprimitive/llgltfmaterial.cpp index b2cd1dc597..1c47001272 100644 --- a/indra/llprimitive/llgltfmaterial.cpp +++ b/indra/llprimitive/llgltfmaterial.cpp @@ -924,6 +924,25 @@ void LLGLTFMaterial::updateTextureTracking() // for material overrides editor will set it } +// Test cases: +// Case 1. +// Input: scale 1.0,1.0; Offset horizontal 0.0, Offset vertical 0.0 Rotation 0.349066; +// Expected output: scale 1.0,1.0; Offset horizontal 0.201, Offset vertical -0.141 Rotation -0.349066; +// Case 2. +// Input: scale 1.0,1.0; Offset horizontal 0.5, Offset vertical 0.1 Rotation 0; +// Expected output: scale 1.0,1.0; Offset horizontal 0.5, Offset vertical -0.1 Rotation -0; +// Case 3. +// Input: scale 1.0,1.0; Offset horizontal 0.1, Offset vertical 0.2 Rotation 0.349066; +// Expected output: scale 1.0,1.0; Offset horizontal 0.295, Offset vertical -0.345 Rotation -0.349066; +// Case 4. +// Input: scale 1.0,1.0; Offset horizontal 0.5, Offset vertical 0.0 Rotation 0.349066; +// Expected output: scale 1.0,1.0; Offset horizontal 0.701, Offset vertical -0.141 Rotation -0.349066; +// +// Legacy offsets are right to left and top to bottom. +// PBR offsets are right to left and bottom to top. +// +// Legacy rotation is relative to face's center counter clockwise, +// PBR rotation is relative to top-left corner, clockwise void LLGLTFMaterial::convertTextureTransformToPBR( F32 tex_scale_s, F32 tex_scale_t, @@ -935,22 +954,65 @@ void LLGLTFMaterial::convertTextureTransformToPBR( F32& pbr_rotation) { pbr_scale.set(tex_scale_s, tex_scale_t); - pbr_rotation = -(tex_rotation) / 2.f; - const F32 adjusted_offset_s = tex_offset_s; - const F32 adjusted_offset_t = -tex_offset_t; + pbr_rotation = -tex_rotation; + + // Center of the tile + const F32 center_s = 0.5f; + const F32 center_t = 0.5f; + + // Center adjustment for scale F32 center_adjust_s = 0.5f * (1.0f - tex_scale_s); F32 center_adjust_t = 0.5f * (1.0f - tex_scale_t); - if (pbr_rotation != 0.0f) - { - const F32 c = cosf(pbr_rotation); - const F32 s = sinf(pbr_rotation); - const F32 tmp_s = center_adjust_s * c - center_adjust_t * s; - const F32 tmp_t = center_adjust_s * s + center_adjust_t * c; - center_adjust_s = tmp_s; - center_adjust_t = tmp_t; - } + // 2. Offset from center + F32 pos_s = center_adjust_s - center_s; + F32 pos_t = center_adjust_t - center_t; + + // 3. Rotate around center (clockwise, as per GLTF spec) + F32 c = cosf(pbr_rotation); + F32 s = sinf(pbr_rotation); + F32 rot_s = pos_s * c + pos_t * s; + F32 rot_t = -pos_s * s + pos_t * c; + + // 4. Move back to top-left and apply offset + pbr_offset.set(rot_s + center_s + tex_offset_s, rot_t + center_t - tex_offset_t); +} + +// Convert PBR transform values back to legacy TE transform values. +// This is the reverse of convertTextureTransformToPBR. +void LLGLTFMaterial::convertPBRTransformToTexture( + const LLVector2& pbr_scale, + const LLVector2& pbr_offset, + F32 pbr_rotation, + F32& tex_scale_s, + F32& tex_scale_t, + F32& tex_offset_s, + F32& tex_offset_t, + F32& tex_rotation) +{ + tex_scale_s = pbr_scale.mV[0]; + tex_scale_t = pbr_scale.mV[1]; + tex_rotation = -pbr_rotation; + + // Center of the tile + const F32 center_s = 0.5f; + const F32 center_t = 0.5f; + + // Center adjustment for scale + F32 center_adjust_s = 0.5f * (1.0f - tex_scale_s); + F32 center_adjust_t = 0.5f * (1.0f - tex_scale_t); + + // 2. Offset from center + F32 pos_s = center_adjust_s - center_s; + F32 pos_t = center_adjust_t - center_t; + + // 3. Rotate around center (clockwise, as per GLTF spec) + F32 c = cosf(pbr_rotation); + F32 s = sinf(pbr_rotation); + F32 rot_s = pos_s * c + pos_t * s; + F32 rot_t = -pos_s * s + pos_t * c; - pbr_offset.set(adjusted_offset_s + center_adjust_s, - adjusted_offset_t + center_adjust_t); + // 3. Recover legacy offset + tex_offset_s = pbr_offset.mV[0] - rot_s - center_s; + tex_offset_t = -(pbr_offset.mV[1] - rot_t - center_t); } diff --git a/indra/llprimitive/llgltfmaterial.h b/indra/llprimitive/llgltfmaterial.h index 8d45cb6185..c37062e7d3 100644 --- a/indra/llprimitive/llgltfmaterial.h +++ b/indra/llprimitive/llgltfmaterial.h @@ -222,6 +222,14 @@ public: LLVector2& pbr_scale, LLVector2& pbr_offset, F32& pbr_rotation); + + // Convert PBR transform values to legacy TE transform values. + static void convertPBRTransformToTexture(const LLVector2& pbr_scale, + const LLVector2& pbr_offset, + F32 pbr_rotation, + F32& tex_scale_s, F32& tex_scale_t, + F32& tex_offset_s, F32& tex_offset_t, + F32& tex_rotation); protected: static LLVector2 vec2FromJson(const std::map& object, const char* key, const LLVector2& default_value); static F32 floatFromJson(const std::map& object, const char* key, const F32 default_value); diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 7c631a7280..2b318dcf5f 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -1067,6 +1067,91 @@ bool LLFace::calcAlignedPlanarTE(const LLFace* align_to, LLVector2* res_st_offs return true; } +F32 dot_product(const LLVector3& a, const LLVector3& b) +{ + return a.mV[VX] * b.mV[VX] + a.mV[VY] * b.mV[VY] + a.mV[VZ] * b.mV[VZ]; +} + +bool LLFace::calcAlignedPlanarGLTF( + const LLFace* align_to, + LLVector2* res_st_offset, + LLVector2* res_st_scale, + F32* res_st_rot, + S32 gltf_info_index) const +{ + if (!align_to) + { + return false; + } + + const LLTextureEntry* orig_tep = align_to->getTextureEntry(); + const LLTextureEntry* tep = getTextureEntry(); + if (!orig_tep || !tep) + { + return false; + } + + // Only support planar mapping for now + if (orig_tep->getTexGen() != LLTextureEntry::TEX_GEN_PLANAR || + tep->getTexGen() != LLTextureEntry::TEX_GEN_PLANAR) + { + return false; + } + + LLGLTFMaterial* orig_mat = orig_tep->getGLTFRenderMaterial(); + LLGLTFMaterial* this_mat = tep->getGLTFRenderMaterial(); + if (!orig_mat || !this_mat) + { + return false; + } + + // Get the original GLTF transform for the specified channel + const auto& orig_tt = orig_mat->mTextureTransform[gltf_info_index]; + + // Convert GLTF transform to legacy TE transform + F32 map_scaleS, map_scaleT, map_offsS, map_offsT, map_rot; + LLGLTFMaterial::convertPBRTransformToTexture( + orig_tt.mScale, + orig_tt.mOffset, + orig_tt.mRotation, + map_scaleS, map_scaleT, map_offsS, map_offsT, map_rot); + + // Calculate aligments + LLVector3 orig_pos, this_pos; + LLQuaternion orig_face_rot, this_face_rot; + F32 orig_proj_scale, this_proj_scale; + align_to->getPlanarProjectedParams(&orig_face_rot, &orig_pos, &orig_proj_scale); + getPlanarProjectedParams(&this_face_rot, &this_pos, &this_proj_scale); + + // The rotation of "this face's" texture: + LLQuaternion orig_st_rot = LLQuaternion(map_rot, LLVector3::z_axis) * orig_face_rot; + LLQuaternion this_st_rot = orig_st_rot * ~this_face_rot; + F32 x_ang, y_ang, z_ang; + this_st_rot.getEulerAngles(&x_ang, &y_ang, &z_ang); + + // Offset and scale of "this face's" texture: + LLVector3 centers_dist = (this_pos - orig_pos) * ~orig_st_rot; + LLVector3 st_scale(map_scaleS, map_scaleT, 1.f); + st_scale *= orig_proj_scale; + centers_dist.scaleVec(st_scale); + LLVector2 orig_st_offset(map_offsS, map_offsT); + + LLVector2 tex_res_st_offset = orig_st_offset + (LLVector2)centers_dist; + tex_res_st_offset.mV[VX] -= (S32)tex_res_st_offset.mV[VX]; + tex_res_st_offset.mV[VY] -= (S32)tex_res_st_offset.mV[VY]; + + st_scale /= this_proj_scale; + + // Convert aligned legacy TE transform back to GLTF transform + LLGLTFMaterial::convertTextureTransformToPBR( + st_scale.mV[0], st_scale.mV[1], + tex_res_st_offset.mV[0], tex_res_st_offset.mV[1], + z_ang, + *res_st_scale, *res_st_offset, *res_st_rot); + + return true; +} + void LLFace::updateRebuildFlags() { if (mDrawablep->isState(LLDrawable::REBUILD_VOLUME)) diff --git a/indra/newview/llface.h b/indra/newview/llface.h index df31e9ea90..6e9d23c3a2 100644 --- a/indra/newview/llface.h +++ b/indra/newview/llface.h @@ -103,6 +103,8 @@ public: LLVector3 getPositionAgent() const; LLVector2 surfaceToTexture(LLVector2 surface_coord, const LLVector4a& position, const LLVector4a& normal); void getPlanarProjectedParams(LLQuaternion* face_rot, LLVector3* face_pos, F32* scale) const; + bool calcAlignedPlanarGLTF(const LLFace* align_to, LLVector2* res_st_offset, + LLVector2* res_st_scale, F32* res_st_rot, S32 gltf_info_index = 0) const; bool calcAlignedPlanarTE(const LLFace* align_to, LLVector2* st_offset, LLVector2* st_scale, F32* st_rot, LLRender::eTexIndex map = LLRender::DIFFUSE_MAP) const; diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index bcb51b22ca..128ba42efd 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -852,6 +852,27 @@ struct LLPanelFaceSetAlignedTEFunctor : public LLSelectedTEFunctor LLPanelFace::LLSelectedTEMaterial::setSpecularRepeatX(mPanel, uv_scale.mV[VX], te, object->getID()); LLPanelFace::LLSelectedTEMaterial::setSpecularRepeatY(mPanel, uv_scale.mV[VY], te, object->getID()); } + + // Also align GLTF material if any + S32 gltf_info_index = 0; // base texture + LLVector2 gltf_offset, gltf_scale; + F32 gltf_rot; + if (facep->calcAlignedPlanarGLTF(mCenterFace, &gltf_offset, &gltf_scale, &gltf_rot, gltf_info_index)) + { + LLGLTFMaterial new_override; + const LLTextureEntry* tep = object->getTE(te); + if (tep && tep->getGLTFMaterialOverride()) + { + new_override = *tep->getGLTFMaterialOverride(); + } + + LLGLTFMaterial::TextureTransform& transform = new_override.mTextureTransform[gltf_info_index]; + transform.mOffset.set(gltf_offset.mV[0], gltf_offset.mV[1]); + transform.mScale.set(gltf_scale.mV[0], gltf_scale.mV[1]); + transform.mRotation = gltf_rot; + + LLGLTFMaterialList::queueModify(object, te, &new_override); + } } if (!set_aligned) { -- cgit v1.3