Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: utzoo!mnetor!uunet!seismo!mcnc!ece-csc!ncrcae!ncr-sd!hp-sdd!hplabs!hplabsb!spach
From: spach@hplabsb.UUCP (Susan Spach)
Newsgroups: comp.graphics
Subject: Standard Procedural Databases (Part 2 of 2)
Message-ID: <4210@hplabsb.UUCP>
Date: Fri, 17-Jul-87 11:50:01 EDT
Article-I.D.: hplabsb.4210
Posted: Fri Jul 17 11:50:01 1987
Date-Received: Sat, 18-Jul-87 14:58:16 EDT
Organization: Hewlett-Packard Laboratories
Lines: 1979
Keywords: raytracing, hidden-surface algorithms, benchmarking

----------------------------------------------------------------------

From Eric Haines, 3D/Eye, INC.

PART 2 of 2:	Standard Procedural Databases

    This software is a set of basic test images for ray tracing
and hidden surface algorithms. 

----------------------------------------------------------------------

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by eye!erich on July 9, 1987
# part 2 of 2 - Standard Procedural Databases
# Contents:  makefile definitions.h lib.h lib.c
#            balls.c gears.c mountain.c rings.c tetra.c tree.c 
echo x - makefile
sed 's/^@//' > "makefile" <<'@//E*O*F makefile//'
# makefile for standard procedural databases
# Version:  2.0 (7/9/87)
# Author:  Eric Haines, 3D/Eye, Inc.
#   Works on the HP 300 machines--probably needs to be modified to your system

# cc	= C compiler
#	-c	compile only, do not try to link

CC=cc -c
INC=definitions.h lib.h
BASELIB=-lm -lmalloc /usr/lib/end.o

all:		balls gears mountain rings tetra tree

lib.o:		$(INC) lib.c
		$(CC) lib.c

balls:		lib.o balls.o
		cc -o balls balls.o lib.o $(BASELIB)

balls.o:	$(INC) balls.c
		$(CC) balls.c

gears:		lib.o gears.o
		cc -o gears gears.o lib.o $(BASELIB)

gears.o:	$(INC) gears.c
		$(CC) gears.c

mountain:	lib.o mountain.o
		cc -o mountain mountain.o lib.o $(BASELIB)

mountain.o:	$(INC) mountain.c
		$(CC) mountain.c

rings:		lib.o rings.o
		cc -o rings rings.o lib.o $(BASELIB)

rings.o:	$(INC) rings.c
		$(CC) rings.c

tetra:		lib.o tetra.o
		cc -o tetra tetra.o lib.o $(BASELIB)

tetra.o:	$(INC) tetra.c
		$(CC) tetra.c

tree:		lib.o tree.o
		cc -o tree tree.o lib.o $(BASELIB)

tree.o:		$(INC) tree.c
		$(CC) tree.c
@//E*O*F makefile//
chmod u=rw,g=r,o= makefile
echo x - definitions.h
sed 's/^@//' > "definitions.h" <<'@//E*O*F definitions.h//'
/*
 * definitions.h contains some useful definitions for "C" programs.
 *
 * Version:  2.0 (7/9/87)
 * Author:  Eric Haines, 3D/Eye, Inc.
 */

#define EPSILON		5.0e-6

#ifndef FALSE
#define	FALSE	0
#endif

#ifndef NULL
#define	NULL	0
#endif

#ifndef TRUE
#define	TRUE	1
#endif

#ifndef PI
#define	PI	3.141592653589793
#endif


typedef	double		MATRIX[4][4] ;	/* row major form */

typedef	struct {
	double	x ;
	double	y ;
	double	z ;
	double	w ;
} COORD4, *COORD4_P ;


#define ABSOLUTE(A)		( (A) < 0 ? -(A) : (A) )
#define	FRACTION(A)		( (A) - (long)(A) )
#define	MAX(A,B)		( (A) > (B) ? (A) : (B) )
#define MAX3(A,B,C)		( MAX( MAX( A,B ), C ) )
#define	MIN(A,B)		( (A) < (B) ? (A) : (B) )
#define MIN3(A,B,C)		( MIN( MIN( A,B ), C ) )
#define SQR(A)			( (A) * (A) )

#define ADD2_COORD(r,a)		{ (r).x += (a).x; (r).y += (a).y;\
				  (r).z += (a).z; }
#define ADD3_COORD(r,a,b)	{ (r).x = (a).x + (b).x;\
				  (r).y = (a).y + (b).y;\
				  (r).z = (a).z + (b).z; }
#define COPY_COORD(r,a)		{ (r).x = (a).x; (r).y = (a).y; (r).z = (a).z; }
#define COPY_COORD4(r,a)	{ (r).x = (a).x; (r).y = (a).y; (r).z = (a).z;\
				  (r).w = (a).w; }
#define CROSS(r,a,b)		{ (r).x = (a).y * (b).z - (a).z * (b).y;\
				  (r).y = (a).z * (b).x - (a).x * (b).z;\
				  (r).z = (a).x * (b).y - (a).y * (b).x; }
#define DOT_PRODUCT(a,b)	( (a).x * (b).x +\
				  (a).y * (b).y +\
				  (a).z * (b).z )
#define SET_COORD(r,a,b,c)	{ (r).x = (a); (r).y = (b); (r).z = (c); }
#define SET_COORD4(r,a,b,c,d)	{ (r).x = (a); (r).y = (b); (r).z = (c);\
				  (r).w = (d); }
#define SUB2_COORD(r,a)		{ (r).x -= (a).x; (r).y -= (a).y;\
				  (r).z -= (a).z; }
#define SUB3_COORD(r,a,b)	{ (r).x = (a).x - (b).x;\
				  (r).y = (a).y - (b).y;\
				  (r).z = (a).z - (b).z; }
@//E*O*F definitions.h//
chmod u=rw,g=r,o= definitions.h
echo x - lib.h
sed 's/^@//' > "lib.h" <<'@//E*O*F lib.h//'
/*
 * lib.h - vector library definitions
 *
 * Version:  2.0 (7/9/87)
 * Author:  Eric Haines, 3D/Eye, Inc.
 */

#define	X_AXIS	0
#define	Y_AXIS	1
#define	Z_AXIS	2

/* Output library definitions */
#define OUTPUT_CURVES		0	/* true curve output */
#define OUTPUT_PATCHES		1	/* polygonal patches output */

#define OUTPUT_RESOLUTION	4	/* amount of polygonalization */

double	lib_normalize_coord3() ;
double	lib_gauss_rand() ;
@//E*O*F lib.h//
chmod u=rw,g=r,o= lib.h
echo x - lib.c
sed 's/^@//' > "lib.c" <<'@//E*O*F lib.c//'
/*
 * lib.c - a library of vector operations, a random number generator, and
 *     object output routines.
 *
 * Version:  2.0 (7/9/87)
 * Author:  Eric Haines, 3D/Eye, Inc.
 */

#include 
#include 
#include 
#include "definitions.h"
#include "lib.h"


/*
 * Normalize the vector (X,Y,Z) so that X*X + Y*Y + Z*Z = 1.
 *
 * The normalization divisor is returned.  If the divisor is zero, no
 * normalization occurs.
 *
 */
double	lib_normalize_coord3( cvec )
COORD4	*cvec;
{
    double divisor;


    divisor = sqrt( (double)DOT_PRODUCT( (*cvec), (*cvec) ) ) ;

    if ( divisor != 0.0 ) {
	cvec->x /= divisor;
	cvec->y /= divisor;
	cvec->z /= divisor;
    }

    return( divisor );
}


/*
 * Set all matrix elements to zero.
 */
lib_zero_matrix( mx )
MATRIX	mx ;
{
    long    i, j ;


    for ( i = 0 ; i < 4 ; ++i ) {
	for ( j = 0 ; j < 4 ; ++j ) {
	    mx[i][j] = 0.0 ;
	}
    }
}


/*
 * Create identity matrix.
 */
lib_create_identity_matrix( mx )
MATRIX	mx ;
{
    long    i ;


    lib_zero_matrix( mx ) ;
    for ( i = 0 ; i < 4 ; ++i ) {
	mx[i][i] = 1.0 ;
    }
}


/*
 * Create a rotation matrix along the given axis by the given angle in radians.
 */
lib_create_rotate_matrix( mx, axis, angle )
MATRIX	mx ;
long	axis ;
double	angle ;
{
    double  cosine ;
    double  sine ;


    lib_zero_matrix( mx ) ;

    cosine = cos( (double)angle ) ;
    sine = sin( (double)angle ) ;

    switch ( axis ) {
	case X_AXIS:
	    mx[0][0] = 1.0 ;
	    mx[1][1] = mx[2][2] = cosine ;
	    mx[1][2] = sine ;
	    mx[2][1] = -sine ;
	    break ;
	case Y_AXIS:
	    mx[1][1] = 1.0 ;
	    mx[0][0] = mx[2][2] = cosine ;
	    mx[2][0] = sine ;
	    mx[0][2] = -sine ;
	    break ;
	case Z_AXIS:
	    mx[2][2] = 1.0 ;
	    mx[0][0] = mx[1][1] = cosine ;
	    mx[0][1] = sine ;
	    mx[1][0] = -sine ;
	    break ;
    }
    mx[3][3] = 1.0 ;
}


/*
 * Create a rotation matrix along the given axis by the given angle in radians.
 * The axis is a set of direction cosines.
 */
lib_create_arbitrary_rotate_matrix( mx, rvec, angle )
MATRIX	mx ;
COORD4	*rvec ;
double	angle ;
{
    COORD4  axis ;
    double  cosine ;
    double  one_minus_cosine ;
    double  sine ;


    lib_zero_matrix( mx ) ;

    COPY_COORD( axis, (*rvec) ) ;

    cosine = cos( (double)angle ) ;
    sine = sin( (double)angle ) ;
    one_minus_cosine = 1.0 - cosine ;

    mx[0][0] = SQR(axis.x) + (1.0 - SQR(axis.x)) * cosine ;
    mx[0][1] = axis.x * axis.y * one_minus_cosine + axis.z * sine ;
    mx[0][2] = axis.x * axis.z * one_minus_cosine - axis.y * sine ;

    mx[1][0] = axis.x * axis.y * one_minus_cosine - axis.z * sine ;
    mx[1][1] = SQR(axis.y) + (1.0 - SQR(axis.y)) * cosine ;
    mx[1][2] = axis.y * axis.z * one_minus_cosine + axis.x * sine ;

    mx[2][0] = axis.x * axis.z * one_minus_cosine + axis.y * sine ;
    mx[2][1] = axis.y * axis.z * one_minus_cosine - axis.x * sine ;
    mx[2][2] = SQR(axis.z) + (1.0 - SQR(axis.z)) * cosine ;

    mx[3][3] = 1.0 ;
}


/*
 * Multiply a 4 element vector by a matrix.
 */
lib_transform_coord( vres, vec, mx )
COORD4	*vres ;
COORD4	*vec ;
MATRIX	mx ;
{
    vres->x =
	vec->x*mx[0][0] + vec->y*mx[1][0] + vec->z*mx[2][0] + vec->w*mx[3][0] ;
    vres->y =
	vec->x*mx[0][1] + vec->y*mx[1][1] + vec->z*mx[2][1] + vec->w*mx[3][1] ;
    vres->z =
	vec->x*mx[0][2] + vec->y*mx[1][2] + vec->z*mx[2][2] + vec->w*mx[3][2] ;
    vres->w =
	vec->x*mx[0][3] + vec->y*mx[1][3] + vec->z*mx[2][3] + vec->w*mx[3][3] ;
}


/*
 * Multiply two 4x4 matrices.
 */
lib_matrix_multiply( mxres, mx1, mx2 )
MATRIX	mxres ;
MATRIX	mx1 ;
MATRIX	mx2 ;
{
    long    i ;
    long    j ;


    for ( i = 0; i < 4; i++ ) {
	for ( j = 0; j < 4; j++ ) {
	    mxres[i][j] = mx1[i][0]*mx2[0][j] +
			  mx1[i][1]*mx2[1][j] +
			  mx1[i][2]*mx2[2][j] +
			  mx1[i][3]*mx2[3][j] ;
	}
    }
}


/*
 * Rotate a vector pointing towards the major-axis faces (i.e. the major-axis
 * component of the vector is defined as the largest value) 90 degrees to
 * another cube face.  Mod_face is a face number.
 *
 * If the routine is called six times, with mod_face=0..5, the vector will be
 * rotated to each face of a cube.  Rotations are:
 *     mod_face = 0 mod 3, +Z axis rotate
 *     mod_face = 1 mod 3, +X axis rotate
 *     mod_face = 2 mod 3, -Y axis rotate
 */
lib_rotate_cube_face( vec, major_axis, mod_face )
COORD4	*vec ;
long	major_axis ;
long	mod_face ;
{
    double  swap ;


    mod_face = (mod_face+major_axis) % 3 ;
    if ( mod_face == 0 ) {
	swap   = vec->x ;
	vec->x = -vec->y ;
	vec->y = swap ;
    }
    else if ( mod_face == 1 ) {
	swap   = vec->y ;
	vec->y = -vec->z ;
	vec->z = swap ;
    }
    else {
	swap   = vec->x ;
	vec->x = -vec->z ;
	vec->z = swap ;
    }
}


/*
 * Portable gaussian random number generator (from "Numerical Recipes", GASDEV)
 * Returns a uniform random deviate between 0.0 and 1.0.  'iseed' must be
 * less than M1 to avoid repetition, and less than (2^31-C1)/A1 [= 300718]
 * to avoid overflow.
 */
#define	M1	134456
#define	IA1	8121
#define	IC1	28411
#define	RM1	1.0/M1

double	lib_gauss_rand(iseed)
long	iseed ;
{
    double  fac ;
    long    ix1, ix2 ;
    double  r ;
    double  v1, v2 ;


    ix2 = iseed ;

    do {
	ix1 = (IC1+ix2*IA1) % M1 ;
	ix2 = (IC1+ix1*IA1) % M1 ;
	v1 = ix1 * 2.0 * RM1 - 1.0 ;
	v2 = ix2 * 2.0 * RM1 - 1.0 ;
	r = v1*v1 + v2*v2 ;
    } while ( r >= 1.0 ) ;

    fac = sqrt( (double)( -2.0 * log( (double)r ) / r ) ) ;
    return( v1 * fac ) ;
}




/* OUTPUT ROUTINES
 *
 * Files are output as lines of text.  For each entity, the first line
 * defines its type.  The rest of the first line and possibly other lines
 * contain further information about the entity.  Entities include:
 *
 * "v"  - viewing vectors and angles
 * "l"  - positional light location
 * "b"  - background color
 * "f"  - object material properties
 * "c"  - cone or cylinder primitive
 * "s"  - sphere primitive
 * "p"  - polygon primitive
 * "pp" - polygonal patch primitive
 */

/*
 * Output viewpoint location.  The parameters are:
 *   From:  the eye location.
 *   At:  a position to be at the center of the image.  A.k.a. "lookat"
 *   Up:  a vector defining which direction is up.
 *
 * Note that no assumptions are made about normalizing the data (e.g. the
 * from-at distance does not have to be 1).  Also, vectors are not
 * required to be perpendicular to each other.
 *
 * For all databases some viewing parameters are always the same:
 *
 *   Viewing angle is defined as from the center of top pixel row to bottom
 *     pixel row and left column to right column.
 *   Yon is "at infinity."
 *   Resolution is always 512 x 512.
 */
lib_output_viewpoint( from, at, up, angle, hither, resx, resy )
COORD4	*from ;
COORD4	*at ;
COORD4	*up ;
double	angle ;
double	hither ;
long	resx ;
long	resy ;
{
    printf( "v\n" ) ;
    printf( "from %g %g %g\n", from->x, from->y, from->z ) ;
    printf( "at %g %g %g\n", at->x, at->y, at->z ) ;
    printf( "up %g %g %g\n", up->x, up->y, up->z ) ;
    printf( "angle %g\n", angle ) ;
    printf( "hither %g\n", hither ) ;
    printf( "resolution %d %d\n", resx, resy ) ;
}


/*
 * Output light.  A light is defined by position.  All lights have the same
 * intensity.
 *
 */
lib_output_light( center_pt )
COORD4	*center_pt ;
{
    printf( "l %g %g %g\n", center_pt->x, center_pt->y, center_pt->z ) ;
}


/*
 * Output background color.  A color is simply RGB (monitor dependent, but
 * that's life).  The format is:
 *     "b" red green blue
 */
lib_output_background_color( color )
COORD4	*color ;
{
    printf( "b %g %g %g\n", color->x, color->y, color->z ) ;
}


/*
 * Output a color and shading parameters for the object in the format:
 *     "f" red green blue Kd Ks Shine T index_of_refraction
 *
 * Kd is the diffuse component, Ks the specular, Shine is the Phong cosine
 * power for highlights, T is transmittance (fraction of light passed per unit).
 * 0 <= Kd <= 1 and 0 <= Ks <= 1, though it is not required that Kd + Ks == 1.
 *
 * The fill color is used to color the objects following it until a new color
 * is assigned or the file ends.
 */
lib_output_color( color, kd, ks, shine, t, i_of_r )
COORD4	*color ;
double	kd ;
double	ks ;
double	shine ;
double	t ;
double	i_of_r ;
{
    printf( "f %g %g %g %g %g %g %g %g\n", color->x, color->y, color->z,
						    kd, ks, shine, t, i_of_r ) ;
}


/*
 * Output cylinder or cone.  A cylinder is defined as having a radius and an
 * axis defined by two points, which also define the top and bottom edge of the
 * cylinder.  A cone is defined similarly, the difference being that the apex
 * and base radii are different.  The apex radius is defined as being smaller
 * than the base radius.  Note that the surface exists without endcaps.
 *
 * If format=OUTPUT_CURVES, output the cylinder/cone in format:
 *     "c"
 *     base.x base.y base.z base_radius
 *     apex.x apex.y apex.z apex_radius
 *
 * If the format=OUTPUT_POLYGONS, the surface is polygonalized and output.
 * (4*OUTPUT_RESOLUTION) polygons are output as rectangles by
 * lib_output_polypatch.
 */
lib_output_cylcone( base_pt, apex_pt, format )
COORD4	*base_pt ;
COORD4	*apex_pt ;
long	format ;
{
    double  angle ;
    COORD4  axis ;
    COORD4  dir ;
    double  divisor ;
    COORD4  lip_norm[4], lip_pt[4] ;
    MATRIX  mx ;
    COORD4  norm_axis ;
    long    num_pol ;
    COORD4  start_norm, start_radius[4] ;


    if ( format == OUTPUT_CURVES ) {
	printf( "c\n" ) ;
	printf( "%g %g %g %g\n",
			    base_pt->x, base_pt->y, base_pt->z, base_pt->w) ;
	printf( "%g %g %g %g\n",
			    apex_pt->x, apex_pt->y, apex_pt->z, apex_pt->w ) ;
    }
    else {
	SUB3_COORD( axis, (*apex_pt), (*base_pt) ) ;
	COPY_COORD( norm_axis, axis ) ;
	lib_normalize_coord3( &norm_axis ) ;

	dir.x = 0.0 ; dir.y = 0.0 ; dir.z = 1.0 ; dir.w = 0.0 ;
	CROSS( start_norm, axis, dir ) ;

	divisor = lib_normalize_coord3( &start_norm ) ;
	if ( ABSOLUTE( divisor ) < EPSILON ) {
	    dir.x = 1.0 ; dir.y = 0.0 ; dir.z = 0.0 ;
	    CROSS( start_norm, axis, dir ) ;
	    lib_normalize_coord3( &start_norm ) ;
	}

	start_radius[0].x = start_norm.x * base_pt->w ;
	start_radius[0].y = start_norm.y * base_pt->w ;
	start_radius[0].z = start_norm.z * base_pt->w ;
	start_radius[0].w = 0.0 ;
	ADD3_COORD( lip_pt[0], (*base_pt), start_radius[0] ) ;

	start_radius[1].x = start_norm.x * apex_pt->w ;
	start_radius[1].y = start_norm.y * apex_pt->w ;
	start_radius[1].z = start_norm.z * apex_pt->w ;
	start_radius[1].w = 0.0 ;
	ADD3_COORD( lip_pt[1], (*apex_pt), start_radius[1] ) ;

	COPY_COORD4( lip_norm[0], start_norm ) ;
	COPY_COORD4( lip_norm[1], start_norm ) ;

	for ( num_pol = 0 ; num_pol < 4*OUTPUT_RESOLUTION ; ++num_pol ) {
	    COPY_COORD4( lip_pt[3], lip_pt[0] ) ;
	    COPY_COORD4( lip_pt[2], lip_pt[1] ) ;
	    COPY_COORD4( lip_norm[3], lip_norm[0] ) ;
	    COPY_COORD4( lip_norm[2], lip_norm[1] ) ;

	    angle = 2.0 * PI *
		(double)( num_pol+1 ) / (double)( 4*OUTPUT_RESOLUTION ) ;
	    lib_create_arbitrary_rotate_matrix( mx, &norm_axis, angle ) ;

	    lib_transform_coord( &lip_pt[0], &start_radius[0], mx ) ;
	    ADD2_COORD( lip_pt[0], (*base_pt) ) ;
	    lib_transform_coord( &lip_pt[1], &start_radius[1], mx ) ;
	    ADD2_COORD( lip_pt[1], (*apex_pt) ) ;

	    lib_transform_coord( &lip_norm[0], &start_norm, mx ) ;
	    COPY_COORD4( lip_norm[1], lip_norm[0] ) ;

	    lib_output_polypatch( 4, lip_pt, lip_norm ) ;
	}
    }
}


/*
 * Output sphere.  A sphere is defined by a radius and center position.
 *
 * If format=OUTPUT_CURVES, output the sphere in format:
 *     "s" center.x center.y center.z radius
 *
 * If the format=OUTPUT_POLYGONS, the sphere is polygonalized and output.
 * The sphere is polygonalized by splitting it into 6 faces (of a cube
 * projected onto the sphere) and dividing these faces by equally spaced
 * great circles.  OUTPUT_RESOLUTION affects the number of great circles.
 * (6*2*OUTPUT_RESOLUTION*OUTPUT_RESOLUTION) polygons are output as triangles
 * using lib_output_polypatch.
 */
lib_output_sphere( center_pt, format )
COORD4	*center_pt ;
long    format ;
{
    double  angle ;
    COORD4  edge_norm[3], edge_pt[3] ;
    long    num_face, num_edge, num_tri, num_vert ;
    COORD4  x_axis[OUTPUT_RESOLUTION+1], y_axis[OUTPUT_RESOLUTION+1] ;
    COORD4  pt[OUTPUT_RESOLUTION+1][OUTPUT_RESOLUTION+1] ;
    COORD4  mid_axis ;
    MATRIX  rot_mx ;
    long    u_pol, v_pol ;


    if ( format == OUTPUT_CURVES ) {
	printf( "s %g %g %g %g\n",
		    center_pt->x, center_pt->y, center_pt->z, center_pt->w ) ;
    }
    else {
	/* calculate axes used to find grid points */
	for ( num_edge = 0 ; num_edge <= OUTPUT_RESOLUTION ; ++num_edge ) {
 	    angle = (PI/4.0) * (2.0*(double)num_edge/OUTPUT_RESOLUTION - 1.0) ;
	    mid_axis.w = 0.0 ;

	    mid_axis.x = 1.0 ; mid_axis.y = 0.0 ; mid_axis.z = 0.0 ;
	    lib_create_rotate_matrix( rot_mx, Y_AXIS, angle ) ;
	    lib_transform_coord( &x_axis[num_edge], &mid_axis, rot_mx ) ;

	    mid_axis.x = 0.0 ; mid_axis.y = 1.0 ; mid_axis.z = 0.0 ;
	    lib_create_rotate_matrix( rot_mx, X_AXIS, angle ) ;
	    lib_transform_coord( &y_axis[num_edge], &mid_axis, rot_mx ) ;
	}
	    
	/* set up grid of points on +Z sphere surface */
	for ( u_pol = 0 ; u_pol <= OUTPUT_RESOLUTION ; ++u_pol ) {
	    for ( v_pol = 0 ; v_pol <= OUTPUT_RESOLUTION ; ++v_pol ) {
		CROSS( pt[u_pol][v_pol], x_axis[u_pol], y_axis[v_pol] ) ;
		lib_normalize_coord3( &pt[u_pol][v_pol] ) ;
		pt[u_pol][v_pol].w = 1.0 ;
	    }
	}
	for ( num_face = 0 ; num_face < 6 ; ++num_face ) {
	    /* transform points to cube face */
	    for ( u_pol = 0 ; u_pol <= OUTPUT_RESOLUTION ; ++u_pol ) {
		for ( v_pol = 0 ; v_pol <= OUTPUT_RESOLUTION ; ++v_pol ) {
		    lib_rotate_cube_face( &pt[u_pol][v_pol]
					, Z_AXIS
					, num_face
					) ;
		}
	    }
	    /* output grid */
	    for ( u_pol = 0 ; u_pol < OUTPUT_RESOLUTION ; ++u_pol ) {
		for ( v_pol = 0 ; v_pol < OUTPUT_RESOLUTION ; ++v_pol ) {
		    for ( num_tri = 0 ; num_tri < 2 ; ++num_tri ) {
			for ( num_edge = 0 ; num_edge < 3 ; ++num_edge ) {
			    num_vert = (num_tri*2 + num_edge) % 4 ;
			    if ( num_vert == 0 ) {
				COPY_COORD4( edge_pt[num_edge],
					     pt[u_pol][v_pol] ) ;
			    }
			    else if ( num_vert == 1 ) {
				COPY_COORD4( edge_pt[num_edge],
					     pt[u_pol][v_pol+1] ) ;
			    }
			    else if ( num_vert == 2 ) {
				COPY_COORD4( edge_pt[num_edge],
					     pt[u_pol+1][v_pol+1] ) ;
			    }
			    else {
				COPY_COORD4( edge_pt[num_edge],
					     pt[u_pol+1][v_pol] ) ;
			    }
			    COPY_COORD4( edge_norm[num_edge],
					 edge_pt[num_edge] ) ;
			    edge_pt[num_edge].x =
					    edge_pt[num_edge].x * center_pt->w +
								  center_pt->x ;
			    edge_pt[num_edge].y =
					    edge_pt[num_edge].y * center_pt->w +
								  center_pt->y ;
			    edge_pt[num_edge].z =
					    edge_pt[num_edge].z * center_pt->w +
								  center_pt->z ;

			}
			lib_output_polypatch( 3, edge_pt, edge_norm ) ;
		    }
		}
	    }
	}
    }
}


/*
 * Output polygon.  A polygon is defined by a set of vertices.  With these
 * databases, a polygon is defined to have all points coplanar.  A polygon has
 * only one side, with the order of the vertices being counterclockwise as you
 * face the polygon (right-handed coordinate system).
 *
 * The output format is always:
 *     "p" total_vertices
 *     vert1.x vert1.y vert1.z
 *     [etc. for total_vertices polygons]
 *
 */
lib_output_polygon( tot_vert, vert )
long	tot_vert ;
COORD4	*vert ;
{
    long    num_vert ;


    printf( "p %d\n", tot_vert ) ;

    for ( num_vert = 0 ; num_vert < tot_vert ; ++num_vert ) {
	printf( "%g %g %g\n", vert[num_vert].x
			    , vert[num_vert].y
			    , vert[num_vert].z
			    ) ;
    }
}


/*
 * Output polygonal patch.  A patch is defined by a set of vertices and their
 * normals.  With these databases, a patch is defined to have all points
 * coplanar.  A patch has only one side, with the order of the vertices being
 * counterclockwise as you face the patch (right-handed coordinate system).
 *
 * The output format is always:
 *     "pp" total_vertices
 *     vert1.x vert1.y vert1.z norm1.x norm1.y norm1.z
 *     [etc. for total_vertices polygonal patches]
 *
 */
lib_output_polypatch( tot_vert, vert, norm )
long	tot_vert ;
COORD4	*vert ;
COORD4	*norm ;
{
    long    num_vert ;


    printf( "pp %d\n", tot_vert ) ;

    for ( num_vert = 0 ; num_vert < tot_vert ; ++num_vert ) {
	printf( "%g %g %g %g %g %g\n", vert[num_vert].x
				     , vert[num_vert].y
				     , vert[num_vert].z
				     , norm[num_vert].x
				     , norm[num_vert].y
				     , norm[num_vert].z
				     ) ;
    }
}
@//E*O*F lib.c//
chmod u=rw,g=r,o= lib.c
echo x - balls.c
sed 's/^@//' > "balls.c" <<'@//E*O*F balls.c//'
/*
 * balls.c - Create a set of shiny spheres, with each sphere blooming sets of
 *     9 more spheres with 1/3rd radius.  None of the spheres are clipped.  A
 *     square floor polygon is added.  Three light sources.
 *
 * Version:  2.0 (7/9/87)
 * Author:  Eric Haines, 3D/Eye, Inc.
 *
 * SIZE_FACTOR determines the number of objects output.
 *     Total spheres = sum of n=0,SF of (9**SF).
 *
 *     SIZE_FACTOR	# spheres	# squares
 *	    1		    10		     1
 *	    2		    91		     1
 *	    3		   820		     1
 *
 *	    4		  7381		     1
 */

#include 
#include 
#include 
#include "definitions.h"
#include "lib.h"

#define	OUTPUT_FORMAT		OUTPUT_CURVES
#define	SIZE_FACTOR		4

static	COORD4	objset[9] ;


main(argc,argv)
int argc; 
char *argv[];
{
    COORD4  back_color, obj_color ;
    COORD4  backg[4], bvec, light ;
    COORD4  from, at, up ;
    COORD4  center_pt, direction ;
    double  radius ;


    /* set radius of sphere which would enclose entire object */
    radius = 1.0 ;

    /* output viewpoint */
    SET_COORD( from, 2.1, 1.3, 1.7 ) ;
    SET_COORD( at, 0.0, 0.0, 0.0 ) ;
    SET_COORD( up, 0.0, 0.0, 1.0 ) ;
    lib_output_viewpoint( &from, &at, &up, 45.0, 1.0, 512, 512 ) ;

    /* output background color - UNC sky blue */
    SET_COORD( back_color, 0.078, 0.361, 0.753 ) ;
    lib_output_background_color( &back_color ) ;

    /* output light sources */
    SET_COORD( light, 4.0, 3.0, 2.0 ) ;
    lib_output_light( &light ) ;
    SET_COORD( light, 1.0, -4.0, 4.0 ) ;
    lib_output_light( &light ) ;
    SET_COORD( light, -3.0, 1.0, 5.0 ) ;
    lib_output_light( &light ) ;

    /* output floor polygon - beige */
    SET_COORD( back_color, 1.0, 0.75, 0.33 ) ;
    lib_output_color( &back_color, 1.0, 0.0, 0.0, 0.0, 0.0 ) ;
    bvec.x = bvec.y = radius * 12.0 ;
    bvec.z = -radius / 2.0 ;
    SET_COORD( backg[0],  bvec.x,  bvec.y, bvec.z ) ;
    SET_COORD( backg[1], -bvec.x,  bvec.y, bvec.z ) ;
    SET_COORD( backg[2], -bvec.x, -bvec.y, bvec.z ) ;
    SET_COORD( backg[3],  bvec.x, -bvec.y, bvec.z ) ;
    lib_output_polygon( 4, backg ) ;

    /* set up object color - off white */
    SET_COORD( obj_color, 1.0, 0.9, 0.7 ) ;
    lib_output_color( &obj_color, 0.5, 0.5, 3.0, 0.0, 0.0 ) ;

    /* create set of spawned points */
    create_objset() ;

    /* compute and output object */
    SET_COORD4( center_pt, 0.0, 0.0, 0.0, radius / 2.0 ) ;
    SET_COORD4( direction, 0.0, 0.0, 1.0, 1.0/3.0 ) ;
    output_object( SIZE_FACTOR, ¢er_pt, &direction ) ;
}


/* Create the set of 9 vectors needed to generate the sphere set. */
/* Uses global 'objset' */
create_objset()
{
    COORD4  axis, temp_pt, trio_dir[3] ;
    double  dist ;
    MATRIX  mx ;
    long    num_set, num_vert ;


    dist = 1.0 / sqrt( (double)2.0 ) ;

    SET_COORD4( trio_dir[0], dist, dist,   0.0, 0.0 ) ;
    SET_COORD4( trio_dir[1], dist,  0.0, -dist, 0.0 ) ;
    SET_COORD4( trio_dir[2],  0.0, dist, -dist, 0.0 ) ;
    
    SET_COORD( axis, 1.0, -1.0, 0.0 ) ;
    lib_normalize_coord3( &axis ) ;
    lib_create_arbitrary_rotate_matrix(
	  mx,
	  &axis,
	  asin( (double) ( 2.0 / sqrt( (double)6.0 ) ) ) ) ;

    for ( num_vert = 0 ; num_vert < 3 ; ++num_vert ) {
	lib_transform_coord( &temp_pt, &trio_dir[num_vert], mx ) ;
	COPY_COORD( trio_dir[num_vert], temp_pt ) ;
    }

    for ( num_set = 0 ; num_set < 3 ; ++num_set ) {
	lib_create_rotate_matrix( mx, Z_AXIS, num_set*2.0*PI/3.0 ) ;
	for ( num_vert = 0 ; num_vert < 3 ; ++num_vert ) {
	    lib_transform_coord( &objset[num_set*3+num_vert],
						    &trio_dir[num_vert], mx ) ;
	}
    }
}


/*
 * Output the parent sphere, then output the children of the sphere.
 * Uses global 'objset'.
 */
output_object( depth, center, direction )
long	depth ;
COORD4	*center ;
COORD4	*direction ;
{
    double  angle ;
    COORD4  axis, z_axis ;
    COORD4  child_pt, child_dir ;
    MATRIX  mx ;
    long    num_vert ;
    double  scale ;


    /* output sphere at location & radius defined by center */
    lib_output_sphere( center, OUTPUT_FORMAT ) ;

    /* check if children should be generated */
    if ( depth > 0 ) {
	--depth ;

	/* rotation matrix to new axis from +Z axis */
	if ( direction->z >= 1.0 ) {
	    /* identity matrix */
	    lib_create_identity_matrix( mx ) ;
	}
	else if ( direction->z <= -1.0 ) {
	    lib_create_rotate_matrix( mx, Y_AXIS, PI ) ;
	}
	else {
	    SET_COORD( z_axis, 0.0, 0.0, 1.0 ) ;
	    CROSS( axis, z_axis, (*direction) ) ;
	    lib_normalize_coord3( &axis ) ;
	    angle = acos( (double)DOT_PRODUCT( z_axis, (*direction) ) ) ;
	    lib_create_arbitrary_rotate_matrix( mx, &axis, angle ) ;
	}

	/* scale down location of new spheres */
	scale = center->w * (1.0 + direction->w ) ;

	for ( num_vert = 0 ; num_vert < 9 ; ++num_vert ) {
	    lib_transform_coord( &child_pt, &objset[num_vert], mx ) ;
	    child_pt.x = child_pt.x * scale + center->x ;
	    child_pt.y = child_pt.y * scale + center->y ;
	    child_pt.z = child_pt.z * scale + center->z ;
	    /* scale down radius */
	    child_pt.w = center->w * direction->w ;
	    SUB3_COORD( child_dir, child_pt, (*center) ) ;
	    child_dir.x /= scale ;
	    child_dir.y /= scale ;
	    child_dir.z /= scale ;
	    child_dir.w = direction->w ;
	    output_object( depth, &child_pt, &child_dir ) ;
	}
    }
}
@//E*O*F balls.c//
chmod u=rw,g=r,o= balls.c
echo x - gears.c
sed 's/^@//' > "gears.c" <<'@//E*O*F gears.c//'
/*
 * gears.c - Create a set of gears.  Each gear face has 144 vertices, and
 *	contains concavities.  Note that the first 3 vertices of all polygons
 *	define the two edges of a convex section of the polygon.  Background
 *	square floor is reflective.  Some gears are clipped.
 *	Five light sources.  
 *
 * Version:  2.0 (7/9/87)
 * Author:  Eric Haines, 3D/Eye, Inc.
 *
 * SIZE_FACTOR determines the number of polygons output.
 *     Total gears = SF**3:  concave polygons = 2 * SF**3
 *     rectangles = 4*TEETH * SF**3
 *
 *     SIZE_FACTOR	# gears	   # gear faces    # rectangles
 *	    1		    1		  2		144
 *	    2		    8		 16	       1152
 *	    3		   27		 54	       3888
 *	    4		   64		128	       9216
 */

#include 
#include 
#include 
#include "definitions.h"
#include "lib.h"

#define	SIZE_FACTOR		4

/* define number of teeth on a gear - must be a multiple of 4 */
#define	TEETH			36
/* define ratio of radius taken up by teeth and the gear thickness */
#define	EDGE_RATIO		0.1
#define	DEPTH_RATIO		0.1


main(argc,argv)
int argc; 
char *argv[];
{
    COORD4  back_color, gear_color ;
    COORD4  center_pt, floor[4], light, offset, zero_pt ;
    COORD4  from, at, up ;
    double  angle, color_scale, outer_radius, thickness ;
    long    ix, iy, iz ;


    /* output viewpoint */
    SET_COORD( from, -1.1, -2.1, 2.6 ) ;
    SET_COORD( at, 0.0, 0.0, 0.0 ) ;
    SET_COORD( up, 0.0, 0.0, 1.0 ) ;
    lib_output_viewpoint( &from, &at, &up, 45.0, 1.0, 512, 512 ) ;

    /* output background color - UNC sky blue */
    SET_COORD( back_color, 0.078, 0.361, 0.753 ) ;
    lib_output_background_color( &back_color ) ;

    /* output light sources */
    SET_COORD( light, 2.0, 4.0, 4.0 ) ;
    lib_output_light( &light ) ;
    SET_COORD( light, -2.0, 4.0, 3.0 ) ;
    lib_output_light( &light ) ;
    SET_COORD( light, 2.0, -2.5, 2.5 ) ;
    lib_output_light( &light ) ;
    SET_COORD( light, -1.0, -4.0, 2.0 ) ;
    lib_output_light( &light ) ;
    /* just behind the eye */
    SET_COORD( light, -1.111, -2.121, 2.626 ) ;
    lib_output_light( &light ) ;

    /* output floor polygon - off white */
    SET_COORD( back_color, 1.0, 0.85, 0.7 ) ;
    lib_output_color( &back_color, 0.75, 0.25, 25.0, 0.0, 0.0 ) ;
    SET_COORD( floor[0], 2.0, 2.0, 0.0 ) ;
    SET_COORD( floor[1], -2.0, 2.0, 0.0 ) ;
    SET_COORD( floor[2], -2.0, -2.0, 0.0 ) ;
    SET_COORD( floor[3], 2.0, -2.0, 0.0 ) ;
    lib_output_polygon( 4, floor ) ;

    outer_radius = 1.0 /
	( (double)SIZE_FACTOR - (double)(SIZE_FACTOR-1) * EDGE_RATIO / 2.0 ) ;
    /* calculate first gear center */
    zero_pt.x = zero_pt.y = -1.0 + outer_radius ;
    zero_pt.z = 1.0 ;
    /* calculate offset */
    offset.x = offset.y = outer_radius * ( 2.0 - EDGE_RATIO ) ;
    offset.z = -1.0 / (double)SIZE_FACTOR ;

    /* create gears */
    for ( iz = 0 ; iz < SIZE_FACTOR ; ++iz ) {
	center_pt.z = zero_pt.z + (double)iz * offset.z ;
	for ( iy = 0 ; iy < SIZE_FACTOR ; ++iy ) {
	    center_pt.y = zero_pt.y + (double)iy * offset.y ;
	    for ( ix = 0 ; ix < SIZE_FACTOR ; ++ix ) {
		center_pt.x = zero_pt.x + (double)ix * offset.x ;

		/* output pseudo-random gear color */
		SET_COORD( gear_color
			 , 0.01 + FRACTION( (double)(ix*3+iy*2+iz+1)*5.0/7.0 )
			 , 0.01 + FRACTION( (double)(iy*3+iz*2+ix+1)*3.0/7.0 )
			 , 0.01 + FRACTION( (double)(iz*3+ix*2+iy+1)*2.0/7.0 )
			 ) ;
		color_scale = MAX3( gear_color.x, gear_color.y, gear_color.z ) ;
		gear_color.x /= color_scale ;
		gear_color.y /= color_scale ;
		gear_color.z /= color_scale ;
		if ( ( ix*4 + iy*2 + iz ) % 5 == 0 ) {
		    lib_output_color( &gear_color,
						0.75, 0.25, 50.0, 0.95, 1.1 ) ;
		}
		else {
		    lib_output_color( &gear_color,
						1.0, 0.0, 0.0, 0.0, 0.0 ) ;
		}

		/* output gear */
		angle = PI * (double)( (ix+iy+iz) % 2 ) / (double)(TEETH) ;
		thickness =
		    MIN( DEPTH_RATIO, 1.0 / ( 2.0 * (double)SIZE_FACTOR ) ) ;
		create_gear( ¢er_pt,
			     angle,
			     outer_radius,
			     (1.0 - EDGE_RATIO) * outer_radius,
			     thickness ) ;
	    }
	}
    }
}


/* Create gear */
create_gear( center, offset_angle, outer_radius, inner_radius, thickness )
COORD4	*center ;
double	offset_angle ;
double	outer_radius ;
double	inner_radius ;
double	thickness ;
{
    COORD4	side_pts[4], gear_pts[4*TEETH], outer_pt, inner_pt ;
    long	next_side, num_side, num_teeth ;
    double	gear_angle, tooth_angle ;


    outer_pt.x = outer_radius ;
    outer_pt.y = 0.0 ;
    outer_pt.z = 0.0 ;
    outer_pt.w = 1.0 ;
    inner_pt.x = inner_radius ;
    inner_pt.y = 0.0 ;
    inner_pt.z = 0.0 ;
    inner_pt.w = 1.0 ;

    tooth_angle = 2.0 * PI / (double)TEETH ;

    /* output gear top */
    for ( num_teeth = 0 ; num_teeth < TEETH ; ++num_teeth ) {
	gear_angle = offset_angle +
		2.0 * PI * (double)num_teeth / (double)TEETH ;
	create_tooth( gear_angle
		    , tooth_angle
		    , center
		    , &outer_pt
		    , &inner_pt
		    , &gear_pts[num_teeth*4]
		    ) ;
    }
    lib_output_polygon( 4*TEETH, gear_pts ) ;

    /* output teeth */
    for ( num_side = 0 ; num_side < 4 * TEETH ; ++num_side ) {
	next_side = ( num_side + 1 ) % ( 4 * TEETH ) ;
	COPY_COORD( side_pts[0], gear_pts[num_side] ) ;
	COPY_COORD( side_pts[1], gear_pts[num_side] ) ;
	side_pts[1].z -= thickness ;
	COPY_COORD( side_pts[2], gear_pts[next_side] ) ;
	side_pts[2].z -= thickness ;
	COPY_COORD( side_pts[3], gear_pts[next_side] ) ;
	lib_output_polygon( 4, side_pts ) ;
    }

    /* output gear bottom */
    outer_pt.z = inner_pt.z = -thickness ;
    for ( num_teeth = 0 ; num_teeth < TEETH ; ++num_teeth ) {
	gear_angle = offset_angle -
		2.0 * PI * (double)num_teeth / (double)TEETH ;
	create_tooth( gear_angle
		    , -tooth_angle
		    , center
		    , &outer_pt
		    , &inner_pt
		    , &gear_pts[num_teeth*4] 
		    ) ;
    }
    lib_output_polygon( 4*TEETH, gear_pts ) ;
}


/* Create gear tooth */
create_tooth( gear_angle, tooth_angle, center, outer_pt, inner_pt, edge_pts )
double	gear_angle ;
double	tooth_angle ;
COORD4	*center ;
COORD4	*outer_pt ;
COORD4	*inner_pt ;
COORD4	*edge_pts ;
{
    MATRIX	mx ;


    lib_create_rotate_matrix( mx
			    , Z_AXIS
			    , gear_angle - 0.19 * tooth_angle ) ;
    lib_transform_coord( &edge_pts[0], outer_pt, mx ) ;
    ADD2_COORD( edge_pts[0], *center ) ;
    lib_create_rotate_matrix( mx
			    , Z_AXIS
			    , gear_angle + 0.19 * tooth_angle ) ;
    lib_transform_coord( &edge_pts[1], outer_pt, mx ) ;
    ADD2_COORD( edge_pts[1], *center ) ;
    lib_create_rotate_matrix( mx
			    , Z_AXIS
			    , gear_angle + 0.3 * tooth_angle ) ;
    lib_transform_coord( &edge_pts[2], inner_pt, mx ) ;
    ADD2_COORD( edge_pts[2], *center ) ;
    lib_create_rotate_matrix( mx
			    , Z_AXIS
			    , gear_angle + 0.7 * tooth_angle ) ;
    lib_transform_coord( &edge_pts[3], inner_pt, mx ) ;
    ADD2_COORD( edge_pts[3], *center ) ;
}
@//E*O*F gears.c//
chmod u=rw,g=r,o= gears.c
echo x - mountain.c
sed 's/^@//' > "mountain.c" <<'@//E*O*F mountain.c//'
/*
 * mountain.c - creates a fractal mountain, using Carpenter's method with a
 *     different extension to square grids.  A pyramid of 4 glass spheres
 *     is added in front of the mountain.  None of the spheres are clipped.
 *     A few of the polygons are clipped.  One light source.
 *
 * Version:  2.0 (7/9/87)
 * Author:  Eric Haines, 3D/Eye, Inc.
 *
 * SIZE_FACTOR determines the number of objects output.
 *     Total triangular polygons = 2 * (4**SIZE_FACTOR)
 *
 *     SIZE_FACTOR	# triangles	# spheres
 *	    1		     8		     4
 *	    2		    32		     4
 *	    3		   128		     4
 *
 *	    6		  8192		     4
 */

#include 
#include 
#include 
#include "definitions.h"
#include "lib.h"

#define	OUTPUT_FORMAT		OUTPUT_CURVES
/* size factor determines number of polygons */
#define	SIZE_FACTOR		6

/* fractal dimension - affects variance of z.  Between 2 and 3 */
#define	FRACTAL_DIMENSION	2.2
/* change MOUNTAIN_NO to get a different mountain */
#define	MOUNTAIN_NO		21

/* lower left corner and width of mountain definitions */
#define	X_CORNER	-1.0
#define	Y_CORNER	-1.0
#define	WIDTH		 2.0

/* hashing function to get a seed for the random number generator */
#define	hash_rand(A,B,C)	( ( ((A)<<(23-(C))) + ((B)<<(15-(C)))\
				  + ((A)<<(7-(C))) ) & 0xffff )

static	long    num_pts ;
static  double	roughness ;

main(argc,argv)
int argc; 
char *argv[];
{
    COORD4  back_color, obj_color ;
    COORD4  center, light ;
    COORD4  from, at, up ;
    double  ratio ;


    /* output viewpoint */
    SET_COORD( from, -1.6, 1.6, 1.7 ) ;
    SET_COORD( at, 0.0, 0.0, 0.0 ) ;
    SET_COORD( up, 0.0, 0.0, 1.0 ) ;
    lib_output_viewpoint( &from, &at, &up, 45.0, 0.5, 512, 512 ) ;

    /* output background color - UNC sky blue */
    SET_COORD( back_color, 0.078, 0.361, 0.753 ) ;
    lib_output_background_color( &back_color ) ;

    /* output light sources */
    SET_COORD( light, -100.0, -100.0, 100.0 ) ;
    lib_output_light( &light ) ;

    /* set up crystal sphere color - clear white */
    SET_COORD( obj_color, 1.0, 1.0, 1.0 ) ;
    lib_output_color( &obj_color, 0.1, 0.9, 100.0, 0.9, 1.5 ) ;

    /* output crystal spheres */
    SET_COORD4( center, -0.8, 0.8, 1.00, 0.17 ) ;
    create_spheres( ¢er ) ;

    /* set up mountain color - grey */
    SET_COORD( obj_color, 0.5, 0.45, 0.35 ) ;
    lib_output_color( &obj_color, 1.0, 0.0, 0.0, 0.0, 0.0 ) ;

    /* grow mountain */
    num_pts = 1<w * sqrt( (double)( 3.0/2.0 ) ), 0.0 ) ;

    COPY_COORD( sphere, pt ) ;
    ADD2_COORD( sphere, *center ) ;
    sphere.w = center->w ;
    lib_output_sphere( &sphere, OUTPUT_FORMAT ) ;

    lib_create_arbitrary_rotate_matrix( mx, &axis, angle ) ;
    lib_transform_coord( &new_pt, &pt, mx ) ;

    for ( i = 0 ; i < 3 ; ++i ) {
	lib_create_rotate_matrix( mx, Z_AXIS, (double)i * 2.0 * PI / 3.0 ) ;
	lib_transform_coord( &sphere, &new_pt, mx ) ;
	ADD2_COORD( sphere, *center ) ;
	sphere.w = center->w ;
	lib_output_sphere( &sphere, OUTPUT_FORMAT ) ;
    }
}




/*
 * Build mountain section.  If at width > 1, split quadrilateral into four
 * parts.  Else if at width == 1, output quadrilateral as two triangles.
 */
grow_mountain( width, ll_x, ll_y, ll_fz, lr_fz, ur_fz, ul_fz )
long	width ;
long	ll_x ;
long	ll_y ;
double	ll_fz ;
double	lr_fz ;
double	ur_fz ;
double	ul_fz ;
{
    long    half_width, iz ;
    double  l_fx, r_fx, l_fy, u_fy ;
    double  lower_fz, right_fz, upper_fz, left_fz, middle_fz ;
    long    num_tri, num_tri_vert, num_vert ;
    double  rise_height, hside_length ;
    COORD4  tri_vert[3] ;


    if ( width == 1 ) {
	/* calculate x and y coordinates of corners */
	l_fx = X_CORNER + (double)ll_x * WIDTH / (double)num_pts ;
	r_fx = X_CORNER + (double)(ll_x+1) * WIDTH / (double)num_pts ;
	l_fy = Y_CORNER + (double)ll_y * WIDTH / (double)num_pts ;
	u_fy = Y_CORNER + (double)(ll_y+1) * WIDTH / (double)num_pts ;

	/* output two triangles for section */
	for ( num_tri = 0 ; num_tri < 2 ; ++num_tri ) {
	    for ( num_vert = 0 ; num_vert < 3 ; ++num_vert ) {
		num_tri_vert = ( num_vert + num_tri * 2 ) % 4 ;
		if ( num_tri_vert == 0 ) {
		    SET_COORD( tri_vert[num_vert], l_fx, l_fy, ll_fz ) ;
		}
		else if ( num_tri_vert == 1 ) {
		    SET_COORD( tri_vert[num_vert], r_fx, l_fy, lr_fz ) ;
		}
		else if ( num_tri_vert == 2 ) {
		    SET_COORD( tri_vert[num_vert], r_fx, u_fy, ur_fz ) ;
		}
		else {
		    SET_COORD( tri_vert[num_vert], l_fx, u_fy, ul_fz ) ;
		}
	    }
	    lib_output_polygon( 3, tri_vert ) ;
	}
    }

    else {
	/* subdivide edges and move in z direction */
	half_width = width>>1 ;
	hside_length = (double)half_width * WIDTH / (double)num_pts ;
	rise_height = hside_length * roughness ;

	/* for each midpoint, find z */
	iz = MOUNTAIN_NO + hash_rand( ll_x + half_width, ll_y, SIZE_FACTOR ) ;
	lower_fz = ( ll_fz + lr_fz ) / 2.0 +
					rise_height * lib_gauss_rand( iz ) ;
	iz = MOUNTAIN_NO +
		hash_rand( ll_x + width, ll_y + half_width, SIZE_FACTOR ) ;
	right_fz = ( lr_fz + ur_fz ) / 2.0 +
					rise_height * lib_gauss_rand( iz ) ;
	iz = MOUNTAIN_NO +
		hash_rand( ll_x + half_width, ll_y + width, SIZE_FACTOR ) ;
	upper_fz = ( ur_fz + ul_fz ) / 2.0 +
					rise_height * lib_gauss_rand( iz ) ;
	iz = MOUNTAIN_NO + hash_rand( ll_x, ll_y + half_width, SIZE_FACTOR ) ;
	left_fz  = ( ul_fz + ll_fz ) / 2.0 +
					rise_height * lib_gauss_rand( iz ) ;
	iz = MOUNTAIN_NO +
		hash_rand( ll_x + half_width, ll_y + half_width, SIZE_FACTOR ) ;
	middle_fz = ( ll_fz + lr_fz + ur_fz + ul_fz ) / 4.0 + 
				1.4142136 * rise_height * lib_gauss_rand( iz ) ;

	/* check subsections for subdivision or output */
	grow_mountain( half_width, ll_x, ll_y,
					ll_fz, lower_fz, middle_fz, left_fz ) ;
	grow_mountain( half_width, ll_x+half_width, ll_y,
					lower_fz, lr_fz, right_fz, middle_fz ) ;
	grow_mountain( half_width, ll_x+half_width, ll_y+half_width,
					middle_fz, right_fz, ur_fz, upper_fz ) ;
	grow_mountain( half_width, ll_x, ll_y+half_width,
					left_fz, middle_fz, upper_fz, ul_fz ) ;
    }
}
@//E*O*F mountain.c//
chmod u=rw,g=r,o= mountain.c
echo x - rings.c
sed 's/^@//' > "rings.c" <<'@//E*O*F rings.c//'
/*
 * rings.c - Create objects with 6 pentagonal rings which connect the midpoints
 *     of the edges of a dodecahedron.  A pyramid of these objects is formed,
 *     which the viewer looks upon from the point.  A plane is placed behind the
 *     pyramid for shadows.  No object is clipped.  Three light sources.
 *
 * Version:  2.0 (7/9/87)
 * Author:  Eric Haines, 3D/Eye, Inc.
 *
 * SIZE_FACTOR determines the number of objects output.
 *     Each object has 30 cylinders and 30 spheres.
 *     Total objects = SF*SF + (SF-1)*(SF-1) + ... + 1 plus 1 backdrop square.
 *     formula for # of spheres or cylinders = 5*SF*(SF+1)*(2*SF+1)
 *
 *     SIZE_FACTOR	# spheres	# cylinders	# squares
 *	    1		    30		      30	     1
 *	    2		   150		     150	     1
 *	    3		   420		     420	     1
 *
 *	    7		  4200		    4200	     1
 */

#include 
#include 
#include 
#include "definitions.h"
#include "lib.h"

#define	OUTPUT_FORMAT		OUTPUT_CURVES
#define	SIZE_FACTOR		7

/* if spread out is > 1, succeeding layers spread out more */
#define	SPREAD_OUT		1

main(argc,argv)
int argc; 
char *argv[];
{
    COORD4  base_pt, apex_pt, offset ;
    COORD4  wall[4], dodec[30] ;
    COORD4  from, at, up ;
    COORD4  wvec, light ;
    COORD4  back_color, ring_color[6] ;
    long    prev_elem ;
    long    num_elem ;
    long    num_depth, num_objx, num_objz ;
    double  radius ;
    double  spread, y_diff, xz_diff ;


    radius = 0.07412 ;	/* cone and sphere radius */

    /* calculate spread of objects */
    spread = 1 / sin( (double)( PI/8.0 ) ) ;
    if ( SPREAD_OUT <= spread ) {
	y_diff = spread / SPREAD_OUT ;
	xz_diff = 1.0 ;
    }
    else {
	y_diff = 1.0 ;
	xz_diff = SPREAD_OUT / spread ;
    }

    /* output viewpoint */
    SET_COORD( from, -1.0, -spread, 0.5 ) ;
    SET_COORD( at, from.x, from.y + 1.0, from.z ) ;
    SET_COORD( up, 0.0, 0.0, 1.0 ) ;
    lib_output_viewpoint( &from, &at, &up, 45.0, 1.0, 512, 512 ) ;

    /* output background color - UNC sky blue */
    /* note that the background color should never be seen */
    SET_COORD( back_color, 0.078, 0.361, 0.753 ) ;
    lib_output_background_color( &back_color ) ;

    /* output light source */
    SET_COORD( light, 3.0, -spread, 3.0 ) ;
    lib_output_light( &light ) ;
    SET_COORD( light, -4.0, -spread, 1.0 ) ;
    lib_output_light( &light ) ;
    SET_COORD( light, 2.0, -spread, -4.0 ) ;
    lib_output_light( &light ) ;

    /* output wall polygon - white */
    SET_COORD( back_color, 1.0, 1.0, 1.0 ) ;
    lib_output_color( &back_color, 1.0, 0.0, 0.0, 0.0, 0.0 ) ;
    /* just spans 45 degree view + 1% */
    wvec.y = y_diff * ( SIZE_FACTOR + 1 ) ;
    wvec.x = wvec.z = 1.01 * ( wvec.y - from.y ) * tan( PI / 8.0 ) ;
    SET_COORD( wall[0],  wvec.x+from.x, wvec.y,  wvec.z+from.z ) ;
    SET_COORD( wall[1], -wvec.x+from.x, wvec.y,  wvec.z+from.z ) ;
    SET_COORD( wall[2], -wvec.x+from.x, wvec.y, -wvec.z+from.z ) ;
    SET_COORD( wall[3],  wvec.x+from.x, wvec.y, -wvec.z+from.z ) ;
    lib_output_polygon( 4, wall ) ;

    /* set up ring colors - RGB and complements */
    SET_COORD( ring_color[0], 1.0, 0.0, 0.0 ) ;
    SET_COORD( ring_color[1], 0.0, 1.0, 0.0 ) ;
    SET_COORD( ring_color[2], 0.0, 0.0, 1.0 ) ;
    SET_COORD( ring_color[3], 0.0, 1.0, 1.0 ) ;
    SET_COORD( ring_color[4], 1.0, 0.0, 1.0 ) ;
    SET_COORD( ring_color[5], 1.0, 1.0, 0.0 ) ;

    create_dodec( radius, dodec ) ;
    /* radius of osculating cylinders and spheres (no derivation given) */
    base_pt.w = apex_pt.w = radius ;

    for ( num_depth = 0 ; num_depth < SIZE_FACTOR ; ++num_depth ) {
	offset.y = y_diff * (double)(num_depth+1) ;
	for ( num_objz = 0 ; num_objz <= num_depth ; ++num_objz ) {
	    offset.z = xz_diff * (double)(2*num_objz - num_depth) ;
	    for ( num_objx = 0 ; num_objx <= num_depth ; ++num_objx ) {
		offset.x = xz_diff * (double)(2*num_objx - num_depth) ;
		for ( num_elem = 0 ; num_elem < 30 ; ++num_elem ) {
		    COPY_COORD( base_pt, dodec[num_elem] ) ;
		    ADD2_COORD( base_pt, offset ) ;
		    if ( num_elem%5 == 0 ) {
			prev_elem = num_elem + 4 ;
			/* new ring beginning - output color */
			lib_output_color( &ring_color[num_elem/5]
					, 0.5
					, 0.5, 3.0
					, 0.0, 0.0
					) ;
		    }
		    else {
			prev_elem = num_elem - 1 ;
		    }
		    COPY_COORD( apex_pt, dodec[prev_elem] ) ;
		    ADD2_COORD( apex_pt, offset ) ;

		    lib_output_cylcone( &base_pt, &apex_pt, OUTPUT_FORMAT ) ;
		    lib_output_sphere( &base_pt, OUTPUT_FORMAT ) ;
		}
	    }
	}
    }
}

/* Create the set of 30 points needed to generate the rings */
create_dodec( minor_radius, vertex )
double	minor_radius ;
COORD4	vertex[30] ;
{
    long    num_vertex, num_pentagon ;
    COORD4  temp_vertex ;
    MATRIX  x_matrix, z_matrix ;
    double  scale, x_rotation, z_rotation ;


    /* scale object to fit in a sphere of radius 1 */

    scale = 1.0 / ( 1.0 + minor_radius ) ;
    /*
     * define one pentagon as on the XY plane, with points starting along +X and
     * N fifths of the way around the Z axis.
     */
    for ( num_vertex = 0 ; num_vertex < 5 ; ++num_vertex ) {
	vertex[num_vertex].x = scale * cos( (double)num_vertex * 2.0*PI/5.0 ) ;
	vertex[num_vertex].y = scale * sin( (double)num_vertex * 2.0*PI/5.0 ) ;
	vertex[num_vertex].z = 0.0 ;
	vertex[num_vertex].w = 1.0 ;
    }

    /*
     * find the rotation angle (in radians) along the X axis:
     * angle between two adjacent dodecahedron faces.
     */
    x_rotation = 2.0 *
		acos( cos( (double)(PI/3.0) ) / sin( (double)(PI/5.0) ) ) ;
    lib_create_rotate_matrix( x_matrix, X_AXIS, x_rotation ) ;

    /*
     * Find each of the other 5 pentagons:  rotate along the X axis,
     * then rotate on the Z axis.
     */
    for ( num_pentagon = 1 ; num_pentagon < 6 ; ++num_pentagon ) {
	/*
	 * find the rotation angle (in radians) along the Z axis:
	 * 1/10th plus N fifths of the way around * 2 * PI.
	 */
	z_rotation = PI*( 2.0*(double)(num_pentagon-1)+1.0 ) / 5.0 ;
	lib_create_rotate_matrix( z_matrix, Z_AXIS, z_rotation ) ;

	for ( num_vertex = 0 ; num_vertex < 5 ; ++num_vertex ) {

	    lib_transform_coord( &temp_vertex
			       , &vertex[num_vertex]
			       , x_matrix
			       ) ;

	    lib_transform_coord( &vertex[5*num_pentagon+num_vertex]
			       , &temp_vertex
			       , z_matrix
			       ) ;
	}
    }
}
@//E*O*F rings.c//
chmod u=rw,g=r,o= rings.c
echo x - tetra.c
sed 's/^@//' > "tetra.c" <<'@//E*O*F tetra.c//'
/*
 * tetra.c - Create a tetrahedral pyramid.  This environment is based on the
 *     scene used by Glassner ("Space Subdivision for Fast Ray Tracing," IEEE
 *     CG&A, October 1984) and Kay & Kajiya ("Ray Tracing Complex Scenes,"
 *     SIGGRAPH '86 Proceedings) for testing their ray tracers.  No polygons
 *     are clipped.  One light source.
 *
 * Version:  2.0 (7/9/87)
 * Author:  Eric Haines, 3D/Eye, Inc.
 *
 * Note:  the view and light positions are the same (after transformation to
 *     a different set of world coordinates) as used by Kay & Kajiya,
 *     courtesy of Tim Kay.  For some reason, the number of shadow rays
 *     generated is different (Kay gets 34K, I get 46K).  One light source.
 *
 * SIZE_FACTOR determines the number of polygons output.
 *     Total triangular polygons = 4**SF
 *
 *     SIZE_FACTOR	# triangles
 *	    1		     4
 *	    2		    16
 *	    3		    64
 *
 *	    6		  4096
 */

#include 
#include 
#include 
#include "definitions.h"
#include "lib.h"

#define	SIZE_FACTOR		6


main(argc,argv)
int argc; 
char *argv[];
{
    COORD4  back_color, tetra_color ;
    COORD4  center_pt, light ;
    COORD4  from, at, up ;


    /* output viewpoint */
    SET_COORD( from, 1.022846, -3.177154, -2.174512 ) ;
    SET_COORD( at, -0.004103, -0.004103, 0.216539 ) ;
    SET_COORD( up, -0.816497, -0.816497, 0.816497 ) ;
    lib_output_viewpoint( &from, &at, &up, 45.0, 1.0, 512, 512 ) ;

    /* output background color - UNC sky blue */
    SET_COORD( back_color, 0.078, 0.361, 0.753 ) ;
    lib_output_background_color( &back_color ) ;

    /* output light source */
    SET_COORD( light, 1.876066, -18.123936, -5.000422 ) ;
    lib_output_light( &light ) ;

    /* output tetrahedron color - red */
    SET_COORD( tetra_color, 1.0, 0.2, 0.2 ) ;
    lib_output_color( &tetra_color, 1.0, 0.0, 0.0, 0.0, 0.0 ) ;

    /* compute and output tetrahedral object */
    SET_COORD4( center_pt, 0.0, 0.0, 0.0, 1.0 ) ;
    create_tetra( SIZE_FACTOR, ¢er_pt ) ;
}


/* Create tetrahedrons recursively */
create_tetra( depth, center )
long	depth ;
COORD4	*center ;
{
    long    num_face, num_vert ;
    COORD4  face_pt[3], obj_pt[4], sub_center ;
    long    swap, vert_ord[3] ;
    long    x_dir, y_dir, z_dir ;


    if ( depth <= 1 ) {
	/* Output tetrahedron */

	/* find opposite corners of a cube which form a tetrahedron */
	for ( num_vert = 0, x_dir = -1 ; x_dir <= 1 ; x_dir += 2 ) {
	    for ( y_dir = -1 ; y_dir <= 1 ; y_dir += 2 ) {
		for ( z_dir = -1 ; z_dir <= 1 ; z_dir += 2 ) {
		    if ( x_dir*y_dir*z_dir == 1 ) {
			obj_pt[num_vert].x =
					center->x + (double)x_dir * center->w ;
			obj_pt[num_vert].y =
					center->y + (double)y_dir * center->w ;
			obj_pt[num_vert].z =
					center->z + (double)z_dir * center->w ;
			++num_vert ;
		    }
		}
	    }
	}

	/* find faces and output */
	for ( num_face = 0 ; num_face < 4 ; ++num_face ) {
	    /* output order:
	     *   face 0:  points 0 1 2
	     *   face 1:  points 3 2 1
	     *   face 2:  points 2 3 0
	     *   face 3:  points 1 0 3
	     */
	    for ( num_vert = 0 ; num_vert < 3 ; ++num_vert ) {
		vert_ord[num_vert] = (num_face + num_vert) % 4 ;
	    }
	    if ( num_face%2 == 1 ) {
		swap = vert_ord[0] ;
		vert_ord[0] = vert_ord[2] ;
		vert_ord[2] = swap ;
	    }

	    for ( num_vert = 0 ; num_vert < 3 ; ++num_vert ) {
		COPY_COORD( face_pt[num_vert], obj_pt[vert_ord[num_vert]] ) ;
	    }
	    lib_output_polygon( 3, face_pt ) ;
	}
    }

    else {
	/* Create sub-tetrahedra */

	/* find opposite corners of a cube to form sub-tetrahedra */
	for ( x_dir = -1 ; x_dir <= 1 ; x_dir += 2 ) {
	    for ( y_dir = -1 ; y_dir <= 1 ; y_dir += 2 ) {
		for ( z_dir = -1 ; z_dir <= 1 ; z_dir += 2 ) {
		    if ( x_dir*y_dir*z_dir == 1 ) {
			sub_center.x =
				center->x + (double)x_dir * center->w / 2.0 ;
			sub_center.y =
				center->y + (double)y_dir * center->w / 2.0 ;
			sub_center.z =
				center->z + (double)z_dir * center->w / 2.0 ;
			sub_center.w = center->w / 2.0 ;

			create_tetra( depth-1, &sub_center ) ;
		    }
		}
	    }
	}
    }
}
@//E*O*F tetra.c//
chmod u=rw,g=r,o= tetra.c
echo x - tree.c
sed 's/^@//' > "tree.c" <<'@//E*O*F tree.c//'
/*
 * tree.c - Creates a tree using Aono & Kunii's generation method.
 *     (See IEEE CG&A May 1984).  A square polygon is placed beneath the
 *     tree to act as a field.  No tree branch is clipped.  Seven light sources.
 *
 * Version:  2.0 (7/9/87)
 * Author:  Eric Haines, 3D/Eye, Inc.
 *
 * SIZE_FACTOR determines the number of objects output.
 *     Total objects = 2**(SF+1)-1 cones and spheres + 1 square polygon.
 *
 *     SIZE_FACTOR	# spheres	   # cones	# squares
 *	    1		     3		       3	     1
 *	    2		     7		       7	     1
 *	    3		    15		      15	     1
 *
 *	   11		  4095		    4095	     1
 */

#include 
#include 
#include 
#include "definitions.h"
#include "lib.h"

#define	OUTPUT_FORMAT		OUTPUT_CURVES
#define	SIZE_FACTOR		11

/* the following affect the shape of the tree */
#define	BR_ANGLE_0		40.0
#define	BR_ANGLE_1		25.0
#define	BR_CONTR_0		0.65
#define	BR_CONTR_1		0.70
#define	BR_DIAMETER		0.67
#define	DIV_ANGLE		140.0
#define	WIDTH_HEIGHTH_RATIO	0.15


static	MATRIX	rst_mx[2] ;


main(argc,argv)
int argc; 
char *argv[];
{
    COORD4  field[4] ;
    COORD4  from, at, up ;
    COORD4  light ;
    COORD4  back_color, tree_color ;


    /* output viewpoint */
    SET_COORD( from, 4.5, 0.4, 2.0 ) ;
    SET_COORD( at, 0.0, 0.0, 1.5 ) ;
    SET_COORD( up, 0.0, 0.0, 1.0 ) ;
    lib_output_viewpoint( &from, &at, &up, 45.0, 1.0, 512, 512 ) ;

    /* output background color - UNC sky blue */
    SET_COORD( back_color, 0.078, 0.361, 0.753 ) ;
    lib_output_background_color( &back_color ) ;

    /* output light source */
    SET_COORD( light, -5.0, 5.0, 50.0 ) ;
    lib_output_light( &light ) ;
    SET_COORD( light, 30.0, -30.0, 30.0 ) ;
    lib_output_light( &light ) ;
    SET_COORD( light, -40.0, -30.0, 20.0 ) ;
    lib_output_light( &light ) ;
    SET_COORD( light, 10.0, 30.0, 40.0 ) ;
    lib_output_light( &light ) ;
    SET_COORD( light, -30.0, 40.0, 10.0 ) ;
    lib_output_light( &light ) ;
    SET_COORD( light, 50.0, 25.0, 20.0 ) ;
    lib_output_light( &light ) ;
    SET_COORD( light, -10.0, -60.0, 30.0 ) ;
    lib_output_light( &light ) ;

    /* output field polygon - green */
    SET_COORD( back_color, 0.2, 0.7, 0.2 ) ;
    lib_output_color( &back_color, 1.0, 0.0, 0.0, 0.0, 0.0 ) ;
    SET_COORD( field[0],  50.0,  50.0, 0.0 ) ;
    SET_COORD( field[1], -50.0,  50.0, 0.0 ) ;
    SET_COORD( field[2], -50.0, -50.0, 0.0 ) ;
    SET_COORD( field[3],  50.0, -50.0, 0.0 ) ;
    lib_output_polygon( 4, field ) ;

    /* set up tree color - brown */
    SET_COORD( tree_color, 0.55, 0.4, 0.2 ) ;
    lib_output_color( &tree_color, 1.0, 0.0, 0.0, 0.0, 0.0 ) ;

    /* create tree */
    create_tree() ;
}


/*
 * Set up matrices for growth of each branch with respect to the
 * parent branch, then grow each branch.
 */
create_tree()
{
    double  branch_angle, branch_contraction, divergence ;
    long    i ;
    MATRIX  ident_mx, temp1_mx, temp2_mx, tempr_mx, tempst_mx ;


    for ( i = 0 ; i < 2 ; ++i ) {
	if ( i == 0 ) {
	    branch_angle = BR_ANGLE_0 ;
	    divergence = 90.0 ;
	    branch_contraction = BR_CONTR_0 ;
	}
	else {
	    branch_angle = BR_ANGLE_1 ;
	    divergence = DIV_ANGLE + 90.0 ;
	    branch_contraction = BR_CONTR_1 ;
	}

	/* rotate along X axis by branching angle */
	lib_create_rotate_matrix( temp1_mx, X_AXIS, branch_angle*PI/180.0 ) ;

	/* rotate along Z axis by divergence angle */
	lib_create_rotate_matrix( temp2_mx, Z_AXIS, divergence*PI/180.0 ) ;

	lib_matrix_multiply( tempr_mx, temp1_mx, temp2_mx ) ;

	/* include translation of branch, scaled */
	lib_create_identity_matrix( tempst_mx ) ;
	tempst_mx[0][0] = tempst_mx[1][1] = tempst_mx[2][2] =
							branch_contraction ;
	tempst_mx[3][2] = 1.0 ;

	/* concatenate */
	lib_matrix_multiply( rst_mx[i], tempr_mx, tempst_mx ) ;
    }

    /* set up initial matrix */
    lib_create_identity_matrix( ident_mx ) ;
    grow_tree( ident_mx, 1.0, SIZE_FACTOR ) ;
}


/* grow tree branches recursively */
grow_tree( cur_mx, scale, depth )
MATRIX	cur_mx ;
double	scale ;
long    depth ;
{
    COORD4  apex, base, vec ;
    long    i ;
    MATRIX  new_mx ;


    /* output branch */
    SET_COORD4( vec, 0.0, 0.0, 0.0, 1.0 ) ;
    lib_transform_coord( &base, &vec, cur_mx ) ;
    base.w = scale * WIDTH_HEIGHTH_RATIO ;

    SET_COORD4( vec, 0.0, 0.0, 1.0, 1.0 ) ;
    lib_transform_coord( &apex, &vec, cur_mx ) ;
    apex.w = base.w * BR_DIAMETER ;

    lib_output_cylcone( &base, &apex, OUTPUT_FORMAT ) ;
    lib_output_sphere( &apex, OUTPUT_FORMAT ) ;

    if ( depth > 0 ) {
	--depth ;

	for ( i = 0; i < 2; ++i ) {
	    lib_matrix_multiply( new_mx, rst_mx[i], cur_mx ) ;
	    grow_tree( new_mx, scale * BR_DIAMETER, depth ) ;
	}
    }
}
@//E*O*F tree.c//
chmod u=rw,g=r,o= tree.c

exit 0