/* Copyright (c) Mark J. Kilgard, 1997. */
/* This program is freely distributable without licensing fees and is
provided without guarantee or warrantee expressed or implied. This
program is -not- in the public domain. */
/* Unix compile line: cc -o shadowfun shadowfun.c -lglut -lGLU -lGL -lXmu -lXext -lX11 -lm */
/* THIS PROGRAM REQUIRES GLU 1.2. If you have IRIX 5.3, you need patch 1449
(the IRIX 5.3 GLU 1.2 functionality patch) or its successor to use this
program. GLU 1.2 is standard on IRIX 6.2 and later. */
/* This program demonstrates a light source and object of arbitrary geometry
casing a shadow on arbitary geometry. The program uses OpenGL's feedback,
stencil, and boundary tessellation support. */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <GL/glut.h>
#ifdef GLU_VERSION_1_2
/* Win32 calling conventions. */
#ifndef CALLBACK
#define CALLBACK
#endif
/* Some <math.h> files do not define M_PI... */
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
const float uniquePassThroughValue = 34567.0;
#define SmallerOf(a,b) ((a) < (b) ? (a) : (b))
int stencilBits;
/* Display list names. */
enum {
/* Display lists should start at 1, not 0. */
DL_BALL = 1, DL_CONE, DL_LIGHT, DL_SHADOW_VOLUME, DL_SPHERE,
DL_ICO, DL_TORUS, DL_CUBE, DL_SHADOW_VOLUME_TOP, DL_BASE_SHADOW_VOLUME
};
/* Menu option names. */
enum {
/* Important for objectMaxRadius array that the shape enums appear first in
this list. */
M_TORUS, M_CUBE, M_SPHERE, M_ICO, M_DOUBLE_TORUS, M_ANGLE, M_BOUNDARY,
M_NO_SHADOW, M_NO_LIGHT, M_FRONT_VOLUME, M_BACK_VOLUME, M_SHADOW,
M_LIGHT_SOURCE_VIEW, M_NORMAL_VIEW, M_SPIN, M_SWING, M_STOP
};
/* Coordinates. */
enum {
X, Y, Z
};
const int TEXDIM = 64;
int shape;
GLfloat maxRadius;
int renderMode = M_SHADOW;
int view = M_NORMAL_VIEW;
int renderBoundary = 0;
GLfloat angle = 0.0;
int frontFace = 1;
int rotatingObject = 1;
int swingingLight = 1;
float swingTime = M_PI / 2.0;
GLfloat lightDiffuse[4] =
{1.0, 0.0, 0.0, 1.0};
GLfloat lightPos[4] =
{60.0, 50.0, -350.0, 1.0};
GLfloat objectPos[4] =
{40.0, 30.0, -360.0, 1.0};
GLfloat sceneEyePos[4] =
{0.0, 0.0, 0.0, 0.0};
struct VertexHolder {
struct VertexHolder *next;
GLfloat v[2];
};
typedef struct _ShadowVolumeMemoryPool {
/* Reference count because ShadowVolumeMemoryPool's can be shared between
multiple ShadowVolumeState's. */
int refcnt;
GLUtesselator *tess;
GLfloat viewScale;
/* Memory used for GLU tessellator combine callbacks. */
GLfloat *combineList;
int combineListSize;
int combineNext;
struct VertexHolder *excessList;
} ShadowVolumeMemoryPool;
typedef struct _ShadowVolumeState {
ShadowVolumeMemoryPool *pool;
GLfloat shadowProjectionDistance;
GLfloat extentScale;
/* Scratch variables used during GLU tessellator callbacks. */
int saveFirst;
GLfloat *firstVertex;
} ShadowVolumeState;
ShadowVolumeState *svs;
static void CALLBACK
begin(GLenum type, void *shadowVolumeState)
{
ShadowVolumeState *svs = (ShadowVolumeState *) shadowVolumeState;
assert(type == GL_LINE_LOOP);
if (renderBoundary) {
glBegin(type);
} else {
svs->saveFirst = 1;
glBegin(GL_TRIANGLE_FAN);
glColor3f(0, 1, 0);
glVertex3f(0.0, 0.0, 0.0);
}
}
static void CALLBACK
vertex(void *data, void *shadowVolumeState)
{
ShadowVolumeState *svs = (ShadowVolumeState *) shadowVolumeState;
GLfloat *v = data;
if (renderBoundary) {
glVertex2fv(v);
} else {
if (svs->saveFirst) {
svs->firstVertex = v;
svs->saveFirst = 0;
}
glColor3f(0, 0, 1);
glVertex3f(svs->extentScale * v[X], svs->extentScale * v[Y],
svs->shadowProjectionDistance);
}
}
static void CALLBACK
end(void *shadowVolumeState)
{
ShadowVolumeState *svs = (ShadowVolumeState *) shadowVolumeState;
if (!renderBoundary) {
glColor3f(0, 0, 1);
glVertex3f(svs->extentScale * svs->firstVertex[X], svs->extentScale * svs->firstVertex[Y],
svs->shadowProjectionDistance);
}
glEnd();
}
static void
freeExcessList(ShadowVolumeMemoryPool * pool)
{
struct VertexHolder *holder, *next;
holder = pool->excessList;
while (holder) {
next = holder->next;
free(holder);
holder = next;
}
pool->excessList = NULL;
}
/* ARGSUSED1 */
static void CALLBACK
combine(GLdouble coords[3], void *d[4], GLfloat w[4], void **dataOut, void *shadowVolumeState)
{
ShadowVolumeState *svs = (ShadowVolumeState *) shadowVolumeState;
ShadowVolumeMemoryPool *pool = svs->pool;
struct VertexHolder *holder;
GLfloat *newCoords;
if (pool->combineNext >= pool->combineListSize) {
holder = (struct VertexHolder *) malloc(sizeof(struct VertexHolder));
holder->next = pool->excessList;
pool->excessList = holder;
newCoords = holder->v;
} else {
newCoords = &pool->combineList[pool->combineNext * 2];
}
newCoords[0] = coords[0];
newCoords[1] = coords[1];
*dataOut = newCoords;
pool->combineNext++;
}
static void CALLBACK
error(GLenum errno)
{
printf("ERROR: %s\n", gluErrorString(errno));
}
static void
processFeedback(GLint size, GLfloat * buffer, ShadowVolumeState * svs)
{
ShadowVolumeMemoryPool *pool = svs->pool;
GLfloat *loc, *end, *eyeLoc;
GLdouble v[3];
int token, nvertices, i;
GLfloat passThroughToken;
int watchingForEyePos;
if (pool->combineNext > pool->combineListSize) {
freeExcessList(pool);
pool->combineListSize = pool->combineNext;
pool->combineList = realloc(pool->combineList, sizeof(GLfloat) * 2 * pool->combineListSize);
}
pool->combineNext = 0;
watchingForEyePos = 0;
eyeLoc = NULL;
glColor3f(1, 1, 1);
gluTessBeginPolygon(pool->tess, svs);
loc = buffer;
end = buffer + size;
while (loc < end) {
token = *loc;
loc++;
switch (token) {
case GL_POLYGON_TOKEN:
nvertices = *loc;
loc++;
assert(nvertices >= 3);
gluTessBeginContour(pool->tess);
for (i = 0; i < nvertices; i++) {
v[0] = loc[0];
v[1] = loc[1];
v[2] = 0.0;
gluTessVertex(pool->tess, v, loc);
loc += 2;
}
gluTessEndContour(pool->tess);
break;
case GL_PASS_THROUGH_TOKEN:
passThroughToken = *loc;
if (passThroughToken == uniquePassThroughValue) {
watchingForEyePos = !watchingForEyePos;
} else {
/* Ignore everything else. */
fprintf(stderr, "ERROR: Unexpected feedback token 0x%x (%d).\n", token, token);
}
loc++;
break;
case GL_POINT_TOKEN:
if (watchingForEyePos) {
fprintf(stderr, "WARNING: Eye point possibly within the shadow volume.\n");
fprintf(stderr, " Program should be improved to handle this.\n");
/* XXX Write code to handle this case. You would need to determine
if the point was instead any of the returned boundary polyons.
Once you found that you were really in the clipping volume, then I
haven't quite thought about what you do. */
eyeLoc = loc;
watchingForEyePos = 0;
} else {
/* Ignore everything else. */
fprintf(stderr, "ERROR: Unexpected feedback token 0x%x (%d).\n",
token, token);
}
loc += 2;
break;
default:
/* Ignore everything else. */
fprintf(stderr, "ERROR: Unexpected feedback token 0x%x (%d).\n",
token, token);
}
}
gluTessEndPolygon(pool->tess);
if (eyeLoc && renderBoundary) {
glColor3f(0, 1, 0);
glPointSize(7.0);
glBegin(GL_POINTS);
glVertex2fv(eyeLoc);
glEnd();
}
}
/* Three element vector dot product. */
static GLfloat
vdot(const GLfloat * v1, const GLfloat * v2)
{
- 1
- 2
- 3
前往页