Raytrace.java

java · 1625 lines · part of Box & Reflective Tetrahedron · raw

import java.awt.image.*;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Event;
import java.awt.Image;
import java.awt.Font;



public class Raytrace extends java.applet.Applet implements Runnable {



//---- DATA ----------------------------------------------------------------//

  //- Thread data

  Thread runThread;

  //- Output image data

  static int image_w = 160, image_h = 160;

  byte r[], g[], b[];
  byte buf[];
  byte back[];
  IndexColorModel pal;

  MemoryImageSource src;
  Image im;

  byte poly_alpha[];

  //- Mouse move stuff

  int mouse_oldx, mouse_oldy;
  int pause = 0;

  //- 3D Object data

  //  Rotation matrix

  double m[][];

  //  Object 1

  int o1_rx, o1_ry, o1_rz;
  double o1_cx, o1_cy, o1_cz;

  int o1_numpoints;
  double o1_x[], o1_y[], o1_z[];
  double o1_tx[], o1_ty[], o1_tz[];
  double o1_sx[], o1_sy[], o1_sz[];

  int o1_numfaces;
  int o1_fn[], o1_f[][];
  double o1_nx[], o1_ny[], o1_nz[];
  double o1_tnx[], o1_tny[], o1_tnz[];

  //  Object 2

  int o2_rx, o2_ry, o2_rz;
  double o2_cx, o2_cy, o2_cz;

  int o2_numpoints;
  double o2_x[], o2_y[], o2_z[];
  double o2_tx[], o2_ty[], o2_tz[];
  double o2_sx[], o2_sy[], o2_sz[];

  int o2_numfaces;
  int o2_fn[], o2_f[][];
  double o2_nx[], o2_ny[], o2_nz[];
  double o2_tnx[], o2_tny[], o2_tnz[];

  //  BSP Tree

  static int bsp_size = 256;
  static int bsp_polysize = 8;

  int bsp_num;
  int bsp_left[];
  int bsp_right[];
  int bsp_poly_num[];
  double bsp_poly_sx[][], bsp_poly_sy[][];
  double bsp_poly_x[][], bsp_poly_y[][], bsp_poly_z[][];
  double bsp_nx[], bsp_ny[], bsp_nz[];
  double bsp_x[], bsp_y[], bsp_z[];
  int bsp_object[];

  //  Polygon stack

  static int polystack_size = 256;
  static int polystack_polysize = 8;

  int polystack_sp, polystack_bp;

  int polystack_num[];
  double polystack_x[][], polystack_y[][], polystack_z[][];
  double polystack_sx[][], polystack_sy[][];
  double polystack_nx[], polystack_ny[], polystack_nz[];
  double polystack_cx[], polystack_cy[], polystack_cz[];
  double polystack_clip[][];
  int polystack_object[];

  //  Polygon data

  int poly_ymin, poly_ymax;
  int poly_xmin[], poly_xmax[];

  int poly2_ymin, poly2_ymax;
  int poly2_xmin[], poly2_xmax[];

  //  Lookup tables

  double sintable[], costable[];



//---- INIT & THREAD FUNCTIONS ---------------------------------------------//

  //---- init

  public void init() {
    int size = image_w*image_h;
    int i, j, k;
    char rr, gg, bb;
    double d;
    String s;

    System.out.println("LKS Renderer v0.1");

    //- Set up image buffer and palette

    buf = new byte[size];
    back = new byte[size];
    r = new byte[256];
    g = new byte[256];
    b = new byte[256];

    s  = "*(!'1*89%])%%.";

    for(i=0; i<size; i++) {
      buf[i] = 0;
    }

    rr = (57*5)&255;
    gg = (57*3)&255;
    bb = (57*7)&255;

    for(i=0; i<64; i++) {
      r[i] = (byte)(i*4);
      g[i] = (byte)(i*4);
      b[i] = (byte)(i*4);
    }

    for(i=0; i<64; i++) {
      r[i+64] = (byte)(i*4);
      g[i+64] = (byte)(0);
      b[i+64] = (byte)(0);
    }

    s += "%^(1@% %) *^).";

    for(i=0; i<64; i++) {
      r[i+128] = (byte)(255);
      g[i+128] = (byte)(i*3);
      b[i+128] = (byte)(i*3);
    }

    for(i=0; i<64; i++) {
      r[i+192] = (byte)(0);
      g[i+192] = (byte)(0);
      b[i+192] = (byte)(i*4);
    }

    s += "%4$(@**.? ^*%-";

    //  Alpha blending

    poly_alpha = new byte[256*64];

    for(j=0; j<64; j++) {
      for(i=0; i<64; i++) {
        poly_alpha[(j<<8)+i] = (byte)((i+j)>>1);
      }

      for(i=0; i<64; i++) {
        poly_alpha[(j<<8)+i+64] = (byte)(64+i+j);
      }

      for(i=0; i<64; i++) {
        poly_alpha[(j<<8)+i+128] = 0;
      }

      for(i=0; i<64; i++) {
        poly_alpha[(j<<8)+i+192] = (byte)((i+j)>>1);
      }

    }

    //  Make background

    s += "* !1(%?*/?^89*";

    for(j=0; j<image_h; j++) {
      for(i=0; i<image_w; i++) {
        d = ((i-image_w/2)*(i-image_w/2)+(j-image_h/2)*(j-image_h/2))*0.0035;
        k = (int)(d);
        if(k<0) { k=0; }
        back[i+j*image_w] = (byte)(192+(k&63));
      }
    }

    s += "*%*%-*//*?*%*+";

    k=image_w*(image_h-7)-16;
    for(j=0; j<5; j++) {
      for(i=0; i<14; i++) {
        if(s.charAt(i+14*j) == '*' || s.charAt(i+14*j) == '%') {
          back[k] = (byte)255;
          back[k+image_w+1] = (byte)192;
        }
        k++;
      }
      k += image_w-14;
    }

    pal = new IndexColorModel(8, 256, r, g, b);
    src = new MemoryImageSource(image_w, image_h, pal, buf, 0, image_w);
    src.setAnimated(true);
    im = createImage(src);

    //- Create lookup tables

    sintable = new double[4096];
    costable = new double[4096];

    for(i=0; i<4096; i++) {
      sintable[i] = Math.sin(3.1415926*i/2048);
      costable[i] = Math.cos(3.1415926*i/2048);
    }

    //- Create matrix

    m = new double[3][3];

    //- Create BSP tree

    bsp_object = new int[bsp_size];
    bsp_left = new int[bsp_size];
    bsp_right = new int[bsp_size];
    bsp_poly_num = new int[bsp_size];
    bsp_poly_sx = new double[bsp_size][bsp_polysize];
    bsp_poly_sy = new double[bsp_size][bsp_polysize];
    bsp_poly_x = new double[bsp_size][bsp_polysize];
    bsp_poly_y = new double[bsp_size][bsp_polysize];
    bsp_poly_z = new double[bsp_size][bsp_polysize];
    bsp_nx = new double[bsp_size];
    bsp_ny = new double[bsp_size];
    bsp_nz = new double[bsp_size];
    bsp_x = new double[bsp_size];
    bsp_y = new double[bsp_size];
    bsp_z = new double[bsp_size];

    //  Create stacks

    polystack_num = new int[polystack_size];
    polystack_sx = new double[polystack_size][polystack_polysize];
    polystack_sy = new double[polystack_size][polystack_polysize];
    polystack_nx = new double[polystack_size];
    polystack_ny = new double[polystack_size];
    polystack_nz = new double[polystack_size];
    polystack_x = new double[polystack_size][polystack_polysize];
    polystack_y = new double[polystack_size][polystack_polysize];
    polystack_z = new double[polystack_size][polystack_polysize];
    polystack_cx = new double[polystack_size];
    polystack_cy = new double[polystack_size];
    polystack_cz = new double[polystack_size];
    polystack_object = new int[polystack_size];
    polystack_clip = new double[polystack_size][polystack_polysize];
    polystack_sp = 0;
    polystack_bp = 0;

    //- Create polygon

    poly_xmin = new int[image_h];
    poly_xmax = new int[image_h];
    poly2_xmin = new int[image_h];
    poly2_xmax = new int[image_h];

    //- Create objects

    //  Initial position of object 1

    o1_cx = 0; o1_cy = 0; o1_cz = 5.0;
    o1_rx = 0; o1_ry = 0; o1_rz = 0;

    //  Define points of object 1

    o1_numpoints = 4;

    o1_x = new double[4]; o1_y = new double[4]; o1_z = new double[4];
    o1_tx = new double[4]; o1_ty = new double[4]; o1_tz = new double[4];
    o1_sx = new double[4]; o1_sy = new double[4]; o1_sz = new double[4];

    o1_x[0] =  1.8; o1_y[0] =  1.8; o1_z[0] =  1.8;
    o1_x[1] = -1.8; o1_y[1] =  1.8; o1_z[1] = -1.8;
    o1_x[2] =  1.8; o1_y[2] = -1.8; o1_z[2] = -1.8;
    o1_x[3] = -1.8; o1_y[3] = -1.8; o1_z[3] =  1.8;
/*
    o1_x[0] =  1.8; o1_y[0] =  1.8; o1_z[0] =    0;
    o1_x[2] =  1.8; o1_y[2] = -1.8; o1_z[2] =    0;
    o1_x[1] = -1.8; o1_y[1] =  1.8; o1_z[1] =    0;
    o1_x[3] = -1.8; o1_y[3] = -1.8; o1_z[3] =    0;
*/
    //  Define faces of object 1

    o1_numfaces = 4;

    o1_nx = new double[4]; o1_ny = new double[4]; o1_nz = new double[4];
    o1_tnx = new double[4]; o1_tny = new double[4]; o1_tnz = new double[4];

    makenormal_o1( 0,  0,  2,  1);
    makenormal_o1( 1,  1,  2,  3);
    makenormal_o1( 2,  0,  1,  3);
    makenormal_o1( 3,  3,  2,  0);

    //  Initial position of object 2

    o2_cx = 0; o2_cy = 0; o2_cz = 5.0;
    o2_rx = 0; o2_ry = 0; o2_rz = 0;

    o2_numpoints = 16;

    //  Define points of object 2

    o2_x = new double[16]; o2_y = new double[16]; o2_z = new double[16];
    o2_tx = new double[16]; o2_ty = new double[16]; o2_tz = new double[16];
    o2_sx = new double[16]; o2_sy = new double[16]; o2_sz = new double[16];

    o2_x[ 0] =  1.5; o2_y[ 0] =  0.5; o2_z[ 0] =  2.5;
    o2_x[ 1] =  1.0; o2_y[ 1] =  0.5; o2_z[ 1] =  2.0;
    o2_x[ 2] =  1.0; o2_y[ 2] = -0.5; o2_z[ 2] =  2.0;
    o2_x[ 3] =  1.5; o2_y[ 3] = -0.5; o2_z[ 3] =  2.5;
    o2_x[ 4] =  1.5; o2_y[ 4] =  0.5; o2_z[ 4] = -2.5;
    o2_x[ 5] =  1.0; o2_y[ 5] =  0.5; o2_z[ 5] = -2.0;
    o2_x[ 6] =  1.0; o2_y[ 6] = -0.5; o2_z[ 6] = -2.0;
    o2_x[ 7] =  1.5; o2_y[ 7] = -0.5; o2_z[ 7] = -2.5;
    o2_x[ 8] = -1.5; o2_y[ 8] =  0.5; o2_z[ 8] = -2.5;
    o2_x[ 9] = -1.0; o2_y[ 9] =  0.5; o2_z[ 9] = -2.0;
    o2_x[10] = -1.0; o2_y[10] = -0.5; o2_z[10] = -2.0;
    o2_x[11] = -1.5; o2_y[11] = -0.5; o2_z[11] = -2.5;
    o2_x[12] = -1.5; o2_y[12] =  0.5; o2_z[12] =  2.5;
    o2_x[13] = -1.0; o2_y[13] =  0.5; o2_z[13] =  2.0;
    o2_x[14] = -1.0; o2_y[14] = -0.5; o2_z[14] =  2.0;
    o2_x[15] = -1.5; o2_y[15] = -0.5; o2_z[15] =  2.5;

    //  Define faces of object 2

    o2_numfaces = 16;

    o2_nx = new double[16]; o2_ny = new double[16]; o2_nz = new double[16];
    o2_tnx = new double[16]; o2_tny = new double[16]; o2_tnz = new double[16];

    makenormal_o2( 0,  0,  4,  5,  1);
    makenormal_o2( 1,  1,  5,  6,  2);
    makenormal_o2( 2,  2,  6,  7,  3);
    makenormal_o2( 3,  3,  7,  4,  0);
    makenormal_o2( 4,  4,  8,  9,  5);
    makenormal_o2( 5,  5,  9, 10,  6);
    makenormal_o2( 6,  6, 10, 11,  7);
    makenormal_o2( 7,  7, 11,  8,  4);
    makenormal_o2( 8,  8, 12, 13,  9);
    makenormal_o2( 9,  9, 13, 14, 10);
    makenormal_o2(10, 10, 14, 15, 11);
    makenormal_o2(11, 11, 15, 12,  8);
    makenormal_o2(12, 12,  0,  1, 13);
    makenormal_o2(13, 13,  1,  2, 14);
    makenormal_o2(14, 14,  2,  3, 15);
    makenormal_o2(15, 15,  3,  0, 12);

  }

  public void start() {
    if(runThread == null) {
      runThread = new Thread(this);
      runThread.start();
    }
  }

  public void stop() {
    if(runThread != null) {
      runThread.stop();
      runThread = null;
    }
  }

  public void run() {

    while(true) {
      try {
        runThread.sleep(20);
      } catch (InterruptedException e) {
        return;
      }

      redraw();
    }
  }



//---- EVENT HANDLERS ------------------------------------------------------//

  public boolean mouseDown(Event evt, int x, int y) {
    pause = 0;
    mouse_oldx = x;
    mouse_oldy = y;
    return true;
  }
  
  public boolean mouseUp(Event evt, int x, int y) {
    pause = 0;
    return true;
  }
  
  public boolean mouseDrag(Event e, int x, int y) {
    pause = 1;
    o1_rx += 12*(y-mouse_oldy);
    o1_ry += 8*(y-mouse_oldy)+4*(x-mouse_oldx);
    o1_rz += 12*(x-mouse_oldx);
    o2_rx += 12*(y-mouse_oldy);
    o2_ry += 8*(y-mouse_oldy)+4*(x-mouse_oldx);
    o2_rz += 12*(x-mouse_oldx);
    mouse_oldx = x;
    mouse_oldy = y;
    return true;
  }

  public void update(Graphics g) {
    paint(g);
  }

  public void paint(Graphics g) {
    g.drawImage(im, 0, 0, this);
  }



//---- VECTOR ROUTINES -----------------------------------------------------//

//---- MakeTrans

  public void maketrans(int rx, int ry, int rz) {
    double cx, sx, cy, sy, cz, sz;

    cx = costable[rx&4095]; cy = costable[ry&4095]; cz = costable[rz&4095];
    sx = sintable[rx&4095]; sy = sintable[ry&4095]; sz = sintable[rz&4095];

    m[0][0] =          cy*cz; m[0][1] =          cy*sz; m[0][2] = -sy;
    m[1][0] = sx*sy*cz-cx*sz; m[1][1] = sx*sy*sz+cx*cz; m[1][2] = sx*cy;
    m[2][0] = cx*sy*cz+sx*sz; m[2][1] = cx*sy*sz-sx*cz; m[2][2] = cx*cy;
  }



//---- MakeNormal_o1

  public void makenormal_o1(int poly, int p1, int p2, int p3) {
    double ax, ay, az, bx, by, bz, nx, ny, nz, n;

    ax = o1_x[p3]-o1_x[p2]; ay = o1_y[p3]-o1_y[p2]; az = o1_z[p3]-o1_z[p2];
    bx = o1_x[p1]-o1_x[p2]; by = o1_y[p1]-o1_y[p2]; bz = o1_z[p1]-o1_z[p2];

    nx = ay*bz-az*by; ny = az*bx-ax*bz; nz = ax*by-ay*bx;

    n = 1/Math.sqrt(nx*nx+ny*ny+nz*nz);

    o1_nx[poly] = nx*n;
    o1_ny[poly] = ny*n;
    o1_nz[poly] = nz*n;
  }



//---- MakeNormal_o2

  public void makenormal_o2(int poly, int p1, int p2, int p3, int p4) {
    double ax, ay, az, bx, by, bz, nx, ny, nz, n;

    ax = o2_x[p3]-o2_x[p2]; ay = o2_y[p3]-o2_y[p2]; az = o2_z[p3]-o2_z[p2];
    bx = o2_x[p1]-o2_x[p2]; by = o2_y[p1]-o2_y[p2]; bz = o2_z[p1]-o2_z[p2];

    nx = ay*bz-az*by; ny = az*bx-ax*bz; nz = ax*by-ay*bx;

    n = 1/Math.sqrt(nx*nx+ny*ny+nz*nz);

    o2_nx[poly] = nx*n;
    o2_ny[poly] = ny*n;
    o2_nz[poly] = nz*n;
  }



//---- PlaneDist

  public double planedist(int ptr, double x, double y, double z) {
    return bsp_nx[ptr]*(x-bsp_x[ptr]) +
           bsp_ny[ptr]*(y-bsp_y[ptr]) +
           bsp_nz[ptr]*(z-bsp_z[ptr]);
  }



//---- NeedSplit

  //  Returns non-zero if polygon at poly is split by bsp plane at ptr.

  public int needsplit(int poly, int ptr) {

    int i, l, r;
    double d;

    l=0; r=0;

    for(i=0; i<polystack_num[poly]; i++) {

      if(bsp_nx[ptr]*(polystack_x[poly][i]-bsp_x[ptr]) +
         bsp_ny[ptr]*(polystack_y[poly][i]-bsp_y[ptr]) +
         bsp_nz[ptr]*(polystack_z[poly][i]-bsp_z[ptr]) < 0) {
        l++;
      } else {
        r++;
      }

      if(l>0 && r>0) { return 1; }
    }

    return 0;
  }



//---- SplitLine

  double split_x, split_y, split_z;
  double split_xx, split_yy, split_zz;
  double split_cx, split_cy, split_cz;

  public void splitline(int ptr) {

    double d1 = planedist(ptr, split_x, split_y, split_z);
    double d2 = planedist(ptr, split_xx, split_yy, split_zz);
  
    if(d1<0) d1 =- d1;
    if(d2<0) d2 =- d2;

    split_cx = split_x + d1*(split_xx-split_x)/(d1+d2);
    split_cy = split_y + d1*(split_yy-split_y)/(d1+d2);
    split_cz = split_z + d1*(split_zz-split_z)/(d1+d2);

  }



//---- SplitPoly

  //  Splits the polygon at stack position poly using the bsp plane at ptr.
  //  Returns the stack position of the half of the poly in front of the plane
  //  and the half behind the plane is is stored at poly.

  public int splitpoly(int poly, int ptr) {

    int i, j, n1, n2;

    //- First check which points need to be clipped
  
    for(i=0; i<polystack_num[poly]; i++) {
      if(bsp_nx[ptr]*(polystack_x[poly][i]-bsp_x[ptr]) +
         bsp_ny[ptr]*(polystack_y[poly][i]-bsp_y[ptr]) +
         bsp_nz[ptr]*(polystack_z[poly][i]-bsp_z[ptr]) < 0) {
        polystack_clip[poly][i] = -1;
      } else {
        polystack_clip[poly][i] = 1;
      }
    }

    //- Go around edge of polygon clipping lines which straddle plane

    n1 = 0;
    n2 = 0;

    for(i=0; i<polystack_num[poly]; i++) {
      j = i+1; if(j == polystack_num[poly]) { j = 0; }

      split_x = polystack_x[poly][i];
      split_y = polystack_y[poly][i];
      split_z = polystack_z[poly][i];
      split_xx = polystack_x[poly][j];
      split_yy = polystack_y[poly][j];
      split_zz = polystack_z[poly][j];

      if(polystack_clip[poly][i] < 0 && polystack_clip[poly][j] < 0) {

        // Both points outside

        polystack_x[polystack_sp+1][n2] = split_x;
        polystack_y[polystack_sp+1][n2] = split_y;
        polystack_z[polystack_sp+1][n2] = split_z;
        n2++;

      } else if(polystack_clip[poly][i] < 0 && polystack_clip[poly][j] > 0) {

        // First point outside, second inside

        splitline(ptr);

        polystack_x[polystack_sp][n1] = split_cx;
        polystack_y[polystack_sp][n1] = split_cy;
        polystack_z[polystack_sp][n1] = split_cz;
        n1++;

        polystack_x[polystack_sp+1][n2] = split_x;
        polystack_y[polystack_sp+1][n2] = split_y;
        polystack_z[polystack_sp+1][n2] = split_z;
        n2++;

        polystack_x[polystack_sp+1][n2] = split_cx;
        polystack_y[polystack_sp+1][n2] = split_cy;
        polystack_z[polystack_sp+1][n2] = split_cz;
        n2++;

      } else if(polystack_clip[poly][i] > 0 && polystack_clip[poly][j] < 0) {

        // First point inside, second outside

        splitline(ptr);

        polystack_x[polystack_sp][n1] = split_x;
        polystack_y[polystack_sp][n1] = split_y;
        polystack_z[polystack_sp][n1] = split_z;
        n1++;

        polystack_x[polystack_sp][n1] = split_cx;
        polystack_y[polystack_sp][n1] = split_cy;
        polystack_z[polystack_sp][n1] = split_cz;
        n1++;

        polystack_x[polystack_sp+1][n2] = split_cx;
        polystack_y[polystack_sp+1][n2] = split_cy;
        polystack_z[polystack_sp+1][n2] = split_cz;
        n2++;

      } else {

        // Both points inside

        polystack_x[polystack_sp][n1] = split_x;
        polystack_y[polystack_sp][n1] = split_y;
        polystack_z[polystack_sp][n1] = split_z;
        n1++;

      }
    }

    //  Poly becomes one of the clipped polygons

    for(i=0; i<n2; i++) {
      polystack_x[poly][i] = polystack_x[polystack_sp+1][i];
      polystack_y[poly][i] = polystack_y[polystack_sp+1][i];
      polystack_z[poly][i] = polystack_z[polystack_sp+1][i];
    }
    polystack_num[poly] = n2;

    polystack_cx[poly] = (polystack_x[poly][0] + polystack_x[poly][1] + polystack_x[poly][2]) * (1.0/3.0);
    polystack_cy[poly] = (polystack_y[poly][0] + polystack_y[poly][1] + polystack_y[poly][2]) * (1.0/3.0);
    polystack_cz[poly] = (polystack_z[poly][0] + polystack_z[poly][1] + polystack_z[poly][2]) * (1.0/3.0);

    //  The other is in the new polystack element

    polystack_num[polystack_sp] = n1;
    polystack_nx[polystack_sp] = polystack_nx[poly];
    polystack_ny[polystack_sp] = polystack_ny[poly];
    polystack_nz[polystack_sp] = polystack_nz[poly];

    polystack_cx[polystack_sp] = (polystack_x[polystack_sp][0] + polystack_x[polystack_sp][1] + polystack_x[polystack_sp][2]) * (1.0/3.0);
    polystack_cy[polystack_sp] = (polystack_y[polystack_sp][0] + polystack_y[polystack_sp][1] + polystack_y[polystack_sp][2]) * (1.0/3.0);
    polystack_cz[polystack_sp] = (polystack_z[polystack_sp][0] + polystack_z[polystack_sp][1] + polystack_z[polystack_sp][2]) * (1.0/3.0);

    polystack_sp++;

    return polystack_sp-1;
  }



//---- Line

  public void line(double x1, double y1, double x2, double y2) {
    int i, x, y;

    for(i=0; i<100; i++) {
      x = (int)(x1 + (x2-x1)*i/100);
      y = (int)(y1 + (y2-y1)*i/100);

      if(x>=0 && x<image_w && y>=0 && y<image_h) {
        buf[x+image_w*y] = (byte)255;
      }
    }
  }



//---- POLYGON ROUTINES ----------------------------------------------------//

//---- Poly_Clear

  public void poly_clear() {
    int i;

    poly_ymin = image_h;
    poly_ymax = 0;

    for(i=0; i<image_h; i++) {
      poly_xmin[i] = image_w;
      poly_xmax[i] = 0;
    }
  }



//---- Poly_Line

  public void poly_line(double x1, double y1, double x2, double y2) {
    double t;
    int x, y, i;
    int iy1, iy2;

    if(y2>y1) {

      // line going down - right hand side

      iy1 = (int)(y1)+1;
      iy2 = (int)(y2)+1;

      if(iy1 >= image_h) { return; }
      if(iy2 <= 0) { return; }
      if(iy1 < 0) { iy1 = 0; }
      if(iy2 > image_h) { iy2 = image_h; }

      if(iy1 < poly_ymin) { poly_ymin = iy1; }
      if(iy2 > poly_ymax) { poly_ymax = iy2; }

      for(y=iy1; y<iy2; y++) {
        x = (int)(x1 + (x2-x1)*(y-y1)/(y2-y1));
        if(x > image_w) { x = image_w; }
        if(x < 0) { x = 0; }
        poly_xmax[y] =  x;
      }

    } else {

      // line going up - left hand side

      t = x1; x1 = x2; x2 = t;
      t = y1; y1 = y2; y2 = t;

      iy1 = (int)(y1)+1;
      iy2 = (int)(y2)+1;

      if(iy1 >= image_h) { return; }
      if(iy2 <= 0) { return; }
      if(iy1 < 0) { iy1 = 0; }
      if(iy2 > image_h) { iy2 = image_h; }

      if(iy1 < poly_ymin) { poly_ymin = iy1; }
      if(iy2 > poly_ymax) { poly_ymax = iy2; }

      for(y=iy1; y<iy2; y++) {
        x = (int)(x1 + (x2-x1)*(y-y1)/(y2-y1));
        if(x > image_w) { x = image_w; }
        if(x < 0) { x = 0; }
        poly_xmin[y] = x;
      }

    }

  }




//---- Poly_Fill_Col

  public void poly_fill_col(byte c) {
    int x, y, o, oo;

    oo = image_w*poly_ymin;

    for(y=poly_ymin; y<poly_ymax; y++) {
      o = oo + poly_xmin[y];
      for(x=poly_xmin[y]; x<poly_xmax[y]; x++) buf[o++] = c;

      //  TEMP - draw backfacing as well
      o = oo + poly_xmax[y];
      for(x=poly_xmax[y]; x<poly_xmin[y]; x++) buf[o++] = c;

      oo += image_w;
    }

  }



//---- Poly_Fill_Alpha

  public void poly_fill_alpha(byte c) {
    int x, y, o, oo;

    oo = image_w*poly_ymin;

    for(y=poly_ymin; y<poly_ymax; y++) {
      o = oo + poly_xmin[y];
      for(x=poly_xmin[y]; x<poly_xmax[y]; x++) { buf[o] = poly_alpha[(c<<8)+buf[o]]; o++; }

      //  TEMP - draw backfacing as well
      o = oo + poly_xmax[y];
      for(x=poly_xmax[y]; x<poly_xmin[y]; x++) { buf[o] = poly_alpha[(c<<8)+buf[o]]; o++; }

      oo += image_w;
    }

  }



//---- Poly_Fill_Add

  public void poly_fill_add(byte c) {
    int x, y, o, oo;

    oo = image_w*poly_ymin;

    for(y=poly_ymin; y<poly_ymax; y++) {
      o = oo + poly_xmin[y];
      for(x=poly_xmin[y]; x<poly_xmax[y]; x++) buf[o++] += c;

      //  TEMP - draw backfacing as well
      o = oo + poly_xmax[y];
      for(x=poly_xmax[y]; x<poly_xmin[y]; x++) buf[o++] += c;

      oo += image_w;
    }

  }



//---- Poly2_Clear

  public void poly2_clear() {
    int i;

    poly2_ymin = image_h;
    poly2_ymax = 0;

    for(i=0; i<image_h; i++) {
      poly2_xmin[i] = image_w;
      poly2_xmax[i] = 0;
    }
  }



//---- Poly2_Line

  public void poly2_line(double x1, double y1, double x2, double y2) {
    double t;
    int x, y, i;
    int iy1, iy2;

//    x1 = image_w-x1;
//    x2 = image_w-x2;

    if(y2>y1) {

      // line going down - right hand side

      iy1 = (int)(y1)+1;
      iy2 = (int)(y2)+1;

      if(iy1 >= image_h) { return; }
      if(iy2 <= 0) { return; }
      if(iy1 < 0) { iy1 = 0; }
      if(iy2 > image_h) { iy2 = image_h; }

      if(iy1 < poly2_ymin) { poly2_ymin = iy1; }
      if(iy2 > poly2_ymax) { poly2_ymax = iy2; }

      for(y=iy1; y<iy2; y++) {
        x = (int)(x1 + (x2-x1)*(y-y1)/(y2-y1));
        if(x > image_w) { x = image_w; }
        if(x < 0) { x = 0; }
        poly2_xmin[y] =  x;
      }

    } else {

      // line going up - left hand side

      t = x1; x1 = x2; x2 = t;
      t = y1; y1 = y2; y2 = t;

      iy1 = (int)(y1)+1;
      iy2 = (int)(y2)+1;

      if(iy1 >= image_h) { return; }
      if(iy2 <= 0) { return; }
      if(iy1 < 0) { iy1 = 0; }
      if(iy2 > image_h) { iy2 = image_h; }

      if(iy1 < poly2_ymin) { poly2_ymin = iy1; }
      if(iy2 > poly2_ymax) { poly2_ymax = iy2; }

      for(y=iy1; y<iy2; y++) {
        x = (int)(x1 + (x2-x1)*(y-y1)/(y2-y1));
        if(x > image_w) { x = image_w; }
        if(x < 0) { x = 0; }
        poly2_xmax[y] = x;
      }

    }

  }



//---- Poly2_Fill_Col

  public void poly2_fill_col(byte c) {
    int x, y, o, oo;
    int t;

    if(poly2_ymin < poly_ymin) { poly2_ymin = poly_ymin; }
    if(poly2_ymax > poly_ymax) { poly2_ymax = poly_ymax; }

    if(poly2_ymin < 0) { poly2_ymin = 0; }
    if(poly2_ymax > image_h) { poly2_ymax = image_h; }

    oo = image_w*poly2_ymin;

    for(y=poly2_ymin; y<poly2_ymax; y++) {

      if(poly2_xmin[y] < poly_xmin[y]) { poly2_xmin[y] = poly_xmin[y]; }
      if(poly2_xmax[y] > poly_xmax[y]) { poly2_xmax[y] = poly_xmax[y]; }

      if(poly2_xmin[y] < 0) { poly2_xmin[y] = 0; }
      if(poly2_xmax[y] > image_w) { poly2_xmax[y] = image_w; }

      o = oo + poly2_xmin[y];
      for(x=poly2_xmin[y]; x<poly2_xmax[y]; x++) buf[o++] = c;

      oo += image_w;
    }

  }



//---- Poly2_Fill_Add

  public void poly2_fill_add(byte c) {
    int x, y, o, oo;
    int t;

    if(poly2_ymin < poly_ymin) { poly2_ymin = poly_ymin; }
    if(poly2_ymax > poly_ymax) { poly2_ymax = poly_ymax; }

    if(poly2_ymin < 0) { poly2_ymin = 0; }
    if(poly2_ymax > image_h) { poly2_ymax = image_h; }

    oo = image_w*poly2_ymin;

    for(y=poly2_ymin; y<poly2_ymax; y++) {

      if(poly2_xmin[y] < poly_xmin[y]) { poly2_xmin[y] = poly_xmin[y]; }
      if(poly2_xmax[y] > poly_xmax[y]) { poly2_xmax[y] = poly_xmax[y]; }

      if(poly2_xmin[y] < 0) { poly2_xmin[y] = 0; }
      if(poly2_xmax[y] > image_w) { poly2_xmax[y] = image_w; }

      o = oo + poly2_xmin[y];
      for(x=poly2_xmin[y]; x<poly2_xmax[y]; x++) buf[o++] += c;

      oo += image_w;
    }

  }



//---- BSP ROUTINES --------------------------------------------------------//

//---- BSP_Clear

  public void bsp_clear() {
    bsp_num = 0;
    polystack_sp = 0;
    polystack_bp = 0;
  }



//---- BSP_Add_o1

  //  This is a simple adding routine because o1 is put in the BSP tree first.

  public void bsp_add_o1(int poly, int p1, int p2, int p3) {

    double x, y, z;
    int ptr;

    //  Ignore backfacing polygons

    if(o1_tnx[poly]*o1_tx[p1] +
       o1_tny[poly]*o1_ty[p1] +
       o1_tnz[poly]*o1_tz[p1] > 0) return;

    //  If tree empty just put polygon as root

    if(bsp_num == 0) {

      bsp_object[bsp_num] = 1;

      bsp_poly_num[bsp_num] = 3;
      bsp_poly_sx[bsp_num][0] = o1_sx[p1]; bsp_poly_sy[bsp_num][0] = o1_sy[p1];
      bsp_poly_sx[bsp_num][1] = o1_sx[p2]; bsp_poly_sy[bsp_num][1] = o1_sy[p2];
      bsp_poly_sx[bsp_num][2] = o1_sx[p3]; bsp_poly_sy[bsp_num][2] = o1_sy[p3];

      bsp_x[bsp_num] = o1_tx[p1];
      bsp_y[bsp_num] = o1_ty[p1];
      bsp_z[bsp_num] = o1_tz[p1];
      bsp_nx[bsp_num] = o1_tnx[poly];
      bsp_ny[bsp_num] = o1_tny[poly];
      bsp_nz[bsp_num] = o1_tnz[poly];
/*
      bsp_x[bsp_num] = 0;
      bsp_y[bsp_num] = 0;
      bsp_z[bsp_num] = 5;
      bsp_nx[bsp_num] = 0;
      bsp_ny[bsp_num] = 0;
      bsp_nz[bsp_num] = 1;
*/
      bsp_left[bsp_num] = -1;
      bsp_right[bsp_num] = -1;

      bsp_num++;

    } else if(bsp_num < bsp_size) {

      //  Add to end of BSP array

      bsp_object[bsp_num] = 1;

      bsp_poly_num[bsp_num] = 3;
      bsp_poly_sx[bsp_num][0] = o1_sx[p1]; bsp_poly_sy[bsp_num][0] = o1_sy[p1];
      bsp_poly_sx[bsp_num][1] = o1_sx[p2]; bsp_poly_sy[bsp_num][1] = o1_sy[p2];
      bsp_poly_sx[bsp_num][2] = o1_sx[p3]; bsp_poly_sy[bsp_num][2] = o1_sy[p3];

      bsp_x[bsp_num] = o1_tx[p1];
      bsp_y[bsp_num] = o1_ty[p1];
      bsp_z[bsp_num] = o1_tz[p1];
      bsp_nx[bsp_num] = o1_tnx[poly];
      bsp_ny[bsp_num] = o1_tny[poly];
      bsp_nz[bsp_num] = o1_tnz[poly];

      bsp_left[bsp_num] = -1;
      bsp_right[bsp_num] = -1;

      //  Add to tree

      ptr = 0;

      x = (o1_tx[p1] + o1_tx[p2] + o1_tx[p3]) * (1.0/3.0);
      y = (o1_ty[p1] + o1_ty[p2] + o1_ty[p3]) * (1.0/3.0);
      z = (o1_tz[p1] + o1_tz[p2] + o1_tz[p3]) * (1.0/3.0);

      while(ptr != -1) {

        if(bsp_object[ptr] == 1) {

          //  If it's this object just use a point within this poly

          if(bsp_nx[ptr]*(x-bsp_x[ptr]) +
             bsp_ny[ptr]*(y-bsp_y[ptr]) +
             bsp_nz[ptr]*(z-bsp_z[ptr]) < 0) {

            if(bsp_left[ptr] == -1) {
              bsp_left[ptr] = bsp_num;
              bsp_num ++;
              ptr = -1;
            } else {
              ptr = bsp_left[ptr];
            }

          } else {

            if(bsp_right[ptr] == -1) {
              bsp_right[ptr] = bsp_num;
              bsp_num ++;
              ptr = -1;
            } else {
              ptr = bsp_right[ptr];
            }

          }

        } else {

          System.out.println("ERROR: Object 1 not alone in BSP tree");

        }

      }

    }

  }



//---- BSP_Add_o2

  public void bsp_add_o2(int poly, int p1, int p2, int p3, int p4) {

    double x, y, z;
    int ptr;
/*
    //  Ignore backfacing polygons

    if(o2_tnx[poly]*o2_tx[p1] +
       o2_tny[poly]*o2_ty[p1] +
       o2_tnz[poly]*o2_tz[p1] > 0) return;
*/
    //  Put this polygon on stack

    polystack_num[polystack_sp] = 4;
    polystack_x[polystack_sp][0] = o2_tx[p1];
    polystack_y[polystack_sp][0] = o2_ty[p1];
    polystack_z[polystack_sp][0] = o2_tz[p1];
    polystack_x[polystack_sp][1] = o2_tx[p2];
    polystack_y[polystack_sp][1] = o2_ty[p2];
    polystack_z[polystack_sp][1] = o2_tz[p2];
    polystack_x[polystack_sp][2] = o2_tx[p3];
    polystack_y[polystack_sp][2] = o2_ty[p3];
    polystack_z[polystack_sp][2] = o2_tz[p3];
    polystack_x[polystack_sp][3] = o2_tx[p4];
    polystack_y[polystack_sp][3] = o2_ty[p4];
    polystack_z[polystack_sp][3] = o2_tz[p4];

    polystack_nx[polystack_sp] = o2_tnx[poly];
    polystack_ny[polystack_sp] = o2_tny[poly];
    polystack_nz[polystack_sp] = o2_tnz[poly];

    polystack_cx[polystack_sp] = (o2_tx[p1] + o2_tx[p2] + o2_tx[p3]) * (1.0/3.0);
    polystack_cy[polystack_sp] = (o2_ty[p1] + o2_ty[p2] + o2_ty[p3]) * (1.0/3.0);
    polystack_cz[polystack_sp] = (o2_tz[p1] + o2_tz[p2] + o2_tz[p3]) * (1.0/3.0);

    polystack_sp++;
    bsp_add_o2_recurse(0, polystack_sp-1);
    polystack_sp--;

  }



  //---- BSP_Add_o2_Recurse

  public void bsp_add_o2_recurse(int ptr, int poly) {

    int split;
    double sx, sy;
    int i, j;

    //  Check if the poly needs splitting at this node.

    split = 0;
    if(bsp_object[ptr] != 2) {
      split = needsplit(poly, ptr);
    }

    if(split == 0) {

      //  No split - just add in front or behind depending on center point

      if(bsp_nx[ptr]*(polystack_cx[poly]-bsp_x[ptr]) +
         bsp_ny[ptr]*(polystack_cy[poly]-bsp_y[ptr]) +
         bsp_nz[ptr]*(polystack_cz[poly]-bsp_z[ptr]) < 0) {

        if(bsp_left[ptr] != -1) {
          bsp_add_o2_recurse(bsp_left[ptr], poly);
        } else {
          bsp_left[ptr] = bsp_add_o2_poly(poly);
        }

      } else {

        if(bsp_right[ptr] != -1) {
          bsp_add_o2_recurse(bsp_right[ptr], poly);
        } else {
          bsp_right[ptr] = bsp_add_o2_poly(poly);
        }

      }

    } else {

      // Split polygon

      int sp = polystack_sp;

      split = splitpoly(poly, ptr);

      if(bsp_left[ptr] != -1) {
        bsp_add_o2_recurse(bsp_left[ptr], poly);
      } else {
        bsp_left[ptr] = bsp_add_o2_poly(poly);
      }

      if(bsp_right[ptr] != -1) {
        bsp_add_o2_recurse(bsp_right[ptr], split);
      } else {
        bsp_right[ptr] = bsp_add_o2_poly(split);
      }

      polystack_sp = sp;

    }

  }



  //---- BSP_Add_o2_Poly

  public int bsp_add_o2_poly(int poly) {

    int i;
    double sx, sy;

    bsp_object[bsp_num] = 2;

    bsp_poly_num[bsp_num] = polystack_num[poly];
    for(i=0; i<polystack_num[poly]; i++) {
      bsp_poly_x[bsp_num][i] = polystack_x[poly][i];
      bsp_poly_y[bsp_num][i] = polystack_y[poly][i];
      bsp_poly_z[bsp_num][i] = polystack_z[poly][i];
      sx = (image_w*0.5) + 100.0*polystack_x[poly][i]/polystack_z[poly][i];
      sy = (image_h*0.5) - 100.0*polystack_y[poly][i]/polystack_z[poly][i];
      bsp_poly_sx[bsp_num][i] = sx; bsp_poly_sy[bsp_num][i] = sy;
    }

    bsp_x[bsp_num] = polystack_x[poly][0];
    bsp_y[bsp_num] = polystack_y[poly][0];
    bsp_z[bsp_num] = polystack_z[poly][0];
    bsp_nx[bsp_num] = polystack_nx[poly];
    bsp_ny[bsp_num] = polystack_ny[poly];
    bsp_nz[bsp_num] = polystack_nz[poly];

    bsp_left[bsp_num] = -1;
    bsp_right[bsp_num] = -1;

    bsp_num++;

    return bsp_num-1;
  }



//---- BSP_Reflect_Recurse

  public void bsp_reflect_recurse(int ptr, int refl) {
    int i, j;
    int left;
    byte col;
    double npa;
    double x, y, z, xx, yy, zz, sx, sy, sxx, syy;

    if(ptr == -1) { return; }

    left = 1;
    if(bsp_nx[ptr]*bsp_x[ptr] +
       bsp_ny[ptr]*bsp_y[ptr] +
       bsp_nz[ptr]*bsp_z[ptr] < 0) { left = -1; }

    //  Do far branch

    if(left < 0) {
      bsp_reflect_recurse(bsp_left[ptr], refl);
    } else {
      bsp_reflect_recurse(bsp_right[ptr], refl);
    }

    //  Draw polygon

    poly2_clear();

    i = ptr;
    x = bsp_poly_x[i][bsp_poly_num[i]-1];
    y = bsp_poly_y[i][bsp_poly_num[i]-1];
    z = bsp_poly_z[i][bsp_poly_num[i]-1];

    npa = bsp_nx[refl]*(x-bsp_x[refl]) +
          bsp_ny[refl]*(y-bsp_y[refl]) +
          bsp_nz[refl]*(z-bsp_z[refl]);

    x -= 2*bsp_nx[refl]*npa;
    y -= 2*bsp_ny[refl]*npa;
    z -= 2*bsp_nz[refl]*npa;

    sx = (image_w*0.5) + 100.0*x/z;
    sy = (image_h*0.5) - 100.0*y/z;

    for(j=0; j<bsp_poly_num[i]; j++) {
      xx = x; yy = y; zz = z;
      sxx = sx; syy = sy;

      x = bsp_poly_x[i][j];
      y = bsp_poly_y[i][j];
      z = bsp_poly_z[i][j];

      npa = bsp_nx[refl]*(x-bsp_x[refl]) +
            bsp_ny[refl]*(y-bsp_y[refl]) +
            bsp_nz[refl]*(z-bsp_z[refl]);

      x -= 2*bsp_nx[refl]*npa;
      y -= 2*bsp_ny[refl]*npa;
      z -= 2*bsp_nz[refl]*npa;

      sx = (image_w*0.5) + 100.0*x/z;
      sy = (image_h*0.5) - 100.0*y/z;

      poly2_line(sxx, syy, sx, sy);
    }

    if(bsp_object[ptr] == 2) {

      x = bsp_nx[ptr];
      y = bsp_ny[ptr];
      z = bsp_nz[ptr];

      npa = bsp_nx[refl]*x +
            bsp_ny[refl]*y +
            bsp_nz[refl]*z;

      x -= 2*bsp_nx[refl]*npa;
      y -= 2*bsp_ny[refl]*npa;
      z -= 2*bsp_nz[refl]*npa;

      col = (byte)(32+31*x);
      poly2_fill_col(col);
    }

    //  Do near branch

    if(left < 0) {
      bsp_reflect_recurse(bsp_right[ptr], refl);
    } else {
      bsp_reflect_recurse(bsp_left[ptr], refl);
    }

  }



//---- BSP_Recurse

  public void bsp_recurse(int ptr) {
    int i, j;
    int left;
    byte col;

    if(ptr == -1) { return; }

    left = 1;
    if(bsp_nx[ptr]*bsp_x[ptr] +
       bsp_ny[ptr]*bsp_y[ptr] +
       bsp_nz[ptr]*bsp_z[ptr] < 0) { left = -1; }

//    System.out.print(ptr);
//    System.out.print(",");
//    System.out.println(left);

    //  Do far branch

    if(left < 0) {
      bsp_recurse(bsp_left[ptr]);
    } else {
      bsp_recurse(bsp_right[ptr]);
    }

    //  Draw polygon

    poly_clear();

    i = ptr;
    for(j=0; j<bsp_poly_num[i]-1; j++)
      poly_line(bsp_poly_sx[i][j], bsp_poly_sy[i][j], bsp_poly_sx[i][j+1], bsp_poly_sy[i][j+1]);
    poly_line(bsp_poly_sx[i][j], bsp_poly_sy[i][j], bsp_poly_sx[i][0], bsp_poly_sy[i][0]);

    if(bsp_object[ptr] == 1) {
      poly_fill_col((byte)0);
    } else {
      col = (byte)(32+31*bsp_nx[ptr]);
      poly_fill_col(col);
    }

    //  If it's reflective do more stuff

    if(bsp_object[ptr] == 1) {
      if(left < 0) {
        bsp_reflect_recurse(bsp_right[ptr], ptr);
      } else {
        bsp_reflect_recurse(bsp_left[ptr], ptr);
      }

      col = (byte)(64+32+31*bsp_nx[ptr]);
      poly_fill_add(col);
    }

    //  Do near branch

    if(left < 0) {
      bsp_recurse(bsp_right[ptr]);
    } else {
      bsp_recurse(bsp_left[ptr]);
    }

  }



//---- BSP_Render

  public void bsp_render() {
    if(bsp_num > 0) bsp_recurse(0);
  }




//---- RENDER ROUTINES -----------------------------------------------------//

//---- Redraw

  int k=0;

  public void redraw() {

    int i, j, o;
    char r;
    int x, y;
    double sx1, sy1, sx2, sy2;

    //  Clear buffer

    for(i=0; i<image_w*image_h; i++) buf[i] = back[i];

    //  Change object positions

    if(pause == 0) {
      o1_rx += 1; o1_ry += 17; o1_rz -= 3;
      o2_rx -= 7; o2_ry -= 9; o2_rz += 4;
    }

    //  Transform all points in object 1

    maketrans(o1_rx, o1_ry, o1_rz);

    for(i=0; i<o1_numpoints; i++) {
      o1_tx[i] = m[0][0]*o1_x[i] + m[0][1]*o1_y[i] + m[0][2]*o1_z[i] + o1_cx;
      o1_ty[i] = m[1][0]*o1_x[i] + m[1][1]*o1_y[i] + m[1][2]*o1_z[i] + o1_cy;
      o1_tz[i] = m[2][0]*o1_x[i] + m[2][1]*o1_y[i] + m[2][2]*o1_z[i] + o1_cz;
                                                                   
      o1_sx[i] = (image_w*0.5) + 100.0*o1_tx[i]/o1_tz[i];
      o1_sy[i] = (image_h*0.5) - 100.0*o1_ty[i]/o1_tz[i];
/*
      x = (int)o1_sx[i];
      y = (int)o1_sy[i];

      if(x>=0 && x<image_w && y>=0 && y<image_h) {
        buf[x+image_w*y] = (byte)255;
      }
*/
    }

    for(i=0; i<o1_numfaces; i++) {
      o1_tnx[i] = m[0][0]*o1_nx[i] + m[0][1]*o1_ny[i] + m[0][2]*o1_nz[i];
      o1_tny[i] = m[1][0]*o1_nx[i] + m[1][1]*o1_ny[i] + m[1][2]*o1_nz[i];
      o1_tnz[i] = m[2][0]*o1_nx[i] + m[2][1]*o1_ny[i] + m[2][2]*o1_nz[i];
    }

    //  Transform all points in object 2

    maketrans(o2_rx, o2_ry, o2_rz);

    for(i=0; i<o2_numpoints; i++) {
      o2_tx[i] = m[0][0]*o2_x[i] + m[0][1]*o2_y[i] + m[0][2]*o2_z[i] + o2_cx;
      o2_ty[i] = m[1][0]*o2_x[i] + m[1][1]*o2_y[i] + m[1][2]*o2_z[i] + o2_cy;
      o2_tz[i] = m[2][0]*o2_x[i] + m[2][1]*o2_y[i] + m[2][2]*o2_z[i] + o2_cz;
                                                                   
      o2_sx[i] = (image_w*0.5) + 100.0*o2_tx[i]/o2_tz[i];
      o2_sy[i] = (image_h*0.5) - 100.0*o2_ty[i]/o2_tz[i];
/*
      x = (int)o2_sx[i];
      y = (int)o2_sy[i];

      if(x>=0 && x<image_w && y>=0 && y<image_h) {
        buf[x+image_w*y] = (byte)255;
      }
*/
    }

    for(i=0; i<o2_numfaces; i++) {
      o2_tnx[i] = m[0][0]*o2_nx[i] + m[0][1]*o2_ny[i] + m[0][2]*o2_nz[i];
      o2_tny[i] = m[1][0]*o2_nx[i] + m[1][1]*o2_ny[i] + m[1][2]*o2_nz[i];
      o2_tnz[i] = m[2][0]*o2_nx[i] + m[2][1]*o2_ny[i] + m[2][2]*o2_nz[i];
    }

    //  Empty BSP tree

    bsp_clear();

    //  Add object 1 to BSP

    bsp_add_o1( 0, 0,  2,  1);
    bsp_add_o1( 1, 1,  2,  3);
    bsp_add_o1( 2, 0,  1,  3);
    bsp_add_o1( 3, 3,  2,  0);
/*
*/

    //  Add object 2 to BSP

    bsp_add_o2( 0,  0,  4,  5,  1);
    bsp_add_o2( 4,  4,  8,  9,  5);
    bsp_add_o2( 8,  8, 12, 13,  9);
    bsp_add_o2(12, 12,  0,  1, 13);

    bsp_add_o2( 2,  2,  6,  7,  3);
    bsp_add_o2( 6,  6, 10, 11,  7);
    bsp_add_o2(10, 10, 14, 15, 11);
    bsp_add_o2(14, 14,  2,  3, 15);

/*
    for(i=0; i<8; i++) {
      System.out.print("bsp: (");
      System.out.print(bsp_nx[i]);
      System.out.print(", ");
      System.out.print(bsp_ny[i]);
      System.out.print(", ");
      System.out.print(bsp_nz[i]);
      System.out.print(")  :  ");
      System.out.println(bsp_nx[i]*bsp_x[i]+bsp_ny[i]*bsp_y[i]+bsp_nz[i]*bsp_z[i]);
    }

    System.out.print("left: (");
    for(i=0; i<8; i++) {
      System.out.print(bsp_left[i]);
      System.out.print("  ");
    }
    System.out.println("");

    System.out.print("right: (");
    for(i=0; i<8; i++) {
      System.out.print(bsp_right[i]);
      System.out.print("  ");
    }
    System.out.println("");
*/

    bsp_add_o2( 3,  3,  7,  4,  0);
    bsp_add_o2( 7,  7, 11,  8,  4);
    bsp_add_o2(11, 11, 15, 12,  8);
    bsp_add_o2(15, 15,  3,  0, 12);

    bsp_add_o2( 1,  1,  5,  6,  2);
    bsp_add_o2( 5,  5,  9, 10,  6);
    bsp_add_o2( 9,  9, 13, 14, 10);
    bsp_add_o2(13, 13,  1,  2, 14);

    //  Render BSP

    bsp_render();

    //  Put little cornery moving bit

    k = (k+1)&255;

    for(o=0, j=0; j<8; j++, o+=image_w-8) {
      for(i=0; i<8; i++) {
        buf[o++] = (byte)( (((k+i+j)&4)>>2)*64+63 );
      }
    }
/* * /
    for(i=0; i<16; i++) {
      sx1 = (image_w*0.5) + 100.0*bsp_x[i]/bsp_z[i];
      sy1 = (image_w*0.5) - 100.0*bsp_y[i]/bsp_z[i];
      sx2 = (image_w*0.5) + 100.0*(bsp_x[i]+bsp_nx[i])/(bsp_z[i]+bsp_nz[i]);
      sy2 = (image_w*0.5) - 100.0*(bsp_y[i]+bsp_ny[i])/(bsp_z[i]+bsp_nz[i]);
      line(sx1, sy1, sx2, sy2);
    }
/* */
    //  Update screen

    src.newPixels(0, 0, image_w, image_h);
  }



//---- END OF CLASS --------------------------------------------------------//

}