From 24f4bf9c28eec36ddb9c4adbd6af46d887674ded Mon Sep 17 00:00:00 2001
From: Dobrohotov Alexei <alexdobrohotov@yandex.ru>
Date: Fri, 30 Apr 2021 12:36:16 +0300
Subject: [PATCH 1/2] Fix NiUVController UV offset calculations (bug #5995)

---
 CHANGELOG.md                     |  1 +
 components/nifosg/controller.cpp | 17 ++++++++++-------
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index dec5e1e7f0..87009f58d4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -118,6 +118,7 @@
     Bug #5923: Clicking on empty spaces between journal entries might show random topics
     Bug #5934: AddItem command doesn't accept negative values
     Bug #5975: NIF controllers from sheath meshes are used
+    Bug #5995: NiUVController doesn't calculate the UV offset properly
     Feature #390: 3rd person look "over the shoulder"
     Feature #832: OpenMW-CS: Handle deleted references
     Feature #1536: Show more information about level on menu
diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp
index 31fd92b43e..7f4bdcef21 100644
--- a/components/nifosg/controller.cpp
+++ b/components/nifosg/controller.cpp
@@ -282,14 +282,17 @@ void UVController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv)
         float uScale = mUScale.interpKey(value);
         float vScale = mVScale.interpKey(value);
 
-        osg::Matrix flipMat;
-        flipMat.preMultTranslate(osg::Vec3f(0,1,0));
-        flipMat.preMultScale(osg::Vec3f(1,-1,1));
-
+        // First scale the UV relative to (0,0),
+        // then offset the UV to change the scaling origin to its center.
         osg::Matrixf mat = osg::Matrixf::scale(uScale, vScale, 1);
-        mat.setTrans(uTrans, vTrans, 0);
-
-        mat = flipMat * mat * flipMat;
+        float uOffset = 0.5f * (1.f - uScale);
+        float vOffset = 0.5f * (1.f - vScale);
+        // Apply the original offsets:
+        // U offset is supposed to be subtracted regardless of the graphics library,
+        // while V offset is made negative to account for OpenGL's Y axis convention.
+        uOffset -= uTrans;
+        vOffset -= vTrans;
+        mat.setTrans(uOffset, vOffset, 0);
 
         // setting once is enough because all other texture units share the same TexMat (see setDefaults).
         if (!mTextureUnits.empty())

From 971ba81ed279f97b2116540d3217a041d6cdb66a Mon Sep 17 00:00:00 2001
From: Dobrohotov Alexei <alexdobrohotov@yandex.ru>
Date: Fri, 30 Apr 2021 18:23:04 +0300
Subject: [PATCH 2/2] Use higher level transformations in UVController

---
 components/nifosg/controller.cpp | 26 +++++++++++---------------
 1 file changed, 11 insertions(+), 15 deletions(-)

diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp
index 7f4bdcef21..d48c55ad7d 100644
--- a/components/nifosg/controller.cpp
+++ b/components/nifosg/controller.cpp
@@ -277,22 +277,18 @@ void UVController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv)
     if (hasInput())
     {
         float value = getInputValue(nv);
-        float uTrans = mUTrans.interpKey(value);
-        float vTrans = mVTrans.interpKey(value);
-        float uScale = mUScale.interpKey(value);
-        float vScale = mVScale.interpKey(value);
 
-        // First scale the UV relative to (0,0),
-        // then offset the UV to change the scaling origin to its center.
-        osg::Matrixf mat = osg::Matrixf::scale(uScale, vScale, 1);
-        float uOffset = 0.5f * (1.f - uScale);
-        float vOffset = 0.5f * (1.f - vScale);
-        // Apply the original offsets:
-        // U offset is supposed to be subtracted regardless of the graphics library,
-        // while V offset is made negative to account for OpenGL's Y axis convention.
-        uOffset -= uTrans;
-        vOffset -= vTrans;
-        mat.setTrans(uOffset, vOffset, 0);
+        // First scale the UV relative to its center, then apply the offset.
+        // U offset is flipped regardless of the graphics library,
+        // while V offset is flipped to account for OpenGL Y axis convention.
+        osg::Vec3f uvOrigin(0.5f, 0.5f, 0.f);
+        osg::Vec3f uvScale(mUScale.interpKey(value), mVScale.interpKey(value), 1.f);
+        osg::Vec3f uvTrans(-mUTrans.interpKey(value), -mVTrans.interpKey(value), 0.f);
+
+        osg::Matrixf mat = osg::Matrixf::translate(uvOrigin);
+        mat.preMultScale(uvScale);
+        mat.preMultTranslate(-uvOrigin);
+        mat.setTrans(mat.getTrans() + uvTrans);
 
         // setting once is enough because all other texture units share the same TexMat (see setDefaults).
         if (!mTextureUnits.empty())