v16i007: sipp 2.0 - a library for 3D graphics, Part03/06
Kent Landfield
kent at sparky.IMD.Sterling.COM
Thu Jan 3 17:57:50 AEST 1991
Submitted-by: ingwa at 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 at isy.liu.se) 1988/89/90
X * Inge Wallin (ingwa at 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 at isy.liu.se | life becomes meaningless." |
| Geoffrey James: The Tao of Programming. |
exit 0 # Just in case...
--
Kent Landfield INTERNET: kent at 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 at uunet.uu.net.
More information about the Comp.sources.misc
mailing list