Sign in to follow this  
godecho

OpenGL issue with manual 2d rotation

Recommended Posts

Background: This is a distillation of a larger project. The code to follow should be sufficient to show the error even if it doesn't 100% reflect the organization of the original code. Objective: Have a 2d triangle rotate in place without the use of glRotatef(). Problem: Continued rotations on the points of a triangle eventually fall victim to the rounding error of sin and cos. The triangle drawn on the right side of the screen provides an example of the rotation working correctly, but the triangle shrinks as a result of the aforementioned error magnification. I have specific reasons for not wanting to use the double version of sin and cos. I attempt to remedy this by copying the original triangle data into a temporary triangle and rotating the temporary triangle by an increasingly larger angle. This triangle is drawn to the left side of the screen. Using this method, the error is never allowed to accumulate like it is in the triangle drawn to the right. Indeed, the left triangle does not shrink, but the resulting transformation makes the triangle look distorted for reasons unknown. This is the issue I am trying to solve. In the following code, I draw both triangles I just described. The left triangle is the one I'm interested in, and the one to the right is a reference.
#ifdef _WIN32
	#pragma comment(lib, "glu32.lib")
	#pragma comment(lib, "opengl32.lib")
	#pragma comment(lib, "SDL.lib")
	#pragma comment(lib, "SDLmain.lib")	

	#define WIN32_LEAN_AND_MEAN 1
	#include <windows.h>
#endif

#include <GL/gl.h>
#include <GL/glu.h>
#include "SDL.h"

#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstddef>
#include <iostream>

const float PI = 3.1415926535897932384626433832795f;

struct Point2f
{
	float x;
	float y;
};

struct Triangle
{
	Point2f points[3];
	
	Triangle operator = (const Triangle &rhs)
	{
		std::copy(rhs.points, rhs.points+3, points);
		return (*this);
	}

	void translate(float x, float y)
	{
		points[0].x += x;
		points[0].y += y;

		points[1].x += x;
		points[1].y += y;

		points[2].x += x;
		points[2].y += y;
	}

	void rotate(float radianAngle)
	{
		// x' = x*cos(theta) + y*sin(theta)
		// y' = y*cos(theta) - x*sin(theta)
		
		float cos_theta = cos(radianAngle);
		float sin_theta = sin(radianAngle);

		points[0].x = (points[0].x * cos_theta) + (points[0].y * sin_theta);
		points[0].y = (points[0].y * cos_theta) - (points[0].x * sin_theta);

		points[1].x = (points[1].x * cos_theta) + (points[1].y * sin_theta);
		points[1].y = (points[1].y * cos_theta) - (points[1].x * sin_theta);

		points[2].x = (points[2].x * cos_theta) + (points[2].y * sin_theta);
		points[2].y = (points[2].y * cos_theta) - (points[2].x * sin_theta);
	}
	
	std::ostream& print(std::ostream &os)	const
	{
		os	<< '(' << points[0].x << ", " << points[0].y << ") "
			<< '(' << points[1].x << ", " << points[1].y << ") "
			<< '(' << points[2].x << ", " << points[2].y << ") ";
		
		return os;
	}
};

std::ostream& operator << (std::ostream &os, const Triangle &tri)
{
	return tri.print(os);
}


const GLuint	g_width		= 800;
const GLuint	g_height	= 800;
const GLuint	g_bpp		= 16;
const GLfloat	g_fov		= 45.0f;
const GLfloat	g_nClip		= 1.0f;
const GLfloat	g_fClip		= 500.0f;

Triangle g_originalTriangle;
Triangle g_transformedTriangle;
Triangle g_selftransform;

Point2f g_center1;
Point2f g_center2;

float g_angle = 0.0f;

const GLfloat g_colors[] = {	1.0f, 0.0f, 0.0f,
								0.0f, 1.0f, 0.0f,
								0.0f, 0.0f, 1.0f	};

bool Init();
bool CheckInput();
void Draw();
void SetOrthoProj();
void ResetPersProj();


int main(int argc, char** argv)
{
	if(Init() == false)
	{
		SDL_Quit();
		std::cerr << "Initialization has failed, exiting." << std::endl;
		return -1;
	}

	GLenum error;

	while(CheckInput() == true)
	{
		Draw();
		
		error = glGetError();
		if(error != GL_NO_ERROR)
		{
			std::cerr << "* OpenGL Error: " << gluErrorString(error) << std::endl;
		}

		SDL_GL_SwapBuffers();
	}

	SDL_Quit();

	return 0;
}

bool Init()
{
	if(SDL_InitSubSystem(SDL_INIT_VIDEO) == -1)
	{
		std::cerr << "InitSubSystem() failed. SDL Error: " << SDL_GetError() << std::endl;
		return false;
	}

	if(SDL_SetVideoMode(g_width, g_height, g_bpp, SDL_OPENGL) == 0)
	{
		std::cerr << "setVideoMode() Failed. SDL_Error: " << SDL_GetError() << std::endl;
		return false;
	}
	
	glViewport(0, 0, g_width, g_height);
	glMatrixMode(GL_PROJECTION);
	gluPerspective(	g_fov,
					float(g_width)/float(g_height),
					g_nClip,
					g_fClip);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glShadeModel(GL_SMOOTH);	// Select Smooth Shading
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	
	glFrontFace(GL_CCW);
	glCullFace(GL_BACK);

	// set up some of our globals

	g_originalTriangle.points[0].x = 100;
	g_originalTriangle.points[0].y = 300;
	
	g_originalTriangle.points[1].x = 300;
	g_originalTriangle.points[1].y = 300;
	
	g_originalTriangle.points[2].x = 200;
	g_originalTriangle.points[2].y = 500;

	g_center1.x = ((g_originalTriangle.points[0].x + g_originalTriangle.points[1].x + g_originalTriangle.points[2].x) / 3.0f);
	g_center1.y = ((g_originalTriangle.points[0].y + g_originalTriangle.points[1].y + g_originalTriangle.points[2].y) / 3.0f);

	g_selftransform.points[0].x = 500;
	g_selftransform.points[0].y = 300;
				   
	g_selftransform.points[1].x = 700;
	g_selftransform.points[1].y = 300;
				   
	g_selftransform.points[2].x = 600;
	g_selftransform.points[2].y = 500;

	g_center2.x = ((g_selftransform.points[0].x + g_selftransform.points[1].x + g_selftransform.points[2].x) / 3.0f);
	g_center2.y = ((g_selftransform.points[0].y + g_selftransform.points[1].y + g_selftransform.points[2].y) / 3.0f);

	return true;
}

void Draw()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	SetOrthoProj();

	glEnableClientState(GL_COLOR_ARRAY);
	glEnableClientState(GL_VERTEX_ARRAY);
	
	glColorPointer(3, GL_FLOAT, 0, g_colors);

	// example triangle 1
	g_transformedTriangle = g_originalTriangle;

	g_angle += 0.01f;

	g_transformedTriangle.translate(-g_center1.x, -g_center1.y);
	g_transformedTriangle.rotate(g_angle);
	g_transformedTriangle.translate( g_center1.x,  g_center1.y);
	
	glVertexPointer(2, GL_FLOAT, 0, g_transformedTriangle.points);

	glDrawArrays(GL_TRIANGLES, 0, 3);

	// example triangle 2
	g_selftransform.translate(-g_center2.x, -g_center2.y);
	g_selftransform.rotate(0.1f);
	g_selftransform.translate( g_center2.x,  g_center2.y);
	
	glVertexPointer(2, GL_FLOAT, 0, g_selftransform.points);

	glDrawArrays(GL_TRIANGLES, 0, 3);

	glDisableClientState(GL_COLOR_ARRAY);
	glDisableClientState(GL_VERTEX_ARRAY);

	ResetPersProj();
}

bool CheckInput()
{
	static SDL_Event event;
	while(SDL_PollEvent(&event))
	{
		if((event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) || event.type == SDL_QUIT)
		{
			return false;
		}
	}
	return true;
}

void SetOrthoProj()
{
	glPushAttrib(GL_DEPTH_TEST);
	glDisable(GL_DEPTH_TEST);	// Turn depth testing off

	glMatrixMode(GL_PROJECTION);	// Switch to projection mode
	glPushMatrix();			// Save previous matrix which contains the settings for the perspective projection
	glLoadIdentity();

	glOrtho(0, g_width, 0, g_height, -1, 1);
	glMatrixMode(GL_MODELVIEW);
}

void ResetPersProj()
{
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();			// Pop the stack
	glMatrixMode(GL_MODELVIEW);	// Put us back in modelview mode                                            

	glEnable(GL_DEPTH_TEST);	// Turn depth testing back on
	glPopAttrib();
}



edit: formatting

Share this post


Link to post
Share on other sites
*argh* stupid error

the rotation wasnt saving the original X value before it was operated on to calculate the Y component. it should look something like:

float cos_theta = cos(radianAngle);
float sin_theta = sin(radianAngle);
float x;

x = points[0].x;
points[0].x = (points[0].x * cos_theta) + (points[0].y * sin_theta);
points[0].y = (points[0].y * cos_theta) - (x * sin_theta);

x = points[1].x;
points[1].x = (points[1].x * cos_theta) + (points[1].y * sin_theta);
points[1].y = (points[1].y * cos_theta) - (x * sin_theta);

x = points[2].x;
points[2].x = (points[2].x * cos_theta) + (points[2].y * sin_theta);
points[2].y = (points[2].y * cos_theta) - (x * sin_theta);

mystery solved: I'm an idiot :)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this