[comp.sys.hp] Starbase fractal viewer

burdick@hpindda.HP.COM (Matt Burdick) (05/16/89)

I was asked to post this shar file to comp.sys.hp for Eric Haines, who was
unable to.  It contains a number of fractal graphics images in Neutral File
Format (NFF), along with a viewer for displaying them under HP's Starbase
product.

	This package by Eric Haines is for displaying scenes in NFF format,
	such as the SPD (Standard Procedural Databases) package.  Mark
	VandeWettering has made the SPD package (and his ray tracer in "C"
	which uses the NFF format) available by anonymous FTP.  Get it from
	drizzle.cs.uoregon.edu .  If you don't have FTP capabilities but have
	UUCP, write to either of:

		alias	arpa_netlib	netlib@anl-mcs.arpa
		alias	research_netlib	...hplabs!research!netlib

	and send the one line message "send Haines from Graphics".  The mailers
	at the other end should automatically send you the SPD package.
	To contact Eric, write him at:

		alias	eric_haines	...hplabs!hpfcla!hpfcrs!eye!erich

	To contact Mark, write him at:

		alias	mark_vandewettering	markv@cs.uoregon.edu

	Two polyhedra databases from Netlib's "polyhedra" section are also
	included in the NFF format in the following, along with a tetrahedron
	test scene.  Enjoy!

---End Of Blurb

Thanks much in advance,

Eric Haines


# 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 Eric Haines <erich@spruce> on Wed May 10 10:01:30 1989
#
# This archive contains:
#	README		def.h		lib.c		lib.h		
#	lookat.c	makefile	pol005.nff	pol006.nff	
#	tetra.nff	
#

LANG=""; export LANG
PATH=/bin:/usr/bin:$PATH; export PATH

echo x - README
cat >README <<'@EOF'
unpack, cd into the new "SPD" directory, "make",
then type "lookat tetra.nff" for a mouse controlled view.
Note that input can be piped to lookat, e.g.
"cat tetra.nff | lookat" is fine.

Define OUTDEV, OUTDRIVER, INDEV and INDRIVER if another
device than the SRX and the mouse is desired.

These are the assumed defaults for each program:

    INDEV=/dev/hil2
    INDRIVER=hp-hil
    OUTDEV=/dev/crt
    OUTDRIVER=hp98721
@EOF

chmod 644 README

echo x - def.h
cat >def.h <<'@EOF'
/*
 * def.h contains some useful definitions for "C" programs.
 *
 * Version:  2.2 (11/17/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; }
@EOF

chmod 640 def.h

echo x - lib.c
cat >lib.c <<'@EOF'
/*
 * lib.c - a library of vector operations, a random number generator, and
 *     object output routines.
 *
 * Version:  2.2 (11/17/87)
 * Author:  Eric Haines, 3D/Eye, Inc.
 */

#include <stdio.h>
#include <math.h>
#include <memory.h>
#include "def.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_axis_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_axis_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
				     ) ;
    }
}
@EOF

chmod 644 lib.c

echo x - lib.h
cat >lib.h <<'@EOF'
/*
 * lib.h - vector library definitions
 *
 * Version:  2.2 (11/17/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() ;
@EOF

chmod 640 lib.h

echo x - lookat.c
cat >lookat.c <<'@EOF'
/************************************************************************/
/*									*/
/*	program:	lookat						*/
/*									*/
/*	usage:		lookat [filename]				*/
/*									*/
/*	function:	examine a file in my neutral file format.	*/
/*			Creates a display list, uses the mouse to move	*/
/*			around (left/right - rotate on screen's y axis,	*/
/*			up/down - rotate on screen's x axis, left	*/
/*			button - move closer, right button - move back.	*/
/*			Buttons also move the screen's axis' z-depth).	*/
/*									*/
/*	input:		filename.nff					*/
/*			Define OUTDEV, OUTDRIVER, INDEV and INDRIVER	*/
/*			(default is SRX and mouse if not defined).	*/
/*			If redefined, make sure the makefile links in	*/
/*			the libraries you need.	 gescape commands may	*/
/*			also have to be modified or deleted.		*/
/*									*/
/*	output:		a display on the screen				*/
/*									*/
/*	comments:	program components are described within	the	*/
/*			corresponding modules				*/
/*									*/
/*	author:		Eric Haines, 12/5/86				*/
/*									*/
/************************************************************************/

#include <stdio.h>
#include <math.h>
#include <fcntl.h>
#include <starbase.c.h>
#include <sbdl.c.h>
#include "def.h"
#include "lib.h"

#define NEG_COORD(r)		(r).x = -(r).x, (r).y = -(r).y, (r).z = -(r).z

extern	char	*getenv() ;
int	devopen() ;

#define BUFSIZE		255
#define OUT_RESOLUTION	4

#define MAX_EDGES	255	/* maximum edges in a polygon */
#define SEG_DATA	1

/* default devices and drivers */
char	default_indev[] = "/dev/hil2" ;
char	default_indriver[] = "hp-hil" ;
char	default_outdev[] = "/dev/crt" ;
char	default_outdriver[] = "hp98721" ;

static	camera_arg	camera ;
COORD4  from ;
COORD4  at ;
COORD4  up ;
static	int		screendes ;
static	int		inputdes ;

main(argc,argv)
int argc;  char *argv[];
{
    char    *device, *driver ;

    int     create_database() ;


    from.x = from.y = from.z = 0.0 ;
    at.x = 0.0 ; at.y = 1.0 ; at.z = 0.0 ;
    up.x = 0.0 ; up.y = 0.0 ; up.z = 1.0 ;

    screendes = devopen(OUTDEV,INIT|THREE_D|MODEL_XFORM) ;
    if ( screendes == -1 ) {
	printf("error opening output device\n" ) ;
	exit(1) ;
    }

    inputdes = devopen(INDEV,INIT) ;
    if ( inputdes == -1 ) {
	printf("error opening input device\n" ) ;
	exit(1) ;
    }

    vdc_extent(inputdes,-1.0,-0.8,0.8, 1.0,0.8,0.8 ) ;

    crt_setup() ;
    camera_setup() ;

    if ( !create_database( argc, argv ) ) {
	exit(1) ;
    }

    showtime() ;

    disable_events( inputdes );
    gclose( inputdes ) ;
    gclose( screendes ) ;
}

crt_setup()
/* this may have to be modified if the various gescapes are
 * not supported on your device */
{
    gescape_arg arg1,arg2 ;


    buffer_mode      ( screendes
		     , TRUE ) ;			/* buffering is on */
    
    gescape( screendes, SWITCH_SEMAPHORE, &arg1, &arg1 ) ;

    arg1.f[0] = 1.0 ;
    gescape( screendes, LS_OVERFLOW_CONTROL, &arg1, &arg2 ) ;

    shade_mode       ( screendes
		     , INIT | CMAP_FULL		/* full color map */
		     , TRUE ) ;			/* use light source
						   equations */

    double_buffer    ( screendes
		     , TRUE | INIT		/* Enable double buffer and
						   initialize color map
						   accordingly. */
		     , 12 ) ;			/* 12 planes for each buffer */

    clear_control(screendes,CLEAR_DISPLAY_SURFACE|CLEAR_ALL_BANKS) ;
    clear_view_surface(screendes) ;
    clear_control(screendes,CLEAR_VIEWPORT) ;
}

camera_setup()
{
    camera.front = 0.0 ;
    camera.back = 100.0 ;
    camera.projection = CAM_PERSPECTIVE ;
}

showtime()
{
    int     curr_buffer ;
    float dummy ;
    int     frame ;
    int     ok ;
    int     mbutton ;
    float x_change, y_change ;


    hidden_surface   ( screendes 
		     , TRUE			/* enable hidden surface
						   removal */
		     , TRUE
		     ) ;			/* cull back-facing
						   polygons */

    set_locator(inputdes,1,0.0, 0.0, 0.0 ) ;
    clear_view_surface(screendes);

    for ( frame = 0, curr_buffer=0; frame < 10000; ++frame ) {
	sample_locator( inputdes,1,&ok,&x_change,&y_change,&dummy);
	sample_choice(inputdes,1,&ok,&mbutton);
	set_locator(inputdes,1,0.0, 0.0, 0.0 ) ;
	move_camera(x_change,y_change,mbutton) ;
	zbuffer_switch(screendes,1) ;	/* clear z-buffer */
	draw_object() ;
	/* Switch buffers */
	curr_buffer = !curr_buffer ;
	dbuffer_switch( screendes, curr_buffer ) ;
	make_picture_current( screendes ) ;
    }
}

move_camera( x_change, y_change, mbutton )
float x_change ;
float y_change ;
int     mbutton ;
{
    COORD4  toat, out, toward, axis, new_toat, new_up, moveat ;
    double   angle, dot_prod, toat_distance ;
    int     i, j ;
    MATRIX  mx ;
    static  double mmove = 0.1 ;
    float   viewmx[4][4] ;


    /* find CROSS product of to-at & up */
    SUB3_COORD( toat, at, from ) ;
    CROSS( out, toat, up ) ;

    /* perpendicularize and normalize up wrto toat */
    toat_distance = lib_normalize_coord3( &toat ) ;
    lib_normalize_coord3( &out ) ;
    lib_normalize_coord3( &toat ) ;
    CROSS( up, out, toat ) ;

    /* find new toat to move to */
    toward.x = x_change * out.x - y_change * up.x + toat.x ;
    toward.y = x_change * out.y - y_change * up.y + toat.y ;
    toward.z = x_change * out.z - y_change * up.z + toat.z ;

    /* find angle between toat and new toat */
    lib_normalize_coord3( &toward ) ;
    dot_prod = DOT_PRODUCT( toat, toward ) ;

    if ( ABSOLUTE(dot_prod) <= 1.000 ) {
	angle = acos( dot_prod ) ;
	CROSS( axis, toat, toward ) ;

	/* angle to rotate */
	lib_normalize_coord3( &axis ) ;

	lib_create_axis_rotate_matrix( mx, &axis, angle ) ;
	toat.w = 0.0 ;
	lib_transform_coord( &new_toat, &toat, mx ) ;
	from.x = at.x - new_toat.x * toat_distance ;
	from.y = at.y - new_toat.y * toat_distance ;
	from.z = at.z - new_toat.z * toat_distance ;
	up.w = 0.0 ;
	lib_transform_coord( &new_up, &up, mx ) ;
	COPY_COORD( up, new_up ) ;
    }

    SUB3_COORD( toat, at, from ) ;
    lib_normalize_coord3( &toat ) ;

    if ( mbutton != 0 ) {
	moveat.x = toat.x * mmove ;
	moveat.y = toat.y * mmove ;
	moveat.z = toat.z * mmove ;
	mmove = mmove * 1.7 ;
	if ( mmove > 1.0 ) {
	    mmove = 1.0 ;
	}

	if ( mbutton == 2 ) {
	    NEG_COORD( moveat ) ;
	}
	ADD2_COORD( from, moveat ) ;
	ADD2_COORD( at, moveat ) ;
    }
    else {
	mmove = 0.1 ;
    }

    camera.camx = from.x ;
    camera.camy = from.y ;
    camera.camz = from.z ;

    camera.upx = up.x ;
    camera.upy = up.y ;
    camera.upz = up.z ;

    camera.refx = camera.camx + toat.x ;
    camera.refy = camera.camy + toat.y ;
    camera.refz = camera.camz + toat.z ;

    /* view right handed coords */
    /*
    for ( i = 0 ; i < 4 ; ++i ) {
	for ( j = 0 ; j < 4 ; ++j ) {
	    if ( i == j ) {
		viewmx[i][j] = 1.0 ;
	    }
	    else {
		viewmx[i][j] = 0.0 ;
	    }
	}
    }
    viewmx[0][0] = -1.0 ;
    view_matrix3d( screendes, viewmx, POST_CONCAT_VW ) ;
    */

    view_camera( screendes, &camera ) ;
}

draw_object()
{
    refresh_segment(screendes,SEG_DATA) ;
}

int	create_database(argc,argv)
int argc;  char *argv[];
{
    gescape_arg arg1, arg2 ;
    COORD4  apex ;
    COORD4  base ;
    char    buffer[BUFSIZE] ;
    COORD4  center ;
    FILE    *fd ;
    double  hither ;
    int     int_shine ;
    double  kd, ks, shine, t, i_of_r ;
    int     light_flag ;
    double  light_int ;
    double  light_color[3] ;
    double  light_pt[24] ;
    int     num_arg ;
    int     num_light ;
    int     num_vert ;
    double  poly_color[3] ;
    int	    polygon_format ; /* 0 - 3pt, 1 - 6pt w/normal */
    int     spec_flag ;
    int     tot_light ;
    int     tot_vert ;
    float   vert[MAX_EDGES*6] ;
    int     xresolution ;
    int     yresolution ;


    polygon_format = -1 ;
    tot_light = 0 ;

    open_segment(screendes,SEG_DATA,TRUE,TRUE);

    num_arg = 1 ;

  do {
    if ( num_arg >= argc ) {
	fd = stdin ;
    }
    else {
	if ( ( fd = fopen( argv[num_arg], "r" ) ) == NULL ) {
	    fprintf( stderr, "cannot open file %s. Bye!\n", argv[num_arg] ) ;
	    return( FALSE ) ;
	}
    }


    while ( fgets( buffer, BUFSIZE, fd ) ) {

	/* polygonal patch */
	if ( sscanf( buffer, "pp %d", &tot_vert ) == 1 ) {
	    for ( num_vert = 0 ; num_vert < tot_vert ; ++num_vert ) {
		fgets( buffer, BUFSIZE, fd ) ;
		sscanf( buffer, "%f %f %f %f %f %f",
				       &vert[num_vert*6],
				       &vert[num_vert*6+1],
				       &vert[num_vert*6+2],
				       &vert[num_vert*6+3],
				       &vert[num_vert*6+4],
				       &vert[num_vert*6+5] ) ;
	    }
	    if ( polygon_format != 1 ) {
		polygon_format = 1 ;
		vertex_format    ( screendes
		     , 3			/* 3 extra coordinates */
		     , 3			/* coordinates used for 
						   normal determination */
		     , FALSE			/* no color coordinates */
		     , FALSE			/* 1st vertex is not polygon 
						   normal */
		     , CLOCKWISE ) ;		/* polygon vertices are in
						   clockwise order */
	    }
	    polygon3d(screendes, vert, tot_vert, FALSE ) ;
	}


	/* polygon */
	else if ( sscanf( buffer, "p %d", &tot_vert ) == 1 ) {
	    for ( num_vert = 0 ; num_vert < tot_vert ; ++num_vert ) {
		fgets( buffer, BUFSIZE, fd ) ;
		sscanf( buffer, "%f %f %f", &vert[num_vert*3],
					      &vert[num_vert*3+1],
					      &vert[num_vert*3+2] ) ;
	    }
	    if ( polygon_format != 0 ) {
		polygon_format = 0 ;
		vertex_format    ( screendes
		     , 0			/* # no extra coordinates */
		     , 0			/* # coordinates used for 
						   normal determination */
		     , FALSE			/* no extra coordinates */
		     , FALSE			/* 1st vertex is not polygon 
						   normal */
		     , CLOCKWISE ) ;		/* polygon vertices are in
						   clockwise order */
	    }
	    polygon3d(screendes, vert, tot_vert, FALSE ) ;
		
	}


	/* sphere */
	else if ( sscanf( buffer, "s %lf %lf %lf %lf",
			&center.x, &center.y, &center.z, &center.w ) == 4 ) {
	    if ( polygon_format != 1 ) {
		polygon_format = 1 ;
		vertex_format    ( screendes
		     , 3			/* 3 extra coordinates */
		     , 3			/* coordinates used for 
						   normal determination */
		     , FALSE			/* no color coordinates */
		     , FALSE			/* 1st vertex is not polygon 
						   normal */
		     , CLOCKWISE ) ;		/* polygon vertices are in
						   clockwise order */
	    }
	    sphere( screendes, &center ) ;
	}


	/* cylinder / cone */
	else if ( buffer[0] == 'c' ) {
	    fgets( buffer, BUFSIZE, fd ) ;
	    sscanf( buffer, "%lf %lf %lf %lf", &base.x, &base.y, &base.z, &base.w ) ;
	    fgets( buffer, BUFSIZE, fd ) ;
	    sscanf( buffer, "%lf %lf %lf %lf", &apex.x, &apex.y, &apex.z, &apex.w ) ;
	    if ( polygon_format != 1 ) {
		polygon_format = 1 ;
		vertex_format    ( screendes
		     , 3			/* 3 extra coordinates */
		     , 3			/* coordinates used for 
						   normal determination */
		     , FALSE			/* no color coordinates */
		     , FALSE			/* 1st vertex is not polygon 
						   normal */
		     , CLOCKWISE ) ;		/* polygon vertices are in
						   clockwise order */
	    }
	    cylcone( screendes, &base, &apex ) ;
	}


	/* fill color */
	else if ( sscanf( buffer, "f %lf %lf %lf %lf %lf %lf %lf"
						  , &poly_color[0]
						  , &poly_color[1]
						  , &poly_color[2]
						  , &kd
						  , &ks
						  , &shine
						  , &t
						  , &i_of_r
						  ) == 7 ) {
	    if ( t > 0.0 ) {
		arg1.i[0] = 0x5A5A ;
		kd = 1.0 ;
	    }
	    else {
		arg1.i[0] = 0xFFFF ;
	    }
	    gescape(screendes, TRANSPARENCY, &arg1, &arg2 ) ;
	    fill_color(screendes, (float)(poly_color[0]*kd)
				, (float)(poly_color[1]*kd)
				, (float)(poly_color[2]*kd)
				) ;
	    spec_flag = ( ks > 0.0 ) ;
	    int_shine = (int)(shine+0.5) ;
	    surface_model(screendes, spec_flag
				, int_shine >= 1 ? int_shine : 1
				, (float)(poly_color[0]*ks)
				, (float)(poly_color[1]*ks)
				, (float)(poly_color[2]*ks)
				) ;
	}


	/* light */
	else if ( sscanf( buffer, "l %lf %lf %lf", &light_pt[tot_light*3+0],
						  &light_pt[tot_light*3+1],
						  &light_pt[tot_light*3+2]
						  ) == 3 ) {
	    if ( polygon_format != 1 ) {
		polygon_format = 1 ;
		vertex_format    ( screendes
		     , 3			/* 3 extra coordinates */
		     , 3			/* coordinates used for 
						   normal determination */
		     , FALSE			/* no color coordinates */
		     , FALSE			/* 1st vertex is not polygon 
						   normal */
		     , CLOCKWISE ) ;		/* polygon vertices are in
						   clockwise order */
	    }
	    arg1.i[0] = 0x5A5A ;
	    gescape(screendes, TRANSPARENCY, &arg1, &arg2 ) ;
	    light_color[0] = 1.0 ;
	    light_color[1] = 1.0 ;
	    light_color[2] = 1.0 ;
	    fill_color(screendes, (float)light_color[0]
				, (float)light_color[1]
				, (float)light_color[2]
				) ;
	    surface_model(screendes, 1, 1, 0.0,0.0,0.0 ) ;
	    center.x = light_pt[tot_light*3+0] ;
	    center.y = light_pt[tot_light*3+1] ;
	    center.z = light_pt[tot_light*3+2] ;
	    center.w = 0.1 ;
	    sphere( screendes, &center ) ;

	    ++tot_light ;
	}


	/* view */
	else if ( buffer[0] == 'v' ) {
	    fgets( buffer, BUFSIZE, fd ) ;
	    sscanf( buffer, "from %lf %lf %lf", &from.x, &from.y, &from.z ) ;
	    fgets( buffer, BUFSIZE, fd ) ;
	    sscanf( buffer, "at %lf %lf %lf", &at.x, &at.y, &at.z ) ;
	    fgets( buffer, BUFSIZE, fd ) ;
	    sscanf( buffer, "up %lf %lf %lf", &up.x, &up.y, &up.z ) ;
	    fgets( buffer, BUFSIZE, fd ) ;
	    sscanf( buffer, "angle %f", &camera.field_of_view ) ;
	    fgets( buffer, BUFSIZE, fd ) ;
	    sscanf( buffer, "hither %lf", &hither ) ;
	    camera.front = (float)(hither - 1.0) ;
	    fgets( buffer, BUFSIZE, fd ) ;
	    /* ???? unused for now */
	    sscanf( buffer, "resolution %d %d\n", &xresolution, &yresolution ) ;
	}


	/* background color */
	else if ( sscanf( buffer, "b %lf %lf %lf", &poly_color[0],
						  &poly_color[1],
						  &poly_color[2]
						  ) == 3 ) {
	    background_color(screendes,
				(float)poly_color[0],(float)poly_color[1],(float)poly_color[2]) ;
	    clear_view_surface(screendes) ;
	}


	/* error */
	else {
	    fprintf( stderr, "Error analyzing %s\n", buffer ) ;
	}
    }

    if ( num_arg < argc ) {
	fclose( fd ) ;
    }

  } while ( ++num_arg < argc ) ;

    close_segment(screendes) ;


    /* set up lights */

    light_flag = 2 ;
    if ( tot_light == 0 ) {
	light_ambient(screendes,1.0,1.0,1.0);
    }
    else {
	light_int = sqrt((double)tot_light) / (double)(tot_light*2) ;
	light_ambient(screendes,(float)light_int,(float)light_int,(float)light_int ) ;

	for ( num_light = 0 ; num_light < tot_light ; ++num_light ) {
	    light_flag *= 2 ;
	    light_source(screendes,num_light+1,POSITIONAL,
		(float)light_int,(float)light_int,(float)light_int,
		(float)light_pt[num_light*3],
		(float)light_pt[num_light*3+1],
		(float)light_pt[num_light*3+2] ) ;
	    light_model(screendes,num_light+1,
					0,1,10.0,30.0,1.0,1.0,1.0) ;
	}
    }
    light_switch(screendes,light_flag-1);

    return( TRUE ) ;
}

/*
 * 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.
 */
cylcone( fildes, base_pt, apex_pt )
int     fildes ;
COORD4	*base_pt ;
COORD4	*apex_pt ;
{
    double   angle ;
    COORD4  axis ;
    COORD4  dir ;
    double   divisor ;
    int     i ;
    COORD4  lip_coord, lip_cnorm ;
    float lip_pt[24] ;
    MATRIX  mx ;
    COORD4  norm_axis ;
    int     num_pol ;
    COORD4  start_norm, start_radius[4] ;


    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 ;

    lip_pt[0] = base_pt->x + start_radius[0].x ;
    lip_pt[1] = base_pt->y + start_radius[0].y ;
    lip_pt[2] = base_pt->z + start_radius[0].z ;

    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 ;

    lip_pt[6] = apex_pt->x + start_radius[1].x ;
    lip_pt[7] = apex_pt->y + start_radius[1].y ;
    lip_pt[8] = apex_pt->z + start_radius[1].z ;

    lip_pt[3] = lip_pt[9] = start_norm.x ;
    lip_pt[4] = lip_pt[10] = start_norm.y ;
    lip_pt[5] = lip_pt[11] = start_norm.z ;

    for ( num_pol = 0 ; num_pol < 4*OUT_RESOLUTION ; ++num_pol ) {
	for ( i = 0 ; i < 6 ; ++i ) {
	    lip_pt[i+18] = lip_pt[i] ;
	    lip_pt[i+12] = lip_pt[i+6] ;
	}

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

	lib_transform_coord( &lip_coord, &start_radius[0], mx ) ;
	ADD2_COORD( lip_coord, (*base_pt) ) ;
	lip_pt[0] = lip_coord.x ;
	lip_pt[1] = lip_coord.y ;
	lip_pt[2] = lip_coord.z ;
	lib_transform_coord( &lip_coord, &start_radius[1], mx ) ;
	ADD2_COORD( lip_coord, (*apex_pt) ) ;
	lip_pt[6] = lip_coord.x ;
	lip_pt[7] = lip_coord.y ;
	lip_pt[8] = lip_coord.z ;

	lib_transform_coord( &lip_cnorm, &start_norm, mx ) ;
	lip_pt[3] = lip_pt[9]  = lip_cnorm.x ;
	lip_pt[4] = lip_pt[10] = lip_cnorm.y ;
	lip_pt[5] = lip_pt[11] = lip_cnorm.z ;

	polygon3d( fildes, lip_pt, 4, FALSE ) ;
    }
}

/*
 * Output sphere. A sphere is defined by a radius and center position.
 */
sphere( fildes, center_pt )
int     fildes ;
COORD4	*center_pt ;
{
    double   angle ;
    float edge_pt[18] ;
    int     num_face, num_edge, num_tri, num_vert ;
    COORD4  x_axis[OUT_RESOLUTION+1], y_axis[OUT_RESOLUTION+1] ;
    COORD4  pt[OUT_RESOLUTION+1][OUT_RESOLUTION+1] ;
    COORD4  mid_axis ;
    MATRIX  rot_mx ;
    int     u_pol, v_pol ;
    int     u_vert, v_vert ;


    /* calculate axes used to find grid points */
    for ( num_edge = 0 ; num_edge <= OUT_RESOLUTION ; ++num_edge ) {
	angle = (PI/4.0) * ( 2.0*(double)num_edge/OUT_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 <= OUT_RESOLUTION ; ++u_pol ) {
	for ( v_pol = 0 ; v_pol <= OUT_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 <= OUT_RESOLUTION ; ++u_pol ) {
	    for ( v_pol = 0 ; v_pol <= OUT_RESOLUTION ; ++v_pol ) {
		lib_rotate_cube_face( &pt[u_pol][v_pol]
				    , Z_AXIS
				    , num_face
				    ) ;
	    }
	}
	/* output grid */
	for ( u_pol = 0 ; u_pol < OUT_RESOLUTION ; ++u_pol ) {
	    for ( v_pol = 0 ; v_pol < OUT_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 ) {
			    u_vert = u_pol ;
			    v_vert = v_pol ;
			}
			else if ( num_vert == 1 ) {
			    u_vert = u_pol ;
			    v_vert = v_pol + 1 ;
			}
			else if ( num_vert == 2 ) {
			    u_vert = u_pol + 1 ;
			    v_vert = v_pol + 1 ;
			}
			else {
			    u_vert = u_pol + 1 ;
			    v_vert = v_pol ;
			}

			edge_pt[num_edge*6+3] = pt[u_vert][v_vert].x ;
			edge_pt[num_edge*6+4] = pt[u_vert][v_vert].y ;
			edge_pt[num_edge*6+5] = pt[u_vert][v_vert].z ;

			edge_pt[num_edge*6+0] =
					edge_pt[num_edge*6+3] * center_pt->w +
							      center_pt->x ;
			edge_pt[num_edge*6+1] =
					edge_pt[num_edge*6+4] * center_pt->w +
							      center_pt->y ;
			edge_pt[num_edge*6+2] =
					edge_pt[num_edge*6+5] * center_pt->w +
							      center_pt->z ;

		    }
		    polygon3d( fildes, edge_pt, 3, FALSE ) ;
		}
	    }
	}
    }
}

int devopen(dev_kind,init_mode)
int dev_kind,init_mode;
{
char	*dev, *driver;
int	fildes ;

    if ( dev_kind == OUTDEV ) {
	dev = getenv("OUTDEV");
	if (!dev) dev = default_outdev ;
	driver = getenv("OUTDRIVER");
	if (!driver) driver = default_outdriver ;
    } else {
	dev = getenv("INDEV");
	if (!dev) dev = default_indev ;
	driver = getenv("INDRIVER");
	if (!driver) driver = default_indriver ;
    }

    fildes = gopen(dev,dev_kind,driver,init_mode);

    return(fildes) ;
}
@EOF

chmod 644 lookat.c

echo x - makefile
cat >makefile <<'@EOF'
# cc	= C compiler
#	-c	compile only, do not try to link
#	-g	debug mode
#	-O	optimize
#	-p	monitor timing
#	-Wc,-Yf	use floating point numbers as themselves - don't convert
#		this option has problems - not sure what to do

CC=cc -c -O
INC=def.h lib.h
BASELIB=-lm -lmalloc

all:		lookat

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

lookat:		lib.o lookat.o
		cc -o lookat lookat.o lib.o -lsbdl \
		-ldd98721 -ldd98731 -lddhil -lsb1 -lsb2 $(BASELIB)

lookat.o:	$(INC) lookat.c
		$(CC) lookat.c
@EOF

chmod 644 makefile

echo x - pol005.nff
cat >pol005.nff <<'@EOF'
v
from 1.77033 1.52969 0.164572
at -0.258552 -0.643251 -2.17386
up 0 1 0
angle 45
hither 1
resolution 512 512
b 0.078 0.361 0.753
l 10 10 10
l 10 -8 -12
l -8 12 -10
l -12 -10 8
f 1 1 1 1 0 0 0 0
p 5
-0.636237 -0.993877 -2.45654
-0.193076 -0.100015 -2.38857
-0.0252202 -1.08063 -2.4897
-0.739978 -0.387826 -2.39404
0.248668 -0.528189 -2.44769
f 1 0.1 0.1 1 0 0 0 0
p 5
-0.193076 -0.100015 -2.38857
-0.636237 -0.993877 -2.45654
-0.491883 -0.205877 -1.85803
-0.282292 -0.587026 -2.75847
-0.765771 -0.758314 -1.90004
f 0 1 0.1 1 0 0 0 0
p 5
-0.0252202 -1.08063 -2.4897
-0.193076 -0.100015 -2.38857
0.222874 -0.898677 -1.95368
-0.282292 -0.587026 -2.75847
0.119134 -0.292626 -1.89118
f 0 0.1 1 1 0 0 0 0
p 5
-0.739978 -0.387826 -2.39404
-0.0252202 -1.08063 -2.4897
-0.765771 -0.758314 -1.90004
-0.282292 -0.587026 -2.75847
-0.324027 -1.18649 -1.95915
f 0 1 1 1 0 0 0 0
p 5
0.248668 -0.528189 -2.44769
-0.739978 -0.387826 -2.39404
0.119134 -0.292626 -1.89118
-0.282292 -0.587026 -2.75847
-0.491883 -0.205877 -1.85803
f 1 0.1 1 1 0 0 0 0
p 5
-0.636237 -0.993877 -2.45654
0.248668 -0.528189 -2.44769
-0.324027 -1.18649 -1.95915
-0.282292 -0.587026 -2.75847
0.222874 -0.898677 -1.95368
f 1 1 0.1 1 0 0 0 0
p 5
0.119134 -0.292626 -1.89118
-0.765771 -0.758314 -1.90004
0.222874 -0.898677 -1.95368
-0.491883 -0.205877 -1.85803
-0.324027 -1.18649 -1.95915
f 1 1 1 1 0 0 0 0
p 5
-0.765771 -0.758314 -1.90004
0.119134 -0.292626 -1.89118
-0.739978 -0.387826 -2.39404
-0.234811 -0.699477 -1.58925
-0.193076 -0.100015 -2.38857
f 1 0.1 0.1 1 0 0 0 0
p 5
0.222874 -0.898677 -1.95368
-0.765771 -0.758314 -1.90004
-0.0252202 -1.08063 -2.4897
-0.234811 -0.699477 -1.58925
-0.636237 -0.993877 -2.45654
f 0 1 0.1 1 0 0 0 0
p 5
-0.491883 -0.205877 -1.85803
0.222874 -0.898677 -1.95368
-0.193076 -0.100015 -2.38857
-0.234811 -0.699477 -1.58925
0.248668 -0.528189 -2.44769
f 0 0.1 1 1 0 0 0 0
p 5
-0.324027 -1.18649 -1.95915
-0.491883 -0.205877 -1.85803
-0.636237 -0.993877 -2.45654
-0.234811 -0.699477 -1.58925
-0.739978 -0.387826 -2.39404
f 0 1 1 1 0 0 0 0
p 5
0.119134 -0.292626 -1.89118
-0.324027 -1.18649 -1.95915
0.248668 -0.528189 -2.44769
-0.234811 -0.699477 -1.58925
-0.0252202 -1.08063 -2.4897
@EOF

chmod 644 pol005.nff

echo x - pol006.nff
cat >pol006.nff <<'@EOF'
v
from 5.65079 1.45769 2.28561
at 2.04071 -2.10085 -1.14963
up 0 1 0
angle 45
hither 1
resolution 512 512
b 0.078 0.361 0.753
l 10 10 10
l 10 -8 -12
l -8 12 -10
l -12 -10 8
f 1 1 1 1 0 0 0 0
p 5
1.4296 -1.42108 -1.41224
2.37642 -1.21122 -1.16835
2.94323 -1.99263 -1.42936
2.34671 -2.68543 -1.83457
1.41123 -2.33219 -1.82399
f 1 0.1 0.1 1 0 0 0 0
p 5
2.37642 -1.21122 -1.16835
1.4296 -1.42108 -1.41224
1.13819 -2.20908 -0.869896
1.90491 -2.48623 -0.290821
2.67018 -1.86951 -0.475274
f 0 1 0.1 1 0 0 0 0
p 5
2.94323 -1.99263 -1.42936
2.37642 -1.21122 -1.16835
1.7347 -1.51628 -0.464689
1.90491 -2.48623 -0.290821
2.65181 -2.78063 -0.887023
f 0 0.1 1 1 0 0 0 0
p 5
2.34671 -2.68543 -1.83457
2.94323 -1.99263 -1.42936
2.67018 -1.86951 -0.475274
1.90491 -2.48623 -0.290821
1.70499 -2.99049 -1.13091
f 0 1 1 1 0 0 0 0
p 5
1.41123 -2.33219 -1.82399
2.34671 -2.68543 -1.83457
2.65181 -2.78063 -0.887023
1.90491 -2.48623 -0.290821
1.13819 -2.20908 -0.869896
f 1 0.1 1 1 0 0 0 0
p 5
1.4296 -1.42108 -1.41224
1.41123 -2.33219 -1.82399
1.70499 -2.99049 -1.13091
1.90491 -2.48623 -0.290821
1.7347 -1.51628 -0.464689
f 1 1 0.1 1 0 0 0 0
p 5
2.65181 -2.78063 -0.887023
2.67018 -1.86951 -0.475274
1.7347 -1.51628 -0.464689
1.13819 -2.20908 -0.869896
1.70499 -2.99049 -1.13091
f 1 1 1 1 0 0 0 0
p 5
2.67018 -1.86951 -0.475274
2.65181 -2.78063 -0.887023
2.34671 -2.68543 -1.83457
2.17651 -1.71548 -2.00844
2.37642 -1.21122 -1.16835
f 1 0.1 0.1 1 0 0 0 0
p 5
1.7347 -1.51628 -0.464689
2.67018 -1.86951 -0.475274
2.94323 -1.99263 -1.42936
2.17651 -1.71548 -2.00844
1.4296 -1.42108 -1.41224
f 0 1 0.1 1 0 0 0 0
p 5
1.13819 -2.20908 -0.869896
1.7347 -1.51628 -0.464689
2.37642 -1.21122 -1.16835
2.17651 -1.71548 -2.00844
1.41123 -2.33219 -1.82399
f 0 0.1 1 1 0 0 0 0
p 5
1.70499 -2.99049 -1.13091
1.13819 -2.20908 -0.869896
1.4296 -1.42108 -1.41224
2.17651 -1.71548 -2.00844
2.34671 -2.68543 -1.83457
f 0 1 1 1 0 0 0 0
p 5
2.65181 -2.78063 -0.887023
1.70499 -2.99049 -1.13091
1.41123 -2.33219 -1.82399
2.17651 -1.71548 -2.00844
2.94323 -1.99263 -1.42936
@EOF

chmod 644 pol006.nff

echo x - tetra.nff
cat >tetra.nff <<'@EOF'
v
from 1.02285 -3.17715 -2.17451
at -0.004103 -0.004103 0.216539
up -0.816497 -0.816497 0.816497
angle 45
hither 1
resolution 512 512
b 0.078 0.361 0.753
l 1.87607 -18.1239 -5.00042
f 1 0.2 0.2 1 0 0 0 0
p 3
-1 -1 1
-1 -0.5 0.5
-0.5 -1 0.5
p 3
-0.5 -0.5 1
-0.5 -1 0.5
-1 -0.5 0.5
p 3
-0.5 -1 0.5
-0.5 -0.5 1
-1 -1 1
p 3
-1 -0.5 0.5
-1 -1 1
-0.5 -0.5 1
p 3
-1 -0.5 0.5
-1 0 0
-0.5 -0.5 0
p 3
-0.5 0 0.5
-0.5 -0.5 0
-1 0 0
p 3
-0.5 -0.5 0
-0.5 0 0.5
-1 -0.5 0.5
p 3
-1 0 0
-1 -0.5 0.5
-0.5 0 0.5
p 3
-0.5 -1 0.5
-0.5 -0.5 0
0 -1 0
p 3
0 -0.5 0.5
0 -1 0
-0.5 -0.5 0
p 3
0 -1 0
0 -0.5 0.5
-0.5 -1 0.5
p 3
-0.5 -0.5 0
-0.5 -1 0.5
0 -0.5 0.5
p 3
-0.5 -0.5 1
-0.5 0 0.5
0 -0.5 0.5
p 3
0 0 1
0 -0.5 0.5
-0.5 0 0.5
p 3
0 -0.5 0.5
0 0 1
-0.5 -0.5 1
p 3
-0.5 0 0.5
-0.5 -0.5 1
0 0 1
p 3
-1 0 0
-1 0.5 -0.5
-0.5 0 -0.5
p 3
-0.5 0.5 0
-0.5 0 -0.5
-1 0.5 -0.5
p 3
-0.5 0 -0.5
-0.5 0.5 0
-1 0 0
p 3
-1 0.5 -0.5
-1 0 0
-0.5 0.5 0
p 3
-1 0.5 -0.5
-1 1 -1
-0.5 0.5 -1
p 3
-0.5 1 -0.5
-0.5 0.5 -1
-1 1 -1
p 3
-0.5 0.5 -1
-0.5 1 -0.5
-1 0.5 -0.5
p 3
-1 1 -1
-1 0.5 -0.5
-0.5 1 -0.5
p 3
-0.5 0 -0.5
-0.5 0.5 -1
0 0 -1
p 3
0 0.5 -0.5
0 0 -1
-0.5 0.5 -1
p 3
0 0 -1
0 0.5 -0.5
-0.5 0 -0.5
p 3
-0.5 0.5 -1
-0.5 0 -0.5
0 0.5 -0.5
p 3
-0.5 0.5 0
-0.5 1 -0.5
0 0.5 -0.5
p 3
0 1 0
0 0.5 -0.5
-0.5 1 -0.5
p 3
0 0.5 -0.5
0 1 0
-0.5 0.5 0
p 3
-0.5 1 -0.5
-0.5 0.5 0
0 1 0
p 3
0 -1 0
0 -0.5 -0.5
0.5 -1 -0.5
p 3
0.5 -0.5 0
0.5 -1 -0.5
0 -0.5 -0.5
p 3
0.5 -1 -0.5
0.5 -0.5 0
0 -1 0
p 3
0 -0.5 -0.5
0 -1 0
0.5 -0.5 0
p 3
0 -0.5 -0.5
0 0 -1
0.5 -0.5 -1
p 3
0.5 0 -0.5
0.5 -0.5 -1
0 0 -1
p 3
0.5 -0.5 -1
0.5 0 -0.5
0 -0.5 -0.5
p 3
0 0 -1
0 -0.5 -0.5
0.5 0 -0.5
p 3
0.5 -1 -0.5
0.5 -0.5 -1
1 -1 -1
p 3
1 -0.5 -0.5
1 -1 -1
0.5 -0.5 -1
p 3
1 -1 -1
1 -0.5 -0.5
0.5 -1 -0.5
p 3
0.5 -0.5 -1
0.5 -1 -0.5
1 -0.5 -0.5
p 3
0.5 -0.5 0
0.5 0 -0.5
1 -0.5 -0.5
p 3
1 0 0
1 -0.5 -0.5
0.5 0 -0.5
p 3
1 -0.5 -0.5
1 0 0
0.5 -0.5 0
p 3
0.5 0 -0.5
0.5 -0.5 0
1 0 0
p 3
0 0 1
0 0.5 0.5
0.5 0 0.5
p 3
0.5 0.5 1
0.5 0 0.5
0 0.5 0.5
p 3
0.5 0 0.5
0.5 0.5 1
0 0 1
p 3
0 0.5 0.5
0 0 1
0.5 0.5 1
p 3
0 0.5 0.5
0 1 0
0.5 0.5 0
p 3
0.5 1 0.5
0.5 0.5 0
0 1 0
p 3
0.5 0.5 0
0.5 1 0.5
0 0.5 0.5
p 3
0 1 0
0 0.5 0.5
0.5 1 0.5
p 3
0.5 0 0.5
0.5 0.5 0
1 0 0
p 3
1 0.5 0.5
1 0 0
0.5 0.5 0
p 3
1 0 0
1 0.5 0.5
0.5 0 0.5
p 3
0.5 0.5 0
0.5 0 0.5
1 0.5 0.5
p 3
0.5 0.5 1
0.5 1 0.5
1 0.5 0.5
p 3
1 1 1
1 0.5 0.5
0.5 1 0.5
p 3
1 0.5 0.5
1 1 1
0.5 0.5 1
p 3
0.5 1 0.5
0.5 0.5 1
1 1 1
@EOF

chmod 644 tetra.nff

exit 0

spirit@uxe.cso.uiuc.edu (05/19/89)

Hello,

Just a note about the above viewer...
We tried to compile it on our HP9000/835 and
SRX.  We have Starbase and GKS.

Unfortunately and include file "sbdl.c.h" 
and a library "-lsbdl" are required.  Does
anybody have these things?

The specific calls used therein are all related
to creating, drawing, and closing segments.

Good luck,
spirit@uxe.cso.uiuc.edu

stroyan@hpfcdc.HP.COM (Mike Stroyan) (05/22/89)

> We tried to compile it on our HP9000/835 and
> SRX.  We have Starbase and GKS.
> 
> Unfortunately and include file "sbdl.c.h" 
> and a library "-lsbdl" are required.  Does
> anybody have these things?

The header and library are from the "Starbase Display List".  It is a
separate product that you would have to purchase in addition to Starbase.

Mike Stroyan, stroyan@hpfcla.hp.com

lavar@grlab.UUCP (LaVar Edwards) (05/24/89)

/ grlab:comp.sys.hp / stroyan@hpfcdc.HP.COM (Mike Stroyan) /  1:33 pm  May 21, 1989 /
>> We tried to compile it on our HP9000/835 and
>> SRX.  We have Starbase and GKS.
>> 
>> Unfortunately and include file "sbdl.c.h" 
>> and a library "-lsbdl" are required.  Does
>> anybody have these things?
>
>The header and library are from the "Starbase Display List".  It is a
>separate product that you would have to purchase in addition to Starbase.
>
>Mike Stroyan, stroyan@hpfcla.hp.com


We own the product HP-UX Display List (98674L Rev. 6.5) on our 370.
This was purchased with our 370 less than 2 months ago.

Unfortunately and include file "sbdl.c.h" and a library "-lsbdl"
are NOT on our system, any suggestions?

We also own Display List on our 320 and 825 and we do not have these
libraries on any of our systems.


LaVar Edwards
USPS:  Graphicus                UUCP:    ...!hpubvwa!grlab!lavar
       150 Lake St., Suite 206  VoicePh: USA (206) 828-4691
       Kirkland, WA 98033       FAX:     USA (206) 828-4236

cnsy@vax5.CIT.CORNELL.EDU (05/24/89)

In article <47500010@uxe.cso.uiuc.edu> spirit@uxe.cso.uiuc.edu writes:
>
>
>Hello,
>
>Just a note about the above viewer...
>We tried to compile it on our HP9000/835 and
>SRX.  We have Starbase and GKS.
>
>Unfortunately and include file "sbdl.c.h" 
>and a library "-lsbdl" are required.  Does
>anybody have these things?

Sorry about that--I forgot that the Starbase Display List software is not
a standard product on every HP machine.  One solution would be to rip out
all the interactive code in lookat.c (mouse calls, and associated loops to
refresh the segment), all SBDL *_segment() code, and to perform the 
lights setup in draw_object() immediately upon encountering a "f" command
in the *.nff files (not fool-proof, but all my NFF generators from the SPD
package do not output any "f" (fill color) commands until all lights are
output).

Alternately, wait a few days and I'll post my lookonce.c code, which does
not have any sbdl calls (it's not interactive, though).

BTW, if you're doing anything 3D & interactive, you should seriously consider
getting the SBDL package, as it'll save you a lot of reinventing the wheel.

Eric Haines, 3D/Eye Inc

rodean@hpfcdc.HP.COM (Bruce Rodean) (05/27/89)

In article <240036@grlab.UUCP> lavar@grlab.UUCP (LaVar Edwards) writes:
>>> We tried to compile it on our HP9000/835 and
>>> SRX.  We have Starbase and GKS.
>>> 
>>> Unfortunately and include file "sbdl.c.h" 
>>> and a library "-lsbdl" are required.  Does
>>> anybody have these things?
>>
>>The header and library are from the "Starbase Display List".  It is a
>>separate product that you would have to purchase in addition to Starbase.
>>
>>Mike Stroyan, stroyan@hpfcla.hp.com

>We own the product HP-UX Display List (98674L Rev. 6.5) on our 370.
>This was purchased with our 370 less than 2 months ago.

>Unfortunately and include file "sbdl.c.h" and a library "-lsbdl"
>are NOT on our system, any suggestions?

If the Starbase Display List product has been installed on your system,
you should find a file called /etc/filesets/SBDL.  That file is a list
of the files that the update program placed on your system.  You should
find both /usr/include/sbdl.c.h and /usr/lib/libsbdl.a listed in that
file.  If that file is missing, then SBDL is not on your system.

The appropriate revision information for Release 6.5 is:

# what libsbdl.a
libsbdl.a:
	 250.1.2.1   01/27/89 libsbdl.a
# what sbdl.c.h
sbdl.c.h:
	sbdl.c.h  01/27/89   07:56:17 */

Bruce Rodean
rodean%hpfcrn@hplabs.HP.COM

stroyan@hpfcdc.HP.COM (Mike Stroyan) (05/27/89)

> We own the product HP-UX Display List (98674L Rev. 6.5) on our 370.
> This was purchased with our 370 less than 2 months ago.
> 
> Unfortunately and include file "sbdl.c.h" and a library "-lsbdl"
> are NOT on our system, any suggestions?

Read the files off the "Starbase Display List" install tape again.
Those files are on the tape.

Mike Stroyan, stroyan@hpfcla.hp.com