1
\$\begingroup\$

In an effort to learn things deeper I'm writing my own mathematics for the first time instead of using libraries. As far as I can tell my matrix multiplication is correct, and translation and scaling work fine. I am however, struggling to debug the rotations. I've tried pretty much everything I can think to check and it still won't function as expected. If I set the angle to zero then as expected the Model matrix will not be effected. Changing to 1.0f will explode, the only value which results in expected behaviours seems to be zero... I haven't written a view matrix yet and I've been trying to debug this with the projection(orthographic) matrix on and off. Below is code with some notes.

Matrices are linear arrays of 16 values. i32 is an int, f32 is float, internal is static.

inline m4x4 operator*(m4x4 a, m4x4 b){
  //TODO redo this with simd 
  m4x4 Result = {};

  for (i32 Row = 0; Row <= 3; ++Row)
  {
    for (i32 Col = 0; Col <= 3; ++Col)
    {
      for (i32 n = 0; n <= 3; ++n)
      {
        Result.c[(Row * 4) + Col] += a.c[(Row * 4) + n] * b.c[4 * n + Col];
      }
    }
  }

  return (Result); }

inline m4x4 GetRotationZ(f32 angle) {
  m4x4 Result;
  f32 c = Cos(angle);
  f32 s = Sin(angle);

  Result =  { c, s, 0, 0,
         -s, c, 0, 0,
         0, 0, 1, 0,
         0, 0, 0, 1 };

    return (Result); }

inline m4x4 RotateZ(m4x4 m, f32 angle) {
  m4x4 Rotation = GetRotationZ(angle);
  m4x4 Result = Rotation * m;

  return (Result); }

internal void DrawSprite(sprite Sprite, v2 Position, v2 Size, f32 RotationAngle, v3 Colour) {
  glUseProgram(Sprite.ShaderProgram);
  glBindVertexArray(Sprite.VAO);

#if 0
  Position = v2{400, 300};
  Size = v2{100.0f, 100.0f};
  RotationAngle = 0.0f;
#endif

#if 0
  m4x4 Projection = Orthographic(0.0f, 800.0f, 0.0f, 600.0f, -1.0f, 1.0f);
  u32 ProjectionUniformID = glGetUniformLocation(GlobalSprite.ShaderProgram, "Projection");
  glUniformMatrix4fv(ProjectionUniformID, 1, GL_TRUE, &Projection.c[0]);
#endif 

  m4x4 Model = IdentityMatrix();
  Position.x = Position.x * 0.01f;
  Position.y = Position.y * 0.01f;
  Model = Translate(Model, V3(Position, 0.0f));

  Model = Translate(Model, v3{0.5f * Size.x, 0.5f * Size.y, 0.0f});
  //Model = RotateZ(Model, RotationAngle);
  Model = RotateZ(Model, 45.0f);
  Model = Translate(Model, v3{-0.5f * Size.x, -0.5f * Size.y, 0.0f});
  Size = v2{0.25, 0.25};
  Model = Scale(Model, V3(Size, 1.0f));

  u32 ModelUniformID = glGetUniformLocation(Sprite.ShaderProgram, "Model");
  glUniformMatrix4fv(ModelUniformID, 1, GL_TRUE, &Model.c[0]);
  glDrawArrays(GL_TRIANGLES, 0, 6); }

Next the shader source

char *VertexShaderSource = R"(
  #version 330 core
  layout (location = 0) in vec4 PositionAndTextureCoords;
  uniform mat4 Model;
  uniform mat4 Projection;

  out vec2 TexCoords;
  void main()
  {
     TexCoords = vec2(PositionAndTextureCoords.zw);
     //gl_Position = vec4(PositionAndTextureCoords.xy, 0.0f, 1.0f);
     //gl_Position = Model * vec4(PositionAndTextureCoords.xy, 0.0f, 1.0f);
     //gl_Position = Projection * Model * vec4(PositionAndTextureCoords.xy, 0.0f, 1.0f);
     gl_Position = Model * vec4(PositionAndTextureCoords.xy, 0.0f, 1.0f);
  }
)";

char *FragmentShaderSource = R"(
   #version 330 core
   in vec2 TexCoords; 
   uniform sampler2D Texture;

   out vec4 FragColour; 
   void main() 
   { 
     FragColour = texture(Texture, TexCoords);
   }
)";

I've stepped through the debugger, I've checked every line of code again and again, I've worked through a few matrices by hand and still I don't know whats going wrong. It should be something so very trivial yet I can't seem to find the bug...

Below are pictures demonstrating the difference between 0.0f and 1.0f, as of this state of the code it not only skews and rotates incorrectly, but its scaling is well off as well.

when the angle is set to 0.0f when the angle is set to 1.0f Debugger showing the values in memory before rotz Debugger showing the values in memory after rotz

\$\endgroup\$
  • 1
    \$\begingroup\$ Can you show an image of the difference between an angle of 0° and 1°. It's not clear what "it explodes" means in this context. Do you mean the angle is too great? (If so are you sure you're using degrees and radians properly in all cases?) Or do you mean it crashes? Or something else? \$\endgroup\$ – user1118321 May 31 at 19:13
  • \$\begingroup\$ The results have varied as I've been changing and tweaking to debug but as of the state of the code I've pasted here it appears I can demonstrate the difference. The results are more dramatic than when I started trying to debug, changing the scale dramatically. I'll upload pictures of 0.0f and 1.0f \$\endgroup\$ – frustratedLoser May 31 at 19:22
  • 1
    \$\begingroup\$ As already pointed out, you don't seem to be converting your angles from Degrees to Radians; did I miss that? \$\endgroup\$ – Vaillancourt May 31 at 19:30
  • \$\begingroup\$ You're right about that, I forgot I deleted that because it didn't seem to work. I did try with that and the rotations were still incorrect, I'll put them back and update further. First I'll upload the last couple of screenshots. Before I was doing the conversion from degrees to radians in the sin function itself \$\endgroup\$ – frustratedLoser May 31 at 19:32
  • 1
    \$\begingroup\$ Try scaling, rotating and translating separately (only rotation, then only scaling, then only translation). If those work by themselves, then the problem is with the order of the multiplications. Scaling, then rotating isn't the same as rotating, then scaling \$\endgroup\$ – Bálint May 31 at 19:56
1
\$\begingroup\$

Your multiplication order is incorrect. If you look at the function calls, it will be in the

$$scale * rotation * translation * identity$$

This is incorrect for the way you set your multiplications up, it should be in the exact opposite order:

$$identity * translation * rotation * scale$$

This is because inside your Translate, RotateZ and Scale functions, you do the multiplication in the wrong order. Replace

m4x4 Result = Rotation * m;

with

m4x4 Result = m * Rotation;

With the respective values for every one of these functions.

\$\endgroup\$
  • \$\begingroup\$ Your answer is close but not quite right, I didn't show the scale or translate functions because I didn't think there was anything wrong with them. The real problem was in the scale function which I had overlooked, the rest of the errors in the code I introduced while trying to debug (including the wrong order or multiplication here) when resorting to trial and error. Your answer was close enough though, but the main lessons here being not to overlook anything and not to assume the problem is where all the complexity is. That mistake cost me a few hours and some embarrassment! Thanks : ) \$\endgroup\$ – frustratedLoser May 31 at 21:22
  • \$\begingroup\$ @frustratedLoser it might be worthwhile to add your own answer here explaining the extra details you found that you needed to fix, and how you fixed them. It's totally OK to answer your own questions here, and it doesn't detract from existing answers (folks can upvote both if they both share different, useful info!) \$\endgroup\$ – DMGregory May 31 at 22:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.