759 lines
23 KiB
C++
759 lines
23 KiB
C++
/**
|
|
* @file llgltfmateriallist.cpp
|
|
*
|
|
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2022, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
|
|
#include "llgltfmateriallist.h"
|
|
|
|
#include "llagent.h"
|
|
#include "llassetstorage.h"
|
|
#include "lldispatcher.h"
|
|
#include "llfetchedgltfmaterial.h"
|
|
#include "llfilesystem.h"
|
|
#include "llsdserialize.h"
|
|
#include "lltinygltfhelper.h"
|
|
#include "llviewercontrol.h"
|
|
#include "llviewergenericmessage.h"
|
|
#include "llviewerobjectlist.h"
|
|
#include "llviewerregion.h"
|
|
#include "llviewerstats.h"
|
|
#include "llcorehttputil.h"
|
|
#include "llagent.h"
|
|
#include "llvocache.h"
|
|
#include "llworld.h"
|
|
|
|
#include "tinygltf/tiny_gltf.h"
|
|
#include <strstream>
|
|
|
|
#include "json/reader.h"
|
|
#include "json/value.h"
|
|
|
|
#include <unordered_set>
|
|
|
|
LLGLTFMaterialList gGLTFMaterialList;
|
|
|
|
LLGLTFMaterialList::modify_queue_t LLGLTFMaterialList::sModifyQueue;
|
|
LLGLTFMaterialList::apply_queue_t LLGLTFMaterialList::sApplyQueue;
|
|
LLSD LLGLTFMaterialList::sUpdates;
|
|
|
|
const LLUUID LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID("968cbad0-4dad-d64e-71b5-72bf13ad051a");
|
|
|
|
#ifdef SHOW_ASSERT
|
|
// return true if given data is (probably) valid update message for ModifyMaterialParams capability
|
|
static bool is_valid_update(const LLSD& data)
|
|
{
|
|
llassert(data.isMap());
|
|
|
|
U32 count = 0;
|
|
|
|
if (data.has("object_id"))
|
|
{
|
|
if (!data["object_id"].isUUID())
|
|
{
|
|
LL_WARNS() << "object_id is not a UUID" << LL_ENDL;
|
|
return false;
|
|
}
|
|
++count;
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS() << "Missing required parameter: object_id" << LL_ENDL;
|
|
return false;
|
|
}
|
|
|
|
if (data.has("side"))
|
|
{
|
|
if (!data["side"].isInteger())
|
|
{
|
|
LL_WARNS() << "side is not an integer" << LL_ENDL;
|
|
return false;
|
|
}
|
|
|
|
if (data["side"].asInteger() < -1)
|
|
{
|
|
LL_WARNS() << "side is invalid" << LL_ENDL;
|
|
}
|
|
++count;
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS() << "Missing required parameter: side" << LL_ENDL;
|
|
return false;
|
|
}
|
|
|
|
if (data.has("gltf_json"))
|
|
{
|
|
if (!data["gltf_json"].isString())
|
|
{
|
|
LL_WARNS() << "gltf_json is not a string" << LL_ENDL;
|
|
return false;
|
|
}
|
|
++count;
|
|
}
|
|
|
|
if (data.has("asset_id"))
|
|
{
|
|
if (!data["asset_id"].isUUID())
|
|
{
|
|
LL_WARNS() << "asset_id is not a UUID" << LL_ENDL;
|
|
return false;
|
|
}
|
|
++count;
|
|
}
|
|
|
|
if (count < 3)
|
|
{
|
|
LL_WARNS() << "Only specified object_id and side, update won't actually change anything and is just noise" << LL_ENDL;
|
|
return false;
|
|
}
|
|
|
|
if (data.size() != count)
|
|
{
|
|
LL_WARNS() << "update data contains unrecognized parameters" << LL_ENDL;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
class LLGLTFMaterialOverrideDispatchHandler : public LLDispatchHandler
|
|
{
|
|
LOG_CLASS(LLGLTFMaterialOverrideDispatchHandler);
|
|
public:
|
|
LLGLTFMaterialOverrideDispatchHandler() = default;
|
|
~LLGLTFMaterialOverrideDispatchHandler() override = default;
|
|
|
|
void addCallback(void(*callback)(const LLUUID& object_id, S32 side))
|
|
{
|
|
mSelectionCallbacks.push_back(callback);
|
|
}
|
|
|
|
bool operator()(const LLDispatcher* dispatcher, const std::string& key, const LLUUID& invoice, const sparam_t& strings) override
|
|
{
|
|
LL_PROFILE_ZONE_SCOPED;
|
|
// receive override data from simulator via LargeGenericMessage
|
|
// message should have:
|
|
// object_id - UUID of LLViewerObject
|
|
// side - S32 index of texture entry
|
|
// gltf_json - String of GLTF json for override data
|
|
|
|
|
|
LLSD message;
|
|
|
|
sparam_t::const_iterator it = strings.begin();
|
|
if (it != strings.end())
|
|
{
|
|
const std::string& llsdRaw = *it++;
|
|
std::istringstream llsdData(llsdRaw);
|
|
if (!LLSDSerialize::deserialize(message, llsdData, llsdRaw.length()))
|
|
{
|
|
LL_WARNS() << "LLGLTFMaterialOverrideDispatchHandler: Attempted to read parameter data into LLSD but failed:" << llsdRaw << LL_ENDL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// malformed message, nothing we can do to handle it
|
|
LL_DEBUGS("GLTF") << "Empty message" << LL_ENDL;
|
|
return false;
|
|
}
|
|
|
|
LLGLTFOverrideCacheEntry object_override;
|
|
if (!object_override.fromLLSD(message))
|
|
{
|
|
// malformed message, nothing we can do to handle it
|
|
LL_DEBUGS("GLTF") << "Message without id:" << message << LL_ENDL;
|
|
return false;
|
|
}
|
|
|
|
// Cache the data
|
|
{
|
|
LL_DEBUGS("GLTF") << "material overrides cache" << LL_ENDL;
|
|
|
|
// default to main region if message doesn't specify
|
|
LLViewerRegion * region = gAgent.getRegion();;
|
|
|
|
if (object_override.mHasRegionHandle)
|
|
{
|
|
// TODO start requiring this once server sends this for all messages
|
|
region = LLWorld::instance().getRegionFromHandle(object_override.mRegionHandle);
|
|
}
|
|
|
|
if (region)
|
|
{
|
|
region->cacheFullUpdateGLTFOverride(object_override);
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS("GLTF") << "could not access region for material overrides message cache, region_handle: " << LL_ENDL;
|
|
}
|
|
}
|
|
applyData(object_override);
|
|
return true;
|
|
}
|
|
|
|
void doSelectionCallbacks(const LLUUID& object_id, S32 side)
|
|
{
|
|
for (auto& callback : mSelectionCallbacks)
|
|
{
|
|
callback(object_id, side);
|
|
}
|
|
}
|
|
|
|
void applyData(const LLGLTFOverrideCacheEntry &object_override)
|
|
{
|
|
// Parse the data
|
|
|
|
LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
|
|
LL::WorkQueue::ptr_t general_queue = LL::WorkQueue::getInstance("General");
|
|
|
|
struct ReturnData
|
|
{
|
|
public:
|
|
LLPointer<LLGLTFMaterial> mMaterial;
|
|
S32 mSide;
|
|
bool mSuccess;
|
|
};
|
|
|
|
// fromJson() is performance heavy offload to a thread.
|
|
main_queue->postTo(
|
|
general_queue,
|
|
[object_override]() // Work done on general queue
|
|
{
|
|
std::vector<ReturnData> results;
|
|
|
|
if (!object_override.mSides.empty())
|
|
{
|
|
results.reserve(object_override.mSides.size());
|
|
// parse json
|
|
std::map<S32, std::string>::const_iterator iter = object_override.mSides.begin();
|
|
std::map<S32, std::string>::const_iterator end = object_override.mSides.end();
|
|
while (iter != end)
|
|
{
|
|
LLPointer<LLGLTFMaterial> override_data = new LLGLTFMaterial();
|
|
std::string warn_msg, error_msg;
|
|
|
|
bool success = override_data->fromJSON(iter->second, warn_msg, error_msg);
|
|
|
|
ReturnData result;
|
|
result.mSuccess = success;
|
|
result.mSide = iter->first;
|
|
|
|
if (success)
|
|
{
|
|
result.mMaterial = override_data;
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS("GLTF") << "failed to parse GLTF override data. errors: " << error_msg << " | warnings: " << warn_msg << LL_ENDL;
|
|
}
|
|
|
|
results.push_back(result);
|
|
iter++;
|
|
}
|
|
}
|
|
return results;
|
|
},
|
|
[object_override, this](std::vector<ReturnData> results) // Callback to main thread
|
|
{
|
|
|
|
LLViewerObject * obj = gObjectList.findObject(object_override.mObjectId);
|
|
|
|
if (results.size() > 0 )
|
|
{
|
|
std::unordered_set<S32> side_set;
|
|
|
|
for (int i = 0; i < results.size(); ++i)
|
|
{
|
|
if (results[i].mSuccess)
|
|
{
|
|
// flag this side to not be nulled out later
|
|
side_set.insert(results[i].mSide);
|
|
|
|
if (!obj || !obj->setTEGLTFMaterialOverride(results[i].mSide, results[i].mMaterial))
|
|
{
|
|
// object not ready to receive override data, queue for later
|
|
gGLTFMaterialList.queueOverrideUpdate(object_override.mObjectId, results[i].mSide, results[i].mMaterial);
|
|
}
|
|
else if (obj && obj->getTE(results[i].mSide) && obj->getTE(results[i].mSide)->isSelected())
|
|
{
|
|
doSelectionCallbacks(object_override.mObjectId, results[i].mSide);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// unblock material editor
|
|
if (obj && obj->getTE(results[i].mSide) && obj->getTE(results[i].mSide)->isSelected())
|
|
{
|
|
doSelectionCallbacks(object_override.mObjectId, results[i].mSide);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (obj && side_set.size() != obj->getNumTEs())
|
|
{ // object exists and at least one texture entry needs to have its override data nulled out
|
|
for (int i = 0; i < obj->getNumTEs(); ++i)
|
|
{
|
|
if (side_set.find(i) == side_set.end())
|
|
{
|
|
obj->setTEGLTFMaterialOverride(i, nullptr);
|
|
if (obj->getTE(i) && obj->getTE(i)->isSelected())
|
|
{
|
|
doSelectionCallbacks(object_override.mObjectId, i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (obj)
|
|
{ // override list was empty or an error occurred, null out all overrides for this object
|
|
for (int i = 0; i < obj->getNumTEs(); ++i)
|
|
{
|
|
obj->setTEGLTFMaterialOverride(i, nullptr);
|
|
if (obj->getTE(i) && obj->getTE(i)->isSelected())
|
|
{
|
|
doSelectionCallbacks(obj->getID(), i);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private:
|
|
|
|
std::vector<void(*)(const LLUUID& object_id, S32 side)> mSelectionCallbacks;
|
|
};
|
|
|
|
namespace
|
|
{
|
|
LLGLTFMaterialOverrideDispatchHandler handle_gltf_override_message;
|
|
}
|
|
|
|
void LLGLTFMaterialList::queueOverrideUpdate(const LLUUID& id, S32 side, LLGLTFMaterial* override_data)
|
|
{
|
|
override_list_t& overrides = mQueuedOverrides[id];
|
|
|
|
if (overrides.size() < side + 1)
|
|
{
|
|
overrides.resize(side + 1);
|
|
}
|
|
|
|
overrides[side] = override_data;
|
|
}
|
|
|
|
void LLGLTFMaterialList::applyQueuedOverrides(LLViewerObject* obj)
|
|
{
|
|
LL_PROFILE_ZONE_SCOPED;
|
|
const LLUUID& id = obj->getID();
|
|
auto iter = mQueuedOverrides.find(id);
|
|
|
|
if (iter != mQueuedOverrides.end())
|
|
{
|
|
override_list_t& overrides = iter->second;
|
|
for (int i = 0; i < overrides.size(); ++i)
|
|
{
|
|
if (overrides[i].notNull())
|
|
{
|
|
if (!obj->getTE(i) || !obj->getTE(i)->getGLTFMaterial())
|
|
{ // object doesn't have its base GLTF material yet, don't apply override (yet)
|
|
return;
|
|
}
|
|
obj->setTEGLTFMaterialOverride(i, overrides[i]);
|
|
if (obj->getTE(i)->isSelected())
|
|
{
|
|
handle_gltf_override_message.doSelectionCallbacks(id, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
mQueuedOverrides.erase(iter);
|
|
}
|
|
}
|
|
|
|
void LLGLTFMaterialList::queueModify(const LLUUID& id, S32 side, const LLGLTFMaterial* mat)
|
|
{
|
|
if (mat == nullptr)
|
|
{
|
|
sModifyQueue.push_back({ id, side, LLGLTFMaterial(), false });
|
|
}
|
|
else
|
|
{
|
|
sModifyQueue.push_back({ id, side, *mat, true});
|
|
}
|
|
}
|
|
|
|
void LLGLTFMaterialList::queueApply(const LLViewerObject* obj, S32 side, const LLUUID& asset_id)
|
|
{
|
|
const LLGLTFMaterial* material_override = obj->getTE(side)->getGLTFMaterialOverride();
|
|
if (material_override)
|
|
{
|
|
LLGLTFMaterial* cleared_override = new LLGLTFMaterial(*material_override);
|
|
cleared_override->setBaseMaterial();
|
|
sApplyQueue.push_back({ obj->getID(), side, asset_id, cleared_override });
|
|
}
|
|
else
|
|
{
|
|
sApplyQueue.push_back({ obj->getID(), side, asset_id, nullptr });
|
|
}
|
|
}
|
|
|
|
void LLGLTFMaterialList::queueUpdate(const LLSD& data)
|
|
{
|
|
llassert(is_valid_update(data));
|
|
|
|
if (!sUpdates.isArray())
|
|
{
|
|
sUpdates = LLSD::emptyArray();
|
|
}
|
|
|
|
sUpdates[sUpdates.size()] = data;
|
|
}
|
|
|
|
void LLGLTFMaterialList::flushUpdates(void(*done_callback)(bool))
|
|
{
|
|
LLSD& data = sUpdates;
|
|
|
|
S32 i = data.size();
|
|
|
|
for (auto& e : sModifyQueue)
|
|
{
|
|
data[i]["object_id"] = e.object_id;
|
|
data[i]["side"] = e.side;
|
|
|
|
if (e.has_override)
|
|
{
|
|
data[i]["gltf_json"] = e.override_data.asJSON();
|
|
}
|
|
else
|
|
{
|
|
// Clear all overrides
|
|
data[i]["gltf_json"] = "";
|
|
}
|
|
|
|
llassert(is_valid_update(data[i]));
|
|
++i;
|
|
}
|
|
sModifyQueue.clear();
|
|
|
|
for (auto& e : sApplyQueue)
|
|
{
|
|
data[i]["object_id"] = e.object_id;
|
|
data[i]["side"] = e.side;
|
|
data[i]["asset_id"] = e.asset_id;
|
|
if (e.override_data)
|
|
{
|
|
data[i]["gltf_json"] = e.override_data->asJSON();
|
|
}
|
|
else
|
|
{
|
|
// Clear all overrides
|
|
data[i]["gltf_json"] = "";
|
|
}
|
|
|
|
llassert(is_valid_update(data[i]));
|
|
++i;
|
|
}
|
|
sApplyQueue.clear();
|
|
|
|
#if 0 // debug output of data being sent to capability
|
|
std::stringstream str;
|
|
LLSDSerialize::serialize(data, str, LLSDSerialize::LLSD_NOTATION, LLSDFormatter::OPTIONS_PRETTY);
|
|
LL_INFOS() << "\n" << str.str() << LL_ENDL;
|
|
#endif
|
|
|
|
if (sUpdates.size() > 0)
|
|
{
|
|
LLCoros::instance().launch("modifyMaterialCoro",
|
|
std::bind(&LLGLTFMaterialList::modifyMaterialCoro,
|
|
gAgent.getRegionCapability("ModifyMaterialParams"),
|
|
sUpdates,
|
|
done_callback));
|
|
|
|
sUpdates = LLSD::emptyArray();
|
|
}
|
|
}
|
|
|
|
void LLGLTFMaterialList::addSelectionUpdateCallback(void(*update_callback)(const LLUUID& object_id, S32 side))
|
|
{
|
|
handle_gltf_override_message.addCallback(update_callback);
|
|
}
|
|
|
|
class AssetLoadUserData
|
|
{
|
|
public:
|
|
AssetLoadUserData() {}
|
|
tinygltf::Model mModelIn;
|
|
LLPointer<LLFetchedGLTFMaterial> mMaterial;
|
|
};
|
|
|
|
void LLGLTFMaterialList::onAssetLoadComplete(const LLUUID& id, LLAssetType::EType asset_type, void* user_data, S32 status, LLExtStat ext_status)
|
|
{
|
|
LL_PROFILE_ZONE_NAMED("gltf asset callback");
|
|
AssetLoadUserData* asset_data = (AssetLoadUserData*)user_data;
|
|
|
|
if (status != LL_ERR_NOERR)
|
|
{
|
|
LL_WARNS("GLTF") << "Error getting material asset data: " << LLAssetStorage::getErrorString(status) << " (" << status << ")" << LL_ENDL;
|
|
asset_data->mMaterial->mFetching = false;
|
|
delete asset_data;
|
|
}
|
|
else
|
|
{
|
|
|
|
LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
|
|
LL::WorkQueue::ptr_t general_queue = LL::WorkQueue::getInstance("General");
|
|
|
|
typedef std::pair<U32, tinygltf::Model> return_data_t;
|
|
|
|
main_queue->postTo(
|
|
general_queue,
|
|
[id, asset_type, asset_data]() // Work done on general queue
|
|
{
|
|
std::vector<char> buffer;
|
|
{
|
|
LL_PROFILE_ZONE_NAMED("gltf read asset");
|
|
LLFileSystem file(id, asset_type, LLFileSystem::READ);
|
|
auto size = file.getSize();
|
|
if (!size)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
buffer.resize(size);
|
|
file.read((U8*)&buffer[0], buffer.size());
|
|
}
|
|
|
|
{
|
|
LL_PROFILE_ZONE_NAMED("gltf deserialize asset");
|
|
|
|
LLSD asset;
|
|
|
|
// read file into buffer
|
|
std::istrstream str(&buffer[0], buffer.size());
|
|
|
|
if (LLSDSerialize::deserialize(asset, str, buffer.size()))
|
|
{
|
|
if (asset.has("version") && asset["version"] == "1.0")
|
|
{
|
|
if (asset.has("type") && asset["type"].asString() == "GLTF 2.0")
|
|
{
|
|
if (asset.has("data") && asset["data"].isString())
|
|
{
|
|
std::string data = asset["data"];
|
|
|
|
std::string warn_msg, error_msg;
|
|
|
|
LL_PROFILE_ZONE_SCOPED;
|
|
tinygltf::TinyGLTF gltf;
|
|
|
|
if (!gltf.LoadASCIIFromString(&asset_data->mModelIn, &error_msg, &warn_msg, data.c_str(), data.length(), ""))
|
|
{
|
|
LL_WARNS("GLTF") << "Failed to decode material asset: "
|
|
<< LL_NEWLINE
|
|
<< warn_msg
|
|
<< LL_NEWLINE
|
|
<< error_msg
|
|
<< LL_ENDL;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LL_WARNS("GLTF") << "Failed to deserialize material LLSD" << LL_ENDL;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
},
|
|
[id, asset_data](bool result) // Callback to main thread
|
|
{
|
|
|
|
if (result)
|
|
{
|
|
asset_data->mMaterial->setFromModel(asset_data->mModelIn, 0/*only one index*/);
|
|
}
|
|
else
|
|
{
|
|
LL_DEBUGS("GLTF") << "Failed to get material " << id << LL_ENDL;
|
|
}
|
|
asset_data->mMaterial->mFetching = false;
|
|
delete asset_data;
|
|
});
|
|
}
|
|
}
|
|
|
|
LLGLTFMaterial* LLGLTFMaterialList::getMaterial(const LLUUID& id)
|
|
{
|
|
LL_PROFILE_ZONE_SCOPED;
|
|
uuid_mat_map_t::iterator iter = mList.find(id);
|
|
if (iter == mList.end())
|
|
{
|
|
LL_PROFILE_ZONE_NAMED("gltf fetch")
|
|
LLFetchedGLTFMaterial* mat = new LLFetchedGLTFMaterial();
|
|
mList[id] = mat;
|
|
|
|
if (!mat->mFetching)
|
|
{
|
|
mat->mFetching = true;
|
|
|
|
AssetLoadUserData *user_data = new AssetLoadUserData();
|
|
user_data->mMaterial = mat;
|
|
|
|
gAssetStorage->getAssetData(id, LLAssetType::AT_MATERIAL, onAssetLoadComplete, (void*)user_data);
|
|
}
|
|
|
|
return mat;
|
|
}
|
|
|
|
return iter->second;
|
|
}
|
|
|
|
void LLGLTFMaterialList::addMaterial(const LLUUID& id, LLFetchedGLTFMaterial* material)
|
|
{
|
|
mList[id] = material;
|
|
}
|
|
|
|
void LLGLTFMaterialList::removeMaterial(const LLUUID& id)
|
|
{
|
|
mList.erase(id);
|
|
}
|
|
|
|
void LLGLTFMaterialList::flushMaterials()
|
|
{
|
|
// Similar variant to what textures use
|
|
static const S32 MIN_UPDATE_COUNT = gSavedSettings.getS32("TextureFetchUpdateMinCount"); // default: 32
|
|
//update MIN_UPDATE_COUNT or 5% of materials, whichever is greater
|
|
U32 update_count = llmax((U32)MIN_UPDATE_COUNT, (U32)mList.size() / 20);
|
|
update_count = llmin(update_count, (U32)mList.size());
|
|
|
|
const F64 MAX_INACTIVE_TIME = 30.f;
|
|
F64 cur_time = LLTimer::getTotalSeconds();
|
|
|
|
// advance iter one past the last key we updated
|
|
uuid_mat_map_t::iterator iter = mList.find(mLastUpdateKey);
|
|
if (iter != mList.end()) {
|
|
++iter;
|
|
}
|
|
|
|
while (update_count-- > 0)
|
|
{
|
|
if (iter == mList.end())
|
|
{
|
|
iter = mList.begin();
|
|
}
|
|
|
|
LLPointer<LLFetchedGLTFMaterial> material = iter->second;
|
|
if (material->getNumRefs() == 2) // this one plus one from the list
|
|
{
|
|
|
|
if (!material->mActive
|
|
&& cur_time > material->mExpectedFlusTime)
|
|
{
|
|
iter = mList.erase(iter);
|
|
}
|
|
else
|
|
{
|
|
if (material->mActive)
|
|
{
|
|
material->mExpectedFlusTime = cur_time + MAX_INACTIVE_TIME;
|
|
material->mActive = false;
|
|
}
|
|
++iter;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
material->mActive = true;
|
|
++iter;
|
|
}
|
|
}
|
|
|
|
if (iter != mList.end())
|
|
{
|
|
mLastUpdateKey = iter->first;
|
|
}
|
|
else
|
|
{
|
|
mLastUpdateKey.setNull();
|
|
}
|
|
|
|
{
|
|
using namespace LLStatViewer;
|
|
sample(NUM_MATERIALS, mList.size());
|
|
}
|
|
}
|
|
|
|
// static
|
|
void LLGLTFMaterialList::registerCallbacks()
|
|
{
|
|
gGenericDispatcher.addHandler("GLTFMaterialOverride", &handle_gltf_override_message);
|
|
}
|
|
|
|
// static
|
|
void LLGLTFMaterialList::modifyMaterialCoro(std::string cap_url, LLSD overrides, void(*done_callback)(bool) )
|
|
{
|
|
LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
|
|
LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
|
|
httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("modifyMaterialCoro", httpPolicy));
|
|
LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
|
|
LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions);
|
|
LLCore::HttpHeaders::ptr_t httpHeaders;
|
|
|
|
httpOpts->setFollowRedirects(true);
|
|
|
|
LL_DEBUGS("GLTF") << "Applying override via ModifyMaterialParams cap: " << overrides << LL_ENDL;
|
|
|
|
LLSD result = httpAdapter->postAndSuspend(httpRequest, cap_url, overrides, httpOpts, httpHeaders);
|
|
|
|
LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
|
|
LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
|
|
|
|
bool success = true;
|
|
if (!status)
|
|
{
|
|
LL_WARNS("GLTF") << "Failed to modify material." << LL_ENDL;
|
|
success = false;
|
|
}
|
|
else if (!result["success"].asBoolean())
|
|
{
|
|
LL_WARNS("GLTF") << "Failed to modify material: " << result["message"] << LL_ENDL;
|
|
success = false;
|
|
}
|
|
|
|
if (done_callback)
|
|
{
|
|
done_callback(success);
|
|
}
|
|
}
|
|
|
|
void LLGLTFMaterialList::loadCacheOverrides(const LLGLTFOverrideCacheEntry& override)
|
|
{
|
|
handle_gltf_override_message.applyData(override);
|
|
}
|