Height Field with average normals - opengl

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();
}
}

Related

Changing a heightmap so it show 16 hieghtmaps on a 4x4 grid

Change the program to display 16 identical heightmaps arranged in a 4 x 4 grid. The edges of the heightmaps should be side by side in X and Z coordinates. However, they will not touch in the Y direction because the heights will be different.
C++
The below code is what I have already, i am just not too sure how to make it show the 16 identical heightmaps arranged in a 4x4 grid. I know it has to do with the squares on the height map, but i am very confused.
const int HEIGHTMAP_SIZE = 12;
float heights[HEIGHTMAP_SIZE + 1][HEIGHTMAP_SIZE + 1];
initDisplay();
for (unsigned int x = 0; x <= HEIGHTMAP_SIZE; x++)
{
for (unsigned int z = 0; z <= HEIGHTMAP_SIZE; z++)
{
heights[x][z] = (x % 2) * 0.5f -
z * z * 0.05f;
}
}
//TextureManager::activate("rainbow.bmp");
initHeightmapDisplayList();
void initHeightmapHeights()
{
for (unsigned int x0 = 0; x0 < HEIGHTMAP_SIZE; x0++)
{
unsigned int x1 = x0 + 1;
float tex_x0 = (float)(x0) / HEIGHTMAP_SIZE;
float tex_x1 = (float)(x1) / HEIGHTMAP_SIZE;
glBegin(GL_TRIANGLE_STRIP);
for (unsigned int z = 0; z <= HEIGHTMAP_SIZE; z++)
{
float tex_z = (float)(z) / HEIGHTMAP_SIZE;
glTexCoord2d(tex_x1, tex_z);
glVertex3d(x1, heights[x1][z], z);
glTexCoord2d(tex_x0, tex_z);
glVertex3d(x0, heights[x0][z], z);
}
glEnd();
}
}
void initHeightmapDisplayList()
{
heightmap_list.begin();
glEnable(GL_TEXTURE_2D);
TextureManager::activate("ground.bmp");
glColor3d(1.0, 1.0, 1.0);
initHeightmapHeights();
glDisable(GL_TEXTURE_2D);
heightmap_list.end();
}
I suspect that your TextureManager already has a way of doing this without direct calls to OpenGL.
What you should do is make the texture repeat itself.
glTexParamteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
By then scaling your texture coordinates up by a factor of 4, you would obtain a 4x4 grid of the original texture.

OpenGL flood fill not recognizing boundary

I'm trying to implement the flood fill algorithm in OpenGL, but I'm encountering an error doing so. The error is that the algorithm does not stop at the boundary, and simply keeps going until the edge of the window, and eventually crashes with a bad memory access error. I'm working on MacOS Mojave 10.14.4.
I think my implementation's logic is correct, however, I've printed out the color of each pixel from getPixel, and it is always white (the background color), even when getting the color of boundary pixels.
The code below draws a circle using Bresenham's Line algorithm (midpoint algorithm), and then flood fills it (unsuccessfully).
#include <GLUT/GLUT.h>
#include <iostream>
struct Color {
GLubyte r;
GLubyte g;
GLubyte b;
};
Color getPixelColor(GLint x, GLint y) {
Color color;
glReadPixels(x, y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &color);
return color;
}
void setPixel (GLint x, GLint y) {
glBegin(GL_POINTS);
glVertex2i(x, y);
glEnd();
Color color = getPixelColor(x, y);
}
void setPixelColor(GLint x, GLint y, Color color) {
glColor3ub(color.r, color.g, color.b);
setPixel(x, y);
glEnd();
glFlush();
}
void floodFill4 (GLint x, GLint y, Color fillColor, Color interiorColor) {
Color color = getPixelColor(x, y);
if (color.r == interiorColor.r && color.g == interiorColor.g &&
color.b == interiorColor.b) {
setPixelColor(x, y, fillColor);
floodFill4 (x + 1, y, fillColor, interiorColor);
floodFill4 (x - 1, y, fillColor, interiorColor);
floodFill4 (x, y + 1, fillColor, interiorColor);
floodFill4 (x, y - 1, fillColor, interiorColor);
}
}
void drawCirclePoint(GLint x, GLint y, GLint cx, GLint cy) {
setPixel(cx+x, cy+y);
setPixel(cx+y, cy+x);
setPixel(cx-y, cy+x);
setPixel(cx-x, cy+y);
setPixel(cx-x, cy-y);
setPixel(cx-y, cy-x);
setPixel(cx+y, cy-x);
setPixel(cx+x, cy-y);
}
void drawCircle(GLint cx, GLint cy, GLint radius) {
int p = 1 - radius;
GLint x = 0;
GLint y = radius;
while (x < y) {
drawCirclePoint(x, y, cx, cy);
if (p < 0) {
x++;
p += (2 * x) + 1;
} else {
x++;
y--;
p += (2 * x) + 1 - (2 * y);
}
}
}
void displayMe(void) {
glClear(GL_COLOR_BUFFER_BIT);
glColor3ub(0, 0, 0);
GLint cx = 0;
GLint cy = 0;
GLint radius = 200;
// Draw head
glColor3ub(0, 0, 0);
drawCircle(cx, cy, radius);
glEnd();
glFlush();
Color interiorColor = {255, 255, 255};
Color fillColor = {0, 0, 255};
// floodFill4(100, 100, fillColor, interiorColor);
}
void init (void) {
glClearColor(1.0, 1.0, 1.0, 0.0); // Set display-window color to white.
glMatrixMode(GL_PROJECTION); // Set projection parameters.
glLoadIdentity();
gluOrtho2D(-1000.0, 1000.0, -1000.0, 1000.0);
glMatrixMode(GL_MODELVIEW);
}
int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(1000, 1000);
glutInitWindowPosition(100, 100);
glutCreateWindow("My Drawing");
init();
glutDisplayFunc(displayMe);
glutMainLoop();
return 0;
}
I've looked at this post, which seems similar, but wasn't able to find a solution.
If you're going to persist with glVertex() for point plotting make sure you set up your matrix stack transforms (GL_PROJECTION/GL_MODELVIEW) so they match the glReadPixels() coordinate system:
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluOrtho2D( 0, glutGet(GLUT_WINDOW_WIDTH), 0, glutGet(GLUT_WINDOW_HEIGHT) );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
Or switch to glRasterPos() + glDrawPixels() for setPixel*().
Better yet, do the flood-fill logic host-side & upload the results to a GL texture for display.
Either way you're going to run into a stack overflow with that recursive solution on even fairly reasonably sized inputs so you'll probably want to switch to an explicit stack/queue:
void floodFill4( GLint aX, GLint aY, Color fillColor, Color interiorColor )
{
typedef std::pair< GLint, GLint > Location;
std::queue< Location > locations;
locations.push( Location( aX, aY ) );
while( !locations.empty() )
{
const Location loc = locations.front();
locations.pop();
GLint x = loc.first;
GLint y = loc.second;
Color color = getPixelColor( x, y );
if( color.r == interiorColor.r &&
color.g == interiorColor.g &&
color.b == interiorColor.b )
{
setPixelColor( x, y, fillColor );
locations.push( Location( x, y - 1 ) );
locations.push( Location( x, y + 1 ) );
locations.push( Location( x - 1, y ) );
locations.push( Location( x + 1, y ) );
}
}
}

How to correctly translate objects independently of one another?

I am attempting to translate one circle independent of a separate, stationary circle, utilizing glTranslatef();. However, with my current, full code, each of my circles remains immobile. To investigate why this may be so, I have researched several answers, each comparable to those found here and here. Additionally, I read up on glLoadIdentity as well as the differences between GL_MODELVIEW and GL_PROJECTION, just to see if their details would offer any further clarification. I've also consulted the OpenGL API for the proper definitions of each of the above.
In the style of these solutions, I produced the following do...while loop:
do{
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, fb_width, fb_height, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(0,1,0);
drawCircle(1280 * 0.50, 720 * 0.25,e[2]);
glPopMatrix();
glPushMatrix();
glTranslatef(0,0,0);
drawTarget(1280 * 0.50, 720 * 0.75,50);
glPopMatrix();
glfwSwapBuffers(w);
glfwPollEvents();
}
while (!glfwWindowShouldClose(w));
In this snippet, the drawCircle drawing remains stationary, but I would like for it to follow the written glTranslatef(0,1,0) instead. Is the stationary nature of the circle due to misplaced a glMatrixMode or glLoadIdentity, or perhaps due to the fact that they are being called within the do...while loop and the proper matrix is never really being utilized? I would appreciate any guidance you may have as to why the aforementioned and accepted answers are not functioning quite as well within my program.
For the sake of full transparency, here is the entirety of my code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <stddef.h>
#include <stdbool.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
GLFWwindow *w;
int fb_width, fb_height;
static void error(int error, const char *desc)
{
fputs(desc, stderr);
}
static void key_callback(GLFWwindow *w, int key, int scancode, int action, int mods)
{
if ((key == GLFW_KEY_ESCAPE || key == GLFW_KEY_Q) && action == GLFW_PRESS)
glfwSetWindowShouldClose(w, GL_TRUE);
}
void drawCircle(float cx, float cy, float radius)
{
float num_segments = 360;
float theta = 2 * 3.1415926 / num_segments;
float c = cosf(theta);//precalculate the sine and cosine
float s = sinf(theta);
float t;
float x = radius;//we start at angle = 0
float y = 0;
glBegin(GL_TRIANGLE_FAN);
glColor3f(1, 0, 1);
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();
}
void drawTarget(float cx, float cy, float radius)
{
float num_segments = 360;
float theta = 2 * 3.1415926 / num_segments;
float c = cosf(theta);//precalculate the sine and cosine
float s = sinf(theta);
float t;
float x = radius;//we start at angle = 0
float y = 0;
glBegin(GL_LINE_LOOP);
glColor3f(1, 1, 1);
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();
}
int main(void)
{
int i;
float e[3] = {140,120,100};
float m[3] = {90,80,70};
float h[3] = {60,50,40};
glfwSetErrorCallback(error);
if (!glfwInit())
exit(EXIT_FAILURE);
w = glfwCreateWindow(1280, 720, "AxTest", NULL, NULL);
if (!w)
{
glfwTerminate();
return 1;
}
glfwMakeContextCurrent(w);
glfwSetKeyCallback(w, key_callback);
glfwGetFramebufferSize(w, &fb_width, &fb_height);
do{
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, fb_width, fb_height, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(0,1,0);
drawCircle(1280 * 0.50, 720 * 0.25,e[2]);
glPopMatrix();
glPushMatrix();
glTranslatef(0,0,0);
drawTarget(1280 * 0.50, 720 * 0.75,50);
glPopMatrix();
glfwSwapBuffers(w);
glfwPollEvents();
}
while (!glfwWindowShouldClose(w));
glfwDestroyWindow(w);
glfwTerminate();
exit(EXIT_SUCCESS);
return 0;
}
The values for the vertex positions with which you draw your circles are in the order of hundreds (likely, because you want to address pixels as indicated by the values for the projection matrix). But glTranslates sees only a small number, so the shift is miniscule (one pixel) and hence you think nothing did happen. If you rewrite your code so that you don't specify the circle/target center by explicit modification of the vertex position offset it'd be clearer.
void drawCircle(float radius)
{
/* ... */
for(int ii = 0; ii < num_segments; ii++)
{
glVertex2f(x, y); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
/* ... */
}
void drawTarget(float radius)
{
/* ... */
for(int ii = 0; ii < num_segments; ii++)
{
glVertex2f(x, y); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
/* ... */
}
int main(void)
{
/* ... */
glPushMatrix();
glTranslatef(1280*0.50, 720*0.25, 0);
drawCircle(e[2]);
glPopMatrix();
glPushMatrix();
glTranslatef(1280 * 0.50, 720 * 0.25, 0);
drawTarget(50);
glPopMatrix();
/* ... */
}
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, fb_width, fb_height, 0, 0, 1);
You don't have to make the projection matrix at every loop, put it before the loop.
Then the error you have is surely due to :
glMatrixMode(GL_MODELVIEW);
// it miss glLoadIdentity() here
glPushMatrix();
glTranslatef(0,1,0);

Picking in OpenGL using glRenderMode(GL_SELECT) and glReadPixels

I'm trying to do selection in opengl but it is not working.
I draw objects I receive from a .obj file (v, vn, f, o and such indices). Each object consists from "groups" and each group is a group of GL_POLYGON.
Here is the draw function:
void draw(GLenum mode) {
glBegin(GL_LINES);
glColor3f(1, 0, 0);
glVertex3f(0.0, 0.0, 0.0);
glVertex3f(100.0, 0.0, 0.0);
glColor3f(0, 0, 1);
glVertex3f(0.0, 0.0, 0.0);
glVertex3f(0.0, 100.0, 0.0);
glColor3f(0, 1, 0);
glVertex3f(0.0, 0.0, 0.0);
glVertex3f(0.0, 0.0, 100.0);
glEnd();
glColor4f(1.0, 1.0, 1.0, 1.0);
if (changeFOV) {
fovAngle += fovScale;
changeFOV = false;
setTransformations();
}
for (unsigned int i = 0; i < objects.size(); i++) {
objectItem currObject = objects[i];
for (unsigned int j = 0; j < currObject.getGroups().size(); j++) {
group currGroup = currObject.getGroups().at(j);
for (unsigned int k = 0; k < currGroup.getFs().size(); k++) {
if (mode == GL_SELECT)
glPushName(currGroup.getName());
glPushMatrix();
vector<pair<int, int> > currF = currGroup.getFs()[k];
glBegin(GL_POLYGON);
for (unsigned int kk = 0; kk < currF.size(); kk++) {
Vector3f currVertex = vertexes.at(
(currF.at(kk).first - 1 >= 0) ?
currF.at(kk).first - 1 : 0);
Vector3f currNormal = vertexesNormal.at(
(currF.at(kk).second - 1 >= 0) ?
currF.at(kk).second - 1 : 0);
glNormal3f(currNormal.x, currNormal.y, currNormal.z);
glVertex3f(currVertex.x / 1, currVertex.y / 1,
currVertex.z / 1);
}
glEnd();
glPopMatrix();
}
}
}
}
The drawing works ok and I see the object on the screen.
This is all the picking procedure
/* PICKING */
void processHits(GLint hits, GLuint *buffer) {
float z1, z2;
for (int i = 0; buffer[i] > 0; i += 5) {
z1 = buffer[i + 1] / 4294967295.0;
z2 = buffer[i + 2] / 4294967295.0;
printf("z1 = %f ,z2 = %f zValue = %f\n", z1, z2, zValue[0]);
if ((zValue[0] <= z1 + 0.0001 && zValue[0] >= z2 - 0.0001)
|| (zValue[0] >= z1 - 0.0001 && zValue[0] <= z2 + 0.0001)) { //try to locate which name is correlated with the pressed pixel according to z value
ii = buffer[i + 3];
jj = buffer[i + 4];
}
}
}
void startPicking(GLuint *selectionBuf) {
glSelectBuffer(bufSize, selectionBuf); //declare buffer for input in selection mode
glRenderMode(GL_SELECT); //change to selecting mode
glInitNames(); //initialize names stack
glPushName(-1); //push name
}
void pick(int button, int x, int y) {
//use selection mode to pick
glReadPixels(x, viewport[3] - y, 1, 1, GL_RGBA, GL_FLOAT, pix);
//printf("depth = %f, x = %d, y = %d\n",pixels[(viewport[3]-y)*512+x],x,viewport[3]-y);
glMatrixMode(GL_PROJECTION);
glReadPixels((GLdouble) x, (GLdouble) viewport[3] - y, 2, 2,
GL_DEPTH_COMPONENT, GL_FLOAT, zValue);
glPushMatrix(); //saves current projection matrix
startPicking(selectionBuf); //preper selection mode
glLoadIdentity();
gluPickMatrix((GLdouble) x, (GLdouble) viewport[3] - y, 1, 1, viewport); //change matrices so only the area of the picking pixel can be seen.
gluPerspective(fovAngle, 1, near, far); //return to perspective state
glMatrixMode(GL_MODELVIEW);
draw(GL_SELECT); //draws board on background
hits = glRenderMode(GL_RENDER); //gets hits number
glMatrixMode(GL_PROJECTION);
glPopMatrix(); //restores projection matrix
glMatrixMode(GL_MODELVIEW);
processHits(hits, selectionBuf); //check hits
if(hits > 0)
printf("touched: %d\n",selectionBuf[3]);
//printf("depth %f hits: %d\n\n",pixels[(viewport[3]-y)*512+x], hits);
if (zValue[0] < 1.0) {
isPick = true;
xx = x;
yy = y;
if (button == GLUT_RIGHT_BUTTON)
zMove = true;
else
zMove = false;
}
}
the pick function is called when the mouse is clicked (using opengl mouse function).
The error I'm receiving is that no objects appears to be hit when clicking on an object.
I'm using Ubuntu 14.04 LTS with Opengl 3.0
I don't know how to ask or what specifically ask, I would appreciate some inputs on the code if you see something wrong..
You seemed to have missed using glPopName()
GLNames used in the selection buffer are pushed on a stack. So unless you call glPopName() the stack would never unwind. The working of this is similar to the calls glPushMatrix() and glPopMatrix().
Typically this is what the code flow looks like.
//Push the name of the primitives on top of selection stack
glPushName(...)
//Set Transformations / Draw the primitives
..
..
//Pop the name (Clear the stack for pushing another name)
glPopName();

gluDisk rotation for mapping

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 ...