Revert "Revert "add rotate to SkM44""
Fix: update the generator code itself for the vec --> ptr change
This reverts commit 44aa1ab584e481089a4eb4fcaab6726cc52f250a.
Change-Id: Idfec2b42239429e58501ca2ba108ec852891e237
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/266575
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Mike Reed <reed@google.com>
diff --git a/include/private/SkM44.h b/include/private/SkM44.h
index 1a75b78..3f1ecb0 100644
--- a/include/private/SkM44.h
+++ b/include/private/SkM44.h
@@ -44,8 +44,8 @@
SkScalar dot(const SkV3& v) const { return Dot(*this, v); }
SkV3 cross(const SkV3& v) const { return Cross(*this, v); }
- const float* vec() const { return &x; }
- float* vec() { return &x; }
+ const float* ptr() const { return &x; }
+ float* ptr() { return &x; }
};
struct SkV4 {
@@ -68,10 +68,18 @@
}
friend SkV4 operator*(SkScalar s, const SkV4& v) { return v*s; }
- const float* vec() const { return &x; }
- float* vec() { return &x; }
+ const float* ptr() const { return &x; }
+ float* ptr() { return &x; }
};
+/**
+ * 4x4 matrix used by SkCanvas and other parts of Skia.
+ *
+ * Skia assumes a right-handed coordinate system:
+ * +X goes to the right
+ * +Y goes down
+ * +Z goes into the screen (away from the viewer)
+ */
class SkM44 {
public:
SkM44(const SkM44& src) = default;
@@ -151,6 +159,12 @@
0, 0, 0, 1);
}
+ static SkM44 Rotate(SkV3 axis, SkScalar radians) {
+ SkM44 m(kUninitialized_Constructor);
+ m.setRotate(axis, radians);
+ return m;
+ }
+
bool operator==(const SkM44& other) const;
bool operator!=(const SkM44& other) const {
return !(other == *this);
@@ -209,7 +223,7 @@
}
void setCol(int i, const SkV4& v) {
SkASSERT(i >= 0 && i <= 3);
- memcpy(&fMat[i*4], v.vec(), sizeof(v));
+ memcpy(&fMat[i*4], v.ptr(), sizeof(v));
}
SkM44& setIdentity() {
@@ -233,6 +247,34 @@
0, 0, 0, 1);
}
+ /**
+ * Set this matrix to rotate about the specified unit-length axis vector,
+ * by an angle specified by its sin() and cos().
+ *
+ * This does not attempt to verify that axis.length() == 1 or that the sin,cos values
+ * are correct.
+ */
+ SkM44& setRotateUnitSinCos(SkV3 axis, SkScalar sinAngle, SkScalar cosAngle);
+
+ /**
+ * Set this matrix to rotate about the specified unit-length axis vector,
+ * by an angle specified in radians.
+ *
+ * This does not attempt to verify that axis.length() == 1.
+ */
+ SkM44& setRotateUnit(SkV3 axis, SkScalar radians) {
+ return this->setRotateUnitSinCos(axis, SkScalarSin(radians), SkScalarCos(radians));
+ }
+
+ /**
+ * Set this matrix to rotate about the specified axis vector,
+ * by an angle specified in radians.
+ *
+ * Note: axis is not assumed to be unit-length, so it will be normalized internally.
+ * If axis is already unit-length, call setRotateAboutUnitRadians() instead.
+ */
+ SkM44& setRotate(SkV3 axis, SkScalar radians);
+
SkM44& setConcat16(const SkM44& a, const SkScalar colMajor[16]);
SkM44& setConcat(const SkM44& a, const SkM44& b) {
@@ -262,7 +304,7 @@
SkV4 operator*(const SkV4& v) const {
return this->map(v.x, v.y, v.z, v.w);
}
- SkV3 operator*(const SkV3& v) const {
+ SkV3 operator*(SkV3 v) const {
auto v4 = this->map(v.x, v.y, v.z, 0);
return {v4.x, v4.y, v4.z};
}
diff --git a/src/core/SkM44.cpp b/src/core/SkM44.cpp
index d490036..73971cd 100644
--- a/src/core/SkM44.cpp
+++ b/src/core/SkM44.cpp
@@ -256,6 +256,32 @@
return trans;
}
+SkM44& SkM44::setRotateUnitSinCos(SkV3 axis, SkScalar sinAngle, SkScalar cosAngle) {
+ // Taken from "Essential Mathematics for Games and Interactive Applications"
+ // James M. Van Verth and Lars M. Bishop -- third edition
+ SkScalar x = axis.x;
+ SkScalar y = axis.y;
+ SkScalar z = axis.z;
+ SkScalar c = cosAngle;
+ SkScalar s = sinAngle;
+ SkScalar t = 1 - c;
+
+ return this->set44(t*x*x + c, t*x*y - s*z, t*x*z + s*y, 0,
+ t*x*y + s*z, t*y*y + c, t*y*z - s*x, 0,
+ t*x*z - s*y, t*y*z + s*x, t*z*z + c, 0,
+ 0, 0, 0, 1);
+}
+
+SkM44& SkM44::setRotate(SkV3 axis, SkScalar radians) {
+ SkScalar len = axis.length();
+ if (len > 0 && SkScalarIsFinite(len)) {
+ this->setRotateUnit(axis * (SK_Scalar1 / len), radians);
+ } else {
+ this->setIdentity();
+ }
+ return *this;
+}
+
///////////////////////////////////////////////////////////////////////////////
void SkM44::dump() const {
diff --git a/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.cpp b/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.cpp
index 9357443..357b1c0 100644
--- a/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.cpp
+++ b/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.cpp
@@ -63,7 +63,7 @@
const SkV4& vValue = _outer.v;
if (vPrev != (vValue)) {
vPrev = vValue;
- pdman.set4fv(vVar, 1, vValue.vec());
+ pdman.set4fv(vVar, 1, vValue.ptr());
}
}
}
diff --git a/src/sksl/SkSLCPPUniformCTypes.cpp b/src/sksl/SkSLCPPUniformCTypes.cpp
index 4c550c3..061ad55 100644
--- a/src/sksl/SkSLCPPUniformCTypes.cpp
+++ b/src/sksl/SkSLCPPUniformCTypes.cpp
@@ -162,7 +162,7 @@
"{SK_FloatNaN, SK_FloatNaN, SK_FloatNaN, SK_FloatNaN}"), // default value
REGISTER(Layout::CType::kSkV4, { "half4", "float4", "double4" },
- "${pdman}.set4fv(${uniform}, 1, ${var}.vec())", // to gpu
+ "${pdman}.set4fv(${uniform}, 1, ${var}.ptr())", // to gpu
"SkV4{SK_FloatNaN, SK_FloatNaN, SK_FloatNaN, SK_FloatNaN}", // default value
"${oldVar} != (${newVar})"), // dirty check
diff --git a/tests/Matrix44Test.cpp b/tests/Matrix44Test.cpp
index dcad75b..3290e0e 100644
--- a/tests/Matrix44Test.cpp
+++ b/tests/Matrix44Test.cpp
@@ -1058,3 +1058,57 @@
REPORTER_ASSERT(reporter, (c0 - r0 == SkV4{c0.x-r0.x, c0.y-r0.y, c0.z-r0.z, c0.w-r0.w}));
REPORTER_ASSERT(reporter, (c0 * r0 == SkV4{c0.x*r0.x, c0.y*r0.y, c0.z*r0.z, c0.w*r0.w}));
}
+
+DEF_TEST(M44_rotate, reporter) {
+ const SkV3 x = {1, 0, 0},
+ y = {0, 1, 0},
+ z = {0, 0, 1};
+
+ // We have radians version of setRotateAbout methods, but even with our best approx
+ // for PI, sin(SK_ScalarPI) != 0, so to make the comparisons in the unittest clear,
+ // I'm using the variants that explicitly take the sin,cos values.
+
+ struct {
+ SkScalar sinAngle, cosAngle;
+ SkV3 aboutAxis;
+ SkV3 expectedX, expectedY, expectedZ;
+ } recs[] = {
+ { 0, 1, x, x, y, z}, // angle = 0
+ { 0, 1, y, x, y, z}, // angle = 0
+ { 0, 1, z, x, y, z}, // angle = 0
+
+ { 0,-1, x, x,-y,-z}, // angle = 180
+ { 0,-1, y, -x, y,-z}, // angle = 180
+ { 0,-1, z, -x,-y, z}, // angle = 180
+
+ // Skia coordinate system is right-handed
+
+ { 1, 0, x, x, z,-y}, // angle = 90
+ { 1, 0, y, -z, y, x}, // angle = 90
+ { 1, 0, z, y,-x, z}, // angle = 90
+
+ {-1, 0, x, x,-z, y}, // angle = -90
+ {-1, 0, y, z, y,-x}, // angle = -90
+ {-1, 0, z, -y, x, z}, // angle = -90
+ };
+
+ for (const auto& r : recs) {
+ SkM44 m(SkM44::kNaN_Constructor);
+ m.setRotateUnitSinCos(r.aboutAxis, r.sinAngle, r.cosAngle);
+
+ auto mx = m * x;
+ auto my = m * y;
+ auto mz = m * z;
+ REPORTER_ASSERT(reporter, mx == r.expectedX);
+ REPORTER_ASSERT(reporter, my == r.expectedY);
+ REPORTER_ASSERT(reporter, mz == r.expectedZ);
+
+ // flipping the axis-of-rotation should flip the results
+ mx = m * -x;
+ my = m * -y;
+ mz = m * -z;
+ REPORTER_ASSERT(reporter, mx == -r.expectedX);
+ REPORTER_ASSERT(reporter, my == -r.expectedY);
+ REPORTER_ASSERT(reporter, mz == -r.expectedZ);
+ }
+}