0
votes

I am developing an analogue of OpenGL. So I have model, view and projection matrices.

Scaling and rotating work pefectly without translating. But when I apply translating to object and trying to rotate it again - it rotates around world origin.

I know that transformation operations should come in next order: scaling -> rotating -> translating. But I don't understand how to do it with only one matrix (model).

So, how can I do it in the right way? Should I use 3 matrices insted?

My work with matrices:

        for (int i = 0; i < vertices.Count; ++i)
        {
            float[,] vertexCoords = { { vertices[i].SX }, { vertices[i].SY }, { vertices[i].SZ }, { vertices[i].SW } };

            var modelViewMatrix = MatrixMultiplier.MultiplyMatrix(camera.ViewMatrix, shape.ModelMatrix);
            var eyeCoordinates = MatrixMultiplier.MultiplyMatrix(modelViewMatrix, vertexCoords);
            var clipCoordinates = MatrixMultiplier.MultiplyMatrix(camera.ProjectionMatrix, eyeCoordinates);


            var ndc = new float[,] {
                { clipCoordinates[0, 0] / clipCoordinates[3, 0] },
                { clipCoordinates[1, 0] / clipCoordinates[3, 0] },
                { clipCoordinates[2, 0] / clipCoordinates[3, 0] },
                { clipCoordinates[3, 0]}
            };

            var windowCoordinates = new float[,] { 
                { 640 / 2 * ndc[0, 0] + (640 / 2) },
                { 360 / 2 * ndc[1, 0] + (360 / 2) },
                { (50 - (-50)) / 2 * ndc[2, 0] + (50 + (-50)) / 2 },
                { ndc[3, 0]}
            };

            SetNewCoordinatesToPoint(vertices[i], windowCoordinates);             
        }

Rotation algorithm example:

private void RotateZ(MCommonPrimitive shape, double angle)
    {
        double rads = angle * Math.PI / 180.0;

        float[,] rotateZ = {
            { (float)Math.Cos(rads), -(float)Math.Sin(rads), 0, 0 },
            { (float)Math.Sin(rads), (float)Math.Cos(rads), 0, 0 },
            { 0, 0, 1, 0 },
            { 0, 0, 0, 1 }
        };

        shape.ModelMatrix = MatrixMultiplier.MultiplyMatrix(rotateZ, shape.ModelMatrix);
    }

MatrixMultiplier:

public static class MatrixMultiplier
{
    public static float[,] MultiplyMatrix(float[,] a, float[,] b)
    {
        float[,] c = null;

        if (a.GetLength(1) == b.GetLength(0))
        {
            c = new float[a.GetLength(0), b.GetLength(1)];
            for (int i = 0; i < c.GetLength(0); i++)
            {
                for (int j = 0; j < c.GetLength(1); j++)
                {
                    c[i, j] = 0;
                    for (int k = 0; k < a.GetLength(1); k++) // OR k<b.GetLength(0)
                        c[i, j] = c[i, j] + a[i, k] * b[k, j];
                }
            }
        }
        else
        {
            Console.WriteLine("\n Number of columns in First Matrix should be equal to Number of rows in Second Matrix.");
            Console.WriteLine("\n Please re-enter correct dimensions.");
            throw new ArithmeticException("Number of columns in First Matrix should be equal to Number of rows in Second Matrix");
        }

        return c;
    }
}
2
It's all a matter of order. See this answer.Nico Schertler
@NicoSchertler wow, this really helped me a lot. Now shape rotates around it's origin. Thank you!Dmitry
Feel free to write an answer to this question or delete it altogether then.Nico Schertler

2 Answers

2
votes

But when I apply translating to object and trying to rotate it again - it rotates around world origin.

Yes, that's how it works. You have to rotate the object first, then translate it to the desired position. Geometric transformations are not commutative, i.e. the order of operations matter.

0
votes

According to Nico Schertler's original answer it all depends on matrices multiplication order. So, for example, if I will use this code

shape.ModelMatrix = MatrixMultiplier.MultiplyMatrix(rotateZ, shape.ModelMatrix);

from my RotateZ method, shape will rotate around world origin. And, if I change my code to this:

shape.ModelMatrix = MatrixMultiplier.MultiplyMatrix(shape.ModelMatrix, rotateZ);

shape will rotate around it's center.