SL-18602 Fix for applying material asset not removing overrides on drag-and-drop

master
Dave Parks 2022-11-09 17:10:31 -06:00
parent 14d901f25f
commit 1ed8f7cd0c
4 changed files with 204 additions and 173 deletions

View File

@ -51,139 +51,141 @@
LLGLTFMaterialList gGLTFMaterialList;
LLGLTFMaterialList::modify_queue_t LLGLTFMaterialList::sModifyQueue;
LLGLTFMaterialList::apply_queue_t LLGLTFMaterialList::sApplyQueue;
const LLUUID LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID("968cbad0-4dad-d64e-71b5-72bf13ad051a");
class LLGLTFMaterialOverrideDispatchHandler : public LLDispatchHandler
{
LOG_CLASS(LLGLTFMaterialOverrideDispatchHandler);
public:
LLGLTFMaterialOverrideDispatchHandler() = default;
~LLGLTFMaterialOverrideDispatchHandler() override = default;
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;
}
}
LLUUID object_id = message["object_id"].asUUID();
LLViewerObject * obj = gObjectList.findObject(object_id); // NOTE: null object here does NOT mean nothing to do, parse message and queue results for later
bool clear_all = true;
if (message.has("sides") && message.has("gltf_json"))
{
LLSD& sides = message["sides"];
LLSD& gltf_json = message["gltf_json"];
if (sides.isArray() && gltf_json.isArray() &&
sides.size() != 0 &&
sides.size() == gltf_json.size())
{
clear_all = false;
// message should be interpreted thusly:
/// sides is a list of face indices
// gltf_json is a list of corresponding json
// any side not represented in "sides" has no override
// parse json
std::unordered_set<S32> side_set;
for (int i = 0; i < sides.size(); ++i)
{
LLPointer<LLGLTFMaterial> override_data = new LLGLTFMaterial();
std::string gltf_json = message["gltf_json"][i].asString();
std::string warn_msg, error_msg;
bool success = override_data->fromJSON(gltf_json, warn_msg, error_msg);
if (!success)
{
LL_WARNS() << "failed to parse GLTF override data. errors: " << error_msg << " | warnings: " << warn_msg << LL_ENDL;
// unblock material editor
if (obj && obj->isAnySelected())
{
LLMaterialEditor::updateLive(object_id, sides[i].asInteger());
}
}
else
{
S32 side = sides[i].asInteger();
// flag this side to not be nulled out later
side_set.insert(sides[i]);
if (!obj || !obj->setTEGLTFMaterialOverride(side, override_data))
{
// object not ready to receive override data, queue for later
gGLTFMaterialList.queueOverrideUpdate(object_id, side, override_data);
}
else if (obj && obj->isAnySelected())
{
LLMaterialEditor::updateLive(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
bool object_has_selection = obj->isAnySelected();
for (int i = 0; i < obj->getNumTEs(); ++i)
{
if (side_set.find(i) == side_set.end())
{
obj->setTEGLTFMaterialOverride(i, nullptr);
if (object_has_selection)
{
LLMaterialEditor::updateLive(object_id, i);
}
}
}
}
}
else
{
LL_WARNS() << "Malformed GLTF override message data: " << message << LL_ENDL;
}
}
if (clear_all && obj)
{ // override list was empty or an error occurred, null out all overrides for this object
bool object_has_selection = obj->isAnySelected();
for (int i = 0; i < obj->getNumTEs(); ++i)
{
obj->setTEGLTFMaterialOverride(i, nullptr);
if (object_has_selection)
{
LLMaterialEditor::updateLive(obj->getID(), i);
}
}
}
return true;
}
};
namespace
{
class LLGLTFMaterialOverrideDispatchHandler : public LLDispatchHandler
{
LOG_CLASS(LLGLTFMaterialOverrideDispatchHandler);
public:
LLGLTFMaterialOverrideDispatchHandler() = default;
~LLGLTFMaterialOverrideDispatchHandler() override = default;
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;
}
}
LLUUID object_id = message["object_id"].asUUID();
LLViewerObject * obj = gObjectList.findObject(object_id); // NOTE: null object here does NOT mean nothing to do, parse message and queue results for later
bool clear_all = true;
if (message.has("sides") && message.has("gltf_json"))
{
LLSD& sides = message["sides"];
LLSD& gltf_json = message["gltf_json"];
if (sides.isArray() && gltf_json.isArray() &&
sides.size() != 0 &&
sides.size() == gltf_json.size())
{
clear_all = false;
// message should be interpreted thusly:
/// sides is a list of face indices
// gltf_json is a list of corresponding json
// any side not represented in "sides" has no override
// parse json
std::unordered_set<S32> side_set;
for (int i = 0; i < sides.size(); ++i)
{
LLPointer<LLGLTFMaterial> override_data = new LLGLTFMaterial();
std::string gltf_json = message["gltf_json"][i].asString();
std::string warn_msg, error_msg;
bool success = override_data->fromJSON(gltf_json, warn_msg, error_msg);
if (!success)
{
LL_WARNS() << "failed to parse GLTF override data. errors: " << error_msg << " | warnings: " << warn_msg << LL_ENDL;
// unblock material editor
if (obj && obj->isAnySelected())
{
LLMaterialEditor::updateLive(object_id, sides[i].asInteger());
}
}
else
{
S32 side = sides[i].asInteger();
// flag this side to not be nulled out later
side_set.insert(sides[i]);
if (!obj || !obj->setTEGLTFMaterialOverride(side, override_data))
{
// object not ready to receive override data, queue for later
gGLTFMaterialList.queueOverrideUpdate(object_id, side, override_data);
}
else if (obj && obj->isAnySelected())
{
LLMaterialEditor::updateLive(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
bool object_has_selection = obj->isAnySelected();
for (int i = 0; i < obj->getNumTEs(); ++i)
{
if (side_set.find(i) == side_set.end())
{
obj->setTEGLTFMaterialOverride(i, nullptr);
if (object_has_selection)
{
LLMaterialEditor::updateLive(object_id, i);
}
}
}
}
}
else
{
LL_WARNS() << "Malformed GLTF override message data: " << message << LL_ENDL;
}
}
if (clear_all && obj)
{ // override list was empty or an error occurred, null out all overrides for this object
bool object_has_selection = obj->isAnySelected();
for (int i = 0; i < obj->getNumTEs(); ++i)
{
obj->setTEGLTFMaterialOverride(i, nullptr);
if (object_has_selection)
{
LLMaterialEditor::updateLive(obj->getID(), i);
}
}
}
return true;
}
};
LLGLTFMaterialOverrideDispatchHandler handle_gltf_override_message;
}
@ -229,12 +231,24 @@ void LLGLTFMaterialList::applyQueuedOverrides(LLViewerObject* obj)
}
}
void LLGLTFMaterialList::queueModifyMaterial(const LLUUID& id, S32 side, const LLGLTFMaterial& mat)
void LLGLTFMaterialList::queueModifyMaterial(const LLUUID& id, S32 side, const LLGLTFMaterial* mat)
{
sModifyQueue.push_back({ id, side, mat, LLUUID::null, true, false });
if (mat == nullptr)
{
sModifyQueue.push_back({ id, side, LLGLTFMaterial(), false });
}
else
{
sModifyQueue.push_back({ id, side, *mat, true});
}
}
void LLGLTFMaterialList::flushModifyMaterialQueue(void(*done_callback)(bool))
void LLGLTFMaterialList::queueApplyMaterialAsset(const LLUUID& object_id, S32 side, const LLUUID& asset_id)
{
sApplyQueue.push_back({ object_id, side, asset_id});
}
void LLGLTFMaterialList::flushUpdates(void(*done_callback)(bool))
{
LLSD data = LLSD::emptyArray();
@ -244,11 +258,6 @@ void LLGLTFMaterialList::flushModifyMaterialQueue(void(*done_callback)(bool))
data[i]["object_id"] = e.object_id;
data[i]["side"] = e.side;
if (e.has_asset_id)
{
data[i]["asset_id"] = e.asset_id;
}
if (e.has_override)
{
data[i]["gltf_json"] = e.override_data.asJSON();
@ -256,19 +265,29 @@ void LLGLTFMaterialList::flushModifyMaterialQueue(void(*done_callback)(bool))
++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;
data[i]["gltf_json"] = ""; // null out any existing overrides when applying a material asset
++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
LLCoros::instance().launch("modifyMaterialCoro",
std::bind(&LLGLTFMaterialList::modifyMaterialCoro,
gAgent.getRegionCapability("ModifyMaterialParams"),
data,
done_callback));
sModifyQueue.clear();
}
LLGLTFMaterial* LLGLTFMaterialList::getMaterial(const LLUUID& id)

View File

@ -51,25 +51,45 @@ public:
static void registerCallbacks();
// save an override update that we want to send to the simulator for later
static void queueModifyMaterial(const LLUUID& id, S32 side, const LLGLTFMaterial& mat);
// Queue an override update that we want to send to the simulator. Call "flushUpdates" to flush pending updates.
// id - ID of object to modify
// side - TexureEntry index to modify, or -1 for all sides
// mat - material to apply as override, or nullptr to remove existing overrides and revert to asset
//
// NOTE: do not use to revert to asset when applying a new asset id, use queueApplyMaterialAsset below
static void queueModifyMaterial(const LLUUID& id, S32 side, const LLGLTFMaterial* mat);
// Queue an application of a material asset we want to send to the simulator. Call "flushUpdates" to flush pending updates.
// object_id - ID of object to apply material asset to
// side - TextureEntry index to apply material to, or -1 for all sides
// asset_id - ID of material asset to apply, or LLUUID::null to disassociate current material asset
//
// NOTE: implicitly removes any override data if present
static void queueApplyMaterialAsset(const LLUUID& object_id, S32 side, const LLUUID& asset_id);
// flush pending material updates to the simulator
static void flushModifyMaterialQueue(void(*done_callback)(bool));
static void flushUpdates(void(*done_callback)(bool) = nullptr);
// apply given override data via given cap url
// cap_url -- should be gAgent.getRegionCapability("ModifyMaterialParams")
// overrides -- LLSD map in the format
// "object_id": LLUUID - object to be modified
// "side": integer - index of face to be modified
// "gltf_json" : string - GLTF compliant json of override data (optional, if omitted any existing override data will be cleared)
// overrides -- LLSD map (or array of maps) in the format:
// object_id UUID(required) id of object
// side integer(required) TE index of face to set, or -1 for all faces
// gltf_json string(optional) override data to set, empty string nulls out override data, omissions of this parameter keeps existing data
// asset_id UUID(optional) id of material asset to set, omission of this parameter keeps existing material asset id
//
// NOTE: if you're calling this from outside of flushUpdates, you're probably doing it wrong. Use the "queue"/"flush" API above.
// If the queue/flush API is insufficient, extend it.
static void modifyMaterialCoro(std::string cap_url, LLSD overrides, void(*done_callback)(bool));
// save an override update that we got from the simulator for later (for example, if an override arrived for an unknown object)
void queueOverrideUpdate(const LLUUID& id, S32 side, LLGLTFMaterial* override_data);
void applyQueuedOverrides(LLViewerObject* obj);
private:
friend class LLGLTFMaterialOverrideDispatchHandler;
// save an override update that we got from the simulator for later (for example, if an override arrived for an unknown object)
// NOTE: this is NOT for applying overrides from the UI, see queueModifyMaterial above
void queueOverrideUpdate(const LLUUID& id, S32 side, LLGLTFMaterial* override_data);
typedef std::unordered_map<LLUUID, LLPointer<LLFetchedGLTFMaterial > > uuid_mat_map_t;
uuid_mat_map_t mList;
@ -84,15 +104,23 @@ private:
LLUUID object_id;
S32 side = -1;
LLGLTFMaterial override_data;
LLUUID asset_id;
bool has_override = false;
bool has_asset_id = false;
};
typedef std::list<ModifyMaterialData> modify_queue_t;
static modify_queue_t sModifyQueue;
struct ApplyMaterialAssetData
{
LLUUID object_id;
S32 side = -1;
LLUUID asset_id;
};
typedef std::list<ApplyMaterialAssetData> apply_queue_t;
static apply_queue_t sApplyQueue;
};
extern LLGLTFMaterialList gGLTFMaterialList;

View File

@ -2526,7 +2526,7 @@ public:
{
mSuccess = true;
}
LLGLTFMaterialList::queueModifyMaterial(objectp->getID(), te, *material);
LLGLTFMaterialList::queueModifyMaterial(objectp->getID(), te, material);
#else
std::string overrides_json = material->asJSON();
@ -2598,7 +2598,7 @@ void LLMaterialEditor::applyToSelection()
void(*done_callback)(bool) = LLRenderMaterialOverrideFunctor::modifyCallback;
LLGLTFMaterialList::flushModifyMaterialQueue(done_callback);
LLGLTFMaterialList::flushUpdates(done_callback);
if (!override_func.getResult())
{

View File

@ -7205,19 +7205,15 @@ void LLViewerObject::setRenderMaterialID(S32 te_in, const LLUUID& id, bool updat
if (update_server)
{
// blank out any override data on the ser
// update via ModifyMaterialParams cap (server will echo back changes)
for (S32 te = start_idx; te < end_idx; ++te)
{
LLCoros::instance().launch("modifyMaterialCoro",
std::bind(&LLGLTFMaterialList::modifyMaterialCoro,
gAgent.getRegionCapability("ModifyMaterialParams"),
llsd::map(
"object_id", getID(),
"side", te), nullptr));
LLGLTFMaterialList::queueApplyMaterialAsset(getID(), te, id);
}
LLGLTFMaterialList::flushUpdates();
}
// update and send LLRenderMaterialParams
// predictively update LLRenderMaterialParams (don't wait for server)
LLRenderMaterialParams* param_block = (LLRenderMaterialParams*)getParameterEntry(LLNetworkData::PARAMS_RENDER_MATERIAL);
if (!param_block && id.notNull())
{ // block doesn't exist, but it will need to
@ -7230,18 +7226,6 @@ void LLViewerObject::setRenderMaterialID(S32 te_in, const LLUUID& id, bool updat
{
param_block->setMaterial(te, id);
}
if (update_server)
{
// If 'in use' changes, it will send an update itself.
bool in_use_changed = setParameterEntryInUse(LLNetworkData::PARAMS_RENDER_MATERIAL, !param_block->isEmpty(), true);
if (!in_use_changed)
{
// In use didn't change, but the parameter did, send an update
parameterChanged(LLNetworkData::PARAMS_RENDER_MATERIAL, param_block, !param_block->isEmpty(), true);
}
}
}
}