diff options
| author | Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> | 2026-01-28 03:43:43 +0200 |
|---|---|---|
| committer | Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> | 2026-01-30 01:07:21 +0200 |
| commit | 98d7f697ce5d7509a4dabde877de2856e27892dd (patch) | |
| tree | 3ce17a419652d89e41985face72bd1f867f90bd2 /indra | |
| parent | 493455a7caf8449ccc4112612a192cd0dfea29df (diff) | |
#2975 PBR support for planar aligment
Since checkbox is shared between pbr and normal textures, I'm making it affect both for now.
Diffstat (limited to 'indra')
| -rw-r--r-- | indra/llprimitive/llgltfmaterial.cpp | 90 | ||||
| -rw-r--r-- | indra/llprimitive/llgltfmaterial.h | 8 | ||||
| -rw-r--r-- | indra/newview/llface.cpp | 85 | ||||
| -rw-r--r-- | indra/newview/llface.h | 2 | ||||
| -rw-r--r-- | indra/newview/llpanelface.cpp | 21 |
5 files changed, 192 insertions, 14 deletions
diff --git a/indra/llprimitive/llgltfmaterial.cpp b/indra/llprimitive/llgltfmaterial.cpp index b2cd1dc597..1c47001272 100644 --- a/indra/llprimitive/llgltfmaterial.cpp +++ b/indra/llprimitive/llgltfmaterial.cpp @@ -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; + + // 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; - pbr_offset.set(adjusted_offset_s + center_adjust_s, - adjusted_offset_t + center_adjust_t); + // 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); } diff --git a/indra/llprimitive/llgltfmaterial.h b/indra/llprimitive/llgltfmaterial.h index 8d45cb6185..c37062e7d3 100644 --- a/indra/llprimitive/llgltfmaterial.h +++ b/indra/llprimitive/llgltfmaterial.h @@ -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); diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 7c631a7280..2b318dcf5f 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -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)) diff --git a/indra/newview/llface.h b/indra/newview/llface.h index df31e9ea90..6e9d23c3a2 100644 --- a/indra/newview/llface.h +++ b/indra/newview/llface.h @@ -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; diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index bcb51b22ca..128ba42efd 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -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) { |
