From 92c3cbbb04968b85ccb090258a2a6e8ba96628c6 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Tue, 9 Sep 2025 22:19:48 +0300 Subject: #1965 Properly terminate particles when HUD is off #2 --- indra/newview/llviewerdisplay.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'indra') diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 71830e0948..a80733cf4c 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -1522,6 +1522,11 @@ void render_ui(F32 zoom_factor, int subfield) render_disconnected_background(); } } + else + { + // Make sure particle effects disappear + LLHUDObject::renderAllForTimer(); + } if (render_ui) { -- cgit v1.3 From 003092955486dfb0882d49d6b40586431df6368b Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 10 Oct 2024 23:42:05 +0300 Subject: viewer#2172 AM/PM selector --- indra/newview/app_settings/settings.xml | 4 +- indra/newview/llchathistory.cpp | 1 + indra/newview/llconversationlog.cpp | 20 ++++++--- indra/newview/llfloaterimsessiontab.cpp | 15 ++++++- indra/newview/llfloaterinspect.cpp | 3 +- indra/newview/llfloaterland.cpp | 3 +- indra/newview/llfloaterpreference.cpp | 11 +++++ indra/newview/llfloaterpreference.h | 2 + indra/newview/lllogchat.cpp | 48 +++++++++++----------- indra/newview/llnotificationlistitem.cpp | 21 ++++++++-- indra/newview/llpanellandmarkinfo.cpp | 4 +- indra/newview/llpanelteleporthistory.cpp | 15 ++++++- indra/newview/llsidepaneliteminfo.cpp | 3 +- indra/newview/lltoastgroupnotifypanel.cpp | 19 +++++++-- indra/newview/llworldmap.cpp | 18 ++++++-- .../skins/default/xui/da/sidepanel_item_info.xml | 3 ++ .../skins/default/xui/de/sidepanel_item_info.xml | 3 ++ .../skins/default/xui/en/floater_about_land.xml | 3 ++ .../skins/default/xui/en/floater_inspect.xml | 4 ++ .../newview/skins/default/xui/en/notifications.xml | 2 +- .../skins/default/xui/en/panel_landmark_info.xml | 4 ++ .../skins/default/xui/en/panel_place_profile.xml | 4 ++ .../default/xui/en/panel_preferences_general.xml | 31 ++++++++++++++ .../skins/default/xui/en/sidepanel_item_info.xml | 4 ++ indra/newview/tests/llworldmap_test.cpp | 6 +++ 25 files changed, 201 insertions(+), 50 deletions(-) (limited to 'indra') diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index d64f82d303..aa6a28f137 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -13036,9 +13036,9 @@ Use24HourClock Comment - 12 vs 24. At the moment only for region restart schedule floater + 12 vs 24. At the moment coverage is partial Persist - 0 + 1 Type Boolean Value diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 1988e2072b..2facc43811 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -441,6 +441,7 @@ public: time_t current_time = time_corrected(); time_t message_time = (time_t)(current_time - LLFrameTimer::getElapsedSeconds() + mTime); + // Report abuse shouldn't use AM/PM, use 24-hour time time_string = "[" + LLTrans::getString("TimeMonth") + "]/[" + LLTrans::getString("TimeDay") + "]/[" + LLTrans::getString("TimeYear") + "] [" diff --git a/indra/newview/llconversationlog.cpp b/indra/newview/llconversationlog.cpp index cea68c1779..65a068e08d 100644 --- a/indra/newview/llconversationlog.cpp +++ b/indra/newview/llconversationlog.cpp @@ -118,11 +118,21 @@ const std::string LLConversation::createTimestamp(const U64Seconds& utc_time) LLSD substitution; substitution["datetime"] = (S32)utc_time.value(); - timeStr = "["+LLTrans::getString ("TimeMonth")+"]/[" - +LLTrans::getString ("TimeDay")+"]/[" - +LLTrans::getString ("TimeYear")+"] [" - +LLTrans::getString ("TimeHour")+"]:[" - +LLTrans::getString ("TimeMin")+"]"; + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + timeStr = "[" + LLTrans::getString("TimeMonth") + "]/[" + + LLTrans::getString("TimeDay") + "]/[" + + LLTrans::getString("TimeYear") + "] ["; + if (use_24h) + { + timeStr += LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "]"; + } + else + { + timeStr += LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "]"; + } LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index e03422780a..65c13797ac 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -618,8 +618,19 @@ void LLFloaterIMSessionTab::deleteAllChildren() std::string LLFloaterIMSessionTab::appendTime() { - std::string timeStr = "[" + LLTrans::getString("TimeHour") + "]:" - "[" + LLTrans::getString("TimeMin") + "]"; + std::string timeStr; + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timeStr = "[" + LLTrans::getString("TimeHour") + "]:" + "[" + LLTrans::getString("TimeMin") + "]"; + } + else + { + timeStr = "[" + LLTrans::getString("TimeHour12") + "]:" + "[" + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "]"; + } LLSD substitution; substitution["datetime"] = (S32)time_corrected(); diff --git a/indra/newview/llfloaterinspect.cpp b/indra/newview/llfloaterinspect.cpp index 4f993ca0e1..c0fe7ad896 100644 --- a/indra/newview/llfloaterinspect.cpp +++ b/indra/newview/llfloaterinspect.cpp @@ -220,7 +220,8 @@ void LLFloaterInspect::refresh() } time_t timestamp = (time_t) (obj->mCreationDate/1000000); - std::string timeStr = getString("timeStamp"); + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + std::string timeStr = use_24h ? getString("timeStamp") : getString("timeStampAMPM"); LLSD substitution; substitution["datetime"] = (S32) timestamp; LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 52a3e78d04..5c5219bcdd 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -733,7 +733,8 @@ void LLPanelLandGeneral::refresh() // Display claim date time_t claim_date = parcel->getClaimDate(); - std::string claim_date_str = getString("time_stamp_template"); + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + std::string claim_date_str = use_24h ? getString("time_stamp_template") : getString("time_stamp_template_ampm"); LLSD substitution; substitution["datetime"] = (S32) claim_date; LLStringUtil::format (claim_date_str, substitution); diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 291f22d78f..734cc73331 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -475,6 +475,8 @@ bool LLFloaterPreference::postBuild() getChild("log_path_string")->setEnabled(false); // make it read-only but selectable getChild("language_combobox")->setCommitCallback(boost::bind(&LLFloaterPreference::onLanguageChange, this)); + mTimeFormatCombobox = getChild("time_format_combobox"); + mTimeFormatCombobox->setCommitCallback(boost::bind(&LLFloaterPreference::onTimeFormatChange, this)); getChild("FriendIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"FriendIMOptions")); getChild("NonFriendIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"NonFriendIMOptions")); @@ -1103,6 +1105,13 @@ void LLFloaterPreference::onLanguageChange() } } +void LLFloaterPreference::onTimeFormatChange() +{ + std::string val = mTimeFormatCombobox->getValue(); + gSavedSettings.setBOOL("Use24HourClock", val == "1"); + onLanguageChange(); +} + void LLFloaterPreference::onNotificationsChange(const std::string& OptionName) { mNotificationOptions[OptionName] = getChild(OptionName)->getSelectedItemLabel(); @@ -1318,6 +1327,8 @@ void LLFloaterPreference::refresh() advanced->refresh(); } updateClickActionViews(); + + mTimeFormatCombobox->selectByValue(gSavedSettings.getBOOL("Use24HourClock") ? "1" : "0"); } void LLFloaterPreference::onCommitWindowedMode() diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 61ac0a4d77..3150054798 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -117,6 +117,7 @@ protected: void onClickClearCache(); // Clear viewer texture cache, file cache on next startup void onClickBrowserClearCache(); // Clear web history and caches as well as viewer caches above void onLanguageChange(); + void onTimeFormatChange(); void onNotificationsChange(const std::string& OptionName); void onNameTagOpacityChange(const LLSD& newvalue); @@ -235,6 +236,7 @@ private: LLButton* mDeleteTranscriptsBtn = nullptr; LLButton* mEnablePopupBtn = nullptr; LLButton* mDisablePopupBtn = nullptr; + LLComboBox* mTimeFormatCombobox = nullptr; std::unique_ptr< ll::prefs::SearchData > mSearchData; bool mSearchDataDirty; diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index fa1f650113..30ea255e24 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -80,8 +80,8 @@ const static std::string MULTI_LINE_PREFIX(" "); * * Note: "You" was used as an avatar names in viewers of previous versions */ -const static boost::regex TIMESTAMP_AND_STUFF("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]\\s+|\\[\\d{1,2}:\\d{2}\\]\\s+)?(.*)$"); -const static boost::regex TIMESTAMP("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]|\\[\\d{1,2}:\\d{2}\\]).*"); +const static boost::regex TIMESTAMP_AND_STUFF("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\s[AaPp][Mm]\\]\\s+|\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]\\s+|\\[\\d{1,2}:\\d{2}\\s[AaPp][Mm]\\]\\s+|\\[\\d{1,2}:\\d{2}\\]\\s+)?(.*)$"); +const static boost::regex TIMESTAMP("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}(\\s[AaPp][Mm])?\\]|\\[\\d{1,2}:\\d{2}(\\s[AaPp][Mm])?\\]).*"); /** * Regular expression suitable to match names like @@ -152,6 +152,10 @@ public: void checkAndCutOffDate(std::string& time_str) { + if (time_str.size() < 10) // not enough space for a date + { + return; + } // Cuts off the "%Y/%m/%d" from string for todays timestamps. // Assume that passed string has at least "%H:%M" time format. date log_date(not_a_date_time); @@ -168,20 +172,12 @@ public: if ( days_alive == zero_days ) { - // Yep, today's so strip "%Y/%m/%d" info - ptime stripped_time(not_a_date_time); - - mTimeStream.str(LLStringUtil::null); - mTimeStream << time_str; - mTimeStream >> stripped_time; - mTimeStream.clear(); - - time_str.clear(); - - mTimeStream.str(LLStringUtil::null); - mTimeStream << stripped_time; - mTimeStream >> time_str; - mTimeStream.clear(); + size_t pos = time_str.find_first_of(' '); + if (pos != std::string::npos) + { + time_str.erase(0, pos + 1); + LLStringUtil::trim(time_str); + } } LL_DEBUGS("LLChatLogParser") @@ -310,16 +306,22 @@ std::string LLLogChat::timestamp2LogString(U32 timestamp, bool withdate) std::string timeStr; if (withdate) { - timeStr = "[" + LLTrans::getString ("TimeYear") + "]/[" - + LLTrans::getString ("TimeMonth") + "]/[" - + LLTrans::getString ("TimeDay") + "] [" - + LLTrans::getString ("TimeHour") + "]:[" - + LLTrans::getString ("TimeMin") + "]"; + timeStr = "[" + LLTrans::getString("TimeYear") + "]/[" + + LLTrans::getString("TimeMonth") + "]/[" + + LLTrans::getString("TimeDay") + "] "; + } + + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timeStr += "[" + LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "]"; } else { - timeStr = "[" + LLTrans::getString("TimeHour") + "]:[" - + LLTrans::getString ("TimeMin")+"]"; + timeStr += "[" + LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "]"; } LLSD substitution; diff --git a/indra/newview/llnotificationlistitem.cpp b/indra/newview/llnotificationlistitem.cpp index 5b8b28ebe6..9a33bcb1b9 100644 --- a/indra/newview/llnotificationlistitem.cpp +++ b/indra/newview/llnotificationlistitem.cpp @@ -38,6 +38,7 @@ #include "lluicolortable.h" #include "message.h" #include "llnotificationsutil.h" +#include "llviewercontrol.h" #include LLNotificationListItem::LLNotificationListItem(const Params& p) : LLPanel(p), @@ -133,10 +134,22 @@ std::string LLNotificationListItem::buildNotificationDate(const LLDate& time_sta default: timeStr = "[" + LLTrans::getString("TimeMonth") + "]/[" +LLTrans::getString("TimeDay")+"]/[" - +LLTrans::getString("TimeYear")+"] [" - +LLTrans::getString("TimeHour")+"]:[" - +LLTrans::getString("TimeMin")+"] [" - +LLTrans::getString("TimeTimezone")+"]"; + +LLTrans::getString("TimeYear")+"] ["; + + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timeStr += LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeTimezone") + "]"; + } + else + { + timeStr += LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "] [" + + LLTrans::getString("TimeTimezone") + "]"; + } break; } LLSD substitution; diff --git a/indra/newview/llpanellandmarkinfo.cpp b/indra/newview/llpanellandmarkinfo.cpp index 41373cd7f5..7596c0eba8 100644 --- a/indra/newview/llpanellandmarkinfo.cpp +++ b/indra/newview/llpanellandmarkinfo.cpp @@ -41,6 +41,7 @@ #include "lllandmarkactions.h" #include "llparcel.h" #include "llslurl.h" +#include "llviewercontrol.h" #include "llviewerinventory.h" #include "llviewerparcelmgr.h" #include "llviewerregion.h" @@ -326,7 +327,8 @@ void LLPanelLandmarkInfo::displayItemInfo(const LLInventoryItem* pItem) } else { - std::string timeStr = getString("acquired_date"); + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + std::string timeStr = use_24h ? getString("acquired_date") : getString("acquired_date_ampm"); LLSD substitution; substitution["datetime"] = (S32) time_utc; LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp index 902412d359..bfdfa68e01 100644 --- a/indra/newview/llpanelteleporthistory.cpp +++ b/indra/newview/llpanelteleporthistory.cpp @@ -42,6 +42,7 @@ #include "llnotificationsutil.h" #include "lltextbox.h" #include "lltoggleablemenu.h" +#include "llviewercontrol.h" #include "llviewermenu.h" #include "lllandmarkactions.h" #include "llclipboard.h" @@ -215,8 +216,18 @@ std::string LLTeleportHistoryFlatItem::getTimestamp() // Only show timestamp for today and yesterday if(time_diff < seconds_today + seconds_in_day) { - timestamp = "[" + LLTrans::getString("TimeHour12")+"]:[" - + LLTrans::getString("TimeMin")+"] ["+ LLTrans::getString("TimeAMPM")+"]"; + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timestamp = "[" + LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "]"; + } + else + { + timestamp = "[" + LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + LLTrans::getString("TimeAMPM") + "]"; + } + LLSD substitution; substitution["datetime"] = (S32) date.secondsSinceEpoch(); LLStringUtil::format(timestamp, substitution); diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp index 3d4b4fb9c1..6d50f216f5 100644 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -487,7 +487,8 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) } else { - std::string timeStr = getString("acquiredDate"); + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + std::string timeStr = use_24h ? getString("acquiredDate") : getString("acquiredDateAMPM"); LLSD substitution; substitution["datetime"] = (S32) time_utc; LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/lltoastgroupnotifypanel.cpp b/indra/newview/lltoastgroupnotifypanel.cpp index 3c3440d41a..95653dc19b 100644 --- a/indra/newview/lltoastgroupnotifypanel.cpp +++ b/indra/newview/lltoastgroupnotifypanel.cpp @@ -87,10 +87,21 @@ LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(const LLNotificationPtr& notifi std::string timeStr = "[" + LLTrans::getString("TimeWeek") + "], [" + LLTrans::getString("TimeMonth") + "]/[" + LLTrans::getString("TimeDay") + "]/[" - + LLTrans::getString("TimeYear") + "] [" - + LLTrans::getString("TimeHour") + "]:[" - + LLTrans::getString("TimeMin") + "] [" - + LLTrans::getString("TimeTimezone") + "]"; + + LLTrans::getString("TimeYear") + "] ["; + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timeStr += LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeTimezone") + "]"; + } + else + { + timeStr += LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "] [" + + LLTrans::getString("TimeTimezone") + "]"; + } const LLDate timeStamp = notification->getDate(); LLDate notice_date = timeStamp.notNull() ? timeStamp : payload["received_time"].asDate(); diff --git a/indra/newview/llworldmap.cpp b/indra/newview/llworldmap.cpp index 7962c28e6d..153bee3aef 100644 --- a/indra/newview/llworldmap.cpp +++ b/indra/newview/llworldmap.cpp @@ -32,6 +32,7 @@ #include "message.h" #include "lltracker.h" #include "lluistring.h" +#include "llviewercontrol.h" #include "llviewertexturelist.h" #include "lltrans.h" #include "llgltexture.h" @@ -492,9 +493,20 @@ bool LLWorldMap::insertItem(U32 x_world, U32 y_world, std::string& name, LLUUID& case MAP_ITEM_MATURE_EVENT: case MAP_ITEM_ADULT_EVENT: { - std::string timeStr = "["+ LLTrans::getString ("TimeHour")+"]:[" - +LLTrans::getString ("TimeMin")+"] [" - +LLTrans::getString ("TimeAMPM")+"]"; + std::string timeStr; + + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timeStr = "[" + LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "]"; + } + else + { + timeStr = "[" + LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "]"; + } LLSD substitution; substitution["datetime"] = (S32) extra; LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/skins/default/xui/da/sidepanel_item_info.xml b/indra/newview/skins/default/xui/da/sidepanel_item_info.xml index d52845160b..e5ad86b315 100644 --- a/indra/newview/skins/default/xui/da/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/da/sidepanel_item_info.xml @@ -15,6 +15,9 @@ [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] + (Beholdning) diff --git a/indra/newview/skins/default/xui/de/sidepanel_item_info.xml b/indra/newview/skins/default/xui/de/sidepanel_item_info.xml index 168bb14248..3570ccacd2 100644 --- a/indra/newview/skins/default/xui/de/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/de/sidepanel_item_info.xml @@ -21,6 +21,9 @@ [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] + (Inventar) diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml index 508aba6ae1..c5b42b6dae 100644 --- a/indra/newview/skins/default/xui/en/floater_about_land.xml +++ b/indra/newview/skins/default/xui/en/floater_about_land.xml @@ -125,6 +125,9 @@ name="no_selection_text"> No parcel selected. + + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] diff --git a/indra/newview/skins/default/xui/en/floater_inspect.xml b/indra/newview/skins/default/xui/en/floater_inspect.xml index 9403d58441..a083683c23 100644 --- a/indra/newview/skins/default/xui/en/floater_inspect.xml +++ b/indra/newview/skins/default/xui/en/floater_inspect.xml @@ -11,6 +11,10 @@ save_rect="true" title="INSPECT OBJECTS" width="400"> + + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 7fc96af55d..fa3fd86d59 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -1670,7 +1670,7 @@ The new skin will appear after you restart [APP_NAME]. icon="alertmodal.tga" name="ChangeLanguage" type="alertmodal"> -Changing language will take effect after you restart [APP_NAME]. +Changing language or time format will take effect after you restart [APP_NAME]. Information about this location is unavailable due to access restrictions. Please check your permissions with the parcel owner. + + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] diff --git a/indra/newview/skins/default/xui/en/panel_place_profile.xml b/indra/newview/skins/default/xui/en/panel_place_profile.xml index 8f5292c531..bb877080b1 100644 --- a/indra/newview/skins/default/xui/en/panel_place_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_place_profile.xml @@ -88,6 +88,10 @@ name="server_forbidden_text"> Information about this location is unavailable due to access restrictions. Please check your permissions with the parcel owner. + + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] diff --git a/indra/newview/skins/default/xui/en/panel_preferences_general.xml b/indra/newview/skins/default/xui/en/panel_preferences_general.xml index 101c506309..ddddb4855f 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_general.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_general.xml @@ -91,6 +91,37 @@ name="Traditional Chinese" value="zh" /> + + Time Format: + + + + + Owner can: + + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] diff --git a/indra/newview/tests/llworldmap_test.cpp b/indra/newview/tests/llworldmap_test.cpp index d5bf189d82..60172b3960 100644 --- a/indra/newview/tests/llworldmap_test.cpp +++ b/indra/newview/tests/llworldmap_test.cpp @@ -32,6 +32,7 @@ #include "lltrans.h" #include "lluistring.h" #include "../llviewertexture.h" +#include "../llviewercontrol.h" #include "../llworldmapmessage.h" // Class to test #include "../llworldmap.h" @@ -71,6 +72,11 @@ void LLUIString::updateResult() const { } void LLUIString::setArg(const std::string& , const std::string& ) { } void LLUIString::assign(const std::string& ) { } +LLControlGroup::LLControlGroup(const std::string& name) : LLInstanceTracker(name) {} +LLControlGroup::~LLControlGroup() {} +bool LLControlGroup::getBOOL(std::string_view) { return true; } +LLControlGroup gSavedSettings("test_settings"); + // End Stubbing // ------------------------------------------------------------------------------------------- -- cgit v1.3 From 2ef17ab9d67f99ad492c83f9363e391e2d87a649 Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Sat, 21 Sep 2024 19:54:02 +0200 Subject: #2618 Roles and Members tab is a mess (fix XML indents for readability) --- .../skins/default/xui/en/panel_group_roles.xml | 1187 ++++++++++---------- 1 file changed, 593 insertions(+), 594 deletions(-) (limited to 'indra') diff --git a/indra/newview/skins/default/xui/en/panel_group_roles.xml b/indra/newview/skins/default/xui/en/panel_group_roles.xml index 02e135a2c7..33f98b37ea 100644 --- a/indra/newview/skins/default/xui/en/panel_group_roles.xml +++ b/indra/newview/skins/default/xui/en/panel_group_roles.xml @@ -18,7 +18,7 @@ Checkbox_Off - + + relative_width="0.44" /> + relative_width="0.2" /> - + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/floater_search.xml b/indra/newview/skins/default/xui/en/floater_search.xml index 76a486e211..43c4aa1b9d 100644 --- a/indra/newview/skins/default/xui/en/floater_search.xml +++ b/indra/newview/skins/default/xui/en/floater_search.xml @@ -1,26 +1,202 @@ - + legacy_header_height="18" + can_minimize="true" + can_close="true" + can_resize="true" + height="775" + layout="topleft" + min_height="500" + min_width="600" + name="Search" + save_rect="true" + single_instance="true" + save_visibility="true" + title="SEARCH" + tab_stop="true" + width="780"> + + + + + + + + + + + + + + + + + + + + -- cgit v1.3 From 694bf1e25aca5cca1b46b86f85404152d29f766b Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Tue, 30 Sep 2025 21:37:49 +0300 Subject: #4762 Better cacheOptimize's issue handling --- indra/llmath/llvolume.cpp | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'indra') diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 7c60253618..b3cb278d59 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -5713,6 +5713,8 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents) { try { + // providing mIndices should help avoid unused vertices + // but those should have been filtered out on upload vert_count = static_cast(meshopt_generateVertexRemapMulti(&remap[0], nullptr, data.p.size(), data.p.size(), mos, stream_count)); } catch (std::bad_alloc&) @@ -5722,10 +5724,16 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents) } } - if (vert_count < 65535 && vert_count != 0) + // Probably should be using meshopt_remapVertexBuffer instead of remaping manually + if (vert_count < 65535 && vert_count > 0) { //copy results back into volume resizeVertices(vert_count); + if (mNumVertices == 0) + { + LLError::LLUserWarningMsg::showOutOfMemory(); + LL_ERRS("LLCoros") << "Failed to allocate memory for resizeVertices(" << vert_count << ")" << LL_ENDL; + } if (!data.w.empty()) { @@ -5738,13 +5746,27 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents) { U32 src_idx = i; U32 dst_idx = remap[i]; - if (dst_idx >= (U32)mNumVertices) + if (dst_idx == U32_MAX) + { + // Unused indices? Probably need to resize mIndices + dst_idx = mNumVertices - 1; + llassert(false); + LL_DEBUGS_ONCE("LLVOLUME") << "U32_MAX destination index, substituting" << LL_ENDL; + } + else if (dst_idx >= (U32)mNumVertices) { dst_idx = mNumVertices - 1; // Shouldn't happen, figure out what gets returned in remap and why. llassert(false); LL_DEBUGS_ONCE("LLVOLUME") << "Invalid destination index, substituting" << LL_ENDL; } + if (src_idx >= (U32)data.p.size()) + { + // data.p.size() is supposed to be equal to mNumIndices + src_idx = (U32)(data.p.size() - 1); + llassert(false); + LL_DEBUGS_ONCE("LLVOLUME") << "Invalid source index, substituting" << LL_ENDL; + } mIndices[i] = dst_idx; mPositions[dst_idx].load3(data.p[src_idx].mV); @@ -5778,7 +5800,7 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents) } else { - if (vert_count == 0) + if (vert_count <= 0) { LL_WARNS_ONCE("LLVOLUME") << "meshopt_generateVertexRemapMulti failed to process a model or model was invalid" << LL_ENDL; } -- cgit v1.3 From ee6a9e7ed10f775bce312a0b8911ea8721342250 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Tue, 30 Sep 2025 22:44:40 +0300 Subject: #4758 Extra logging to track discard 6 Plus clamped some values and preventing decode from starting --- indra/llrender/llimagegl.cpp | 3 ++- indra/newview/lltexturefetch.cpp | 21 +++++++++++++++++---- indra/newview/llviewertexture.cpp | 2 +- 3 files changed, 20 insertions(+), 6 deletions(-) (limited to 'indra') diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 52738ec626..94115a06fd 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -1544,7 +1544,7 @@ bool LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S if (discard_level < 0) { llassert(mCurrentDiscardLevel >= 0); - discard_level = mCurrentDiscardLevel; + discard_level = llmin(mCurrentDiscardLevel, MAX_DISCARD_LEVEL); } // Actual image width/height = raw image width/height * 2^discard_level @@ -1644,6 +1644,7 @@ bool LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, bool data_ discard_level = mCurrentDiscardLevel; } discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel); + discard_level = llmin(discard_level, MAX_DISCARD_LEVEL); if (main_thread // <--- always force creation of new_texname when not on main thread ... && !defer_copy // <--- ... or defer copy is set diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 8b991f3f34..f7cb0ee7ed 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1703,10 +1703,10 @@ bool LLTextureFetchWorker::doWork(S32 param) mHttpReplyOffset = 0; mLoadedDiscard = mRequestedDiscard; - if (mLoadedDiscard < 0) + if (mLoadedDiscard < 0 || (mLoadedDiscard > MAX_DISCARD_LEVEL && mFormattedImage->getCodec() == IMG_CODEC_J2C)) { LL_WARNS(LOG_TXT) << mID << " mLoadedDiscard is " << mLoadedDiscard - << ", should be >=0" << LL_ENDL; + << ", should be >=0 and <=" << MAX_DISCARD_LEVEL << LL_ENDL; } setState(DECODE_IMAGE); if (mWriteToCacheState != NOT_WRITE) @@ -1768,14 +1768,27 @@ bool LLTextureFetchWorker::doWork(S32 param) LL_DEBUGS(LOG_TXT) << mID << " DECODE_IMAGE abort: mLoadedDiscard < 0" << LL_ENDL; return true; } + + llassert_always(mFormattedImage.notNull()); + S32 discard = mHaveAllData && mFormattedImage->getCodec() != IMG_CODEC_J2C ? 0 : mLoadedDiscard; + if (discard > MAX_DISCARD_LEVEL) // only warn for j2c + { + // We encode j2c with fixed amount of discard levels, + // Trying to decode beyound that will fail. + LL_WARNS(LOG_TXT) << "Decode entered with invalid discard. ID = " << mID << LL_ENDL; + + //abort, don't decode + setState(DONE); + LL_DEBUGS(LOG_TXT) << mID << " DECODE_IMAGE abort: mLoadedDiscard > MAX_DISCARD_LEVEL" << LL_ENDL; + return true; + } + mDecodeTimer.reset(); mRawImage = NULL; mAuxImage = NULL; - llassert_always(mFormattedImage.notNull()); // if we have the entire image data (and the image is not J2C), decode the full res image // DO NOT decode a higher res j2c than was requested. This is a waste of time and memory. - S32 discard = mHaveAllData && mFormattedImage->getCodec() != IMG_CODEC_J2C ? 0 : mLoadedDiscard; mDecoded = false; setState(DECODE_IMAGE_UPDATE); LL_DEBUGS(LOG_TXT) << mID << ": Decoding. Bytes: " << mFormattedImage->getDataSize() << " Discard: " << discard diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 3142c9950b..6135e18840 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -2153,7 +2153,7 @@ bool LLViewerFetchedTexture::updateFetch() } const U32 override_tex_discard_level = gSavedSettings.getU32("TextureDiscardLevel"); - if (override_tex_discard_level != 0) + if (override_tex_discard_level != 0 && override_tex_discard_level <= MAX_DISCARD_LEVEL) { desired_discard = override_tex_discard_level; } -- cgit v1.3 From 503affdc9799311e6adeaf0271e470c46f8ceda8 Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Wed, 1 Oct 2025 17:54:46 +0300 Subject: #vp484 Crash in LLFolderViewFolder::setOpen --- indra/llui/llfolderviewitem.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'indra') diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index 9ca77dbe46..0f9fd35868 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -2106,10 +2106,14 @@ void LLFolderViewFolder::setOpen(bool openitem) { // navigateToFolder can destroy this view // delay it in case setOpen was called from click or key processing - doOnIdleOneTime([this]() - { - getViewModelItem()->navigateToFolder(); - }); + LLPointer view_model_item = mViewModelItem; + doOnIdleOneTime([view_model_item]() + { + if (view_model_item.notNull()) + { + view_model_item.get()->navigateToFolder(); + } + }); } else { -- cgit v1.3 From 69312e248e65a03fca67dd38b00a84164461269e Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Wed, 1 Oct 2025 18:41:01 +0300 Subject: #4768 Crash at retransmitUnackedPackets For some reason there are no logs for these crashes, but lack of connection data indicates that viewer was disconnected prior to crash. --- indra/newview/llappviewer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 442a37e977..eadc5e7e63 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5553,7 +5553,10 @@ void LLAppViewer::idleNetwork() add(LLStatViewer::NUM_NEW_OBJECTS, gObjectList.mNumNewObjects); // Retransmit unacknowledged packets. - gXferManager->retransmitUnackedPackets(); + if (gXferManager) + { + gXferManager->retransmitUnackedPackets(); + } gAssetStorage->checkForTimeouts(); gViewerThrottle.setBufferLoadRate(gMessageSystem->getBufferLoadRate()); gViewerThrottle.updateDynamicThrottle(); -- cgit v1.3 From f05fe9c195d589da6726be32ce89ba7a42124e71 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:28:51 +0300 Subject: #4770 Crashes on ~LLCoprocedureManager() Situation is not clear, so hoping that explicit cleanup will reveal something. --- indra/llmessage/llcoproceduremanager.cpp | 33 +++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp index 563dd9459c..5c7b1c4235 100644 --- a/indra/llmessage/llcoproceduremanager.cpp +++ b/indra/llmessage/llcoproceduremanager.cpp @@ -138,7 +138,22 @@ LLCoprocedureManager::LLCoprocedureManager() LLCoprocedureManager::~LLCoprocedureManager() { - close(); + try + { + close(); + } + catch (const boost::fibers::fiber_error&) + { + LL_WARNS() << "Fiber error during ~LLCoprocedureManager()" << LL_ENDL; + } + catch (const std::exception& e) + { + // Shutting down, just log it + LL_WARNS() << "Exception during ~LLCoprocedureManager(): " << e.what() << LL_ENDL; + } + mPropertyQueryFn.clear(); + mPropertyDefineFn.clear(); + mPoolMap.clear(); } void LLCoprocedureManager::initializePool(const std::string &poolName, size_t queue_size) @@ -365,6 +380,22 @@ LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size, s LLCoprocedurePool::~LLCoprocedurePool() { + try + { + close(); // should have been closed already, but shouldn't hurt + mStatusListener.disconnect(); + mPendingCoprocs.reset(); + mCoroMapping.clear(); + } + catch (const boost::fibers::fiber_error&) + { + LL_WARNS() << "Fiber error during ~LLCoprocedurePool() " << mPoolName << LL_ENDL; + } + catch (const std::exception& e) + { + // Shutting down, just log it + LL_WARNS() << "Exception " << e.what() << " during ~LLCoprocedurePool() in " << mPoolName << LL_ENDL; + } } //------------------------------------------------------------------------- -- cgit v1.3 From 45528d33280a2ea8ce03211e8e60d9ef991d07ea Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Wed, 1 Oct 2025 20:33:53 +0300 Subject: #4773 Crash on sub_image_lines Just caught it and discard_level is somehow 7, which seems like it resulted in src going out of bounds, which crashed glTexSubImage2D --- indra/llrender/llimagegl.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 94115a06fd..97ea6f67bd 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -1097,6 +1097,8 @@ void sub_image_lines(U32 target, S32 miplevel, S32 x_offset, S32 y_offset, S32 w // full width texture, do 32 lines at a time for (U32 y_pos = y_offset; y_pos < y_offset_end; y_pos += batch_size) { + // If this keeps crashing, pass down data_size, looks like it is using + // imageraw->getData(); for data, but goes way over allocated size limit glTexSubImage2D(target, miplevel, x_offset, y_pos, width, batch_size, pixformat, pixtype, src); src += line_width * batch_size; } @@ -1106,6 +1108,8 @@ void sub_image_lines(U32 target, S32 miplevel, S32 x_offset, S32 y_offset, S32 w // partial width or strange height for (U32 y_pos = y_offset; y_pos < y_offset_end; y_pos += 1) { + // If this keeps crashing, pass down data_size, looks like it is using + // imageraw->getData(); for data, but goes way over allocated size limit glTexSubImage2D(target, miplevel, x_offset, y_pos, width, 1, pixformat, pixtype, src); src += line_width; } @@ -1544,8 +1548,9 @@ bool LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S if (discard_level < 0) { llassert(mCurrentDiscardLevel >= 0); - discard_level = llmin(mCurrentDiscardLevel, MAX_DISCARD_LEVEL); + discard_level = mCurrentDiscardLevel; } + discard_level = llmin(discard_level, MAX_DISCARD_LEVEL); // Actual image width/height = raw image width/height * 2^discard_level S32 raw_w = imageraw->getWidth() ; -- cgit v1.3 From 589498fece769446310c085e9feb08ccd8400c2c Mon Sep 17 00:00:00 2001 From: Brad Linden Date: Wed, 1 Oct 2025 17:50:28 -0700 Subject: Reenable havok llphysicsextensions on Darwin x86_64 with llphysicsextensions_stub used for aarch64 --- autobuild.xml | 40 ++++------------------------------- build.sh | 1 - indra/cmake/LLPhysicsExtensions.cmake | 9 +++++++- indra/newview/CMakeLists.txt | 9 ++++++++ 4 files changed, 21 insertions(+), 38 deletions(-) (limited to 'indra') diff --git a/autobuild.xml b/autobuild.xml index c9355c73c0..280078b4c2 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -1432,53 +1432,21 @@ platforms - darwin64 - - archive - - creds - github - hash - 7facda95e2f00c260513f3d4db42588fa8ba703c - hash_algorithm - sha1 - url - https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/196289774 - - name - darwin64 - - linux64 - - archive - - creds - github - hash - 01d08f13c7bc8d1b95b0330fa6833b7d8274e4d0 - hash_algorithm - sha1 - url - https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/196289775 - - name - linux64 - - windows64 + common archive creds github hash - 6d00345c7d3471bc5f7c1218e014dd0f1a2c069b + 1949ae355a70a4cbe2f0969de636680a0b5d7b15 hash_algorithm sha1 url - https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/196289778 + https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/299453833 name - windows64 + common license diff --git a/build.sh b/build.sh index 36e332cf42..325386e7e9 100755 --- a/build.sh +++ b/build.sh @@ -158,7 +158,6 @@ pre_build() if [[ "$arch" == "Darwin" ]] then - HAVOK=OFF SIGNING=("-DENABLE_SIGNING:BOOL=YES" \ "-DSIGNING_IDENTITY:STRING=Developer ID Application: Linden Research, Inc.") fi diff --git a/indra/cmake/LLPhysicsExtensions.cmake b/indra/cmake/LLPhysicsExtensions.cmake index 80d243d9f8..6112621b5a 100644 --- a/indra/cmake/LLPhysicsExtensions.cmake +++ b/indra/cmake/LLPhysicsExtensions.cmake @@ -22,7 +22,14 @@ if (HAVOK) include(Havok) use_prebuilt_binary(llphysicsextensions_source) set(LLPHYSICSEXTENSIONS_SRC_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/src) - target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions) + if(DARWIN) + set(LLPHYSICSEXTENSIONS_STUB_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/stub) + # can't set these library dependencies per-arch here, need to do it using XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=*] in newview/CMakeLists.txt + #target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions) + #target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensionsstub) + else() + target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions) + endif() elseif (HAVOK_TPV) use_prebuilt_binary(llphysicsextensions_tpv) target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions_tpv) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c727d5ae57..72baac73ae 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -59,6 +59,10 @@ if (NOT HAVOK_TPV) # which means we need to duct tape this togther ... add_subdirectory(${LLPHYSICSEXTENSIONS_SRC_DIR} llphysicsextensions) + if (NOT "${LLPHYSICSEXTENSIONS_STUB_DIR}" EQUAL "") + # for darwin universal builds we need both real llphysicsextensions and the stub for aarch64 fallback + add_subdirectory(${LLPHYSICSEXTENSIONS_STUB_DIR} llphysicsextensionsstub) + endif() # Another hack that works with newer cmake versions: cmake_policy( SET CMP0079 NEW) @@ -1952,6 +1956,11 @@ elseif (DARWIN) RESOURCE SecondLife.xib #LINK_FLAGS_RELEASE "${LINK_FLAGS_RELEASE} -Xlinker -dead_strip -Xlinker -map -Xlinker ${CMAKE_CURRENT_BINARY_DIR}/${VIEWER_BINARY_NAME}.MAP" LINK_FLAGS_RELEASE "${LINK_FLAGS_RELEASE} -Xlinker -dead_strip" + # arch specific flags for universal builds: https://stackoverflow.com/a/77942065 + XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=x86_64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_FULL" + XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=arm64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_STUB" + XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=x86_64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensions/$,$,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensions" + XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=arm64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensionsstub/$,$,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensionsstub" ) else (WINDOWS) # Linux -- cgit v1.3 From b8cf54d909263e14622d9669b192c9ad14dc7173 Mon Sep 17 00:00:00 2001 From: Brad Linden Date: Wed, 1 Oct 2025 18:17:16 -0700 Subject: Fix configurations like windows where LLPHYSICSEXTENSIONS_STUB_DIR is unset --- indra/newview/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 72baac73ae..1444eeaba3 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -59,7 +59,7 @@ if (NOT HAVOK_TPV) # which means we need to duct tape this togther ... add_subdirectory(${LLPHYSICSEXTENSIONS_SRC_DIR} llphysicsextensions) - if (NOT "${LLPHYSICSEXTENSIONS_STUB_DIR}" EQUAL "") + if (NOT "${LLPHYSICSEXTENSIONS_STUB_DIR}" STREQUAL "") # for darwin universal builds we need both real llphysicsextensions and the stub for aarch64 fallback add_subdirectory(${LLPHYSICSEXTENSIONS_STUB_DIR} llphysicsextensionsstub) endif() -- cgit v1.3 From fc41f57a06065957bc377d7a1716c5c04b8fec70 Mon Sep 17 00:00:00 2001 From: Brad Linden Date: Thu, 2 Oct 2025 11:26:48 -0700 Subject: Attempt to fix build error --- indra/newview/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'indra') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 1444eeaba3..9e566096fc 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -61,6 +61,7 @@ if (NOT HAVOK_TPV) add_subdirectory(${LLPHYSICSEXTENSIONS_SRC_DIR} llphysicsextensions) if (NOT "${LLPHYSICSEXTENSIONS_STUB_DIR}" STREQUAL "") # for darwin universal builds we need both real llphysicsextensions and the stub for aarch64 fallback + # this will only be set when HAVOK is ON, otherwise the normal stub fallback will be in effect add_subdirectory(${LLPHYSICSEXTENSIONS_STUB_DIR} llphysicsextensionsstub) endif() @@ -73,6 +74,9 @@ if (NOT HAVOK_TPV) target_link_libraries(llphysicsextensions llrender ) if (DARWIN) target_compile_options( llphysicsextensions PRIVATE -Wno-unused-local-typedef) + + # TODO - move this into llphysicsextensions/CMakeLists.txt once I've proved it works + set_target_properties(llphysicsextensions PROPERTIES OSX_ARCHITECTURES "x86_64") endif (DARWIN) endif() endif (NOT HAVOK_TPV) -- cgit v1.3 From 99d8f22c99e54ba4c22a2b5834bba2c0de549fca Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Thu, 2 Oct 2025 22:24:59 +0300 Subject: #4757 Model uploader warning when physics library is missing --- indra/newview/llviewermenufile.cpp | 18 +++++++++++++++++- indra/newview/skins/default/xui/en/notifications.xml | 19 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) (limited to 'indra') diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 3695478061..801ff3c212 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -912,7 +912,23 @@ class LLFileUploadModel : public view_listener_t { bool handleEvent(const LLSD& userdata) { - LLFloaterModelPreview::showModelPreview(); + if (LLConvexDecomposition::isFunctional()) + { + LLFloaterModelPreview::showModelPreview(); + } + else + { + if (gGLManager.mIsApple) + { + LLNotificationsUtil::add("ModelUploaderMissingPhysicsApple"); + } + else + { + // TPV? + LLNotificationsUtil::add("ModelUploaderMissingPhysics"); + LLFloaterModelPreview::showModelPreview(); + } + } return true; } }; diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 7fc96af55d..536008c85c 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -2237,6 +2237,25 @@ Couldn't open uploaded sound file for reading: fail + +Model upload is not yet available on Apple Silicon, but will be supported in an upcoming release. + +Workaround: Right-click the Second Life app in Finder, select +"Get Info", then check "Open using Rosetta" + fail + + + +Physics library is not present, some of the model uploader's functionality might not work or might not work correctly. + fail + + Date: Thu, 2 Oct 2025 13:35:54 -0700 Subject: new llphysicsextensions_source package that sets OSX_ARCHITECTURES properly and should hopefully package llphysicsextensions_tpv successfully --- autobuild.xml | 4 ++-- indra/newview/CMakeLists.txt | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'indra') diff --git a/autobuild.xml b/autobuild.xml index 280078b4c2..56831346c1 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -1439,11 +1439,11 @@ creds github hash - 1949ae355a70a4cbe2f0969de636680a0b5d7b15 + fff82c79edb900c547c40dca9a0e3ebac5a8c7da hash_algorithm sha1 url - https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/299453833 + https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/299858950 name common diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 9e566096fc..0d50aeca82 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -74,9 +74,6 @@ if (NOT HAVOK_TPV) target_link_libraries(llphysicsextensions llrender ) if (DARWIN) target_compile_options( llphysicsextensions PRIVATE -Wno-unused-local-typedef) - - # TODO - move this into llphysicsextensions/CMakeLists.txt once I've proved it works - set_target_properties(llphysicsextensions PROPERTIES OSX_ARCHITECTURES "x86_64") endif (DARWIN) endif() endif (NOT HAVOK_TPV) -- cgit v1.3 From e30bc61d9a63ed29985bd6e07405f0051bfceaf6 Mon Sep 17 00:00:00 2001 From: Brad Linden Date: Thu, 2 Oct 2025 14:51:14 -0700 Subject: Restore creation of secondlife-bin.MAP file on Darwin x86_64 for production of llphysicsextensions_tpv --- indra/newview/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 0d50aeca82..eb40067930 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1955,12 +1955,12 @@ elseif (DARWIN) set_target_properties(${VIEWER_BINARY_NAME} PROPERTIES RESOURCE SecondLife.xib - #LINK_FLAGS_RELEASE "${LINK_FLAGS_RELEASE} -Xlinker -dead_strip -Xlinker -map -Xlinker ${CMAKE_CURRENT_BINARY_DIR}/${VIEWER_BINARY_NAME}.MAP" LINK_FLAGS_RELEASE "${LINK_FLAGS_RELEASE} -Xlinker -dead_strip" # arch specific flags for universal builds: https://stackoverflow.com/a/77942065 XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=x86_64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_FULL" XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=arm64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_STUB" - XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=x86_64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensions/$,$,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensions" + # only generate the .MAP file for llphysicsextensions_tpv on x86_64 + XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=x86_64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensions/$,$,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensions -Xlinker -map -Xlinker ${CMAKE_CURRENT_BINARY_DIR}/${VIEWER_BINARY_NAME}.MAP" XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=arm64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensionsstub/$,$,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensionsstub" ) else (WINDOWS) -- cgit v1.3 From 9a413357ad5bc5f5492ece90b2c9cc9dbe6087c8 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 2 Oct 2025 06:18:22 -0400 Subject: Fix infinite growth of mLoadingPhysicsShapes in mesh repository --- indra/newview/llmeshrepository.cpp | 45 +++++++++++++++++++++++++++++--------- indra/newview/llmeshrepository.h | 5 ++++- 2 files changed, 39 insertions(+), 11 deletions(-) (limited to 'indra') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 8d5f94cdbb..1f6a046919 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -256,6 +256,7 @@ // mDecompositionRequests mMutex rw.repo.mMutex, ro.repo.none [5] // mPhysicsShapeRequests mMutex rw.repo.mMutex, ro.repo.none [5] // mDecompositionQ mMutex rw.repo.mLoadedMutex, rw.main.mLoadedMutex [5] (was: [0]) +// mPhysicsQ mMutex rw.repo.mLoadedMutex, rw.main.mLoadedMutex [5] (was: [0]) // mHeaderReqQ mMutex ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex // mLODReqQ mMutex ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex // mUnavailableQ mMutex rw.repo.none [0], ro.main.none [5], rw.main.mLoadedMutex @@ -982,6 +983,12 @@ LLMeshRepoThread::~LLMeshRepoThread() mDecompositionQ.pop_front(); } + while (!mPhysicsQ.empty()) + { + delete mPhysicsQ.front(); + mPhysicsQ.pop_front(); + } + delete mHttpRequest; mHttpRequest = nullptr; delete mMutex; @@ -2565,7 +2572,7 @@ EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_ { LLMutexLock lock(mLoadedMutex); - mDecompositionQ.push_back(d); + mPhysicsQ.push_back(d); } return MESH_OK; } @@ -3441,13 +3448,14 @@ void LLMeshRepoThread::notifyLoadedMeshes() } } - if (!mSkinInfoQ.empty() || !mSkinUnavailableQ.empty() || ! mDecompositionQ.empty()) + if (!mSkinInfoQ.empty() || !mSkinUnavailableQ.empty() || !mDecompositionQ.empty() || !mPhysicsQ.empty()) { if (mLoadedMutex->trylock()) { std::deque> skin_info_q; std::deque skin_info_unavail_q; std::list decomp_q; + std::list physics_q; if (! mSkinInfoQ.empty()) { @@ -3464,6 +3472,11 @@ void LLMeshRepoThread::notifyLoadedMeshes() decomp_q.swap(mDecompositionQ); } + if (!mPhysicsQ.empty()) + { + physics_q.swap(mPhysicsQ); + } + mLoadedMutex->unlock(); // Process the elements free of the lock @@ -3480,9 +3493,15 @@ void LLMeshRepoThread::notifyLoadedMeshes() while (! decomp_q.empty()) { - gMeshRepo.notifyDecompositionReceived(decomp_q.front()); + gMeshRepo.notifyDecompositionReceived(decomp_q.front(), false); decomp_q.pop_front(); } + + while (!physics_q.empty()) + { + gMeshRepo.notifyDecompositionReceived(physics_q.front(), true); + physics_q.pop_front(); + } } } @@ -4724,13 +4743,13 @@ void LLMeshRepository::notifySkinInfoUnavailable(const LLUUID& mesh_id) } } -void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decomp) +void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decomp, bool physics_mesh) { - decomposition_map::iterator iter = mDecompositionMap.find(decomp->mMeshID); + LLUUID decomp_id = decomp->mMeshID; // Copy to avoid invalidation in below deletion + decomposition_map::iterator iter = mDecompositionMap.find(decomp_id); if (iter == mDecompositionMap.end()) { //just insert decomp into map - mDecompositionMap[decomp->mMeshID] = decomp; - mLoadingDecompositions.erase(decomp->mMeshID); + mDecompositionMap[decomp_id] = decomp; sCacheBytesDecomps += decomp->sizeBytes(); } else @@ -4738,10 +4757,17 @@ void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decom sCacheBytesDecomps -= iter->second->sizeBytes(); iter->second->merge(decomp); sCacheBytesDecomps += iter->second->sizeBytes(); - - mLoadingDecompositions.erase(decomp->mMeshID); delete decomp; } + + if (physics_mesh) + { + mLoadingPhysicsShapes.erase(decomp_id); + } + else + { + mLoadingDecompositions.erase(decomp_id); + } } void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume, S32 lod) @@ -4888,7 +4914,6 @@ void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id) std::unordered_set::iterator iter = mLoadingPhysicsShapes.find(mesh_id); if (iter == mLoadingPhysicsShapes.end()) { //no request pending for this skin info - // *FIXME: Nothing ever deletes entries, can't be right mLoadingPhysicsShapes.insert(mesh_id); mPendingPhysicsShapeRequests.push(mesh_id); } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index ab17b921d6..cf05807164 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -521,6 +521,9 @@ public: // list of completed Decomposition info requests std::list mDecompositionQ; + // list of completed Physics Mesh info requests + std::list mPhysicsQ; + //queue of requested headers std::queue mHeaderReqQ; @@ -855,7 +858,7 @@ public: void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 request_lod, S32 volume_lod); void notifySkinInfoReceived(LLMeshSkinInfo* info); void notifySkinInfoUnavailable(const LLUUID& info); - void notifyDecompositionReceived(LLModel::Decomposition* info); + void notifyDecompositionReceived(LLModel::Decomposition* info, bool physics_mesh); S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod); static S32 getActualMeshLOD(LLMeshHeader& header, S32 lod); -- cgit v1.3 From b277cc6a8fd2a48ed3ba6498734d9fae209d181e Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 18 Sep 2025 13:38:54 -0400 Subject: Initial VHACD based llconvexdecomposition --- autobuild.xml | 68 +-- indra/CMakeLists.txt | 3 + indra/cmake/CMakeLists.txt | 1 + indra/cmake/LLPhysicsExtensions.cmake | 8 +- indra/cmake/VHACD.cmake | 9 + indra/llconvexdecomposition/CMakeLists.txt | 39 ++ .../llconvexdecomposition.cpp | 83 ++++ .../llconvexdecomposition/llconvexdecomposition.h | 231 ++++++++++ .../llconvexdecompositionvhacd.cpp | 492 +++++++++++++++++++++ .../llconvexdecompositionvhacd.h | 339 ++++++++++++++ indra/llprimitive/CMakeLists.txt | 6 + indra/llprimitive/llmodel.cpp | 4 +- indra/llprimitive/llmodel.h | 3 +- indra/newview/CMakeLists.txt | 8 +- indra/newview/llfloatermodelpreview.cpp | 12 +- indra/newview/llmodelpreview.cpp | 14 +- indra/newview/llviewerobjectlist.cpp | 4 +- .../skins/default/xui/en/floater_model_preview.xml | 149 ++++++- 18 files changed, 1418 insertions(+), 55 deletions(-) create mode 100644 indra/cmake/VHACD.cmake create mode 100644 indra/llconvexdecomposition/CMakeLists.txt create mode 100644 indra/llconvexdecomposition/llconvexdecomposition.cpp create mode 100644 indra/llconvexdecomposition/llconvexdecomposition.h create mode 100644 indra/llconvexdecomposition/llconvexdecompositionvhacd.cpp create mode 100644 indra/llconvexdecomposition/llconvexdecompositionvhacd.h (limited to 'indra') diff --git a/autobuild.xml b/autobuild.xml index c9355c73c0..8a022cd304 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -1496,41 +1496,19 @@ platforms - darwin64 - - archive - - hash - f290b000b31f9e36f2489946cbc99f5e - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/59995/563653/llphysicsextensions_stub-1.0.542456-darwin64-542456.tar.bz2 - - name - darwin64 - - linux64 - - archive - - hash - 711f4ec769e4b5f59ba25ee43c11bcbc - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4724/14846/llphysicsextensions_stub-1.0.504712-linux64-504712.tar.bz2 - - name - linux64 - - windows64 + common archive hash - 2e5f1f7046a49d8b0bc295aa878116bc + bc41438b10ac6474cf5560465a3662a64f9e65a81342e4c33f18f6694581c7ee28c9ee6f091c36e80a0b1e10c68205be71eb5f8e40fef115d2c744fc2bbfcb43 + hash_algorithm + blake2b url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/60043/564063/llphysicsextensions_stub-1.0.542456-windows-542456.tar.bz2 + https://github.com/AlchemyViewer/llphysicsextensions_stub/releases/download/v1.0-cb4900e/llphysicsextensions_stub-1.0-common-17836965684.tar.zst name - windows64 + common license @@ -1540,7 +1518,7 @@ copyright Copyright (c) 2010, Linden Research, Inc. version - 1.0.542456 + 1.0 name llphysicsextensions_stub @@ -2966,6 +2944,38 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors description Discord Social SDK + vhacd + + platforms + + common + + archive + + hash + 140d8fc952a10edb5f2d72ab405336019ef32cadfa64f0cfce76c9de4bc6268cbc87cc8cd89d3417fb78b531d441701afc8d016bafe4bd275df2707f7daf1387 + hash_algorithm + blake2b + url + https://github.com/AlchemyViewer/3p-vhacd/releases/download/v4.1.0-r2/vhacd-4.1.0-r2-common-18166921729.tar.zst + + name + common + + + license + BSD + license_file + LICENSES/vhacd.txt + copyright + Copyright (c) 2011, Khaled Mamou + version + 4.1.0-r2 + name + vhacd + description + Voxelized Hierarchical Approximate Convex Decomposition + package_description diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 6504002dd9..246bd7bfa0 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -61,6 +61,9 @@ add_subdirectory(cmake) add_subdirectory(${LIBS_OPEN_PREFIX}llaudio) add_subdirectory(${LIBS_OPEN_PREFIX}llappearance) add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter) +if (NOT HAVOK AND NOT HAVOK_TPV) + add_subdirectory(${LIBS_OPEN_PREFIX}llconvexdecomposition) +endif () add_subdirectory(${LIBS_OPEN_PREFIX}llcommon) add_subdirectory(${LIBS_OPEN_PREFIX}llcorehttp) add_subdirectory(${LIBS_OPEN_PREFIX}llimage) diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 4608f68f50..2ba282bdb7 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -62,6 +62,7 @@ set(cmake_SOURCE_FILES UI.cmake UnixInstall.cmake Variables.cmake + VHACD.cmake ViewerMiscLibs.cmake VisualLeakDetector.cmake LibVLCPlugin.cmake diff --git a/indra/cmake/LLPhysicsExtensions.cmake b/indra/cmake/LLPhysicsExtensions.cmake index 80d243d9f8..bb6cb703d2 100644 --- a/indra/cmake/LLPhysicsExtensions.cmake +++ b/indra/cmake/LLPhysicsExtensions.cmake @@ -23,9 +23,15 @@ if (HAVOK) use_prebuilt_binary(llphysicsextensions_source) set(LLPHYSICSEXTENSIONS_SRC_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/src) target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions) + target_compile_definitions( llphysicsextensions_impl INTERFACE LL_HAVOK=1 ) elseif (HAVOK_TPV) use_prebuilt_binary(llphysicsextensions_tpv) - target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions_tpv) + if(WINDOWS) + target_link_libraries( llphysicsextensions_impl INTERFACE ${ARCH_PREBUILT_DIRS}/llphysicsextensions_tpv.lib) + else() + target_link_libraries( llphysicsextensions_impl INTERFACE ${ARCH_PREBUILT_DIRS}/libllphysicsextensions_tpv.a) + endif() + target_compile_definitions( llphysicsextensions_impl INTERFACE LL_HAVOK=1 ) else (HAVOK) use_prebuilt_binary(llphysicsextensions_stub) set(LLPHYSICSEXTENSIONS_SRC_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/stub) diff --git a/indra/cmake/VHACD.cmake b/indra/cmake/VHACD.cmake new file mode 100644 index 0000000000..9f37f32b2d --- /dev/null +++ b/indra/cmake/VHACD.cmake @@ -0,0 +1,9 @@ +# -*- cmake -*- +include(Prebuilt) + +add_library(ll::vhacd INTERFACE IMPORTED) + +use_system_binary(vhacd) +use_prebuilt_binary(vhacd) + +target_include_directories(ll::vhacd SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include/vhacd/) diff --git a/indra/llconvexdecomposition/CMakeLists.txt b/indra/llconvexdecomposition/CMakeLists.txt new file mode 100644 index 0000000000..7dae884db8 --- /dev/null +++ b/indra/llconvexdecomposition/CMakeLists.txt @@ -0,0 +1,39 @@ +# -*- cmake -*- + +project(llconvexdecomposition) + +include(00-Common) +include(LLCommon) +include(LLMath) +include(VHACD) + +set(llconvexdecomposition_SOURCE_FILES + llconvexdecomposition.cpp + llconvexdecompositionvhacd.cpp + ) + +set(llconvexdecomposition_HEADER_FILES + CMakeLists.txt + llconvexdecomposition.h + llconvexdecompositionvhacd.h + ) + +set_source_files_properties(${llconvexdecomposition_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND llconvexdecomposition_SOURCE_FILES ${llconvexdecomposition_HEADER_FILES}) + +add_library (llconvexdecomposition ${llconvexdecomposition_SOURCE_FILES}) +target_include_directories(llconvexdecomposition INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +target_link_libraries(llconvexdecomposition + llcommon + llmath + ll::vhacd) + +if(WINDOWS) + target_compile_options(llconvexdecomposition PRIVATE /bigobj) +endif() + +# Add tests + diff --git a/indra/llconvexdecomposition/llconvexdecomposition.cpp b/indra/llconvexdecomposition/llconvexdecomposition.cpp new file mode 100644 index 0000000000..7b9d775c53 --- /dev/null +++ b/indra/llconvexdecomposition/llconvexdecomposition.cpp @@ -0,0 +1,83 @@ +/** +* @file llconvexdecomposition.cpp +* @author falcon@lindenlab.com +* @brief Inner implementation of LLConvexDecomposition interface +* +* $LicenseInfo:firstyear=2011&license=lgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2011, 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 "linden_common.h" + +#include "llconvexdecompositionvhacd.h" +#include "llconvexdecomposition.h" + +bool LLConvexDecomposition::s_isInitialized = false; + +// static +bool LLConvexDecomposition::isFunctional() +{ + return LLConvexDecompositionVHACD::isFunctional(); +} + +// static +LLConvexDecomposition* LLConvexDecomposition::getInstance() +{ + if ( !s_isInitialized ) + { + return nullptr; + } + else + { + return LLConvexDecompositionVHACD::getInstance(); + } +} + +// static +LLCDResult LLConvexDecomposition::initSystem() +{ + LLCDResult result = LLConvexDecompositionVHACD::initSystem(); + if ( result == LLCD_OK ) + { + s_isInitialized = true; + } + return result; +} + +// static +LLCDResult LLConvexDecomposition::initThread() +{ + return LLConvexDecompositionVHACD::initThread(); +} + +// static +LLCDResult LLConvexDecomposition::quitThread() +{ + return LLConvexDecompositionVHACD::quitThread(); +} + +// static +LLCDResult LLConvexDecomposition::quitSystem() +{ + return LLConvexDecompositionVHACD::quitSystem(); +} + + diff --git a/indra/llconvexdecomposition/llconvexdecomposition.h b/indra/llconvexdecomposition/llconvexdecomposition.h new file mode 100644 index 0000000000..8008bc6e12 --- /dev/null +++ b/indra/llconvexdecomposition/llconvexdecomposition.h @@ -0,0 +1,231 @@ +/** + * @file llconvexdecomposition.cpp + * @brief LLConvexDecomposition interface definition + * + * $LicenseInfo:firstyear=2011&license=lgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, 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_CONVEX_DECOMPOSITION +#define LL_CONVEX_DECOMPOSITION + +typedef int bool32; + +#if defined(_WIN32) || defined(_WIN64) +#define LLCD_CALL __cdecl +#else +#define LLCD_CALL +#endif + +struct LLCDParam +{ + enum LLCDParamType + { + LLCD_INVALID = 0, + LLCD_INTEGER, + LLCD_FLOAT, + LLCD_BOOLEAN, + LLCD_ENUM + }; + + struct LLCDEnumItem + { + const char* mName; + int mValue; + }; + + union LLCDValue + { + float mFloat; + int mIntOrEnumValue; + bool32 mBool; + }; + + union LLCDParamDetails + { + struct { + LLCDValue mLow; + LLCDValue mHigh; + LLCDValue mDelta; + } mRange; + + struct { + int mNumEnums; + LLCDEnumItem* mEnumsArray; + } mEnumValues; + }; + + const char* mName; + const char* mDescription; + LLCDParamType mType; + LLCDParamDetails mDetails; + LLCDValue mDefault; + int mStage; + + // WARNING: Only the LLConvexDecomposition implementation + // should change this value + int mReserved; +}; + +struct LLCDStageData +{ + const char* mName; + const char* mDescription; + bool32 mSupportsCallback; +}; + +struct LLCDMeshData +{ + enum IndexType + { + INT_16, + INT_32 + }; + + const float* mVertexBase; + int mVertexStrideBytes; + int mNumVertices; + const void* mIndexBase; + IndexType mIndexType; + int mIndexStrideBytes; + int mNumTriangles; +}; + +struct LLCDHull +{ + const float* mVertexBase; + int mVertexStrideBytes; + int mNumVertices; +}; + +enum LLCDResult +{ + LLCD_OK = 0, + LLCD_UNKOWN_ERROR, + LLCD_NULL_PTR, + LLCD_INVALID_STAGE, + LLCD_UNKNOWN_PARAM, + LLCD_BAD_VALUE, + LLCD_REQUEST_OUT_OF_RANGE, + LLCD_INVALID_MESH_DATA, + LLCD_INVALID_HULL_DATA, + LLCD_STAGE_NOT_READY, + LLCD_INVALID_THREAD, + LLCD_NOT_IMPLEMENTED +}; + +// This callback will receive a string describing the current subtask being performed +// as well as a pair of numbers indicating progress. (The values should not be interpreted +// as a completion percentage as 'current' may be greater than 'final'.) +// If the callback returns zero, the decomposition will be terminated +typedef int (LLCD_CALL *llcdCallbackFunc)(const char* description, int current_progress, int final_progress); + +class LLConvexDecomposition +{ +public: + // Obtain a pointer to the actual implementation + static LLConvexDecomposition* getInstance(); + + /// @returns false if this is the stub + static bool isFunctional(); + + static LLCDResult initSystem(); + static LLCDResult initThread(); + static LLCDResult quitThread(); + static LLCDResult quitSystem(); + + // Generate a decomposition object handle + virtual void genDecomposition(int& decomp) = 0; + // Delete decomposition object handle + virtual void deleteDecomposition(int decomp) = 0; + // Bind given decomposition handle + // Commands operate on currently bound decomposition + virtual void bindDecomposition(int decomp) = 0; + + // Sets *paramsOut to the address of the LLCDParam array and returns + // the number of parameters + virtual int getParameters(const LLCDParam** paramsOut) = 0; + + + // Sets *stagesOut to the address of the LLCDStageData array and returns + // the number of stages + virtual int getStages(const LLCDStageData** stagesOut) = 0; + + + // Set a parameter by name. Pass enum values as integers. + virtual LLCDResult setParam(const char* name, float val) = 0; + virtual LLCDResult setParam(const char* name, int val) = 0; + virtual LLCDResult setParam(const char* name, bool val) = 0; + + + // Set incoming mesh data. Data is copied to local buffers and will + // persist until the next setMeshData call + virtual LLCDResult setMeshData( const LLCDMeshData* data, bool vertex_based ) = 0; + + + // Register a callback to be called periodically during the specified stage + // See the typedef above for more information + virtual LLCDResult registerCallback( int stage, llcdCallbackFunc callback ) = 0; + + + // Execute the specified decomposition stage + virtual LLCDResult executeStage(int stage) = 0; + virtual LLCDResult buildSingleHull() = 0 ; + + + // Gets the number of hulls generated by the specified decompositions stage + virtual int getNumHullsFromStage(int stage) = 0; + + + // Populates hullOut to reference the internal copy of the requested hull + // The data will persist only until the next executeStage call for that stage. + virtual LLCDResult getHullFromStage( int stage, int hull, LLCDHull* hullOut ) = 0; + + virtual LLCDResult getSingleHull( LLCDHull* hullOut ) = 0 ; + + + // TODO: Implement lock of some kind to disallow this call if data not yet ready + // Populates the meshDataOut to reference the utility's copy of the mesh geometry + // for the hull and stage specified. + // You must copy this data if you want to continue using it after the next executeStage + // call + virtual LLCDResult getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut) = 0; + + + // Creates a mesh from hullIn and temporarily stores it internally in the utility. + // The mesh data persists only until the next call to getMeshFromHull + virtual LLCDResult getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut ) = 0; + + // Takes meshIn, generates a single convex hull from it, converts that to a mesh + // stored internally, and populates meshOut to reference the internally stored data. + // The data is persistent only until the next call to generateSingleHullMeshFromMesh + virtual LLCDResult generateSingleHullMeshFromMesh( LLCDMeshData* meshIn, LLCDMeshData* meshOut) = 0; + + // + /// Debug + virtual void loadMeshData(const char* fileIn, LLCDMeshData** meshDataOut) = 0; + +private: + static bool s_isInitialized; +}; + +#endif //LL_CONVEX_DECOMPOSITION + diff --git a/indra/llconvexdecomposition/llconvexdecompositionvhacd.cpp b/indra/llconvexdecomposition/llconvexdecompositionvhacd.cpp new file mode 100644 index 0000000000..78876f9f36 --- /dev/null +++ b/indra/llconvexdecomposition/llconvexdecompositionvhacd.cpp @@ -0,0 +1,492 @@ +/** +* @file llconvexdecompositionvhacd.cpp +* @author rye@alchemyviewer.org +* @brief A VHACD based implementation of LLConvexDecomposition +* +* $LicenseInfo:firstyear=2025&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2025, 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 "linden_common.h" + +#include "llmath.h" +#include "v3math.h" + +#include +#include + +#define ENABLE_VHACD_IMPLEMENTATION 1 +#include "VHACD.h" + +#include "llconvexdecompositionvhacd.h" + +constexpr S32 MAX_HULLS = 256; +constexpr S32 MAX_VERTICES_PER_HULL = 256; + +bool LLConvexDecompositionVHACD::isFunctional() +{ + return true; +} + +LLConvexDecomposition* LLConvexDecompositionVHACD::getInstance() +{ + return LLSimpleton::getInstance(); +} + +LLCDResult LLConvexDecompositionVHACD::initSystem() +{ + createInstance(); + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::initThread() +{ + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::quitThread() +{ + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::quitSystem() +{ + deleteSingleton(); + return LLCD_OK; +} + +LLConvexDecompositionVHACD::LLConvexDecompositionVHACD() +{ + //Create our vhacd instance and setup default parameters + mVHACD = VHACD::CreateVHACD(); + + mVHACDParameters.m_callback = &mVHACDCallback; + mVHACDParameters.m_logger = &mVHACDLogger; + + mDecompStages[0].mName = "Analyze"; + mDecompStages[0].mDescription = nullptr; + + LLCDParam param; + param.mName = "Fill Mode"; + param.mDescription = nullptr; + param.mType = LLCDParam::LLCD_ENUM; + param.mDetails.mEnumValues.mNumEnums = 3; + + static LLCDParam::LLCDEnumItem fill_enums[3]; + fill_enums[(size_t)VHACD::FillMode::FLOOD_FILL].mName = "Flood"; + fill_enums[(size_t)VHACD::FillMode::FLOOD_FILL].mValue = (int)VHACD::FillMode::FLOOD_FILL; + fill_enums[(size_t)VHACD::FillMode::SURFACE_ONLY].mName = "Surface Only"; + fill_enums[(size_t)VHACD::FillMode::SURFACE_ONLY].mValue = (int)VHACD::FillMode::SURFACE_ONLY; + fill_enums[(size_t)VHACD::FillMode::RAYCAST_FILL].mName = "Raycast"; + fill_enums[(size_t)VHACD::FillMode::RAYCAST_FILL].mValue = (int)VHACD::FillMode::RAYCAST_FILL; + + param.mDetails.mEnumValues.mEnumsArray = fill_enums; + param.mDefault.mIntOrEnumValue = (int)VHACD::FillMode::FLOOD_FILL; + param.mStage = 0; + param.mReserved = -1; + mDecompParams.push_back(param); + + enum EVoxelQualityLevels + { + E_LOW_QUALITY = 0, + E_NORMAL_QUALITY, + E_HIGH_QUALITY, + E_VERY_HIGH_QUALITY, + E_ULTRA_QUALITY, + E_MAX_QUALITY, + E_NUM_QUALITY_LEVELS + }; + + param.mName = "Voxel Resolution"; + param.mDescription = nullptr; + param.mType = LLCDParam::LLCD_ENUM; + param.mDetails.mEnumValues.mNumEnums = E_NUM_QUALITY_LEVELS; + + static LLCDParam::LLCDEnumItem voxel_quality_enums[E_NUM_QUALITY_LEVELS]; + voxel_quality_enums[E_LOW_QUALITY].mName = "Low"; + voxel_quality_enums[E_LOW_QUALITY].mValue = 200000; + voxel_quality_enums[E_NORMAL_QUALITY].mName = "Normal"; + voxel_quality_enums[E_NORMAL_QUALITY].mValue = 400000; + voxel_quality_enums[E_HIGH_QUALITY].mName = "High"; + voxel_quality_enums[E_HIGH_QUALITY].mValue = 800000; + voxel_quality_enums[E_VERY_HIGH_QUALITY].mName = "Very High"; + voxel_quality_enums[E_VERY_HIGH_QUALITY].mValue = 1200000; + voxel_quality_enums[E_ULTRA_QUALITY].mName = "Ultra"; + voxel_quality_enums[E_ULTRA_QUALITY].mValue = 1600000; + voxel_quality_enums[E_MAX_QUALITY].mName = "Maximum"; + voxel_quality_enums[E_MAX_QUALITY].mValue = 2000000; + + param.mDetails.mEnumValues.mEnumsArray = voxel_quality_enums; + param.mDefault.mIntOrEnumValue = 400000; + param.mStage = 0; + param.mReserved = -1; + mDecompParams.push_back(param); + + param.mName = "Num Hulls"; + param.mDescription = nullptr; + param.mType = LLCDParam::LLCD_FLOAT; + param.mDetails.mRange.mLow.mFloat = 1.f; + param.mDetails.mRange.mHigh.mFloat = MAX_HULLS; + param.mDetails.mRange.mDelta.mFloat = 1.f; + param.mDefault.mFloat = 8.f; + param.mStage = 0; + param.mReserved = -1; + mDecompParams.push_back(param); + + param.mName = "Num Vertices"; + param.mDescription = nullptr; + param.mType = LLCDParam::LLCD_FLOAT; + param.mDetails.mRange.mLow.mFloat = 3.f; + param.mDetails.mRange.mHigh.mFloat = MAX_VERTICES_PER_HULL; + param.mDetails.mRange.mDelta.mFloat = 1.f; + param.mDefault.mFloat = 32.f; + param.mStage = 0; + param.mReserved = -1; + mDecompParams.push_back(param); + + param.mName = "Error Tolerance"; + param.mDescription = nullptr; + param.mType = LLCDParam::LLCD_FLOAT; + param.mDetails.mRange.mLow.mFloat = 0.0001f; + param.mDetails.mRange.mHigh.mFloat = 99.f; + param.mDetails.mRange.mDelta.mFloat = 0.001f; + param.mDefault.mFloat = 1.f; + param.mStage = 0; + param.mReserved = -1; + mDecompParams.push_back(param); + + for (const LLCDParam& param : mDecompParams) + { + const char* const name = param.mName; + + switch (param.mType) + { + case LLCDParam::LLCD_FLOAT: + { + setParam(name, param.mDefault.mFloat); + break; + } + case LLCDParam::LLCD_ENUM: + case LLCDParam::LLCD_INTEGER: + { + setParam(name, param.mDefault.mIntOrEnumValue); + break; + } + case LLCDParam::LLCD_BOOLEAN: + { + setParam(name, (param.mDefault.mBool != 0)); + break; + } + case LLCDParam::LLCD_INVALID: + default: + { + break; + } + } + } +} + +LLConvexDecompositionVHACD::~LLConvexDecompositionVHACD() +{ + mBoundDecomp = nullptr; + mDecompData.clear(); + + mVHACD->Release(); +} + +void LLConvexDecompositionVHACD::genDecomposition(int& decomp) +{ + int new_decomp_id = static_cast(mDecompData.size()) + 1; + mDecompData[new_decomp_id] = LLDecompData(); + decomp = new_decomp_id; +} + +void LLConvexDecompositionVHACD::deleteDecomposition(int decomp) +{ + auto iter = mDecompData.find(decomp); + if (iter != mDecompData.end()) + { + if (mBoundDecomp == &iter->second) + { + mBoundDecomp = nullptr; + } + mDecompData.erase(iter); + } +} + +void LLConvexDecompositionVHACD::bindDecomposition(int decomp) +{ + auto iter = mDecompData.find(decomp); + if (iter != mDecompData.end()) + { + mBoundDecomp = &iter->second; + } + else + { + LL_WARNS() << "Failed to bind unknown decomposition: " << decomp << LL_ENDL; + mBoundDecomp = nullptr; + } +} + +LLCDResult LLConvexDecompositionVHACD::setParam(const char* name, float val) +{ + if (name == std::string("Num Hulls")) + { + mVHACDParameters.m_maxConvexHulls = llclamp(ll_round(val), 1, MAX_HULLS); + } + else if (name == std::string("Num Vertices")) + { + mVHACDParameters.m_maxNumVerticesPerCH = llclamp(ll_round(val), 3, MAX_VERTICES_PER_HULL); + } + else if (name == std::string("Error Tolerance")) + { + mVHACDParameters.m_minimumVolumePercentErrorAllowed = val; + } + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::setParam(const char* name, bool val) +{ + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::setParam(const char* name, int val) +{ + if (name == std::string("Fill Mode")) + { + mVHACDParameters.m_fillMode = (VHACD::FillMode)val; + } + else if (name == std::string("Voxel Resolution")) + { + mVHACDParameters.m_resolution = val; + } + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::setMeshData( const LLCDMeshData* data, bool vertex_based ) +{ + if (!mBoundDecomp) + { + return LLCD_NULL_PTR; + } + + return mBoundDecomp->mSourceMesh.from(data, vertex_based); +} + +LLCDResult LLConvexDecompositionVHACD::registerCallback(int stage, llcdCallbackFunc callback ) +{ + if (stage == 0) + { + mVHACDCallback.setCallbackFunc(callback); + return LLCD_OK; + } + else + { + return LLCD_INVALID_STAGE; + } +} + +LLCDResult LLConvexDecompositionVHACD::executeStage(int stage) +{ + if (!mBoundDecomp) + { + return LLCD_NULL_PTR; + } + + if (stage != 0) + { + return LLCD_INVALID_STAGE; + } + + mBoundDecomp->mDecomposedHulls.clear(); + + const auto& decomp_mesh = mBoundDecomp->mSourceMesh; + if (!mVHACD->Compute((const double* const)decomp_mesh.mVertices.data(), static_cast(decomp_mesh.mVertices.size()), (const uint32_t* const)decomp_mesh.mIndices.data(), static_cast(decomp_mesh.mIndices.size()), mVHACDParameters)) + { + return LLCD_INVALID_HULL_DATA; + } + + uint32_t num_nulls = mVHACD->GetNConvexHulls(); + if (num_nulls == 0) + { + return LLCD_INVALID_HULL_DATA; + } + + for (uint32_t i = 0; num_nulls > i; ++i) + { + VHACD::IVHACD::ConvexHull ch; + if (!mVHACD->GetConvexHull(i, ch)) + continue; + + LLConvexMesh out_mesh; + out_mesh.setVertices(ch.m_points); + out_mesh.setIndices(ch.m_triangles); + + mBoundDecomp->mDecomposedHulls.push_back(std::move(out_mesh)); + } + + mVHACD->Clean(); + + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::buildSingleHull() +{ + LL_INFOS() << "Building single hull mesh" << LL_ENDL; + if (!mBoundDecomp || mBoundDecomp->mSourceMesh.mVertices.empty()) + { + return LLCD_NULL_PTR; + } + + mBoundDecomp->mSingleHullMesh.clear(); + + VHACD::QuickHull quickhull; + uint32_t num_tris = quickhull.ComputeConvexHull(mBoundDecomp->mSourceMesh.mVertices, MAX_VERTICES_PER_HULL); + if (num_tris > 0) + { + mBoundDecomp->mSingleHullMesh.setVertices(quickhull.GetVertices()); + mBoundDecomp->mSingleHullMesh.setIndices(quickhull.GetIndices()); + + return LLCD_OK; + } + + return LLCD_INVALID_MESH_DATA; +} + +int LLConvexDecompositionVHACD::getNumHullsFromStage(int stage) +{ + if (!mBoundDecomp || stage != 0) + { + return 0; + } + + return narrow(mBoundDecomp->mDecomposedHulls.size()); +} + +LLCDResult LLConvexDecompositionVHACD::getSingleHull( LLCDHull* hullOut ) +{ + memset( hullOut, 0, sizeof(LLCDHull) ); + + if (!mBoundDecomp) + { + return LLCD_NULL_PTR; + } + + if (mBoundDecomp->mSingleHullMesh.vertices.empty()) + { + return LLCD_INVALID_HULL_DATA; + } + + mBoundDecomp->mSingleHullMesh.to(hullOut); + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::getHullFromStage( int stage, int hull, LLCDHull* hullOut ) +{ + memset( hullOut, 0, sizeof(LLCDHull) ); + + if (!mBoundDecomp) + { + return LLCD_NULL_PTR; + } + + if (stage != 0) + { + return LLCD_INVALID_STAGE; + } + + if (mBoundDecomp->mDecomposedHulls.empty() || mBoundDecomp->mDecomposedHulls.size() <= hull) + { + return LLCD_REQUEST_OUT_OF_RANGE; + } + + mBoundDecomp->mDecomposedHulls[hull].to(hullOut); + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut ) +{ + memset( meshDataOut, 0, sizeof(LLCDMeshData)); + if (!mBoundDecomp) + { + return LLCD_NULL_PTR; + } + + if (stage != 0) + { + return LLCD_INVALID_STAGE; + } + + if (mBoundDecomp->mDecomposedHulls.empty() || mBoundDecomp->mDecomposedHulls.size() <= hull) + { + return LLCD_REQUEST_OUT_OF_RANGE; + } + + mBoundDecomp->mDecomposedHulls[hull].to(meshDataOut); + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut ) +{ + memset(meshOut, 0, sizeof(LLCDMeshData)); + + LLVHACDMesh inMesh(hullIn); + + VHACD::QuickHull quickhull; + uint32_t num_tris = quickhull.ComputeConvexHull(inMesh.mVertices, MAX_VERTICES_PER_HULL); + if (num_tris > 0) + { + mMeshFromHullData.setVertices(quickhull.GetVertices()); + mMeshFromHullData.setIndices(quickhull.GetIndices()); + + mMeshFromHullData.to(meshOut); + return LLCD_OK; + } + + return LLCD_INVALID_HULL_DATA; +} + +LLCDResult LLConvexDecompositionVHACD::generateSingleHullMeshFromMesh(LLCDMeshData* meshIn, LLCDMeshData* meshOut) +{ + memset( meshOut, 0, sizeof(LLCDMeshData) ); + + LLVHACDMesh inMesh(meshIn, true); + + VHACD::QuickHull quickhull; + uint32_t num_tris = quickhull.ComputeConvexHull(inMesh.mVertices, MAX_VERTICES_PER_HULL); + if (num_tris > 0) + { + mSingleHullMeshFromMeshData.setVertices(quickhull.GetVertices()); + mSingleHullMeshFromMeshData.setIndices(quickhull.GetIndices()); + + mSingleHullMeshFromMeshData.to(meshOut); + return LLCD_OK; + } + + return LLCD_INVALID_MESH_DATA; +} + +void LLConvexDecompositionVHACD::loadMeshData(const char* fileIn, LLCDMeshData** meshDataOut) +{ + static LLCDMeshData meshData; + memset( &meshData, 0, sizeof(LLCDMeshData) ); + *meshDataOut = &meshData; +} diff --git a/indra/llconvexdecomposition/llconvexdecompositionvhacd.h b/indra/llconvexdecomposition/llconvexdecompositionvhacd.h new file mode 100644 index 0000000000..675356629c --- /dev/null +++ b/indra/llconvexdecomposition/llconvexdecompositionvhacd.h @@ -0,0 +1,339 @@ +/** +* @file llconvexdecompositionvhacd.h +* @author rye@alchemyviewer.org +* @brief A VHACD based implementation of LLConvexDecomposition +* +* $LicenseInfo:firstyear=2025&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2025, 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_CONVEX_DECOMP_UTIL_VHACD_H +#define LL_CONVEX_DECOMP_UTIL_VHACD_H + +#include "llconvexdecomposition.h" +#include "llsingleton.h" +#include "llmath.h" + +#include + +#include "VHACD.h" + +class LLDecompDataVHACD; + +class LLConvexDecompositionVHACD : public LLSimpleton, public LLConvexDecomposition +{ + class VHACDCallback : public VHACD::IVHACD::IUserCallback + { + public: + void Update(const double overallProgress, const double stageProgress, const char* const stage, const char* operation) override + { + std::string out_msg = llformat("Stage: %s Operation: %s", stage, operation); + if (mCurrentStage != stage && mCurrentOperation != operation) + { + mCurrentStage = stage; + mCurrentOperation = operation; + LL_INFOS("VHACD") << out_msg << LL_ENDL; + } + + if(mCallbackFunc) + { + mCallbackFunc(out_msg.c_str(), ll_round(static_cast(stageProgress)), ll_round(static_cast(overallProgress))); + } + } + + void setCallbackFunc(llcdCallbackFunc func) + { + mCallbackFunc = func; + } + + private: + std::string mCurrentStage; + std::string mCurrentOperation; + llcdCallbackFunc mCallbackFunc = nullptr; + }; + + class VHACDLogger : public VHACD::IVHACD::IUserLogger + { + void Log(const char* const msg) override + { + LL_INFOS("VHACD") << msg << LL_ENDL; + } + }; + +public: + + LLConvexDecompositionVHACD(); + virtual ~LLConvexDecompositionVHACD(); + + static bool isFunctional(); + static LLConvexDecomposition* getInstance(); + static LLCDResult initSystem(); + static LLCDResult initThread(); + static LLCDResult quitThread(); + static LLCDResult quitSystem(); + + void genDecomposition(int& decomp); + void deleteDecomposition(int decomp); + void bindDecomposition(int decomp); + + // Sets *paramsOut to the address of the LLCDParam array and returns + // the length of the array + int getParameters(const LLCDParam** paramsOut) + { + *paramsOut = mDecompParams.data(); + return narrow(mDecompParams.size()); + } + + int getStages(const LLCDStageData** stagesOut) + { + *stagesOut = mDecompStages.data(); + return narrow(mDecompStages.size()); + } + + // Set a parameter by name. Returns false if out of bounds or unsupported parameter + LLCDResult setParam(const char* name, float val); + LLCDResult setParam(const char* name, int val); + LLCDResult setParam(const char* name, bool val); + LLCDResult setMeshData( const LLCDMeshData* data, bool vertex_based ); + LLCDResult registerCallback(int stage, llcdCallbackFunc callback ); + + LLCDResult executeStage(int stage); + LLCDResult buildSingleHull(); + + int getNumHullsFromStage(int stage); + + LLCDResult getHullFromStage( int stage, int hull, LLCDHull* hullOut ); + LLCDResult getSingleHull( LLCDHull* hullOut ) ; + + // TODO: Implement lock of some kind to disallow this call if data not yet ready + LLCDResult getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut); + LLCDResult getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut ); + + // For visualizing convex hull shapes in the viewer physics shape display + LLCDResult generateSingleHullMeshFromMesh( LLCDMeshData* meshIn, LLCDMeshData* meshOut); + + /// Debug + void loadMeshData(const char* fileIn, LLCDMeshData** meshDataOut); + +private: + std::vector mDecompParams; + std::array mDecompStages; + + struct LLVHACDMesh + { + using vertex_type = VHACD::Vertex; + using index_type = VHACD::Triangle; + using vertex_array_type = std::vector; + using index_array_type = std::vector; + + LLVHACDMesh() = default; + LLVHACDMesh(const LLCDHull* hullIn) + { + if (hullIn) + { + from(hullIn); + } + }; + + LLVHACDMesh(const LLCDMeshData* meshIn, bool vertex_based) + { + if (meshIn) + { + from(meshIn, vertex_based); + } + }; + + void clear() + { + mVertices.clear(); + mIndices.clear(); + } + + void setVertices(const float* data, int num_vertices, int vertex_stride_bytes) + { + vertex_array_type vertices; + vertices.reserve(num_vertices); + + const int stride = vertex_stride_bytes / sizeof(float); + for (int i = 0; i < num_vertices; ++i) + { + vertices.emplace_back(data[i * stride + 0], + data[i * stride + 1], + data[i * stride + 2]); + } + + mVertices = std::move(vertices); + } + + void setIndices(const void* data, int num_indices, int index_stride_bytes, LLCDMeshData::IndexType type) + { + index_array_type indices; + indices.reserve(num_indices); + + if (type == LLCDMeshData::INT_16) + { + const U16* index_data = static_cast(data); + const int stride = index_stride_bytes / sizeof(U16); + for (int i = 0; i < num_indices; ++i) + { + indices.emplace_back(index_data[i * stride + 0], + index_data[i * stride + 1], + index_data[i * stride + 2]); + } + } + else + { + const U32* index_data = static_cast(data); + const int stride = index_stride_bytes / sizeof(U32); + for (int i = 0; i < num_indices; ++i) + { + indices.emplace_back(index_data[i * stride + 0], + index_data[i * stride + 1], + index_data[i * stride + 2]); + } + } + + mIndices = std::move(indices); + } + + LLCDResult from(const LLCDHull* hullIn) + { + clear(); + + if (!hullIn || !hullIn->mVertexBase || (hullIn->mNumVertices < 3) || (hullIn->mVertexStrideBytes != 12 && hullIn->mVertexStrideBytes != 16)) + { + return LLCD_INVALID_HULL_DATA; + } + + setVertices(hullIn->mVertexBase, hullIn->mNumVertices, hullIn->mVertexStrideBytes); + + return LLCD_OK; + } + + LLCDResult from(const LLCDMeshData* meshIn, bool vertex_based) + { + clear(); + + if (!meshIn || !meshIn->mVertexBase || (meshIn->mNumVertices < 3) || (meshIn->mVertexStrideBytes != 12 && meshIn->mVertexStrideBytes != 16)) + { + return LLCD_INVALID_MESH_DATA; + } + + if (!vertex_based && ((meshIn->mNumTriangles < 1) || !meshIn->mIndexBase)) + { + return LLCD_INVALID_MESH_DATA; + } + + setVertices(meshIn->mVertexBase, meshIn->mNumVertices, meshIn->mVertexStrideBytes); + if(!vertex_based) + { + setIndices(meshIn->mIndexBase, meshIn->mNumTriangles, meshIn->mIndexStrideBytes, meshIn->mIndexType); + } + + return LLCD_OK; + } + + vertex_array_type mVertices; + index_array_type mIndices; + }; + + struct LLConvexMesh + { + using vertex_type = glm::vec3; + using index_type = glm::u32vec3; + using vertex_array_type = std::vector; + using index_array_type = std::vector; + + LLConvexMesh() = default; + + void clear() + { + vertices.clear(); + indices.clear(); + } + + void setVertices(const std::vector& in_vertices) + { + vertices.clear(); + vertices.reserve(in_vertices.size()); + + for (const auto& vertex : in_vertices) + { + vertices.emplace_back(narrow(vertex.mX), narrow(vertex.mY), narrow(vertex.mZ)); + } + } + + void setIndices(const std::vector& in_indices) + { + indices.clear(); + indices.reserve(in_indices.size()); + + for (const auto& triangle : in_indices) + { + indices.emplace_back(narrow(triangle.mI0), narrow(triangle.mI1), narrow(triangle.mI2)); + } + } + + void to(LLCDHull* meshOut) const + { + meshOut->mVertexBase = (float*)vertices.data(); + meshOut->mVertexStrideBytes = sizeof(vertex_type); + meshOut->mNumVertices = (int)vertices.size(); + } + + void to(LLCDMeshData* meshOut) const + { + meshOut->mVertexBase = (float*)vertices.data(); + meshOut->mVertexStrideBytes = sizeof(vertex_type); + meshOut->mNumVertices = (int)vertices.size(); + + meshOut->mIndexType = LLCDMeshData::INT_32; + meshOut->mIndexBase = indices.data(); + meshOut->mIndexStrideBytes = sizeof(index_type); + meshOut->mNumTriangles = (int)indices.size(); + } + + vertex_array_type vertices; + index_array_type indices; + }; + + struct LLDecompData + { + LLVHACDMesh mSourceMesh; + LLConvexMesh mSingleHullMesh; + + std::vector mDecomposedHulls; + }; + + std::unordered_map mDecompData; + + LLDecompData* mBoundDecomp = nullptr; + + VHACD::IVHACD* mVHACD = nullptr; + VHACDCallback mVHACDCallback; + VHACDLogger mVHACDLogger; + VHACD::IVHACD::Parameters mVHACDParameters; + + LLConvexMesh mMeshFromHullData; + LLConvexMesh mSingleHullMeshFromMeshData; +}; + +#endif //LL_CONVEX_DECOMP_UTIL_VHACD_H diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt index e13f0bbd96..9e90314a51 100644 --- a/indra/llprimitive/CMakeLists.txt +++ b/indra/llprimitive/CMakeLists.txt @@ -70,6 +70,12 @@ target_link_libraries(llprimitive ll::glm ) +if (TARGET llconvexdecomposition) + target_link_libraries(llprimitive + llconvexdecomposition + ) +endif () + #add unit tests if (LL_TESTS) INCLUDE(LLAddBuildTest) diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index 00ef79ce7f..8055bffd32 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -1296,10 +1296,10 @@ LLModel::weight_list& LLModel::getJointInfluences(const LLVector3& pos) } void LLModel::setConvexHullDecomposition( - const LLModel::convex_hull_decomposition& decomp) + const LLModel::convex_hull_decomposition& decomp, const std::vector& decomp_mesh) { mPhysics.mHull = decomp; - mPhysics.mMesh.clear(); + mPhysics.mMesh = decomp_mesh; updateHullCenters(); } diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h index 6501b3dc50..ac88af18f0 100644 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -305,7 +305,8 @@ public: S32 mDecompID; void setConvexHullDecomposition( - const convex_hull_decomposition& decomp); + const convex_hull_decomposition& decomp, + const std::vector& decomp_mesh); void updateHullCenters(); LLVector3 mCenterOfHullCenters; diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c727d5ae57..0f7670a57a 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1709,10 +1709,6 @@ if (WINDOWS) list(APPEND viewer_SOURCE_FILES ${viewer_INSTALLER_FILES}) endif (WINDOWS) -if (HAVOK OR HAVOK_TPV) - set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_HAVOK") -endif (HAVOK OR HAVOK_TPV) - if( DEFINED LLSTARTUP_COMPILE_FLAGS ) # progress view disables/enables icons based on available packages set_source_files_properties(llprogressview.cpp PROPERTIES COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS}") @@ -2029,6 +2025,10 @@ if( TARGET ll::nvapi ) target_link_libraries(${VIEWER_BINARY_NAME} ll::nvapi ) endif() +if ( TARGET llconvexdecomposition ) + target_link_libraries(${VIEWER_BINARY_NAME} llconvexdecomposition ) +endif () + set(ARTWORK_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH "Path to artwork files.") diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index f76f39222b..ef29be8200 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -1035,8 +1035,13 @@ void LLFloaterModelPreview::onPhysicsStageExecute(LLUICtrl* ctrl, void* data) gMeshRepo.mDecompThread->submitRequest(request); } } - - if (stage == "Decompose") + if (stage == "Analyze") + { + sInstance->setStatusMessage(sInstance->getString("decomposing")); + sInstance->childSetVisible("Analyze", false); + sInstance->childSetVisible("analyze_cancel", true); + } + else if (stage == "Decompose") { sInstance->setStatusMessage(sInstance->getString("decomposing")); sInstance->childSetVisible("Decompose", false); @@ -1137,6 +1142,7 @@ void LLFloaterModelPreview::initDecompControls() childSetCommitCallback("simplify_cancel", onPhysicsStageCancel, NULL); childSetCommitCallback("decompose_cancel", onPhysicsStageCancel, NULL); + childSetCommitCallback("analyze_cancel", onPhysicsStageCancel, NULL); childSetCommitCallback("physics_lod_combo", onPhysicsUseLOD, NULL); childSetCommitCallback("physics_browse", onPhysicsBrowse, NULL); @@ -2018,7 +2024,7 @@ void LLFloaterModelPreview::DecompRequest::completed() { //called from the main thread if (mContinue) { - mModel->setConvexHullDecomposition(mHull); + mModel->setConvexHullDecomposition(mHull, mHullMesh); if (sInstance) { diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index e0649e1d88..d71f368232 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -2626,7 +2626,16 @@ void LLModelPreview::updateStatusMessages() //enable = enable && !use_hull && fmp->childGetValue("physics_optimize").asBoolean(); //enable/disable "analysis" UI - LLPanel* panel = fmp->getChild("physics analysis"); +#if LL_HAVOK + LLPanel* panel = fmp->getChild("physics simplification"); + panel->setVisible(true); + + panel = fmp->getChild("physics analysis havok"); + panel->setVisible(true); +#else + LLPanel* panel = fmp->getChild("physics analysis vhacd"); + panel->setVisible(true); +#endif LLView* child = panel->getFirstChild(); while (child) { @@ -2650,6 +2659,8 @@ void LLModelPreview::updateStatusMessages() fmp->childSetVisible("simplify_cancel", false); fmp->childSetVisible("Decompose", true); fmp->childSetVisible("decompose_cancel", false); + fmp->childSetVisible("Analyze", true); + fmp->childSetVisible("analyze_cancel", false); if (phys_hulls > 0) { @@ -2659,6 +2670,7 @@ void LLModelPreview::updateStatusMessages() if (phys_tris || phys_hulls > 0) { fmp->childEnable("Decompose"); + fmp->childEnable("Analyze"); } } else diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index d72d428c08..fb9e5fd0a9 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -1070,7 +1070,7 @@ void LLViewerObjectList::fetchObjectCostsCoro(std::string url) if (diff.empty()) { - LL_INFOS() << "No outstanding object IDs to request. Pending count: " << mPendingObjectCost.size() << LL_ENDL; + LL_DEBUGS() << "No outstanding object IDs to request. Pending count: " << mPendingObjectCost.size() << LL_ENDL; return; } @@ -1205,7 +1205,7 @@ void LLViewerObjectList::fetchPhisicsFlagsCoro(std::string url) if (idList.size() < 1) { - LL_INFOS() << "No outstanding object physics flags to request." << LL_ENDL; + LL_DEBUGS() << "No outstanding object physics flags to request." << LL_ENDL; return; } 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 f11d687840..39e9de0980 100644 --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml @@ -40,7 +40,7 @@ Simplifying... TBD One or more textures in this model were scaled to be within the allowed limits. - + Skinning disabled due to too many joints: [JOINTS], maximum: [MAX] Rigged to unrecognized joint name [NAME] @@ -807,7 +807,7 @@ help_topic="upload_model_physics" label="Physics" name="physics_panel"> - + --> - + - + + + Step 2: Convert to hulls (optional) + + + Fill Mode: + + + Resolution: + + + Hulls per Mesh: + + + Vertices per hull: + + + Error tolerance: + + + + + + +