summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/CMakeLists.txt6
-rw-r--r--indra/newview/gltfscenemanager.cpp2
-rw-r--r--indra/newview/llcurrencyuimanager.cpp137
-rw-r--r--indra/newview/llfloateravatar.cpp5
-rw-r--r--indra/newview/llfloaterbuyland.cpp201
-rw-r--r--indra/newview/llfloaterdestinations.cpp5
-rw-r--r--indra/newview/llfloaterfonttest.cpp5
-rw-r--r--indra/newview/llfloatergltfasseteditor.cpp527
-rw-r--r--indra/newview/llfloatergltfasseteditor.h101
-rw-r--r--indra/newview/llgltffolderitem.cpp164
-rw-r--r--indra/newview/llgltffolderitem.h128
-rw-r--r--indra/newview/llgltffoldermodel.cpp73
-rw-r--r--indra/newview/llgltffoldermodel.h91
-rw-r--r--indra/newview/lllogininstance.cpp5
-rw-r--r--indra/newview/llslurl.cpp6
-rw-r--r--indra/newview/llversioninfo.cpp22
-rw-r--r--indra/newview/llversioninfo.h26
-rw-r--r--indra/newview/llviewerfloaterreg.cpp2
-rw-r--r--indra/newview/llviewermenu.cpp10
-rw-r--r--indra/newview/llvoicewebrtc.cpp1
-rw-r--r--indra/newview/llxmlrpclistener.cpp278
-rw-r--r--indra/newview/llxmlrpclistener.h3
-rw-r--r--indra/newview/llxmlrpctransaction.cpp361
-rw-r--r--indra/newview/llxmlrpctransaction.h92
-rw-r--r--indra/newview/skins/default/xui/da/floater_about.xml1
-rw-r--r--indra/newview/skins/default/xui/de/floater_about.xml1
-rw-r--r--indra/newview/skins/default/xui/en/floater_about.xml1
-rw-r--r--indra/newview/skins/default/xui/en/floater_gltf_asset_editor.xml284
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml8
-rw-r--r--indra/newview/skins/default/xui/es/floater_about.xml1
-rw-r--r--indra/newview/skins/default/xui/fr/floater_about.xml1
-rw-r--r--indra/newview/skins/default/xui/it/floater_about.xml1
-rw-r--r--indra/newview/skins/default/xui/ja/floater_about.xml1
-rw-r--r--indra/newview/skins/default/xui/pt/floater_about.xml1
-rw-r--r--indra/newview/skins/default/xui/ru/floater_about.xml1
-rw-r--r--indra/newview/skins/default/xui/tr/floater_about.xml1
-rw-r--r--indra/newview/skins/default/xui/zh/floater_about.xml1
37 files changed, 1756 insertions, 798 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 3aaeb426da..cbcc85cc1c 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -219,6 +219,7 @@ set(viewer_SOURCE_FILES
llfloaterfonttest.cpp
llfloaterforgetuser.cpp
llfloatergesture.cpp
+ llfloatergltfasseteditor.cpp
llfloatergodtools.cpp
llfloatergotoline.cpp
llfloatergridstatus.cpp
@@ -313,6 +314,8 @@ set(viewer_SOURCE_FILES
llgesturemgr.cpp
llgiveinventory.cpp
llglsandbox.cpp
+ llgltffolderitem.cpp
+ llgltffoldermodel.cpp
llgltfmateriallist.cpp
llgltfmaterialpreviewmgr.cpp
llgroupactions.cpp
@@ -879,6 +882,7 @@ set(viewer_HEADER_FILES
llfloaterfonttest.h
llfloaterforgetuser.h
llfloatergesture.h
+ llfloatergltfasseteditor.h
llfloatergodtools.h
llfloatergotoline.h
llfloatergridstatus.h
@@ -975,6 +979,8 @@ set(viewer_HEADER_FILES
llgesturelistener.h
llgesturemgr.h
llgiveinventory.h
+ llgltffolderitem.h
+ llgltffoldermodel.h
llgltfmateriallist.h
llgltfmaterialpreviewmgr.h
llgroupactions.h
diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp
index a0cbe9290c..b161ec8492 100644
--- a/indra/newview/gltfscenemanager.cpp
+++ b/indra/newview/gltfscenemanager.cpp
@@ -42,6 +42,7 @@
#include "llviewertexturelist.h"
#include "llimagej2c.h"
#include "llfloaterperms.h"
+#include "llfloaterreg.h"
#include "llagentbenefits.h"
#include "llfilesystem.h"
#include "boost/json.hpp"
@@ -314,6 +315,7 @@ void GLTFSceneManager::load(const std::string& filename)
{
mObjects.push_back(obj);
}
+ LLFloaterReg::showInstance("gltf_asset_editor");
}
}
else
diff --git a/indra/newview/llcurrencyuimanager.cpp b/indra/newview/llcurrencyuimanager.cpp
index 06c87343e2..8a4ab091a3 100644
--- a/indra/newview/llcurrencyuimanager.cpp
+++ b/indra/newview/llcurrencyuimanager.cpp
@@ -111,16 +111,17 @@ public:
bool hasEstimate() const;
std::string getLocalEstimate() const;
- void startTransaction(TransactionType type,
- const char* method, LLXMLRPCValue params);
+ void startTransaction(TransactionType type, const char* method, const LLSD& params);
+
+ // return true if update needed
bool checkTransaction();
- // return true if update needed
void setError(const std::string& message, const std::string& uri);
void clearError();
+ // return true if update needed
bool considerUpdateCurrency();
- // return true if update needed
+
void currencyKey(S32);
static void onCurrencyKey(LLLineEditor* caller, void* data);
@@ -160,32 +161,29 @@ void LLCurrencyUIManager::Impl::updateCurrencyInfo()
return;
}
- LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct();
- keywordArgs.appendString("agentId", gAgent.getID().asString());
- keywordArgs.appendString(
- "secureSessionId",
- gAgent.getSecureSessionID().asString());
- keywordArgs.appendString("language", LLUI::getLanguage());
- keywordArgs.appendInt("currencyBuy", mUserCurrencyBuy);
- keywordArgs.appendString("viewerChannel", LLVersionInfo::instance().getChannel());
- keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::instance().getMajor());
- keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::instance().getMinor());
- keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::instance().getPatch());
+ const LLVersionInfo& vi(LLVersionInfo::instance());
+
+ LLSD params = LLSD::emptyMap();
+ params["agentId"] = gAgent.getID().asString();
+ params["secureSessionId"] = gAgent.getSecureSessionID().asString();
+ params["language"] = LLUI::getLanguage();
+ params["currencyBuy"] = mUserCurrencyBuy;
+ params["viewerChannel"] = vi.getChannel();
+ params["viewerMajorVersion"] = vi.getMajor();
+ params["viewerMinorVersion"] = vi.getMinor();
+ params["viewerPatchVersion"] = vi.getPatch();
// With GitHub builds, the build number is too big to fit in a 32-bit int,
- // and XMLRPC_VALUE doesn't deal with integers wider than int. Use string.
- keywordArgs.appendString("viewerBuildVersion", stringize(LLVersionInfo::instance().getBuild()));
-
- LLXMLRPCValue params = LLXMLRPCValue::createArray();
- params.append(keywordArgs);
+ // and XMLRPC value doesn't deal with integers wider than int. Use string.
+ params["viewerBuildVersion"] = std::to_string(vi.getBuild());
startTransaction(TransactionCurrency, "getCurrencyQuote", params);
}
void LLCurrencyUIManager::Impl::finishCurrencyInfo()
{
- LLXMLRPCValue result = mTransaction->responseValue();
+ const LLSD& result = mTransaction->response();
- bool success = result["success"].asBool();
+ bool success = result["success"].asBoolean();
if (!success)
{
setError(
@@ -195,24 +193,24 @@ void LLCurrencyUIManager::Impl::finishCurrencyInfo()
return;
}
- LLXMLRPCValue currency = result["currency"];
+ const LLSD& currency = result["currency"];
// old XML-RPC server: estimatedCost = value in US cents
- mUSDCurrencyEstimated = currency["estimatedCost"].isValid();
+ mUSDCurrencyEstimated = currency.has("estimatedCost");
if (mUSDCurrencyEstimated)
{
- mUSDCurrencyEstimatedCost = currency["estimatedCost"].asInt();
+ mUSDCurrencyEstimatedCost = currency["estimatedCost"].asInteger();
}
// newer XML-RPC server: estimatedLocalCost = local currency string
- mLocalCurrencyEstimated = currency["estimatedLocalCost"].isValid();
+ mLocalCurrencyEstimated = currency.has("estimatedLocalCost");
if (mLocalCurrencyEstimated)
{
mLocalCurrencyEstimatedCost = currency["estimatedLocalCost"].asString();
mSupportsInternationalBilling = true;
}
- S32 newCurrencyBuy = currency["currencyBuy"].asInt();
+ S32 newCurrencyBuy = currency["currencyBuy"].asInteger();
if (newCurrencyBuy != mUserCurrencyBuy)
{
mUserCurrencyBuy = newCurrencyBuy;
@@ -224,36 +222,36 @@ void LLCurrencyUIManager::Impl::finishCurrencyInfo()
void LLCurrencyUIManager::Impl::startCurrencyBuy(const std::string& password)
{
- LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct();
- keywordArgs.appendString("agentId", gAgent.getID().asString());
- keywordArgs.appendString(
- "secureSessionId",
- gAgent.getSecureSessionID().asString());
- keywordArgs.appendString("language", LLUI::getLanguage());
- keywordArgs.appendInt("currencyBuy", mUserCurrencyBuy);
+ const LLVersionInfo& vi(LLVersionInfo::instance());
+
+ LLSD params = LLSD::emptyMap();
+ params["agentId"] = gAgent.getID().asString();
+ params["secureSessionId"] = gAgent.getSecureSessionID().asString();
+ params["language"] = LLUI::getLanguage();
+ params["currencyBuy"] = mUserCurrencyBuy;
+ params["confirm"] = mSiteConfirm;
+ params["viewerChannel"] = vi.getChannel();
+ params["viewerMajorVersion"] = vi.getMajor();
+ params["viewerMinorVersion"] = vi.getMinor();
+ params["viewerPatchVersion"] = vi.getPatch();
+ // With GitHub builds, the build number is too big to fit in a 32-bit int,
+ // and XMLRPC value doesn't deal with integers wider than int. Use string.
+ params["viewerBuildVersion"] = std::to_string(vi.getBuild());
+
if (mUSDCurrencyEstimated)
{
- keywordArgs.appendInt("estimatedCost", mUSDCurrencyEstimatedCost);
+ params["estimatedCost"] = mUSDCurrencyEstimatedCost;
}
+
if (mLocalCurrencyEstimated)
{
- keywordArgs.appendString("estimatedLocalCost", mLocalCurrencyEstimatedCost);
+ params["estimatedLocalCost"] = mLocalCurrencyEstimatedCost;
}
- keywordArgs.appendString("confirm", mSiteConfirm);
+
if (!password.empty())
{
- keywordArgs.appendString("password", password);
+ params["password"] = password;
}
- keywordArgs.appendString("viewerChannel", LLVersionInfo::instance().getChannel());
- keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::instance().getMajor());
- keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::instance().getMinor());
- keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::instance().getPatch());
- // With GitHub builds, the build number is too big to fit in a 32-bit int,
- // and XMLRPC_VALUE doesn't deal with integers wider than int. Use string.
- keywordArgs.appendString("viewerBuildVersion", stringize(LLVersionInfo::instance().getBuild()));
-
- LLXMLRPCValue params = LLXMLRPCValue::createArray();
- params.append(keywordArgs);
startTransaction(TransactionBuy, "buyCurrency", params);
@@ -263,9 +261,9 @@ void LLCurrencyUIManager::Impl::startCurrencyBuy(const std::string& password)
void LLCurrencyUIManager::Impl::finishCurrencyBuy()
{
- LLXMLRPCValue result = mTransaction->responseValue();
+ const LLSD& result = mTransaction->response();
- bool success = result["success"].asBool();
+ bool success = result["success"].asBoolean();
if (!success)
{
setError(
@@ -282,7 +280,7 @@ void LLCurrencyUIManager::Impl::finishCurrencyBuy()
}
void LLCurrencyUIManager::Impl::startTransaction(TransactionType type,
- const char* method, LLXMLRPCValue params)
+ const char* method, const LLSD& params)
{
static std::string transactionURI;
if (transactionURI.empty())
@@ -293,12 +291,7 @@ void LLCurrencyUIManager::Impl::startTransaction(TransactionType type,
delete mTransaction;
mTransactionType = type;
- mTransaction = new LLXMLRPCTransaction(
- transactionURI,
- method,
- params,
- false /* don't use gzip */
- );
+ mTransaction = new LLXMLRPCTransaction(transactionURI, method, params);
clearError();
}
@@ -352,12 +345,17 @@ bool LLCurrencyUIManager::Impl::checkTransaction()
{
setError(mTransaction->statusMessage(), mTransaction->statusURI());
}
- else {
+ else
+ {
switch (mTransactionType)
{
- case TransactionCurrency: finishCurrencyInfo(); break;
- case TransactionBuy: finishCurrencyBuy(); break;
- default: ;
+ case TransactionCurrency:
+ finishCurrencyInfo();
+ break;
+ case TransactionBuy:
+ finishCurrencyBuy();
+ break;
+ default:;
}
}
@@ -385,9 +383,8 @@ void LLCurrencyUIManager::Impl::clearError()
bool LLCurrencyUIManager::Impl::considerUpdateCurrency()
{
- if (mCurrencyChanged
- && !mTransaction
- && mCurrencyKeyTimer.getElapsedTimeF32() >= CURRENCY_ESTIMATE_FREQUENCY)
+ if (mCurrencyChanged && !mTransaction &&
+ mCurrencyKeyTimer.getElapsedTimeF32() >= CURRENCY_ESTIMATE_FREQUENCY)
{
updateCurrencyInfo();
return true;
@@ -408,7 +405,8 @@ void LLCurrencyUIManager::Impl::currencyKey(S32 value)
mUserCurrencyBuy = value;
- if (hasEstimate()) {
+ if (hasEstimate())
+ {
clearEstimate();
//cannot just simply refresh the whole UI, as the edit field will
// get reset and the cursor will change...
@@ -421,8 +419,7 @@ void LLCurrencyUIManager::Impl::currencyKey(S32 value)
}
// static
-void LLCurrencyUIManager::Impl::onCurrencyKey(
- LLLineEditor* caller, void* data)
+void LLCurrencyUIManager::Impl::onCurrencyKey(LLLineEditor* caller, void* data)
{
S32 value = atoi(caller->getText().c_str());
LLCurrencyUIManager::Impl* self = (LLCurrencyUIManager::Impl*)data;
@@ -589,14 +586,12 @@ bool LLCurrencyUIManager::inProcess()
bool LLCurrencyUIManager::canCancel()
{
- return impl.mTransactionType != Impl::TransactionBuy;
+ return !buying();
}
bool LLCurrencyUIManager::canBuy()
{
- return impl.mTransactionType == Impl::TransactionNone
- && impl.hasEstimate()
- && impl.mUserCurrencyBuy > 0;
+ return !inProcess() && impl.hasEstimate() && impl.mUserCurrencyBuy > 0;
}
bool LLCurrencyUIManager::buying()
diff --git a/indra/newview/llfloateravatar.cpp b/indra/newview/llfloateravatar.cpp
index 6a38d7549c..404316275d 100644
--- a/indra/newview/llfloateravatar.cpp
+++ b/indra/newview/llfloateravatar.cpp
@@ -25,11 +25,6 @@
* $/LicenseInfo$
*/
-/**
- * Floater that appears when buying an object, giving a preview
- * of its contents and their permissions.
- */
-
#include "llviewerprecompiledheaders.h"
#include "llfloateravatar.h"
diff --git a/indra/newview/llfloaterbuyland.cpp b/indra/newview/llfloaterbuyland.cpp
index 570a223908..11505e3047 100644
--- a/indra/newview/llfloaterbuyland.cpp
+++ b/indra/newview/llfloaterbuyland.cpp
@@ -182,7 +182,7 @@ public:
void refreshUI();
- void startTransaction(TransactionType type, const LLXMLRPCValue& params);
+ void startTransaction(TransactionType type, const LLSD& params);
bool checkTransaction();
void tellUserError(const std::string& message, const std::string& uri);
@@ -396,11 +396,10 @@ void LLFloaterBuyLandUI::updateParcelInfo()
// Can't have more than region max tasks, regardless of parcel
// object bonus factor.
LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
- if(region)
+ if (region)
{
S32 max_tasks_per_region = (S32)region->getMaxTasks();
- mParcelSupportedObjects = llmin(
- mParcelSupportedObjects, max_tasks_per_region);
+ mParcelSupportedObjects = llmin(mParcelSupportedObjects, max_tasks_per_region);
}
mParcelSoldWithObjects = parcel->getSellWithObjects();
@@ -423,7 +422,7 @@ void LLFloaterBuyLandUI::updateParcelInfo()
// checks that we can buy the land
- if(mIsForGroup && !gAgent.hasPowerInActiveGroup(GP_LAND_DEED))
+ if (mIsForGroup && !gAgent.hasPowerInActiveGroup(GP_LAND_DEED))
{
mCannotBuyReason = getString("cant_buy_for_group");
return;
@@ -492,85 +491,56 @@ void LLFloaterBuyLandUI::updateParcelInfo()
void LLFloaterBuyLandUI::updateCovenantInfo()
{
LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion();
- if(!region) return;
+ if (!region)
+ return;
U8 sim_access = region->getSimAccess();
std::string rating = LLViewerRegion::accessToString(sim_access);
LLTextBox* region_name = getChild<LLTextBox>("region_name_text");
- if (region_name)
- {
- std::string region_name_txt = region->getName() + " ("+rating +")";
- region_name->setText(region_name_txt);
+ std::string region_name_txt = region->getName() + " ("+rating +")";
+ region_name->setText(region_name_txt);
- LLIconCtrl* rating_icon = getChild<LLIconCtrl>("rating_icon");
- LLRect rect = rating_icon->getRect();
- S32 region_name_width = llmin(region_name->getRect().getWidth(), region_name->getTextBoundingRect().getWidth());
- S32 icon_left_pad = region_name->getRect().mLeft + region_name_width + ICON_PAD;
- region_name->setToolTip(region_name->getText());
- rating_icon->setRect(rect.setOriginAndSize(icon_left_pad, rect.mBottom, rect.getWidth(), rect.getHeight()));
+ LLIconCtrl* rating_icon = getChild<LLIconCtrl>("rating_icon");
+ LLRect rect = rating_icon->getRect();
+ S32 region_name_width = llmin(region_name->getRect().getWidth(), region_name->getTextBoundingRect().getWidth());
+ S32 icon_left_pad = region_name->getRect().mLeft + region_name_width + ICON_PAD;
+ region_name->setToolTip(region_name->getText());
+ rating_icon->setRect(rect.setOriginAndSize(icon_left_pad, rect.mBottom, rect.getWidth(), rect.getHeight()));
- switch(sim_access)
- {
- case SIM_ACCESS_PG:
- rating_icon->setValue(getString("icon_PG"));
- break;
+ switch (sim_access)
+ {
+ case SIM_ACCESS_PG:
+ rating_icon->setValue(getString("icon_PG"));
+ break;
- case SIM_ACCESS_ADULT:
- rating_icon->setValue(getString("icon_R"));
- break;
+ case SIM_ACCESS_ADULT:
+ rating_icon->setValue(getString("icon_R"));
+ break;
- default:
- rating_icon->setValue(getString("icon_M"));
- }
+ default:
+ rating_icon->setValue(getString("icon_M"));
}
LLTextBox* region_type = getChild<LLTextBox>("region_type_text");
- if (region_type)
- {
- region_type->setText(region->getLocalizedSimProductName());
- region_type->setToolTip(region->getLocalizedSimProductName());
- }
+ region_type->setText(region->getLocalizedSimProductName());
+ region_type->setToolTip(region->getLocalizedSimProductName());
LLTextBox* resellable_clause = getChild<LLTextBox>("resellable_clause");
- if (resellable_clause)
- {
- if (region->getRegionFlag(REGION_FLAGS_BLOCK_LAND_RESELL))
- {
- resellable_clause->setText(getString("can_not_resell"));
- }
- else
- {
- resellable_clause->setText(getString("can_resell"));
- }
- }
+ const char* can_resell = region->getRegionFlag(REGION_FLAGS_BLOCK_LAND_RESELL) ? "can_not_resell" : "can_resell";
+ resellable_clause->setText(getString(can_resell));
LLTextBox* changeable_clause = getChild<LLTextBox>("changeable_clause");
- if (changeable_clause)
- {
- if (region->getRegionFlag(REGION_FLAGS_ALLOW_PARCEL_CHANGES))
- {
- changeable_clause->setText(getString("can_change"));
- }
- else
- {
- changeable_clause->setText(getString("can_not_change"));
- }
- }
+ const char* can_change = region->getRegionFlag(REGION_FLAGS_ALLOW_PARCEL_CHANGES) ? "can_change" : "can_not_change";
+ changeable_clause->setText(getString(can_change));
LLCheckBoxCtrl* check = getChild<LLCheckBoxCtrl>("agree_covenant");
- if(check)
- {
- check->set(false);
- check->setEnabled(true);
- check->setCommitCallback(onChangeAgreeCovenant, this);
- }
+ check->set(false);
+ check->setEnabled(true);
+ check->setCommitCallback(onChangeAgreeCovenant, this);
LLTextBox* box = getChild<LLTextBox>("covenant_text");
- if(box)
- {
- box->setVisible(false);
- }
+ box->setVisible(false);
// send EstateCovenantInfo message
LLMessageSystem *msg = gMessageSystem;
@@ -584,10 +554,9 @@ void LLFloaterBuyLandUI::updateCovenantInfo()
// static
void LLFloaterBuyLandUI::onChangeAgreeCovenant(LLUICtrl* ctrl, void* user_data)
{
- LLFloaterBuyLandUI* self = (LLFloaterBuyLandUI*)user_data;
- if(self)
+ if (user_data)
{
- self->refreshUI();
+ ((LLFloaterBuyLandUI*)user_data)->refreshUI();
}
}
@@ -626,13 +595,13 @@ void LLFloaterBuyLandUI::updateFloaterEstateName(const std::string& name)
void LLFloaterBuyLandUI::updateFloaterLastModified(const std::string& text)
{
LLTextBox* editor = getChild<LLTextBox>("covenant_timestamp_text");
- if (editor) editor->setText(text);
+ editor->setText(text);
}
void LLFloaterBuyLandUI::updateFloaterEstateOwnerName(const std::string& name)
{
LLTextBox* box = getChild<LLTextBox>("estate_owner_text");
- if (box) box->setText(name);
+ box->setText(name);
}
void LLFloaterBuyLandUI::updateWebSiteInfo()
@@ -640,9 +609,10 @@ void LLFloaterBuyLandUI::updateWebSiteInfo()
S32 askBillableArea = mIsForGroup ? 0 : mParcelBillableArea;
S32 askCurrencyBuy = mCurrency.getAmount();
- if (mTransaction && mTransactionType == TransactionPreflight
- && mPreflightAskBillableArea == askBillableArea
- && mPreflightAskCurrencyBuy == askCurrencyBuy)
+ if (mTransaction &&
+ mTransactionType == TransactionPreflight &&
+ mPreflightAskBillableArea == askBillableArea &&
+ mPreflightAskCurrencyBuy == askCurrencyBuy)
{
return;
}
@@ -664,27 +634,21 @@ void LLFloaterBuyLandUI::updateWebSiteInfo()
mSiteCurrencyEstimatedCost = 0;
#endif
- LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct();
- keywordArgs.appendString("agentId", gAgent.getID().asString());
- keywordArgs.appendString(
- "secureSessionId",
- gAgent.getSecureSessionID().asString());
- keywordArgs.appendString("language", LLUI::getLanguage());
- keywordArgs.appendInt("billableArea", mPreflightAskBillableArea);
- keywordArgs.appendInt("currencyBuy", mPreflightAskCurrencyBuy);
-
- LLXMLRPCValue params = LLXMLRPCValue::createArray();
- params.append(keywordArgs);
+ LLSD params = LLSD::emptyMap();
+ params["agentId"] = gAgent.getID().asString();
+ params["secureSessionId"] = gAgent.getSecureSessionID().asString();
+ params["language"] = LLUI::getLanguage();
+ params["billableArea"] = mPreflightAskBillableArea;
+ params["currencyBuy"] = mPreflightAskCurrencyBuy;
startTransaction(TransactionPreflight, params);
}
void LLFloaterBuyLandUI::finishWebSiteInfo()
{
+ const LLSD& result = mTransaction->response();
- LLXMLRPCValue result = mTransaction->responseValue();
-
- mSiteValid = result["success"].asBool();
+ mSiteValid = result["success"].asBoolean();
if (!mSiteValid)
{
tellUserError(
@@ -694,31 +658,30 @@ void LLFloaterBuyLandUI::finishWebSiteInfo()
return;
}
- LLXMLRPCValue membership = result["membership"];
- mSiteMembershipUpgrade = membership["upgrade"].asBool();
+ const LLSD& membership = result["membership"];
+ mSiteMembershipUpgrade = membership["upgrade"].asBoolean();
mSiteMembershipAction = membership["action"].asString();
mSiteMembershipPlanIDs.clear();
mSiteMembershipPlanNames.clear();
- LLXMLRPCValue levels = membership["levels"];
- for (LLXMLRPCValue level = levels.rewind();
- level.isValid();
- level = levels.next())
+ const LLSD& levels = membership["levels"];
+ for (auto it = levels.beginArray(); it != levels.endArray(); ++it)
{
+ const LLSD& level = *it;
mSiteMembershipPlanIDs.push_back(level["id"].asString());
mSiteMembershipPlanNames.push_back(level["description"].asString());
}
mUserPlanChoice = 0;
- LLXMLRPCValue landUse = result["landUse"];
- mSiteLandUseUpgrade = landUse["upgrade"].asBool();
+ const LLSD& landUse = result["landUse"];
+ mSiteLandUseUpgrade = landUse["upgrade"].asBoolean();
mSiteLandUseAction = landUse["action"].asString();
- LLXMLRPCValue currency = result["currency"];
- if (currency["estimatedCost"].isValid())
+ const LLSD& currency = result["currency"];
+ if (currency.has("estimatedCost"))
{
- mCurrency.setUSDEstimate(currency["estimatedCost"].asInt());
+ mCurrency.setUSDEstimate(currency["estimatedCost"].asInteger());
}
- if (currency["estimatedLocalCost"].isValid())
+ if (currency.has("estimatedLocalCost"))
{
mCurrency.setLocalEstimate(currency["estimatedLocalCost"].asString());
}
@@ -760,35 +723,30 @@ void LLFloaterBuyLandUI::runWebSitePrep(const std::string& password)
}
}
- LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct();
- keywordArgs.appendString("agentId", gAgent.getID().asString());
- keywordArgs.appendString(
- "secureSessionId",
- gAgent.getSecureSessionID().asString());
- keywordArgs.appendString("language", LLUI::getLanguage());
- keywordArgs.appendString("levelId", newLevel);
- keywordArgs.appendInt("billableArea",
- mIsForGroup ? 0 : mParcelBillableArea);
- keywordArgs.appendInt("currencyBuy", mCurrency.getAmount());
- keywordArgs.appendInt("estimatedCost", mCurrency.getUSDEstimate());
- keywordArgs.appendString("estimatedLocalCost", mCurrency.getLocalEstimate());
- keywordArgs.appendString("confirm", mSiteConfirm);
+ LLSD params = LLSD::emptyMap();
+ params["agentId"] = gAgent.getID().asString();
+ params["secureSessionId"] = gAgent.getSecureSessionID().asString();
+ params["language"] = LLUI::getLanguage();
+ params["levelId"] = newLevel;
+ params["billableArea"] = mIsForGroup ? 0 : mParcelBillableArea;
+ params["currencyBuy"] = mCurrency.getAmount();
+ params["estimatedCost"] = mCurrency.getUSDEstimate();
+ params["estimatedLocalCost"] = mCurrency.getLocalEstimate();
+ params["confirm"] = mSiteConfirm;
+
if (!password.empty())
{
- keywordArgs.appendString("password", password);
+ params["password"] = password;
}
- LLXMLRPCValue params = LLXMLRPCValue::createArray();
- params.append(keywordArgs);
-
startTransaction(TransactionBuy, params);
}
void LLFloaterBuyLandUI::finishWebSitePrep()
{
- LLXMLRPCValue result = mTransaction->responseValue();
+ const LLSD& result = mTransaction->response();
- bool success = result["success"].asBool();
+ bool success = result["success"].asBoolean();
if (!success)
{
tellUserError(
@@ -850,7 +808,7 @@ void LLFloaterBuyLandUI::updateGroupName(const LLUUID& id,
}
}
-void LLFloaterBuyLandUI::startTransaction(TransactionType type, const LLXMLRPCValue& params)
+void LLFloaterBuyLandUI::startTransaction(TransactionType type, const LLSD& params)
{
delete mTransaction;
mTransaction = NULL;
@@ -878,12 +836,7 @@ void LLFloaterBuyLandUI::startTransaction(TransactionType type, const LLXMLRPCVa
return;
}
- mTransaction = new LLXMLRPCTransaction(
- transaction_uri,
- method,
- params,
- false /* don't use gzip */
- );
+ mTransaction = new LLXMLRPCTransaction(transaction_uri, method, params);
}
bool LLFloaterBuyLandUI::checkTransaction()
diff --git a/indra/newview/llfloaterdestinations.cpp b/indra/newview/llfloaterdestinations.cpp
index 93cf02e835..fad9693e8f 100644
--- a/indra/newview/llfloaterdestinations.cpp
+++ b/indra/newview/llfloaterdestinations.cpp
@@ -25,11 +25,6 @@
* $/LicenseInfo$
*/
-/**
- * Floater that appears when buying an object, giving a preview
- * of its contents and their permissions.
- */
-
#include "llviewerprecompiledheaders.h"
#include "llfloaterdestinations.h"
diff --git a/indra/newview/llfloaterfonttest.cpp b/indra/newview/llfloaterfonttest.cpp
index 95d08cb9ce..d39b061d40 100644
--- a/indra/newview/llfloaterfonttest.cpp
+++ b/indra/newview/llfloaterfonttest.cpp
@@ -25,11 +25,6 @@
* $/LicenseInfo$
*/
-/**
- * Floater that appears when buying an object, giving a preview
- * of its contents and their permissions.
- */
-
#include "llviewerprecompiledheaders.h"
#include "llfloaterfonttest.h"
diff --git a/indra/newview/llfloatergltfasseteditor.cpp b/indra/newview/llfloatergltfasseteditor.cpp
new file mode 100644
index 0000000000..f21b8032d0
--- /dev/null
+++ b/indra/newview/llfloatergltfasseteditor.cpp
@@ -0,0 +1,527 @@
+/**
+ * @file llfloatergltfasseteditor.cpp
+ * @author Andrii Kleshchev
+ * @brief LLFloaterGltfAssetEditor class implementation
+ *
+ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2024, 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 "llviewerprecompiledheaders.h"
+
+#include "llfloatergltfasseteditor.h"
+
+#include "gltf/asset.h"
+#include "llcallbacklist.h"
+#include "llmenubutton.h"
+#include "llselectmgr.h"
+#include "llspinctrl.h"
+#include "llviewerobject.h"
+
+const LLColor4U DEFAULT_WHITE(255, 255, 255);
+
+/// LLFloaterGLTFAssetEditor
+
+LLFloaterGLTFAssetEditor::LLFloaterGLTFAssetEditor(const LLSD& key)
+ : LLFloater(key)
+ , mUIColor(LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE))
+{
+ setTitle("GLTF Asset Editor (WIP)");
+
+ mCommitCallbackRegistrar.add("PanelObject.menuDoToSelected", [this](LLUICtrl* ctrl, const LLSD& data) { onMenuDoToSelected(data); });
+ mEnableCallbackRegistrar.add("PanelObject.menuEnable", [this](LLUICtrl* ctrl, const LLSD& data) { return onMenuEnableItem(data); });
+}
+
+LLFloaterGLTFAssetEditor::~LLFloaterGLTFAssetEditor()
+{
+ gIdleCallbacks.deleteFunction(idle, this);
+
+ if (mScroller)
+ {
+ removeChild(mScroller);
+ delete mScroller;
+ mScroller = NULL;
+ }
+}
+
+bool LLFloaterGLTFAssetEditor::postBuild()
+{
+ // Position
+ mMenuClipboardPos = getChild<LLMenuButton>("clipboard_pos_btn");
+ mCtrlPosX = getChild<LLSpinCtrl>("Pos X", true);
+ mCtrlPosX->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) { onCommitTransform(); });
+ mCtrlPosY = getChild<LLSpinCtrl>("Pos Y", true);
+ mCtrlPosY->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) { onCommitTransform(); });
+ mCtrlPosZ = getChild<LLSpinCtrl>("Pos Z", true);
+ mCtrlPosZ->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) { onCommitTransform(); });
+
+ // Scale
+ mMenuClipboardScale = getChild<LLMenuButton>("clipboard_size_btn");
+ mCtrlScaleX = getChild<LLSpinCtrl>("Scale X", true);
+ mCtrlScaleX->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) { onCommitTransform(); });
+ mCtrlScaleY = getChild<LLSpinCtrl>("Scale Y", true);
+ mCtrlScaleY->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) { onCommitTransform(); });
+ mCtrlScaleZ = getChild<LLSpinCtrl>("Scale Z", true);
+ mCtrlScaleZ->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) { onCommitTransform(); });
+
+ // Rotation
+ mMenuClipboardRot = getChild<LLMenuButton>("clipboard_rot_btn");
+ mCtrlRotX = getChild<LLSpinCtrl>("Rot X", true);
+ mCtrlRotX->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) { onCommitTransform(); });
+ mCtrlRotY = getChild<LLSpinCtrl>("Rot Y", true);
+ mCtrlRotY->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) { onCommitTransform(); });
+ mCtrlRotZ = getChild<LLSpinCtrl>("Rot Z", true);
+ mCtrlPosZ->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) { onCommitTransform(); });
+ setTransformsEnabled(false);
+ // todo: do multiple panels based on selected element.
+ mTransformsPanel = getChild<LLPanel>("transform_panel", true);
+ mTransformsPanel->setVisible(false);
+
+ mItemListPanel = getChild<LLPanel>("item_list_panel", true);
+ initFolderRoot();
+
+ return true;
+}
+
+void LLFloaterGLTFAssetEditor::initFolderRoot()
+{
+ if (mScroller || mFolderRoot)
+ {
+ LL_ERRS() << "Folder root already initialized" << LL_ENDL;
+ return;
+ }
+
+ LLRect scroller_view_rect = mItemListPanel->getRect();
+ scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom);
+ LLScrollContainer::Params scroller_params(LLUICtrlFactory::getDefaultParams<LLFolderViewScrollContainer>());
+ scroller_params.rect(scroller_view_rect);
+ scroller_params.name("folder_scroller");
+ mScroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params);
+ mScroller->setFollowsAll();
+
+ // Insert that scroller into the panel widgets hierarchy
+ mItemListPanel->addChild(mScroller);
+
+ // Create the root model and view for all conversation sessions
+ LLGLTFFolderItem* base_item = new LLGLTFFolderItem(mGLTFViewModel);
+
+ LLFolderView::Params p(LLUICtrlFactory::getDefaultParams<LLFolderView>());
+ p.name = "Root";
+ p.title = "Root";
+ p.rect = LLRect(0, 0, getRect().getWidth(), 0);
+ p.parent_panel = mItemListPanel;
+ p.tool_tip = p.name;
+ p.listener = base_item;
+ p.view_model = &mGLTFViewModel;
+ p.root = NULL;
+ p.use_ellipses = true;
+ p.options_menu = "menu_gltf.xml"; // *TODO : create this or fix to be optional
+ mFolderRoot = LLUICtrlFactory::create<LLFolderView>(p);
+ mFolderRoot->setCallbackRegistrar(&mCommitCallbackRegistrar);
+ mFolderRoot->setEnableRegistrar(&mEnableCallbackRegistrar);
+ // Attach root to the scroller
+ mScroller->addChild(mFolderRoot);
+ mFolderRoot->setScrollContainer(mScroller);
+ mFolderRoot->setFollowsAll();
+ mFolderRoot->setOpen(true);
+ mFolderRoot->setSelectCallback([this](const std::deque<LLFolderViewItem*>& items, bool user_action) { onFolderSelectionChanged(items, user_action); });
+ mScroller->setVisible(true);
+
+ gIdleCallbacks.addFunction(idle, this);
+}
+
+void LLFloaterGLTFAssetEditor::onOpen(const LLSD& key)
+{
+ loadFromSelection();
+}
+
+void LLFloaterGLTFAssetEditor::idle(void* user_data)
+{
+ LLFloaterGLTFAssetEditor* floater = (LLFloaterGLTFAssetEditor*)user_data;
+
+ if (floater->mFolderRoot)
+ {
+ floater->mFolderRoot->update();
+ }
+}
+
+void LLFloaterGLTFAssetEditor::loadItem(S32 id, const std::string& name, LLGLTFFolderItem::EType type, LLFolderViewFolder* parent)
+{
+ LLGLTFFolderItem* listener = new LLGLTFFolderItem(id, name, type, mGLTFViewModel);
+
+ LLFolderViewItem::Params params;
+ params.name(name);
+ params.creation_date(0);
+ params.root(mFolderRoot);
+ params.listener(listener);
+ params.rect(LLRect());
+ params.tool_tip = params.name;
+ params.font_color = mUIColor;
+ params.font_highlight_color = mUIColor;
+ LLFolderViewItem* view = LLUICtrlFactory::create<LLFolderViewItem>(params);
+
+ view->addToFolder(parent);
+ view->setVisible(true);
+}
+
+void LLFloaterGLTFAssetEditor::loadFromNode(S32 node_id, LLFolderViewFolder* parent)
+{
+ if (mAsset->mNodes.size() <= node_id)
+ {
+ return;
+ }
+
+ LL::GLTF::Node& node = mAsset->mNodes[node_id];
+
+ std::string name = node.mName;
+ if (node.mName.empty())
+ {
+ name = getString("node_tittle");
+ }
+ else
+ {
+ name = node.mName;
+ }
+
+ LLGLTFFolderItem* listener = new LLGLTFFolderItem(node_id, name, LLGLTFFolderItem::TYPE_NODE, mGLTFViewModel);
+
+ LLFolderViewFolder::Params p;
+ p.root = mFolderRoot;
+ p.listener = listener;
+ p.name = name;
+ p.tool_tip = name;
+ p.font_color = mUIColor;
+ p.font_highlight_color = mUIColor;
+ LLFolderViewFolder* view = LLUICtrlFactory::create<LLFolderViewFolder>(p);
+
+ view->addToFolder(parent);
+ view->setVisible(true);
+ view->setOpen(true);
+
+ for (S32& node_id : node.mChildren)
+ {
+ loadFromNode(node_id, view);
+ }
+
+ if (node.mMesh != LL::GLTF::INVALID_INDEX && mAsset->mMeshes.size() > node.mMesh)
+ {
+ std::string name = mAsset->mMeshes[node.mMesh].mName;
+ if (name.empty())
+ {
+ name = getString("mesh_tittle");
+ }
+ loadItem(node.mMesh, name, LLGLTFFolderItem::TYPE_MESH, view);
+ }
+
+ if (node.mSkin != LL::GLTF::INVALID_INDEX && mAsset->mSkins.size() > node.mSkin)
+ {
+ std::string name = mAsset->mSkins[node.mSkin].mName;
+ if (name.empty())
+ {
+ name = getString("skin_tittle");
+ }
+ loadItem(node.mSkin, name, LLGLTFFolderItem::TYPE_SKIN, view);
+ }
+
+ view->setChildrenInited(true);
+}
+
+void LLFloaterGLTFAssetEditor::loadFromSelection()
+{
+ if (!mFolderRoot || LLSelectMgr::getInstance()->getSelection()->getObjectCount() != 1)
+ {
+ return;
+ }
+
+ LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(NULL);
+ LLViewerObject* objectp = node->getObject();
+ if (!objectp)
+ {
+ return;
+ }
+
+ mAsset = objectp->mGLTFAsset;
+ if (!mAsset)
+ {
+ return;
+ }
+
+ if (node->mName.empty())
+ {
+ setTitle(getString("floater_title"));
+ }
+ else
+ {
+ setTitle(node->mName);
+ }
+
+ LLUIColor item_color = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE);
+ for (S32 i = 0; i < mAsset->mScenes.size(); i++)
+ {
+ LL::GLTF::Scene& scene = mAsset->mScenes[i];
+ std::string name = scene.mName;
+ if (scene.mName.empty())
+ {
+ name = getString("scene_tittle");
+ }
+ else
+ {
+ name = scene.mName;
+ }
+
+ LLGLTFFolderItem* listener = new LLGLTFFolderItem(i, name, LLGLTFFolderItem::TYPE_SCENE, mGLTFViewModel);
+
+
+ LLFolderViewFolder::Params p;
+ p.name = name;
+ p.root = mFolderRoot;
+ p.listener = listener;
+ p.tool_tip = name;
+ p.font_color = mUIColor;
+ p.font_highlight_color = mUIColor;
+ LLFolderViewFolder* view = LLUICtrlFactory::create<LLFolderViewFolder>(p);
+
+ view->addToFolder(mFolderRoot);
+ view->setVisible(true);
+ view->setOpen(true);
+
+ for (S32& node_id : scene.mNodes)
+ {
+ loadFromNode(node_id, view);
+ }
+ view->setChildrenInited(true);
+ }
+
+ mGLTFViewModel.requestSortAll();
+ mFolderRoot->setChildrenInited(true);
+ mFolderRoot->arrangeAll();
+ mFolderRoot->update();
+}
+
+void LLFloaterGLTFAssetEditor::onFolderSelectionChanged(const std::deque<LLFolderViewItem*>& items, bool user_action)
+{
+ if (items.empty())
+ {
+ setTransformsEnabled(false);
+ return;
+ }
+
+ LLFolderViewItem* item = items.front();
+ LLGLTFFolderItem* vmi = static_cast<LLGLTFFolderItem*>(item->getViewModelItem());
+
+ switch (vmi->getType())
+ {
+ case LLGLTFFolderItem::TYPE_NODE:
+ {
+ setTransformsEnabled(true);
+ loadNodeTransforms(vmi->getItemId());
+ break;
+ }
+ default:
+ {
+ setTransformsEnabled(false);
+ break;
+ }
+ }
+}
+
+void LLFloaterGLTFAssetEditor::setTransformsEnabled(bool val)
+{
+ mMenuClipboardPos->setEnabled(val);
+ mCtrlPosX->setEnabled(val);
+ mCtrlPosY->setEnabled(val);
+ mCtrlPosZ->setEnabled(val);
+ mMenuClipboardScale->setEnabled(val);
+ mCtrlScaleX->setEnabled(val);
+ mCtrlScaleY->setEnabled(val);
+ mCtrlScaleZ->setEnabled(val);
+ mMenuClipboardRot->setEnabled(val);
+ mCtrlRotX->setEnabled(val);
+ mCtrlRotY->setEnabled(val);
+ mCtrlRotZ->setEnabled(val);
+}
+
+void LLFloaterGLTFAssetEditor::loadNodeTransforms(S32 node_id)
+{
+ if (node_id < 0 || node_id >= mAsset->mNodes.size())
+ {
+ LL_ERRS() << "Node id out of range: " << node_id << LL_ENDL;
+ return;
+ }
+
+ LL::GLTF::Node& node = mAsset->mNodes[node_id];
+
+ mCtrlPosX->set(node.mTranslation[0]);
+ mCtrlPosY->set(node.mTranslation[1]);
+ mCtrlPosZ->set(node.mTranslation[2]);
+
+ mCtrlScaleX->set(node.mScale[0]);
+ mCtrlScaleY->set(node.mScale[1]);
+ mCtrlScaleZ->set(node.mScale[2]);
+
+ LLQuaternion object_rot = LLQuaternion(node.mRotation[0], node.mRotation[1], node.mRotation[2], node.mRotation[3]);
+ object_rot.getEulerAngles(&(mLastEulerDegrees.mV[VX]), &(mLastEulerDegrees.mV[VY]), &(mLastEulerDegrees.mV[VZ]));
+ mLastEulerDegrees *= RAD_TO_DEG;
+ mLastEulerDegrees.mV[VX] = fmod(ll_round(mLastEulerDegrees.mV[VX], OBJECT_ROTATION_PRECISION) + 360.f, 360.f);
+ mLastEulerDegrees.mV[VY] = fmod(ll_round(mLastEulerDegrees.mV[VY], OBJECT_ROTATION_PRECISION) + 360.f, 360.f);
+ mLastEulerDegrees.mV[VZ] = fmod(ll_round(mLastEulerDegrees.mV[VZ], OBJECT_ROTATION_PRECISION) + 360.f, 360.f);
+
+ mCtrlRotX->set(mLastEulerDegrees.mV[VX]);
+ mCtrlRotY->set(mLastEulerDegrees.mV[VY]);
+ mCtrlRotZ->set(mLastEulerDegrees.mV[VZ]);
+}
+
+void LLFloaterGLTFAssetEditor::onCommitTransform()
+{
+ if (!mFolderRoot)
+ {
+ LL_ERRS() << "Folder root not initialized" << LL_ENDL;
+ return;
+ }
+
+ LLFolderViewItem* item = mFolderRoot->getCurSelectedItem();
+ if (!item)
+ {
+ LL_ERRS() << "Nothing selected" << LL_ENDL;
+ return;
+ }
+
+ LLGLTFFolderItem* vmi = static_cast<LLGLTFFolderItem*>(item->getViewModelItem());
+
+ if (!vmi || vmi->getType() != LLGLTFFolderItem::TYPE_NODE)
+ {
+ LL_ERRS() << "Only nodes implemented" << LL_ENDL;
+ return;
+ }
+ S32 node_id = vmi->getItemId();
+ LL::GLTF::Node& node = mAsset->mNodes[node_id];
+
+ LL::GLTF::vec3 tr(mCtrlPosX->get(), mCtrlPosY->get(), mCtrlPosZ->get());
+ node.setTranslation(tr);
+
+ LL::GLTF::vec3 scale(mCtrlScaleX->get(), mCtrlScaleY->get(), mCtrlScaleZ->get());
+ node.setScale(scale);
+
+ LLVector3 new_rot(mCtrlRotX->get(), mCtrlRotY->get(), mCtrlRotZ->get());
+ new_rot.mV[VX] = ll_round(new_rot.mV[VX], OBJECT_ROTATION_PRECISION);
+ new_rot.mV[VY] = ll_round(new_rot.mV[VY], OBJECT_ROTATION_PRECISION);
+ new_rot.mV[VZ] = ll_round(new_rot.mV[VZ], OBJECT_ROTATION_PRECISION);
+
+ // Note: must compare before conversion to radians, some value can go 'around' 360
+ LLVector3 delta = new_rot - mLastEulerDegrees;
+
+ if (delta.magVec() >= 0.0005f)
+ {
+ mLastEulerDegrees = new_rot;
+ new_rot *= DEG_TO_RAD;
+
+ LLQuaternion rotation;
+ rotation.setQuat(new_rot.mV[VX], new_rot.mV[VY], new_rot.mV[VZ]);
+ LL::GLTF::quat q;
+ q[0] = rotation.mQ[VX];
+ q[1] = rotation.mQ[VY];
+ q[2] = rotation.mQ[VZ];
+ q[3] = rotation.mQ[VW];
+
+ node.setRotation(q);
+ }
+
+ mAsset->updateTransforms();
+}
+
+void LLFloaterGLTFAssetEditor::onMenuDoToSelected(const LLSD& userdata)
+{
+ std::string command = userdata.asString();
+
+ if (command == "psr_paste")
+ {
+ // todo: implement
+ // onPastePos();
+ // onPasteSize();
+ // onPasteRot();
+ }
+ else if (command == "pos_paste")
+ {
+ // todo: implement
+ }
+ else if (command == "size_paste")
+ {
+ // todo: implement
+ }
+ else if (command == "rot_paste")
+ {
+ // todo: implement
+ }
+ else if (command == "psr_copy")
+ {
+ // onCopyPos();
+ // onCopySize();
+ // onCopyRot();
+ }
+ else if (command == "pos_copy")
+ {
+ // todo: implement
+ }
+ else if (command == "size_copy")
+ {
+ // todo: implement
+ }
+ else if (command == "rot_copy")
+ {
+ // todo: implement
+ }
+}
+
+bool LLFloaterGLTFAssetEditor::onMenuEnableItem(const LLSD& userdata)
+{
+ if (!mFolderRoot)
+ {
+ return false;
+ }
+
+ LLFolderViewItem* item = mFolderRoot->getCurSelectedItem();
+ if (!item)
+ {
+ return false;
+ }
+
+ LLGLTFFolderItem* vmi = static_cast<LLGLTFFolderItem*>(item->getViewModelItem());
+
+ if (!vmi || vmi->getType() != LLGLTFFolderItem::TYPE_NODE)
+ {
+ return false;
+ }
+
+ std::string command = userdata.asString();
+ if (command == "pos_paste" || command == "size_paste" || command == "rot_paste")
+ {
+ // todo: implement
+ return true;
+ }
+ if (command == "psr_copy")
+ {
+ // todo: implement
+ return true;
+ }
+
+ return false;
+}
+
diff --git a/indra/newview/llfloatergltfasseteditor.h b/indra/newview/llfloatergltfasseteditor.h
new file mode 100644
index 0000000000..e35ed30ed0
--- /dev/null
+++ b/indra/newview/llfloatergltfasseteditor.h
@@ -0,0 +1,101 @@
+/**
+ * @file llfloatergltfasseteditor.h
+ * @author Andrii Kleshchev
+ * @brief LLFloaterGltfAssetEditor header file
+ *
+ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2024, 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_LLFLOATERGLTFASSETEDITOR_H
+#define LL_LLFLOATERGLTFASSETEDITOR_H
+
+#include "llfloater.h"
+
+#include "llgltffoldermodel.h"
+
+namespace LL
+{
+ namespace GLTF
+ {
+ class Asset;
+ }
+}
+
+class LLSpinCtrl;
+class LLMenuButton;
+
+class LLFloaterGLTFAssetEditor : public LLFloater
+{
+public:
+ LLFloaterGLTFAssetEditor(const LLSD& key);
+ ~LLFloaterGLTFAssetEditor();
+
+ bool postBuild() override;
+ void onOpen(const LLSD& key) override;
+ void initFolderRoot();
+
+ LLGLTFViewModel& getRootViewModel() { return mGLTFViewModel; }
+
+ static void idle(void* user_data);
+ void loadItem(S32 id, const std::string& name, LLGLTFFolderItem::EType type, LLFolderViewFolder* parent);
+ void loadFromNode(S32 node, LLFolderViewFolder* parent);
+ void loadFromSelection();
+
+protected:
+ void onFolderSelectionChanged(const std::deque<LLFolderViewItem*>& items, bool user_action);
+ void onCommitTransform();
+ void onMenuDoToSelected(const LLSD& userdata);
+ bool onMenuEnableItem(const LLSD& userdata);
+
+ void setTransformsEnabled(bool val);
+ void loadNodeTransforms(S32 id);
+
+private:
+
+ std::shared_ptr<LL::GLTF::Asset> mAsset;
+
+ // Folder view related
+ LLUIColor mUIColor;
+ LLGLTFViewModel mGLTFViewModel;
+ LLPanel* mItemListPanel = nullptr;
+ LLFolderView* mFolderRoot = nullptr;
+ LLScrollContainer* mScroller = nullptr;
+
+ // Transforms panel
+ LLVector3 mLastEulerDegrees;
+
+ LLPanel* mTransformsPanel = nullptr;
+ LLMenuButton* mMenuClipboardPos = nullptr;
+ LLSpinCtrl* mCtrlPosX = nullptr;
+ LLSpinCtrl* mCtrlPosY = nullptr;
+ LLSpinCtrl* mCtrlPosZ = nullptr;
+ LLMenuButton* mMenuClipboardScale = nullptr;
+ LLSpinCtrl* mCtrlScaleX = nullptr;
+ LLSpinCtrl* mCtrlScaleY = nullptr;
+ LLSpinCtrl* mCtrlScaleZ = nullptr;
+ LLMenuButton* mMenuClipboardRot = nullptr;
+ LLSpinCtrl* mCtrlRotX = nullptr;
+ LLSpinCtrl* mCtrlRotY = nullptr;
+ LLSpinCtrl* mCtrlRotZ = nullptr;
+};
+
+#endif
diff --git a/indra/newview/llgltffolderitem.cpp b/indra/newview/llgltffolderitem.cpp
new file mode 100644
index 0000000000..77a19c060d
--- /dev/null
+++ b/indra/newview/llgltffolderitem.cpp
@@ -0,0 +1,164 @@
+/**
+ * @file llgltffolderitem.cpp
+ * @author Andrey Kleshchev
+ * @brief LLGLTFFolderItem class implementation
+ *
+ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2024, 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 "llviewerprecompiledheaders.h"
+
+#include "llgltffolderitem.h"
+
+#include "llinventoryicon.h"
+
+/// LLGLTFItem
+
+LLGLTFFolderItem::LLGLTFFolderItem(S32 id, const std::string &display_name, EType type, LLFolderViewModelInterface& root_view_model)
+ : LLFolderViewModelItemCommon(root_view_model)
+ , mName(display_name)
+ , mItemType(type)
+ , mItemId(id)
+{
+ init();
+}
+
+LLGLTFFolderItem::LLGLTFFolderItem(LLFolderViewModelInterface& root_view_model)
+ : LLFolderViewModelItemCommon(root_view_model)
+{
+ init();
+}
+
+LLGLTFFolderItem::~LLGLTFFolderItem()
+{
+
+}
+
+void LLGLTFFolderItem::init()
+{
+ // using inventory icons as a placeholder.
+ // Todo: GLTF needs to have own icons
+ switch (mItemType)
+ {
+ case TYPE_SCENE:
+ pIcon = LLInventoryIcon::getIcon(LLInventoryType::ICONNAME_OBJECT_MULTI);
+ break;
+ case TYPE_NODE:
+ pIcon = LLInventoryIcon::getIcon(LLInventoryType::ICONNAME_OBJECT);
+ break;
+ case TYPE_MESH:
+ pIcon = LLInventoryIcon::getIcon(LLInventoryType::ICONNAME_MESH);
+ break;
+ case TYPE_SKIN:
+ pIcon = LLInventoryIcon::getIcon(LLInventoryType::ICONNAME_BODYPART_SKIN);
+ break;
+ default:
+ pIcon = LLInventoryIcon::getIcon(LLInventoryType::ICONNAME_OBJECT);
+ break;
+ }
+}
+
+
+bool LLGLTFFolderItem::filterChildItem(LLFolderViewModelItem* item, LLFolderViewFilter& filter)
+{
+ S32 filter_generation = filter.getCurrentGeneration();
+
+ bool continue_filtering = true;
+ if (item)
+ {
+ if (item->getLastFilterGeneration() < filter_generation)
+ {
+ // Recursive application of the filter for child items (CHUI-849)
+ continue_filtering = item->filter(filter);
+ }
+
+ // Update latest generation to pass filter in parent and propagate up to root
+ if (item->passedFilter())
+ {
+ LLGLTFFolderItem* view_model = this;
+
+ while (view_model && view_model->mMostFilteredDescendantGeneration < filter_generation)
+ {
+ view_model->mMostFilteredDescendantGeneration = filter_generation;
+ view_model = static_cast<LLGLTFFolderItem*>(view_model->mParent);
+ }
+ }
+ }
+ return continue_filtering;
+}
+
+bool LLGLTFFolderItem::filter(LLFolderViewFilter& filter)
+{
+ const S32 filter_generation = filter.getCurrentGeneration();
+ const S32 must_pass_generation = filter.getFirstRequiredGeneration();
+
+ if (getLastFilterGeneration() >= must_pass_generation
+ && getLastFolderFilterGeneration() >= must_pass_generation
+ && !passedFilter(must_pass_generation))
+ {
+ // failed to pass an earlier filter that was a subset of the current one
+ // go ahead and flag this item as not pass
+ setPassedFilter(false, filter_generation);
+ setPassedFolderFilter(false, filter_generation);
+ return true;
+ }
+
+ bool is_folder = true;
+ const bool passed_filter_folder = is_folder ? filter.checkFolder(this) : true;
+ setPassedFolderFilter(passed_filter_folder, filter_generation);
+
+ bool continue_filtering = true;
+
+ if (!mChildren.empty()
+ && (getLastFilterGeneration() < must_pass_generation // haven't checked descendants against minimum required generation to pass
+ || descendantsPassedFilter(must_pass_generation))) // or at least one descendant has passed the minimum requirement
+ {
+ // now query children
+ for (child_list_t::iterator iter = mChildren.begin(), end_iter = mChildren.end(); iter != end_iter; ++iter)
+ {
+ continue_filtering = filterChildItem((*iter), filter);
+ if (!continue_filtering)
+ {
+ break;
+ }
+ }
+ }
+
+ // If we didn't use all the filter time that means we filtered all of our descendants so we can filter ourselves now
+ if (continue_filtering)
+ {
+ // This is where filter check on the item done (CHUI-849)
+ const bool passed_filter = filter.check(this);
+ if (passed_filter && mChildren.empty() && is_folder) // Update the latest filter generation for empty folders
+ {
+ LLGLTFFolderItem* view_model = this;
+ while (view_model && view_model->mMostFilteredDescendantGeneration < filter_generation)
+ {
+ view_model->mMostFilteredDescendantGeneration = filter_generation;
+ view_model = static_cast<LLGLTFFolderItem*>(view_model->mParent);
+ }
+ }
+ setPassedFilter(passed_filter, filter_generation, filter.getStringMatchOffset(this), filter.getFilterStringSize());
+ continue_filtering = !filter.isTimedOut();
+ }
+ return continue_filtering;
+}
diff --git a/indra/newview/llgltffolderitem.h b/indra/newview/llgltffolderitem.h
new file mode 100644
index 0000000000..89d90c81cc
--- /dev/null
+++ b/indra/newview/llgltffolderitem.h
@@ -0,0 +1,128 @@
+/**
+ * @file llgltffolderitem.h
+ * @author Andrey Kleshchev
+ * @brief LLGLTFFolderItem header file
+ *
+ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2024, 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_LLGLTFFOLDERITEM_H
+#define LL_LLGLTFFOLDERITEM_H
+
+#include "llfloater.h"
+
+#include "llfolderviewmodel.h"
+
+class LLGLTFFolderItem : public LLFolderViewModelItemCommon
+{
+public:
+ enum EType
+ {
+ TYPE_ROOT,
+ TYPE_SCENE,
+ TYPE_NODE,
+ TYPE_MESH,
+ TYPE_SKIN,
+ };
+
+ LLGLTFFolderItem(S32 id, const std::string &display_name, EType type, LLFolderViewModelInterface& root_view_model);
+ LLGLTFFolderItem(LLFolderViewModelInterface& root_view_model);
+ virtual ~LLGLTFFolderItem();
+
+ void init();
+
+ const std::string& getName() const override { return mName; }
+ const std::string& getDisplayName() const override { return mName; }
+ const std::string& getSearchableName() const override { return mName; }
+
+ std::string getSearchableDescription() const override { return std::string(); }
+ std::string getSearchableCreatorName()const override { return std::string(); }
+ std::string getSearchableUUIDString() const override { return std::string(); }
+
+ LLPointer<LLUIImage> getIcon() const override { return pIcon; }
+ LLPointer<LLUIImage> getIconOpen() const override { return getIcon(); }
+ LLPointer<LLUIImage> getIconOverlay() const override { return NULL; }
+
+ LLFontGL::StyleFlags getLabelStyle() const override { return LLFontGL::NORMAL; }
+ std::string getLabelSuffix() const override { return std::string(); }
+
+ void openItem(void) override {}
+ void closeItem(void) override {}
+ void selectItem(void) override {}
+
+ void navigateToFolder(bool new_window = false, bool change_mode = false) override {}
+
+ bool isItemWearable() const override { return false; }
+
+ bool isItemRenameable() const override { return false; }
+ bool renameItem(const std::string& new_name) override { return false; }
+
+ bool isItemMovable(void) const override { return false; } // Can be moved to another folder
+ void move(LLFolderViewModelItem* parent_listener) override {}
+
+ bool isItemRemovable(bool check_worn = true) const override { return false; }
+ bool removeItem() override { return false; }
+ void removeBatch(std::vector<LLFolderViewModelItem*>& batch) override {}
+
+ bool isItemCopyable(bool can_copy_as_link = true) const override { return false; }
+ bool copyToClipboard() const override { return false; }
+ bool cutToClipboard() override { return false; }
+ bool isCutToClipboard() override { return false; }
+
+ bool isClipboardPasteable() const override { return false; }
+ void pasteFromClipboard() override {}
+ void pasteLinkFromClipboard() override {}
+
+ void buildContextMenu(LLMenuGL& menu, U32 flags) override {};
+
+ bool potentiallyVisible() override { return true; }; // is the item definitely visible or we haven't made up our minds yet?
+
+ bool hasChildren() const override { return mChildren.size() > 0; }
+
+ bool dragOrDrop(
+ MASK mask,
+ bool drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ std::string& tooltip_msg) override
+ {
+ return false;
+ }
+
+ bool filterChildItem(LLFolderViewModelItem* item, LLFolderViewFilter& filter);
+ bool filter(LLFolderViewFilter& filter) override;
+
+ EType getType() const { return mItemType; }
+ S32 getItemId() const { return mItemId; }
+
+private:
+ LLUIImagePtr pIcon;
+ std::string mName;
+ EType mItemType = TYPE_ROOT;
+
+ // mItemId can be an id in a mesh vector, node vector or any other vector.
+ // mItemId is not nessesarily unique, ex: some nodes can reuse the same
+ // mesh or skin, so mesh-items can have the same id.
+ S32 mItemId = -1;
+};
+
+#endif
diff --git a/indra/newview/llgltffoldermodel.cpp b/indra/newview/llgltffoldermodel.cpp
new file mode 100644
index 0000000000..de2510dc4a
--- /dev/null
+++ b/indra/newview/llgltffoldermodel.cpp
@@ -0,0 +1,73 @@
+/**
+ * @file llgltffoldermodel.cpp
+ * @author Andrey Kleshchev
+ * @brief gltf model's folder structure related classes
+ *
+ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2024, 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 "llviewerprecompiledheaders.h"
+
+#include "llgltffoldermodel.h"
+
+#include "llfolderviewitem.h"
+
+bool LLGLTFSort::operator()(const LLGLTFFolderItem* const& a, const LLGLTFFolderItem* const& b) const
+{
+ // Comparison operator: returns "true" is a comes before b, "false" otherwise
+ S32 compare = LLStringUtil::compareDict(a->getName(), b->getName());
+ return (compare < 0);
+}
+
+/// LLGLTFViewModel
+
+LLGLTFViewModel::LLGLTFViewModel()
+ : base_t(new LLGLTFSort(), new LLGLTFFilter())
+{}
+
+void LLGLTFViewModel::sort(LLFolderViewFolder* folder)
+{
+ base_t::sort(folder);
+}
+
+ /// LLGLTFNode
+// LLUICtrlFactory::create<LLGLTFNode>(params);
+class LLGLTFNode : public LLFolderViewItem
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLFolderViewItem::Params>
+ {
+ Params();
+ };
+ ~LLGLTFNode();
+protected:
+ LLGLTFNode(const Params& p);
+};
+
+LLGLTFNode::LLGLTFNode(const LLGLTFNode::Params& p)
+ : LLFolderViewItem(p)
+{
+}
+
+LLGLTFNode::~LLGLTFNode()
+{
+}
diff --git a/indra/newview/llgltffoldermodel.h b/indra/newview/llgltffoldermodel.h
new file mode 100644
index 0000000000..69b284aa31
--- /dev/null
+++ b/indra/newview/llgltffoldermodel.h
@@ -0,0 +1,91 @@
+/**
+ * @file llfloatergltfasseteditor.h
+ * @author Andrey Kleshchev
+ * @brief gltf model's folder structure related classes
+ *
+ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2024, 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_LLGLTFFOLDERMODEL_H
+#define LL_LLGLTFFOLDERMODEL_H
+
+#include "llfolderviewmodel.h"
+#include "llgltffolderitem.h"
+
+class LLGLTFSort
+{
+public:
+ LLGLTFSort() { }
+ bool operator()(const LLGLTFFolderItem* const& a, const LLGLTFFolderItem* const& b) const;
+private:
+};
+
+class LLGLTFFilter : public LLFolderViewFilter
+{
+public:
+ LLGLTFFilter() { mEmpty = ""; }
+ ~LLGLTFFilter() {}
+
+ bool check(const LLFolderViewModelItem* item) { return true; }
+ bool checkFolder(const LLFolderViewModelItem* folder) const { return true; }
+ void setEmptyLookupMessage(const std::string& message) { }
+ std::string getEmptyLookupMessage(bool is_empty_folder = false) const { return mEmpty; }
+ bool showAllResults() const { return true; }
+ std::string::size_type getStringMatchOffset(LLFolderViewModelItem* item) const { return std::string::npos; }
+ std::string::size_type getFilterStringSize() const { return 0; }
+
+ bool isActive() const { return false; }
+ bool isModified() const { return false; }
+ void clearModified() { }
+ const std::string& getName() const { return mEmpty; }
+ const std::string& getFilterText() { return mEmpty; }
+ void setModified(EFilterModified behavior = FILTER_RESTART) { }
+
+ void resetTime(S32 timeout) { }
+ bool isTimedOut() { return false; }
+
+ bool isDefault() const { return true; }
+ bool isNotDefault() const { return false; }
+ void markDefault() { }
+ void resetDefault() { }
+
+ S32 getCurrentGeneration() const { return 0; }
+ S32 getFirstSuccessGeneration() const { return 0; }
+ S32 getFirstRequiredGeneration() const { return 0; }
+private:
+ std::string mEmpty;
+};
+
+class LLGLTFViewModel
+ : public LLFolderViewModel<LLGLTFSort, LLGLTFFolderItem, LLGLTFFolderItem, LLGLTFFilter>
+{
+public:
+ typedef LLFolderViewModel< LLGLTFSort, LLGLTFFolderItem, LLGLTFFolderItem, LLGLTFFilter> base_t;
+ LLGLTFViewModel();
+
+ void sort(LLFolderViewFolder* folder);
+ bool startDrag(std::vector<LLFolderViewModelItem*>& items) { return false; }
+
+private:
+};
+
+#endif
diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp
index 282a273be6..d015c0ed95 100644
--- a/indra/newview/lllogininstance.cpp
+++ b/indra/newview/lllogininstance.cpp
@@ -33,9 +33,6 @@
#include "stringize.h"
#include "llsdserialize.h"
-// llmessage (!)
-#include "llfiltersd2xmlrpc.h" // for xml_escape_string()
-
// login
#include "lllogin.h"
@@ -612,7 +609,7 @@ std::string construct_start_string()
<< position[VX] << "&"
<< position[VY] << "&"
<< position[VZ]);
- start = xml_escape_string(unescaped_start);
+ start = LLStringFn::xml_encode(unescaped_start, true);
break;
}
case LLSLURL::HOME_LOCATION:
diff --git a/indra/newview/llslurl.cpp b/indra/newview/llslurl.cpp
index 432ec3899a..d80cf2e80e 100644
--- a/indra/newview/llslurl.cpp
+++ b/indra/newview/llslurl.cpp
@@ -32,13 +32,15 @@
#include "llpanellogin.h"
#include "llviewercontrol.h"
#include "llviewernetwork.h"
-#include "llfiltersd2xmlrpc.h"
+
#include "curl/curl.h"
+
const char* LLSLURL::SLURL_HTTP_SCHEME = "http";
const char* LLSLURL::SLURL_HTTPS_SCHEME = "https";
const char* LLSLURL::SLURL_SECONDLIFE_SCHEME = "secondlife";
const char* LLSLURL::SLURL_SECONDLIFE_PATH = "secondlife";
const char* LLSLURL::SLURL_COM = "slurl.com";
+
// For DnD - even though www.slurl.com redirects to slurl.com in a browser, you can copy and drag
// text with www.slurl.com or a link explicitly pointing at www.slurl.com so testing for this
// version is required also.
@@ -437,7 +439,7 @@ std::string LLSLURL::getLoginString() const
LL_WARNS("AppInit") << "Unexpected SLURL type (" << (int)mType << ")for login string" << LL_ENDL;
break;
}
- return xml_escape_string(unescaped_start.str());
+ return LLStringFn::xml_encode(unescaped_start.str(), true);
}
bool LLSLURL::operator ==(const LLSLURL& rhs)
diff --git a/indra/newview/llversioninfo.cpp b/indra/newview/llversioninfo.cpp
index a571b5544b..4e8320b72a 100644
--- a/indra/newview/llversioninfo.cpp
+++ b/indra/newview/llversioninfo.cpp
@@ -76,37 +76,37 @@ LLVersionInfo::~LLVersionInfo()
{
}
-S32 LLVersionInfo::getMajor()
+S32 LLVersionInfo::getMajor() const
{
return LL_VIEWER_VERSION_MAJOR;
}
-S32 LLVersionInfo::getMinor()
+S32 LLVersionInfo::getMinor() const
{
return LL_VIEWER_VERSION_MINOR;
}
-S32 LLVersionInfo::getPatch()
+S32 LLVersionInfo::getPatch() const
{
return LL_VIEWER_VERSION_PATCH;
}
-U64 LLVersionInfo::getBuild()
+U64 LLVersionInfo::getBuild() const
{
return LL_VIEWER_VERSION_BUILD;
}
-std::string LLVersionInfo::getVersion()
+std::string LLVersionInfo::getVersion() const
{
return version;
}
-std::string LLVersionInfo::getShortVersion()
+std::string LLVersionInfo::getShortVersion() const
{
return short_version;
}
-std::string LLVersionInfo::getChannelAndVersion()
+std::string LLVersionInfo::getChannelAndVersion() const
{
if (mVersionChannel.empty())
{
@@ -117,7 +117,7 @@ std::string LLVersionInfo::getChannelAndVersion()
return mVersionChannel;
}
-std::string LLVersionInfo::getChannel()
+std::string LLVersionInfo::getChannel() const
{
return mWorkingChannelName;
}
@@ -128,7 +128,7 @@ void LLVersionInfo::resetChannel(const std::string& channel)
mVersionChannel.clear(); // Reset version and channel string til next use.
}
-LLVersionInfo::ViewerMaturity LLVersionInfo::getViewerMaturity()
+LLVersionInfo::ViewerMaturity LLVersionInfo::getViewerMaturity() const
{
ViewerMaturity maturity;
@@ -166,12 +166,12 @@ LLVersionInfo::ViewerMaturity LLVersionInfo::getViewerMaturity()
}
-std::string LLVersionInfo::getBuildConfig()
+std::string LLVersionInfo::getBuildConfig() const
{
return build_configuration;
}
-std::string LLVersionInfo::getReleaseNotes()
+std::string LLVersionInfo::getReleaseNotes() const
{
return mReleaseNotes;
}
diff --git a/indra/newview/llversioninfo.h b/indra/newview/llversioninfo.h
index aed43263a6..237b37a084 100644
--- a/indra/newview/llversioninfo.h
+++ b/indra/newview/llversioninfo.h
@@ -52,38 +52,38 @@ public:
~LLVersionInfo();
/// return the major version number as an integer
- S32 getMajor();
+ S32 getMajor() const;
/// return the minor version number as an integer
- S32 getMinor();
+ S32 getMinor() const;
/// return the patch version number as an integer
- S32 getPatch();
+ S32 getPatch() const;
/// return the build number as an integer
- U64 getBuild();
+ U64 getBuild() const;
/// return the full viewer version as a string like "2.0.0.200030"
- std::string getVersion();
+ std::string getVersion() const;
/// return the viewer version as a string like "2.0.0"
- std::string getShortVersion();
+ std::string getShortVersion() const;
/// return the viewer version and channel as a string
/// like "Second Life Release 2.0.0.200030"
- std::string getChannelAndVersion();
+ std::string getChannelAndVersion() const;
/// return the channel name, e.g. "Second Life"
- std::string getChannel();
+ std::string getChannel() const;
/// return the CMake build type
- std::string getBuildConfig();
+ std::string getBuildConfig() const;
/// reset the channel name used by the viewer.
void resetChannel(const std::string& channel);
/// return the bit width of an address
- S32 getAddressSize() { return ADDRESS_SIZE; }
+ S32 getAddressSize() const { return ADDRESS_SIZE; }
typedef enum
{
@@ -92,11 +92,11 @@ public:
BETA_VIEWER,
RELEASE_VIEWER
} ViewerMaturity;
- ViewerMaturity getViewerMaturity();
+ ViewerMaturity getViewerMaturity() const;
/// get the release-notes URL, once it becomes available -- until then,
/// return empty string
- std::string getReleaseNotes();
+ std::string getReleaseNotes() const;
private:
std::string version;
@@ -107,7 +107,7 @@ private:
std::string mWorkingChannelName;
// Storage for the "version and channel" string.
// This will get reset too.
- std::string mVersionChannel;
+ mutable std::string mVersionChannel;
std::string build_configuration;
std::string mReleaseNotes;
// Store unique_ptrs to the next couple things so we don't have to explain
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index ceda2675d5..3e1705b8a1 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -76,6 +76,7 @@
#include "llfloaterfonttest.h"
#include "llfloaterforgetuser.h"
#include "llfloatergesture.h"
+#include "llfloatergltfasseteditor.h"
#include "llfloatergodtools.h"
#include "llfloatergridstatus.h"
#include "llfloatergroups.h"
@@ -372,6 +373,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("forget_username", "floater_forget_user.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterForgetUser>);
LLFloaterReg::add("gestures", "floater_gesture.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterGesture>);
+ LLFloaterReg::add("gltf_asset_editor", "floater_gltf_asset_editor.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterGLTFAssetEditor>);
LLFloaterReg::add("god_tools", "floater_god_tools.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterGodTools>);
LLFloaterReg::add("grid_status", "floater_grid_status.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterGridStatus>);
LLFloaterReg::add("group_picker", "floater_choose_group.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterGroupPicker>);
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 9a9a316adf..2687938b35 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -8139,6 +8139,15 @@ class LLAdvancedClickGLTFUpload: public view_listener_t
}
};
+class LLAdvancedClickGLTFEdit : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ LLFloaterReg::showInstance("gltf_asset_editor");
+ return true;
+ }
+};
+
class LLAdvancedClickResizeWindow : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
@@ -9794,6 +9803,7 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedClickGLTFOpen(), "Advanced.ClickGLTFOpen");
view_listener_t::addMenu(new LLAdvancedClickGLTFSaveAs(), "Advanced.ClickGLTFSaveAs");
view_listener_t::addMenu(new LLAdvancedClickGLTFUpload(), "Advanced.ClickGLTFUpload");
+ view_listener_t::addMenu(new LLAdvancedClickGLTFEdit(), "Advanced.ClickGLTFEdit");
view_listener_t::addMenu(new LLAdvancedClickResizeWindow(), "Advanced.ClickResizeWindow");
view_listener_t::addMenu(new LLAdvancedPurgeShaderCache(), "Advanced.ClearShaderCache");
view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain");
diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp
index ede1542bd1..8049bb7a73 100644
--- a/indra/newview/llvoicewebrtc.cpp
+++ b/indra/newview/llvoicewebrtc.cpp
@@ -24,7 +24,6 @@
* $/LicenseInfo$
*/
#include <algorithm>
-#include <format>
#include "llvoicewebrtc.h"
#include "llsdutil.h"
diff --git a/indra/newview/llxmlrpclistener.cpp b/indra/newview/llxmlrpclistener.cpp
index 1148e81fd5..7c7bd98bcd 100644
--- a/indra/newview/llxmlrpclistener.cpp
+++ b/indra/newview/llxmlrpclistener.cpp
@@ -39,12 +39,6 @@
#include <boost/scoped_ptr.hpp>
#include <boost/range.hpp> // boost::begin(), boost::end()
-#ifdef LL_USESYSTEMLIBS
-#include <xmlrpc.h>
-#else
-#include <xmlrpc-epi/xmlrpc.h>
-#endif
-
#include "curl/curl.h"
// other Linden headers
@@ -178,13 +172,6 @@ public:
static const CURLcodeMapper sCURLcodeMapper;
-LLXMLRPCListener::LLXMLRPCListener(const std::string& pumpname):
- mBoundListener(LLEventPumps::instance().
- obtain(pumpname).
- listen("LLXMLRPCListener", boost::bind(&LLXMLRPCListener::process, this, _1)))
-{
-}
-
/**
* Capture an outstanding LLXMLRPCTransaction and poll it periodically until
* done.
@@ -213,38 +200,20 @@ public:
mMethod(command["method"]),
mReplyPump(command["reply"])
{
- // LL_ERRS if any of these are missing
- const char* required[] = { "uri", "method", "reply" };
- // optional: "options" (array of string)
- // Validate the request
- std::set<std::string> missing;
- for (const char** ri = boost::begin(required); ri != boost::end(required); ++ri)
+ // LL_ERRS if any of these keys are missing or empty
+ if (mUri.empty() || mMethod.empty() || mReplyPump.empty())
{
- // If the command does not contain this required entry, add it to 'missing'.
- if (! command.has(*ri))
- {
- missing.insert(*ri);
- }
- }
- if (! missing.empty())
- {
- LL_ERRS("LLXMLRPCListener") << mMethod << " request missing params: ";
- const char* separator = "";
- for (std::set<std::string>::const_iterator mi(missing.begin()), mend(missing.end());
- mi != mend; ++mi)
- {
- LL_CONT << separator << *mi;
- separator = ", ";
- }
- LL_CONT << LL_ENDL;
+ LL_ERRS("LLXMLRPCListener")
+ << "Some params are missing: "
+ << "reply: '" << mReplyPump << "', "
+ << "method: '" << mMethod << "', "
+ << "uri: '" << mUri << "'"
+ << LL_ENDL;
}
- // Build the XMLRPC request.
- XMLRPC_REQUEST request = XMLRPC_RequestNew();
- XMLRPC_RequestSetMethodName(request, mMethod.c_str());
- XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
- XMLRPC_VALUE xparams = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
- LLSD params(command["params"]);
+ LLSD request_params = LLSD::emptyMap();
+
+ LLSD params = command.get("params");
if (params.isMap())
{
for (LLSD::map_const_iterator pi(params.beginMap()), pend(params.endMap());
@@ -252,44 +221,33 @@ public:
{
std::string name(pi->first);
LLSD param(pi->second);
- if (param.isString())
+ switch (param.type())
{
- XMLRPC_VectorAppendString(xparams, name.c_str(), param.asString().c_str(), 0);
- }
- else if (param.isInteger() || param.isBoolean())
- {
- XMLRPC_VectorAppendInt(xparams, name.c_str(), param.asInteger());
- }
- else if (param.isReal())
- {
- XMLRPC_VectorAppendDouble(xparams, name.c_str(), param.asReal());
- }
- else
- {
- LL_ERRS("LLXMLRPCListener") << mMethod << " request param "
- << name << " has unknown type: " << param << LL_ENDL;
+ case LLSD::TypeString:
+ case LLSD::TypeInteger:
+ case LLSD::TypeReal:
+ request_params.insert(name, param);
+ break;
+ case LLSD::TypeBoolean:
+ request_params.insert(name, param.asInteger());
+ break;
+ default:
+ LL_ERRS("LLXMLRPCListener") << mMethod
+ << " request param '" << name << "' has unknown type: " << param << LL_ENDL;
}
}
}
- LLSD options(command["options"]);
+
+ LLSD options = command.get("options");
if (options.isArray())
{
- XMLRPC_VALUE xoptions = XMLRPC_CreateVector("options", xmlrpc_vector_array);
- for (LLSD::array_const_iterator oi(options.beginArray()), oend(options.endArray());
- oi != oend; ++oi)
- {
- XMLRPC_VectorAppendString(xoptions, NULL, oi->asString().c_str(), 0);
- }
- XMLRPC_AddValueToVector(xparams, xoptions);
+ request_params.insert("options", options);
}
- XMLRPC_RequestSetData(request, xparams);
- mTransaction.reset(new LLXMLRPCTransaction(mUri, request, true, command.has("http_params")? LLSD(command["http_params"]) : LLSD()));
+ LLSD http_params = command.get("http_params");
+ mTransaction.reset(new LLXMLRPCTransaction(mUri, mMethod, request_params, http_params));
mPreviousStatus = mTransaction->status(NULL);
- // Free the XMLRPC_REQUEST object and the attached data values.
- XMLRPC_RequestFree(request, 1);
-
// Now ensure that we get regular callbacks to poll for completion.
mBoundListener =
LLEventPumps::instance().
@@ -323,7 +281,7 @@ public:
data["error"] = "";
data["transfer_rate"] = 0.0;
LLEventPump& replyPump(LLEventPumps::instance().obtain(mReplyPump));
- if (! done)
+ if (!done)
{
// Not done yet, carry on.
if (status == LLXMLRPCTransaction::StatusDownloading
@@ -367,10 +325,8 @@ public:
// Given 'message', need we care?
if (status == LLXMLRPCTransaction::StatusComplete)
{
- // Success! Parse data.
- std::string status_string(data["status"]);
- data["responses"] = parseResponse(status_string);
- data["status"] = status_string;
+ // Success! Retrieve response data.
+ data["responses"] = mTransaction->response();
}
// whether successful or not, send reply on requested LLEventPump
@@ -388,159 +344,6 @@ public:
}
private:
- /// Derived from LLUserAuth::parseResponse() and parseOptionInto()
- LLSD parseResponse(std::string& status_string)
- {
- // Extract every member into data["responses"] (a map of string
- // values).
- XMLRPC_REQUEST response = mTransaction->response();
- if (! response)
- {
- LL_DEBUGS("LLXMLRPCListener") << "No response" << LL_ENDL;
- return LLSD();
- }
-
- XMLRPC_VALUE param = XMLRPC_RequestGetData(response);
- if (! param)
- {
- LL_DEBUGS("LLXMLRPCListener") << "Response contains no data" << LL_ENDL;
- return LLSD();
- }
-
- // Now, parse everything
- return parseValues(status_string, "", param);
- }
-
- LLSD parseValue(std::string& status_string, const std::string& key, const std::string& key_pfx, XMLRPC_VALUE param)
- {
- LLSD response;
-
- XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(param);
- switch (type)
- {
- case xmlrpc_type_empty:
- LL_INFOS("LLXMLRPCListener") << "Empty result for key " << key_pfx << key << LL_ENDL;
- break;
- case xmlrpc_type_base64:
- {
- S32 len = XMLRPC_GetValueStringLen(param);
- const char* buf = XMLRPC_GetValueBase64(param);
- if ((len > 0) && buf)
- {
- // During implementation this code was not tested
- // If you encounter this, please make sure this is correct,
- // then remove llassert
- llassert(0);
-
- LLSD::Binary data;
- data.resize(len);
- memcpy((void*)&data[0], (void*)buf, len);
- response = data;
- }
- else
- {
- LL_WARNS("LLXMLRPCListener") << "Potentially malformed xmlrpc_type_base64 for key "
- << key_pfx << key << LL_ENDL;
- }
- break;
- }
- case xmlrpc_type_boolean:
- {
- response = LLSD::Boolean(XMLRPC_GetValueBoolean(param));
- LL_DEBUGS("LLXMLRPCListener") << "val: " << response << LL_ENDL;
- break;
- }
- case xmlrpc_type_datetime:
- {
- std::string iso8601_date(XMLRPC_GetValueDateTime_ISO8601(param));
- LL_DEBUGS("LLXMLRPCListener") << "val: " << iso8601_date << LL_ENDL;
- response = LLSD::Date(iso8601_date);
- break;
- }
- case xmlrpc_type_double:
- {
- response = LLSD::Real(XMLRPC_GetValueDouble(param));
- LL_DEBUGS("LLXMLRPCListener") << "val: " << response << LL_ENDL;
- break;
- }
- case xmlrpc_type_int:
- {
- response = LLSD::Integer(XMLRPC_GetValueInt(param));
- LL_DEBUGS("LLXMLRPCListener") << "val: " << response << LL_ENDL;
- break;
- }
- case xmlrpc_type_string:
- {
- response = LLSD::String(XMLRPC_GetValueString(param));
- LL_DEBUGS("LLXMLRPCListener") << "val: " << response << LL_ENDL;
- break;
- }
- case xmlrpc_type_mixed:
- case xmlrpc_type_array:
- {
- // We expect this to be an array of submaps. Walk the array,
- // recursively parsing each submap and collecting them.
- LLSD array;
- int i = 0; // for descriptive purposes
- for (XMLRPC_VALUE row = XMLRPC_VectorRewind(param); row;
- row = XMLRPC_VectorNext(param), ++i)
- {
- // Recursive call. For the lower-level key_pfx, if 'key'
- // is "foo", pass "foo[0]:", then "foo[1]:", etc. In the
- // nested call, a subkey "bar" will then be logged as
- // "foo[0]:bar", and so forth.
- // Parse the scalar subkey/value pairs from this array
- // entry into a temp submap. Collect such submaps in 'array'.
-
- array.append(parseValue(status_string, "",
- STRINGIZE(key_pfx << key << '[' << i << "]:"),
- row));
- }
- // Having collected an 'array' of 'submap's, insert that whole
- // 'array' as the value of this 'key'.
- response = array;
- break;
- }
- case xmlrpc_type_struct:
- {
- response = parseValues(status_string,
- STRINGIZE(key_pfx << key << ':'),
- param);
- break;
- }
- case xmlrpc_type_none: // Not expected
- default:
- // whoops - unrecognized type
- LL_WARNS("LLXMLRPCListener") << "Unhandled xmlrpc type " << type << " for key "
- << key_pfx << key << LL_ENDL;
- response = STRINGIZE("<bad XMLRPC type " << type << '>');
- status_string = "BadType";
- }
- return response;
- }
-
- /**
- * Parse key/value pairs from a given XMLRPC_VALUE into an LLSD map.
- * @param key_pfx Used to describe a given key in log messages. At top
- * level, pass "". When parsing an options array, pass the top-level key
- * name of the array plus the index of the array entry; to this we'll
- * append the subkey of interest.
- * @param param XMLRPC_VALUE iterator. At top level, pass
- * XMLRPC_RequestGetData(XMLRPC_REQUEST).
- */
- LLSD parseValues(std::string& status_string, const std::string& key_pfx, XMLRPC_VALUE param)
- {
- LLSD responses;
- for (XMLRPC_VALUE current = XMLRPC_VectorRewind(param); current;
- current = XMLRPC_VectorNext(param))
- {
- std::string key(XMLRPC_GetValueID(current));
- LL_DEBUGS("LLXMLRPCListener") << "key: " << key_pfx << key << LL_ENDL;
- responses.insert(key, parseValue(status_string, key, key_pfx, current));
- }
- return responses;
- }
-
const LLReqID mReqID;
const std::string mUri;
const std::string mMethod;
@@ -550,11 +353,18 @@ private:
LLXMLRPCTransaction::EStatus mPreviousStatus; // To detect state changes.
};
-bool LLXMLRPCListener::process(const LLSD& command)
+LLXMLRPCListener::LLXMLRPCListener(const std::string& pumpname)
+: mBoundListener(LLEventPumps::instance().obtain(pumpname).listen
+(
+ "LLXMLRPCListener",
+ [&](const LLSD& command) -> bool
+ {
+ // Allocate a new heap Poller, but do not save a pointer to it. Poller
+ // will check its own status and free itself on completion of the request.
+ (new Poller(command));
+ // Conventional event listener return
+ return false;
+ }
+))
{
- // Allocate a new heap Poller, but do not save a pointer to it. Poller
- // will check its own status and free itself on completion of the request.
- (new Poller(command));
- // conventional event listener return
- return false;
}
diff --git a/indra/newview/llxmlrpclistener.h b/indra/newview/llxmlrpclistener.h
index aaed98eec5..fd75acb8b1 100644
--- a/indra/newview/llxmlrpclistener.h
+++ b/indra/newview/llxmlrpclistener.h
@@ -42,9 +42,6 @@ public:
/// Specify the pump name on which to listen
LLXMLRPCListener(const std::string& pumpname);
- /// Handle request events on the event pump specified at construction time
- bool process(const LLSD& command);
-
private:
LLTempBoundListener mBoundListener;
};
diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp
index ec6e22cd7a..55622fb6b7 100644
--- a/indra/newview/llxmlrpctransaction.cpp
+++ b/indra/newview/llxmlrpctransaction.cpp
@@ -42,22 +42,11 @@
#include "bufferarray.h"
#include "llversioninfo.h"
#include "llviewercontrol.h"
+#include "llxmlnode.h"
#include "stringize.h"
// Have to include these last to avoid queue redefinition!
-#ifdef LL_USESYSTEMLIBS
-#include <xmlrpc.h>
-#else
-#include <xmlrpc-epi/xmlrpc.h>
-#endif
-// <xmlrpc-epi/queue.h> contains a harmful #define queue xmlrpc_queue. This
-// breaks any use of std::queue. Ditch that #define: if any of our code wants
-// to reference xmlrpc_queue, let it reference it directly.
-#if defined(queue)
-#undef queue
-#endif
-
#include "llappviewer.h"
#include "lltrans.h"
@@ -75,111 +64,6 @@ namespace boost
// nothing.
static LLXMLRPCListener listener("LLXMLRPCTransaction");
-LLXMLRPCValue LLXMLRPCValue::operator[](const char* id) const
-{
- return LLXMLRPCValue(XMLRPC_VectorGetValueWithID(mV, id));
-}
-
-std::string LLXMLRPCValue::asString() const
-{
- const char* s = XMLRPC_GetValueString(mV);
- return s ? s : "";
-}
-
-int LLXMLRPCValue::asInt() const { return XMLRPC_GetValueInt(mV); }
-bool LLXMLRPCValue::asBool() const { return XMLRPC_GetValueBoolean(mV) != 0; }
-double LLXMLRPCValue::asDouble() const { return XMLRPC_GetValueDouble(mV); }
-
-LLXMLRPCValue LLXMLRPCValue::rewind()
-{
- return LLXMLRPCValue(XMLRPC_VectorRewind(mV));
-}
-
-LLXMLRPCValue LLXMLRPCValue::next()
-{
- return LLXMLRPCValue(XMLRPC_VectorNext(mV));
-}
-
-bool LLXMLRPCValue::isValid() const
-{
- return mV != NULL;
-}
-
-LLXMLRPCValue LLXMLRPCValue::createArray()
-{
- return LLXMLRPCValue(XMLRPC_CreateVector(NULL, xmlrpc_vector_array));
-}
-
-LLXMLRPCValue LLXMLRPCValue::createStruct()
-{
- return LLXMLRPCValue(XMLRPC_CreateVector(NULL, xmlrpc_vector_struct));
-}
-
-
-void LLXMLRPCValue::append(LLXMLRPCValue& v)
-{
- XMLRPC_AddValueToVector(mV, v.mV);
-}
-
-void LLXMLRPCValue::appendString(const std::string& v)
-{
- XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueString(NULL, v.c_str(), 0));
-}
-
-void LLXMLRPCValue::appendInt(int v)
-{
- XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueInt(NULL, v));
-}
-
-void LLXMLRPCValue::appendBool(bool v)
-{
- XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueBoolean(NULL, v));
-}
-
-void LLXMLRPCValue::appendDouble(double v)
-{
- XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueDouble(NULL, v));
-}
-
-
-void LLXMLRPCValue::append(const char* id, LLXMLRPCValue& v)
-{
- XMLRPC_SetValueID(v.mV, id, 0);
- XMLRPC_AddValueToVector(mV, v.mV);
-}
-
-void LLXMLRPCValue::appendString(const char* id, const std::string& v)
-{
- XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueString(id, v.c_str(), 0));
-}
-
-void LLXMLRPCValue::appendInt(const char* id, int v)
-{
- XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueInt(id, v));
-}
-
-void LLXMLRPCValue::appendBool(const char* id, bool v)
-{
- XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueBoolean(id, v));
-}
-
-void LLXMLRPCValue::appendDouble(const char* id, double v)
-{
- XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueDouble(id, v));
-}
-
-void LLXMLRPCValue::cleanup()
-{
- XMLRPC_CleanupValue(mV);
- mV = NULL;
-}
-
-XMLRPC_VALUE LLXMLRPCValue::getValue() const
-{
- return mV;
-}
-
-
class LLXMLRPCTransaction::Handler : public LLCore::HttpHandler
{
public:
@@ -192,6 +76,9 @@ public:
private:
+ bool parseResponse(LLXMLNodePtr root);
+ bool parseValue(LLSD& target, LLXMLNodePtr source);
+
LLXMLRPCTransaction::Impl *mImpl;
LLCore::HttpRequest::ptr_t mRequest;
};
@@ -213,26 +100,26 @@ public:
LLCore::HttpHandle mPostH;
std::string mURI;
-
std::string mProxyAddress;
std::string mResponseText;
- XMLRPC_REQUEST mResponse;
+ LLSD mResponseData;
+
std::string mCertStore;
- LLSD mErrorCertData;
+ LLSD mErrorCertData;
- Impl(const std::string& uri, XMLRPC_REQUEST request, bool useGzip, const LLSD& httpParams);
- Impl(const std::string& uri,
- const std::string& method, LLXMLRPCValue params, bool useGzip);
- ~Impl();
+ Impl
+ (
+ const std::string& uri,
+ const std::string& method,
+ const LLSD& params,
+ const LLSD& httpParams
+ );
bool process();
void setStatus(EStatus code, const std::string& message = "", const std::string& uri = "");
void setHttpStatus(const LLCore::HttpStatus &status);
-
-private:
- void init(XMLRPC_REQUEST request, bool useGzip, const LLSD& httpParams);
};
LLXMLRPCTransaction::Handler::Handler(LLCore::HttpRequest::ptr_t &request,
@@ -275,89 +162,113 @@ void LLXMLRPCTransaction::Handler::onCompleted(LLCore::HttpHandle handle,
mImpl->setStatus(LLXMLRPCTransaction::StatusComplete);
mImpl->mTransferStats = response->getTransferStats();
- // the contents of a buffer array are potentially noncontiguous, so we
+ // The contents of a buffer array are potentially noncontiguous, so we
// will need to copy them into an contiguous block of memory for XMLRPC.
LLCore::BufferArray *body = response->getBody();
- char * bodydata = new char[body->size()];
+ mImpl->mResponseText.resize(body->size());
- body->read(0, bodydata, body->size());
+ body->read(0, mImpl->mResponseText.data(), body->size());
- mImpl->mResponse = XMLRPC_REQUEST_FromXML(bodydata, static_cast<int>(body->size()), 0);
+ LLXMLNodePtr root;
+ if (!LLXMLNode::parseBuffer(mImpl->mResponseText.data(), body->size(), root, nullptr))
+ {
+ LL_WARNS() << "Failed parsing XML response; request URI: " << mImpl->mURI << LL_ENDL;
+ return;
+ }
- delete[] bodydata;
+ if (!parseResponse(root))
+ return;
- bool hasError = false;
- bool hasFault = false;
- int faultCode = 0;
- std::string faultString;
+ LL_INFOS() << "XML response parsed successfully; request URI: " << mImpl->mURI << LL_ENDL;
+}
- LLXMLRPCValue error(XMLRPC_RequestGetError(mImpl->mResponse));
- if (error.isValid())
+struct XMLTreeNode final : public LLSD::TreeNode
+{
+ XMLTreeNode(const LLXMLNodePtr impl)
+ : mImpl(impl)
+ , mFirstChild(impl ? create(impl->getFirstChild()) : nullptr)
+ , mNextSibling(impl ? create(impl->getNextSibling()) : nullptr)
{
- hasError = true;
- faultCode = error["faultCode"].asInt();
- faultString = error["faultString"].asString();
}
- else if (XMLRPC_ResponseIsFault(mImpl->mResponse))
+
+ static XMLTreeNode* create(LLXMLNodePtr node) { return node ? new XMLTreeNode(node) : nullptr; }
+
+ virtual bool hasName(const LLSD::String& name) const override { return mImpl && mImpl->hasName(name); }
+ virtual LLSD::String getTextContents() const override { return mImpl ? mImpl->getTextContents() : LLStringUtil::null; }
+ virtual TreeNode* getFirstChild() const override { return mFirstChild.get(); }
+ virtual TreeNode* getNextSibling() const override { return mNextSibling.get(); }
+
+private:
+ const LLXMLNodePtr mImpl;
+ const std::shared_ptr<XMLTreeNode> mFirstChild;
+ const std::shared_ptr<XMLTreeNode> mNextSibling;
+};
+
+bool LLXMLRPCTransaction::Handler::parseResponse(LLXMLNodePtr root)
+{
+ // We have alreasy checked in LLXMLNode::parseBuffer()
+ // that root contains exactly one child
+ if (!root->hasName("methodResponse"))
{
- hasFault = true;
- faultCode = XMLRPC_GetResponseFaultCode(mImpl->mResponse);
- faultString = XMLRPC_GetResponseFaultString(mImpl->mResponse);
+ LL_WARNS() << "Invalid root element in XML response; request URI: " << mImpl->mURI << LL_ENDL;
+ return false;
}
- if (hasError || hasFault)
+ LLXMLNodePtr first = root->getFirstChild();
+ LLXMLNodePtr second = first->getFirstChild();
+ if (!first->getNextSibling() && second && !second->getNextSibling())
{
- mImpl->setStatus(LLXMLRPCTransaction::StatusXMLRPCError);
-
- LL_WARNS() << "LLXMLRPCTransaction XMLRPC "
- << (hasError ? "error " : "fault ")
- << faultCode << ": "
- << faultString << LL_ENDL;
- LL_WARNS() << "LLXMLRPCTransaction request URI: "
- << mImpl->mURI << LL_ENDL;
+ if (first->hasName("fault"))
+ {
+ LLSD fault;
+ if (parseValue(fault, second) &&
+ fault.isMap() && fault.has("faultCode") && fault.has("faultString"))
+ {
+ LL_WARNS() << "Request failed;"
+ << " faultCode: '" << fault.get("faultCode").asString() << "',"
+ << " faultString: '" << fault.get("faultString").asString() << "',"
+ << " request URI: " << mImpl->mURI << LL_ENDL;
+ return false;
+ }
+ }
+ else if (first->hasName("params") &&
+ second->hasName("param") && !second->getNextSibling())
+ {
+ LLXMLNodePtr third = second->getFirstChild();
+ if (third && !third->getNextSibling() && parseValue(mImpl->mResponseData, third))
+ {
+ return true;
+ }
+ }
}
-}
-
-//=========================================================================
+ LL_WARNS() << "Invalid response format; request URI: " << mImpl->mURI << LL_ENDL;
-LLXMLRPCTransaction::Impl::Impl(const std::string& uri,
- XMLRPC_REQUEST request, bool useGzip, const LLSD& httpParams)
- : mHttpRequest(),
- mStatus(LLXMLRPCTransaction::StatusNotStarted),
- mURI(uri),
- mResponse(0)
-{
- init(request, useGzip, httpParams);
+ return false;
}
-
-LLXMLRPCTransaction::Impl::Impl(const std::string& uri,
- const std::string& method, LLXMLRPCValue params, bool useGzip)
- : mHttpRequest(),
- mStatus(LLXMLRPCTransaction::StatusNotStarted),
- mURI(uri),
- mResponse(0)
+bool LLXMLRPCTransaction::Handler::parseValue(LLSD& target, LLXMLNodePtr source)
{
- XMLRPC_REQUEST request = XMLRPC_RequestNew();
- XMLRPC_RequestSetMethodName(request, method.c_str());
- XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
- XMLRPC_RequestSetData(request, params.getValue());
-
- init(request, useGzip, LLSD());
- // DEV-28398: without this XMLRPC_RequestFree() call, it looks as though
- // the 'request' object is simply leaked. It's less clear to me whether we
- // should also ask to free request value data (second param 1), since the
- // data come from 'params'.
- XMLRPC_RequestFree(request, 1);
+ XMLTreeNode tn(source);
+ return target.fromXMLRPCValue(&tn);
}
-void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip, const LLSD& httpParams)
+//=========================================================================
+
+LLXMLRPCTransaction::Impl::Impl
+(
+ const std::string& uri,
+ const std::string& method,
+ const LLSD& params,
+ const LLSD& http_params
+)
+ : mHttpRequest()
+ , mStatus(LLXMLRPCTransaction::StatusNotStarted)
+ , mURI(uri)
{
LLCore::HttpOptions::ptr_t httpOpts;
LLCore::HttpHeaders::ptr_t httpHeaders;
-
if (!mHttpRequest)
{
mHttpRequest = LLCore::HttpRequest::ptr_t(new LLCore::HttpRequest);
@@ -366,37 +277,34 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip, const
// LLRefCounted starts with a 1 ref, so don't add a ref in the smart pointer
httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions());
- // delay between repeats will start from 5 sec and grow to 20 sec with each repeat
+ // Delay between repeats will start from 5 sec and grow to 20 sec with each repeat
httpOpts->setMinBackoff(5E6L);
httpOpts->setMaxBackoff(20E6L);
- httpOpts->setTimeout(httpParams.has("timeout") ? httpParams["timeout"].asInteger() : 40L);
- if (httpParams.has("retries"))
+ httpOpts->setTimeout(http_params.has("timeout") ? http_params["timeout"].asInteger() : 40L);
+ if (http_params.has("retries"))
{
- httpOpts->setRetries(httpParams["retries"].asInteger());
+ httpOpts->setRetries(http_params["retries"].asInteger());
}
- if (httpParams.has("DNSCacheTimeout"))
+ if (http_params.has("DNSCacheTimeout"))
{
- httpOpts->setDNSCacheTimeout(httpParams["DNSCacheTimeout"].asInteger());
+ httpOpts->setDNSCacheTimeout(http_params["DNSCacheTimeout"].asInteger());
}
bool vefifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert");
mCertStore = gSavedSettings.getString("CertStore");
- httpOpts->setSSLVerifyPeer( vefifySSLCert );
- httpOpts->setSSLVerifyHost( vefifySSLCert ? 2 : 0);
+ httpOpts->setSSLVerifyPeer(vefifySSLCert);
+ httpOpts->setSSLVerifyHost(vefifySSLCert ? 2 : 0);
// LLRefCounted starts with a 1 ref, so don't add a ref in the smart pointer
httpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders());
httpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_XML);
- std::string user_agent = stringize(
- LLVersionInfo::instance().getChannel(), ' ',
- LLVersionInfo::instance().getMajor(), '.',
- LLVersionInfo::instance().getMinor(), '.',
- LLVersionInfo::instance().getPatch(), " (",
- LLVersionInfo::instance().getBuild(), ')');
+ const LLVersionInfo& vi(LLVersionInfo::instance());
+ std::string user_agent = vi.getChannel() + llformat(" %d.%d.%d (%llu)",
+ vi.getMajor(), vi.getMinor(), vi.getPatch(), vi.getBuild());
httpHeaders->append(HTTP_OUT_HEADER_USER_AGENT, user_agent);
@@ -404,31 +312,19 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip, const
//This might help with bug #503 */
//httpOpts->setDNSCacheTimeout(-1);
- LLCore::BufferArray::ptr_t body = LLCore::BufferArray::ptr_t(new LLCore::BufferArray());
+ std::string request =
+ "<?xml version=\"1.0\"?><methodCall><methodName>" + method +
+ "</methodName><params><param>" + params.asXMLRPCValue() +
+ "</param></params></methodCall>";
- // TODO: See if there is a way to serialize to a preallocated buffer I'm
- // not fond of the copy here.
- int requestSize(0);
- char * requestText = XMLRPC_REQUEST_ToXML(request, &requestSize);
-
- body->append(requestText, requestSize);
+ LLCore::BufferArray::ptr_t body = LLCore::BufferArray::ptr_t(new LLCore::BufferArray());
- XMLRPC_Free(requestText);
+ body->append(request.c_str(), request.size());
- mHandler = LLXMLRPCTransaction::Handler::ptr_t(new Handler( mHttpRequest, this ));
+ mHandler = LLXMLRPCTransaction::Handler::ptr_t(new Handler(mHttpRequest, this));
mPostH = mHttpRequest->requestPost(LLCore::HttpRequest::DEFAULT_POLICY_ID,
mURI, body.get(), httpOpts, httpHeaders, mHandler);
-
-}
-
-
-LLXMLRPCTransaction::Impl::~Impl()
-{
- if (mResponse)
- {
- XMLRPC_RequestFree(mResponse, 1);
- }
}
bool LLXMLRPCTransaction::Impl::process()
@@ -539,18 +435,16 @@ void LLXMLRPCTransaction::Impl::setHttpStatus(const LLCore::HttpStatus &status)
}
-
-LLXMLRPCTransaction::LLXMLRPCTransaction(
- const std::string& uri, XMLRPC_REQUEST request, bool useGzip, const LLSD& httpParams)
-: impl(* new Impl(uri, request, useGzip, httpParams))
-{ }
-
-
-LLXMLRPCTransaction::LLXMLRPCTransaction(
+LLXMLRPCTransaction::LLXMLRPCTransaction
+(
const std::string& uri,
- const std::string& method, LLXMLRPCValue params, bool useGzip)
-: impl(* new Impl(uri, method, params, useGzip))
-{ }
+ const std::string& method,
+ const LLSD& params,
+ const LLSD& http_params
+)
+: impl(*new Impl(uri, method, params, http_params))
+{
+}
LLXMLRPCTransaction::~LLXMLRPCTransaction()
{
@@ -590,14 +484,9 @@ std::string LLXMLRPCTransaction::statusURI()
return impl.mStatusURI;
}
-XMLRPC_REQUEST LLXMLRPCTransaction::response()
-{
- return impl.mResponse;
-}
-
-LLXMLRPCValue LLXMLRPCTransaction::responseValue()
+const LLSD& LLXMLRPCTransaction::response()
{
- return LLXMLRPCValue(XMLRPC_RequestGetData(impl.mResponse));
+ return impl.mResponseData;
}
diff --git a/indra/newview/llxmlrpctransaction.h b/indra/newview/llxmlrpctransaction.h
index 4c8796f936..f7a38f5f90 100644
--- a/indra/newview/llxmlrpctransaction.h
+++ b/indra/newview/llxmlrpctransaction.h
@@ -29,73 +29,22 @@
#include <string>
-typedef struct _xmlrpc_request* XMLRPC_REQUEST;
-typedef struct _xmlrpc_value* XMLRPC_VALUE;
- // foward decl of types from xmlrpc.h (this usage is type safe)
-class LLCertificate;
-
-class LLXMLRPCValue
- // a c++ wrapper around XMLRPC_VALUE
-{
-public:
- LLXMLRPCValue() : mV(NULL) { }
- LLXMLRPCValue(XMLRPC_VALUE value) : mV(value) { }
-
- bool isValid() const;
-
- std::string asString() const;
- int asInt() const;
- bool asBool() const;
- double asDouble() const;
-
- LLXMLRPCValue operator[](const char*) const;
-
- LLXMLRPCValue rewind();
- LLXMLRPCValue next();
-
- static LLXMLRPCValue createArray();
- static LLXMLRPCValue createStruct();
-
- void append(LLXMLRPCValue&);
- void appendString(const std::string&);
- void appendInt(int);
- void appendBool(bool);
- void appendDouble(double);
- void appendValue(LLXMLRPCValue&);
-
- void append(const char*, LLXMLRPCValue&);
- void appendString(const char*, const std::string&);
- void appendInt(const char*, int);
- void appendBool(const char*, bool);
- void appendDouble(const char*, double);
- void appendValue(const char*, LLXMLRPCValue&);
-
- void cleanup();
- // only call this on the top level created value
-
- XMLRPC_VALUE getValue() const;
-
-private:
- XMLRPC_VALUE mV;
-};
-
-
+/// An asynchronous request and responses via XML-RPC
class LLXMLRPCTransaction
- // an asynchronous request and responses via XML-RPC
{
public:
- LLXMLRPCTransaction(const std::string& uri,
- XMLRPC_REQUEST request, bool useGzip = true, const LLSD& httpParams = LLSD());
- // does not take ownership of the request object
- // request can be freed as soon as the transaction is constructed
-
- LLXMLRPCTransaction(const std::string& uri,
- const std::string& method, LLXMLRPCValue params, bool useGzip = true);
- // *does* take control of the request value, you must not free it
+ LLXMLRPCTransaction
+ (
+ const std::string& uri,
+ const std::string& method,
+ const LLSD& params,
+ const LLSD& http_params = LLSD()
+ );
~LLXMLRPCTransaction();
- typedef enum e_status {
+ typedef enum e_status
+ {
StatusNotStarted,
StatusStarted,
StatusDownloading,
@@ -105,26 +54,25 @@ public:
StatusOtherError
} EStatus;
+ /// Run the request a little, returns true when done
bool process();
- // run the request a little, returns true when done
+ /// Return a status, and extended CURL code, if code isn't null
EStatus status(int* curlCode);
- // return status, and extended CURL code, if code isn't null
LLSD getErrorCertData();
+
+ /// Return a message string, suitable for showing the user
std::string statusMessage();
- // return a message string, suitable for showing the user
+
+ /// Return a URI for the user with more information (can be empty)
std::string statusURI();
- // return a URI for the user with more information
- // can be empty
- XMLRPC_REQUEST response();
- LLXMLRPCValue responseValue();
- // only valid if StatusComplete, otherwise NULL
- // retains ownership of the result object, don't free it
+ /// Only non-empty if StatusComplete, otherwise Undefined
+ const LLSD& response();
+ /// Only valid if StsatusComplete, otherwise 0.0
F64 transferRate();
- // only valid if StsatusComplete, otherwise 0.0
private:
class Handler;
@@ -133,6 +81,4 @@ private:
Impl& impl;
};
-
-
#endif // LLXMLRPCTRANSACTION_H
diff --git a/indra/newview/skins/default/xui/da/floater_about.xml b/indra/newview/skins/default/xui/da/floater_about.xml
index 7bcae69779..604eb7c58f 100644
--- a/indra/newview/skins/default/xui/da/floater_about.xml
+++ b/indra/newview/skins/default/xui/da/floater_about.xml
@@ -69,7 +69,6 @@ OpenSSL Copyright (C) 1998-2002 The OpenSSL Project.
PCRE Copyright (c) 1997-2008 University of Cambridge
SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
-xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
xxHash Copyright (C) 2012-2020 Yann Collet.
zlib Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler.
google-perftools Copyright (c) 2005, Google Inc.
diff --git a/indra/newview/skins/default/xui/de/floater_about.xml b/indra/newview/skins/default/xui/de/floater_about.xml
index 10ccf0d5da..320db7f654 100644
--- a/indra/newview/skins/default/xui/de/floater_about.xml
+++ b/indra/newview/skins/default/xui/de/floater_about.xml
@@ -28,7 +28,6 @@ mit Open-Source-Beiträgen von:</text>
PCRE Copyright (c) 1997-2012 University of Cambridge.
SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga.
SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com).
- xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
xxHash Copyright (C) 2012-2020 Yann Collet.
zlib Copyright (C) 1995-2012 Jean-loup Gailly und Mark Adler.
diff --git a/indra/newview/skins/default/xui/en/floater_about.xml b/indra/newview/skins/default/xui/en/floater_about.xml
index ff2fa93cbb..126cd84d56 100644
--- a/indra/newview/skins/default/xui/en/floater_about.xml
+++ b/indra/newview/skins/default/xui/en/floater_about.xml
@@ -111,7 +111,6 @@ Dummy Name replaced at run time
PCRE Copyright (c) 1997-2012 University of Cambridge
SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
xxHash Copyright (C) 2012-2020 Yann Collet.
zlib Copyright (C) 1995-2012 Jean-loup Gailly and Mark Adler.
diff --git a/indra/newview/skins/default/xui/en/floater_gltf_asset_editor.xml b/indra/newview/skins/default/xui/en/floater_gltf_asset_editor.xml
new file mode 100644
index 0000000000..b17d0aa5b6
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_gltf_asset_editor.xml
@@ -0,0 +1,284 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ legacy_header_height="18"
+ can_resize="true"
+ default_tab_group="1"
+ height="530"
+ width="256"
+ min_height="400"
+ min_width="200"
+ layout="topleft"
+ name="gltf asset editor"
+ title="[OBJECT_NAME]">
+ <floater.string name="floater_title" value="GLTF Scene Editor"/>
+ <floater.string name="scene_tittle" value="Scene"/>
+ <floater.string name="node_tittle" value="Node"/>
+ <floater.string name="mesh_tittle" value="Mesh"/>
+ <floater.string name="skin_tittle" value="Skin"/>
+
+ <layout_stack
+ name="main_layout"
+ orientation="vertical"
+ follows="all"
+ bottom="-1"
+ top="16"
+ left="5"
+ right="-1"
+ border_size="0">
+
+ <layout_panel
+ name="top_lp"
+ border="true"
+ bevel_style="in"
+ auto_resize="false"
+ user_resize="true"
+ visible="true"
+ height="200">
+ <panel
+ follows="all"
+ layout="topleft"
+ name="item_list_panel"
+ visible="true"
+ bottom="-1"
+ top="1"
+ left="5"
+ right="-1"/>
+ </layout_panel>
+
+ <layout_panel
+ name="transforms_panel"
+ border="true"
+ bevel_style="in"
+ auto_resize="true"
+ user_resize="true"
+ visible="true"
+ height="150">
+ <menu_button
+ menu_filename="menu_copy_paste_pos.xml"
+ follows="top|left"
+ height="11"
+ image_disabled="ClipboardSmallMenu_Disabled"
+ image_selected="ClipboardSmallMenu_Press"
+ image_unselected="ClipboardSmallMenu_Off"
+ layout="topleft"
+ left="4"
+ top="10"
+ name="clipboard_pos_btn"
+ tool_tip="Paste options"
+ width="19"/>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ name="label position"
+ tool_tip="Position (meters)"
+ left_pad="8"
+ top_delta="0"
+ width="121">
+ Position (m)
+ </text>
+ <spinner
+ follows="left|top"
+ height="19"
+ increment="0.01"
+ initial_value="0"
+ label="X"
+ label_width="10"
+ layout="topleft"
+ left_delta="-27"
+ max_val="512"
+ min_val="-256"
+ name="Pos X"
+ text_enabled_color="1 0 0.3 .7"
+ top_pad="8"
+ width="87" />
+ <spinner
+ follows="left|top"
+ height="19"
+ increment="0.01"
+ initial_value="0"
+ label="Y"
+ label_width="10"
+ layout="topleft"
+ left_delta="0"
+ max_val="512"
+ min_val="-256"
+ name="Pos Y"
+ text_enabled_color="EmphasisColor"
+ top_pad="3"
+ width="87" />
+ <spinner
+ follows="left|top"
+ height="19"
+ increment="0.01"
+ initial_value="0"
+ label="Z"
+ label_width="10"
+ layout="topleft"
+ left_delta="0"
+ max_val="4096"
+ min_val="-32"
+ name="Pos Z"
+ text_enabled_color="0 0.8 1 .65"
+ top_pad="3"
+ width="87" />
+ <menu_button
+ menu_filename="menu_copy_paste_size.xml"
+ follows="top|left"
+ height="11"
+ image_disabled="ClipboardSmallMenu_Disabled"
+ image_selected="ClipboardSmallMenu_Press"
+ image_unselected="ClipboardSmallMenu_Off"
+ layout="topleft"
+ left_delta="0"
+ top_pad="13"
+ name="clipboard_size_btn"
+ tool_tip="Paste options"
+ width="19"/>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left_pad="8"
+ top_delta="0"
+ name="label size"
+ tool_tip="Size (meters)"
+ width="121">
+ Size (m)
+ </text>
+ <spinner
+ follows="left|top"
+ height="19"
+ increment="0.01"
+ initial_value="0"
+ label="X"
+ label_width="10"
+ layout="topleft"
+ left_delta="-27"
+ max_val="64"
+ min_val="0.01"
+ name="Scale X"
+ text_enabled_color="1 1 1 1"
+ top_pad="8"
+ width="87" />
+ <spinner
+ follows="left|top"
+ height="19"
+ increment="0.01"
+ initial_value="0"
+ label="Y"
+ label_width="10"
+ layout="topleft"
+ left_delta="0"
+ max_val="64"
+ min_val="0.01"
+ name="Scale Y"
+ text_enabled_color="1 1 1 1"
+ top_pad="3"
+ width="87" />
+ <spinner
+ follows="left|top"
+ height="19"
+ increment="0.01"
+ initial_value="0"
+ label="Z"
+ label_width="10"
+ layout="topleft"
+ left_delta="0"
+ max_val="64"
+ min_val="0.01"
+ name="Scale Z"
+ text_enabled_color="1 1 1 1"
+ top_pad="3"
+ width="87" />
+ <menu_button
+ menu_filename="menu_copy_paste_rot.xml"
+ follows="top|left"
+ height="11"
+ image_disabled="ClipboardSmallMenu_Disabled"
+ image_selected="ClipboardSmallMenu_Press"
+ image_unselected="ClipboardSmallMenu_Off"
+ layout="topleft"
+ left_delta="0"
+ top_pad="13"
+ name="clipboard_rot_btn"
+ tool_tip="Paste options"
+ width="19"/>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left_pad="8"
+ top_delta="0"
+ name="label rotation"
+ tool_tip="Rotation (degrees)"
+ width="121">
+ Rotation (°)
+ </text>
+ <spinner
+ decimal_digits="2"
+ follows="left|top"
+ height="19"
+ increment="1"
+ initial_value="0"
+ label="X"
+ label_width="10"
+ layout="topleft"
+ left_delta="-27"
+ max_val="9999"
+ min_val="-9999"
+ name="Rot X"
+ text_enabled_color="1 1 1 1"
+ top_pad="8"
+ width="87" />
+ <spinner
+ decimal_digits="2"
+ follows="left|top"
+ height="19"
+ increment="1"
+ initial_value="0"
+ label="Y"
+ label_width="10"
+ layout="topleft"
+ left_delta="0"
+ max_val="9999"
+ min_val="-9999"
+ name="Rot Y"
+ text_enabled_color="1 1 1 1"
+ top_pad="3"
+ width="87" />
+ <spinner
+ decimal_digits="2"
+ follows="left|top"
+ height="19"
+ increment="1"
+ initial_value="0"
+ label="Z"
+ label_width="10"
+ layout="topleft"
+ left_delta="0"
+ max_val="9999"
+ min_val="-9999"
+ name="Rot Z"
+ text_enabled_color="1 1 1 1"
+ top_pad="3"
+ width="87" />
+ </layout_panel>
+
+ <layout_panel
+ name="blank_panel"
+ border="true"
+ bevel_style="in"
+ auto_resize="true"
+ user_resize="true"
+ visible="false"
+ height="150">
+ </layout_panel>
+ </layout_stack>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index d5d2d00630..347638249b 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -2851,6 +2851,14 @@ function="World.EnvPreset"
<menu_item_call.on_click
function="Advanced.ClickGLTFUpload" />
</menu_item_call>
+ <menu_item_call
+ label="Edit..."
+ name="Edit...">
+ <menu_item_call.on_enable
+ function="EnableGLTFUpload"/>
+ <menu_item_call.on_click
+ function="Advanced.ClickGLTFEdit" />
+ </menu_item_call>
</menu>
<menu
create_jump_keys="true"
diff --git a/indra/newview/skins/default/xui/es/floater_about.xml b/indra/newview/skins/default/xui/es/floater_about.xml
index e14ba32f69..8103a95376 100644
--- a/indra/newview/skins/default/xui/es/floater_about.xml
+++ b/indra/newview/skins/default/xui/es/floater_about.xml
@@ -28,7 +28,6 @@ con contribuciones de código abierto de:</text>
PCRE Copyright (c) 1997-2012 University of Cambridge
SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
xxHash Copyright (C) 2012-2020 Yann Collet.
zlib Copyright (C) 1995-2012 Jean-loup Gailly y Mark Adler.
diff --git a/indra/newview/skins/default/xui/fr/floater_about.xml b/indra/newview/skins/default/xui/fr/floater_about.xml
index 09da1fb5fd..b6ea621177 100644
--- a/indra/newview/skins/default/xui/fr/floater_about.xml
+++ b/indra/newview/skins/default/xui/fr/floater_about.xml
@@ -28,7 +28,6 @@ avec les contributions Open Source de :</text>
PCRE Copyright (c) 1997-2012 University of Cambridge
SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
xxHash Copyright (C) 2012-2020 Yann Collet.
zlib Copyright (C) 1995-2012 Jean-Loup Gailly et Mark Adler.
diff --git a/indra/newview/skins/default/xui/it/floater_about.xml b/indra/newview/skins/default/xui/it/floater_about.xml
index 7e195d3ca9..77be47d749 100644
--- a/indra/newview/skins/default/xui/it/floater_about.xml
+++ b/indra/newview/skins/default/xui/it/floater_about.xml
@@ -28,7 +28,6 @@ con contributi open source da:</text>
PCRE Copyright (c) 1997-2012 University of Cambridge
SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
xxHash Copyright (C) 2012-2020 Yann Collet.
zlib Copyright (C) 1995-2012 Jean-loup Gailly e Mark Adler.
diff --git a/indra/newview/skins/default/xui/ja/floater_about.xml b/indra/newview/skins/default/xui/ja/floater_about.xml
index 12d763be37..6cd22f6a31 100644
--- a/indra/newview/skins/default/xui/ja/floater_about.xml
+++ b/indra/newview/skins/default/xui/ja/floater_about.xml
@@ -33,7 +33,6 @@ OpenSSL Copyright (C) 1998-2008 The OpenSSL Project.
PCRE Copyright (c) 1997-2012 University of Cambridge
SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
-xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
xxHash Copyright (C) 2012-2020 Yann Collet.
zlib Copyright (C) 1995-2012 Jean-loup Gailly and Mark Adler.
diff --git a/indra/newview/skins/default/xui/pt/floater_about.xml b/indra/newview/skins/default/xui/pt/floater_about.xml
index aaed728f84..0e95c53109 100644
--- a/indra/newview/skins/default/xui/pt/floater_about.xml
+++ b/indra/newview/skins/default/xui/pt/floater_about.xml
@@ -28,7 +28,6 @@ com contribuições de código aberto de:</text>
PCRE Copyright (c) 1997-2012 University of Cambridge
SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
xxHash Copyright (C) 2012-2020 Yann Collet.
zlib Copyright (C) 1995-2012 Jean-loup Gailly and Mark Adler.
diff --git a/indra/newview/skins/default/xui/ru/floater_about.xml b/indra/newview/skins/default/xui/ru/floater_about.xml
index a65a979ccd..22827bc397 100644
--- a/indra/newview/skins/default/xui/ru/floater_about.xml
+++ b/indra/newview/skins/default/xui/ru/floater_about.xml
@@ -28,7 +28,6 @@
PCRE (c) 1997-2012, Кембриджский университет
SDL (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
SSLeay (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- xmlrpc-epi (C) 2000 Epinions, Inc.
xxHash Copyright (C) 2012-2020 Yann Collet.
zlib (C) 1995-2012 Jean-loup Gailly и Mark Adler.
diff --git a/indra/newview/skins/default/xui/tr/floater_about.xml b/indra/newview/skins/default/xui/tr/floater_about.xml
index 40ca3707c3..ca21bee464 100644
--- a/indra/newview/skins/default/xui/tr/floater_about.xml
+++ b/indra/newview/skins/default/xui/tr/floater_about.xml
@@ -28,7 +28,6 @@ açık kaynak kod katkısında bulunanlar şunlardır:</text>
PCRE Telif Hakkı (c) 1997-2012 University of Cambridge
SDL Telif Hakkı (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
SSLeay Telif Hakkı (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- xmlrpc-epi Telif Hakkı (C) 2000 Epinions, Inc.
xxHash Copyright (C) 2012-2020 Yann Collet.
zlib Telif Hakkı (C) 1995-2012 Jean-loup Gailly ve Mark Adler.
diff --git a/indra/newview/skins/default/xui/zh/floater_about.xml b/indra/newview/skins/default/xui/zh/floater_about.xml
index a56ae753d1..727f598894 100644
--- a/indra/newview/skins/default/xui/zh/floater_about.xml
+++ b/indra/newview/skins/default/xui/zh/floater_about.xml
@@ -28,7 +28,6 @@
PCRE Copyright (c) 1997-2012 University of Cambridge
SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- xmlrpc-epi Copyright (C) 2000 Epinions, Inc.
xxHash Copyright (C) 2012-2020 Yann Collet.
zlib Copyright (C) 1995-2012 Jean-loup Gailly and Mark Adler.