I have an OpenGL visualizer (using OpenTK). It's supposed to open a mesh file in the PLY format, and display it. The user is able to move around the object, rotate or scale it. At the end, the user should be able to save the mesh into a PLY file from the exact same view that he/she sees it before saving.
I've abstracted the mesh object into a class. Here is what the class (sort of) looks like:
public class Mesh {
Vector3d vertices[];
int[] triangleIndices;
Matrix4d translation = Matrix4d.Identity();
Matrix4d rotation = Matrix4d.Identity();
Matrix4d scale = Matrix4d.Identity();
int vboHandle;
int faceHandle;
public void Draw() {
GL.PushMatrix();
/**
* Apply transformations
*/
// Do VBO drawing stuff
// ...
GL.PopMatrix();
}
}
What I'm doing is loading the PLY file as a VBO. When the mesh is to be drawn, I apply the translation first, apply a rotation (around the center of the object by finding the object's centroid, translating the coordinate system, rotating and translating back) and then (in the same manner as the rotation) apply scaling.
This works fine and the visualizer draws the mesh correctly. However, when I want to save, I don't know how I can apply the same set of transformations to all vertices and write the PLY file.
I tried doing this: (pretty much mimicking what I have for the Draw
method above):
for(int i = 0 ; i <this.vertices.Length; i++)
{
// Apply the transformations
Vector3d transformed = Vector3d.Transform(this.vertices[i], this.translation);
// Handle rotation
Matrix4d centerTrans = Matrix4d.CreateTranslation(Center);
transformed = Vector3d.Transform(transformed, centerTrans);
transformed = Vector3d.Transform(transformed, this.rotation);
centerTrans = Matrix4d.CreateTranslation(-Center);
transformed = Vector3d.Transform(transformed, centerTrans);
// Handle scale
centerTrans = Matrix4d.CreateTranslation(Center);
transformed = Vector3d.Transform(transformed, centerTrans);
transformed = Vector3d.Transform(transformed, this.scale);
centerTrans = Matrix4d.CreateTranslation(-Center);
transformed = Vector3d.Transform(transformed, centerTrans);
result[i] = transformed;
}
But the problem is that the output is a little bit off (especially when a rotation is applied to the object).
My main question here is how I can fix this and what is the correct way of going about this problem.
PS. I know that my approach is probably archaic, childish and wrong with the whole mesh rendering thing (and considering OpenGL best practices), but I'm creating this visualizer to solve a completely unrelated and much bigger problem. So In a way I'm hacking things for my purposes. Any suggestions regarding a good approach for this problem are highly appreciated).
PS2. The reason for multiple Matrix4d
transforms is that I always want the rotation to be applied to the center of the object and I didn't know any other way to achieve the same effect.
One aspect to watch out for is the order of transformations. When you specify transformations using the legacy OpenGL matrix stack, they are applied to the vertices in reverse order of the order they are specified in.
For example, if you have this sequence:
glTranslatef(...);
glRotatef(...);
This means that vertices are first rotated, then translated.
While this behavior may seem counterintuitive at first, it's actually much more useful than it would be the opposite way. For example, if you use a view matrix, you specify it at the start of the frame. Then you specify model transformations while drawing your objects. But the view transformation needs to be applied after the model transformation, even though it was specified first.
Similarly, if you have a hierarchy of objects, you often want to specify "global" transformations that apply to all objects, and then "local" transformations while you're drawing objects deeper in the hierarchy. But again, the local transformations need to be applied before the global transformations.
Based on this, if you apply transformations one by one in your own code, you will have to apply them in an order opposite from the order you used when you specified them for your OpenGL rendering.
There are a few possible alternatives to what you currently use:
You can get the current transformation matrix, and then apply it to your vertices:
GLfloat mat[16];
glGetFloatv(GL_MODELVIEW_MATRIX, mat);
This will only work well if used while you render a frame. If you want to apply the transformations outside of rendering, your approach is more viable. Also, it gets you deeper into using the legacy matrix stack, which means that you have more work if you ever want to carry your code into the OpenGL Core Profile, or port it to OpenGL ES.
As suggested by @Ike in a comment above, you can change your code to first combine the matrices, and then apply the combined matrix to your vertices. This is more efficient if you have a lot of vertices that use the same sequence of transformations.
OpenGL has a feature called "transform feedback" that allows you to get transformed coordinates back out of the graphics pipeline. I don't think it's ideally suited for your use case, but you may want to read up on it anyway.