2
\$\begingroup\$

I want to rotate a 2D orthographic projection around the centre point of my view. However, something is off (it looks like a translation) but I'm not sure what.

To put some concrete values on the variables below:

  • rotation = 90.0 degrees
  • zoom = 0.5
  • size = (640, 480)
  • center = (320, 340)

My code is as follows:

Transform::orthographic(0.0, self.size.x / self.zoom, 0.0, self.size.y / self.zoom, 0.0, 1.0) * Transform::from_trs(self.center, -self.rotation, Vec2f::ONE, self.center) 

And the values from the transform matrix (columnar):

[-0.00000000006829905, 0.0015625, 0, -1.03125, 0.0020833334, 0.0000000000910654, 0, -0.37500012, 0, 0, 1, 0, 0, 0, 0, 1] 

Here's the correct rotation and the code (borrowed from SFML and re-written in Rust for my own transform class):

let angle = self.rotation.as_radians(); let cosine = angle.cos(); let sine = angle.sin(); let tx = -self.center.x * cosine - self.center.y * sine + self.center.x; let ty = self.center.x * sine - self.center.y * cosine + self.center.y; let a = 2.0 / (self.size.x / self.zoom); let b = -2.0 / (self.size.y / self.zoom); let c = -a * self.center.x; let d = -b * self.center.y; Transform::new( a * cosine, a * sine, 0.0, a * tx + c, -b * sine, b * cosine, 0.0, b * ty + d, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, ) 

And the values from the transform matrix:

[-0.00000000006829905, 0.0015625, 0, -0.53125, 0.0020833334, 0.0000000000910654, 0, -0.66666675, 0, 0, 1, 0, 0, 0, 0, 1] 

Having done the calculations by hand, the difference for the translation in x is the value of -c from the correct implementation, but I don't know why this is negated in comparison. I have no idea where the difference for the translation in y comes from.

What the incorrect rotation looks like:

enter image description here

And the correct rotation:

enter image description here

FWIW, here is the code for the Transform class:

impl Transform { pub const fn new( m00: f32, m01: f32, m02: f32, m03: f32, m10: f32, m11: f32, m12: f32, m13: f32, m20: f32, m21: f32, m22: f32, m23: f32, m30: f32, m31: f32, m32: f32, m33: f32, ) -> Self { Self { matrix: [ m00, m10, m20, m30, m01, m11, m21, m31, m02, m12, m22, m32, m03, m13, m23, m33, ] } } pub fn from_trs<V, A>(position: V, rotation: A, scale: V, origin: V) -> Self where V: Into<Vec2f>, A: Into<Angle>, { let position = position.into(); let rotation = rotation.into(); let scale = scale.into(); let origin = origin.into(); let angle = -rotation.as_radians(); let cosine = angle.cos(); let sine = angle.sin(); let sxc = scale.x * cosine; let syc = scale.y * cosine; let sxs = scale.x * sine; let sys = scale.y * sine; let tx = -origin.x * sxc - origin.y * sys + position.x; let ty = origin.x * sxs - origin.y * syc + position.y; Self::new( cosine, sine, 0.0, tx, -sine, cosine, 0.0, ty, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, ) } pub fn orthographic(left: f32, right: f32, top: f32, bottom: f32, near: f32, far: f32) -> Self { // note: depth is in range 0 to 1, not -1 to 1 like in OpenGL. let a = 2.0 / (right - left); let b = 2.0 / (top - bottom); let c = 1.0 / (far - near); let tx = (left + right) / (right - left); let ty = (top + bottom) / (top - bottom); let tz = near / (far - near); Self::new ( a, 0.0, 0.0, -tx, 0.0, b, 0.0, -ty, 0.0, 0.0, c, -tz, 0.0, 0.0, 0.0, 1.0, ) } } impl Mul for Transform { type Output = Transform; fn mul(self, rhs: Self) -> Self::Output { let r0 = [self.matrix[0], self.matrix[4], self.matrix[8], self.matrix[12]]; let r1 = [self.matrix[1], self.matrix[5], self.matrix[9], self.matrix[13]]; let r2 = [self.matrix[2], self.matrix[6], self.matrix[10], self.matrix[14]]; let r3 = [self.matrix[3], self.matrix[7], self.matrix[11], self.matrix[15]]; let c0 = [rhs.matrix[0], rhs.matrix[1], rhs.matrix[2], rhs.matrix[3]]; let c1 = [rhs.matrix[4], rhs.matrix[5], rhs.matrix[6], rhs.matrix[7]]; let c2 = [rhs.matrix[8], rhs.matrix[9], rhs.matrix[10], rhs.matrix[11]]; let c3 = [rhs.matrix[12], rhs.matrix[13], rhs.matrix[14], rhs.matrix[15]]; Self::new( dot(&r0, &c0), dot(&r0, &c1), dot(&r0, &c2), dot(&r0, &c3), dot(&r1, &c0), dot(&r1, &c1), dot(&r1, &c2), dot(&r1, &c3), dot(&r2, &c0), dot(&r2, &c1), dot(&r2, &c2), dot(&r2, &c3), dot(&r3, &c0), dot(&r3, &c1), dot(&r3, &c2), dot(&r3, &c3), ) } } fn dot(u: &[f32; 4], v: &[f32; 4]) -> f32 { u[0] * v[0] + u[1] * v[1] + u[2] * v[2] + u[3] * v[3] } 

Why does this extra translation exist in the working implementation when I already account for moving the origin in my TRS matrix? And, how can I correctly calculate this translation and apply it to my transform?

\$\endgroup\$

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.