[comp.sources.misc] v16i007: sipp 2.0 - a library for 3D graphics, Part03/06

kent@sparky.IMD.Sterling.COM (Kent Landfield) (01/03/91)

Submitted-by: ingwa@isy.liu.se (Inge Wallin)
Posting-number: Volume 16, Issue 7
Archive-name: sipp2.0/part03

#!/bin/sh
# This is part 03 of sipp-2.0
# ============= libsipp/sipp.c ==============
if test ! -d 'libsipp'; then
    echo 'x - creating directory libsipp'
    mkdir 'libsipp'
fi
if test -f 'libsipp/sipp.c' -a X"$1" != X"-c"; then
	echo 'x - skipping libsipp/sipp.c (File already exists)'
else
echo 'x - extracting libsipp/sipp.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'libsipp/sipp.c' &&
/*
X * sipp - SImple Polygon Processor
X *
X *  A general 3d graphic package
X *
X *  Copyright Jonas Yngvesson  (jonas-y@isy.liu.se) 1988/89/90
X *            Inge Wallin      (ingwa@isy.liu.se)         1990
X *
X * This program is free software; you can redistribute it and/or modify
X * it under the terms of the GNU General Public License as published by
X * the Free Software Foundation; either version 1, or any later version.
X * This program is distributed in the hope that it will be useful,
X * but WITHOUT ANY WARRANTY; without even the implied warranty of
X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X * GNU General Public License for more details.
X * You can receive a copy of the GNU General Public License from the
X * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X * See the README for more information.
X *
X * Revision history:
X *
X * 901219  At last! 2.0 is out of the bag. And right in time for X-mas :-)
X *         *Major* rewrite. A new level in the object hierarchy introduced.
X *         An object is now a collection of surfaces and other objects
X *         (subobjects). This allows creation of complex composite objects.
X *         Objects in the hierarchy have coupled transformations.
X *         The old "Object" is now, more appropriately, called Surface.
X *         A fatal bug in the viewing transformation fixed.
X *         Objects and surfaces have internal reference counters so deletion
X *         won't leave dangling references in an hierachy.
X *         The code is now almost readable.
X *         Images format changed to PPM since much more applications
X *         exists that can handle that format.
X *         The shader package is extended and a package with functions
X *         to create geometric primitives as objects is provided.
X *
X * 901030  More general shader interface. The simple internal shader
X *         was moved out of sipp into its own file. Several shaders
X *         provided together with it in a "shader package".
X *
X * 900712  Major code beautifying, quite a lot left to do though...
X *         Typedefs instead of raw structures,
X *         much better typenames, lots of comments added (probably
X *         not enough though). Split into a few header files and
X *         a source file. More portable interface.
X *
X * 891124  Made the object representation independent of the
X *         illumination model used. A user can now supply his
X *         own surface description and shading function.
X *         Added support for texture mapping (both solid and 2d).
X *         No texture mapping is built into the internal shader though.
X *
X * 891120  Added colour. New illumination model. Simple antialiasing
X *         with double oversampling and a box filter.
X *
X * 891028  Converted from sippuz to sipp. Z-buffer changed 
X *         to scan-line z-buffer.
X *         Phong-shading.
X *         The stack notion introduced.
X *         Changed fixed viewpoint to a user defined one.
X *         World coordinates and picture resolution can be chosen
X *         freely.
X *
X * 881128  Converted from Pascal to C. Gouraud shading. Redesign of the
X *         data structures.
X *
X * 88????  Original program "sippuz - simple polygon processing using a
X *         z-buffer", written in Hedrick Pascal on a DEC-20 system
X *         running TOPS-20. Greyscales only. Flat shading.
X */
X
X
#include <stdio.h>
#include <math.h>
#include <malloc.h>
#ifndef NOMEMCPY
#include <memory.h>
#endif
#include <xalloca.h>
X
#include <sipp.h>
#include <sipptypes.h>
#include <geometric.h>
X
X
#define VERSION "2.0"
X
#define ZCLIPF 100.0        /* Magic number used when defining hither & yon */
X
X
X
/*
X * Global variables.
X */
static Vertex      *vertex_tree;     /* Vertex tree for current object. */
static Vertex_ref  *vertex_stack;    /* Vertex stack for current polygon. */
static Vertex_ref  *vstack_bottom;   /* Last entry in vertex stack. */
static Polygon     *poly_stack;      /* Polygon stack for current object. */
static Inst_object *object_db;       /* Object database. */
static Lightsource *lightsrc_stack;  /* Lightsource list. */
static Bucket      *y_bucket;        /* Y-bucket for edge lists. */
static double       dist_limit;      /* Minimal distance between two      */
X                                     /* vertices without them being       */
X                                     /* considered to be the same vertex. */
static int          first_vertex;    /* Used when determining if we are   */
X                                     /* installing the first vertex in an */
X                                     /* object. *Not* a boolean!          */
X
/*
X * Stack of transformation matrices used
X * when traversing an object hierarchy.
X */
static struct tm_stack_t {
X    Transf_mat         mat;
X    struct tm_stack_t *next;
} *tm_stack;
X
static Transf_mat      curr_mat;     /* Current transformation matrix */
X
static Transf_mat      ident_matrix = {{        /* Unit tranfs. matrix */
X                           { 1.0, 0.0, 0.0 },
X                           { 0.0, 1.0, 0.0 },
X                           { 0.0, 0.0, 1.0 },
X                           { 0.0, 0.0, 0.0 }
X                       }};
X
X
/*
X * Viewpoint definition
X */
static struct {
X    double x0, y0, z0;    /* viewpoint position */
X    double x, y, z;       /* point to look at */
X    Vector vec;           /* vector from point to eye, used in shading calc. */
X    Vector up;            /* Up direction in the view */ 
X    double focal_ratio;
} camera;
X
X
X
/*======================== "Safe" malloc ================================*/
static char *
smalloc(size)
X    int size;
{
X    char *p;
X
X    p = (char *)malloc(size);
X    if (p == NULL) {
X        fprintf(stderr, "Out of virtual memory.\n");
X        exit(1);
X    }
X
X    return p;
}
X
X
X
/*================ Functions that handles lightsources ==================*/
X
/*
X * Define a new lightsource in the scene.
X */
void
lightsource_push(x, y, z, intensity)
X    double  x, y, z, intensity;
{
X    double       norm;
X    Lightsource *lp;
X
X    norm = sqrt(x * x + y * y + z * z);
X    lp = (Lightsource *)smalloc(sizeof(Lightsource));
X    lp->dir.x = x / norm;
X    lp->dir.y = y / norm;
X    lp->dir.z = z / norm;
X    lp->intensity = intensity;
X    lp->next = lightsrc_stack;
X    lightsrc_stack = lp;
}
X
X
X
/*================ Functions that handles the viewpoint ==================*/
X
/*
X * Calculate the vector from the point of interest
X * to the viewpoint. The shaders needs this vector normalized
X * while the view coordinate transformation needs it
X * non normalized, therefore we need this routine to
X * recalculate the non normalized value.
X */
static void
view_vec_eval()
{
X    MakeVector(camera.vec, 
X               camera.x0 - camera.x, 
X               camera.y0 - camera.y, 
X               camera.z0 - camera.z);
}
X
X
X    
/*
X * Define the viewpoint.
X */
void
view_from(x0, y0, z0)
X    double x0, y0, z0;
{
X    camera.x0 = x0;
X    camera.y0 = y0;
X    camera.z0 = z0;
X    view_vec_eval();
}
X
X
/*
X * Define the point that we are looking at.
X */
void
view_at(x, y, z)
X    double x, y, z;
{
X    camera.x = x;
X    camera.y = y;
X    camera.z = z;
X    view_vec_eval();
}
X
X
/*
X * Define the "up" direction of the view (well, rather the y-z plane).
X */
void
view_up(x, y, z)
X    double x, y, z;
{
X    MakeVector(camera.up, x, y, z);
}
X
X
/*
X * Set the focal ratio for the "camera".
X */
void
view_focal(ratio)
X    double ratio;
{
X    camera.focal_ratio = ratio;
}
X
X
/*
X * Set all viewpoint parameters in one call.
X */
void
viewpoint(x0, y0, z0, x, y, z, ux, uy, uz, ratio)
X    double x0, y0, z0, x, y, z, ux, uy, uz, ratio;
{
X    camera.x0 = x0;
X    camera.y0 = y0;
X    camera.z0 = z0;
X    camera.x = x;
X    camera.y = y;
X    camera.z = z;
X    MakeVector(camera.up, ux, uy, uz);
X    camera.focal_ratio = ratio;
X    view_vec_eval();
}
X
X
X
/*============= Functions that handles the object database ================*/
X
/*
X * Search for a vertex in a vertex tree. Vertices are asumed
X * to be equal if they differ less than dist_limit in all directions.
X *
X * If the vertex is not found, install it in the tree.
X */
static Vertex *
vertex_lookup(x, y, z, u, v, w, p)
X    double   x, y, z, u, v, w;
X    Vertex **p;
{
X    double  xdist, ydist, zdist;
X
X    if (*p == NULL) {
X        *p = (Vertex *)smalloc(sizeof(Vertex));
X        (*p)->x = x;
X        (*p)->y = y;
X        (*p)->z = z;
X        (*p)->a = 0;
X        (*p)->b = 0;
X        (*p)->c = 0;
X        (*p)->u = u;
X        (*p)->v = v;
X        (*p)->w = w;
X        (*p)->big = NULL;
X        (*p)->sml = NULL;
X        return *p;
X    } else if ((xdist = x - ((*p)->x)) > dist_limit) {
X        return (vertex_lookup(x, y, z, u, v, w, &((*p)->big)));
X    } else if (xdist < -dist_limit) {
X        return (vertex_lookup(x, y, z, u, v, w, &((*p)->sml)));
X    } else if ((ydist = y - ((*p)->y)) > dist_limit) {
X        return (vertex_lookup(x, y, z, u, v, w, &((*p)->big)));
X    } else if (ydist < -dist_limit) {
X        return (vertex_lookup(x, y, z, u, v, w, &((*p)->sml)));
X    } else if ((zdist = z - ((*p)->z)) > dist_limit) {
X        return (vertex_lookup(x, y, z, u, v, w, &((*p)->big)));
X    } else if (zdist < -dist_limit) {
X        return (vertex_lookup(x, y, z, u, v, w, &((*p)->sml)));
X    } else {
X        return *p;
X    }
}
X
X
/*
X * Push a vertex on the vertex stack (without texture coordinates).
X */
void
vertex_push(x, y, z)
X    double  x, y, z;
{
X    vertex_tx_push(x, y, z, (double)0.0, (double)0.0, (double)0.0);
}
X
X
/*
X * Push a vertex on the vertex stack (with texture coordinates).
X */
void
vertex_tx_push(x, y, z, u, v, w)
X    double  x, y, z, u, v, w;
{
X    Vertex_ref *vref;
X
X   /* 
X    * To get a reasonable dist_limit we use the following "heuristic" 
X    * value:
X    * The distance between the first two vertices installed in
X    * the scene, multiplied by the magic number 1e-10, unless
X    * they are the same vertex. In that case 1e-10 is used until
X    * we get a vertex that differs from the first.
X    */
X    if (!first_vertex)
X        first_vertex++;
X    else if (first_vertex == 1) {
X        dist_limit = sqrt((x - vertex_tree->x) * (x - vertex_tree->x)
X                        + (y - vertex_tree->y) * (y - vertex_tree->y)
X                        + (z - vertex_tree->z) * (z - vertex_tree->z))
X                   * 1e-10;                      /* Magic!!! */
X        if (dist_limit != 0.0)
X            first_vertex++;
X        else
X            dist_limit = 1e-10;                  /* More Magic */
X    }
X    vref = (Vertex_ref *)smalloc(sizeof(Vertex_ref));
X    if (vertex_stack == NULL) {
X        vertex_stack = vref;
X    } else {
X        vstack_bottom->next = vref;
X    }
X    vstack_bottom = vref;
X    vref->vertex = vertex_lookup(x, y, z, u, v, w, &vertex_tree);
X    vref->next = NULL;
}
X
X
/*
X * Push a polygon on the polygon stack. Empty the vertex stack afterwards.
X */
void
polygon_push()
{
X    Polygon *polyref;
X
X    if (vertex_stack != NULL) {
X        polyref = (Polygon *)smalloc(sizeof(Polygon));
X        polyref->vertices = vertex_stack;
X        polyref->backface = 0;
X        polyref->next = poly_stack;
X        poly_stack = polyref;
X        vertex_stack = NULL;
X    }
}
X
X
/*
X * Create a surface of all polygons in the polygon stack.
X * Empty the polygon stack afterwards.
X */
Surface *
surface_create(surf_desc, shader)
X    void   *surf_desc;
X    Shader *shader;
{
X    Surface *surfref;
X    
X    if (poly_stack != NULL) {
X        surfref = (Surface *)smalloc(sizeof(Surface));
X        surfref->vertices = vertex_tree;
X        surfref->polygons = poly_stack;
X        surfref->surface = surf_desc;
X        surfref->shader = shader;
X        surfref->ref_count = 0;
X        surfref->next = NULL;
X        vertex_tree = NULL;
X        poly_stack = NULL;
X        first_vertex = 0;
X        return surfref;
X    } else
X        return NULL;
}
X
X
/*
X * Create a surface to be shaded with the simple shader.
X */
Surface *
surface_basic_create(ambient, red, grn, blu, specular, c3)
X    double ambient, red, grn, blu, specular, c3;
{
X    Surf_desc *surf_desc;
X
X    surf_desc = (Surf_desc *)smalloc(sizeof(Surf_desc));
X    surf_desc->ambient = ambient;
X    surf_desc->color.red = red;
X    surf_desc->color.grn = grn;
X    surf_desc->color.blu = blu;
X    surf_desc->specular = specular;
X    surf_desc->c3 = c3;
X    return surface_create(surf_desc, basic_shader);
}
X
X
/*
X * Set SURFACE to be shaded with the shading function SHADER
X * using the surface description SURF_DESC.
X */
void
surface_set_shader(surface, surf_desc, shader)
X    Surface *surface;
X    void    *surf_desc;
X    Shader  *shader;
{
X    
X    if (surface != NULL) {
X        surface->surface = surf_desc;
X        surface->shader = shader;
X    }
}
X
X
/*
X * Set SURFACE to be shaded with the simple shader.
X */
void
surface_basic_shader(surface, ambient, red, grn, blu, specular, c3)
X    Surface *surface;
X    double  ambient, red, grn, blu, specular, c3;
{
X    Surf_desc *surf_desc;
X
X    surf_desc = (Surf_desc *)smalloc(sizeof(Surf_desc));
X    surf_desc->ambient = ambient;
X    surf_desc->color.red = red;
X    surf_desc->color.grn = grn;
X    surf_desc->color.blu = blu;
X    surf_desc->specular = specular;
X    surf_desc->c3 = c3;
X    surface_set_shader(surface, surf_desc, basic_shader);
}
X
X
X
/*
X * Copy a vertex tree.
X */
static Vertex *
copy_vertices(vp)
X    Vertex *vp;
{
X    Vertex *tmp;
X
X    if (vp == NULL)
X        return NULL;
X    tmp = (Vertex *)smalloc(sizeof(Vertex));
X    *tmp = *vp;
X    tmp->big = copy_vertices(vp->big);
X    tmp->sml = copy_vertices(vp->sml);
X    return tmp;
}
X
X
/*
X * We have a list of vertes references, each pointing into a certain
X * vertex tree. Create a new list with pointers into a copy of the
X * first vertex tree.
X */
static Vertex_ref *
copy_vlist(vp, surface)
X    Vertex_ref *vp;
X    Surface    *surface;
{
X    Vertex_ref *tmp;
X    
X    if (vp == NULL)
X        return NULL;
X    tmp = (Vertex_ref *)smalloc(sizeof(Vertex_ref));
X    tmp->vertex = vertex_lookup(vp->vertex->x, vp->vertex->y, vp->vertex->z,
X                                vp->vertex->u, vp->vertex->v, vp->vertex->w,
X                                &(surface->vertices));
X    tmp->next = copy_vlist(vp->next, surface);
X    return tmp;
}
X
X
/*
X * Copy a list of polygons.
X */
static Polygon *
copy_polygons(pp, surface)
X    Polygon *pp;
X    Surface *surface;
{
X    Polygon *tmp;
X
X    if (pp == NULL)
X        return NULL;
X    tmp = (Polygon *)smalloc(sizeof(Polygon));
X    tmp->vertices = copy_vlist(pp->vertices, surface);
X    tmp->next = copy_polygons(pp->next, surface);
X    return tmp;
}
X
X
/*
X * Copy a list of surfaces. All polygons and vertices are copied but
X * the shader and surface descriptions are the same as in the
X * original surfaces.
X */
static Surface *
surface_copy(surface)
X    Surface  *surface;
{
X    Surface  *newsurf;
X
X    if (surface != NULL) {
X        newsurf = (Surface *)smalloc(sizeof(Surface));
X        if (newsurf == NULL) {
X            return NULL;
X        }
X        memcpy(newsurf, surface, sizeof(Surface));
X        newsurf->vertices = copy_vertices(surface->vertices);
X        newsurf->polygons = copy_polygons(surface->polygons, newsurf);
X        newsurf->ref_count = 1;
X        newsurf->next = surface_copy(surface->next);
X        return newsurf;
X    } else {
X        return NULL;
X    }
}
X
X
/*
X * Delete a vertex tree.
X */
static void
delete_vertices(vtree)
X    Vertex **vtree;
{
X    if (*vtree != NULL) {
X        delete_vertices(&((*vtree)->big));
X        delete_vertices(&((*vtree)->sml));
X        free(*vtree);
X        *vtree = NULL;
X    }
}
X
X
/*
X * Delete a surface list.
X */
static void
surface_delete(surface)
X    Surface *surface;
{
X    Vertex_ref *vref1, *vref2;
X    Polygon    *polyref1, *polyref2;
X
X    if (surface != NULL) {
X        if (--surface->ref_count == 0) {
X            if (surface->next != NULL) {
X                surface_delete(surface->next);
X            }
X            polyref2 = surface->polygons;
X            while (polyref2 != NULL) {
X                vref2 = polyref2->vertices;
X                while (vref2 != NULL) {
X                    vref1 = vref2;
X                    vref2 = vref2->next;
X                    free(vref1);
X                }
X                polyref1 = polyref2;
X                polyref2 = polyref2->next;
X                free(polyref1);
X            }
X            delete_vertices(&(surface->vertices));
X            free(surface);
X        }
X    }
}
X
X
/*
X * Install an object in the rendering database.
X */
static void
r_object_install(obj, obj_tree)
X    Object       *obj;
X    Inst_object **obj_tree;
{
X    if (obj != NULL) {
X        obj->ref_count++;
X        if (*obj_tree == NULL) {
X            *obj_tree = (Inst_object *)smalloc(sizeof(Inst_object));
X            (*obj_tree)->object = obj;
X            (*obj_tree)->big = NULL;
X            (*obj_tree)->sml = NULL;
X        } else if (obj > (*obj_tree)->object) {
X            r_object_install(obj, &(*obj_tree)->big);
X        } else if (obj < (*obj_tree)->object) {
X            r_object_install(obj, &(*obj_tree)->sml);
X        }
X    }
}
X
X
/*
X * Interface to r_object_install(). (Why is there no
X * subfunctions in C?...)
X */
void
object_install(obj)
X    Object *obj;
{
X    r_object_install(obj, &object_db);
}
X
X
X
/*
X * Subfunction to r_object_uninstall.
X */
static void
r_del(r, q)
X    Inst_object **r;
X    Inst_object  *q;
{
X    if ((*r)->big != NULL) {
X        r_del(&((*r)->big), q);
X    } else {
X        q->object = (*r)->object;
X        q = *r;
X        *r = (*r)->sml;
X        free(q);
X    }
}
X
X
/*
X * Delete an object from the rendering database.
X * The object itself is not deleted of course.
X */
static void
r_object_uninstall(obj, root)
X    Object       *obj;
X    Inst_object **root;
{
X    Inst_object *ptr;
X
X    if (*root == NULL) {
X        return;            /* Object is not in the tree */
X    } else if (obj < (*root)->object) {
X        r_object_uninstall(obj, &(*root)->sml);
X    } else if (obj > (*root)->object) {
X        r_object_uninstall(obj, &(*root)->big);
X    } else {
X        obj->ref_count--;
X        ptr = *root;
X        if (ptr->big == NULL) {
X            *root = ptr->sml;
X        } else if (ptr->sml == NULL) {
X            *root = ptr->big;
X        } else {
X            r_del(&ptr->sml, ptr);
X        }
X    }
}
X    
X    
/*
X * Interface to r_object_uninstall.
X */
void
object_uninstall(obj)
X    Object *obj;
{
X    r_object_uninstall(obj, &object_db);
}
X
X
X
/*
X * Create an empty object. Before it is rendered it
X * must get a surface or a subobject. 
X */
Object *
object_create()
{
X    Object *obj;
X
X    obj = (Object *)smalloc(sizeof(Object));
X    obj->surfaces = NULL;
X    obj->sub_obj = NULL;
X    MatCopy(&obj->transf, &ident_matrix);
X    obj->ref_count = 0;
X    obj->next = NULL;
X
X    return obj;
}
X
X
X
/*
X * Copy the top object in an object hierarchy.
X * The new object will reference the same
X * subobjects and surfaces as the original object.
X * if REF_COUNT_UPDATE is true, the reference counts
X * in the surfaces and subobjects will be incremented.
X */
static Object *
object_copy(object, ref_count_update)
X    Object *object;
X    bool    ref_count_update;
{
X    Object *newobj;
X
X    if (object == NULL) {
X        return NULL;
X    }
X
X    if ((newobj = (Object *)smalloc(sizeof(Object))) != NULL) {
X        memcpy(newobj, object, sizeof(Object));
X        if (ref_count_update) {
X            if (newobj->sub_obj != NULL) {
X                newobj->sub_obj->ref_count++;
X            }
X            if (newobj->surfaces != NULL) {
X                newobj->surfaces->ref_count++;
X            }
X        }
X        MatCopy(&newobj->transf, &ident_matrix);
X        newobj->ref_count = 0;
X        newobj->next = NULL;
X    }
X
X    return newobj;
}
X
X
/*
X * Copy a list of objects. If SURF_COPY is true
X * the surfaces in the objects will be copied too.
X */
static Object *
object_list_copy(object, surf_copy)
X    Object *object;
X    bool    surf_copy;
{
X    Object *newobj;
X
X    if (object == NULL) {
X        return NULL;
X    }
X
X    if ((newobj = (Object *)smalloc(sizeof(Object))) != NULL) {
X        memcpy(newobj, object, sizeof(Object));
X        newobj->ref_count = 0;
X    } else {
X        return NULL;
X    }
X
X    if (surf_copy) {
X        newobj->surfaces = surface_copy(object->surfaces);
X    } else if (newobj->surfaces != NULL){
X        newobj->surfaces->ref_count++;
X    }
X
X    newobj->sub_obj = object_list_copy(object->sub_obj, surf_copy);
X    if (newobj->sub_obj != NULL) {
X        newobj->sub_obj->ref_count++;
X    }
X    newobj->next = object_list_copy(object->next, surf_copy);
X    if (newobj->next != NULL) {
X        newobj->next->ref_count++;
X    }
X
X    return newobj;
}
X    
X
X
/*
X * Copy the top node of an object hierarchy. The
X * subobjects and surface references will be the
X * same as in the original.
X */
Object *
object_instance(obj)
X    Object *obj;
{
X    return object_copy(obj, TRUE);
}
X
X
X
/*
X * Copy an object hierarchy. The objects in
X * the new hierarchy will reference the same
X * surfaces as the object in
X * the old hierarchy, but all object nodes
X * will be duplicated.
X */
Object *
object_dup(object)
X    Object *object;
{
X    Object *newobj;
X
X    if ((newobj = object_copy(object, FALSE)) == NULL) {
X        return NULL;
X    }
X
X    newobj->sub_obj = object_list_copy(object->sub_obj, FALSE);
X    newobj->next = NULL;
X
X    return newobj;
}
X 
X
X
/*
X * Copy an object hierarchy. All object nodes
X * and surfaces in the old hierarchy
X * will be duplicated.
X */
Object *
object_deep_dup(object)
X    Object *object;
{
X    Object *newobj;
X
X    if ((newobj = object_copy(object, FALSE)) == NULL) {
X        return NULL;
X    }
X
X    newobj->surfaces = surface_copy(object->surfaces);
X    newobj->sub_obj = object_list_copy(object->sub_obj, TRUE);
X    newobj->next = NULL;
X
X    return newobj;
}
X
X
X
/*
X * Recursively delete an object hierarchy. Reference
X * counts are decremented and if the result is zero
X * the recursion continues and the memory used is freed.
X */
static void
r_object_delete(object)
X    Object * object;
{
X    if (object != NULL) {
X        if (--object->ref_count == 0) {
X            surface_delete(object->surfaces);
X            r_object_delete(object->sub_obj);
X            r_object_delete(object->next);
X            free(object);
X        }
X    }
}
X
X
X
/*
X * Delete an object hierarchy. This is only possible
X * to do on a top level object.
X */
void
object_delete(object)
X    Object * object;
{
X    if (object != NULL) {
X        if (object->ref_count == 0) {         /* Is it a top level object? */
X            surface_delete(object->surfaces);
X            r_object_delete(object->sub_obj);
X            r_object_delete(object->next);
X            free(object);
X        }
X    }
}
X
X
X
/*
X * Add SUBOBJ as a subobject in OBJECT. SUBOBJ is appended
X * on the *end* of OBJECT's subobject list, 
X * so that if SUBOBJ, for some obscure reason, 
X * were the head of an object list, we don't loose
X * the rest of that list. 
X * Remove SUBOBJ from the rendering database since it is no
X * longer a root object in an hierarchy.
X */
void
object_add_subobj(object, subobj)
X    Object *object, *subobj;
{
X    Object *oref;
X
X    if (object == NULL || subobj == NULL) {
X        return;
X    }
X
X    if (object->sub_obj == NULL) {
X        object->sub_obj = subobj;
X    } else {
X        oref = object->sub_obj;
X        while (oref->next != NULL) {
X            oref = oref->next;
X        }
X        oref->next = subobj;
X    }
X
X    subobj->ref_count++;
X    object_uninstall(subobj);
}
X
X
X
/*
X * Add SURFACE to the list of surfaces belonging
X * to OBJECT. SURFACE is appended on the *end* of the
X * list for the same reasons as in object_add_subobj.
X */
void
object_add_surface(object, surface)
X    Object  *object;
X    Surface *surface;
{
X    Surface *sref;
X
X    if (object == NULL || surface == NULL) {
X        return;
X    }
X
X    if (object->surfaces == NULL) {
X        object->surfaces = surface;
X    } else {
X        sref = object->surfaces;
X        while (sref->next != NULL) {
X            sref = sref->next;
X        }
X        sref->next = surface;
X    }
X
X    surface->ref_count++;
}
X    
X    
X    
X    
/*============= Functions that handles object transformations==============*/
X
/*
X * Set the transformation matrix of OBJ to MATRIX.
X */
void
object_set_transf(obj, matrix)
X    Object     *obj;
X    Transf_mat *matrix;
{
X    MatCopy(&obj->transf, matrix);
}
X
X
/*
X * Set the transformation matrix of OBJ to the identity matrix.
X */
void
object_clear_transf(obj)
X    Object *obj;
{
X    MatCopy(&obj->transf, &ident_matrix);
}
X
X
/*
X * Post multiply MATRIX into the transformation matrix of OBJ.
X */
void
object_transform(obj, matrix)
X    Object     *obj;
X    Transf_mat *matrix;
{
X    mat_mul(&obj->transf, &obj->transf, matrix);
}
X
X
/*
X * Rotate the object OBJ ANG radians about the x-axis.
X */
void
object_rot_x(obj, ang)
X    Object *obj;
X    double  ang;
{
X    mat_rotate_x(&obj->transf, ang);
}
X
X
/*
X * Rotate the object OBJ ANG radians about the y-axis.
X */
void
object_rot_y(obj, ang)
X    Object *obj;
X    double  ang;
{
X    mat_rotate_y(&obj->transf, ang);
}
X
X
/*
X * Rotate the object OBJ ANG radians about the z-axis.
X */
void
object_rot_z(obj, ang)
X    Object *obj;
X    double  ang;
{
X    mat_rotate_z(&obj->transf, ang);
}
X
X
/*
X * Rotate the object OBJ ANG radians about the line defined
X * by POINT and VEC.
X */
void
object_rot(obj, point, vec, ang)
X    Object *obj;
X    Vector *point;
X    Vector *vec;
X    double  ang;
{
X    mat_rotate(&obj->transf, point, vec, ang);
}
X
X
/*
X * Scale the object OBJ with respect to the origin.
X */
void
object_scale(obj, xscale, yscale, zscale)
X    Object *obj;
X    double  xscale, yscale, zscale;
{
X    mat_scale(&obj->transf, xscale, yscale, zscale);
}
X
X
/*
X * Translate the object OBJ.
X */
void
object_move(obj, dx, dy, dz)
X    Object *obj;
X    double  dx, dy, dz;
{
X    mat_translate(&obj->transf, dx, dy, dz);
}
X
X
X
X
/*============= Functions that handles rendering of the scene==============*/
X
X
/*
X * Calculate the normal vector for all polygons in the polygon list PSTART.
X *
X * Check if the polygon is backfacing with respect to the current
X * viewpoint.
X *
X * The normalized normal is added to a normal kept at each vertex
X * in the polygon. This will produce, at each vertex, an average of the
X * normals of the adjectent plygons.
X */
static void
calc_normals(pstart, eyepoint)
X    Polygon *pstart;    /* Head of polygon list */
X    Vector   eyepoint;  /* Viewpoint transformed to local coordinate system */
{
X    Vector      normal;
X    Vertex_ref *vref1, *vref2;
X    Polygon    *polyref;
X    double      plane_const;
X
X    for (polyref = pstart; polyref != NULL; polyref = polyref->next) {
X        vref1 = polyref->vertices;
X        vref2 = vref1->next;
X
X        normal.x = normal.y = normal.z = 0.0;
X        do {
X            normal.x += ((vref1->vertex->y - vref2->vertex->y)
X                         * (vref1->vertex->z + vref2->vertex->z));
X            normal.y += ((vref1->vertex->z - vref2->vertex->z)
X                         * (vref1->vertex->x + vref2->vertex->x));
X            normal.z += ((vref1->vertex->x - vref2->vertex->x)
X                         * (vref1->vertex->y + vref2->vertex->y));
X            vref1 = vref1->next;
X            vref2 = ((vref2->next == NULL)?polyref->vertices:vref2->next);
X        } while (vref1 != NULL);
X        vecnorm(&normal);
X
X        plane_const = -(normal.x * vref2->vertex->x
X                        + normal.y * vref2->vertex->y
X                        + normal.z * vref2->vertex->z);
X        if (VecDot(eyepoint, normal) + plane_const <= 0.0) {
X            polyref->backface = TRUE;
X        } else {
X            polyref->backface = FALSE;
X        }
X            
X        for (vref1 = polyref->vertices; vref1 != NULL; vref1 = vref1->next) {
X            vref1->vertex->a += normal.x;
X            vref1->vertex->b += normal.y;
X            vref1->vertex->c += normal.z;
X        }
X    }
}
X
X
X
/*
X * Walk around a polygon, create the surrounding
X * edges and sort them into the y-bucket.
X * Clip polygons in y at the same time.
X */
static void
create_edges(view_vert, yres, polygon, surface)
X    View_coord *view_vert;
X    int         yres;
X    int         polygon;
X    Surface    *surface;
{
X    Edge       *edge;
X    View_coord *view_ref, *last;
X    int         nderiv, y1, y2, ymax;
X    int         clip1, clip2;
X    double      deltay, ratio;
X    double      x1, x2, xstep;
X    double      z1, z2, zstep;
X    double      nx1, nx2, nxstep;
X    double      ny1, ny2, nystep;
X    double      nz1, nz2, nzstep;
X    double      u1, u2, ustep;
X    double      v1, v2, vstep;
X    double      w1, w2, wstep;
X
X    view_ref = last = view_vert;
X    ymax = yres - 1;
X    do {
X        view_ref = view_ref->next;
X        x1 = view_ref->x;
X        x2 = view_ref->next->x;
X        y1 = view_ref->y;
X        y2 = view_ref->next->y;
X        z1 = view_ref->z;
X        z2 = view_ref->next->z;
X        nx1 = view_ref->nx;
X        nx2 = view_ref->next->nx;
X        ny1 = view_ref->ny;
X        ny2 = view_ref->next->ny;
X        nz1 = view_ref->nz;
X        nz2 = view_ref->next->nz;
X        u1 = view_ref->u;
X        u2 = view_ref->next->u;
X        v1 = view_ref->v;
X        v2 = view_ref->next->v;
X        w1 = view_ref->w;
X        w2 = view_ref->next->w;
X        clip1 = (y1 < 0) + ((y1 > ymax) << 1);
X        clip2 = (y2 < 0) + ((y2 > ymax) << 1);
X        if (!(clip1 & clip2)) {
X            if (clip1 != 0) {
X                if (clip1 == 1)
X                    ratio = (0.0 - (double)y1) / (double)(y2 - y1);
X                else 
X                    ratio = (double)(ymax - y1) / (double)(y2 - y1);
X                x1 = x1 + ratio * (x2 - x1);
X                y1 = y1 + ratio * (y2 - y1);
X                z1 = z1 + ratio * (z2 - z1);
X                nx1 = nx1 + ratio * (nx2 - nx1);
X                ny1 = ny1 + ratio * (ny2 - ny1);
X                nz1 = nz1 + ratio * (nz2 - nz1);
X                u1 = u1 + ratio * (u2 - u1);
X                v1 = v1 + ratio * (v2 - v1);
X                w1 = w1 + ratio * (w2 - w1);
X            }
X            if (clip2 != 0) {
X                if (clip2 == 1)
X                    ratio = (0.0 - (double)y2) / (double)(y1 - y2);
X                else 
X                    ratio = (double)(ymax - y2) / (double)(y1 - y2);
X                x2 = x2 + ratio * (x1 - x2);
X                y2 = y2 + ratio * (y1 - y2);
X                z2 = z2 + ratio * (z1 - z2);
X                nx2 = nx2 + ratio * (nx1 - nx2);
X                ny2 = ny2 + ratio * (ny1 - ny2);
X                nz2 = nz2 + ratio * (nz1 - nz2);
X                u2 = u2 + ratio * (u1 - u2);
X                v2 = v2 + ratio * (v1 - v2);
X                w2 = w2 + ratio * (w1 - w2);
X            }
X            deltay = (double)(y2 - y1);
X            if (deltay > 0.0)
X	        nderiv = 1;
X	    else if (deltay < 0.0)
X	        nderiv = -1;
X	    else
X	        nderiv = 0;
X            if (nderiv) {
X	        deltay = fabs(deltay);
X                xstep = (x2 - x1) / deltay;
X                zstep = (z2 - z1) / deltay;
X                nxstep = (nx2 - nx1) / deltay;
X                nystep = (ny2 - ny1) / deltay;
X                nzstep = (nz2 - nz1) / deltay;
X                ustep = (u2 - u1) / deltay;
X                vstep = (v2 - v1) / deltay;
X                wstep = (w2 - w1) / deltay;
X                edge = (Edge *)smalloc(sizeof(Edge));
X                if (nderiv > 0) {
X                    edge->y = y2;
X                    edge->y_stop = y1;
X                    edge->x = x2;
X                    edge->z = z2;
X                    edge->nx = nx2;
X                    edge->ny = ny2;
X                    edge->nz = nz2;
X                    edge->u = u2;
X                    edge->v = v2;
X                    edge->w = w2;
X                    edge->xstep = -xstep;
X                    edge->zstep = -zstep;
X                    edge->nxstep = -nxstep;
X                    edge->nystep = -nystep;
X                    edge->nzstep = -nzstep;
X                    edge->ustep = -ustep;
X                    edge->vstep = -vstep;
X                    edge->wstep = -wstep;
X                } else {
X                    edge->y = y1;
X                    edge->y_stop = y2;
X                    edge->x = x1;
X                    edge->z = z1;
X                    edge->nx = nx1;
X                    edge->ny = ny1;
X                    edge->nz = nz1;
X                    edge->u = u1;
X                    edge->v = v1;
X                    edge->w = w1;
X                    edge->xstep = xstep;
X                    edge->zstep = zstep;
X                    edge->nxstep = nxstep;
X                    edge->nystep = nystep;
X                    edge->nzstep = nzstep;
X                    edge->ustep = ustep;
X                    edge->vstep = vstep;
X                    edge->wstep = wstep;
X                }
X            } else {
X                zstep = (z2 - z1) / fabs(x2 - x1);
X                edge = (Edge *)smalloc(sizeof(Edge));
X                edge->y = y2;
X                edge->y_stop = y1;
X                if (x1 < x2) {
X                    edge->x = x1;
X                    edge->z = z1;
X                    edge->xstep = x2;
X                    edge->zstep = zstep;
X                } else {
X                    edge->x = x2;
X                    edge->z = z2;
X                    edge->xstep = x1;
X                    edge->zstep = -zstep;
X                }
X            }
X            edge->polygon = polygon;
X            edge->surface = surface;
X            edge->next = NULL;
X            if (y_bucket[edge->y].last == NULL) {
X                y_bucket[edge->y].first = edge;
X                y_bucket[edge->y].last = edge;
X            } else {
X                y_bucket[edge->y].last->next = edge;
X                y_bucket[edge->y].last = edge;
X            }
X        }
X    } while (view_ref != last);
}
X
X
/*
X * Transform vertices into view coordinates. The transform is
X * defined in MATRIX. Store the transformed vertices in a
X * temporary list, create edges in the y_bucket.
X */
static void
transf_vertices(vertex_list, surface, matrix, tr_mat, xsiz, ysiz)
X    Vertex_ref *vertex_list;
X    Surface    *surface;
X    double      matrix[4][4];
X    Transf_mat *tr_mat;
X    double      xsiz, ysiz;
{
X    static int  polygon = 0;        /* incremented for each call to provide */
X                                    /* unique polygon id numbers */
X    Vertex_ref *vref;
X    View_coord *view_ref, *nhead, *ntail;
X    double      minsize;
X    double      w, tmp;
X    bool        first, last;
X
X    first = TRUE;
X    last = FALSE;
X    vref = vertex_list;
X    nhead = NULL;
X
X    minsize = ((xsiz > ysiz) ? ysiz : xsiz);
X
X    while ((vref != NULL) && !last) {
X
X        view_ref = (View_coord *)alloca(sizeof(View_coord));
X        last = (vref->next == NULL);
X
X        view_ref->nx = (vref->vertex->a * tr_mat->mat[0][0] 
X                        + vref->vertex->b * tr_mat->mat[1][0] 
X                        + vref->vertex->c * tr_mat->mat[2][0]);
X        view_ref->ny = (vref->vertex->a * tr_mat->mat[0][1] 
X                        + vref->vertex->b * tr_mat->mat[1][1] 
X                        + vref->vertex->c * tr_mat->mat[2][1]);
X        view_ref->nz = (vref->vertex->a * tr_mat->mat[0][2] 
X                        + vref->vertex->b * tr_mat->mat[1][2] 
X                        + vref->vertex->c * tr_mat->mat[2][2]);
X
X        w = vref->vertex->x * matrix[0][3] + vref->vertex->y * matrix[1][3] +
X	    vref->vertex->z * matrix[2][3] + matrix[3][3];
X        view_ref->x = ((vref->vertex->x * matrix[0][0] 
X                        + vref->vertex->y * matrix[1][0] 
X                        + vref->vertex->z * matrix[2][0]
X                        + matrix[3][0]) * minsize / w + xsiz);
X        tmp         = ((vref->vertex->x * matrix[0][1] 
X                        + vref->vertex->y * matrix[1][1] 
X                        + vref->vertex->z * matrix[2][1]
X                        + matrix[3][1]) * minsize / w + ysiz) ;
X
X        view_ref->y = (int)(tmp + 0.5);
X        view_ref->z = (vref->vertex->x * matrix[0][2] + vref->vertex->y *
X                       matrix[1][2] + vref->vertex->z * matrix[2][2] +
X                       matrix[3][2]) / w; 
X
X        view_ref->u = vref->vertex->u;
X        view_ref->v = vref->vertex->v;
X        view_ref->w = vref->vertex->w;
X        view_ref->next = nhead;
X        nhead = view_ref;
X
X        if (first) {
X            ntail = view_ref;
X            first = FALSE;
X        }
X        if (!last)
X            vref = vref->next;
X    }
X
X    ntail->next = nhead;
X    create_edges(nhead, (int)ysiz << 1, polygon++, surface);
}
X
X
/*
X * Initialize the scanline z-buffer and the actual picture
X * scanline buffer.
X */
static void
init_buffers(res, z_buffer, scanline)
X    int            res;
X    double        *z_buffer;
X    unsigned char *scanline;
{
X    int i;
X    
#ifdef NOMEMCPY
X    bzero(scanline, res * 3);
#else
X    memset(scanline, 0, res * 3);
#endif
X    for (i = 0; i < res; i++)
X        z_buffer[i] = 2.0;
}
X    
X
/*
X * Read edge pairs from the edge list EDGE_LIST. Walk along the scanline
X * and interpolate z value, texture coordinates and normal vector as 
X * we go. Call the shader and write into scanline buffer according to 
X * result on z-buffer test.
X */
static void
render_line(res, z_buffer, scanline, edge_list)
X    int            res;
X    double        *z_buffer;
X    unsigned char *scanline;
X    Edge          *edge_list;
{
X    double z, zstep;
X    double nx, nxstep;
X    double ny, nystep;
X    double nz, nzstep;
X    double u, ustep;
X    double v, vstep;
X    double w, wstep;
X    double ratio;
X    Color  color;
X    int    i, j, x, xstop;
X    Edge  *edgep, *next;
X    
X    edgep = edge_list;
X    next = NULL;
X    while (edgep != NULL) {
X        if (edgep->y != edgep->y_stop) {
X            next = edgep->next;
X            while (next->y == next->y_stop)
X                next = next->next;
X            x = (int)(edgep->x + 0.5);
X            xstop = (int)(next->x + 0.5);
X            z = edgep->z;
X            nx = edgep->nx;
X            ny = edgep->ny;
X            nz = edgep->nz;
X            u = edgep->u;
X            v = edgep->v;
X            w = edgep->w;
X            if (x < xstop) {
X                ratio = (double)(xstop - x);
X                zstep = (next->z - z) / ratio;
X                nxstep = (next->nx - nx) / ratio;
X                nystep = (next->ny - ny) / ratio;
X                nzstep = (next->nz - nz) / ratio;
X                ustep = (next->u - u) / ratio;
X                vstep = (next->v - v) / ratio;
X                wstep = (next->w - w) / ratio;
X            } else {
X                zstep = 0.0;
X                nxstep = nystep = nzstep = 0.0;
X                ustep = vstep = wstep = 0.0;
X            }
X            for (i = x, j = i * 3; i <= xstop; i++) {
X                if ((i >= 0) && (i < res) && (z >= 0.0) && (z <= 1.0)
X                    && (z < z_buffer[i])) {
X                    (*edgep->surface->shader)
X                        (nx, ny, nz, u, v, w,
X                         camera.vec, lightsrc_stack, 
X                         edgep->surface->surface, &color);
X                    scanline[j++] = (unsigned char)(color.red * 255.0 + 0.5);
X                    scanline[j++] = (unsigned char)(color.grn * 255.0 + 0.5);
X                    scanline[j++] = (unsigned char)(color.blu * 255.0 + 0.5);
X                    z_buffer[i] = z;
X                } else if (i >= res) {
X                    break;
X                } else {
X                    j += 3;
X                }
X                z += zstep;
X                nx += nxstep;
X                ny += nystep;
X                nz += nzstep;
X                u += ustep;
X                v += vstep;
X                w += wstep;
X            }
X        }
X        edgep = edgep->next;
X        if ((edgep == next) && (next != NULL))
X            edgep = edgep->next;
X    }
}
X
X
X
/*
X * Insert an edge into an edge list. Edges belonging to the same
X * polygon must be inserted sorted in x, so that edge pairs are
X * created.
X */
static Edge *
insert_edge(edge_list, edge, poly_found)
X    Edge *edge_list, *edge;
X    bool  poly_found;
{
X    if (edge_list == NULL) {
X        edge_list = edge;
X	edge->next = NULL;
X    } else if (edge_list->polygon == edge->polygon) {
X        if (edge_list->x > edge->x) {
X	    edge->next = edge_list;
X	    edge_list = edge;
X	} else if ((((int)(edge_list->x + 0.5)) == ((int)(edge->x + 0.5)))
X                   && (edge_list->xstep > edge->xstep)) {
X	    edge->next = edge_list;
X	    edge_list = edge;
X        } else {
X	    edge_list->next = insert_edge(edge_list->next, edge, TRUE);
X        }
X    } else if (poly_found) {
X        edge->next = edge_list;
X        edge_list = edge;
X    } else {
X        edge_list->next = insert_edge(edge_list->next, edge, FALSE);
X    }
X
X    return edge_list;
}
X        
X
X
/*
X * Merge two edge lists.
X */
static Edge *
merge_edge_lists(list1, list2)
X    Edge *list1, *list2;
{
X    Edge *eref1, *eref2, *next;
X    
X    if (list2 == NULL)
X        return NULL;
X    eref1 = list1;
X    eref2 = list2;
X    do {
X        next = eref2->next;
X        eref1 = insert_edge(eref1, eref2, FALSE);
X	eref2 = next;
X    } while (eref2 != NULL);
X    return eref1;
}
X
X
X
/*
X * Allocate the needed buffers. Create a list of active edges and
X * move down the y-bucket, inserting and deleting edges from this
X * active list as we go. Call render_line for each scanline and
X * do an average filtering before writing the scanline to the result
X * file descriptor.
X */
static void
scan_and_render(xres, yres, image_file)
X    int  xres, yres;
X    FILE *image_file;
{
X    Edge          *active_list, *edgep, *edgep2;
X    double        *z_buffer;
X    unsigned char *scanline1, *scanline2, *stmp;
X    int            i, y, next_edge;
X    
X    z_buffer = (double *)calloc(xres, sizeof(double));
X    scanline1 = (unsigned char *)calloc(xres * 3, sizeof(unsigned char));
X    scanline2 = (unsigned char *)calloc(xres * 3, sizeof(unsigned char));
X
X    fprintf(image_file, "P6\n");
X    fprintf(image_file, "#Image rendered with SIPP %s\n", VERSION);
X    fprintf(image_file, "%d\n%d\n255\n", xres >> 1, yres >> 1);
X 
X    y = yres - 1;
X    active_list = NULL;
X    stmp = scanline1;
X 
X    while (y >= 0) {
X        active_list = merge_edge_lists(active_list, y_bucket[y].first);
X        next_edge = y - 1;
X        while (next_edge >=0 && y_bucket[next_edge].first == NULL)
X            next_edge--;
X        while (y > next_edge) {
X            init_buffers(xres, z_buffer, stmp);
X            render_line(xres, z_buffer, stmp, active_list);
X            if (stmp == scanline1)
X                stmp = scanline2;
X            else {
X                for (i = 0; i < (xres * 3); i += 6) {
X                    scanline1[i >> 1] = (scanline1[i] +
X                                         scanline1[i + 3] +
X                                         scanline2[i] +
X                                         scanline2[i + 3]) >> 2;
X                    scanline1[(i >> 1) + 1] = (scanline1[i + 1] +
X                                               scanline1[i + 4] +
X                                               scanline2[i + 1] +
X                                               scanline2[i + 4]) >> 2;
X                    scanline1[(i >> 1) + 2] = (scanline1[i + 2] +
X                                               scanline1[i + 5] +
X                                               scanline2[i + 2] +
X                                               scanline2[i + 5]) >> 2;
X                }
X                fwrite(scanline1, (xres >> 1) * 3, 1, image_file);
X                fflush(image_file);
X                stmp = scanline1;
X            }
X	    if (active_list != NULL) {
X	        edgep2 = active_list;
X	        edgep = active_list->next;
X	        while (edgep != NULL)
X	            if (edgep->y <= (edgep->y_stop + 1)) {
X                        edgep2->next = edgep->next;
X		        free(edgep);
X	                edgep = edgep2->next;
X		    } else {
X		        edgep2 = edgep;
X		        edgep = edgep->next;
X		    }
X  	        if (active_list->y <= (active_list->y_stop + 1)) {
X	            edgep = active_list;
X		    active_list = active_list->next;
X	            free(edgep);
X	        }
X	        edgep = active_list;
X	        while (edgep != NULL) {
X	            edgep->y--;
X		    edgep->x += edgep->xstep;
X		    edgep->z += edgep->zstep;
X		    edgep->nx += edgep->nxstep;
X		    edgep->ny += edgep->nystep;
X		    edgep->nz += edgep->nzstep;
X		    edgep->u += edgep->ustep;
X		    edgep->v += edgep->vstep;
X		    edgep->w += edgep->wstep;
X		    edgep = edgep->next;
X	        }
X	    }
X	    y--;
X	}
X    }
X    free(z_buffer);
X    free(scanline1);
X    free(scanline2);
}
X
X
X
/*
X * Reset the averaged normals in the vertex tree P.
X */
static void
reset_normals(vref)
X    Vertex *vref;
{
X    if (vref != NULL) {
X        vref->a = 0;
X        vref->b = 0;
X        vref->c = 0;
X        reset_normals(vref->big);
X        reset_normals(vref->sml);
X    }
}
X
X
X
/*
X * Build a transformation matrix for transformation
X * into view coordinates. Perpective transformation
X * is also included
X */
static void
get_view_transf(mat)
X    double mat[4][4];
{
X    Vector tmp;
X    double transl[3];
X    double vy, vz;
X    double hither, yon;
X    double alfa, beta;
X    int i, j;
X    
X    /*
X     * First we need a translation so the origo
X     * of the view coordinat system is placed
X     * in the viewpoint.
X     */
X    transl[0] = -camera.x0;
X    transl[1] = -camera.y0;
X    transl[2] = -camera.z0;
X
X    /*
X     * Then we need a rotation that makes the
X     * up-vector point up, and alignes the sightline
X     * with the z-axis.
X     * This code might seem magic but the algebra behind
X     * it can be found in Jim Blinn's Corner in IEEE CG&A July 1988
X     */
X    VecCopy(tmp, camera.vec);
X    VecNegate(tmp);
X    vecnorm(&tmp);
X    vecnorm(&camera.up);
X    vz = VecDot(tmp, camera.up);
X    if ((vz * vz) > 1.0) {        /* this should not happen, but... */
X        vz = 1.0;
X    } else {
X        vy = sqrt(1.0 - vz * vz);
X        if (vy == 0.0) {          /* oops, the world collapses... */
X            vy = 1.0e10;
X            vz = 1.0;
X        } else {
X            vy = 1.0 / vy;
X        }
X    }
X
X    mat[0][2] = tmp.x;
X    mat[1][2] = tmp.y;
X    mat[2][2] = tmp.z;
X
X    VecScalMul(tmp, vz, tmp);
X    VecSub(tmp, camera.up, tmp);
X    mat[0][1] = tmp.x * vy;
X    mat[1][1] = tmp.y * vy;
X    mat[2][1] = tmp.z * vy;
X
X    mat[0][0] = mat[1][1] * mat[2][2] - mat[1][2] * mat[2][1];
X    mat[1][0] = mat[2][1] * mat[0][2] - mat[2][2] * mat[0][1];
X    mat[2][0] = mat[0][1] * mat[1][2] - mat[0][2] * mat[1][1];
X
X    /*
X     * Install the translation into the matrix.
X     * Note that it is PRE-multiplied into the matrix.
X     */
X    for (i = 0; i < 3; i++) {
X        mat[3][i] = 0.0;
X        for (j = 0; j < 3; j++) {
X            mat[3][i] += transl[j] * mat[j][i];
X        }
X    }
X     
X    /*
X     * Include the perspective transformation
X     * using heuristic values for hither and yon.
X     */
X    hither = VecLen(camera.vec) / ZCLIPF;
X    yon    = VecLen(camera.vec) * ZCLIPF;
X    alfa = camera.focal_ratio / (1.0 - hither / yon);
X    beta = -hither * alfa;
X
X    mat[0][3] = mat[0][2] * camera.focal_ratio;
X    mat[0][2] *= alfa;
X    mat[1][3] = mat[1][2] * camera.focal_ratio;
X    mat[1][2] *= alfa;
X    mat[2][3] = mat[2][2] * camera.focal_ratio;
X    mat[2][2] *= alfa;
X    mat[3][3] = mat[3][2] * camera.focal_ratio;
X    mat[3][2] = mat[3][2] * alfa + beta;
X
X    /*
X     * Since the screen coordinates are defined in a left handed
X     * coordinate system, we must switch the sign of the first
X     * column in the matrix to get appropriate signs of x-values.
X     */
X    mat[0][0] = -mat[0][0];
X    mat[1][0] = -mat[1][0];
X    mat[2][0] = -mat[2][0];
X    mat[3][0] = -mat[3][0];
}
X
X
/*
X * Free the memory used by an edge list.
X */
static void
delete_edges(edge_list)
X    Edge *edge_list;
{
X    Edge *edgeref1, *edgeref2;
X
X    edgeref1 = edge_list;
X    while (edgeref1 != NULL) {
X        edgeref2 = edgeref1->next;
X        free(edgeref1);
X        edgeref1 = edgeref2;
X    }
}
X
X
X
/*
X * Push the current transformation matrix on the matrix stack.
X */
static void
matrix_push()
{
X    struct tm_stack_t *new_tm;
X
X    new_tm = (struct tm_stack_t *)smalloc(sizeof(struct tm_stack_t));
X    MatCopy(&new_tm->mat, &curr_mat);
X    new_tm->next = tm_stack;
X    tm_stack     = new_tm;
}
X
X
/*
X * Pop the top of the matrix stack and make
X * it the new current transformation matrix.
X */
static void
matrix_pop()
{
X    struct tm_stack_t *tmp;
X
X    MatCopy(&curr_mat, &tm_stack->mat);
X    tmp = tm_stack;
X    tm_stack = tm_stack->next;
X    free(tmp);
}
X
X
X
/*
X * Traverse an object hierarchy, transform each object
X * according to its transformation matrix.
X * Transform all polygons in the object to view coordinates.
X * Build the edge lists in y_bucket.
X */
static void
traverse_object_tree(object, glob_view_mat, xres, yres)
X    Object *object;
X    double  glob_view_mat[4][4];
X    int     xres, yres;
{
X    Object  *objref;
X    Surface *surfref;
X    Polygon *polyref;
X    Vector   eyepoint, tmp;
X    double   loc_view_mat[4][4];
X
X
X    if (object == NULL) {
X        return;
X    }
X
X    for (objref = object; objref != NULL; objref = objref->next) {
X
X        matrix_push();
X        mat_mul(&curr_mat, &objref->transf, &curr_mat);
X        mat_mul34(loc_view_mat, &curr_mat, glob_view_mat);
X
X        tmp.x = camera.x0;
X        tmp.y = camera.y0;
X        tmp.z = camera.z0;
X
X        /*
X         * Do an inverse transformation of the viewpoint to use
X         * when doing backface culling (in calc_normals()).
X         */
X        tmp.x -= curr_mat.mat[3][0];
X        tmp.y -= curr_mat.mat[3][1];
X        tmp.z -= curr_mat.mat[3][2];
X        eyepoint.x = (tmp.x * curr_mat.mat[0][0] + tmp.y * curr_mat.mat[0][1]
X                      + tmp.z * curr_mat.mat[0][2]);
X        eyepoint.y = (tmp.x * curr_mat.mat[1][0] + tmp.y * curr_mat.mat[1][1]
X                      + tmp.z * curr_mat.mat[1][2]);
X        eyepoint.z = (tmp.x * curr_mat.mat[2][0] + tmp.y * curr_mat.mat[2][1]
X                      + tmp.z * curr_mat.mat[2][2]);
X
X        for (surfref = objref->surfaces; surfref != NULL; 
X             surfref = surfref->next) {
X
X            calc_normals(surfref->polygons, eyepoint);
X
X            for (polyref = surfref->polygons; polyref != NULL; 
X                 polyref = polyref->next) {
X
X                if (!polyref->backface) {
X                    transf_vertices(polyref->vertices, surfref, loc_view_mat, 
X                                    &curr_mat, (double)xres, (double)yres);
X                }
X
X            }
X            reset_normals(surfref->vertices);
X
X        }
X        traverse_object_tree(objref->sub_obj, glob_view_mat, xres, yres);
X        matrix_pop();
X    }
}
X
X
X
/*
X * Recursively traverse the rendering database (preorder).
X * Call traverse_object_tree for each object.
X */
static void
traverse_object_db(obj_root, view_mat, xres, yres)
X    Inst_object *obj_root;
X    double       view_mat[4][4];
X    int          xres, yres;
{
X    if (obj_root != NULL) {
X        traverse_object_tree(obj_root->object, view_mat, xres, yres);
X        traverse_object_db(obj_root->sml, view_mat, xres, yres);
X        traverse_object_db(obj_root->big, view_mat, xres, yres);
X    }
}
X
X
X
X
/*
X * "Main" function in rendering. Allocate y-bucket, transform vertices
X * into viewing coordinates, make edges and sort them into the y-bucket.
X * Call scan_and_render to do the real work.
X */
void
render_image(xres, yres, image_file)
X    int   xres, yres;
X    FILE *image_file;
{
X    double      matrix[4][4];
X    int         i;
X
X    y_bucket = (Bucket *)calloc(yres << 1, sizeof(Bucket));
X
X    get_view_transf(matrix);
X    MatCopy(&curr_mat, &ident_matrix);
X    
X    traverse_object_db(object_db, matrix, xres, yres);
X
X    vecnorm(&camera.vec);
X    scan_and_render(xres << 1, yres << 1, image_file);
X    view_vec_eval();
X
X    for (i = 0; i < (yres << 1); i++)
X        delete_edges(y_bucket[i].first);
X    free(y_bucket);
}
X
X
X
/*============= Functions that handles global initializations==============*/
X
/*
X * Necessary initializations.
X */
void
sipp_init()
{
X    vertex_tree    = NULL;
X    vertex_stack   = NULL;
X    poly_stack     = NULL;
X    object_db      = NULL;
X    lightsrc_stack = NULL;
X    first_vertex   = 0;
X    viewpoint(0.0, 0.0, 10.0,  0.0, 0.0, 0.0,  0.0, 1.0, 0.0,  0.25);
}
SHAR_EOF
chmod 0644 libsipp/sipp.c ||
echo 'restore of libsipp/sipp.c failed'
Wc_c="`wc -c < 'libsipp/sipp.c'`"
test 52154 -eq "$Wc_c" ||
	echo 'libsipp/sipp.c: original size 52154, current size' "$Wc_c"
fi
true || echo 'restore of libsipp/sipp.h failed'
echo End of part 3, continue with part 4
exit 0

-- 
Inge Wallin               | Thus spake the master programmer:               |
                          |      "After three days without programming,     |
ingwa@isy.liu.se          |       life becomes meaningless."                |
                          | Geoffrey James: The Tao of Programming.         |


exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.