[BACK]Return to flyLightPoints.c CVS log [TXT][DIR] Up to [Development] / performer / src / sample / C / asdfly

File: [Development] / performer / src / sample / C / asdfly / flyLightPoints.c (download)

Revision 1.1, Tue Nov 21 21:39:43 2000 UTC (16 years, 11 months ago) by flynnt
Branch: MAIN
CVS Tags: HEAD

Initial check-in based on OpenGL Performer 2.4 tree.
-flynnt

/*
 *	PROPRIETARY RIGHTS NOTICE: All rights reserved. This program
 *	contains proprietary information and trade secrets of 
 *	MultiGen of San Jose, CA., and embodies substantial creative
 *	efforts and confidential information, ideas, and expressions.
 *	No part of this program may be reproduced in any form, or by
 *	any means electronic, mechanical, or otherwise, without the
 *	written permission of MultiGen Inc.
 *
 *	COPYRIGHT NOTICE: Copyright (C) 1995, MultiGen Inc.,
 *	550 S Winchester Blvd. Suite 500, San Jose, CA 95128.
 *
 */


#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#ifndef __linux__
#include <bstring.h>
#endif
#include <math.h>
#include <ctype.h>

#include <Performer/pf.h>
#include <Performer/prmath.h>
#include <Performer/pfutil.h>
#include <Performer/pfdu.h>

#include <GL/glx.h>

#include "perfly.h"
#include "gui.h"
#include "env.h"
#include "terrain.h"
#include "flyLightPoints.h"
#include "pfuLightPointsPrototypes.h"

#define	NOF_LIGHT_POINT_SETS	4
#define	X_SIZE			20000.0
#define	Y_SIZE			20000.0
#define	X_BASE			3000.0
#define	Y_BASE			3000.0
#define	SET_X_SPAN		1000.0
#define	SET_Y_SPAN		1000.0
#define	LIGHT_POINT_SET_SIZE	30

static	float   random_0_1(void);

static lightPointSetList  	lightPoints;
static terrainObjectList	objects;
static terrainSegmentList	segments;
static terrainTriangleList 	triangles;
static terrainAnimationList 	animations;

void    generateLightPoints (lightPointSetList *lightPoints);
void    readLightPoints (
		lightPointSetList *lightPoints, 
		terrainObjectList *object, 
		terrainSegmentList *segmentList,
		terrainTriangleList *triangleList,
		terrainAnimationList *animationList,
		char *filename);
void    initLightPointSetList (lightPointSetList *lightPoints);
void    addLightPoint (lightPointSetList *lightPoints,
					int     setId,
					int     pointId,
					float   x,
					float   y,
					float   z);

void    initTerrainObjectList (terrainObjectList *objectList);
void    addTerrainObject (
                        terrainObjectList       *objectList,
                        float                   x,
                        float                   y,
                        float                   z,
			float			*down,
			float			*xy_direction,
                        char                    *filename,
			int			is_normal,
			int			is_azimuth);

void	addTerrainBillboard (
			terrainObjectList 	*objectList, 
			float			x, 
			float			y, 
			float			z, 
			float			*down,
			char			*filename,
			float			width,
			float			height);

void    initTerrainSegmentList (terrainSegmentList *segments);

void    addTerrainSegment (
			terrainSegmentList 	*segmentList,
			float           	*v0,
			float           	*v1);

void	initTerrainTriangleList (terrainTriangleList *triangles);

void 	addTerrainGeoSet (
			terrainTriangleList 	*triangleList,
			pfGeoSet            	*gset,
			float               	*base,
			float               	*down,
			float               	*azimuth,
			float               	*projection,
			char                	*texture_filename,
			float               	*c,
			unsigned long       	mask,
			unsigned long       	transform_mask);

void    initTerrainAnimationList (terrainAnimationList *animations);
void	addTerrainAnimation (
			terrainAnimationList 	*segmentList, 
			float			x, 
			float			y, 
			float 			z, 
			float			*down, 
			float 			radius, 
			float 			d_angle, 
			char			*name);

int 	flyIsectFunc (pfHit *hit);

void   composeGset (
			pfGeoSet		*gset, 
			float 			*v, 
			float 			*t, 
			float 			*c, 
			float 			*n, 
			int			nof_triangles);



/*===========================================================================*/
void  
flyLightsInit(pfASD *asd)
/*===========================================================================*/
{
    pfGroup 		*hook;
    int			i;
    unsigned long	bit;

    ViewState -> isIsect = 0;

    readLightPoints (&lightPoints, &objects, &segments, 
			&triangles, &animations,
			ViewState -> lightPoints . filename);

    for (i = 0 ; i < lightPoints . nofSets ; i ++)
    {
	hook = (pfGroup *) pfNewLightPointAlignNode (
			lightPoints . sets[i] . nofPoints,
			lightPoints . sets[i] . coord,
			lightPoints . sets[i] . color,
			lightPoints . sets[i] . normal,
                        (lightPoints . sets[i] . coord[2] == 0.0) ? 1 : 0, 
			asd);

	pfAddChild(ViewState->lightGroup,hook);
    }

    for (i = 0 ; i < objects . nofObjects ; i ++)
    {
	if (! objects . object[i].is_billboard)
	    hook = (pfGroup *) pfNewObjectAlignNode (
				objects . object[i].filename,
				objects . object[i].x, 
				objects . object[i].y,
				objects . object[i].z,
				objects . object[i].down,
				objects . object[i].is_normal,
				objects . object[i].is_azimuth,
				objects . object[i].xy_direction,
				asd);
	else
	    hook = (pfGroup *) pfNewBillboardAlignNode(
				objects . object[i].filename,
				objects . object[i].x, 
				objects . object[i].y,
				objects . object[i].z,
				objects . object[i].down,
				objects . object[i].width,
				objects . object[i].height,
				asd);

	pfAddChild(ViewState->lightGroup,hook);
    }

    /*
     *	If we have any segments, generate Isect structures and GeoSet.
     */
    if (segments . nofSegments > 0)
    {
	fprintf (stderr, "Found %d Isect segments\n", segments . nofSegments);

	ViewState -> isIsect = 1;
	ViewState -> segSet.mode = PFTRAV_IS_PRIM;
	ViewState -> segSet.activeMask = 0x0;
	ViewState -> segSet.isectMask = 0xffffffff; /* default pfNode value */
	ViewState -> segSet.bound = NULL;
	ViewState -> segSet.discFunc = flyIsectFunc;

	bit = 0x01;
	for (i = 0 ; i < segments . nofSegments ; i ++)
	{
	    ViewState -> segSet.activeMask |= bit;
	    pfSetVec3 (ViewState -> segSet.segs[i].pos, 
			segments . segment[i].v0[0], 
			segments . segment[i].v0[1], 
			segments . segment[i].v0[2]);

	    pfSetVec3 (ViewState -> segSet.segs[i].dir, 
			segments . segment[i].v1[0] 
					- segments . segment[i].v0[0], 
			segments . segment[i].v1[1] 
					- segments . segment[i].v0[1], 
			segments . segment[i].v1[2] 
					- segments . segment[i].v0[2]);

	    ViewState -> segSet.segs[i].length = 
	    		pfNormalizeVec3 (ViewState -> segSet.segs[i].dir);


	    bit <<= 1;
	}

	printf ("Active mask = 0x0%x\n", ViewState -> segSet.activeMask);
    }

    /*
     *	============================================================
     *	Add all query triangles.
     *	============================================================
     */
    for (i = 0 ; i < triangles . nofTriangles ; i ++)
    {
	hook = (pfGroup *) pfuNewGSetShadowNode (
		    triangles . triangle[i] . gset,
		    (float *) triangles . triangle[i] . base,
		    (float *) triangles . triangle[i] . down,
		    (float *) triangles . triangle[i] . azimuth,
		    (float *) triangles . triangle[i] . projection,
		    triangles . triangle[i] . texture_filename,
		    triangles . triangle[i] . mask,
		    triangles . triangle[i] . transformation_mask,
		    (float *) triangles . triangle[i] . c,
		    asd);
	

	pfAddChild(ViewState->lightGroup,hook);
    }

    /*
     *	============================================================
     *	Add all Animations
     *	============================================================
     */
    for (i = 0 ; i < animations . nofAnimations ; i ++)
    {
	hook = (pfGroup *) pfNewAnimationAlignNode (
			animations . animation[i] . model_filename, 
			animations . animation[i] . center[0], 
			animations . animation[i] . center[1], 
			animations . animation[i] . center[2], 
			animations . animation[i] . down, 
			animations . animation[i] . radius, 
			&animations . animation[i] . query_id, 
			&animations . animation[i] . azimuth_flux, 
			asd);

	animations . animation[i] . asd = asd;

	pfAddChild(ViewState->lightGroup,hook);
    }
}

/*===========================================================================*/
static	float    random_0_1(void)
/*===========================================================================*/
{
return (((float) (random() % 10000)) / 10000.0);
}


/*===========================================================================*/
void    generateLightPoints (lightPointSetList *lightPoints)
/*===========================================================================*/
{
int	i,j;
float	xmin, ymin, x, y;

initLightPointSetList (lightPoints);

/*
 *	First set: diagonal across database.
 *	Second set: another diagonal across database.
 */
for (i = 0 ; i < 300 ; i ++)
	{
	x = ((float) i) * (float) X_SIZE / 300.0;

	addLightPoint (lightPoints, 0, i, X_BASE + x, Y_BASE + x, 0.0);
	addLightPoint (lightPoints, 1, i, X_BASE + X_SIZE - x, Y_BASE + x, 0.0);
	}

for (i = 2 ; i < NOF_LIGHT_POINT_SETS ; i ++)
	{
	xmin = random_0_1() * X_SIZE;
	ymin = random_0_1() * Y_SIZE;

	for (j = 0 ; j < LIGHT_POINT_SET_SIZE ; j ++)
		{
		x = xmin + random_0_1() * SET_X_SPAN;
		y = ymin + random_0_1() * SET_Y_SPAN;

		addLightPoint (lightPoints, i, j, x, y, 0.0);
		}
	}
}

/*===========================================================================*/
void    readLightPoints (lightPointSetList *lightPoints, 
			terrainObjectList *objectList,
			terrainSegmentList *segmentList,
			terrainTriangleList *triangleList,
			terrainAnimationList *animationList,
			char *filename)
/*===========================================================================*/
{
    float		x, y, z;
    FILE		*fp;
    int			nof_points;
    int			set, point;
    float		from_x, from_y, from_z, to_x, to_y, to_z;
    float		f;
    char		line[200];
    char		name[200];
    int			l, nof_lines;
    float		xy_direction[3];
    float		down[3];
    float		width, height;
    float		v0[3], v1[3];
    float		v[MAX_POINTS][3], t[MAX_POINTS][2], c[MAX_POINTS][4];
    float		azimuth[3], base[3], projection[3];
    float		*c_p, *t_p, *azimuth_p, *down_p, *base_p, *projection_p;
    int			c_index, t_index, v_index;
    float		a0, a1, a2, a3;
    int			done_triangle;
    char		op[100];
    int			nof_triangles;
    int			mask;
    unsigned long	transform_mask;
    char		transform[200];
    char		model_filename[200];
    char		texture_filename[200];
    pfNode		*model;
    int				is_model;
    float			radius, d_angle;
    pfGeoSet			*gset;


    initLightPointSetList (lightPoints);
    initTerrainObjectList (objectList);
    initTerrainSegmentList (segmentList);
    initTerrainTriangleList (triangleList);
    initTerrainAnimationList (animationList);

    if ((fp = fopen (filename, "r")) == NULL)
    {
	perror (filename);
	return;
    }

    
    fgets (line, 200, fp);
    sscanf (line, "%d", &nof_lines);

    set = 0;
    for (l = 0 ; (l < nof_lines) && (! feof (fp)); l ++)
    {
	line[0] = '\0';
	fgets (line, 200, fp);
	switch (line[0])
	{
	    case 'L':
		sscanf (&line[1], "%d%f%f%f%f", 
			&nof_points, &from_x, &from_y, &to_x, &to_y);
		for (point = 0 ; point < nof_points ; point ++)
		{
		    f = ((float) point) / ((float) nof_points - 1.0);
		    x = from_x * (1-f) + to_x * f;
		    y = from_y * (1-f) + to_y * f;
		    addLightPoint (lightPoints, set, point, x, y, 0.0);
		}
		set ++;
		break;

	    case 'S':
		sscanf (&line[1], "%d%f%f%f%f", 
			&nof_points, &from_x, &from_y, &to_x, &to_y);
		for (point = 0 ; point < nof_points ; point ++)
		{
		    x = from_x + (to_x - from_x) 
				    * ((float) (random() % 10000)) / 10000.0;
		    y = from_y + (to_y - from_y) 
				    * ((float) (random() % 10000)) / 10000.0;
		    addLightPoint (lightPoints, set, point, x, y, 0.0);
		}
		set ++;
		break;

	    case 's':
		sscanf (&line[1], "%d%f%f%f%f%f%f", 
			&nof_points, &from_x, &from_y, &from_z, 
					&to_x, &to_y, &to_z);

		for (point = 0 ; point < nof_points ; point ++)
		{
		    x = from_x + (to_x - from_x) 
				    * ((float) (random() % 10000)) / 10000.0;
		    y = from_y + (to_y - from_y) 
				    * ((float) (random() % 10000)) / 10000.0;
		    z = from_z + (to_z - from_z) 
				    * ((float) (random() % 10000)) / 10000.0;
		    addLightPoint (lightPoints, set, point, x, y, z);
		}
		set ++;
		break;

	    case 'N':

		down[0] = 0.0;
		down[1] = 0.0;
		down[2] = -1.0;

		sscanf (&line[1], "%f%f%f%f%s", &x, &y, 
						&xy_direction[0],
						&xy_direction[1], 
						name);
		xy_direction[2] = 0.0;
		addTerrainObject (objectList, x, y, 0.0, down,
				    xy_direction, name, 1, 1);
		break;

	    case 'n':
		sscanf (&line[1], "%f%f%f%f%f%f%f%f%f%s", &x, &y, &z,
						&down[0],
						&down[1],
						&down[2],
						&xy_direction[0],
						&xy_direction[1], 
						&xy_direction[2], 
						name);
		addTerrainObject (objectList, x, y, z, 
					down, xy_direction, name, 0, 1);
		break;

	    case 'O':
		down[0] = 0.0;
		down[1] = 0.0;
		down[2] = -1.0;
		sscanf (&line[1], "%f%f%s", &x, &y, name);
		xy_direction[0] = 1.0;
		xy_direction[1] = 0.0;
		xy_direction[2] = 0.0;
		addTerrainObject (objectList, x, y, 0.0, 
					down,
					xy_direction, name,0, 0);
		break;

	    case 'o':

		down[0] = 0.0;
		down[1] = 0.0;
		down[2] = -1.0;
		sscanf (&line[1], "%f%f%f%f%f%s", &x, &y, 
						&xy_direction[0],
						&xy_direction[1], 
						&xy_direction[2], 
						name);
		addTerrainObject (objectList, x, y, 0.0, 
					down,
					xy_direction, name,0, 1);
		break;

	    case 'B':

		down[0] = 0.0;
		down[1] = 0.0;
		down[2] = -1.0;

		sscanf (&line[1], "%f%f%f%f%s", &x, &y, &width, &height, name);

		printf ("Billboard at (%f, %f), size (%f x %f), name (%s)\n", 
				x, y, width, height, name);

		addTerrainBillboard (objectList, x, y, 0.0, down,
				    name, width, height);
		break;

	    case 'b':

		sscanf (&line[1], "%f%f%f%f%f%f%f%f%s", &x, &y, &z,
						&down[0],
						&down[1],
						&down[2],
						&width, &height,
						name);

		addTerrainBillboard (objectList, x, y, z, 
					down, name, width, height);
		break;

	    case 'I':

		sscanf (&line[1], "%f%f%f%f%f%f", 
					&v0[0], &v0[1], &v0[2],
					&v1[0], &v1[1], &v1[2]);

		addTerrainSegment (segmentList, v0, v1);

		break;

	    case 'A': /* Animation */

		down[0] = 0.0;
		down[1] = 0.0;
		down[2] = -1.0;
		sscanf (&line[1], "%f%f%f%f%s", 
					&x, &y, &radius, &d_angle, name);

		addTerrainAnimation (animationList, x, y, 0.0, 
					down, radius, d_angle, 
					name);
		break;


	    case 'T':

		done_triangle = 0;
		v_index = 0;
		c_index = 0;
		t_index = 0;
		c_p = NULL;
		t_p = NULL;
		azimuth_p = NULL;
		down_p = NULL;
		base_p = NULL;
		texture_filename[0] = '\0';
		model_filename[0] = '\0';
		projection_p = NULL;
		transform_mask = PR_ALIGN_TRANSLATE;
		is_model = 0;

		while (! done_triangle)
		{
		    fgets (line, 200, fp);
		    l ++;
		    if (line[0] == '.')
			done_triangle = 1;
		    else
		    if (line[0] != ';')
		    {
			sscanf (line, "%s%f%f%f%f\n", op, &a0, &a1, &a2, &a3);
			if (strcmp(op, "v") == 0)
			{
			    v[v_index][0] = a0;
			    v[v_index][1] = a1;
			    v[v_index][2] = a2;
			    v_index ++; 
			    if (v_index >= MAX_POINTS)
				v_index = MAX_POINTS - 1;
			}
			else
			if (strcmp(op, "t") == 0)
			{
			    t[t_index][0] = a0;
			    t[t_index][1] = a1;
			    t_index ++; 
			    if (t_index >= MAX_POINTS)
				t_index = MAX_POINTS - 1;
			    t_p = &t[0][0];
			}
			else
			if (strcmp(op, "c") == 0)
			{
			    c[c_index][0] = a0;
			    c[c_index][1] = a1;
			    c[c_index][2] = a2;
			    c[c_index][3] = a3;
			    c_index ++; 
			    if (c_index >= MAX_POINTS)
				c_index = MAX_POINTS - 1;
			    c_p = &c[0][0];
			}
			else
			if (strcmp(op, "down") == 0)
			{
			    down[0] = a0;
			    down[1] = a1;
			    down[2] = a2;
			    down_p = down;
			}
			else
			if (strcmp(op, "azimuth") == 0)
			{
			    azimuth[0] = a0;
			    azimuth[1] = a1;
			    azimuth[2] = a2;
			    azimuth_p = azimuth;
			}
			else
			if (strcmp(op, "base") == 0)
			{
			    base[0] = a0;
			    base[1] = a1;
			    base[2] = a2;
			    base_p = base;
			}
			else
			if (strcmp(op, "projection") == 0)
			{
			    projection[0] = a0;
			    projection[1] = a1;
			    projection[2] = a2;
			    projection_p = projection;
			}
			else
			if (strcmp(op, "texture") == 0)
			{
			    sscanf (line, "%s%s", op, texture_filename);
			}
			else
			if (strcmp(op, "model") == 0)
			{
			    sscanf (line, "%s%s", op, model_filename);
			    is_model = 1;
			}
			else
			if (strcmp(op, "transform") == 0)
			{
			    sscanf (line, "%s%s", op, transform);
			    if (strcmp(transform, "AXIS_BILLBOARD") == 0)
				transform_mask |= PR_ALIGN_AXIS_BILLBOARD;
			    else
			    if (strcmp(transform, "TRANSLATE") == 0)
				transform_mask |= PR_ALIGN_TRANSLATE;
			    else
			    if (strcmp(transform, "NORMAL") == 0)
				transform_mask |= PR_ALIGN_NORMAL;
			    else
			    if (strcmp(transform, "AZIMUTH") == 0)
				transform_mask |= PR_ALIGN_AZIMUTH;
			}
			else
			    fprintf (stderr, "#### Bad Triangle cmd: %s\n", 
									line);
		    }
		}


		if (is_model)
		{
		    /*
		     *		If we have amodel - load it and extract its
		     *		geometry into a single GeoSet.
		     */
		    model = pfdLoadFile(model_filename);

		    if (texture_filename[0] != '\0')
		    	mask = PR_QUERY_TRI_COORD | PR_QUERY_TRI_TEXTURE;
		    else
			mask = PR_QUERY_TRI_COORD;

		    gset = pfNewGSet (pfGetSharedArena());
		    pfdExtractGraphTriangles (model, gset, mask);

		    addTerrainGeoSet (triangleList, 
					gset, base_p, down_p, azimuth_p, 
					projection_p, texture_filename, 
					c_p, mask, 
					transform_mask);
		}
		else
		{
		    /*
		     *		If we have an explicit list of triangles, 
		     *		compose into a GeoSet.
		     */
		    nof_triangles = v_index / 3;

		    mask = PR_QUERY_TRI_COORD;
		    if (t_index >= nof_triangles * 3)
			mask |= PR_QUERY_TRI_TEXTURE;

		    if (c_index >= nof_triangles * 3)
			mask |= PR_QUERY_TRI_COLOR;

		    if ((mask & PR_QUERY_TRI_TEXTURE) && 
			(texture_filename[0] == '\0'))
		    {
			fprintf (stderr, 
			    "#####  No shadow texture filename specified.\n");
			mask &= (~ PR_QUERY_TRI_TEXTURE);
		    }

		    gset = pfNewGSet (pfGetSharedArena());
		    composeGset (gset, 
				    (float *) v, 
				    (mask & PR_QUERY_TRI_TEXTURE) ? t_p : NULL, 
				    (mask & PR_QUERY_TRI_COLOR) ? c_p : NULL, 
				    NULL, 
				    nof_triangles);


		    addTerrainGeoSet (triangleList, 
					gset, base_p, down_p, azimuth_p, 
					projection_p, texture_filename, 
					c_p, mask, 
					transform_mask);

		}
		break;
	}
    }

    fclose (fp);
}

/*===========================================================================*/
void	initLightPointSetList (lightPointSetList *lightPoints)
/*===========================================================================*/
{
bzero (lightPoints, sizeof (lightPointSetList));
}

/*===========================================================================*/
void	addLightPoint (lightPointSetList *lightPoints, 
			int	setId, 
			int	pointId, 
			float	x, 
			float	y, 
			float	z)
/*===========================================================================*/
{
    int		new;
    lightPointSet	*set;

    if (lightPoints -> nofAllocatedSets <= setId)
    {
	new = (lightPoints -> nofAllocatedSets + 2) * 5 / 3;

	lightPoints -> sets = (lightPointSet *) realloc (lightPoints -> sets, 
					new * sizeof (lightPointSet));

	bzero (& lightPoints -> sets[lightPoints -> nofAllocatedSets], 
		(new - lightPoints -> nofAllocatedSets) 
		* sizeof (lightPointSet));

	lightPoints -> nofAllocatedSets = new;
    }

    if (lightPoints -> nofSets <= setId)
	lightPoints -> nofSets = setId + 1;

    set = & lightPoints -> sets[setId];

    if (set -> nofAllocatedPoints <= pointId)
    {
	new = (set -> nofAllocatedPoints + 2) * 5 / 3;

	set -> coord = (float *) realloc (set -> coord, 
						new * 3 * sizeof (float));

	set -> color = (float *) realloc (set -> color, 
						new * 4 * sizeof (float));
	set -> nofAllocatedPoints = new;
    }

    if (set -> nofPoints <= pointId)
	set -> nofPoints = pointId + 1;

    set -> isActive = 1;
    set -> coord[3 * pointId] = x;
    set -> coord[3 * pointId + 1] = y;
    set -> coord[3 * pointId + 2] = z;

    set -> color[4 * pointId    ] = 1.0;
    set -> color[4 * pointId + 1] = 1.0;
    set -> color[4 * pointId + 2] = 1.0;
    set -> color[4 * pointId + 3] = 1.0;
}

/*===========================================================================*/
void	initTerrainObjectList (terrainObjectList *objectList)
/*===========================================================================*/
{
    bzero (objectList, sizeof (terrainObjectList));
}

/*===========================================================================*/
void	addTerrainObject (
			terrainObjectList 	*objectList, 
			float			x, 
			float			y, 
			float			z, 
			float			*down,
			float			*xy_direction,
			char			*filename,
			int			is_normal,
			int			is_azimuth)
/*===========================================================================*/
{
    int		new;
    terrainObject	*object;
    float		mag, mag2;

    if (objectList -> nofAllocatedObjects <= objectList -> nofObjects)
    {
	new = (objectList -> nofAllocatedObjects + 2) * 5 / 3;

	objectList -> object = (terrainObject *) realloc (objectList -> object, 
					new * sizeof (terrainObject));

	bzero (& objectList -> object[objectList -> nofAllocatedObjects], 
		(new - objectList -> nofAllocatedObjects) 
		* sizeof (terrainObject));

	objectList -> nofAllocatedObjects = new;
    }

    object = & (objectList -> object[objectList -> nofObjects]);

    objectList -> nofObjects ++;

    object -> x = x;
    object -> y = y;
    object -> z = z;

    mag2 = down[0] * down[0] + down[1] * down[1] + down[2] * down[2];
    if (fabs (mag2 - 1.0) > 0.001)
    {
        mag = sqrtf(mag2);
	object -> down[0] = down[0] / mag;
	object -> down[1] = down[1] / mag;
	object -> down[2] = down[2] / mag;
    }
    else
    {
	object -> down[0] = down[0];
	object -> down[1] = down[1];
	object -> down[2] = down[2];
    }

    mag2 = xy_direction[0] * xy_direction[0] 
	 + xy_direction[1] * xy_direction[1] 
	 + xy_direction[2] * xy_direction[2];

    if (fabs (mag2 - 1.0) > 0.001)
    {
        mag = sqrtf(mag2);
	object -> xy_direction[0] = xy_direction[0] / mag;
	object -> xy_direction[1] = xy_direction[1] / mag;
	object -> xy_direction[2] = xy_direction[2] / mag;
    }
    else
    {
	object -> xy_direction[0] = xy_direction[0];
	object -> xy_direction[1] = xy_direction[1];
	object -> xy_direction[2] = xy_direction[2];
    }

    strcpy (object -> filename, filename);
    object -> is_normal = is_normal;
    object -> is_azimuth = is_azimuth;
    object -> is_billboard = 0;
}

/*===========================================================================*/
void	addTerrainBillboard (
			terrainObjectList 	*objectList, 
			float			x, 
			float			y, 
			float			z, 
			float			*down,
			char			*filename,
			float			width,
			float			height)
/*===========================================================================*/
{
    int		new;
    terrainObject	*object;
    float		mag, mag2;

    if (objectList -> nofAllocatedObjects <= objectList -> nofObjects)
    {
	new = (objectList -> nofAllocatedObjects + 2) * 5 / 3;

	objectList -> object = (terrainObject *) realloc (objectList -> object, 
					new * sizeof (terrainObject));

	bzero (& objectList -> object[objectList -> nofAllocatedObjects], 
		(new - objectList -> nofAllocatedObjects) 
		* sizeof (terrainObject));

	objectList -> nofAllocatedObjects = new;
    }

    object = & (objectList -> object[objectList -> nofObjects]);

    objectList -> nofObjects ++;

    object -> x = x;
    object -> y = y;
    object -> z = z;

    mag2 = down[0] * down[0] + down[1] * down[1] + down[2] * down[2];
    if (fabs (mag2 - 1.0) > 0.001)
    {
        mag = sqrtf(mag2);
	object -> down[0] = down[0] / mag;
	object -> down[1] = down[1] / mag;
	object -> down[2] = down[2] / mag;
    }
    else
    {
	object -> down[0] = down[0];
	object -> down[1] = down[1];
	object -> down[2] = down[2];
    }

    strcpy (object -> filename, filename);
    object -> is_billboard = 1;
    object -> width = width;
    object -> height = height;
}

/*===========================================================================*/
void	initTerrainSegmentList (terrainSegmentList *segments)
/*===========================================================================*/
{
bzero (segments, sizeof (terrainSegmentList));
}

/*===========================================================================*/
void	addTerrainSegment (terrainSegmentList *segmentList, 
			float		*v0, 
			float		*v1) 
/*===========================================================================*/
{
    int			new;
    terrainSegment	*segment;
    float		mag, mag2;

    if (segmentList -> nofAllocatedSegments <= segmentList -> nofSegments)
    {
	new = (segmentList -> nofAllocatedSegments + 2) * 5 / 3;

	segmentList -> segment = (terrainSegment *) 
					realloc (segmentList -> segment, 
					new * sizeof (terrainSegment));

	bzero (& segmentList -> segment[segmentList -> nofAllocatedSegments], 
		(new - segmentList -> nofAllocatedSegments) 
		* sizeof (terrainSegment));

	segmentList -> nofAllocatedSegments = new;
    }

    segment = & (segmentList -> segment[segmentList -> nofSegments]);

    segmentList -> nofSegments ++;

    segment -> v0[0] = v0[0];
    segment -> v0[1] = v0[1];
    segment -> v0[2] = v0[2];

    segment -> v1[0] = v1[0];
    segment -> v1[1] = v1[1];
    segment -> v1[2] = v1[2];
}

/*==========================================================================*/
int 	flyIsectFunc (pfHit *hit)
/*==========================================================================*/
{
    int		faceIndex;
    int		segmentIndex;

    pfQueryHit(hit, PFQHIT_FACEINDEX, &faceIndex);
    pfQueryHit(hit, PFQHIT_SEGNUM, &segmentIndex);

    fprintf (stderr, "Segment %d hits Face %d\n", segmentIndex, faceIndex);

    return PFTRAV_CONT;
}

/*===========================================================================*/
void	initTerrainTriangleList (terrainTriangleList *triangles)
/*===========================================================================*/
{
bzero (triangles, sizeof (terrainTriangleList));
}

/*===========================================================================*/
void addTerrainGeoSet (
		    terrainTriangleList *triangleList,
		    pfGeoSet 		*gset, 
		    float		*base, 
		    float		*down, 
		    float		*azimuth, 
		    float		*projection, 
		    char		*texture_filename, 
		    float		*c,
		    unsigned long	mask, 
		    unsigned long 	transform_mask)
/*===========================================================================*/
{
    int			new;
    terrainTriangle	*triangle;
    float		mag, mag2;
    int			i;
    ushort		*dummy_ushort;
    float		*v;

    if (triangleList -> nofAllocatedTriangles <= triangleList -> nofTriangles)
    {
	new = (triangleList -> nofAllocatedTriangles + 2) * 5 / 3;

	triangleList -> triangle = (terrainTriangle *) 
					realloc (triangleList -> triangle, 
					new * sizeof (terrainTriangle));

	bzero (& triangleList -> 
		triangle[triangleList -> nofAllocatedTriangles], 
		(new - triangleList -> nofAllocatedTriangles) 
		* sizeof (terrainTriangle));

	triangleList -> nofAllocatedTriangles = new;
    }

    triangle = & (triangleList -> triangle[triangleList -> nofTriangles]);

    triangleList -> nofTriangles ++;

    triangle -> mask = mask;
    triangle -> transformation_mask = transform_mask;
    triangle -> gset = gset;

    triangle -> type = TYPE_GSET;

    if (c) /* If we have color - grab it anyway (ignore mask) */
    {
	triangle -> c[0][0] = c[0];
	triangle -> c[0][1] = c[1];
	triangle -> c[0][2] = c[2];
	triangle -> c[0][3] = c[3];
    }
    else
    {
	triangle -> c[0][0] = 0.0;
	triangle -> c[0][1] = 0.0;
	triangle -> c[0][2] = 0.0;
	triangle -> c[0][3] = 0.6;
    }

    if (base)
    {
	triangle -> base[0] = base[0];
	triangle -> base[1] = base[1];
	triangle -> base[2] = base[2];
    }
    else
    {
	pfGetGSetAttrLists (gset, PFGS_COORD3, (void **) &v, &dummy_ushort);
	if (v)
	{
	    triangle -> base[0] = v[0];
	    triangle -> base[1] = v[1];
	    triangle -> base[2] = v[2];
	}
	else
	{
	    triangle -> base[0] = 0.0;
	    triangle -> base[1] = 0.0;
	    triangle -> base[2] = 0.0;
	}
    }

    if (down)
    {
	triangle -> down[0] = down[0];
	triangle -> down[1] = down[1];
	triangle -> down[2] = down[2];
    }
    else
    {
	triangle -> down[0] = 0.0;
	triangle -> down[1] = 0.0;
	triangle -> down[2] = -1.0;
    }

    if (azimuth)
    {
	triangle -> azimuth[0] = azimuth[0];
	triangle -> azimuth[1] = azimuth[1];
	triangle -> azimuth[2] = azimuth[2];
    }
    else
    {
	triangle -> azimuth[0] = 1.0;
	triangle -> azimuth[1] = 0.0;
	triangle -> azimuth[2] = 0.0;
    }

    if (projection)
    {
	triangle -> projection[0] = projection[0];
	triangle -> projection[1] = projection[1];
	triangle -> projection[2] = projection[2];
    }
    else
    {
	triangle -> projection[0] = 0.0;
	triangle -> projection[1] = 1.0;
	triangle -> projection[2] = -1.0;
    }

    strcpy (triangle -> texture_filename, texture_filename);
}


/*===========================================================================*/
void	initTerrainAnimationList (terrainAnimationList *animations)
/*===========================================================================*/
{
bzero (animations, sizeof (terrainAnimationList));
}

/*===========================================================================*/
void	addTerrainAnimation (terrainAnimationList 	*animationList, 
					float		x, 
					float		y, 
					float 		z, 
					float		*down, 
					float 		radius, 
					float 		d_angle, 
					char		*name)
/*===========================================================================*/
{
    int			new;
    terrainAnimation	*animation;

    if (animationList -> nofAllocatedAnimations <= 
	animationList -> nofAnimations)
    {
	new = (animationList -> nofAllocatedAnimations + 2) * 5 / 3;

	animationList -> animation = (terrainAnimation *) 
					realloc (animationList -> animation, 
					new * sizeof (terrainAnimation));

	bzero (& animationList -> 
		animation[animationList -> nofAllocatedAnimations], 
		(new - animationList -> nofAllocatedAnimations) 
		* sizeof (terrainAnimation));

	animationList -> nofAllocatedAnimations = new;
    }

    animation = & (animationList -> animation[animationList -> nofAnimations]);

    animationList -> nofAnimations ++;

    animation -> center[0] = x;
    animation -> center[1] = y;
    animation -> center[2] = z;

    animation -> down[0] = down[0];
    animation -> down[1] = down[1];
    animation -> down[2] = down[2];

    strcpy (animation -> model_filename, name);

    animation -> radius = radius;
    animation -> d_angle = d_angle;

    animation -> angle = ((float) (random() % 1000)) * M_PI * 2.0 / 1000.0 ;
}

/*===========================================================================*/
void	advanceAnimations (void)
/*===========================================================================*/
{
    int		i;

    for (i = 0 ; i < animations . nofAnimations ; i ++)
	advanceAnimation (&animations . animation[i]);
}

/*===========================================================================*/
void advanceAnimation (terrainAnimation *animation)
/*===========================================================================*/
{
    float	*azimuth;
    float	position[3];

    /*
     *	=============================================
     *	Advance angle around center of motion
     *	=============================================
     */
    animation -> angle += animation -> d_angle;
    if (animation -> angle > 2.0 * M_PI)
	animation -> angle -= 2.0 * M_PI;

    /*
     *	=============================================
     *	Set new position. 
     *	=============================================
     */
    position[0] = animation -> center[0] + 
			animation -> radius * cos (animation -> angle);
    position[1] = animation -> center[1] + 
			animation -> radius * sin (animation -> angle);
    position[2] = animation -> center[2];

    pfASDQueryArrayElement (animation -> asd, animation -> query_id, 
				    0, position, animation -> down);

    /*
     *	=============================================
     *	Set new azimuth. 
     *	=============================================
     */
    azimuth = (float *) pfGetFluxWritableData (animation -> azimuth_flux);

    azimuth[0] = cos (animation -> angle + M_PI * 0.5);
    azimuth[1] = sin (animation -> angle + M_PI * 0.5);

    pfFluxWriteComplete (animation -> azimuth_flux);
}

/*===========================================================================*/
void   composeGset (
		pfGeoSet		*gset, 
		float 			*_v, 
		float 			*_t, 
		float 			*_c, 
		float 			*_n, 
		int			nof_triangles)
/*===========================================================================*/
{
    float	*v, *t, *n, *c;

    if (_v)
    {
	v = (float *) pfMalloc (nof_triangles * 3 * 3 * sizeof (float), 
						    pfGetSharedArena());
	memcpy (v, _v, nof_triangles * 3 * 3 * sizeof (float));
        pfGSetAttr(gset, PFGS_COORD3, PFGS_PER_VERTEX, (void *) v, NULL);
    }
    else
    {
	fprintf (stderr, "composeGset: no COORD3 !\n");
        pfGSetAttr(gset, PFGS_COORD3, PFGS_OFF, NULL, NULL);
    }

    if (_t)
    {
	t = (float *) pfMalloc (nof_triangles * 2 * 3 * sizeof (float), 
						    pfGetSharedArena());
	memcpy (t, _t, nof_triangles * 2 * 3 * sizeof (float));
        pfGSetAttr(gset, PFGS_TEXCOORD2, PFGS_PER_VERTEX, (void *) t, NULL);
    }
    else
        pfGSetAttr(gset, PFGS_TEXCOORD2, PFGS_OFF, NULL, NULL);

    if (_c)
    {
	c = (float *) pfMalloc (nof_triangles * 4 * 3 * sizeof (float), 
						    pfGetSharedArena());
	memcpy (c, _c, nof_triangles * 4 * 3 * sizeof (float));
        pfGSetAttr(gset, PFGS_COLOR4, PFGS_PER_VERTEX, (void *) c, NULL);
    }
    else
        pfGSetAttr(gset, PFGS_COLOR4, PFGS_OFF, NULL, NULL);

    if (_n)
    {
	n = (float *) pfMalloc (nof_triangles * 3 * 3 * sizeof (float), 
						    pfGetSharedArena());
	memcpy (n, _n, nof_triangles * 3 * 3 * sizeof (float));
        pfGSetAttr(gset, PFGS_NORMAL3, PFGS_PER_VERTEX, (void *) n, NULL);
    }
    else
        pfGSetAttr(gset, PFGS_NORMAL3, PFGS_OFF, NULL, NULL);

    pfGSetNumPrims(gset, nof_triangles);

    pfGSetPrimType (gset, PFGS_TRIS);
}