[comp.sys.hp] Neutral File Format

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

Here's the Neutral File Format file viewer that doesn't need sbdl (Starbase
Display List) software.  It's called "lookonce", since it's not interactive.
If you want more databases, check pub/nff in skinner.cs.uoregon.edu, via
anonymous ftp.

Enjoy,

Eric Haines   hpfcla!hpfcrs!eye!erich@hplabs.hp.com


# 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@juniper> on Thu May 25 14:04:41 1989
#
# This archive contains:
#	README		def.h		lib.c		lib.h
#	lookat.c	lookonce.c	makefile	pol005.nff
#	pol006.nff	tetra.nff	
#

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

echo x - README
cat >README <<'@EOF'
Lookat
------

Needs sbdl (Starbase Display List) library to run.

unpack, cd into the new "SPD" directory, "make",
then type "lookat tetra.nff" for a mouse controlled view.

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


Lookonce
--------

unpack, cd into the new "SPD" directory, "make",
then type "lookonce tetra.nff" for a single view.

Define OUTDEV, OUTDRIVER if another device than the SRX is desired.

These are the assumed defaults for each program:

    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 - lookonce.c
cat >lookonce.c <<'@EOF'
/************************************************************************/
/*									*/
/*	program:	lookonce					*/
/*									*/
/*	usage:		lookonce [filename]				*/
/*									*/
/*	function:	examine a file in my neutral file format. Does	*/
/*			not use display lists: displays as data is read.*/
/*									*/
/*	input:		filename.nff					*/
/*			Define OUTDEV and OUTDRIVER			*/
/*			(default is SRX 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, 3D/Eye Inc, 12/5/86		*/
/*									*/
/************************************************************************/

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

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

#define BUFSIZE		255
#define OUT_RESOLUTION	4

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

/* 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 ;

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) ;
    }

    crt_setup() ;
    camera_setup() ;

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

    gclose( screendes ) ;
}

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


    gescape( screendes, SWITCH_SEMAPHORE, &arg1, &arg2 ) ;

    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 */

    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 ;
}

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


    polygon_format = -1 ;
    tot_light = 0 ;

    num_arg = 1 ;

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

    zbuffer_switch(screendes,1) ;		/* clear z-buffer */

  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 %f %f %f %f %f %f %f"
						  , &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, poly_color[0]*kd
				, poly_color[1]*kd
				, 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
				, poly_color[0]*ks
				, poly_color[1]*ks
				, poly_color[2]*ks
				) ;
	}


	/* light */
	else if ( sscanf( buffer, "l %f %f %f", &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, light_color[0]
				, light_color[1]
				, 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 ;

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

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


	/* 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 %f", &hither ) ;
	    camera.front = hither - 1.0 ;
	    fgets( buffer, BUFSIZE, fd ) ;
	    /* ???? unused for now */
	    sscanf( buffer, "resolution %d %d\n", &xresolution, &yresolution ) ;

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

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

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

	    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 ;

	    camera.front = hither - 1.0 ;

	    view_camera( screendes, &camera ) ;

	    /* view right handed coords - doesn't work
	    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 ) ;
	    */
	}


	/* background color */
	else if ( sscanf( buffer, "b %f %f %f", &poly_color[0],
						  &poly_color[1],
						  &poly_color[2]
						  ) == 3 ) {
	    background_color(screendes,
				poly_color[0],poly_color[1],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 ) ;

    make_picture_current( screendes ) ;

    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 ;
    float   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 *
	    (float)( num_pol+1 ) / (float)( 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*(float)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 lookonce.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 lookonce

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


lookonce:	lib.o lookonce.o
		cc -o lookonce lookonce.o lib.o \
		-ldd98721 -ldd98731 -lsb1 -lsb2 $(BASELIB)

lookonce.o:	$(INC) lookonce.c
		$(CC) lookonce.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 20
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 25
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