Newsgroups: alt.sources
From: wware@world.std.com (Will Ware)
Subject: triangle rendering code
Message-ID: <FyJK42.Cnu@world.std.com>
Date: Mon, 31 Jul 2000 02:55:13 GMT
References: <FyFBzM.846@world.std.com>
Organization: The World Public Access UNIX, Brookline, MA

#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.2).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 2000-07-30 21:50 EDT by <wware@grendel.std.com>.
# Source directory was `/home/wware/anima'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode       name
# ------ ---------- ------------------------------------------
#   5891 -r--r--r-- zbuf.c
#   2866 -rwxr-xr-x anima.py
#    218 -r--r--r-- Makefile
#
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=FAILED
locale_dir=FAILED
first_param="$1"
for dir in $PATH
do
  if test "$gettext_dir" = FAILED && test -f $dir/gettext \
     && ($dir/gettext --version >/dev/null 2>&1)
  then
    set `$dir/gettext --version 2>&1`
    if test "$3" = GNU
    then
      gettext_dir=$dir
    fi
  fi
  if test "$locale_dir" = FAILED && test -f $dir/shar \
     && ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
  then
    locale_dir=`$dir/shar --print-text-domain-dir`
  fi
done
IFS="$save_IFS"
if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED
then
  echo=echo
else
  TEXTDOMAINDIR=$locale_dir
  export TEXTDOMAINDIR
  TEXTDOMAIN=sharutils
  export TEXTDOMAIN
  echo="$gettext_dir/gettext -s"
fi
touch -am 1231235999 $$.touch >/dev/null 2>&1
if test ! -f 1231235999 && test -f $$.touch; then
  shar_touch=touch
else
  shar_touch=:
  echo
  $echo 'WARNING: not restoring timestamps.  Consider getting and'
  $echo "installing GNU \`touch', distributed in GNU File Utilities..."
  echo
fi
rm -f 1231235999 $$.touch
#
if mkdir _sh00792; then
  $echo 'x -' 'creating lock directory'
else
  $echo 'failed to create lock directory'
  exit 1
fi
# ============= zbuf.c ==============
if test -f 'zbuf.c' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'zbuf.c' '(file already exists)'
else
  $echo 'x -' extracting 'zbuf.c' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'zbuf.c' &&
/*
X * C code to implement a Z-buffer
X */
X
#include <stdio.h>
#include "Python.h"
X
#define max(a,b) ((a) >= (b) ? (a) : (b))
#define min(a,b) ((a) <= (b) ? (a) : (b))
X
#define HORIZON  1.0e10
X
int height, width;
struct pixel
{
X  double z;
X  double r, g, b;
};
struct pixel *band, *bandptr;
X
struct big_pixel
{
X  double x, y, z;
X  double r, g, b;
};
X
static PyObject *
zbuf_init(PyObject *self, PyObject *args)
{
X  if (band != NULL)
X    goto done;
X  if (!PyArg_ParseTuple(args, "ii", &width, &height))
X    return NULL;
X  band = malloc(width * height * sizeof(struct pixel));
X  if (band == NULL)
X    {
X      PyErr_NoMemory();
X      return NULL;
X    }
X done:
X  Py_INCREF(Py_None);
X  return Py_None;
}
X
static PyObject *
zbuf_deinit(PyObject *self, PyObject *args)
{
X  if (band == NULL)
X    goto done;
X  if (!PyArg_ParseTuple(args, ""))
X    return NULL;
X  free(band);
X  band = NULL;
X done:
X  Py_INCREF(Py_None);
X  return Py_None;
}
X
static PyObject *
zbuf_clear(PyObject *self, PyObject *args)
{
X  int i, rbackground, gbackground, bbackground;
X  rbackground = gbackground = bbackground = 0;
X  if (!PyArg_ParseTuple(args, "|iii", &rbackground, &gbackground, &bbackground))
X    return NULL;
X  for (i = 0; i < width * height; i++)
X    {
X      band[i].r = rbackground;
X      band[i].g = gbackground;
X      band[i].b = bbackground;
X      band[i].z = HORIZON;
X    }
X  Py_INCREF(Py_None);
X  return Py_None;
}
X
static void half_triangle(int ytop,
X                          struct big_pixel *pstart,
X                          struct big_pixel *pfinish,
X                          struct big_pixel *pbeyond)
{
X
X  int x, y, ybottom;
X  struct big_pixel *pleft, *pright;
X  double mult;
X  double xleft, xright, dxleft, dxright;
X  double z, dz, zleft, zright, dzleft, dzright;
X  double dr, rleft, rright, drleft, drright;
X  double dg, gleft, gright, dgleft, dgright;
X  double db, bleft, bright, dbleft, dbright;
X
X  ybottom = ytop + height;
X
X  if (! ((ytop >= pstart->y && ytop <= pfinish->y) ||
X	 (ytop <= pstart->y && ytop >= pfinish->y) ||
X	 (ybottom >= pstart->y && ybottom <= pfinish->y) ||
X	 (ybottom <= pstart->y && ybottom >= pfinish->y) ||
X	 (pstart->y >= ytop && pstart->y <= ybottom) ||
X	 (pfinish->y >= ytop && pfinish->y <= ybottom)) )
X    return;
X
X  /* Figure out whether finish or beyond represents the left side */
X  if ((pfinish->y < pstart->y) ^
X      ((pbeyond->y - pstart->y) * (pfinish->x - pstart->x) <
X       (pfinish->y - pstart->y) * (pbeyond->x - pstart->x)))
X    {
X      pleft = pfinish;
X      pright = pbeyond;
X    }
X  else
X    {
X      pleft = pbeyond;
X      pright = pfinish;
X    }
X
X  mult = 1.0 / (pleft->y - pstart->y);
X  dxleft = mult * (pleft->x - pstart->x);
X  dzleft = mult * (pleft->z - pstart->z);
X  drleft = mult * (pleft->r - pstart->r);
X  dgleft = mult * (pleft->g - pstart->g);
X  dbleft = mult * (pleft->b - pstart->b);
X  mult = 1.0 / (pright->y - pstart->y);
X  dxright = mult * (pright->x - pstart->x);
X  dzright = mult * (pright->z - pstart->z);
X  drright = mult * (pright->r - pstart->r);
X  dgright = mult * (pright->g - pstart->g);
X  dbright = mult * (pright->b - pstart->b);
X
X  for (bandptr = band, y = ytop; y < ybottom; y++, bandptr += width)
X    {
X      if ((y >= pstart->y && y <= pfinish->y) ||
X	  (y <= pstart->y && y >= pfinish->y))
X	{
X	  double dy = y - pstart->y;
X	  xleft = pstart->x + dxleft * dy;
X	  xright = pstart->x + dxright * dy;
X	  if (xleft < xright)
X	    {
X	      zleft = pstart->z + dzleft * dy;
X	      rleft = pstart->r + drleft * dy;
X	      gleft = pstart->g + dgleft * dy;
X	      bleft = pstart->b + dbleft * dy;
X	      zright = pstart->z + dzright * dy;
X	      rright = pstart->r + drright * dy;
X	      gright = pstart->g + dgright * dy;
X	      bright = pstart->b + dbright * dy;
X	      dz = (zright - zleft) / (xright - xleft);
X	      dr = (rright - rleft) / (xright - xleft);
X	      dg = (gright - gleft) / (xright - xleft);
X	      db = (bright - bleft) / (xright - xleft);
X	      for (x = 0; x < width; x++)
X		{
X		  double dx = x - xleft;
X		  z = zleft + dz * dx;
X		  if (x >= xleft && x <= xright &&
X		      z < bandptr[x].z)
X		    {
X		      bandptr[x].z = z;
X		      bandptr[x].r = rleft + dr * dx;
X		      bandptr[x].g = gleft + dg * dx;
X		      bandptr[x].b = bleft + db * dx;
X		    }
X		}
X	    }
X	}
X    }
}
X
#define SWAP(p1,p2) \
X  { struct big_pixel ptmp; ptmp = p1; p1 = p2; p2 = ptmp; }
X
static PyObject *
zbuf_tri(PyObject *self, PyObject *args)
{
X  int ytop;
X  struct big_pixel p0, p1, p2;
X
X  if (!PyArg_ParseTuple(args, "idddddddddddddddddd",
X			&ytop,
X			&p0.x, &p0.y, &p0.z, &p0.r, &p0.g, &p0.b,
X			&p1.x, &p1.y, &p1.z, &p1.r, &p1.g, &p1.b,
X			&p2.x, &p2.y, &p2.z, &p2.r, &p2.g, &p2.b))
X    return NULL;
X  if (((p0.y < p2.y) && (p2.y < p1.y)) || ((p1.y < p2.y) && (p2.y < p0.y)))
X    SWAP(p1, p2);
X  if (((p2.y < p0.y) && (p0.y < p1.y)) || ((p1.y < p0.y) && (p0.y < p2.y)))
X    SWAP(p0, p1);
X  half_triangle(ytop, &p0, &p1, &p2);
X  half_triangle(ytop, &p2, &p1, &p0);
X  Py_INCREF(Py_None);
X  return Py_None;
}
X
static PyObject *
zbuf_tostring(PyObject *self, PyObject *args)
{
X  char *str;
X  int i;
X  if (!PyArg_ParseTuple(args, ""))
X    return NULL;
X  str = malloc(width * height * 3);
X  for (i = 0; i < width * height; i++)
X    {
X      str[3*i + 0] = (unsigned char)band[i].r;
X      str[3*i + 1] = (unsigned char)band[i].g;
X      str[3*i + 2] = (unsigned char)band[i].b;
X    }
X  return PyString_FromStringAndSize(str, width * height * 3);
}
X
/* List of functions defined in the module */
X
static PyMethodDef zbuf_methods[] = {
X  {"init",        zbuf_init,        1},
X  {"deinit",      zbuf_deinit,      1},
X  {"clear",       zbuf_clear,       1},
X  {"tri",         zbuf_tri,         1},
X  {"tostring",    zbuf_tostring,    1},
X  {NULL,          NULL}           /* sentinel */
};
X
X
/* Initialization function for the module (*must* be called initzbuf) */
X
DL_EXPORT(void)
initzbuf()
{
X  Py_InitModule("zbuf", zbuf_methods);
}
SHAR_EOF
  $shar_touch -am 07300015100 'zbuf.c' &&
  chmod 0444 'zbuf.c' ||
  $echo 'restore of' 'zbuf.c' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'zbuf.c:' 'MD5 check failed'
a7da83e772566c654dec353e939f9da9  zbuf.c
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'zbuf.c'`"
    test 5891 -eq "$shar_count" ||
    $echo 'zbuf.c:' 'original size' '5891,' 'current size' "$shar_count!"
  fi
fi
# ============= anima.py ==============
if test -f 'anima.py' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'anima.py' '(file already exists)'
else
  $echo 'x -' extracting 'anima.py' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'anima.py' &&
#!/usr/bin/env python
X
import math, sys, os, random, types
X
os.system('make zbuf.so')
import zbuf
X
# Vertices are 6-tuples: (x, y, z, r, g, b)
X
def randomVertex():
X    return (width * random.random(),
X            height * random.random(),
X            1000 * random.random(),
X            256 * random.random(),
X            256 * random.random(),
X            256 * random.random())
X
def wobbler(xlo, xhi):
X    def func1(t, xlo=xlo, xhi=xhi,
X             center=xlo+(xhi-xlo)*random.random(),
X             spread=.2*(xhi-xlo),
X             freq=2*math.pi*int(3 * random.random()),
X             phase=2*math.pi*random.random()):
X        x = center + spread * math.sin(freq * t + phase)
X        return min(max(x, xlo), xhi)
X    return func1
X
def randomVertex():
X    z = [wobbler(0, width),
X         wobbler(0, height),
X         wobbler(0, 1000),
X         wobbler(0, 256),
X         wobbler(0, 256),
X         wobbler(0, 256)]
X    def func2(args,z=z):
X        lst = [ ]
X        for x in z:
X            lst.append(x(args))
X        return tuple(lst)
X    return func2
X
class Triangle:
X    def __init__(self, vertex0, vertex1, vertex2):
X        self.vertex0 = vertex0
X        self.vertex1 = vertex1
X        self.vertex2 = vertex2
X    def render(self, ytop, args=None):
X        v0, v1, v2 = self.vertex0, self.vertex1, self.vertex2
X        try:
X            v0 = v0(args)
X            v1 = v1(args)
X            v2 = v2(args)
X        except AttributeError:
X            pass
X        args = (ytop,) + v0 + v1 + v2
X        apply(zbuf.tri, args)
X
def main(numtriangles):
X    global height, width
X    os.system('rm -f foobar*.jpg')
X    width, height, bandheight = 640, 480, 60
X    numframes = 20
X    zbuf.init(width, bandheight)
X    tlist = [ ]
X    for i in range(numtriangles):
X        tlist.append(Triangle(randomVertex(),
X                              randomVertex(),
X                              randomVertex()))
X    for frame in range(numframes):
X        print 'Frame %d ' % frame,
X        sys.stdout.flush()
X        outf = open('tmp.rgb', 'w')
X        lines = 0
X        for i in range(height / bandheight):
X            zbuf.clear(40, 60, 80)
X            for t in tlist:
X                t.render(i * bandheight, 1. * frame / numframes)
X            dlines = min(lines + bandheight, height) - lines
X            s = zbuf.tostring()
X            s = s[:3*width*dlines]
X            outf.write(s)
X            sys.stdout.write(".")
X            sys.stdout.flush()
X            lines = lines + dlines
X        print
X        outf.close()
X        s = ('convert -size %dx%d tmp.rgb foobar%03d.jpg; rm -f tmp.rgb'
X             % (width, height, frame))
X        os.system(s)
X    s = 'convert -size %dx%d foobar*.jpg foobar.mpg' % (width, height)
X    print s
X    os.system(s)
X    os.system('rm -f foobar*.jpg')
X
if __name__ == '__main__':
X    numtriangles = eval(sys.argv[1])
X    main(numtriangles)
SHAR_EOF
  $shar_touch -am 07302150100 'anima.py' &&
  chmod 0755 'anima.py' ||
  $echo 'restore of' 'anima.py' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'anima.py:' 'MD5 check failed'
757ae43ecc5147e4fe557bc0469c62ca  anima.py
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'anima.py'`"
    test 2866 -eq "$shar_count" ||
    $echo 'anima.py:' 'original size' '2866,' 'current size' "$shar_count!"
  fi
fi
# ============= Makefile ==============
if test -f 'Makefile' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'Makefile' '(file already exists)'
else
  $echo 'x -' extracting 'Makefile' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
all: zbuf.so
X
clean:
X	rm -f *.o *.so *.rgb *.gif *.jpg *.mpg *.pyc
X
zbuf.so: zbuf.c
X	gcc -g -Wall -O2 -c -I/usr/include/python1.5 zbuf.c
X	gcc -shared -o zbuf.so zbuf.o
X
shar:
X	shar zbuf.c anima.py Makefile > zbuf.shar
SHAR_EOF
  $shar_touch -am 07302150100 'Makefile' &&
  chmod 0444 'Makefile' ||
  $echo 'restore of' 'Makefile' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'Makefile:' 'MD5 check failed'
52ed119d9bbc5c285f45eab189ebb5b7  Makefile
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'Makefile'`"
    test 218 -eq "$shar_count" ||
    $echo 'Makefile:' 'original size' '218,' 'current size' "$shar_count!"
  fi
fi
rm -fr _sh00792
exit 0

-- 
 - - - - - - - - - - - - - - - - - - - - - - - -
Resistance is futile. Capacitance is efficacious.
Will Ware	email:    wware @ world.std.com
