gluDisk rotation for mapping - c++

I'm trying to create sub-cursor for terrain mapping.
Basic by code: (old image, but rotation is same)
image http://www.sdilej.eu/pics/274a90360f9c46e2eaf94e095e0b6223.png
This is when i testing change glRotate ax to my numbers:
image2 http://www.sdilej.eu/pics/146bda9dc51708da54b9249706f874fc.png
What i want:
image3 http://www.sdilej.eu/pics/69721aa237608b423b635945d430e561.png
My code:
void renderDisk(float x1, float y1, float z1, float x2, float y2, float z2, float radius, int subdivisions, GLUquadricObj* quadric)
{
float vx = x2 - x1;
float vy = y2 - y1;
float vz = z2 - z1;
//handle the degenerate case of z1 == z2 with an approximation
if( vz == 0.0f )
vz = .0001f;
float v = sqrt( vx*vx + vy*vy + vz*vz );
float ax = 57.2957795f * acos( vz/v );
if(vz < 0.0f)
ax = -ax;
float rx = -vy * vz;
float ry = vx * vz;
glPushMatrix();
glTranslatef(x1, y1, z1);
glRotatef(ax, rx, ry, 0.0);
gluQuadricOrientation(quadric, GLU_OUTSIDE);
gluDisk(quadric, radius - 0.25, radius + 5.0, subdivisions, 5);
glPopMatrix();
}
void renderDisk_convenient(float x, float y, float z, float radius, int subdivisions)
{
// Mouse opacity
glColor4f( 0.0f, 7.5f, 0.0f, 0.5f );
GLUquadricObj* quadric = gluNewQuadric();
gluQuadricDrawStyle(quadric, GLU_LINE);
gluQuadricNormals(quadric, GLU_SMOOTH);
gluQuadricTexture(quadric, GL_TRUE);
renderDisk(x, y, z, x, y, z, radius, subdivisions, quadric);
gluDeleteQuadric(quadric);
}
renderDisk_convenient(posX, posY, posZ, radius, 20);

This is a simple one. In your call to renderDisk() you supply bad arguments. Looks like you copied the function from some tutorial without understanding how it works. The first three parameters control the center position, and the other three parameters control rotation using a second position which the disk is always facing. If the two positions are equal (which is your case), this line is executed:
//handle the degenerate case of z1 == z2 with an approximation
if( vz == 0.0f )
vz = .0001f;
And setting z to nonzero makes the disc perpendicular to XZ plane, which is also the horizontal plane for your terrain. So ... to make it okay, you need to modify your function like this:
void renderDisk_convenient(float x, float y, float z, float radius, int subdivisions)
{
// Mouse opacity
glColor4f( 0.0f, 7.5f, 0.0f, 0.5f );
GLUquadricObj* quadric = gluNewQuadric();
gluQuadricDrawStyle(quadric, GLU_LINE);
gluQuadricNormals(quadric, GLU_SMOOTH);
gluQuadricTexture(quadric, GL_TRUE);
float upX = 0, upY = 1, upZ = 0; // up vector (does not need to be normalized)
renderDisk(x, y, z, x + upX, y + upY, z + upZ, radius, subdivisions, quadric);
gluDeleteQuadric(quadric);
}
This should turn the disc into the xz plane so it will be okay if the terrain is flat. But in other places, you actually need to modify the normal direction (the (upX, upY, upZ) vector). If your terrain is generated from a heightmap, then the normal can be calculated using code such as this:
const char *p_s_heightmap16 = "ps_height_1k.png";
const float f_terrain_height = 50; // terrain is 50 units high
const float f_terrain_scale = 1000; // the longer edge of terrain is 1000 units long
TBmp *p_heightmap;
if(!(p_heightmap = p_LoadHeightmap_HiLo(p_s_heightmap16))) {
fprintf(stderr, "error: failed to load heightmap (%s)\n", p_s_heightmap16);
return false;
}
// load heightmap
TBmp *p_normalmap = TBmp::p_Alloc(p_heightmap->n_width, p_heightmap->n_height);
// alloc normalmap
const float f_width_scale = f_terrain_scale / max(p_heightmap->n_width, p_heightmap->n_height);
// calculate the scaling factor
for(int y = 0, hl = p_normalmap->n_height, hh = p_heightmap->n_height; y < hl; ++ y) {
for(int x = 0, wl = p_normalmap->n_width, wh = p_heightmap->n_width; x < wl; ++ x) {
Vector3f v_normal(0, 0, 0);
{
Vector3f v_pos[9];
for(int yy = -1; yy < 2; ++ yy) {
for(int xx = -1; xx < 2; ++ xx) {
int sx = xx + x;
int sy = yy + y;
float f_height;
if(sx >= 0 && sy >= 0 && sx < wh && sy < hh)
f_height = ((const uint16_t*)p_heightmap->p_buffer)[sx + sy * wh] / 65535.0f * f_terrain_height;
else
f_height = 0;
v_pos[(xx + 1) + 3 * (yy + 1)] = Vector3f(xx * f_width_scale, f_height, yy * f_width_scale);
}
}
// read nine-neighbourhood
/*
0 1 2
+----------+----------+
|\ | /|
| \ | / |
| \ | / |
| \ | / |
3|_________\|/_________|5
| 4/|\ |
| / | \ |
| / | \ |
| / | \ |
|/ | \|
+----------+----------+
6 7 8
*/
const int p_indices[] = {
0, 1, //4,
1, 2, //4,
2, 5, //4,
5, 8, //4,
8, 7, //4,
7, 6, //4,
6, 3, //4,
3, 0 //, 4
};
for(int i = 0; i < 8; ++ i) {
Vector3f a = v_pos[p_indices[i * 2]];
Vector3f b = v_pos[p_indices[i * 2 + 1]];
Vector3f c = v_pos[4];
// triangle
Vector3f v_tri_normal = (a - c).v_Cross(b - c);
v_tri_normal.Normalize();
// calculate normals
v_normal += v_tri_normal;
}
v_normal.Normalize();
}
// calculate normal from the heightmap (by averaging the normals of eight triangles that share the current point)
uint32_t n_normalmap =
0xff000000U |
(max(0, min(255, int(v_normal.z * 127 + 128))) << 16) |
(max(0, min(255, int(v_normal.y * 127 + 128))) << 8) |
max(0, min(255, int(-v_normal.x * 127 + 128)));
// calculate normalmap color
p_normalmap->p_buffer[x + wl * y] = n_normalmap;
// use the lightmap bitmap to store the results
}
}
(note this contains some structures and functions that are not included here so you won't be able to use this code directly, but the basic concept is there)
Once you have the normals, you need to sample normal under location (x, z) and use that in your function. This will still make the disc intersect the terrain where there is a steep slope next to flat surface (where the second derivative is high). In order to cope with that, you can either lift the cursor up a bit (along the normal), or disable depth testing.
If your terrain is polygonal, you could use vertex normals just as well, just take triangle that is below (x, y, z) and interpolate it's vertices normals to get the normal for the disc.
I hope this helps, feel free to comment if you need further advice ...

Related

Height Field with average normals

I am creating height field and i want to create its normals (face/vertex), I am using Triangles to create a mesh. I already calculated GL quad Normals but confused here for Triangles instead of quads.
I searched the internet and found out that two types of normals exists like per face and per vertex but not found any my code related help.
Here is what i am doing right now to create heightfield.
int average_normal()
{
glPushMatrix();
GLfloat xdelta=xsize/xsteps;
GLfloat zdelta=zsize/zsteps;
glNormal3f(0,1,0);
for (int x=0; x<xsteps-1; x++)
for (int z=0; z<zsteps-1; z++)
{
glBegin(GL_TRIANGLES);
setMaterialHeight(map[x][z]);
glNormal3f(0, 0, 1);
glVertex3f(xdelta*x,map[x][z],zdelta*z);
setMaterialHeight(map[x+1][z]);
glNormal3f(0, 0, 1);
glVertex3f(xdelta*x+xdelta,map[x+1][z],zdelta*z);
setMaterialHeight(map[x][z+1]);
glNormal3f(0, 0, 1);
glVertex3f(xdelta*x,map[x][z+1],zdelta*z+zdelta);
glEnd();
glBegin(GL_TRIANGLES);
setMaterialHeight(map[x+1][z+1]);
glNormal3f(0, 0, 1);
glVertex3f(xdelta*x+xdelta,map[x+1][z+1],zdelta*z+zdelta);
setMaterialHeight(map[x][z+1]);
glNormal3f(0, 0, 1);
glVertex3f(xdelta*x,map[x][z+1],zdelta*z+zdelta);
setMaterialHeight(map[x+1][z]);
glNormal3f(0, 0, 1);
glVertex3f(xdelta*x+xdelta,map[x+1][z],zdelta*z);
glEnd();
}
glPopMatrix();
return true;
}
How can I calculate its normal?
The normal vector can be computed by the Cross product of 2 vectors. Create a function, which computes the cross product and sets the normal vector attribute:
void set_normal_from_cross_product(
float x1, float y1, float z1,
float x2, float y2, float z2)
{
float nx = y1*z2 - z1*y2;
float ny = z1*x2 - x1*z2;
float nz = x1*y2 - y1*x2;
float len = sqrt(nx*nx + ny*ny + nz*nz);
if (len != 0.0f)
glNormal3f(nx/len, ny/len, nz/len);
}
In a grid the normal vectors can be computed by the vectors between the adjacent vertices. Write function which computes the vectors between the adjacent vertices and invokes set_normal_from_cross_product:
void set_grid_normal(int x, int z)
{
int prev_x = x > 0 ? x - 1 : x;
int next_x = x < xsteps - 1 ? x + 1 : x;
int prev_z = z > 0 ? z - 1 : z;
int next_z = z < zsteps - 1 ? z + 1 : z;
set_normal_from_cross_product(
xdelta * 2, map[next_x][z]-map[prev_x][z], 0,
0, map[x][next_z]-map[x][prev_z], zdelta * 2);
}
Use the function to s et the normal vectors:
void set_attributes(int x, int z)
{
setMaterialHeight(map[x][z]);
set_grid_normal(x, z);
glVertex3f(xdelta*x,map[x][z],zdelta*z);
}
for (int x=0; x<xsteps-1; x++)
{
for (int z=0; z<zsteps-1; z++)
{
glBegin(GL_TRIANGLES);
set_attributes(x, z);
set_attributes(x+1, z);
set_attributes(x, z+1);
glEnd();
glBegin(GL_TRIANGLES);
set_attributes(x+1, z+1);
set_attributes(x, z+1);
set_attributes(x+1, z);
glEnd();
}
}

How to bevel a stretched rectangle

I am able to bevel the corner of a rectangle.
When i strech the rectangle and than try to bevel it then the result does not look smooth , it should look like the rectangle on the right side.
How do i calculate the points for the trianlgle fan when the rectangle is streched ?
currently this is the way i am calculating the points for the Quarter circle.
std::vector<float> bevelData;
bevelData.push_back(0.0); // First set the centre of the rectangle to the data
bevelData.push_back(0.0);
bevelData.push_back(0.0);
bevelData.push_back(0);
bevelData.push_back(0);
bevelData.push_back(1);
bevelData.push_back(0);
bevelData.push_back(0);
for (int i = 0; i <= segments; ++i) {
float x, y;
float angle = start_angle + 0.5 * M_PI * i / static_cast<float>(segments);
x = circX + cos(angle) * rad; // circX is the centre of the circle as marked in yellow in the first image
y = circY + sin(angle) * rad; // circY is the centre of the circle as marked in yellow in the first image , rad is the radius of the circle
bevelData.push_back(x);
bevelData.push_back(y);
bevelData.push_back(0.0);
bevelData.push_back(0);
bevelData.push_back(0);
bevelData.push_back(1);
bevelData.push_back(0);
bevelData.push_back(0);
}
After applying soultion this is the result i get.
//Bevel Bottom Right
float rightWidthBottom = (width / 2) - rightBottomBevel;
float rightHeightBottom = (height / 2) - rightBottomBevel;
std::vector<float> bottomRightBevelData = draw_bevel(rightWidthBottom, rightHeightBottom, rightBottomBevel, 1, -1, iSegmentsRightBottom);
std::vector<float> SuperRectangle::draw_bevel(float p_x, float p_y, float rad, int dir_x, int dir_y , int segments)
{
std::vector<float> bevelData;
float c_x, c_y; // the center of the circle
float start_angle; // the angle where to start the arc
bevelData.push_back(0.0);
bevelData.push_back(0.0);
bevelData.push_back(0.0);
bevelData.push_back(0);
bevelData.push_back(0);
bevelData.push_back(1);
bevelData.push_back(0);
bevelData.push_back(0);
c_x = p_x * dir_x;
c_y = p_y * dir_y;
if (dir_x == 1 && dir_y == 1)
start_angle = 0.0;
else if (dir_x == 1 && dir_y == -1)
start_angle = -M_PI * 0.5f;
else if (dir_x == -1 && dir_y == 1)
start_angle = M_PI * 0.5f;
else if (dir_x == -1 && dir_y == -1)
start_angle = M_PI;
for (int i = 0; i <= segments; ++i) {
float x, y;
float angle = start_angle + 0.5 * M_PI * i / static_cast<float>(segments);
x = c_x + cos(angle) * rad;
y = c_y + sin(angle) * rad;
float fscale = (y / (float)(height / 2.0f));
x = (x + (strech * fscale));
bevelData.push_back(x);
bevelData.push_back(y);
bevelData.push_back(0.0);
bevelData.push_back(0);
bevelData.push_back(0);
bevelData.push_back(1);
bevelData.push_back(0);
bevelData.push_back(0);
}
return bevelData;
}
//////////////////////////////////////////////////////////////////
float xWidth = width / 2;
float yHeight = height / 2;
float TriangleRight[] = {
// positions // Normals // Texture Coord
0.0f , 0.0f , 0.0f , 0.0f,0.0,1.0, 0.0,0.0,
xWidth + strech , yHeight - rightTopBevel,0.0f, 0.0f,0.0,1.0 , 0.0,0.0,
xWidth - strech , -yHeight + rightBottomBevel,0.0f, 0.0f,0.0,1.0 , 0.0,0.0,
};
float TriangleLeft[] = {
// positions
0.0f , 0.0f , 0.0f , 0.0f,0.0,1.0, 0.0,0.0,
-xWidth + strech , yHeight - leftTopBevel ,0.0f, 0.0f,0.0,1.0 , 0.0,0.0,
-xWidth - strech , -yHeight + leftBottomBevel,0.0f, 0.0f,0.0,1.0 , 0.0,0.0,
};
float TriangleTop[] = {
// positions
0.0f , 0.0f , 0.0f , 0.0f,0.0,1.0, 0.0,0.0,
xWidth - rightTopBevel + strech , yHeight ,0.0f, 0.0f,0.0,1.0 , 0.0,0.0,
-xWidth + leftTopBevel + strech , yHeight,0.0f, 0.0f,0.0,1.0 , 0.0,0.0,
};
float TriangleBottom[] = {
// positions
0.0f , 0.0f , 0.0f , 0.0f,0.0,1.0, 0.0,0.0,
xWidth - rightBottomBevel - strech , -yHeight ,0.0f, 0.0f,0.0,1.0 , 0.0,0.0,
-xWidth + leftBottomBevel - strech , -yHeight,0.0f, 0.0f,0.0,1.0 , 0.0,0.0,
};
You've a rectangle with a width w and a height h
(-w/2, h/2) (w/2, h/2)
+----------------+
| |
| |
| |
| |
+----------------+
(-w/2, -h/2) (w/2, -h/2)
The points for the rounded corner of the rectangle are calculated by:
x = circX + cos(angle) * rad;
y = circY + sin(angle) * rad;
Then the rectangle is displaced by d. At the top d is add to the x component of the corner points and at the bottom d is subtracted from the x component of the corner points:
(-w/2 + d, h/2) (w/2 + d, h/2)
+----------------+
/ /
/ /
/ /
/ /
+----------------+
(-w/2 - d, -h/2) (w/2 - d, -h/2)
You have to apply the displacement d to the points along the arc, too. The displacement has to be scaled, in relation to the y coordinate of the point.
Points near the bottom edge have to be displaced by a larger scale, than points near the center of the left edge:
x = circX + cos(angle) * rad
y = circY + sin(angle) * rad
scale = y / (h/2)
x = x - d * scale

Rotation: Quaternion to matrix

I am trying to display a 360 panorama using an IMU for head tracking.
Yaw works correctly but the roll and pitch are reverse. I also notice that the pitch contains some roll (and maybe vice-versa).
I am receiving (W, X, Y, Z) coordinate from the IMU that I am storing in an array as X, Y, Z, W.
The next step is converting the quaternion to a rotation matrix. I have looked at many examples, and can't seem to find anything wrong with the following code:
static GLfloat rotation[16];
// Quaternion (x, y, z, w)
static void quaternionToRotation(float* quaternion)
{
// Normalize quaternion
float magnitude = sqrt(quaternion[0] * quaternion[0] +
quaternion[1] * quaternion[1] +
quaternion[2] * quaternion[2] +
quaternion[3] * quaternion[3]);
for (int i = 0; i < 4; ++i)
{
quaternion[i] /= magnitude;
}
double xx = quaternion[0] * quaternion[0], xy = quaternion[0] * quaternion[1],
xz = quaternion[0] * quaternion[2], xw = quaternion[0] * quaternion[3];
double yy = quaternion[1] * quaternion[1], yz = quaternion[1] * quaternion[2],
yw = quaternion[1] * quaternion[3];
double zz = quaternion[2] * quaternion[2], zw = quaternion[2] * quaternion[3];
// Column major order
rotation[0] = 1.0f - 2.0f * (yy + zz);
rotation[1] = 2.0f * (xy - zw);
rotation[2] = 2.0f * (xz + yw);
rotation[3] = 0;
rotation[4] = 2.0f * (xy + zw);
rotation[5] = 1.0f - 2.0f * (xx + zz);
rotation[6] = 2.0f * (yz - xw);
rotation[7] = 0;
rotation[8] = 2.0f * (xz - yw);
rotation[9] = 2.0f * (yz + xw);
rotation[10] = 1.0f - 2.0f * (xx + yy);
rotation[11] = 0;
rotation[12] = 0;
rotation[13] = 0;
rotation[14] = 0;
rotation[15] = 1;
}
The rotation matrix is then used in the draw call as such:
static void draw()
{
// Get IMU quaternion
float* quaternion = tracker.getTrackingData();
if (quaternion != NULL)
{
quaternionToRotation(quaternion);
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glPushMatrix();
// TODO: Multiply initialRotation quaternion with IMU quaternion
glMultMatrixf(initialRotation); // Initial rotation to point forward
glMultMatrixf(rotation); // Rotation based on IMU
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);
gluSphere(quad, 0.1, 50, 50);
glBindTexture(GL_TEXTURE_2D, 0);
glPopMatrix();
glFlush();
glutSwapBuffers();
}
I tried to set all but one fields in the quaternion to 0, and I notice that they all work individually, except roll and pitch is swapped around. I tried swapping X and Y but this does not seem to help.
Any help would be really appreciated. Please let me know as well if you have any steps that can let me debug my issue. Thanks!

How to draw a tapered arc (curve with decreasing thickness) in OpenGL?

I have the following code to draw an arbitrary arc:
void CenteredArc::drawPolygonArc(float radius, float thickness, float startAngle, float arcAngle) {
float num_segments = 360.0;
float radiusOuter = radius + thickness / 2;
float radiusInner = radius - thickness / 2;
float theta = arcAngle / num_segments;
float tangetial_factor = tanf(theta);//calculate the tangential factor
float radial_factor = cosf(theta);//calculate the radial factor
float xxOuter = radiusOuter * cosf(startAngle);
float yyOuter = radiusOuter * sinf(startAngle);
float xxInner = radiusInner * cosf(startAngle);
float yyInner = radiusInner * sinf(startAngle);
float prevXXOuter = -1;
float prevYYOuter = -1;
float prevXXInner = -1;
float prevYYInner = -1;
glPolygonMode(GL_FRONT, GL_FILL);
for(int ii = 0; ii < num_segments; ii++)
{
if (prevXXOuter != -1) {
glBegin(GL_POLYGON);
glVertex2f(prevXXOuter, prevYYOuter);
glVertex2f(xxOuter, yyOuter);
glVertex2f(xxInner, yyInner);
glVertex2f(prevXXInner, prevYYInner);
glEnd();
}
//calculate the tangential vector
//remember, the radial vector is (x, y)
//to get the tangential vector we flip those coordinates and negate one of them
float txOuter = -yyOuter;
float tyOuter = xxOuter;
float txInner = -yyInner;
float tyInner = xxInner;
//add the tangential vector
prevXXOuter = xxOuter;
prevYYOuter = yyOuter;
prevXXInner = xxInner;
prevYYInner = yyInner;
xxOuter += txOuter * tangetial_factor;
yyOuter += tyOuter * tangetial_factor;
xxInner += txInner * tangetial_factor;
yyInner += tyInner * tangetial_factor;
//correct using the radial factor
xxOuter *= radial_factor;
yyOuter *= radial_factor;
xxInner *= radial_factor;
yyInner *= radial_factor;
}
}
However, I would like for the arc to start off with the specified thickness on one end and gradually decrease to a thickness of zero on the other end. Any suggestions?
Edit: I am not using GL_LINE_STRIP because I am trying to avoid having overlapping lines and gaps like so:
I would use a line strip with decreasing glLineWidth.
This is my implementation, it doesn't gradially reduce the lineWidth but it could be modified to do so. Sorry for the extra stuff, it's from my game engine.
for(int i=0;i<arcs().size();i++)
{
Entities::Arc temp = arcs().at(i);
glLineWidth(temp.LW.value); // change LWidth
glColor3f( temp.CL.R, temp.CL.G, temp.CL.B );
// theta is now calculated from the arc angle instead, the
// - 1 part comes from the fact that the arc is open
float theta = temp.A.value*DEG2RAD / float(WW_SPLINE_ACCURACY - 1);
float tan = tanf(theta);
float cos = cosf(theta);
// we are now at the starting angle
double x = temp.R.value * cosf(temp.A.value*DEG2RAD);
double y = temp.R.value * sinf(temp.A.value*DEG2RAD);
// since the arc is not a closed curve, this is a strip now
glBegin(GL_LINE_STRIP);
for(int ii = 0; ii < WW_SPLINE_ACCURACY; ii++)
{
glVertex2d(x + temp.C.X, y + temp.C.Y);
double tx = -y;
double ty = x;
x += tx * tan;
y += ty * tan;
x *= cos;
y *= cos; //y = ( y + (ty*tan) )*cos;
}
glEnd();
glLineWidth(WW_DEFAULT_LWIDTH); // reset LWidth
}
I also used these values
#define WW_SPLINE_ACCURACY 72 // 72 for extra smooth arcs/circles, 32 minimum
#define WW_BEZIER_ACCURACY 20
/* Math stuff */
#define DEG2RAD 3.14159/180
#define PI 3.1415926535897932384626433832795;
...
glDisable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glDisable(GL_COLOR_MATERIAL);
glEnable (GL_LINE_SMOOTH);
glEnable (GL_BLEND);
//glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glHint (GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
glEnable(GL_POLYGON_SMOOTH);
glClearColor(0.188f, 0.169f, 0.329f, 1.0f); //#302b54
I'm not allowed to release the full source since I wrote it for a company but sharing a part or two wont hurt anybody :D

Efficient way to draw Ellipse with OpenGL or D3D

There is a fast way to draw circle like this
void DrawCircle(float cx, float cy, float r, int num_segments)
{
float theta = 2 * 3.1415926 / float(num_segments);
float c = cosf(theta);//precalculate the sine and cosine
float s = sinf(theta);
float t;
float x = r;//we start at angle = 0
float y = 0;
glBegin(GL_LINE_LOOP);
for(int ii = 0; ii < num_segments; ii++)
{
glVertex2f(x + cx, y + cy);//output vertex
//apply the rotation matrix
t = x;
x = c * x - s * y;
y = s * t + c * y;
}
glEnd();
}
I am wondering if there is a similar way to draw ellipse where its major/minor axes vector and size are both known.
If we take your example we can use an internal radius of 1 and apply horizontal/vertical radius separately in order to get an ellipse:
void DrawEllipse(float cx, float cy, float rx, float ry, int num_segments)
{
float theta = 2 * 3.1415926 / float(num_segments);
float c = cosf(theta);//precalculate the sine and cosine
float s = sinf(theta);
float t;
float x = 1;//we start at angle = 0
float y = 0;
glBegin(GL_LINE_LOOP);
for(int ii = 0; ii < num_segments; ii++)
{
//apply radius and offset
glVertex2f(x * rx + cx, y * ry + cy);//output vertex
//apply the rotation matrix
t = x;
x = c * x - s * y;
y = s * t + c * y;
}
glEnd();
}
There is no way to draw a curve in openGL, just a lot of straight lines. But if you used vertex buffer objects then you won't have to send each vertex to the graphics card which will be much faster.
My Java Example
If the ellipse is ((x-cx)/a)^2 + ((y-cy)/b)^2 = 1 then change the glVertex2f call to
glVertext2d(a*x + cx, b*y + cy);
To simplify the sums, lets suppose for a while that the ellipse is centred at the origin.
If the ellipse is rotated so that the semi-major axis (of length a) makes an angle theta with the x axis, then the ellipse is the set of points p so that p' * inv(C) * p = 1, where C is the matrix R(theta) * D * R(theta)' where ' denotes transpose and D is the diagonal matrix with entries a*a,b*b (b the length of the semi-minor axis). If L is the cholesky factor (eg here) of C then the ellipse is the set of points p so that (inv(L) * p)'*(inv(L) *p ) = 1, so that L maps the unit circle to the ellipse. If we have computed L as ( u 0 ; v w) (just once, before the loop) then the glVertexf call becomes glVertex2f( u*x + cx, v*x + w*y + cy);
L can be calculated like this (where C is cos(theta) and S is sin(theta)):
u = sqrt( C*C*a*a + S*S*b*b); v = C*S*(a*a-b*b); w = a*b/u;