RE: QvLib questions

Jan Hardenbergh (jch@nell.oki.com)
Mon, 27 Mar 95 08:53:00 E


> I am trying to make a VMRL brower using the QvLib routines. I am a C
> programmer, NOT C++ (but I think I get the idea). My Question, I guess, is
> what
> do I do with the library/parser? Is it intented that I add my code to, say
> QvCube.c++, which makes a cube? I fill in the {} of:
>
> > QvCube::~QvCube()
> > {
> > }
>
> ?

What I did was to add code to QvTraverse. This is the startegy that uses the
QvLib code to maintain the scene database. The code and a warning are below:

An alternative: If you were going to do something
like generate PEX/PHIGS structures, then you would modify QvCube. Then you
would stop using the QvLib code when you were done parsing.

YON, jch@oki.com, Jan C. Hardenbergh, Oki Advanced Products 508-460-8655
http://www.oki.com/people/jch/ =|= 100 Nickerson Rd. Marlborough, MA 01776
Imagination is more important than knowledge - Albert Einstein (1879-1955)
=============================================================================
This code is incomplete and has various bugs. I wrote it as a "sandbox" to
explore things. It seems that REAL browsers are coming soon. I am no
longer working on this code.

This is QvTraverse.c++ and some support routines tacked on at the end.

/*
* OpenGL version of the VRML traverser. The rest of the VRML
* code can be found at ftp.eit.com /pub/vrml. Hopefully, a better
* version of this file will be found there, too.
*
* This code was written to optimize programmer/debug time. It reinits
* the rendering state for every primitive. This will kill performance.
* But, if people care they will by supported VRML browsers, or this
* function will get incorporated into standard browsers.
* Also, many things are not implemented yet, materialBinding,
* LevelOfDetail, Switch, ... If they were not in Sample.wrl or the
* copy of ...
*
************************************* 3 utility routines are needed.
* void InitView(void);
* void InitLights(void);
* void SetPerspectiveCamera( int, float [3], float [4], float,
* float, float, float, float );
*
* These are external because I think the application will be calling
* them. But it is pretty arbitrary.
*
* YON - jch@oki.com, Oki Advanced Products. http://www.oki.com/people/jch
*
* QvLib copyright and LICENSE:
*
* Copyright (c) 1991-95 Silicon Graphics, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the name of Silicon Graphics may not be used in any advertising or
* publicity relating to the software without the specific, prior written
* permission of Silicon Graphics.
*
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
THE
* POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR
IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include <QvElement.h>
#include <QvNodes.h>
#include <QvState.h>

#ifdef WIN32
#include <windows.h>
#endif
#include <GL/gl.h>
#include <GL/glu.h>

extern "C" {
extern int thePickFlag;
void RegisterAnchor(const char *);
void WeighAnchor(void);
void InitView(void);
void InitLights(void);
void SetPerspectiveCamera( int, float [3], float [4], float,
float, float, float, float );
int FollowAnchor(const char *, QvNode **);
}

////////////////////////////////////////////////////////////////////////////
//
//
// Traversal code for all nodes. The default method (in QvNode) does
// nothing. Because traverse() is defined in the header for ALL node
// classes, each one has an implementation here.
//
////////////////////////////////////////////////////////////////////////////
//

static int indexed = 0;
static int indexCheck = 103;
// For debugging
static int indent = 0;
static void
announce(const char *className)
{
for (int i = 0; i < indent; i++)
printf("\t");
printf("Traversing a %s\n", className);
}
#define ANNOUNCE(className) announce(QV__QUOTE(className))

#define DEFAULT_TRAVERSE(className) \
void \
className::traverse(QvState *) \
{ \
/* ANNOUNCE(className); */ \
}

////////////////////////////////////////////////////////////////////////////
//
//
// Groups.
//
////////////////////////////////////////////////////////////////////////////
//

void
QvGroup::traverse(QvState *state)
{
// ANNOUNCE(QvGroup);
indent++;
for (int i = 0; i < getNumChildren(); i++)
getChild(i)->traverse(state);
indent--;
}

void
QvLevelOfDetail::traverse(QvState *state)
{
// ANNOUNCE(QvLevelOfDetail);
indent++;

// ??? In a real implementation, this would choose a child based
// ??? on the projected screen areas.
if (getNumChildren() > 0)
getChild(0)->traverse(state);

indent--;
}

void
QvSeparator::traverse(QvState *state)
{
// ANNOUNCE(QvSeparator);
state->push();
indent++;
for (int i = 0; i < getNumChildren(); i++)
getChild(i)->traverse(state);
indent--;
state->pop();
}

void
QvSwitch::traverse(QvState *state)
{
// ANNOUNCE(QvSwitch);
indent++;

int which = whichChild.value;

if (which == QV_SWITCH_NONE)
;

else if (which == QV_SWITCH_ALL)
for (int i = 0; i < getNumChildren(); i++)
getChild(i)->traverse(state);

else
if (which < getNumChildren())
getChild(which)->traverse(state);

indent--;
}

void
QvTransformSeparator::traverse(QvState *state)
{
// ANNOUNCE(QvTransformSeparator);

// We need to "push" just the transformation stack. We'll
// accomplish this by just pushing a no-op transformation onto
// that stack. When we "pop", we'll restore that stack to its
// previous state.

QvElement *markerElt = new QvElement;
markerElt->data = this;
markerElt->type = QvElement::NoOpTransform;
state->addElement(QvState::TransformationIndex, markerElt);

indent++;
for (int i = 0; i < getNumChildren(); i++)
getChild(i)->traverse(state);
indent--;

// Now do the "pop"
while (state->getTopElement(QvState::TransformationIndex) != markerElt)
state->popElement(QvState::TransformationIndex);
}

////////////////////////////////////////////////////////////////////////////
//
//
// Properties.
//
////////////////////////////////////////////////////////////////////////////
//

#define DO_PROPERTY(className, stackIndex) \
void \
className::traverse(QvState *state) \
{ \
/* ANNOUNCE(className); */ \
QvElement *elt = new QvElement; \
elt->data = this; \
state->addElement(QvState::stackIndex, elt); \
}

#define DO_TYPED_PROPERTY(className, stackIndex, eltType) \
void \
className::traverse(QvState *state) \
{ \
/* ANNOUNCE(className); */ \
QvElement *elt = new QvElement; \
elt->data = this; \
elt->type = QvElement::eltType; \
state->addElement(QvState::stackIndex, elt); \
}

void
QvRotation::traverse(QvState *state)
{
QvElement *elt = new QvElement;
elt->data = this;
elt->type = QvElement::Rotation;
state->addElement(QvState::TransformationIndex, elt);
}

DO_PROPERTY(QvCoordinate3, Coordinate3Index)
DO_PROPERTY(QvMaterial, MaterialIndex)
DO_PROPERTY(QvMaterialBinding, MaterialBindingIndex)
DO_PROPERTY(QvNormal, NormalIndex)
DO_PROPERTY(QvNormalBinding, NormalBindingIndex)
DO_PROPERTY(QvShapeHints, ShapeHintsIndex)
DO_PROPERTY(QvTextureCoordinate2, TextureCoordinate2Index)
DO_PROPERTY(QvTexture2, Texture2Index)
DO_PROPERTY(QvTexture2Transform, Texture2TransformationIndex)

DO_TYPED_PROPERTY(QvDirectionalLight, LightIndex, DirectionalLight)
DO_TYPED_PROPERTY(QvPointLight, LightIndex, PointLight)
DO_TYPED_PROPERTY(QvSpotLight, LightIndex, SpotLight)

DO_TYPED_PROPERTY(QvOrthographicCamera, CameraIndex, OrthographicCamera)
DO_TYPED_PROPERTY(QvPerspectiveCamera, CameraIndex, PerspectiveCamera)

DO_TYPED_PROPERTY(QvTransform, TransformationIndex, Transform)
/* DO_TYPED_PROPERTY(QvRotation, TransformationIndex, Rotation) */
DO_TYPED_PROPERTY(QvMatrixTransform, TransformationIndex, MatrixTransform)
DO_TYPED_PROPERTY(QvTranslation, TransformationIndex, Translation)
DO_TYPED_PROPERTY(QvScale, TransformationIndex, Scale)

////////////////////////////////////////////////////////////////////////////
//
//
// Shapes.
//
////////////////////////////////////////////////////////////////////////////
//

/*
static void
printProperties(QvState *state)
{

printf("--------------------------------------------------------------\n");
state->print();

printf("--------------------------------------------------------------\n");
}
*/

/* quick way to use recursion to apply the matrices in reverse order */
static void
HandleXForm(QvElement *attribute)
{
if (attribute->next != NULL) HandleXForm(attribute->next);

switch (attribute->type) {
case QvElement::Translation: {
QvTranslation *t = (QvTranslation *)attribute->data;
glTranslatef(t->translation.value[0], t->translation.value[1],
t->translation.value[2] );
break;
}
case QvElement::Scale: {
QvScale *t = (QvScale *)attribute->data;
glScalef(t->scaleFactor.value[0], t->scaleFactor.value[1],
t->scaleFactor.value[2] );
break;
}
case QvElement::Rotation: {
QvRotation *t = (QvRotation *)attribute->data;
glRotatef(180*(t->rotation.angle)/M_PI, t->rotation.axis[0],
t->rotation.axis[1], t->rotation.axis[2] );
break;
}
} // end of switch
}

static void
loadProperties(QvState *state)
{
QvElement *attribute;


attribute = state->getTopElement(QvState::CameraIndex);
if (attribute != NULL) {
switch (attribute->type) {
case QvElement::PerspectiveCamera: {
QvPerspectiveCamera *pc = (QvPerspectiveCamera *)attribute->data;
int i;
float orient[4];
for (i = 0; i < 3; i++ ) { orient[i] = pc->orientation.axis[i];}
orient[3] = pc->orientation.angle;
SetPerspectiveCamera( 1, pc->position.value, orient, 1.0,
1.0, 1000.0, pc->focalDistance.value,
pc->heightAngle.value );
break;
}
case QvElement::OrthographicCamera: break;
}
}

attribute = state->getTopElement(QvState::TransformationIndex);
InitView();
if (attribute != NULL) {
HandleXForm(attribute);
}

attribute = state->getTopElement(QvState::LightIndex);
InitLights(); // this is screwed up if there are more lights
while (attribute != NULL) {

switch (attribute->type) {
GLfloat glfLightDir[4];
GLfloat glfLightColor[4];
int i;

case QvElement::DirectionalLight: {

QvDirectionalLight *dl = (QvDirectionalLight *)attribute->data;
if (dl->on.value != TRUE) break;
/* ignore intensity for right now */

for (i=0; i < 3; i++) glfLightColor[i] = dl->color.value[i];
glfLightColor[3] = 1.0f;
glLightfv (GL_LIGHT0, GL_DIFFUSE, glfLightColor);
glLightfv (GL_LIGHT0, GL_SPECULAR, glfLightColor);

/* a directional light is specified by placing the light
* infinitely far away from the scene - using w = 0.
* So, move the light in the opposite direction
*/
for (i=0; i < 3; i++) glfLightDir[i] = -dl->direction.value[i];
glfLightDir[3] = 0.0f;
glLightfv (GL_LIGHT0, GL_POSITION, glfLightDir);
break;
}
case QvElement::PointLight: {

QvPointLight *pl = (QvPointLight *)attribute->data;
if (pl->on.value != TRUE) break;
/* ignore intensity for right now */

for (i=0; i < 3; i++) glfLightColor[i] = pl->color.value[i];
glfLightColor[3] = 1.0f;
glLightfv (GL_LIGHT0, GL_DIFFUSE, glfLightColor);
glLightfv (GL_LIGHT0, GL_SPECULAR, glfLightColor);

for (i=0; i < 3; i++) glfLightDir[i] = pl->location.value[i];
glfLightDir[3] = 1.0f;
glLightfv (GL_LIGHT0, GL_POSITION, glfLightDir);
break;
}
case QvElement::SpotLight: {

QvSpotLight *sl = (QvSpotLight *)attribute->data;
if (sl->on.value != TRUE) break;
/* ignore intensity for right now */

for (i=0; i < 3; i++) glfLightColor[i] = sl->color.value[i];
glfLightColor[3] = 1.0f;
glLightfv (GL_LIGHT0, GL_DIFFUSE, glfLightColor);
glLightfv (GL_LIGHT0, GL_SPECULAR, glfLightColor);

for (i=0; i < 3; i++) glfLightDir[i] = sl->location.value[i];
glfLightDir[3] = 1.0f;
glLightfv (GL_LIGHT0, GL_POSITION, glfLightDir);

for (i=0; i < 3; i++) glfLightDir[i] = sl->direction.value[i];
glLightfv (GL_LIGHT0, GL_SPOT_DIRECTION, glfLightDir);
glLightf (GL_LIGHT0, GL_LINEAR_ATTENUATION, sl->dropOffRate.value);
glLightf (GL_LIGHT0, GL_SPOT_CUTOFF,
180*(sl->cutOffAngle.value)/M_PI);
break;
}
default:break;
}
attribute = attribute->next;
}

attribute = state->getTopElement(QvState::MaterialBindingIndex);
if (attribute != NULL) {

}

attribute = state->getTopElement(QvState::MaterialIndex);
if (attribute != NULL) {
QvMaterial *m = (QvMaterial *)attribute->data;

GLfloat color[4];
int i;

for (i=0; i < 3; i++) color[i] = m->ambientColor.values[i];
color[3] = 1.0f - m->transparency.values[0];
glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, color );

for (i=0; i < 3; i++) color[i] = m->diffuseColor.values[i];
glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, color );

for (i=0; i < 3; i++) color[i] = m->specularColor.values[i];
glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, color );

for (i=0; i < 3; i++) color[i] = m->emissiveColor.values[i];
glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, color );

glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, m->shininess.values[0] );
}

attribute = state->getTopElement(QvState::NormalBindingIndex);
if (attribute != NULL) {
}

attribute = state->getTopElement(QvState::NormalIndex);
if (attribute != NULL) {
}

attribute = state->getTopElement(QvState::ShapeHintsIndex);
if (attribute != NULL) {
}

attribute = state->getTopElement(QvState::Texture2Index);
if (attribute != NULL) {
}

attribute = state->getTopElement(QvState::Texture2TransformationIndex);
if (attribute != NULL) {
}

attribute = state->getTopElement(QvState::TextureCoordinate2Index);
if (attribute != NULL) {
}

}

void
QvCone::traverse(QvState *state)
{
loadProperties(state);

GLUquadricObj *cyl = gluNewQuadric();
gluQuadricNormals(cyl, GLU_SMOOTH);
gluQuadricOrientation(cyl, GLU_OUTSIDE);
gluQuadricDrawStyle(cyl, GLU_FILL);
glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
gluCylinder(cyl, bottomRadius.value, 0, height.value, 12, 1);
gluDisk(cyl, 0.0, bottomRadius.value, 12, 1);
glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
gluDeleteQuadric(cyl);
}

void
QvCube::traverse(QvState *state)
{
loadProperties(state);

GLfloat w2, h2, d2;

w2 = width.value/2;
h2 = height.value/2;
d2 = depth.value/2;

glBegin (GL_POLYGON);
glNormal3f (0.0f, 0.0f, 1.0f);
glVertex3f (w2, h2, d2);
glVertex3f (-w2, h2, d2);
glVertex3f (-w2, -h2, d2);
glVertex3f (w2, -h2, d2);
glEnd ();

glBegin (GL_POLYGON);
glNormal3f (0.0f, 0.0f, -1.0f);
glVertex3f (w2, h2, -d2);
glVertex3f (w2, -h2, -d2);
glVertex3f (-w2, -h2, -d2);
glVertex3f (-w2, h2, -d2);
glEnd ();

glBegin (GL_POLYGON);
glNormal3f (-1.0f, 0.0f, 0.0f);
glVertex3f (-w2, h2, d2);
glVertex3f (-w2, h2, -d2);
glVertex3f (-w2, -h2, -d2);
glVertex3f (-w2, -h2, d2);
glEnd ();

glBegin (GL_POLYGON);
glNormal3f (1.0f, 0.0f, 0.0f);
glVertex3f (w2, h2, d2);
glVertex3f (w2, -h2, d2);
glVertex3f (w2, -h2, -d2);
glVertex3f (w2, h2, -d2);
glEnd ();

glBegin (GL_POLYGON);
glNormal3f (0.0f, 1.0f, 0.0f);
glVertex3f (-w2, h2, -d2);
glVertex3f (-w2, h2, d2);
glVertex3f (w2, h2, d2);
glVertex3f (w2, h2, -d2);
glEnd ();

glBegin (GL_POLYGON);
glNormal3f (0.0f, -1.0f, 0.0f);
glVertex3f (-w2, -h2, -d2);
glVertex3f (w2, -h2, -d2);
glVertex3f (w2, -h2, d2);
glVertex3f (-w2, -h2, d2);
glEnd ();
}

void
QvCylinder::traverse(QvState *state)
{
loadProperties(state);

GLUquadricObj *cyl = gluNewQuadric();
gluQuadricNormals(cyl, GLU_SMOOTH);
gluQuadricOrientation(cyl, GLU_OUTSIDE);
gluQuadricDrawStyle(cyl, GLU_FILL);
glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
gluCylinder(cyl, radius.value, radius.value, height.value, 12, 1);
gluDisk(cyl, 0.0, radius.value, 12, 1);
glTranslatef(0.0f, 0.0f, height.value );
gluDisk(cyl, 0.0, radius.value, 12, 1);
glTranslatef(0.0f, 0.0f, -height.value );
glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
gluDeleteQuadric(cyl);
}

void
QvIndexedFaceSet::traverse(QvState *state)
{
loadProperties(state);

QvElement *attribute = state->getTopElement(QvState::Coordinate3Index);
if (attribute == NULL) return;

QvCoordinate3 *coords = (QvCoordinate3 *)attribute->data;

QvMFVec3f p = coords->point;

float *vals = p.values;

indexed++;
if (vals[0] != -2.0) {
indexed = -indexed; /* chasing memory overwrite */
}
if (indexed == indexCheck) {
indexed = 0;
}

int i = 0;
glBegin (GL_POLYGON);
/* what about normal generation? Let GL do it. */
while (i < coordIndex.num) {

if (coordIndex.values[i] != -1) {
if ((coordIndex.values[i] >= 0) &&
(coordIndex.values[i] < p.num)) {
glVertex3f (vals[coordIndex.values[i]*3],
vals[coordIndex.values[i]*3+1],
vals[coordIndex.values[i]*3+2]);
}
} else { /* -1 marks the end of this loop */
glEnd ();
glBegin (GL_POLYGON); /* if ((i+1) < coordIndex.num) ??? */
}
i++;
}
glEnd (); /* if (coordIndex.values[i-1] != -1) ??? */
}

void
QvIndexedLineSet::traverse(QvState *state)
{
loadProperties(state);

QvElement *attribute = state->getTopElement(QvState::Coordinate3Index);
if (attribute == NULL) return;

QvCoordinate3 *coords = (QvCoordinate3 *)attribute->data;

QvMFVec3f p = coords->point;

float *vals = p.values;

int i = 0;
glBegin (GL_LINES);

while (i < coordIndex.num) {

if (coordIndex.values[i] != -1) {
if ((coordIndex.values[i] >= 0) &&
(coordIndex.values[i] < p.num)) {
glVertex3f (vals[coordIndex.values[i]*3],
vals[coordIndex.values[i]*3+1],
vals[coordIndex.values[i]*3+2]);
}
} else { /* -1 marks the end of this loop */
glEnd ();
glBegin (GL_LINES); /* if ((i+1) < coordIndex.num) ??? */
}
i++;
}
glEnd (); /* if (coordIndex.values[i-1] != -1) ??? */
}

void
QvPointSet::traverse(QvState *state)
{
loadProperties(state);

QvElement *attribute = state->getTopElement(QvState::Coordinate3Index);
if (attribute == NULL) return;

QvCoordinate3 *coords = (QvCoordinate3 *)attribute->data;

QvMFVec3f p = coords->point;

float *vals = p.values;
int i = startIndex.value;
glBegin (GL_POINTS);
while (i < p.num) {
glVertex3f (vals[i*3], vals[i*3+1], vals[i*3+2]);
i++;
}
glEnd (); /* if (coordIndex.values[i-1] != -1) ??? */
}

void
QvSphere::traverse(QvState *state)
{
loadProperties(state);

GLUquadricObj *cyl = gluNewQuadric();
gluQuadricNormals(cyl, GLU_SMOOTH);
gluQuadricOrientation(cyl, GLU_OUTSIDE);
gluQuadricDrawStyle(cyl, GLU_FILL);
gluSphere(cyl, radius.value, 12, 12);
gluDeleteQuadric(cyl);
}

////////////////////////////////////////////////////////////////////////////
//
//
// WWW-specific nodes.
//
////////////////////////////////////////////////////////////////////////////
//

void
QvWWWAnchor::traverse(QvState *state)
{
state->push();
indent++;
if (thePickFlag) RegisterAnchor(name.value.getString());
for (int i = 0; i < getNumChildren(); i++)
getChild(i)->traverse(state);
if (thePickFlag) WeighAnchor();
indent--;
state->pop();
}

void
QvWWWInline::traverse(QvState *state)
{
QvNode *newNode;

if (getNumChildren() == 0) {
int stat = FollowAnchor(name.value.getString(), &newNode );
if (stat) { return; } // problem with inline file
(getChildren())->append(newNode);
}

getChild(0)->traverse(state);
}

////////////////////////////////////////////////////////////////////////////
//
//
// Default traversal methods. These nodes have no effects during traversal.
//
////////////////////////////////////////////////////////////////////////////
//

DEFAULT_TRAVERSE(QvInfo)
DEFAULT_TRAVERSE(QvUnknownNode)

////////////////////////////////////////////////////////////////////////////
//

#undef ANNOUNCE
#undef DEFAULT_TRAVERSE
#undef DO_PROPERTY
#undef DO_SHAPE
#undef DO_TYPED_PROPERTY

========================================================================
from vsop.c... send me email if you want more.

/*
* InitView - this gets called to set up the MODELVIEW xform.
* Called from traverser.
*/
void InitView()
{
//
// Define the modelview transformation.
//
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();

glRotatef(180*(-theOrient[3])/M_PI, theOrient[0],
theOrient[1], theOrient[2] );

glTranslatef(-thePos[0], -thePos[1], -thePos[2]);
glRotatef(theAngle, eye[0], eye[1], eye[2] );
glScalef (0.6f, 0.6f, 0.6f);
}
/*
* InitLights called from traverser to init the lights.
*/
void InitLights()
{
GLfloat glfLightAmbient[] = { 0.3f, 0.3f, 0.3f, 1.0f };
GLfloat glfLightDiff[] = { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat glfLightSpec[] = { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat glfLightPos[] = { 0.0f, 0.0f, 1.0f, 0.0f };
GLfloat glfLightSpotDir[] = { 0.0f, 0.0f, -1.0f };

glLightfv (GL_LIGHT0, GL_AMBIENT, glfLightAmbient);
glLightfv (GL_LIGHT0, GL_DIFFUSE, glfLightDiff);
glLightfv (GL_LIGHT0, GL_SPECULAR, glfLightSpec);
glLightfv (GL_LIGHT0, GL_POSITION, glfLightPos);
glLightfv (GL_LIGHT0, GL_SPOT_DIRECTION, glfLightSpotDir);
glLightf (GL_LIGHT0, GL_SPOT_CUTOFF, 180.0f );
glLightf (GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.0f );
}

/*
* DrawScene uses OpenGL commands to draw a cube.
*
* Input parameters:
* hdc = Device context handle
*
* Returns:
* Nothing
*/
void DrawScene (HDC hdc)
{
//
// Clear the color and depth buffers.
//
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

InitView();

TraverseVRMLFile(theRootNode);

//
// Swap the buffers.
//
SwapBuffers (hdc);
if (msgLength > 0) {
TextOut(hdc, 100, 20, msgBuffer, msgLength);
}
}

/*
* start DynView & DoDynView do a little direct manipulation of the
* MODELVIEW transform to be able to look at the model a tiny bit.
*/
void StartDynView(int x, int y)
{
double tsin;

Normalize(eye);
theDynX = x;
theDynY = y;
theTheta = atan2(eye[0],eye[2]);
tsin = sin(theTheta);
if (tsin >= 0.0) {
thePhi = atan2( eye[1] * tsin, eye[0]);
} else {
thePhi = atan2( -(eye[1] * tsin), -eye[0]);
}

}

void DoDynView(int x, int y)
{
/*
theTheta += 0.005f * (theDynX - x);
thePhi += 0.005f * (y - theDynY);
*/
theAngle -= 0.2f * (theDynX - x);
theDynX = x;
/* theDynY = y;

eye[0] = cos( thePhi ) * sin( theTheta );
eye[1] = sin( thePhi );
eye[2] = cos( thePhi ) * cos( theTheta );
*/
}

void Normalize(float v[3])
{
float inv;

inv = 1/sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);

v[0] *= inv;
v[1] *= inv;
v[2] *= inv;
}

/*
* SetPerspectiveCamera - take the Inventor notion of a camera and make
* some OpenGL calls.
*/
void SetPerspectiveCamera( int vm, float pos[3], float orient[4], float
aspect,
float zNear, float zFar, float focal, float height )
{
GLdouble aspectRatio;
int i;

/* set the perspective projection matrix */
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();

if (vm) {
aspectRatio = gldAspect;
} else {
aspectRatio = aspect;
}

gluPerspective( 180.0*(height)/M_PI, aspectRatio, zNear, zFar );

for (i = 0; i < 4; i++ ) theOrient[i] = orient[i];
for (i = 0; i < 3; i++ ) thePos[i] = pos[i];
InitView();
}

/* OpenGL programming guide - page 372 */
#define BUFSIZE 512
void PickOne(int x, int y)
{
GLuint selectBuf[BUFSIZE];
GLfloat feedbackBuf[BUFSIZE];
GLint i, hits;
GLint viewport[4];
GLint name, *ptr;

glGetIntegerv (GL_VIEWPORT, viewport);

/* glFeedbackBuffer (BUFSIZE, GL_3D, feedbackBuf);
(void) glRenderMode (GL_FEEDBACK); */
glSelectBuffer (BUFSIZE, selectBuf);
(void) glRenderMode (GL_SELECT);
glInitNames();
glPushName((unsigned)-1);
thePickName = 0;

glMatrixMode (GL_PROJECTION);
glPushMatrix ();
glLoadIdentity ();
// create 5x5 pixel picking region near cursor location
gluPickMatrix ((GLdouble) x, (GLdouble) (viewport[3] - y),
5.0, 5.0, viewport);
gluPerspective (10.0, // Field-of-view angle
gldAspect, // Aspect ratio of viewing volume
1.0, // Distance to near clipping plane
100.0); // Distance to far clipping plane
InitView();
thePickFlag = 1;
TraverseVRMLFile(theRootNode);
thePickFlag = 0;
glMatrixMode (GL_PROJECTION);
glPopMatrix ();
glFlush ();

hits = glRenderMode (GL_RENDER);
msgLength = sprintf(msgBuffer, "number of hits = %d", hits);

theNewAnchor = (char *)0;
if (hits > 0) { /* see if any are anchors */
ptr = (GLuint *) selectBuf;

for (i = 0; i < hits; i++ ) {
name = ptr[2 + ptr[0]];
if ((name != -1) && (name < MAXURLS)) {
theNewAnchor = theURLS[name];
msgLength = sprintf(msgBuffer, "%d hits - URL = %.80s",
hits, theNewAnchor);
break;
}
ptr = &ptr[3 + ptr[0]];
}
}
}

void RegisterAnchor(const char *url)
{
glPushName(thePickName);
if (thePickName < MAXURLS)
theURLS[thePickName] = url;
thePickName++;
}

void WeighAnchor()
{
glPopName();
}

int FollowAnchor(const char *url, QvNode **new)
{
char *fname;
int status;
static char *lastName[4] = {(char *)0, (char *)0, (char *)0, (char *)0};
int i;

status = CopyURLtoFile(url,&fname);
if (status == 0) {
status = ReadVRMLFile(fname, new);

/* now start to clean up, otherwise we cannot play too long */
/* currently does not work, I think the file may be left open */

if (lastName[0] != (char *)0)
{ i = remove(lastName[0]); free(lastName[0]);}
for (i=0;i<3;i++) {lastName[i] = lastName[i+1]; }
lastName[3] = (char *)malloc(strlen(fname)+1);
strcpy(lastName[3], fname);
}

return status;
}

int QualifyAnchor(const char *url)
{
int len, stat = 0;
char *wrl;

len = strlen(url);
wrl = &url[len-4];
if ((strcmp(wrl,".wrl")) == 0)
stat = 1;

return stat;
}