OpenGL image disappears on selection if the OS is not Linux - c

I need to check if an object or an empty space has been clicked in OpenGL. I capture the click event in the MouseCallback function which passes the x and y click coordinates to ProcessSelection. The function is:
void MouseCallback(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
ProcessSelection(x, y);
}
Then I coordinates process the selection coordinates in ProcessSelection:
#define BUFFER_LENGTH 64
void ProcessSelection(int xPos, int yPos)
{
printf("xpos: %d\n", xPos);
printf("ypos: %d\n", yPos);
GLfloat fAspect; // Screen aspect ratio
GLuint selectBuff[BUFFER_LENGTH];
GLint viewport[4];
glSelectBuffer(BUFFER_LENGTH, selectBuff);
glGetIntegerv(GL_VIEWPORT, viewport);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glRenderMode(GL_SELECT);
glLoadIdentity();
gluPickMatrix(xPos, viewport[3] - yPos, 4, 4, viewport);
fAspect = (float)(viewport[2]) / (float)viewport[3];
gluPerspective(120, fAspect, 1, 300);
display();
// Restore the projection matrix
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
hits = glRenderMode(GL_RENDER);
if (hits >= 1)
enable_animation = !enable_animation;
else
glutSetWindowTitle("You clicked empty space!");
}
The gluPerspective() is the exact same function that is called in the reshape function. I noticed that it works well in Linux but not in Windows and Mac OS X where the image disappears at every click. I can't understand why. Can someone help me?

Related

Boundary fill incorrectly stops recursion?

I have been trying to execute a simple boundary fill program on my m1 mac using visual studio code where I had the setup, included all the libraries configuring default build task and the build is getting done nicely.
But the issue is when the window is visible, the program has a mouse click event listener, on click it should start region filling with desired color, but it seems to be stopping after drawing one line only.
Here is my program,
#include<stdio.h>
#include <GLUT/glut.h>
int xmin, ymin, xmax, ymax; //Polygon boundaries
float FillColor[3] = {1.0, 0.0, 0.0}; //Color to be filled - red
float BorderColor[3] = {0.0, 0.0, 0.0}; // Border color of polygon - black
void setPixel(int x, int y)
{
glBegin(GL_POINTS);
glColor3fv(FillColor);
glVertex2i(x, y);
glEnd();
glFlush();
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
//Drawing polygon
glColor3fv(BorderColor);
glLineWidth(6);
glBegin(GL_LINES);
glVertex2i(xmin, ymin);
glVertex2i(xmin, ymax);
glEnd();
glBegin(GL_LINES);
glVertex2i(xmax, ymin);
glVertex2i(xmax, ymax);
glEnd();
glBegin(GL_LINES);
glVertex2i(xmin, ymin);
glVertex2i(xmax, ymin);
glEnd();
glBegin(GL_LINES);
glVertex2i(xmin, ymax);
glVertex2i(xmax, ymax);
glEnd();
glFlush();
}
void BoundaryFill(int x,int y)
{
float CurrentColor[3];
glReadPixels(x, y, 1.0, 1.0, GL_RGB, GL_FLOAT, CurrentColor);
// if CurrentColor != BorderColor and CurrentColor != FillColor
if((CurrentColor[0] != BorderColor[0] && (CurrentColor[1]) != BorderColor[1] &&
(CurrentColor[2])!= BorderColor[2]) && (CurrentColor[0] != FillColor[0] &&
(CurrentColor[1]) != FillColor[1] && (CurrentColor[2]) != FillColor[2]))
{
setPixel(x, y);
BoundaryFill(x+1, y);
BoundaryFill(x-1, y);
BoundaryFill(x, y+1);
BoundaryFill(x, y-1);
//Using 4-connected approach, remove comment from below lines to make it 8-connected approach
BoundaryFill(x+1, y+1);
BoundaryFill(x+1, y-1);
BoundaryFill(x-1, y+1);
BoundaryFill(x-1, y-1);
}
}
void mouse(int btn, int state, int x, int y)
{
if(btn == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
{
printf("%d, %d\n", x, y);
BoundaryFill(x, 500-y);
}
}
void init()
{
glClearColor(0.101, 1.0, 0.980, 1.0); //Background color - cyan
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0, 500, 0, 500);
}
int main(int argc, char** argv)
{
printf("Window size - 500x500 i.e. range of x and y is 0 -> 500\n");
printf("\nEnter polygon boundaries:-\n");
printf("Enter xmin: ");
scanf("%d", &xmin);
printf("Enter ymin: ");
scanf("%d", &ymin);
printf("Enter xmax: ");
scanf("%d", &xmax);
printf("Enter ymax: ");
scanf("%d", &ymax);
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(500, 500);
glutCreateWindow("Boundary-Fill Algorithm");
init();
glutDisplayFunc(display);
glutMouseFunc(mouse);
glutMainLoop();
return 0;
}
Please check the image -- where after this stage, the recursion stops which meant to be filling more pixels in 8 connected approach.
The orthographic projection covers an area of [0, 500]^2 pixels, whereas the algorithm assumes an area of [0, 500)^2 pixels. Note the difference between the inclusive range and the exclusive range.
This may cause glReadPixels to read the value of the pixel left (and/or down) of where you'd expect it to read, which breaks the algorithm. To solve this, use different dimensions for glOrtho2D:
gluOrtho2D(0, 499, 0, 499);
Additionally, note that the width and height parameters of glReadPixels should be integers, i.e.:
glReadPixels(x, y, 1, 1, GL_RGB, GL_FLOAT, CurrentColor);
and that it is not a good idea to compare float values directly (see elsewhere on StackOverflow). Instead, use something like this:
#include <math.h>
int isEqualFloat(float x, float y)
{
return fabs(x - y) < 0.001F;
}
I'm assuming the use of glReadPixels is here for educational purposes. A better approach would be to store a boolean matrix for "visited" pixels, which avoids the need for comparing colors in floating point values and retrieving data from GPU memory.
Lastly, using a recent Mac may have influence. For some low resolution program, the OS decides to repeat every pixel to avoid having a very small window. However, this effectively doubles the resolution without OpenGL knowing about it.

Getting the mouse position when ortho is set from -1.0 to 1.0

I'm new to OpenGL/GLUT using c. I want to implement a button that has a callback when a user clicks it. To understand that better, I have a simple program that draws a dot where the mouse is clicked.
Here's the code
#include <freeglut.h>
GLint mousePressed = 0;
GLfloat mouseX, mouseY;
GLint windowHieght = 400;
GLint windowWidth = 500;
void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);
if (mousePressed)
{
// draw the dot
glBegin(GL_POINTS);
// draw the vertex at that point
glVertex2f(mouseX, mouseY);
glEnd();
}
glFlush();
}
void myMouseButton(int button, int state, int x, int y)
{
if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
exit(0);
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
{
mousePressed = 1;
mouseX = (GLfloat)x / (GLfloat)windowWidth;
mouseY = (GLfloat)windowHieght - (GLfloat)y;
mouseY = mouseY / (GLfloat)windowHieght;
glutPostRedisplay();
}
void main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB);
glutInitWindowSize(windowWidth, windowHieght);
glutInitWindowPosition(100, 150);
glutCreateWindow("dots");
gluOrtho2D(0.0, 1.0, 0.0, 1.0);
glutDisplayFunc(myDisplay);
glutMouseFunc(myMouseButton);
initializeGL();
glutMainLoop();
}
Everything works as expected, but when I change the ortho to (-1.0,1.0,-1.0,1.0), I don't get the same result. How can I get the same behavior?
Your myMouseButton function relies on the orthographic projection being 0-1 on both axes. Since you have changed that, you need to change the math in this function too.
To be very brief, your new orthographic co-ordinate range can be thought of as the old range scaled by 2 and then having 1 subtracted...
[0, 1] * 2 - [1, 1] => [-1, 1]
So you just need to do the same thing to your existing mouse co-ordinate equations.
mouseX = mouseX * 2.0f - 1.0f;
mouseY = mouseY * 2.0f - 1.0f;

Get mouse click coordinates in OpenGL during animation?

I want to make a game in which when somebody clicks on the moving ball, it bursts. I have added the codes for animation and the mouse click event, but when the animation is going on, the click function is not working. When I tried it without the animation, it worked properly. I want to know why is this happening.
#include<stdio.h>
#include<GL/glut.h>
#include<unistd.h>
#include<math.h>
int x, y;
float mx, my;
float i, j;
void mouse(int button, int state, int mousex, int mousey)
{
if(button==GLUT_LEFT_BUTTON && state==GLUT_DOWN)
{
mx = mousex;
my = mousey;
printf("%f %f\n",mx,my);
glutPostRedisplay();
}
}
void init()
{
glClearColor(0.0, 0.0, 0.0, 1.0);
glColor3f(0.0, 1.0, 0.0);
glPointSize(1.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, 1560, 0, 840);
}
int randValue()
{
int i = rand();
int num = i%1000;
return num;
}
void blast(int x, int y)
{
glBegin(GL_POLYGON);
glColor3f(1.0f,0.0f,0.0f);
glVertex2i(x-100, y-100);
glVertex2i(x, y-100);
glVertex2i(x-22, y-20);
glVertex2i(x-100, y-30);
glVertex2i(x-30, y-40);
glVertex2i(x-150, y-80);
glVertex2i(x-20, y);
glVertex2i(x, y-40);
glVertex2i(x-66, y-125);
glVertex2i(x-34, y-32);
glVertex2i(x-32, y-55);
glVertex2i(x-32, y);
glVertex2i(x-60, y-57);
glVertex2i(x-75, y-69);
glVertex2i(x-100, y);
glEnd();
glFlush();
}
void display()
{
int j = 0, k = 0, l = 1;
while(1)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0, 1.0, 0.0);
glBegin(GL_POINTS);
for (i = 0;i < 6.29;i += 0.001)
{
x = 100 * cos(i);
y = 100 * sin(i);
glVertex2i(x / 2 + j, y / 2 + k);
if((x / 2 + j) >= 1560 || (y / 2 + k) >= 840)
{
glEnd();
glFlush();
glClear(GL_COLOR_BUFFER_BIT);
blast(x / 2 + j, y / 2 + k);
sleep(2);
j = randValue();
k = 0;
}
}
j = j + 3;
k = k + 5;
glEnd();
glFlush();
}
}
int main (int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(1360, 768);
glutInitWindowPosition(0, 0);
glutCreateWindow("{Project}");
init();
glutDisplayFunc(display);
glutMouseFunc(mouse);
glutMainLoop();
}
Your code has an infinite loop inside the display function, thus you never give the control back to GLUT. GLUT already has an infinite loop like that inside glutMainLoop.
Instead you shall render only ONE frame in display, post glutPostRedisplay and return:
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
// ... draw the frame here ...
// for exmaple:
i += 0.001;
float x = 100 * cos(i);
float y = 100 * sin(i);
glColor3f(0.0, 1.0, 0.0);
glBegin(GL_POINTS);
glVertex2f(x, y);
glEnd();
glFlush();
glutPostRedisplay();
}
Then your mouse function will be called and you'll be able to update the state as necessary.
There are two problems here:
OpenGL has no support for an input device by itself, you normally use OpenGL to present information but you have something else attached to the window where you present the info that is what gives you mouse access. this involves to know which is the other environment you are using that offers you a pointing device into the screen area.
if you have the window mouse coordinates you need to map well on the window you present your OpenGL output, but you have to convert them back to some point in your scene, but probably your ball is not there. There's some ambiguity when passing from a plane image representing a 3D scene to a point in that scene in 3D, as you have all points in the Z axis sharing the same screen coordinates in 2D screen. so you have to trace back to the possible position of the ball from the point of view (the camera), based on the window coordinates of the mouse. This is a geometrical problem that involves the inverse transformation of a projection, that is always singular.
you can solve this without having to guess, as you know where your ball is, you can redo the transformation that made it to appear in the two dimensional window, and then compare coordinates based on those. OpenGL allows you to know the actual transformation it is doing to represent your scene, and you can use it to see where in the screen your ball is represented (you don't need to do this for every vertex of the ball, only for the center, for example) and then check if your shot has gone close enough to hit the ball. You should consider also if some other object upper in the Z axis is in the way, so you don't kill anybody behind a wall.

Mouse click changing the screen face in OpenGL

I'm programming a code that when the user click on the screen It marks a point and when he continues clicking on other points on the screen It'll be linking the points with lines.
I don't know why but I'm working with two different screen plan. When I click with the mouse It marks a point in the screen that I only see when I'm with the left mouse button clicked and when the button isn't clicked I see a clean screen.
My code that marks the points and link them with lines:
void exerciseThree(int x, int y){
write("Exercise Three", -5, 18);
float cx = 0, cy = 0;
if(x != 0 && y != 0 && isFinished == false){
glPushMatrix();
glPointSize(6.0f);
//Draw the points
glBegin(GL_POINTS);
cx = ((x * (maxx - minx)) / width) + minx;
cy = (((height - y) * (maxy - miny)) / height) + miny;
glVertex2f(cx , cy);
glEnd();
if(lastCx != 0 && lastCy != 0){
glBegin(GL_LINE_STRIP);
glVertex2f(lastCx , lastCy);
glVertex2f(cx , cy);
glEnd();
}
lastCx = cx;
lastCy = cy;
glPopMatrix();
}
write("Press 0 (zero) to come back.", -10, -18);
}
The mouse function:
void mouse(int button, int state, int x, int y){
switch(button){
case GLUT_LEFT_BUTTON:
if( option == 3){
exerciseThree(x, y);
projecao();
glutSwapBuffers();
}
break;
}
}
I know that I should handle the GLUT_DOWN and GLUT_UP of the mouse, but does exist a way of work with only one screen face?
You're only seeing something on the screen when you click because that's the only time you are drawing to and updating the buffer. You should just update the list of x/y coordinates when the mouse is clicked. However, you should draw the points and call glutSwapBuffers() every time in your main loop so that they are always on screen regardless of a button press.
The flow should be something like the following pseudo code:
ArrayOfPoints[];
while(Running)
{
CheckForMouseInput(); // Update array of points to draw if pressed
DrawPoints(); // Use same code draw code from exerciseThree()
glutSwapBuffers(); // Update video buffer
}

Problems when dealing with lighting and shadowing in opengl

I am trying to put lights, materials and shadows to my robot arm but unfortunately something weird happens (please compile or see the below picture), now I am still annoying by
1) Not showing correct lighting and reflection properties as well as material properties
2) No shadow painted, although I have done the shadow casting in function "void showobj(void)"
I appreciate if anyone can help, I have already working for it 2 days with no progress :(
The following is my code
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <math.h>
#include <GL/glut.h>
#include "gsrc.h"
#include <Windows.h>
const double PI = 3.14159265;
// angles to rotate the base, lower and upper arms of the robot arm
static GLfloat theta, phi, psi = 0.0;
//Starting time
double startT;
//Time Diff variable
double dif,startTime,endTime,deltaT;
//define n
double n = 3;
//Set the parameters of the light number one
GLfloat Xs = 35.0;
GLfloat Ys = 35.0;
GLfloat Zs = 35.0;
//Shadow color
GLfloat shadowcolor[] = {0.0,0.0,0.0};
//initialize the window and everything to prepare for display
void init_gl() {
//set display color to white
glClearColor(1,1,1,0);
//clear and enable z-buffer
glClear (GL_DEPTH_BUFFER_BIT);
glEnable (GL_DEPTH_TEST);
//clear display window
glClear(GL_COLOR_BUFFER_BIT);
}
//Draw the base of the robot arm
void draw_base(){
glPushMatrix();
//to create the quadric objects
GLUquadric *qobj,*qobjl,*qobju;
qobj = gluNewQuadric();
qobjl = gluNewQuadric();
qobju = gluNewQuadric();
//set the color of the cylinder
glColor3f(1.0,0.0,0.0);
//Re-position the cylinder (x-z plane is the base)
glRotatef(-90,1.0,0.0,0.0);
//Draw the cylinder
gluCylinder(qobj, 30.0, 30.0, 40.0, 40.0, 40.0);
//Draw the upper disk of the base
gluDisk(qobju,0,30,40,40);
glPushMatrix();
//Change the M(lowdisk<updisk)
glTranslatef(0,0,40);
glColor3f(0,0,0);
//Draw the lower disk of the base
gluDisk(qobjl,0,30,40,40);
glPopMatrix();
glPopMatrix();
}
/***********************Texture Work Starts************************************/
//Load the raw file for texture
/* Global Declarations */
#define IW 256 // Image Width
#define IH 256 // Image Height
//3D array to store image data
unsigned char InputImage [IW][IH][4];
// Read an input image from a .raw file with double
void ReadRawImage ( unsigned char Image[][IH][4] )
{
FILE *fp;
int i, j, k;
char* filename;
unsigned char temp;
filename = "floor.raw";
if ((fp = fopen (filename, "rb")) == NULL)
{
printf("Error (ReadImage) : Cannot read the file!!\n");
exit(1);
}
for ( i=0; i<IW; i++)
{
for ( j=0; j<IH; j++)
{
for (k = 0; k < 3; k++) // k = 0 is Red k = 1 is Green K = 2 is Blue
{
fscanf(fp, "%c", &temp);
Image[i][j][k] = (unsigned char) temp;
}
Image[i][j][3] = (unsigned char) 0; // alpha = 0.0
}
}
fclose(fp);
}
/****************************Texture Work Ends***************************************/
/****************************Light and Shadows***************************************/
void lightsrc(){
GLfloat light1PosType [] = {Xs, Ys, Zs, 1.0};
//GLfloat light2PosType [] = {0.0, 100.0, 0.0, 0.0};
glLightfv(GL_LIGHT1, GL_POSITION, light1PosType);
//glEnable(GL_LIGHT1);
//glLightfv(GL_LIGHT2, GL_POSITION, light2PosType);
//glEnable(GL_LIGHT2);
GLfloat whiteColor[] = {1.0, 1.0, 1.0, 1.0};
GLfloat blackColor[] = {0.0, 0.0, 0.0, 1.0};
glLightfv(GL_LIGHT1, GL_AMBIENT, blackColor);
glLightfv(GL_LIGHT1, GL_DIFFUSE, whiteColor);
glLightfv(GL_LIGHT1, GL_SPECULAR, whiteColor);
glEnable(GL_LIGHT1);
glEnable( GL_LIGHTING );
}
/****************************Light and Shadows work ends***************************************/
//Draw the 2x2x2 cube with center (0,1,0)
void cube(){
glPushMatrix();
glTranslatef(0,1,0);
glutSolidCube(2);
glPopMatrix();
}
//Draw the lower arm
void draw_lower_arm(){
glPushMatrix();
glScalef(15.0/2.0,70.0/2.0,15.0/2.0);//scale half is enough (some part is in the negative side)
cube();
glPopMatrix();
}
//Draw the upper arm
void draw_upper_arm(){
glPushMatrix();
glScalef(15.0/2.0,40.0/2.0,15.0/2.0);//scale half is enough (some part is in the negative side)
cube();
glPopMatrix();
}
void drawCoordinates(){
glBegin (GL_LINES);
glColor3f (1,0,0);
glVertex3f (0,0,0);
glVertex3f (600,0,0);
glColor3f (0,1,0);
glVertex3f (0,0,0);
glVertex3f (0,600,0);
glColor3f (0,0,1);
glVertex3f (0,0,0);
glVertex3f (0,0,600);
glEnd();
}
//To draw the whole robot arm
void drawRobot(){
//Robot Drawing Starts
//Rotate the base by theta degrees
glRotatef(theta,0.0,1.0,0.0);
//Draw the base
draw_base();
//M(B<La)
glTranslatef(0.0,40.0,0.0);
//Rotate the lower arm by phi degree
glRotatef(phi,0.0,0.0,1.0);
//change the color of the lower arm
glColor3f(0.0,0.0,1.0);
//Draw the lower arm
draw_lower_arm();
//M(La<Ua)
glTranslatef(0.0,70.0,0.0);
//Rotate the upper arm by psi degree
glRotatef(psi,0.0,0.0,1.0);
//change the color of the upper arm
glColor3f(0.0,1.0,0.0);
//Draw the upper arm
draw_upper_arm();
//Drawing Finish
glutSwapBuffers();
}
void showobj(void) {
//set the projection and perspective parameters/arguments
GLint viewport[4];
glGetIntegerv( GL_VIEWPORT, viewport );
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective( 45, double(viewport[2])/viewport[3], 0.1, 1000 );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(-200, 300, 200, 0, 0, 0, 0,1,0 );
// get the rotation matrix from the rotation user-interface
glMultMatrixf(gsrc_getmo() );
//Clear the display and ready to show the robot arm
init_gl();
//put the light source
lightsrc();
//Draw coordinates
drawCoordinates();
//give material properties
GLfloat diffuseCoeff[] = {0.2, 0.4, 0.9, 1.0}; // kdR= 0.2, kdG= 0.4, kdB= 0.9
GLfloat specularCoeff[] = {1.0, 1.0, 1.0, 1.0}; //
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, diffuseCoeff);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specularCoeff);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 100.0 ); // ns= 25
//Draw the ground floor
glColor3f(0.4,0.4,0.4);
glPushMatrix();
glRotatef(90,1,0,0);
glRectf(-500,-500,500,500);
glPopMatrix();
int i,j;
GLfloat M[4][4];
for (i=0; i<4; i++){
for (j=0; j<4; j++){
M[i][j] = 0;
}
M[0][0]=M[1][1]=M[2][2]=1;
M[2][3]=-1.0/Zs;
}
//Start drawing shadow
drawRobot(); // draw the objects
glPushMatrix( ); // save state
glMatrixMode(GL_MODELVIEW);
glTranslatef(Xs, Ys, Zs);// Mwc←s
glMultMatrixf(M[4]);// perspective project
glTranslatef(-Xs, -Ys, -Zs);// Ms←wc
glColor3fv (shadowcolor);
//Draw the robot arm
drawRobot();
glPopMatrix(); // restore state
//Shadow drawing ends
glFlush ();
}
//To animate the robot arm
void animate(void)
{
//get the end time
endTime = timeGetTime();
//float angle;
//calculate deltaT
deltaT = (endTime - startTime); //in msecs
//float test;
float deltaTSecs = deltaT/1000.0f; //in secs
//apply moving equation
psi = (90.0) * 0.50 * (1-cos((deltaTSecs/(n+1)) * PI));
glutPostRedisplay ();
}
void main (int argc, char** argv)
{
glutInit(&argc, argv);
//DOUBLE mode better for animation
// Set display mode.
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB);
glutInitWindowPosition( 50, 100 ); // Set top-left display-window position.
glutInitWindowSize( 400, 300 ); // Set display-window width and height.
glutCreateWindow( "Robot arm : my first self-learning opengl program" ); // Create display window.
// Register mouse-click and mouse-move glut callback functions
// for the rotation user-interface.
//Allow user to drag the mouse and view the object
glutMouseFunc( gsrc_mousebutton );
glutMotionFunc( gsrc_mousemove );
//record the starting time
startTime = timeGetTime();
// Display everything in showobj function
glutDisplayFunc(showobj);
//Perform background processing tasks or continuous animation
glutIdleFunc(animate);
glutMainLoop();
}
your screen flashes because you are calling glutSwapBuffers() in drawRobot(). That makes your screen repaint two times, once when you draw the robot, and once more when you draw the shadow. Also, you are missing glPushMatrix() at the beginning of drawRobot() and glPopMatrix() at the end. You need to put it there, otherwise it will affect rendering afterwards (the shadow will move with the upper link of the arm).
Then, you specify the shadow matrix wrong. Let's try this:
int i,j;
GLfloat M[4][4];
for (i=0; i<4; i++){
for (j=0; j<4; j++){
M[i][j] = 0;
}
}
M[0][0]=M[1][1]=M[2][2]=1;
M[2][3]=-1.0/Zs;
drawRobot(); // draw the objects
//Start drawing shadow
glEnable(GL_CULL_FACE);
glDisable(GL_LIGHTING); // want constant-color shadow
glPushMatrix( ); // save state
glMatrixMode(GL_MODELVIEW);
glTranslatef(Xs, Ys, Zs);// Mwc←s
glMultMatrixf(&M[0][0]);// perspective project
glTranslatef(-Xs, -Ys, -Zs);// Ms←wc
glColor3fv (shadowcolor);
//Draw the robot arm
drawRobot();
glPopMatrix(); // restore state
glDisable(GL_CULL_FACE);
glEnable(GL_LIGHTING); // enable again ...
//Shadow drawing ends
Also, you can see i've added GL_CULL_FACE arround the shadow, it is to avoid depth fighting. This more or less fixes it technically.
But still - the shadow position is calculated incorrectly. Let's try looking at projection shadows.
So first, we need to have position for the ground plane and for the light:
float g[] = {0, 1, 0, 0}; // ground plane
float l[] = {20, 300, 50, 1}; // light position and "1"
That is a plane equation and a homogenous light position (normal 3D position, padded with a "1"). Then you throw away your shadow matrix setup (glTranslatef(), glMultMatrixf() and glTranslatef()) and call myShadowMatrix(g, l) instead, so it becomes:
glPushMatrix( ); // save state
glMatrixMode(GL_MODELVIEW);
float g[] = {0, 1, 0, 0}; // ground plane
float l[] = {20, 300, 50, 1}; // light position and "1"
myShadowMatrix(g, l);
glColor3fv (shadowcolor);
//Draw the robot arm
drawRobot();
glPopMatrix(); // restore state
And that mostly does work. There is still a lot of z-fighting going on, and the shadow has four different colors. As for the colors, stop calling glColor3f() in drawRobot(), as for the z-fighting, use this:
glPolygonOffset(-1, -1);
glEnable(GL_POLYGON_OFFSET_FILL);
// before
// draw shadow
glDisable(GL_POLYGON_OFFSET_FILL);
// afterwards
And that makes one nice planar shadows demo :). Cheers ...
sw.

Resources