From pa.dec.com!decwrl!uunet!sparky!kent Thu Jul 25 08:57:30 PDT 1991
Article: 2529 of comp.sources.misc
Newsgroups: comp.sources.misc
Path: pa.dec.com!decwrl!uunet!sparky!kent
From: Jonas Yngvesson <jonas-y@isy.liu.se>
Subject:  v21i029:  sipp - A 3D rendering library v2.1, Part04/08
Message-ID: <1991Jul23.181700.27805@sparky.IMD.Sterling.COM>
X-Md4-Signature: b09f7aedec58793386724d8ce2f19af1
Keywords: rendering graphics z-buffer
Sender: kent@sparky.IMD.Sterling.COM (Kent Landfield)
Organization: Dept of EE, University of Linkoping
References: <csm-v21i026=sipp.130352@sparky.imd.sterling.com>
Date: Tue, 23 Jul 1991 18:17:00 GMT
Approved: kent@sparky.imd.sterling.com
Lines: 1672

Submitted-by: Jonas Yngvesson <jonas-y@isy.liu.se>
Posting-number: Volume 21, Issue 29
Archive-name: sipp/part04
Supersedes: sipp2.0: Volume 16, Issue 5-10
Environment: UNIX

#!/bin/sh
# This is part 04 of sipp-2.1
# ============= libsipp/rendering.c ==============
if test ! -d 'libsipp'; then
    echo 'x - creating directory libsipp'
    mkdir 'libsipp'
fi
if test -f 'libsipp/rendering.c' -a X"$1" != X"-c"; then
	echo 'x - skipping libsipp/rendering.c (File already exists)'
else
echo 'x - extracting libsipp/rendering.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'libsipp/rendering.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/91
X **            Inge Wallin      (ingwa@isy.liu.se)         1990/91
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 **/
X
/**
X ** rendering.c - Functions that handles rendering of the scene.
X **/
X
#include <stdio.h>
#ifndef NOMEMCPY
#include <memory.h>
#endif
X
#include <xalloca.h>
#include <smalloc.h>
X
#include <lightsource.h>
#include <geometric.h>
#include <rendering.h>
#include <objects.h>
#include <sipp.h>
#include <sipp_bitmap.h>
#include <viewpoint.h>
#include <patchlevel.h>
X
char *SIPP_VERSION = "2.1";
X
/*
X * Static global variables.
X */
static bool          show_backfaces;  /* Don't do backface culling */
static Edge        **y_bucket;        /* Y-bucket for edge lists. */
static FILE         *image_file;      /* File to store image in      */
X                                      /* when rendering into a file. */
static void         *image_pm;        /* Pixmap to store image in when */
X                                      /* rendering into a pix/bitmap.  */
static void        (*pixmap_set)();   /* Pointer to function for setting    */
X                                      /* a pixel or draw a line in image_pm */
X
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
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        /*
X         * Take care of backfacing polygons.
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            if (show_backfaces) {
X                polyref->backface = FALSE;
X                VecNegate(normal);
X            } else {
X                polyref->backface = TRUE;
X            }
X        } else {
X            polyref->backface = FALSE;
X        }
X            
X        /*
X         * Add the calculated normal to all vertices
X         * in the poygon. This will result in an avaraged normal
X         * at each vertex after all polygons have been pprocessed.
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 */
static void
create_edges(view_vert, polygon, surface, render_mode)
X    View_coord *view_vert;
X    int         polygon;
X    Surface    *surface;
X    int         render_mode;
{
X    Edge       *edge;
X    View_coord *view_ref, *last;
X    int         nderiv, y1, y2;
X    double      deltay;
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
X    view_ref = last = view_vert;
X
X    do {
X        view_ref = view_ref->next;
X
X        /*
X         * If we are drawing a line image we dont need
X         * to build a complete edgelist. We draw the
X         * lines directly instead.
X         *
X         * Since many lines are drawn twice (edges shared between
X         * two polygons) and many line drawing algorithms are unsymmetrical
X         * we need to make sure lines are always drawn in the same
X         * direction
X         */
X        if (render_mode == LINE) {
X            if (view_ref->y < view_ref->next->y) {
X                (*pixmap_set)(image_pm, 
X                              (int)(view_ref->x + 0.5), 
X                              (int)(view_ref->y + 0.5),
X                              (int)(view_ref->next->x + 0.5), 
X                              (int)(view_ref->next->y + 0.5));
X            } else {
X                (*pixmap_set)(image_pm, 
X                              (int)(view_ref->next->x + 0.5), 
X                              (int)(view_ref->next->y + 0.5), 
X                              (int)(view_ref->x + 0.5), 
X                              (int)(view_ref->y + 0.5));
X            }
X            continue;
X        }
X
X        /*
X         * Check if the slope of the edge is positive or negative
X         * or zero.
X         */
X        y1 = (int)(view_ref->y + 0.5);
X        y2 = (int)(view_ref->next->y + 0.5);
X        deltay = (double)(y2 - y1);
X
X        if (deltay > 0.0)
X            nderiv = 1;
X        else if (deltay < 0.0)
X            nderiv = -1;
X        else
X            nderiv = 0;
X
X        /*
X         * Check if the edge is horizontal. In that case we
X         * just skip it.
X         */
X        if (nderiv != 0) {
X
X            edge = (Edge *)smalloc(sizeof(Edge));
X
X            x1 = view_ref->x;
X            x2 = view_ref->next->x;
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
X            deltay = fabs(deltay);
X            xstep = (x2 - x1) / deltay;
X            zstep = (z2 - z1) / deltay;
X            if (render_mode != FLAT) {
X                nxstep = (nx2 - nx1) / deltay;
X                nystep = (ny2 - ny1) / deltay;
X                nzstep = (nz2 - nz1) / deltay;
X                if (render_mode == PHONG) {
X                    ustep = (u2 - u1) / deltay;
X                    vstep = (v2 - v1) / deltay;
X                    wstep = (w2 - w1) / deltay;
X                }
X            }
X
X            if (nderiv > 0) {       
X
X                /*
X                 * The edge has positive slope
X                 */
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                if (render_mode != FLAT) {
X                    edge->nxstep = -nxstep;
X                    edge->nystep = -nystep;
X                    edge->nzstep = -nzstep;
X                    if (render_mode == PHONG) {
X                        edge->ustep = -ustep;
X                        edge->vstep = -vstep;
X                        edge->wstep = -wstep;
X                    }
X                }
X
X            } else {
X
X                /*
X                 * The edge has negative slope.
X                 */
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                if (render_mode != FLAT) {
X                    edge->nxstep = nxstep;
X                    edge->nystep = nystep;
X                    edge->nzstep = nzstep;
X                    if (render_mode == PHONG) {
X                        edge->ustep = ustep;
X                        edge->vstep = vstep;
X                        edge->wstep = wstep;
X                    }
X                }
X            }
X            edge->polygon = polygon;
X            edge->surface = surface;
X            edge->next = y_bucket[edge->y];
X            y_bucket[edge->y] = edge;
X        }
X    } while (view_ref != last);
}
X
X
X
/*
X * Calculate a new vertex by interpolation between
X * V1 and V2.
X */
static View_coord *
interpolate(v1, v2, ratio)
X    View_coord *v1, *v2;
X    double      ratio;
{
X    View_coord *tmp;
X
X    tmp = (View_coord *)smalloc(sizeof(View_coord));
X
X    tmp->x = v1->x + ratio * (v2->x - v1->x);
X    tmp->y = v1->y + ratio * (v2->y - v1->y);
X    tmp->z = v1->z + ratio * (v2->z - v1->z);
X    tmp->nx = v1->nx + ratio * (v2->nx - v1->nx);
X    tmp->ny = v1->ny + ratio * (v2->ny - v1->ny);
X    tmp->nz = v1->nz + ratio * (v2->nz - v1->nz);
X    tmp->u = v1->u + ratio * (v2->u - v1->u);
X    tmp->v = v1->v + ratio * (v2->v - v1->v);
X    tmp->w = v1->w + ratio * (v2->w - v1->w);
X    tmp->next = NULL;
X
X    return tmp;
}
X
X
X
/*
X * Clip a polygon using the Sutherland-Hodgeman algorithm for
X * reentrant clipping;
X */
#define XMIN 0
#define XMAX 1
#define YMIN 2
#define YMAX 3
#define ZMIN 4
#define ZMAX 5
X
static View_coord *
polygon_clip(vlist, plane, first_vert)
X    View_coord *vlist;
X    int         plane;
X    bool        first_vert;
{
X    static View_coord   *first;
X    static View_coord   *curr;
X    View_coord          *out1;
X    View_coord          *out2;
X    double               curr_limit;
X    double               first_limit;
X    double               vlist_limit;
X    double               ratio;
X    bool                 visible;
X
X    out1 = out2 = NULL;
X
X    if (vlist == NULL) {
X
X        /*
X         * Did we get an empty list from the start?
X         */
X        if (first_vert) {
X            return NULL;
X        }
X
X        /*
X         * Last vertex, close the polygon.
X         */
X        ratio = 0.0;
X        curr_limit = curr->z * camera.focal_ratio;
X        first_limit = first->z * camera.focal_ratio;
X
X        switch (plane) {
X
X          case XMIN:
X            if ((curr->x < -curr_limit && first->x >= -first_limit)
X                || (curr->x >= -curr_limit && first->x < -first_limit)) {
X                ratio = fabs(curr->x + curr_limit);
X                ratio /= (ratio + fabs(first->x + first_limit)); 
X            }
X            break;
X
X          case XMAX:
X            if ((curr->x <= curr_limit && first->x > first_limit)
X                || (curr->x > curr_limit && first->x <= first_limit)) {
X                ratio = fabs(curr->x - curr_limit);
X                ratio /= (ratio + fabs(first->x - first_limit));
X            }
X            break;
X
X          case YMIN:
X            if ((curr->y < -curr_limit && first->y >= -first_limit)
X                || (curr->y >= -curr_limit && first->y < -first_limit)) {
X                ratio = fabs(curr->y + curr_limit);
X                ratio /= (ratio + fabs(first->y + first_limit));
X            }
X            break;
X
X          case YMAX:
X            if ((curr->y <= curr_limit && first->y > first_limit)
X                || (curr->y > curr_limit && first->y <= first_limit)) {
X                ratio = fabs(curr->y - curr_limit);
X                ratio /= (ratio + fabs(first->y - first_limit));
X            }
X            break;
X
X          case ZMIN:
X            if ((curr->z < hither && first->z >= hither)
X                || (curr->z >= hither && first->z < hither)) {
X                ratio = fabs(curr->z - hither);
X                ratio = ratio / (ratio + fabs(first->z - hither));
X            }
X            break;
X
X          case ZMAX:
X            if ((curr->z <= yon && first->z > yon)
X                || (curr->z > yon && first->z <= yon)) {
X                ratio = fabs(curr->z - yon);
X                ratio = ratio / (ratio + fabs(first->z - yon));
X            }
X            break;
X        }
X
X        if (ratio != 0.0) {
X            out1 = interpolate(curr, first, ratio);
X            return out1;
X        } else {
X            return NULL;
X        }
X    }
X
X    vlist_limit = vlist->z * camera.focal_ratio;
X    
X    if (first_vert) {
X        first = vlist;
X    } else {
X        ratio = 0.0;
X        curr_limit = curr->z * camera.focal_ratio;
X
X        switch (plane) {
X
X          case XMIN:
X            if ((curr->x < -curr_limit && vlist->x >= -vlist_limit)
X                || (curr->x >= -curr_limit && vlist->x < -vlist_limit)) {
X                ratio = fabs(curr->x + curr_limit);
X                ratio /= (ratio + fabs(vlist->x + vlist_limit));
X            }
X            break;
X
X          case XMAX:
X            if ((curr->x <= curr_limit && vlist->x > vlist_limit)
X                || (curr->x > curr_limit && vlist->x <= vlist_limit)) {
X                ratio = fabs(curr->x - curr_limit);
X                ratio /= (ratio + fabs(vlist->x - vlist_limit));
X            }
X            break;
X
X          case YMIN:
X            if ((curr->y < -curr_limit && vlist->y >= -vlist_limit)
X                || (curr->y >= -curr_limit && vlist->y < -vlist_limit)) {
X                ratio = fabs(curr->y + curr_limit);
X                ratio /= (ratio + fabs(vlist->y + vlist_limit));
X            }
X            break;
X
X          case YMAX:
X            if ((curr->y <= curr_limit && vlist->y > vlist_limit)
X                || (curr->y > curr_limit && vlist->y <= vlist_limit)) {
X                ratio = fabs(curr->y - curr_limit);
X                ratio /= (ratio + fabs(vlist->y - vlist_limit));
X            }
X            break;
X
X          case ZMIN:
X            if ((curr->z < hither && vlist->z >= hither)
X                || (curr->z >= hither && vlist->z < hither)) {
X                ratio = fabs(curr->z - hither);
X                ratio = ratio / (ratio + fabs(vlist->z - hither));
X            }
X            break;
X
X          case ZMAX:
X            if ((curr->z <= yon && vlist->z > yon)
X                || (curr->z > yon && vlist->z <= yon)) {
X                ratio = fabs(curr->z - yon);
X                ratio = ratio / (ratio + fabs(vlist->z - yon));
X            }
X            break;
X        }
X
X        if (ratio != 0.0) {
X            out1 = interpolate(curr, vlist, ratio);
X            out1->next = vlist;
X        }
X    }
X
X    curr = vlist;
X    visible = FALSE;
X    switch (plane) {
X
X      case XMIN:
X        visible = (curr->x >= -vlist_limit);
X        break;
X
X      case XMAX:
X        visible = (curr->x <= vlist_limit);
X        break;
X
X      case YMIN:
X        visible = (curr->y >= -vlist_limit);
X        break;
X
X      case YMAX:
X        visible = (curr->y <= vlist_limit);
X        break;
X
X      case ZMIN:
X        visible = (curr->z >= hither);
X        break;
X
X      case ZMAX:
X        visible = (curr->z <= yon);
X        break;
X    }
X
X    if (visible) {
X        out2 = curr;
X        out2->next = polygon_clip(curr->next, plane, FALSE);
X        return ((out1) ? (out1) : (out2));
X
X    } else {
X        if (out1) {
X            out1->next = polygon_clip(curr->next, plane, FALSE);
X        } else {
X            out1 = polygon_clip(curr->next, plane, FALSE);
X        }
X        free(vlist);
X        return out1;
X    }
}
X
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, view_mat, tr_mat, 
X                xsiz, ysiz, render_mode)
X    Vertex_ref *vertex_list;
X    Surface    *surface;
X    Transf_mat *view_mat;
X    Transf_mat *tr_mat;
X    double      xsiz, ysiz;
X    int         render_mode;
{
X    static int  polygon = 0;        /* incremented for each call to provide */
X                                    /* unique polygon id numbers */
X    Vertex_ref *vref;
X    View_coord *nhead;
X    View_coord *view_ref;
X    View_coord *mark;
X    Color       color;                    
X    double      minsize;
X    double      tmp;
X
X    vref = vertex_list;
X    nhead = NULL;
X
X    minsize = ((xsiz > ysiz) ? ysiz : xsiz);
X
X    while (vref != NULL) {
X
X        view_ref = (View_coord *)smalloc(sizeof(View_coord));
X
X
X        /*
X         * Transform the normal (world coordinates).
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        /*
X         * Transform the vertex into view coordinates.
X         */
X        view_ref->x = (vref->vertex->x * view_mat->mat[0][0] 
X                       + vref->vertex->y * view_mat->mat[1][0] 
X                       + vref->vertex->z * view_mat->mat[2][0]
X                       + view_mat->mat[3][0]);
X        view_ref->y = (vref->vertex->x * view_mat->mat[0][1] 
X                       + vref->vertex->y * view_mat->mat[1][1] 
X                       + vref->vertex->z * view_mat->mat[2][1]
X                       + view_mat->mat[3][1]);
X        view_ref->z = (vref->vertex->x * view_mat->mat[0][2] 
X                       + vref->vertex->y * view_mat->mat[1][2] 
X                       + vref->vertex->z * view_mat->mat[2][2]
X                       + view_mat->mat[3][2]);
X
X        /*
X         * Texture coordinates is not affected by transformations.
X         */
X        view_ref->u = vref->vertex->u;
X        view_ref->v = vref->vertex->v;
X        view_ref->w = vref->vertex->w;
X
X        view_ref->next = nhead;
X        nhead = view_ref;
X
X        vref = vref->next;
X    }
X
X    /* 
X     * Clip the resulting polygon. We need to do this
X     * before the perpective transformation to keep texture
X     * coordinates correct.
X     */
X    nhead = polygon_clip(nhead, ZMIN, TRUE);
X    nhead = polygon_clip(nhead, ZMAX, TRUE);
X    if (xsiz > ysiz) {
X        tmp = camera.focal_ratio;
X        camera.focal_ratio *= xsiz / ysiz;
X        nhead = polygon_clip(nhead, XMIN, TRUE);
X        nhead = polygon_clip(nhead, XMAX, TRUE);
X        camera.focal_ratio = tmp;
X        nhead = polygon_clip(nhead, YMIN, TRUE);
X        nhead = polygon_clip(nhead, YMAX, TRUE);
X    } else {
X        tmp = camera.focal_ratio;
X        camera.focal_ratio *= ysiz / xsiz;
X        nhead = polygon_clip(nhead, YMIN, TRUE);
X        nhead = polygon_clip(nhead, YMAX, TRUE);
X        camera.focal_ratio = tmp;
X        nhead = polygon_clip(nhead, XMIN, TRUE);
X        nhead = polygon_clip(nhead, XMAX, TRUE);
X    }
X
X    if (nhead == NULL) {    /* Nothing left? */
X        return;
X    }
X
X
X    
X    /*
X     * If we are flat shading, we need a color for the polygon.
X     * We call the shader at the first vertex to get this.
X     * (This is not quite correct since the normal here is
X     * an averaged normal of the surrounding polygons)
X     */
X    if (render_mode == FLAT) {
X        (*surface->shader)
X            (nhead->nx, nhead->ny, nhead->nz, 
X             nhead->u, nhead->v, nhead->w,
X             camera.vec, lightsrc_stack, 
X             surface->surface, &color);
X    }
X
X
X    /*
X     * Walk around the new (clipped and transformed) polygon and 
X     * transform it into perspective screen coordinates.
X     * We have to transform the texture coordinates too in order
X     * to interpolate them linearly in "screen space". This
X     * transformation is inverted in render_line().
X     * If we are doing gouraud shading we call the shader at each
X     * vertex.
X     * Last we tie the head and tail together forming a cirkular
X     * list, this simplifies edge creation.
X     */
X    for (view_ref = nhead;; view_ref = view_ref->next) {
X        view_ref->z *= camera.focal_ratio;
X        view_ref->x  = view_ref->x * minsize / view_ref->z + xsiz;
X        view_ref->y  = view_ref->y * minsize / view_ref->z + ysiz;
X        view_ref->u /= view_ref->z;
X        view_ref->v /= view_ref->z;
X        view_ref->w /= view_ref->z;
X
X        if (render_mode == GOURAUD) {
X            (*surface->shader)
X                (view_ref->nx, view_ref->ny, view_ref->nz, 
X                 view_ref->u, view_ref->v, view_ref->w,
X                 camera.vec, lightsrc_stack, 
X                 surface->surface, &color);
X        } 
X
X        if (render_mode == GOURAUD || render_mode == FLAT) {
X            view_ref->nx = color.red;
X            view_ref->ny = color.grn;
X            view_ref->nz = color.blu;
X        } 
X
X        if (view_ref->next == NULL) {
X            view_ref->next = nhead;
X            break;
X        }
X    }
X
X    create_edges(nhead, polygon++, surface, render_mode);
X
X    /*
X     * Free the memory used by the transformed polygon.
X     */
X    mark = nhead;
X    do {
X        view_ref = nhead;
X        nhead = nhead->next;
X        free(view_ref);
X    } while (nhead != mark);
}
X
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] = 1.0e100;
}
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, render_mode)
X    int            res;
X    double        *z_buffer;
X    unsigned char *scanline;
X    Edge          *edge_list;
X    int            render_mode;
{
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 ur, vr, wr;
X    double ratio;
X    Color  color;
X    int    i, j, x, xstop;
X    Edge  *startedge, *stopedge;
X    
X    startedge = edge_list;
X    stopedge = NULL;
X    while (startedge != NULL) {
X        stopedge = startedge->next;
X        x = (int)(startedge->x + 0.5);
X        xstop = (int)(stopedge->x + 0.5);
X        z = startedge->z;
X        nx = startedge->nx;
X        ny = startedge->ny;
X        nz = startedge->nz;
X        u = startedge->u;
X        v = startedge->v;
X        w = startedge->w;
X        if (x < xstop) {
X            ratio = (double)(xstop - x);
X            zstep = (stopedge->z - z) / ratio;
X            if (render_mode != FLAT) {
X                nxstep = (stopedge->nx - nx) / ratio;
X                nystep = (stopedge->ny - ny) / ratio;
X                nzstep = (stopedge->nz - nz) / ratio;
X                if (render_mode == PHONG) {
X                    ustep = (stopedge->u - u) / ratio;
X                    vstep = (stopedge->v - v) / ratio;
X                    wstep = (stopedge->w - w) / ratio;
X                }
X            }
X        } else {
X            zstep = 0.0;
X            nxstep = nystep = nzstep = 0.0;
X            ustep = vstep = wstep = 0.0;
X        }
X
X        for (i = x, j = i * 3; i <= xstop; i++) {
X
X            if (z < z_buffer[i]) {
X
X                if (render_mode == PHONG) {
X                    ur = u * z;  /* Transform texture coordinates back */
X                    vr = v * z;  /* from their homogenouos form */
X                    wr = w * z;
X                    (*startedge->surface->shader)
X                        (nx, ny, nz, ur, vr, wr,
X                         camera.vec, lightsrc_stack, 
X                         startedge->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                    
X                } else if (render_mode == FLAT || render_mode == GOURAUD) {
X                    scanline[j++] = (unsigned char)(nx * 255.0 + 0.5);
X                    scanline[j++] = (unsigned char)(ny * 255.0 + 0.5);
X                    scanline[j++] = (unsigned char)(nz * 255.0 + 0.5);
X                }
X                
X                z_buffer[i] = z;
X                
X            } else if (i >= res) {
X                break;
X                
X            } else {
X                j += 3;
X            }
X            
X            z += zstep;
X            if (render_mode != FLAT) {
X                nx += nxstep;
X                ny += nystep;
X                nz += nzstep;
X                if (render_mode == PHONG) {
X                    u += ustep;
X                    v += vstep;
X                    w += wstep;
X                }
X            }
X        }
X        startedge = stopedge->next;
X    }
}
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 list1;
X
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
X    return eref1;
}
X
X
X
/*
X * Store a rendered line on the place indicated by STORAGE_MODE.
X */
static void
store_line(buf, npixels, line, storage_mode)
X    unsigned char *buf;
X    int            npixels;
X    int            line;
X    int            storage_mode;
{
X    int i, j;
X
X    switch (storage_mode) {
X      case PBM_FILE:
X        fwrite(buf, sizeof(unsigned char), npixels, image_file);
X        fflush(image_file);
X        break;
X        
X      case PPM_FILE:
X        fwrite(buf, sizeof(unsigned char), npixels * 3, image_file);
X        fflush(image_file);
X        break;
X
X      case PIXMAP:
X        for (i = 0, j = 0; j < npixels; j++, i += 3) {
X            (*pixmap_set)(image_pm, j, line, buf[i], buf[i + 1], buf[i + 2]);
X        }
X        break;
X
X      default:
X        break;
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 storing the scanline.
X */
static void
scan_and_render(xres, yres, storage_mode, render_mode, oversampl)
X    int   xres, yres;
X    int   storage_mode;
X    int   render_mode;
X    int   oversampl;
{
X    Edge           *active_list, *edgep, *edgep2;
X    double         *z_buffer;
X    unsigned char **linebuf;
X    int             curr_line;
X    int             y, next_edge;
X    int             sum;
X    int             i, j, k, l;
X    
X    z_buffer = (double *)scalloc(xres, sizeof(double));
X    linebuf  = (unsigned char **)alloca(oversampl * sizeof(unsigned char *));
X    for (i = 0; i < oversampl; i++) {
X        linebuf[i] = (unsigned char *)scalloc(xres * 3, sizeof(unsigned char));
X    }
X
X    if (storage_mode == PPM_FILE) {
X        fprintf(image_file, "P6\n");
X        fprintf(image_file, "#Image rendered with SIPP %s%s\n", 
X                SIPP_VERSION, PATCHLEVEL);
X        fprintf(image_file, "%d\n%d\n255\n", xres / oversampl, 
X                                             yres / oversampl);
X    }
X
X    y = yres - 1;
X    active_list = NULL;
X    curr_line = 0;
X 
X    while (y >= 0) {
X
X        active_list = merge_edge_lists(active_list, y_bucket[y]);
X        next_edge = y - 1;
X
X        while (next_edge >=0 && y_bucket[next_edge] == NULL)
X            next_edge--;
X
X        while (y > next_edge) {
X
X            init_buffers(xres, z_buffer, linebuf[curr_line]);
X            render_line(xres, z_buffer, linebuf[curr_line], active_list,
X                        render_mode); 
X
X            if (++curr_line == oversampl) {
X
X                /*
X                 * Average the pixel.
X                 */
X                for (i = 0; i < ((xres / oversampl)); i++) {
X                    for (l = 0; l < 3; l++) {
X                        sum = 0;
X                        for (j = i * 3 * oversampl + l; 
X                             j < (i * 3 * oversampl + l + 3 * oversampl); 
X                             j += 3) {
X                            for (k = 0; k < oversampl; k++) {
X                                sum += *(linebuf[k] + j);
X                            }
X                        }
X                        *(linebuf[0] + i * 3 + l)
X                            = sum / (oversampl * oversampl);
X                    }
X                }
X                store_line(linebuf[0], xres / oversampl, 
X                           (yres - y - 1) / oversampl, storage_mode);
X                curr_line = 0;
X            }
X
X	    if (active_list != NULL) {
X
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                }
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
X	        edgep = active_list;
X	        while (edgep != NULL) {
X	            edgep->y--;
X		    edgep->x += edgep->xstep;
X		    edgep->z += edgep->zstep;
X                    if (render_mode != FLAT) {
X                        edgep->nx += edgep->nxstep;
X                        edgep->ny += edgep->nystep;
X                        edgep->nz += edgep->nzstep;
X                        if (render_mode == PHONG) {
X                            edgep->u += edgep->ustep;
X                            edgep->v += edgep->vstep;
X                            edgep->w += edgep->wstep;
X                        }
X                    }
X		    edgep = edgep->next;
X	        }
X	    }
X	    y--;
X	}
X    }
X    free(z_buffer);
X    for (i = 0; i < oversampl; i++) {
X        free(linebuf[i]);
X    }
}
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 * 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, view_mat, xres, yres, render_mode)
X    Object      *object;
X    Transf_mat  *view_mat;
X    int          xres, yres;
X    int          render_mode;
{
X    Object      *objref;
X    Surface     *surfref;
X    Polygon     *polyref;
X    Vector       eyepoint, tmp;
X    Transf_mat   loc_view_mat;
X    double       m[3][4], dtmp;
X    int          i, j;
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_mul(&loc_view_mat, &curr_mat, 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        m[0][0] = curr_mat.mat[0][0] ; m[0][1] = curr_mat.mat[1][0];
X        m[0][2] = curr_mat.mat[2][0] ; m[0][3] = tmp.x;
X        m[1][0] = curr_mat.mat[0][1] ; m[1][1] = curr_mat.mat[1][1];
X        m[1][2] = curr_mat.mat[2][1] ; m[1][3] = tmp.y;
X        m[2][0] = curr_mat.mat[0][2] ; m[2][1] = curr_mat.mat[1][2];
X        m[2][2] = curr_mat.mat[2][2] ; m[2][3] = tmp.z;
X
X        if (m[0][0] == 0.0) {
X            if (m[1][0] != 0.0)
X                j = 1;
X            else
X                j = 2;
X            for (i = 0; i < 4; i++) {
X                dtmp     = m[0][i];
X                m[0][i] = m[j][i];
X                m[j][i] = dtmp;
X            }
X        }
X        
X        for (j = 1; j < 3; j++) {
X            m[j][0] /= (-m[0][0]);
X            for (i = 1; i < 4; i++)
X                m[j][i] += m[0][i] * m[j][0];
X        }
X        
X        if (m[1][1] == 0.0)
X            for (i = 1; i < 4; i++) {
X                dtmp     = m[1][i];
X                m[1][i] = m[2][i];
X                m[2][i] = dtmp;
X            }
X        
X        if (m[1][1] != 0.0) {
X            m[2][1] /= (-m[1][1]);
X            m[2][2] += m[1][2] * m[2][1];
X            m[2][3] += m[1][3] * m[2][1];
X        }
X        
X        eyepoint.z = m[2][3] / m[2][2];
X        eyepoint.y = (m[1][3] - eyepoint.z * m[1][2]) / m[1][1];
X        eyepoint.x = (m[0][3] - eyepoint.z * m[0][2] 
X                      - eyepoint.y * m[0][1]) / m[0][0];
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, 
X                                    &loc_view_mat, 
X                                    &curr_mat, (double)xres / 2.0,
X                                    (double)yres / 2.0, render_mode);
X                }
X
X            }
X            reset_normals(surfref->vertices);
X
X        }
X        traverse_object_tree(objref->sub_obj, view_mat, xres, yres,
X                             render_mode); 
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, render_mode)
X    Inst_object  *obj_root;
X    Transf_mat   *view_mat;
X    int           xres, yres;
X    int           render_mode;
{
X    if (obj_root != NULL) {
X        traverse_object_tree(obj_root->object, view_mat,
X                             xres, yres, render_mode); 
X        traverse_object_db(obj_root->sml, view_mat,
X                           xres, yres, render_mode);
X        traverse_object_db(obj_root->big, view_mat,
X                           xres, yres, render_mode);
X    }
}
X
X
X
X
/*
X * "Main" functions 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 */
static void
render_main(xres, yres, storage_mode, render_mode, oversampling)
X    int   xres, yres;
X    int   storage_mode;
X    int   render_mode;
X    int   oversampling;
{
X    Transf_mat      view_mat;
X
X    if (render_mode != LINE) {
X        xres *= oversampling;
X        yres *= oversampling;
X        y_bucket = (Edge **)scalloc(yres, sizeof(Edge *));
X
X    } else if (storage_mode == PBM_FILE) {
X        image_pm = sipp_bitmap_create(xres, yres);
X        pixmap_set = sipp_bitmap_line;
X    }
X
X    get_view_transf(&view_mat);
X    MatCopy(&curr_mat, &ident_matrix);
X    
X    vecnorm(&camera.vec);
X
X    traverse_object_db(object_db, &view_mat,
X                       xres - 1, yres - 1, render_mode);
X
X    if (render_mode != LINE) {
X        scan_and_render(xres, yres, storage_mode, render_mode, oversampling);
X        free(y_bucket);
X
X    } else if (storage_mode == PBM_FILE) {
X        sipp_bitmap_write(image_file, image_pm);
X        sipp_bitmap_destruct(image_pm);
X    }
X
X    view_vec_eval();
X
}
X    
X
void
render_image_file(xres, yres, im_file, render_mode, oversampling)
X    int   xres, yres;
X    FILE *im_file;
X    int   render_mode;
X    int   oversampling;
{
X    image_file = im_file;
X
X    if (render_mode == LINE) {
X        render_main(xres, yres, PBM_FILE, render_mode, oversampling);
X    } else {
X        render_main(xres, yres, PPM_FILE, render_mode, oversampling);
X    }
}
X
X
void
render_image_pixmap(xres, yres, pixmap, pixmap_func, render_mode, oversampling)
X    int     xres, yres;
X    void   *pixmap;
X    void  (*pixmap_func)();
X    int     render_mode;
X    int     oversampling;
{
X    image_pm = pixmap;
X    pixmap_set = pixmap_func;
X    render_main(xres, yres, PIXMAP, render_mode, oversampling);
}
X
X
X
/*============= Functions that handles global initializations==============*/
X
/*
X * If called with TRUE as argument,  no backface culling will 
X * be performed. If a polygon is backfacing it will be rendered
X * as facing in the opposit direction.
X */
void
sipp_show_backfaces(flag)
X    bool flag;
{
X    show_backfaces = flag;
}
X
X
X
/*
X * Necessary initializations.
X */
void
sipp_init()
{
X    objects_init();
X    lightsource_init();
X    sipp_show_backfaces(FALSE);
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/rendering.c ||
echo 'restore of libsipp/rendering.c failed'
Wc_c="`wc -c < 'libsipp/rendering.c'`"
test 39990 -eq "$Wc_c" ||
	echo 'libsipp/rendering.c: original size 39990, current size' "$Wc_c"
fi
# ============= libsipp/rendering.h ==============
if test -f 'libsipp/rendering.h' -a X"$1" != X"-c"; then
	echo 'x - skipping libsipp/rendering.h (File already exists)'
else
echo 'x - extracting libsipp/rendering.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'libsipp/rendering.h' &&
/**
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/91
X **            Inge Wallin      (ingwa@isy.liu.se)         1990/91
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 **/
X
/**
X ** rendering.h - Types and interface to the rendering.c
X **/
X
#ifndef RENDERING_H
#define RENDERING_H
X
#include <sipp.h>
X
X
/*
X * Modes for storing the image.
X */
#define PBM_FILE   0
#define PPM_FILE   1
#define PIXMAP     2
X
X
/*
X * Temporary storage of transformed vertices.
X */
typedef struct view_coord_3d {
X    double                x, y, z;     /* Transformed vertex coordinates */
X    double                nx, ny, nz;  /* average normal */
X    double                u, v, w;     /* texture parameters */
X    struct view_coord_3d *next;        /* next vertex in the list */
} View_coord;
X
X
/*
X * Entry in the edge list used in rendering.
X */
typedef struct edges_3d {
X    int              y, y_stop;   /* Current point and interpolation steps */
X    double           x, xstep;            
X    double           z, zstep;
X    double           nx, nxstep;  /* Current normal and interp. steps */
X    double           ny, nystep;
X    double           nz, nzstep;
X    double           u, ustep;    /* Current texture coordinates and */
X    double           v, vstep;    /* interp. steps                   */
X    double           w, wstep;
X    int              polygon;     /* Uniqe polygon id of the polygon to */
X                                  /* which the edge belongs */
X    Surface         *surface;     /* Surface that the edge belongs to */
X    struct edges_3d *next;        /* Next edge on this scanline */
} Edge;
X
X
#endif /* RENDERING_H */
SHAR_EOF
chmod 0644 libsipp/rendering.h ||
echo 'restore of libsipp/rendering.h failed'
Wc_c="`wc -c < 'libsipp/rendering.h'`"
test 2300 -eq "$Wc_c" ||
	echo 'libsipp/rendering.h: original size 2300, current size' "$Wc_c"
fi
# ============= libsipp/shaders.h ==============
if test -f 'libsipp/shaders.h' -a X"$1" != X"-c"; then
	echo 'x - skipping libsipp/shaders.h (File already exists)'
else
echo 'x - extracting libsipp/shaders.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'libsipp/shaders.h' &&
/**
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/91
X **            Inge Wallin      (ingwa@isy.liu.se)         1990/91
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 **/
X
/**
X ** shaders.h - This include file defines the different shaders availiable
X **             in sipp. Each shader is defined by a structure containing 
X **             the necessary parameters to describe how a surface should 
X **             be shaded with that particular shader, and the extern 
X **             declaration of the shader function itself.
X **/
X
X
#ifndef _SHADERS_H
#define _SHADERS_H
X
X
#include <sipp.h>
X
X
X
/*
X * Surface description used in strauss_shader().
X */
typedef struct {
X    double ambient;       /* Fraction of color visible in ambient light */
X    double smoothness;    /* Smoothness of the surface [0, 1] */
X    double metalness;     /* Metalness of the surface [0, 1] */
X    Color  color;         /* Base color of the surface */
} Strauss_desc;
X
X
X
/* 
X * Surface description for the bozo shader.
X */
typedef struct {
X    Color   *colors;
X    int      no_of_cols;
X    double   ambient;
X    double   specular;
X    double   c3;
X    double   scale;        /* Scale the texture by this value */
} Bozo_desc;
X    
X
X
/*
X * Surface description used by the wood shader. This shader
X * creates a solid texture (using noise & turbulence) that
X * simulates wood.
X */
typedef struct {
X    double   ambient;
X    double   specular;
X    double   c3;
X    double   scale;        /* Scale the wood texture by this value */
X    Color    base;         /* "Base" color of the surface */
X    Color    ring;         /* Color of the darker rings */
} Wood_desc;
X
X
X
/*
X * Surface description used by the marble shader. marble_shader
X * creates a solid texture (using noise & turbulence) that
X * simulates marble.
X */
typedef struct {
X    double   ambient;
X    double   specular;
X    double   c3;
X    double   scale;        /* Scale the marble texture by this value */
X    Color    base;         /* "Base" color of the surface */
X    Color    strip;        /* Color of the "stripes" in the marble */
} Marble_desc;
X
X
X
/*
X * Surface description used by the granite shader. granite_shader
X * creates a solid texture (using noise) that mixes two colors
X * to simulate granite.
X */
typedef struct {
X    double   ambient;
X    double   specular;
X    double   c3;
X    double   scale;        /* Scale the texture by this value */
X    Color    col1;         /* The two color components */
X    Color    col2;         /*                          */
} Granite_desc;
X
X
X
/*
X * Mask shader. It uses mask image (ususally a bitmap) to
X * choose between two other shaders. It projects the mask
X * image on the u-v plane (in texture coordinates). When a
X * surface is shaded it calls pixel_test() to check the
X * u, v coordinate in the mask and calls one of two other 
X * shaders depending of the outcome of that test.
X */
typedef struct {
X    Shader *fg_shader;          /* Shader to call if mask(x, y) != 0 */
X    void   *fg_surface;         /* Surface description for fg_shader */
X    Shader *bg_shader;          /* Shader to call if mask(x, y) == 0 */
X    void   *bg_surface;         /* Surface description for bg_shader */
X    void   *mask;               /* Pointer to mask image */
X    bool  (*pixel_test)();      /* Function that tests a pixel value */
X    int     x0, y0;             /* Where to put origo in the mask image */
X    int     xsize, ysize;       /* Size of the mask image */
X    double  xscale, yscale;     /* Scale the mask image with these values */
} Mask_desc;
X
X
/*
X * Surface description for the bumpy_shader(). This shader 
X * fiddles with the surface normals of a surface so the surface 
X * looks bumpy.
X */
X
typedef struct {
X    Shader *shader;
X    void   *surface;
X    double scale;
X    bool   bumpflag;
X    bool   holeflag;
} Bumpy_desc;
X
X
/*
X * Declarations of the actual shading functions.
X */
extern void strauss_shader();
extern void wood_shader();
extern void marble_shader();
extern void granite_shader();
extern void bozo_shader();
extern void mask_shader();
extern void bumpy_shader();
extern void planet_shader();
X
#endif /* _SHADERS_H */
SHAR_EOF
chmod 0664 libsipp/shaders.h ||
echo 'restore of libsipp/shaders.h failed'
Wc_c="`wc -c < 'libsipp/shaders.h'`"
test 4808 -eq "$Wc_c" ||
	echo 'libsipp/shaders.h: original size 4808, current size' "$Wc_c"
fi
true || echo 'restore of libsipp/sipp.h failed'
echo End of part 4, continue with part 5
exit 0

-- 
------------------------------------------------------------------------------
 J o n a s   Y n g v e s s o n
Dept. of Electrical Engineering	                         jonas-y@isy.liu.se
University of Linkoping, Sweden                   ...!uunet!isy.liu.se!jonas-y

exit 0 # Just in case...


