Related
I have developed an opengl application where we draw strings of text using freetype and opengl.
I want to achieve rotation capability for the text that I put on OpenGL window.
For instance, "This is a text" string should be calculated and put into a buffer on a plain background and then refactored with a rotation value, so that the text will be visible as such below
I also have a text background that is just a regular texture with a buffer. I manually fill this background with a uint8_t buffer which can contain anything ranging from a single colour to an image buffer.
struct Background{
Color color;
Texture* bg_texture;
int x, y;
int w, h;
uint8_t* buffer;
explicit Background(int x, int y):x(x), y(y)
{
};
void create_bg_buffer();
~Background()
{
free(buffer);
}
};
void Background::create_bg_buffer()
{
int w = this->w;
int h = this->h;
if (posix_memalign((void**)&this->buffer, 128, w * h * 4) != 0)
{
VI_ERROR("ERROR::FREETYTPE: Couldn't allocate frame buffer ");
}
int c = 0;
for ( int i = 0; i < w; i++ )
{
for ( int j = 0; j < h; j++ )
{
this->buffer[ c + 0 ] = this->color.get_color_char(Utils::RED);
this->buffer[ c + 1 ] = this->color.get_color_char(Utils::GREEN);
this->buffer[ c + 2 ] = this->color.get_color_char(Utils::BLUE);
this->buffer[ c + 3 ] = 0xFF;
c += 4;
}
}
}
I want users to be able to rotate this text with it's background with a given angle. In on itself, rotating this is a tedious task. So I want to draw the text inside the backgrounds buffer itself, and then rotate it.
Please note that the way I rotate a background, for different reasons is not using an opengl function but rather taking the rectangle's middle point and rotating each point manually and passing those points to opengl with this code:
cpp
...
GLfloat vertices[32] = {
// positions // colors // texture coords
pos.TR_x, pos.TR_y, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top right
pos.BR_x, pos.BR_y, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, // bottom right
pos.BL_x, pos.BL_y, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom left
pos.TL_x, pos.TL_y, 1.0f, 0.1f, 0.1f, 0.1f, 0.0f, 0.0f // top left
};
unsigned int indices[] = {
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
...
Every pos stands for a rotated position, with labels indicating positions such as TR stands for top-right.
We want to use a Framebuffer for the output buffer. Then we want to use this framebuffer to be used for actual OpenGL output.
How should we alter the render_text function so that it will use the framebuffer to prepare the string from each individual character.
void Text::render_text(float angle_rad, bool has_bg)
{
if(has_bg) background->bg_texture->render(background->w, background->h, background->buffer, 1);
int start_y = ty + background->h;
start_y = ( std::abs(start_y - SCR_HEIGHT) / 2);
int total_h_index = 0;
for(auto& line: lines)
{
line.y = start_y;
line.x = tx;
total_h_index += line.total_height + LINE_GAP;
calc_pos(line.x, line.y, line.total_width, line.total_height, total_h_index);
for (c = line.text.begin(); c != line.text.end(); c++)
{
Character ch = Characters[*c];
line.char_h.push_back(ch.Size.y);
line.chars_y.push_back( line.y - (ch.Size.y - ch.Bearing.y) );
}
}
// glEnable(GL_CULL_FACE);
// glDisable(GL_BLEND);
// glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
shader.use();
glUniform3f(glGetUniformLocation(shader.ID, "textColor"), color.r, color.g, color.b);
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(VAO);
GLfloat vertices[6][4] = {
{ 0.0, 1.0, 0.0, 0.0 },
{ 0.0, 0.0, 0.0, 1.0 },
{ 1.0, 0.0, 1.0, 1.0 },
{ 0.0, 1.0, 0.0, 0.0 },
{ 1.0, 0.0, 1.0, 1.0 },
{ 1.0, 1.0, 1.0, 0.0 }
};
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // Be sure to use glBufferSubData and not glBufferData
glBindBuffer(GL_ARRAY_BUFFER, 0);
GLint transition_loc = glGetUniformLocation(shader.ID, "transparency");
glUniform1f(transition_loc, 1.0f);
for(auto& line: lines)
{
GLfloat char_x = 0.0f;
std::string str = line.text;
glm::mat4 transOriginM = glm::translate(glm::mat4(1.0f), glm::vec3(line.x, line.y, 0));
glm::mat4 rotateM = glm::rotate(glm::mat4(1.0f), glm::radians(-angle_rad), glm::vec3(0.0f, 0.0f, 1.0f));
int e = 0;
std::vector<glm::vec2> rotated_pos = calc_rotation(line.chars_x, line.chars_y, -angle_rad, line.total_width);
for (c = str.begin(); c != str.end(); c++)
{
Character ch = Characters[*c];
GLfloat w = ch.Size.x;
GLfloat h = ch.Size.y;
GLfloat xrel = rotated_pos[e].x ; // char_x
GLfloat yrel = rotated_pos[e].y;
// Now advance cursors for next glyph (note that advance is number of 1/64 pixels)
e++; // Bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels))
glm::mat4 transRelM = glm::translate(glm::mat4(1.0f), glm::vec3(xrel, yrel, 0));
glm::mat4 scaleM = glm::scale(glm::mat4(1.0f), glm::vec3(w, h, 1.0f));
// Keep the translation matrix that sets the position of the text before the rotation matrix
glm::mat4 modelM = transOriginM * transRelM * rotateM * scaleM;
GLint model_loc = glGetUniformLocation(shader.ID, "model");
glUniformMatrix4fv(model_loc, 1, GL_FALSE, glm::value_ptr(modelM));
// Render glyph texture over quad
glBindTexture(GL_TEXTURE_2D, ch.TextureID);
// Render quad
glDrawArrays(GL_TRIANGLES, 0, 6);
}
}
As of now, "Adding a character or text" is completely independent from the background operation.
They are just positioned in a way, so it looks like it has a background.
Our aim is to use a single output buffer that will hold both background color and freetype text data.
Following is how we handle the texture and texture rotation mechanism :
#define _VERTICIZE_X(number, global) _VERTICIZE(number, global) - 1
#define _VERTICIZE_Y(number, global) _VERTICIZE(number, global) + 1
namespace OpenGL
{
Texture::Texture(int x, int y, int w, int h, int gw, int gh, float angle)
{
Utils::Point rotatedPoints[4] = {
{x, y},
{x + w, y},
{x, y + h},
{x + w, y + h},
};
Utils::RotateRectangle(rotatedPoints, angle);
pos.TL_x = _VERTICIZE_X(rotatedPoints[0].x, gw); pos.TL_y = -_VERTICIZE_Y(rotatedPoints[0].y, gh);
pos.TR_x = _VERTICIZE_X(rotatedPoints[1].x, gw); pos.TR_y = -_VERTICIZE_Y(rotatedPoints[1].y, gh);
pos.BL_x = _VERTICIZE_X(rotatedPoints[2].x, gw); pos.BL_y = -_VERTICIZE_Y(rotatedPoints[2].y, gh);
pos.BR_x = _VERTICIZE_X(rotatedPoints[3].x, gw); pos.BR_y = -_VERTICIZE_Y(rotatedPoints[3].y, gh);
}
int Texture::init(float alpha, std::string* filter, Utils::Color proj_filt)
{
shader = Shader("./src/opengl/shaders/texture_shaders/texture.vs", "./src/opengl/shaders/texture_shaders/texture.fs");
void RotateRectangle(Point (&points)[4], float angle) {
// Calculate the center point
Point center = { 0 };
for (int i = 0; i < 4; i++) {
center.x += points[i].x;
center.y += points[i].y;
}
center.x /= 4;
center.y /= 4;
// Rotate each point
float angleRadians = angle * M_PI / 180.0f;
float s = sin(angleRadians);
float c = cos(angleRadians);
for (int i = 0; i < 4; i++) {
// Subtract the center point to get a vector from the center to the point
Point vector = { points[i].x - center.x, points[i].y - center.y };
// Rotate the vector
float x = vector.x;
float y = vector.y;
vector.x = x * c - y * s;
vector.y = x * s + y * c;
// Add the center point back to the rotated vector to get the new point
points[i].x = vector.x + center.x;
points[i].y = vector.y + center.y;
}
}
How can we use a framebuffer so that all OpenGL and FreeType operation are going to be executed in a single output space, and following that depending our way we can rotate the whole text using this single output framebuffer ?
I'm trying to draw these shaped bellow this this:
What I want
Tried this code:
glLoadIdentity();
glColor3f(0.98f, 0.83f, 0.73f);
glBegin(GL_POLYGON);
for (float i = 0; i <= (2 * p); i += 0.001) {
x = 100 * cos(i)-10;
y = 115 * sin(i)+270;
glVertex2f(x, y);
}
glEnd();
glRotatef(-135.0f, 0.0f, 0.0f, 1.0f);
glColor3f(1.0f, 0.83f, 0.0f);
glBegin(GL_POLYGON);
for (float i = p; i <= (2 * p); i += 0.001) {
x = 100 * cos(i) - 10;
y = 115 * sin(i) + 270;
glVertex2f(x, y);
}
But this is what I get:
What I get
If I want to only use the glLoadIdentity and glRotatef for rotation, do you have any idea about how to fix it?
Note:
I don't want to use push/pop or translation
You have to rotate the object around its center and move the rotated object to its position in the world. glRotatef rotates the vertices around (0, 0). Draw the object around (0, 0) and glTranslate to move the object to its position in the world:
glTranslate(-10.0f, 270.0f, 0.0f);
glRotatef(-135.0f, 0.0f, 0.0f, 1.0f);
glColor3f(1.0f, 0.83f, 0.0f);
glBegin(GL_POLYGON);
for (float i = p; i <= (2 * p); i += 0.001) {
x = 100 * cos(i);
y = 115 * sin(i);
glVertex2f(x, y);
}
Note, the matrix operations like glRotate and glTranslate specify a new matrix and multiply the current matrix by the new matrix.
If you are not allowed to use glTranslate, you have to rotate the translation vector (-10, 270) in the opposite direction. Use the trigonometric functions sin an cos to rotate the vector (see Rotation matrix). You need to invert the angle and convert it to Radians since the unit of sin and cos is Radian.
float tx = -10.0f;
float ty = 270.0f;
float angle = -135.0f;
float inv_angle_rad = -angle * M_PI / 180.0f;
float tx_rot = tx * cos(inv_angle_rad) - ty * sin(inv_angle_rad);
float ty_rot = tx * sin(inv_angle_rad) + ty * cos(inv_angle_rad);
glRotatef(angle, 0.0f, 0.0f, 1.0f);
glColor3f(1.0f, 0.83f, 0.0f);
glBegin(GL_POLYGON);
for (float i = p; i <= (2 * p); i += 0.001) {
x = 100 * cos(i) + tx_rot;
y = 115 * sin(i) + ty_rot;
glVertex2f(x, y);
}
I'm using opengl for educational purposes, but I'm having trouble creating multiple spotlights to represent street lamps. I use an iterator to create several however in the end only the last spotlight gets the light, I believe the problem is in the addlight method however I do not know what is happening.
In image below you can see happen.
https://imgur.com/Y77eHln
#include "CreateLamps.h"
std::vector<SpotLight> lamp;
CreateLamps::CreateLamps(int number) {
for (int i = 0; i < number; i++) {
SpotLight *spot = new SpotLight(-8.0f, 5.f, (i*10) + 30.0f, 1.f);
lamp.push_back(*spot);
}
}
void CreateLamps::Add() {
std::vector<SpotLight>::iterator it = lamp.begin();
while (it != lamp.end())
{
glPushMatrix();
glTranslatef(1, 0, 30.0);
glTranslatef(it->position[0], it->position[3] * 3, it->position[2]);
glRotatef(100.f, -5.0, -10, 0);
it->addlight();
it->draw();
it++;
glPopMatrix();
}
}
#include "SpotLight.h"
using namespace std;
GLfloat target[3] = { 0.0f, 0.0f, 0.0f };
GLfloat color[3] = { 1.0f, 1.0f, 1.0f };
GLfloat cutoff(5.0f);
GLfloat exponent(15.0f);
SpotLight::SpotLight(GLfloat x, GLfloat y, GLfloat z, GLfloat w) {
position[0] = x;
position[1] = y;
position[2] = z;
position[3] = w;
direction[0] = target[0] - position[0];
direction[1] = target[1] - position[1];
direction[2] = (target[2] - position[2]);
}
void SpotLight::addlight() {
glEnable(GL_LIGHT1);
glLightfv(GL_LIGHT1, GL_DIFFUSE, color);
glLightfv(GL_LIGHT1, GL_SPECULAR, color);
glLightfv(GL_LIGHT1, GL_POSITION, position);
glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, direction);
glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, cutoff);
glLightf(GL_LIGHT1, GL_SPOT_EXPONENT, exponent);
}
void SpotLight::draw() {
if (!glIsEnabled(GL_LIGHT1))
return;
glPushMatrix();
GLfloat up[3] = { 0, 1, 0 };
lookAt(position, target, up);
GLfloat ambient[4] = { 0.8f, 0.8f, 0.8f, 1.0f };
GLfloat diffuse[4] = { 0.01f, 0.01f, 0.01f, 1.0f };
GLfloat specular[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
GLfloat shininess = 32.0f;
glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
glMaterialf(GL_FRONT, GL_SHININESS, shininess);
glutSolidCone(0.3, 0.6, 10, 10);
glPushMatrix();
glTranslatef(0, 0, 0.1f);
glutSolidCylinder(0.2, 0.39, 10, 10);
glPopMatrix();
glDisable(GL_LIGHTING);
glColor3fv(color);
glutSolidSphere(0.2, 100, 100);
glEnable(GL_LIGHTING);
glPopMatrix();
}
void SpotLight::normalize(const GLfloat* vec, GLfloat* output)
{
GLfloat length = sqrtf(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);
output[0] /= length;
output[1] /= length;
output[2] /= length;
}
void SpotLight::cross(const GLfloat* vec1, const GLfloat* vec2, GLfloat * output) {
output[0] = vec1[1] * vec2[2] - vec1[2] * vec2[1];
output[1] = vec1[2] * vec2[0] - vec1[0] * vec2[2];
output[2] = vec1[0] * vec2[1] - vec1[1] * vec2[0];
}
void SpotLight::lookAt(GLfloat* eye, GLfloat* center, GLfloat* up)
{
GLfloat f[3] = { center[0] - eye[0],
center[1] - eye[1],
center[2] - eye[2] };
normalize(f, f);
GLfloat u[3];
normalize(up, u);
GLfloat s[3];
cross(f, u, s);
normalize(s, s);
cross(s, f, u);
normalize(u, u);
}
void drawScene() {
glPushMatrix();
glTranslatef(pointlight.position[0], pointlight.position[1], pointlight.position[2]);
pointlight.addLight();
glPopMatrix();
// Draw road
glPushMatrix();
glScalef(10, 10, 8.5);
glTranslatef(-0.018f, 0, 0.75);
glRotatef(180.f, 0, 1, 0);
models[0]->renderTheModel();
glPopMatrix();
//Draw Car Model
glPushMatrix();
glMultMatrixf(carros[0]->local);
carros[0]->draw();
glPopMatrix();
//Draw spotlights
glPushMatrix();
lamps->Add();
glPopMatrix();
}
You are only ever setting LIGHT1, which means only 1 light is going to have been enabled (the last one). If you specify GL_LIGHT0 + index, you'll be able to enable more.
void SpotLight::addlight(int index) {
glEnable(GL_LIGHT0 + index);
glLightfv(GL_LIGHT0 + index, GL_DIFFUSE, color);
glLightfv(GL_LIGHT0 + index, GL_SPECULAR, color);
glLightfv(GL_LIGHT0 + index, GL_POSITION, position);
glLightfv(GL_LIGHT0 + index, GL_SPOT_DIRECTION, direction);
glLightf(GL_LIGHT0 + index, GL_SPOT_CUTOFF, cutoff);
glLightf(GL_LIGHT0 + index, GL_SPOT_EXPONENT, exponent);
}
And then you simply need to pass the index in when enabling
void CreateLamps::Add() {
std::vector<SpotLight>::iterator it = lamp.begin();
while (it != lamp.end())
{
glPushMatrix();
glTranslatef(1, 0, 30.0);
glTranslatef(it->position[0], it->position[3] * 3, it->position[2]);
glRotatef(100.f, -5.0, -10, 0);
it->addlight(it - lamp.begin());
it->draw();
it++;
glPopMatrix();
}
}
Just be aware that you might run out of lights after 8, so you might want to check the value of GL_MAX_LIGHTS...
int numLights = 0;
glGetIntegerv(GL_MAX_LIGHTS, &numLights);
std::cout << "GL_MAX_LIGHTS " << numLights << std::endl;
Presumably you are drawing the road after drawing all of the lights.
Your code does this:
For each light:
Set light 1 according to that light's parameters
Draw the shape of the light itself
Draw the road.
When the road gets drawn, light 1 is set up with the parameters of the last light that was drawn. So it uses these light parameters to draw the road. You overwrote all the parameters of the other lights already.
If you want to draw the road with all the lights, then all the lights have to be set up when you draw the road. Not just the last one.
Note that you can only set up 8 lights at once in OpenGL. If have more than 8 lights pointing at the road, you will have to split up the road into different sections so that each section has up to 8 lights.
Standard disclaimer in case you aren't aware already: You are using old-style (fixed-function) OpenGL which has been superseded by shader-based OpenGL (version 3 and 4). This API is okay for simple programs but it won't let you use the full flexibility and performance of your graphics card.
I am rather new to c++ and would like to achieve the following:
ttgl::vec3f positions[] = {
ttgl::vec3f(-1.0f, 1.0f, 0.0f),
ttgl::vec3f(1.0f, 1.0f, 0.0f),
ttgl::vec3f(1.0f, -1.0f, 0.0f),
ttgl::vec3f(1.0f, -1.0f, 0.0f),
ttgl::vec3f(-1.0f, -1.0f, 0.0f),
ttgl::vec3f(-1.0f, 1.0f, 0.0f),
};
The problem is, that I don't know the values and have to fill this array dynamically.
I try to achieve it with the following function:
void getCirclePositions(GLfloat radius, GLint sides) {
ttgl::vec3f center = ttgl::vec3f(0.0f, 0.0f, 0.0f);
GLfloat angle = (2.0f * M_PI) / sides;
ttgl::vec3f positions[100];
positions[0] = center;
for (int i = 1; i <= sides; i++) {
GLfloat angleFan = angle * (i + 1);
GLfloat xCoordinate = radius * cos(angleFan);
GLfloat yCoordinate = radius * sin(angleFan);
ttgl::vec3f point = ttgl::vec3f(xCoordinate, yCoordinate, 0.0f);
positions[i] = point;
}
return positions;
};
This leads to the following error:
Run-Time Check Failure #2 - Stack around the variable 'positions' was
corrupted.
How could I insert the values correctly?
EDIT
The function is called as follows:
getCirclePositions(1.0f, 100);
I edited the code accordingly and the error is solved. Thanks for that.
void getCirclePositions(GLfloat radius, GLint sides) {
ttgl::vec3f center = ttgl::vec3f(0.0f, 0.0f, 0.0f);
GLfloat angle = (2.0f * M_PI) / sides;
ttgl::vec3f positions[100];
positions[0] = center;
for (int i = 1; i < sides; i++) {
GLfloat angleFan = angle * (i + 1);
GLfloat xCoordinate = radius * cos(angleFan);
GLfloat yCoordinate = radius * sin(angleFan);
ttgl::vec3f point = ttgl::vec3f(xCoordinate, yCoordinate, 0.0f);
positions[i] = point;
}
for (int i = 0; i >sides; i++) {
std::cout << positions[i];
}
};
How can I print this array?
I've got an OpenGL program running, and it displays geometry, but it's all "flat," one gray tone, with no diffuse shading or specular reflection:
Pictured are three tori, each made of quad strips. We should see shading, but we don't. What am I doing wrong?
Here is the code where I set the vertices and normals (draw_torus() is called to build a display list):
/* WrapTorus, adapted from
http://www.math.ucsd.edu/~sbuss/MathCG/OpenGLsoft/WrapTorus/WrapTorus.html
by Sam Buss */
/*
* Issue vertex command for segment number j of wrap number i.
* Normal added by Lars Huttar.
* slices1 = numWraps; slices2 = numPerWrap.
*/
void putVert(float i, float j, float slices1, float slices2, float majR, float minR) {
float wrapFrac = j / slices2;
/* phi is rotation about the circle of revolution */
float phi = PI2 * wrapFrac;
/* theta is rotation about the origin, in the xz plane. */
float theta = PI2 * (i + wrapFrac) / slices1;
float y = minR * (float)sin(phi);
float r = majR + minR * (float)cos(phi);
float x = (float)sin(theta) * r;
float z = (float)cos(theta) * r;
/* normal vector points to (x,y,z) from: */
float xb = (float)sin(theta) * majR;
float zb = (float)cos(theta) * majR;
glNormal3f(x - xb, y, z - zb);
glVertex3f(x, y, z);
}
static void draw_torus(int numPerWrap, int numWraps, float majR, float minR) {
int i, j;
glBegin( GL_QUAD_STRIP );
for (i=0; i < numWraps; i++ ) {
for (j=0; j < numPerWrap; j++) {
putVert((float)i, (float)j, (float)numWraps, (float)numPerWrap, majR, minR);
putVert((float)(i + 1), (float)j, (float)numWraps, (float)numPerWrap, majR, minR);
}
}
putVert(0.0, 0.0, (float)numWraps, (float)numPerWrap, majR, minR);
putVert(1.0, 0.0, (float)numWraps, (float)numPerWrap, majR, minR);
glEnd();
}
Is there something wrong with the order of vertices?
Here is the part of the init function where the display list is built:
GLfloat white[4] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat color[4] = { 0.5, 0.6, 0.7, 1.0 };
...
glShadeModel(GL_SMOOTH);
torusDL = glGenLists (1);
glNewList(torusDL, GL_COMPILE);
setMaterial(color, white, 100);
draw_torus(8, 45, 1.0, 0.05);
glEndList();
where setMaterial() just does:
static void setMaterial(const GLfloat color[3], const GLfloat hlite[3], int shininess) {
glColor3fv(color);
glMaterialfv(GL_FRONT, GL_SPECULAR, hlite);
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
glMateriali(GL_FRONT, GL_SHININESS, shininess); /* [0,128] */
}
Here is lighting that's also done during initialization:
GLfloat pos[4] = {0.4, 0.2, 0.4, 0.0};
GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
GLfloat color[4] = {0.20, 0.20, 0.20, 1.00};
GLfloat spec[4] = {0.30, 0.30, 0.30, 1.00};
GLfloat shiny = 8.0;
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glLightfv(GL_LIGHT0, GL_POSITION, pos);
glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny);
Here is where the display list gets called, in the draw function:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glLoadIdentity();
glScalef(3.5, 3.5, 3.5);
for (i = 0; i < ac->nrings; i++) {
glScalef(0.8, 0.8, 0.8);
glRotatef(...);
glCallList(torusDL);
}
glFlush();
glPopMatrix();
glXSwapBuffers(dpy, window);
The full .c source file for this "glx hack" is here. In case it makes a difference, this code is in the context of xscreensaver.
As you see, glEnable(GL_NORMALIZE) normalizes the normal vectors after the transformations used for lighting calculations (in fixed-function pipelines). These calculations rely on unit length normals for correct results.
It's worth pointing out that the transforms applied to normal vectors are not the same as the transforms applied to vertex geometry. The OpenGL 2.1 specification describes the transform, as do many other resources. As a vector, a normal has the homogeneous representation: [nx, ny, nz, 0] - a point at 'infinity', and a mathematically elegant way to unify matrix and 4-vector operations in the GL pipeline.
Of course, you could perform this normalization yourself, and it may be more efficient to do so, as your torus geometry is only generated once for a pre-compiled display list:
nx = x - b, ny = y, nz = z - zb;
nl = 1.0f / sqrtf(nx * nx + ny * ny + nz * nz);
glNormal3f(nx * nl, ny * nl, nz * nl);
Be sure to check (nl) for division by zero (or some epsilon), if that's a possibility.