casanovaC4's picture

Permanent Rotation Help . . . . ..

Hy,

I am using OpenTK in C# to create my application.
I want to rotate the 3D objects not by rotating the modelview matrix but by manipulating the points of the 3D objects.
I cant figure out how to.
The Matrix4 Rotate function in OpenTK.Math is not working for me, whereas GL.Rotate rotates the current matrix.
Tell me how i can rotate the coords of the 3D object itself, what function or class should i use in OpenTK, does OpenTK even provides any such functionality?
Will be greatly thankful.

Regards.


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

Matrix4.Rotate returns a *new* rotation matrix that you can apply to the points you wish to rotate.

var points = new Vector3[] { ... };
var rotation = Matrix4. CreateFromAxisAngle(...); // Supersedes 'Rotate'
 
for (int i = 0; i < points.Length; i++)
{
    Vector3 point = points[i];
    Vector4 result;
    // Yes, this method should return a Vector3 rather than Vector4.
    Vector3.Transform(ref point, ref rotation, out result);
    points[i] = result.Xyz;
}

Edit: note that you should only ever do this if you wish to deform your 3d object permanently. The OpenGL matrix stack (and the newer shader hardware) is several orders of magnitude faster than the code above.

Reading your post on GD.net, it seems you are trying to do this as a kind of optimization. This approach is wrong! The GPU will transform vertices anyway, even if you use the identity matrix. You are essentially moving computations from the GPU (where they come for "free") to the CPU (where they will completely kill performance).

casanovaC4's picture

What i want is that suppose a 3D object's been collided with another any other 3D object.
So after collision the object will definitely acquire new position in the 3D world.
I intend to do the same thing, manipulate that particular collided object instead of the whole 3D world(modelview matrix).
That's why i want to rotate or translate the 3D objects and keep them that way.
My approach may be wrong but i want the required functionality.
If you understood what I want to do then please tell me someway i can do this, even if i have to use the same LoadIdentity() and related functions then it's ok just teach me how to do it, i just want to do what i want to do.

Regards.

the Fiddler's picture

Conceptually, every object in the world has an associated 'modelview' matrix that defines its position and rotation. (This can be recursive: if object A is composed by three parts, each of those parts will have each own a modelview matrix.)

To render your objects, you can use the following pseudocode:

// 1. Setup projection and world matrix.
// 2. For each object
// 2a. Save the current world matrix
// 2b. Multiply the world matrix by the object's modelview matrix
// 2c. Render the object
// 2d. Restore the world matrix.
 
// Assume you have implemented step 1 already.
// Step 2 will look similar to this:
GL.MatrixMode(MatrixMode.Modelview);
 
foreach (var obj in world)
{
    GL.PushMatrix(); // Save the current matrix
 
    // Use GL.Translate/Rotate/MultMatrix/LoadMatrix
    // to set the position of this object
    GL.Translate(obj.Position);
    GL.Rotate(obj.Rotation);
 
    // Render the object using any method you'd like
 
    GL.PopMatrix(); // Restore the world matrix
}

Hope this helps. Don't hesitate to ask if you have any questions!

casanovaC4's picture

Thank you Fiddler, your solution solved my problem.
I have another question to ask.
3D objects in my application have two methods, Draw() and Update().
Update method affects the points of the object like translating or rotating it, where as Draw method simply draws the updated points.
Now i want the GL.Translate() and GL.Rotate() to do their job in the Update section and the GL.Begin() and GL.End() in the Draw section.
Update is called before the Draw.
The code in the Update section doesnot work.
Here is the Update():

public void Update()
{
            GL.MatrixMode(MatrixMode.Modelview);           //Set matrix mode
            GL.Translate(0, 0, -5);                                                   //Translate the matrix
            GL.Rotate(45, 0, 1, 0);                                                   //Rotate the matrix
            GL.PushMatrix();                                                            //And push it into the stack.
}

Here is the Draw():

public override void Draw(Graphics e)
{
            GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
            GL.PopMatrix();                                                               //Pop the pushed matrix and draw
 
            GL.Begin(BeginMode.Quads);
 
            for (int i = 0; i < side.Length;i++ )
            {
                for (int j = 0; j < side[i].Point.Length;j++ )
                {
                    GL.Vertex3(side[i].Point[j].X, side[i].Point[j].Y, side[i].Point[j].Z);
                }
           }
 
            GL.End();
}

Now the problem is:
Code in the Update() does not affect anything.
If it somehow did work then Popping the pushed matrix will waste it after execution.

The solution i need is :
1) to keep the Update() and Draw() approach.
2)make the code in Update section work.
3) somehow save the matrix updated for drawing.

If you think my approach is not right please do let me know and please suggest any other approach too.

I am sorry if i am irritating you or anything but your solutions really work for me.
Looking forward for this problem's solution as well.

Thanks,
Regards.

the Fiddler's picture

Instead of relying on the OpenGL matrix stack, I would simply calculate the modelview matrix in the Update method, store it, and apply it in the Draw method. In other words:

Matrix4 modevliew = Matrix4.Identity;
 
public void Update()
{
    GL.MatrixMode(MatrixMode.Modelview);
    GL.PushMatrix();
 
    GL.Translate(0, 0, -5);
    GL.Rotate(45, 0, 1, 0);
    GL.GetFloat(GetPName.ModelviewMatrix, out modelview);
 
    GL.PopMatrix();
}
 
public void Draw()
{
            GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
 
            // Load the matrix we calculated in the Update method
            GL.MatrixMode(MatrixMode.Modelview);
            GL.PushMatrix();-
            GL.LoadMatrix(ref modelview);
 
            GL.Begin(BeginMode.Quads);
 
            for (int i = 0; i < side.Length;i++ )
            {
                for (int j = 0; j < side[i].Point.Length;j++ )
                {
                    GL.Vertex3(side[i].Point[j].X, side[i].Point[j].Y, side[i].Point[j].Z);
                }
           }
 
            GL.End();
 
            GL.PopMatrix();
}

This way, you decouple the Update method from the draw method.

I am a little worried about the cost of calling the GL functions to calculate the matrix. The OpenTK equivalent might be faster, but you'll have to benchmark this...

// OpenTK.Matrix4 equivalent of the GL.Translate/Rotate methods:
Matrix4 modelview = Matrix4.Identity;
 
public void Update()
{
    Matrix4 translation, rotation;
    // Note: this is not exactly equivalent to the above method, since
    // we are disregarding the current modelview matrix. To get identical results,
    // you can use GL.GetFloat to get the current matrix and multiply it with 'modelview'
    Matrix4.CreateTranslation(0, 0, -5, out translation);
    Matrix4.CreateFromAxisAngle(Vector3.UnitY, MathHelper.PiOver2, out rotation);
    Matrix4.Mult(ref translation, ref rotation, out modelview);
}
 
// Draw method is the same.
casanovaC4's picture

Thank you again for solving my problem.
I now know how to get a modelview matrix and store it.
But i have another problem and i think i might already know whats wrong.
I told you the code in the Update() does not work and then i figured that the Update() and Draw() are called from a different threads(due to my application design).
Means i setup all the GLControl properties and run the Draw() in another thread ant try to update them(Update()) from another thread, maybe thats the reason the code in Update() doesnot work.
So my question is that does OpenGL runs differently on different threads? that is one thread settings does not affect other threads settings.

Regards.

the Fiddler's picture

To use OpenGL from multiple threads, you have to construct a different OpenGL context for each thread. This is complex and thus not recommended. (Read the documentation on the topic.)

The best solution is to limit your GL calls to the main thread and modify your Update() method to rely on OpenTK.Matrix4 (code above).

Finally, try building against the debug version of OpenTK. It will catch and warn you about threading issues (and various other potential OpenGL errors).

casanovaC4's picture

I decided to use Matrix4.Rotate and Matrix4.CreateTranslation because of the thread issues.

CreateTranslation works fine whereas Matrix4.Rotate works too but not how i need it too.
When rotation is executed after translation the 3D object starts rotating around the screen, that is going behind from left and reappearing from right in a circular manner(yaw rotation).

But when i use GL.Rotate(), to check if there is something wrong with the translated matrix or not, it rotates properly, that is around itself where it is placed.

I even used Matrix4.Rotate() on single thread to make sure threads dont mess with it but still i get the same around-the-screen rotation.
Is Matrix4.Rotate() rotating matrix as it should be or it needs some other settings.
here is the code for 3D Object's translation and rotation:

public void Translate(float x, float y, float z)
{
            myCurrentModelViewMatrix *= Matrix4.CreateTranslation(x, y, z);
}
 
public void Rotate(float angle, int xAxis, int yAxis, int zAxis)
{
            myCurrentModelViewMatrix *= Matrix4.Rotate(new Vector3(xAxis, yAxis, zAxis), Functions.DegreesToRadians(angle));     //I have OpenTK 0.9.8.3
}

I even checked OpenTK 0.9.9.2b and used CreateAxisFromAngle() just to make sure the rotation is done properly but same rotation is performed.

I am thinking about using same old each-point-rotation but it will waste GPU power as you said.

Help me through this, please.

Kind Regards.

the Fiddler's picture

You need to swap the order of the translation and the rotation.

For example, if you do:

Translate(...);
Rotate(...);

change that to:

Rotate(...);
Translate(...);

The reason is that matrix multiplication is not commutative (A * B != B * A) and the order in which you apply the matrices is significant. (Both orders are correct - which one you use depends on the specific movement you wish to achieve).

Edit: GD.net has a good tutorial on matrices.

casanovaC4's picture

You did not understand what i am trying to do here, here it goes in detail . . . ..

I have now removed the Update() and replaced it with Rotate() and Translate()'s, as mentioned above.

When the 3D Object is initialized it's modelview matrix is translated to a certain position e.g, (0, 0, -5).
From now on forward it is continuously rotated with Rotate().
Here starts the problem when i use the translated matrix with this Rotate(), object rotates around the screen, but, when i use GL.Rotate(), it rotates around itself at fixed position.

So my question is why Matrix4.Rotate() is doing around-the-screen rotation with the Objects matrix while GL.Rotate() is rotating the very same matrix just fine?