#2975 PBR support for planar aligment

Since checkbox is shared between pbr and normal textures, I'm making it affect both for now.
This commit is contained in:
Andrey Kleshchev
2026-01-30 01:07:21 +02:00
parent 493455a7ca
commit 98d7f697ce
5 changed files with 192 additions and 14 deletions
+76 -14
View File
@@ -924,6 +924,25 @@ void LLGLTFMaterial::updateTextureTracking()
// for material overrides editor will set it
}
// Test cases:
// Case 1.
// Input: scale 1.0,1.0; Offset horizontal 0.0, Offset vertical 0.0 Rotation 0.349066;
// Expected output: scale 1.0,1.0; Offset horizontal 0.201, Offset vertical -0.141 Rotation -0.349066;
// Case 2.
// Input: scale 1.0,1.0; Offset horizontal 0.5, Offset vertical 0.1 Rotation 0;
// Expected output: scale 1.0,1.0; Offset horizontal 0.5, Offset vertical -0.1 Rotation -0;
// Case 3.
// Input: scale 1.0,1.0; Offset horizontal 0.1, Offset vertical 0.2 Rotation 0.349066;
// Expected output: scale 1.0,1.0; Offset horizontal 0.295, Offset vertical -0.345 Rotation -0.349066;
// Case 4.
// Input: scale 1.0,1.0; Offset horizontal 0.5, Offset vertical 0.0 Rotation 0.349066;
// Expected output: scale 1.0,1.0; Offset horizontal 0.701, Offset vertical -0.141 Rotation -0.349066;
//
// Legacy offsets are right to left and top to bottom.
// PBR offsets are right to left and bottom to top.
//
// Legacy rotation is relative to face's center counter clockwise,
// PBR rotation is relative to top-left corner, clockwise
void LLGLTFMaterial::convertTextureTransformToPBR(
F32 tex_scale_s,
F32 tex_scale_t,
@@ -935,22 +954,65 @@ void LLGLTFMaterial::convertTextureTransformToPBR(
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;
pbr_rotation = -tex_rotation;
// Center of the tile
const F32 center_s = 0.5f;
const F32 center_t = 0.5f;
// Center adjustment for scale
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;
}
// 2. Offset from center
F32 pos_s = center_adjust_s - center_s;
F32 pos_t = center_adjust_t - center_t;
pbr_offset.set(adjusted_offset_s + center_adjust_s,
adjusted_offset_t + center_adjust_t);
// 3. Rotate around center (clockwise, as per GLTF spec)
F32 c = cosf(pbr_rotation);
F32 s = sinf(pbr_rotation);
F32 rot_s = pos_s * c + pos_t * s;
F32 rot_t = -pos_s * s + pos_t * c;
// 4. Move back to top-left and apply offset
pbr_offset.set(rot_s + center_s + tex_offset_s, rot_t + center_t - tex_offset_t);
}
// Convert PBR transform values back to legacy TE transform values.
// This is the reverse of convertTextureTransformToPBR.
void LLGLTFMaterial::convertPBRTransformToTexture(
const LLVector2& pbr_scale,
const LLVector2& pbr_offset,
F32 pbr_rotation,
F32& tex_scale_s,
F32& tex_scale_t,
F32& tex_offset_s,
F32& tex_offset_t,
F32& tex_rotation)
{
tex_scale_s = pbr_scale.mV[0];
tex_scale_t = pbr_scale.mV[1];
tex_rotation = -pbr_rotation;
// Center of the tile
const F32 center_s = 0.5f;
const F32 center_t = 0.5f;
// Center adjustment for scale
F32 center_adjust_s = 0.5f * (1.0f - tex_scale_s);
F32 center_adjust_t = 0.5f * (1.0f - tex_scale_t);
// 2. Offset from center
F32 pos_s = center_adjust_s - center_s;
F32 pos_t = center_adjust_t - center_t;
// 3. Rotate around center (clockwise, as per GLTF spec)
F32 c = cosf(pbr_rotation);
F32 s = sinf(pbr_rotation);
F32 rot_s = pos_s * c + pos_t * s;
F32 rot_t = -pos_s * s + pos_t * c;
// 3. Recover legacy offset
tex_offset_s = pbr_offset.mV[0] - rot_s - center_s;
tex_offset_t = -(pbr_offset.mV[1] - rot_t - center_t);
}
+8
View File
@@ -222,6 +222,14 @@ public:
LLVector2& pbr_scale,
LLVector2& pbr_offset,
F32& pbr_rotation);
// Convert PBR transform values to legacy TE transform values.
static void convertPBRTransformToTexture(const LLVector2& pbr_scale,
const LLVector2& pbr_offset,
F32 pbr_rotation,
F32& tex_scale_s, F32& tex_scale_t,
F32& tex_offset_s, F32& tex_offset_t,
F32& tex_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);
+85
View File
@@ -1067,6 +1067,91 @@ bool LLFace::calcAlignedPlanarTE(const LLFace* align_to, LLVector2* res_st_offs
return true;
}
F32 dot_product(const LLVector3& a, const LLVector3& b)
{
return a.mV[VX] * b.mV[VX] + a.mV[VY] * b.mV[VY] + a.mV[VZ] * b.mV[VZ];
}
bool LLFace::calcAlignedPlanarGLTF(
const LLFace* align_to,
LLVector2* res_st_offset,
LLVector2* res_st_scale,
F32* res_st_rot,
S32 gltf_info_index) const
{
if (!align_to)
{
return false;
}
const LLTextureEntry* orig_tep = align_to->getTextureEntry();
const LLTextureEntry* tep = getTextureEntry();
if (!orig_tep || !tep)
{
return false;
}
// Only support planar mapping for now
if (orig_tep->getTexGen() != LLTextureEntry::TEX_GEN_PLANAR ||
tep->getTexGen() != LLTextureEntry::TEX_GEN_PLANAR)
{
return false;
}
LLGLTFMaterial* orig_mat = orig_tep->getGLTFRenderMaterial();
LLGLTFMaterial* this_mat = tep->getGLTFRenderMaterial();
if (!orig_mat || !this_mat)
{
return false;
}
// Get the original GLTF transform for the specified channel
const auto& orig_tt = orig_mat->mTextureTransform[gltf_info_index];
// Convert GLTF transform to legacy TE transform
F32 map_scaleS, map_scaleT, map_offsS, map_offsT, map_rot;
LLGLTFMaterial::convertPBRTransformToTexture(
orig_tt.mScale,
orig_tt.mOffset,
orig_tt.mRotation,
map_scaleS, map_scaleT, map_offsS, map_offsT, map_rot);
// Calculate aligments
LLVector3 orig_pos, this_pos;
LLQuaternion orig_face_rot, this_face_rot;
F32 orig_proj_scale, this_proj_scale;
align_to->getPlanarProjectedParams(&orig_face_rot, &orig_pos, &orig_proj_scale);
getPlanarProjectedParams(&this_face_rot, &this_pos, &this_proj_scale);
// The rotation of "this face's" texture:
LLQuaternion orig_st_rot = LLQuaternion(map_rot, LLVector3::z_axis) * orig_face_rot;
LLQuaternion this_st_rot = orig_st_rot * ~this_face_rot;
F32 x_ang, y_ang, z_ang;
this_st_rot.getEulerAngles(&x_ang, &y_ang, &z_ang);
// Offset and scale of "this face's" texture:
LLVector3 centers_dist = (this_pos - orig_pos) * ~orig_st_rot;
LLVector3 st_scale(map_scaleS, map_scaleT, 1.f);
st_scale *= orig_proj_scale;
centers_dist.scaleVec(st_scale);
LLVector2 orig_st_offset(map_offsS, map_offsT);
LLVector2 tex_res_st_offset = orig_st_offset + (LLVector2)centers_dist;
tex_res_st_offset.mV[VX] -= (S32)tex_res_st_offset.mV[VX];
tex_res_st_offset.mV[VY] -= (S32)tex_res_st_offset.mV[VY];
st_scale /= this_proj_scale;
// Convert aligned legacy TE transform back to GLTF transform
LLGLTFMaterial::convertTextureTransformToPBR(
st_scale.mV[0], st_scale.mV[1],
tex_res_st_offset.mV[0], tex_res_st_offset.mV[1],
z_ang,
*res_st_scale, *res_st_offset, *res_st_rot);
return true;
}
void LLFace::updateRebuildFlags()
{
if (mDrawablep->isState(LLDrawable::REBUILD_VOLUME))
+2
View File
@@ -103,6 +103,8 @@ public:
LLVector3 getPositionAgent() const;
LLVector2 surfaceToTexture(LLVector2 surface_coord, const LLVector4a& position, const LLVector4a& normal);
void getPlanarProjectedParams(LLQuaternion* face_rot, LLVector3* face_pos, F32* scale) const;
bool calcAlignedPlanarGLTF(const LLFace* align_to, LLVector2* res_st_offset,
LLVector2* res_st_scale, F32* res_st_rot, S32 gltf_info_index = 0) const;
bool calcAlignedPlanarTE(const LLFace* align_to, LLVector2* st_offset,
LLVector2* st_scale, F32* st_rot, LLRender::eTexIndex map = LLRender::DIFFUSE_MAP) const;
+21
View File
@@ -852,6 +852,27 @@ struct LLPanelFaceSetAlignedTEFunctor : public LLSelectedTEFunctor
LLPanelFace::LLSelectedTEMaterial::setSpecularRepeatX(mPanel, uv_scale.mV[VX], te, object->getID());
LLPanelFace::LLSelectedTEMaterial::setSpecularRepeatY(mPanel, uv_scale.mV[VY], te, object->getID());
}
// Also align GLTF material if any
S32 gltf_info_index = 0; // base texture
LLVector2 gltf_offset, gltf_scale;
F32 gltf_rot;
if (facep->calcAlignedPlanarGLTF(mCenterFace, &gltf_offset, &gltf_scale, &gltf_rot, gltf_info_index))
{
LLGLTFMaterial new_override;
const LLTextureEntry* tep = object->getTE(te);
if (tep && tep->getGLTFMaterialOverride())
{
new_override = *tep->getGLTFMaterialOverride();
}
LLGLTFMaterial::TextureTransform& transform = new_override.mTextureTransform[gltf_info_index];
transform.mOffset.set(gltf_offset.mV[0], gltf_offset.mV[1]);
transform.mScale.set(gltf_scale.mV[0], gltf_scale.mV[1]);
transform.mRotation = gltf_rot;
LLGLTFMaterialList::queueModify(object, te, &new_override);
}
}
if (!set_aligned)
{