diff options
author | Alexander Gavriliuk <alexandrgproductengine@lindenlab.com> | 2023-11-30 17:47:58 +0100 |
---|---|---|
committer | Alexander Gavriliuk <alexandrgproductengine@lindenlab.com> | 2023-12-05 03:37:06 +0100 |
commit | c9cd5631e4b149f83c5a49c8fbf869cf2fb5b6a7 (patch) | |
tree | d09566a4132530ea65574a886540e0f258103017 /indra/newview/llgltfmateriallist.cpp | |
parent | 54db4206e9302e7510bc4f103ff59714c1be942d (diff) | |
parent | 683bf84bb38adc88d4a4b7fedaed89b41fcac45e (diff) |
Merge branch 'main' into DRTVWR-489
Diffstat (limited to 'indra/newview/llgltfmateriallist.cpp')
-rw-r--r-- | indra/newview/llgltfmateriallist.cpp | 870 |
1 files changed, 870 insertions, 0 deletions
diff --git a/indra/newview/llgltfmateriallist.cpp b/indra/newview/llgltfmateriallist.cpp new file mode 100644 index 0000000000..8919229c78 --- /dev/null +++ b/indra/newview/llgltfmateriallist.cpp @@ -0,0 +1,870 @@ +/** + * @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 + // sides - array of S32 indices of texture entries + // gltf_json - array of corresponding Strings of GLTF json for override data + + LLSD message; + bool success = true; +#if 0 //deprecated + for(const std::string& llsdRaw : strings) + { + 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; + success = false; + continue; + } + + 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; + success = false; + continue; + } + + // Cache the data + { + LLViewerRegion * 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); + } + +#endif + return success; + } + + 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 + +#if 0 // DEPRECATED + LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop"); + LL::WorkQueue::ptr_t general_queue = LL::WorkQueue::getInstance("General"); + + struct ReturnData + { + public: + LLGLTFMaterial mMaterial; + S32 mSide; + bool mSuccess; + }; + + if (!object_override.mSides.empty()) + { + // fromJson() is performance heavy offload to a thread. + main_queue->postTo( + general_queue, + [sides=object_override.mSides]() // Work done on general queue + { + std::vector<ReturnData> results; + + results.reserve(sides.size()); + // parse json + std::unordered_map<S32, LLSD>::const_iterator iter = sides.begin(); + std::unordered_map<S32, LLSD>::const_iterator end = sides.end(); + while (iter != end) + { + ReturnData result; + + result.mMaterial.applyOverrideLLSD(iter->second); + + result.mSuccess = true; + result.mSide = iter->first; + + results.push_back(result); + iter++; + } + return results; + }, + [object_id=object_override.mObjectId, this](std::vector<ReturnData> results) // Callback to main thread + { + LLViewerObject * obj = gObjectList.findObject(object_id); + + if (results.size() > 0 ) + { + std::unordered_set<S32> side_set; + + for (auto const & result : results) + { + S32 side = result.mSide; + if (result.mSuccess) + { + // copy to heap here because LLTextureEntry is going to take ownership with an LLPointer + LLGLTFMaterial * material = new LLGLTFMaterial(result.mMaterial); + + // flag this side to not be nulled out later + side_set.insert(side); + + if (obj) + { + obj->setTEGLTFMaterialOverride(side, material); + } + } + + // unblock material editor + if (obj && obj->getTE(side) && obj->getTE(side)->isSelected()) + { + doSelectionCallbacks(object_id, side); + } + } + + 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_id, 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); + } + } + } + }); + } +#endif + } + +private: + + std::vector<void(*)(const LLUUID& object_id, S32 side)> mSelectionCallbacks; +}; + +namespace +{ + LLGLTFMaterialOverrideDispatchHandler handle_gltf_override_message; +} + +void LLGLTFMaterialList::applyOverrideMessage(LLMessageSystem* msg, const std::string& data_in) +{ + std::istringstream str(data_in); + + LLSD data; + + LLSDSerialize::fromNotation(data, str, data_in.length()); + + const LLHost& host = msg->getSender(); + + LLViewerRegion* region = LLWorld::instance().getRegion(host); + llassert(region); + + if (region) + { + U32 local_id = data.get("id").asInteger(); + LLUUID id; + gObjectList.getUUIDFromLocal(id, local_id, host.getAddress(), host.getPort()); + LLViewerObject* obj = gObjectList.findObject(id); + + // NOTE: obj may be null if the viewer hasn't heard about the object yet, cache update in any case + + if (obj && gShowObjectUpdates) + { // display a cyan blip for override updates when "Show Updates to Objects" enabled + LLColor4 color(0.f, 1.f, 1.f, 1.f); + gPipeline.addDebugBlip(obj->getPositionAgent(), color); + } + + const LLSD& tes = data["te"]; + const LLSD& od = data["od"]; + + constexpr U32 MAX_TES = 45; + bool has_te[MAX_TES] = { false }; + + if (tes.isArray()) // NOTE: if no "te" array exists, this is a malformed message (null out all overrides will come in as an empty te array) + { + LLGLTFOverrideCacheEntry cache; + cache.mLocalId = local_id; + cache.mObjectId = id; + cache.mRegionHandle = region->getHandle(); + + U32 count = llmin(tes.size(), MAX_TES); + for (U32 i = 0; i < count; ++i) + { + LLGLTFMaterial* mat = new LLGLTFMaterial(); // setTEGLTFMaterialOverride and cache will take ownership + mat->applyOverrideLLSD(od[i]); + + S32 te = tes[i].asInteger(); + + has_te[te] = true; + cache.mSides[te] = od[i]; + cache.mGLTFMaterial[te] = mat; + + if (obj) + { + obj->setTEGLTFMaterialOverride(te, mat); + if (obj->getTE(te) && obj->getTE(te)->isSelected()) + { + handle_gltf_override_message.doSelectionCallbacks(id, te); + } + } + } + + if (obj) + { // null out overrides on TEs that shouldn't have them + U32 count = llmin(obj->getNumTEs(), MAX_TES); + for (U32 i = 0; i < count; ++i) + { + LLTextureEntry* te = obj->getTE(i); + if (!has_te[i] && te && te->getGLTFMaterialOverride()) + { + obj->setTEGLTFMaterialOverride(i, nullptr); + handle_gltf_override_message.doSelectionCallbacks(id, i); + } + } + } + + region->cacheFullUpdateGLTFOverride(cache); + } + + } +} + +void LLGLTFMaterialList::queueOverrideUpdate(const LLUUID& id, S32 side, LLGLTFMaterial* override_data) +{ +#if 0 + override_list_t& overrides = mQueuedOverrides[id]; + + if (overrides.size() < side + 1) + { + overrides.resize(side + 1); + } + + overrides[side] = override_data; +#endif +} + +void LLGLTFMaterialList::applyQueuedOverrides(LLViewerObject* obj) +{ + LL_PROFILE_ZONE_SCOPED; + + llassert(obj); + +#if 0 + 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)) + { // object is incomplete + return; + } + + if (!obj->getTE(i)->getGLTFMaterial()) + { + // doesn't have its base GLTF material yet, don't apply override(yet) + return; + } + + S32 status = obj->setTEGLTFMaterialOverride(i, overrides[i]); + if (status == TEM_CHANGE_NONE) + { + // can't apply this yet, since failure to change the material override + // probably means the base material is still being fetched. leave in + // the queue for later + //obj->setDebugText("early out 3"); + return; + } + + if (obj->getTE(i)->isSelected()) + { + handle_gltf_override_message.doSelectionCallbacks(id, i); + } + // success! + overrides[i] = nullptr; + } + } + + mQueuedOverrides.erase(iter); + } +#else + // the override cache is the authoritarian source of the most recent override data + LLViewerRegion* regionp = obj->getRegion(); + if (regionp) + { + regionp->applyCacheMiscExtras(obj); + } +#endif +} + +void LLGLTFMaterialList::queueModify(const LLViewerObject* obj, S32 side, const LLGLTFMaterial* mat) +{ + if (obj && obj->getRenderMaterialID(side).notNull()) + { + if (mat == nullptr) + { + sModifyQueue.push_back({ obj->getID(), side, LLGLTFMaterial(), false }); + } + else + { + sModifyQueue.push_back({ obj->getID(), 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::queueApply(const LLViewerObject* obj, S32 side, const LLUUID& asset_id, const LLGLTFMaterial* material_override) +{ + if (asset_id.isNull() || material_override == nullptr) + { + queueApply(obj, side, asset_id); + } + else + { + LLGLTFMaterial* material = new LLGLTFMaterial(*material_override); + sApplyQueue.push_back({ obj->getID(), side, asset_id, material }); + } +} + +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 (ModifyMaterialData& e : sModifyQueue) + { +#ifdef SHOW_ASSERT + // validate object has a material id + LLViewerObject* obj = gObjectList.findObject(e.object_id); + llassert(obj && obj->getRenderMaterialID(e.side).notNull()); +#endif + + 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 (ApplyMaterialAssetData& 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->materialComplete(); + delete asset_data; + } + else + { + + LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop"); + LL::WorkQueue::ptr_t general_queue = LL::WorkQueue::getInstance("General"); + + 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") && LLGLTFMaterial::isAcceptedVersion(asset["version"].asString())) + { + if (asset.has("type") && asset["type"].asString() == LLGLTFMaterial::ASSET_TYPE) + { + 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->materialComplete(); + + delete asset_data; + }); + } +} + +LLFetchedGLTFMaterial* 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->materialBegin(); + + 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); +} |