TwixEmma's picture

Am I performing object translation and rotation correctly?

Hi, I've been trying to develop my own game engine with some good progress, and I decided that I would use OpenTK, for OpenGL and OpenAL specifically. I'm trying to get the rendering pipeline for my engine setup, and I am up to a point where I can load meshes into my engine, and render them, I am trying to make the renderer position and rotate the objects in the scene, but to no prevail. Below is my code, not much special at the moment, it sets up the perspective and modelview matrices. Draws all objects loaded into a render queue during update cycle, and I'm trying to use the GL.Rotate and GL.Translate methods to position and orient the objects as they're being drawn.

public static Queue<RenderDetails> renderQueue = new Queue<RenderDetails>();
        public struct RenderDetails
        {
            public Mesh mesh;
            public Vector3 position;
            public Vector4 rotation;    // I use Vector4 for simplicty at the moment, I will eventually change this to Quaternion, unless better advised of alternative methods
            public Vector3 scale;
 
            public RenderDetails(GameObject gameObject)
            {
//store the details of this GameObject object
                mesh = gameObject.mesh;
                position = gameObject.transform.position;
                rotation = gameObject.transform.rotation;
                scale = gameObject.transform.scale;
            }
        }
 
        private void Render()
        {
            if (!loaded || !running) { return; }
 
//setup camera details
            GL.ClearColor(Camera.main.backgroundColor);
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
            Matrix4 perspective = Matrix4.CreatePerspectiveFieldOfView(Camera.main.fieldOfView, Camera.main.aspect, Camera.main.nearClipPane, Camera.main.farClipPane);
            Matrix4 lookat = Matrix4.LookAt(100, 50, 0, 0, 0, 0, 0, 1, 0);
 
//load matrices
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadIdentity();
            GL.LoadMatrix(ref perspective);
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadIdentity();
            GL.LoadMatrix(ref lookat);
            GL.Viewport(0, 0, glControl1.Width, glControl1.Height);
            GL.Enable(EnableCap.DepthTest);
            GL.DepthFunc(DepthFunction.Less);
 
//process render queue
            while (renderQueue.Count > 0)
            {
                RenderDetails details = renderQueue.Dequeue();
                if (details.mesh != null && details.mesh.loaded)
                {
                    GL.Begin(PrimitiveType.Triangles);
//the following two lines don't seem to work for me :( I'm not sure why
                    GL.Rotate(details.rotation.W, details.rotation.X, details.rotation.Y, details.rotation.Z);
                    GL.Translate(details.position);
//draw the mesh
                    for (int i = 0; i < details.mesh.triangles.Length; i++)
                    {
                        Vector3 vertex = details.mesh.vertices[details.mesh.triangles[i]];
                        Color color = details.mesh.colors[details.mesh.triangles[i]];
                        GL.Color3(color);
                        GL.Vertex3(vertex.X, vertex.Y, vertex.Z);
                    }
                    GL.End();
                }
            }
 
            glControl1.SwapBuffers();
        }

Also just a further note, I would appreciate any tips, pointers, links to details; specifically for scaling the mesh at render time? or any methods typically used in this situation. Thanks in advance. If you have any questions about the code feel free to ask I will be obliged to assist in order to fix my problem.


Comments

Comment viewing options

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

Ok I kindof half solved this problem after positing this, but I have another problem along the same lines, the problem was that I was calling the GL.Rotate and GL.Translate methods AFTER the GL.Begin method, moving them to before that method, fixed the problem, my mesh is now rotating. The new problem is that it appears as though I'm rotating the world, rather than the object, aka world matrix translation as opposed to local/model matrix translation. My question now is how would I convert this to work locally?
Thanks in advance.

flopoloco's picture

Hello, I hacked a simple example in order to play and experiment with the concept of transformations.
It's self explanatory (I hope :) ) but if it won't cover your questions please refer again to analyse futher.

using System;
using OpenTK;
using OpenTK.Graphics.OpenGL;
 
namespace Test
{
	class MainClass
	{
		public static void Main (string[] args)
		{
			var win = new GameWindow ();
			var matProject = Matrix4.Identity;
			var matModel = Matrix4.Identity;
			var matQuad = Matrix4.Identity;
			var rotQuad = 0f;
 
			win.Load += (object sender, EventArgs e) => {
				GL.Enable(EnableCap.DepthTest);
 
				matProject = Matrix4.CreatePerspectiveFieldOfView((float)Math.PI / 4, (float)win.Width/win.Height, 0.1f, 100f);
				matModel = Matrix4.LookAt(new Vector3(0, 0, 10), Vector3.Zero, Vector3.UnitY);
				GL.MatrixMode(MatrixMode.Projection);
				GL.LoadMatrix(ref matProject);
			};
 
			win.RenderFrame += (object sender, FrameEventArgs e) => {
				GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
				GL.MatrixMode(MatrixMode.Modelview);
				// Normally you would allow this to happen if you use push and pop states.
				// But because in this case you will throw all the transformations into the
				// matrix of the quad shape then this is not needed in this case.
//				GL.LoadMatrix(ref matModel);
 
				// Rotate the quad based on render frame timing and then
				// grab the model matrix and use it to transform everything according to it.
				rotQuad += (float)e.Time;
				matQuad = matModel;
 
				// Example 1: Default order of matrix translations, this is the most common.
//				matQuad *= Matrix4.CreateRotationZ((float)rotQuad);
//				matQuad *= Matrix4.CreateTranslation((float)Math.Sin(rotQuad), 0f, 0f);
//				matQuad *= Matrix4.CreateScale(1, 1, 1);
 
				// Example 2: A skewing like effect
//				matQuad *= Matrix4.CreateRotationZ((float)rotQuad);
//				matQuad *= Matrix4.CreateScale(2, 1, 1);
 
				// Example 3: A rectangle rotated by it's corner.
				// This is very common for hierarchy based translations (bones, nodes, etc)
				matQuad *= Matrix4.CreateScale(2, 1, 1);
				matQuad *= Matrix4.CreateTranslation(2, 1, 0);
				matQuad *= Matrix4.CreateRotationZ(rotQuad);
 
 
				// If you want to use push states then you must use GL.LoadMatrix(ref matModel);
//				GL.PushMatrix();
				GL.LoadMatrix(ref matQuad);
				GL.Begin(PrimitiveType.Quads);
				GL.Vertex3(-1, -1, 0);
				GL.Vertex3( 1, -1, 0);
				GL.Vertex3( 1,  1, 0);
				GL.Vertex3(-1,  1, 0);
				GL.End();
//				GL.PopMatrix();
 
				win.SwapBuffers();
			};
 
			win.Run();
		}
	}
}
TwixEmma's picture

Ok thank you for your reply, it helped me get my translations working.. I'm still not quite sure if I'm doing things right though, it works, but I get a flicker, where the background color replaces the object and the object reappears. I don't know why :3... I have one question that might solve that though, is it possible to keep changing the background color? I'm currently lerping between two colors for the background as to test my engines lerping stuff and other good stuff. But is OpenGL capable of this kind of behaviour? Maybe I'm overthinking it, on the other hand I don't know why it's flickering so... some more code:

...
//this stuff is done in the constructor
            this.WindowBorder = OpenTK.WindowBorder.Hidden;
            this.WindowState = OpenTK.WindowState.Fullscreen;
            this.Load += new EventHandler<EventArgs>(GameGUI_Load);
            this.Resize += new EventHandler<EventArgs>(GameGUI_Resize);
            this.UpdateFrame += new EventHandler<FrameEventArgs>(GameGUI_UpdateFrame);
            this.RenderFrame += new EventHandler<FrameEventArgs>(GameGUI_RenderFrame);
            this.Run(UPDATE_INTERVAL);
...
        void GameGUI_RenderFrame(object sender, FrameEventArgs e)
        {
            Render();
        }
        void GameGUI_UpdateFrame(object sender, FrameEventArgs e)
        {
            if (this.Keyboard[Key.Escape])
            {
                this.Exit();
            }
            if (game.state == Game.State.Running)
            {
                GameUpdate();
                Time.BeginFrame();
                GameUpdate();
                Time.EndFrame();
                Time.DeltaSnapshot();
            }
        }
        void GameGUI_Resize(object sender, EventArgs e)
        {
            GL.Viewport(0, 0, this.Width, this.Height);
        }
        void GameGUI_Load(object sender, EventArgs e)
        {
            //...
            // irrelevant code has been excluded as to not bloat the reply
 
            Camera mainCamera = Camera.main;
            if (mainCamera == null) { mainCamera = new Camera(); mainCamera.enabled = true; }
 
            mainCamera.aspect = 4.0f / 1.0f;
            mainCamera.backgroundColor = Color.MediumAquamarine;
 
            mainCamera.fieldOfView = (float)Math.PI / 4.0f;
            mainCamera.farClipPane = 20000;
            mainCamera.nearClipPane = 1;
 
            mainCamera.position = Vector3.Zero;
            mainCamera.rotation = Vector4.Zero;
 
            this.VSync = VSyncMode.On;
 
            game = new Game();
            game.Run();
 
            loaded = true;
        }
        public static Queue<RenderDetails> renderQueue = new Queue<RenderDetails>();
        public struct RenderDetails
        {
            public Mesh mesh;
            public Vector3 position;
            public Quaternion rotation;
            public Vector3 scale;
 
            public RenderDetails(GameObject gameObject)
            {
//take mesh details
                mesh = gameObject.mesh;
                position = gameObject.transform.position;
                rotation = gameObject.transform.rotation;
                scale = gameObject.transform.scale;
            }
        }
        private void Render()
        {
            if (!loaded) { return; }
 
//setup camera
            GL.ClearColor(Camera.main.backgroundColor);
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
            Matrix4 perspective = Matrix4.CreatePerspectiveFieldOfView(Camera.main.fieldOfView, Camera.main.aspect, Camera.main.nearClipPane, Camera.main.farClipPane);
 
//setup matrices
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadIdentity();
            GL.LoadMatrix(ref perspective);
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadIdentity();
            GL.Viewport(0, 0, this.Width, this.Height);
            GL.Enable(EnableCap.DepthTest);
            GL.DepthFunc(DepthFunction.Less);
 
//process render queue
            while (renderQueue.Count > 0)
            {
                //get next mesh
                RenderDetails details = renderQueue.Dequeue();
                if (details.mesh != null && details.mesh.loaded)
                {
                    //the following matrix code now works :)
                    //but is this the correct way of doing this?
                    //each game object transform component has these three states stored, position, rotation and scale
                    //so it's technically dynamic, if I change the values, it changes this matrix result.
                    //also I'm just curious but how would I go about adding VBOs and VAOs to this instead of doing it this way?
                    //what would you guys suggest?
                    Matrix4 modelMatrix = Matrix4.CreateFromQuaternion(details.rotation);
                    modelMatrix *= Matrix4.CreateScale(details.scale);
                    modelMatrix *= Matrix4.CreateTranslation(details.position);
                    GL.LoadMatrix(ref modelMatrix);
                    //draw mesh
                    GL.Begin(PrimitiveType.Triangles);
                    for (int i = 0; i < details.mesh.triangles.Length; i++)
                    {
                        Vector3 vertex = details.mesh.vertices[details.mesh.triangles[i]];
                        Color color = details.mesh.colors[details.mesh.triangles[i]];
                        GL.Color3(color);
                        GL.Vertex3(vertex.X, vertex.Y, vertex.Z);
                    }
                    GL.End();
                }
            }
            //finite
            this.SwapBuffers();
        }

Thanks for your help, I appreciate it greatly, I'm just trying to get the basics going, I'm not a game engine programmer, I'm just a game programmer, and I'm trying to broaden my horizons. ;3

EDIT: I did a test, by taking out the code that constantly changes the background color, and it still flickers. I hope somebody knows why :(