Fix #4195: Preserve transforms when switching PBR materials (#4725)

Fixes texture transforms being reset when switching from Blinn-Phong to PBR materials and between PBR. Previously, custom scale, offset, and rotation settings would be lost, making it tedious to switch between PBR materials.
master
Aqil Ahmad 2025-10-06 18:52:33 +05:00 committed by GitHub
parent a54a96a1f4
commit 5fe152cfea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 339 additions and 13 deletions

View File

@ -923,3 +923,34 @@ void LLGLTFMaterial::updateTextureTracking()
// setTEGLTFMaterialOverride is responsible for tracking
// for material overrides editor will set it
}
void LLGLTFMaterial::convertTextureTransformToPBR(
F32 tex_scale_s,
F32 tex_scale_t,
F32 tex_offset_s,
F32 tex_offset_t,
F32 tex_rotation,
LLVector2& pbr_scale,
LLVector2& pbr_offset,
F32& pbr_rotation)
{
pbr_scale.set(tex_scale_s, tex_scale_t);
pbr_rotation = -(tex_rotation) / 2.f;
const F32 adjusted_offset_s = tex_offset_s;
const F32 adjusted_offset_t = -tex_offset_t;
F32 center_adjust_s = 0.5f * (1.0f - tex_scale_s);
F32 center_adjust_t = 0.5f * (1.0f - tex_scale_t);
if (pbr_rotation != 0.0f)
{
const F32 c = cosf(pbr_rotation);
const F32 s = sinf(pbr_rotation);
const F32 tmp_s = center_adjust_s * c - center_adjust_t * s;
const F32 tmp_t = center_adjust_s * s + center_adjust_t * c;
center_adjust_s = tmp_s;
center_adjust_t = tmp_t;
}
pbr_offset.set(adjusted_offset_s + center_adjust_s,
adjusted_offset_t + center_adjust_t);
}

View File

@ -214,6 +214,14 @@ public:
bool hasLocalTextures() { return !mTrackingIdToLocalTexture.empty(); }
virtual bool replaceLocalTexture(const LLUUID& tracking_id, const LLUUID &old_id, const LLUUID& new_id);
virtual void updateTextureTracking();
// Convert legacy TE transform values to PBR transform values.
static void convertTextureTransformToPBR(F32 tex_scale_s, F32 tex_scale_t,
F32 tex_offset_s, F32 tex_offset_t,
F32 tex_rotation,
LLVector2& pbr_scale,
LLVector2& pbr_offset,
F32& pbr_rotation);
protected:
static LLVector2 vec2FromJson(const std::map<std::string, tinygltf::Value>& object, const char* key, const LLVector2& default_value);
static F32 floatFromJson(const std::map<std::string, tinygltf::Value>& object, const char* key, const F32 default_value);

View File

@ -380,17 +380,6 @@ void LLGLTFMaterialList::queueApply(const LLViewerObject* obj, S32 side, const L
LLGLTFMaterial* material = new LLGLTFMaterial(*material_override);
sApplyQueue.push_back({ obj->getID(), side, asset_id, material });
}
if (sUpdates.size() >= MAX_TASK_UPDATES)
{
LLCoros::instance().launch("modifyMaterialCoro",
std::bind(&LLGLTFMaterialList::modifyMaterialCoro,
gAgent.getRegionCapability("ModifyMaterialParams"),
sUpdates,
std::shared_ptr<CallbackHolder>(nullptr)));
sUpdates = LLSD::emptyArray();
}
}
void LLGLTFMaterialList::queueUpdate(const LLSD& data)

View File

@ -1996,9 +1996,96 @@ bool LLSelectMgr::selectionSetGLTFMaterial(const LLUUID& mat_id)
asset_id = BLANK_MATERIAL_ASSET_ID;
}
}
// If this face already has the target material ID, do nothing.
// This prevents re-sending the same ID on OK, which can cause the server
// to drop overrides when queueApply is invoked with the OLD id.
if (objectp->getRenderMaterialID(te) == asset_id)
{
return true;
}
// Preserve existing texture transforms when switching to PBR material
LLTextureEntry* tep = objectp->getTE(te);
bool should_preserve_transforms = false;
LLGLTFMaterial* preserved_override = nullptr;
if (tep && asset_id.notNull())
{
// Only preserve transforms from existing GLTF material override
// Do not fall back to texture entry transforms when switching between PBR materials
LLGLTFMaterial* existing_override = tep->getGLTFMaterialOverride();
if (existing_override)
{
// Check if existing override has non-default transforms
const LLGLTFMaterial::TextureTransform& existing_transform = existing_override->mTextureTransform[0];
const LLGLTFMaterial::TextureTransform& default_transform = LLGLTFMaterial::TextureTransform();
if (existing_transform.mScale != default_transform.mScale ||
existing_transform.mOffset != default_transform.mOffset ||
existing_transform.mRotation != default_transform.mRotation)
{
// Preserve non-default transforms from current PBR material
preserved_override = new LLGLTFMaterial();
for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
{
preserved_override->mTextureTransform[i].mScale = existing_transform.mScale;
preserved_override->mTextureTransform[i].mOffset = existing_transform.mOffset;
preserved_override->mTextureTransform[i].mRotation = existing_transform.mRotation;
}
should_preserve_transforms = true;
}
// If existing override has default transforms, don't preserve anything
}
else
{
// No existing PBR material override - check texture entry transforms
// This handles the case of switching from Blinn-Phong to PBR material
F32 existing_scale_s, existing_scale_t, existing_offset_s, existing_offset_t, existing_rotation;
tep->getScale(&existing_scale_s, &existing_scale_t);
tep->getOffset(&existing_offset_s, &existing_offset_t);
existing_rotation = tep->getRotation();
const LLGLTFMaterial::TextureTransform& default_transform = LLGLTFMaterial::TextureTransform();
if (existing_scale_s != default_transform.mScale.mV[0] || existing_scale_t != default_transform.mScale.mV[1] ||
existing_offset_s != default_transform.mOffset.mV[0] || existing_offset_t != default_transform.mOffset.mV[1] ||
existing_rotation != default_transform.mRotation)
{
// Preserve non-default transforms from texture entry
preserved_override = new LLGLTFMaterial();
for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
{
LLVector2 pbr_scale, pbr_offset;
F32 pbr_rotation;
LLGLTFMaterial::convertTextureTransformToPBR(
existing_scale_s, existing_scale_t,
existing_offset_s, existing_offset_t,
existing_rotation,
pbr_scale, pbr_offset, pbr_rotation);
preserved_override->mTextureTransform[i].mScale = pbr_scale;
preserved_override->mTextureTransform[i].mOffset = pbr_offset;
preserved_override->mTextureTransform[i].mRotation = pbr_rotation;
}
should_preserve_transforms = true;
}
}
}
objectp->clearTEWaterExclusion(te);
// Blank out most override data on the object and send to server
objectp->setRenderMaterialID(te, asset_id);
if (should_preserve_transforms && preserved_override)
{
// Apply material with preserved transforms
LLGLTFMaterialList::queueApply(objectp, te, asset_id, preserved_override);
// Update local state
objectp->setRenderMaterialID(te, asset_id, false, true);
tep->setGLTFMaterialOverride(preserved_override);
}
else
{
objectp->setRenderMaterialID(te, asset_id);
}
return true;
}

View File

@ -29,6 +29,8 @@
// library headers
#include "llnotificationsutil.h"
#include <vector>
#include <tuple>
// project headers
#include "llagent.h"
#include "llagentcamera.h"
@ -1297,7 +1299,89 @@ void LLToolDragAndDrop::dropMaterialOneFace(LLViewerObject* hit_obj,
asset_id = BLANK_MATERIAL_ASSET_ID;
}
hit_obj->setRenderMaterialID(hit_face, asset_id);
// Preserve existing texture transforms when switching to PBR material
LLTextureEntry* tep = hit_obj->getTE(hit_face);
F32 existing_scale_s = LLGLTFMaterial::TextureTransform().mScale.mV[0];
F32 existing_scale_t = LLGLTFMaterial::TextureTransform().mScale.mV[1];
F32 existing_offset_s = LLGLTFMaterial::TextureTransform().mOffset.mV[0];
F32 existing_offset_t = LLGLTFMaterial::TextureTransform().mOffset.mV[1];
F32 existing_rotation = LLGLTFMaterial::TextureTransform().mRotation;
bool should_preserve_transforms = false;
LLGLTFMaterial* preserved_override = nullptr;
if (tep && asset_id.notNull())
{
// Only preserve transforms from existing GLTF material override
// Do not fall back to texture entry transforms when switching between PBR materials
LLGLTFMaterial* existing_override = tep->getGLTFMaterialOverride();
if (existing_override)
{
// Check if existing override has non-default transforms
const LLGLTFMaterial::TextureTransform& existing_transform = existing_override->mTextureTransform[0];
const LLGLTFMaterial::TextureTransform& default_transform = LLGLTFMaterial::TextureTransform();
if (existing_transform.mScale != default_transform.mScale ||
existing_transform.mOffset != default_transform.mOffset ||
existing_transform.mRotation != default_transform.mRotation)
{
// Preserve non-default transforms from current PBR material
preserved_override = new LLGLTFMaterial();
for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
{
preserved_override->mTextureTransform[i].mScale = existing_transform.mScale;
preserved_override->mTextureTransform[i].mOffset = existing_transform.mOffset;
preserved_override->mTextureTransform[i].mRotation = existing_transform.mRotation;
}
should_preserve_transforms = true;
}
// If existing override has default transforms, don't preserve anything
}
else
{
// No existing PBR material override - check texture entry transforms
// This handles the case of switching from Blinn-Phong to PBR material
F32 existing_scale_s, existing_scale_t, existing_offset_s, existing_offset_t, existing_rotation;
tep->getScale(&existing_scale_s, &existing_scale_t);
tep->getOffset(&existing_offset_s, &existing_offset_t);
existing_rotation = tep->getRotation();
const LLGLTFMaterial::TextureTransform& default_transform = LLGLTFMaterial::TextureTransform();
if (existing_scale_s != default_transform.mScale.mV[0] || existing_scale_t != default_transform.mScale.mV[1] ||
existing_offset_s != default_transform.mOffset.mV[0] || existing_offset_t != default_transform.mOffset.mV[1] ||
existing_rotation != default_transform.mRotation)
{
// Preserve non-default transforms from texture entry
preserved_override = new LLGLTFMaterial();
for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
{
LLVector2 pbr_scale, pbr_offset;
F32 pbr_rotation;
LLGLTFMaterial::convertTextureTransformToPBR(
existing_scale_s, existing_scale_t,
existing_offset_s, existing_offset_t,
existing_rotation,
pbr_scale, pbr_offset, pbr_rotation);
preserved_override->mTextureTransform[i].mScale = pbr_scale;
preserved_override->mTextureTransform[i].mOffset = pbr_offset;
preserved_override->mTextureTransform[i].mRotation = pbr_rotation;
}
should_preserve_transforms = true;
}
}
}
if (should_preserve_transforms && preserved_override)
{
// Apply material with preserved transforms
LLGLTFMaterialList::queueApply(hit_obj, hit_face, asset_id, preserved_override);
// Update local state
hit_obj->setRenderMaterialID(hit_face, asset_id, false, true);
tep->setGLTFMaterialOverride(preserved_override);
}
else
{
hit_obj->setRenderMaterialID(hit_face, asset_id);
}
dialog_refresh_all();
@ -1333,7 +1417,134 @@ void LLToolDragAndDrop::dropMaterialAllFaces(LLViewerObject* hit_obj,
asset_id = BLANK_MATERIAL_ASSET_ID;
}
hit_obj->setRenderMaterialIDs(asset_id);
// Preserve existing texture transforms when switching to PBR material for all faces
std::vector<std::pair<bool, LLGLTFMaterial*>> preserved_transforms(hit_obj->getNumTEs());
if (asset_id.notNull())
{
for (S32 te = 0; te < hit_obj->getNumTEs(); ++te)
{
LLTextureEntry* tep = hit_obj->getTE(te);
if (!tep) continue;
bool should_preserve = false;
LLGLTFMaterial* preserved_override = nullptr;
// Only preserve transforms from existing GLTF material override
// Do not fall back to texture entry transforms when switching between PBR materials
LLGLTFMaterial* existing_override = tep->getGLTFMaterialOverride();
if (existing_override)
{
// Check if existing override has non-default transforms
const LLGLTFMaterial::TextureTransform& existing_transform = existing_override->mTextureTransform[0];
const LLGLTFMaterial::TextureTransform& default_transform = LLGLTFMaterial::TextureTransform();
if (existing_transform.mScale != default_transform.mScale ||
existing_transform.mOffset != default_transform.mOffset ||
existing_transform.mRotation != default_transform.mRotation)
{
// Preserve non-default transforms from current PBR material
preserved_override = new LLGLTFMaterial();
for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
{
preserved_override->mTextureTransform[i].mScale = existing_transform.mScale;
preserved_override->mTextureTransform[i].mOffset = existing_transform.mOffset;
preserved_override->mTextureTransform[i].mRotation = existing_transform.mRotation;
}
should_preserve = true;
}
else
{
// Existing override has default transforms, fall back to texture entry
F32 existing_scale_s, existing_scale_t, existing_offset_s, existing_offset_t, existing_rotation;
tep->getScale(&existing_scale_s, &existing_scale_t);
tep->getOffset(&existing_offset_s, &existing_offset_t);
existing_rotation = tep->getRotation();
if (existing_scale_s != default_transform.mScale.mV[0] || existing_scale_t != default_transform.mScale.mV[1] ||
existing_offset_s != default_transform.mOffset.mV[0] || existing_offset_t != default_transform.mOffset.mV[1] ||
existing_rotation != default_transform.mRotation)
{
// Preserve non-default transforms from texture entry
preserved_override = new LLGLTFMaterial();
for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
{
LLVector2 pbr_scale, pbr_offset;
F32 pbr_rotation;
LLGLTFMaterial::convertTextureTransformToPBR(
existing_scale_s, existing_scale_t,
existing_offset_s, existing_offset_t,
existing_rotation,
pbr_scale, pbr_offset, pbr_rotation);
preserved_override->mTextureTransform[i].mScale = pbr_scale;
preserved_override->mTextureTransform[i].mOffset = pbr_offset;
preserved_override->mTextureTransform[i].mRotation = pbr_rotation;
}
should_preserve = true;
}
}
}
else
{
// No existing PBR material override - check texture entry transforms
// This handles the case of switching from Blinn-Phong to PBR material
F32 existing_scale_s, existing_scale_t, existing_offset_s, existing_offset_t, existing_rotation;
tep->getScale(&existing_scale_s, &existing_scale_t);
tep->getOffset(&existing_offset_s, &existing_offset_t);
existing_rotation = tep->getRotation();
const LLGLTFMaterial::TextureTransform& default_transform = LLGLTFMaterial::TextureTransform();
if (existing_scale_s != default_transform.mScale.mV[0] || existing_scale_t != default_transform.mScale.mV[1] ||
existing_offset_s != default_transform.mOffset.mV[0] || existing_offset_t != default_transform.mOffset.mV[1] ||
existing_rotation != default_transform.mRotation)
{
// Preserve non-default transforms from texture entry
preserved_override = new LLGLTFMaterial();
for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
{
LLVector2 pbr_scale, pbr_offset;
F32 pbr_rotation;
LLGLTFMaterial::convertTextureTransformToPBR(
existing_scale_s, existing_scale_t,
existing_offset_s, existing_offset_t,
existing_rotation,
pbr_scale, pbr_offset, pbr_rotation);
preserved_override->mTextureTransform[i].mScale = pbr_scale;
preserved_override->mTextureTransform[i].mOffset = pbr_offset;
preserved_override->mTextureTransform[i].mRotation = pbr_rotation;
}
should_preserve = true;
}
}
preserved_transforms[te] = std::make_pair(should_preserve, preserved_override);
}
}
// Apply materials with preserved transforms
if (asset_id.notNull())
{
for (S32 te = 0; te < hit_obj->getNumTEs(); ++te)
{
LLGLTFMaterial* preserved_override = preserved_transforms[te].second;
if (preserved_override)
{
// Apply material with preserved transforms
LLGLTFMaterialList::queueApply(hit_obj, te, asset_id, preserved_override);
// Update local state
hit_obj->setRenderMaterialID(te, asset_id, false, true);
hit_obj->getTE(te)->setGLTFMaterialOverride(preserved_override);
}
else
{
hit_obj->setRenderMaterialID(te, asset_id, false, true);
}
}
}
else
{
hit_obj->setRenderMaterialIDs(asset_id);
}
dialog_refresh_all();
// send the update to the simulator
hit_obj->sendTEUpdate();