home games dev
Lewpen.com»Research & Development»3D Graphics»Java 3D Engine»3D Pillars»White Outline (Cartoon Shading)

White Outline (Cartoon Shading)

Using a postprocessing effect which finds outlines using discontinuities in the Z-buffer

/ Source / Site3D.java

import java.awt.image.*;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Event;
import java.awt.Image;
import java.awt.Font;
import java.util.Vector;  //  http://java.sun.com/products/jdk/1.1/docs/api/java.util.Vector.html

/*

TODOs:

* Poly.line - clip z to horizontal edges of buffer
* Frame rate dependent time increments

*/



//---- Triangle rasteriser ---------------------------------------------------//

class Tri
{
  //- Output buffers

  private int w, h;
  private int[] buf;   //  Pixel buffer (0x00rrggbb)
  private int[] zbuf;  //  1/Z Buffer
  private int[] ibuf;  //  Index buffer (for shape picking)

  //- Rendering workspace

  private int ymin, ymax;
  private int xmin[], xmax[];
  private int zmin[], zmax[];

  //- Triangle parameters

  public double x1, y1, z1, r1, g1, b1, a1, u1, v1;
  public double x2, y2, z2, r2, g2, b2, a2, u2, v2;
  public double x3, y3, z3, r3, g3, b3, a3, u3, v3;

  //---- Tri

  public Tri(int[] buf, int[] zbuf, int w, int h)
  {
    this.w = w;
    this.h = h;
    this.buf = buf;
    this.zbuf = zbuf;
    
    xmin = new int[h];
    xmax = new int[h];

    zmin = new int[h];
    zmax = new int[h];
  }

  //---- col
  
  public void col(int c)
  {
    clear();
    line(x1, y1, z1, x2, y2, z2);
    line(x2, y2, z2, x3, y3, z3);
    line(x3, y3, z3, x1, y1, z1);
    fill_col(c);
  }

  //---- clearZBuf

  public void clearZBuf()
  {
    for(int i=0; i<w*h; i++) zbuf[i] = 0;
  }

  //---- clear

  public void clear()
  {
    int i;
 
    ymin = h;
    ymax = 0;
 
    for(i=0; i<h; i++)
    {
      xmin[i] = w;
      xmax[i] = 0;
    }
  }

  public void line(double x1, double y1, double z1, double x2, double y2, double z2)
  {
    double t;
    int x, y, z, i;
    int iy1, iy2;

    if(y2>y1) {

      // line going down - right hand side

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

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

      if(iy1 < ymin) { ymin = iy1; }
      if(iy2 > ymax) { ymax = iy2; }

      for(y=iy1; y<iy2; y++)
      {
        x = (int) (x1 + (x2-x1)*(y-y1)/(y2-y1));
        z = (int)(32*65536/(z1 + (z2-z1)*(y-y1)/(y2-y1)));
        if(x > w) { x = w; }  //  TODO:  Clip z to edge of screen too
        if(x < 0) { x = 0; }  //  TODO:  Clip z to edge of screen too
        xmax[y] =  x;
        zmax[y] =  z;
      }

    } else {

      // line going up - left hand side

      t = x1; x1 = x2; x2 = t;
      t = y1; y1 = y2; y2 = t;
      t = z1; z1 = z2; z2 = t;

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

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

      if(iy1 < ymin) { ymin = iy1; }
      if(iy2 > ymax) { ymax = iy2; }

      for(y=iy1; y<iy2; y++)
      {
        x = (int) (x1 + (x2-x1)*(y-y1)/(y2-y1));
        z = (int)(32*65536/(z1 + (z2-z1)*(y-y1)/(y2-y1)));
        if(x > w) { x = w; }  //  TODO:  Clip z to edge of screen too
        if(x < 0) { x = 0; }  //  TODO:  Clip z to edge of screen too
        xmin[y] = x;
        zmin[y] = z;
      }

    }

  }

  public void fill_col(int c)
  {
    int x, y, z, zd=0;
    int o, oo;

    oo = w*ymin;

    for(y=ymin; y<ymax; y++)
    {
      o = oo + xmin[y];
      
      z = zmin[y];
      if(xmax[y] > xmin[y]) zd = (zmax[y]-z)/(xmax[y]-xmin[y]);
      
      for(x=xmin[y]; x<xmax[y]; x++)
      {
        if(z > zbuf[o])
        {
          buf[o] = c;
          zbuf[o] = z;
        }
        z += zd;

        o++;
      }

      oo += w;
    }

  }

}


//---- Polygon rasteriser ----------------------------------------------------//

class Poly
{
  int w, h;
  int[] buf;
  int[] zbuf;

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

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

  public Poly(int[] buf, int[] zbuf, int w, int h)
  {
    this.w = w;
    this.h = h;
    this.buf = buf;
    this.zbuf = zbuf;
    
    poly_xmin = new int[h];
    poly_xmax = new int[h];

    poly_zmin = new int[h];
    poly_zmax = new int[h];
  }

  public void clearZBuf()
  {
    for(int i=0; i<w*h; i++) zbuf[i] = 0;
  }

  public void clear()
  {
    int i;
 
    poly_ymin = h;
    poly_ymax = 0;
 
    for(i=0; i<h; i++)
    {
      poly_xmin[i] = w;
      poly_xmax[i] = 0;
    }
  }

  public void line(double x1, double y1, double z1, double x2, double y2, double z2)
  {
    double t;
    int x, y, z, i;
    int iy1, iy2;

    if(y2>y1) {

      // line going down - right hand side

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

      if(iy1 >= h) { return; }
      if(iy2 <= 0) { return; }
      if(iy1 < 0) { iy1 = 0; }
      if(iy2 > h) { iy2 = 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));
        z = (int)(4*4096*65536/ ( ( z1 + (z2-z1) * (y-y1) / (y2-y1) )) );
        if(x > w) { x = w; }  //  TODO:  Clip z to edge of screen too
        if(x < 0) { x = 0; }  //  TODO:  Clip z to edge of screen too
        poly_xmax[y] =  x;
        poly_zmax[y] =  z;
      }

    } else {

      // line going up - left hand side

      t = x1; x1 = x2; x2 = t;
      t = y1; y1 = y2; y2 = t;
      t = z1; z1 = z2; z2 = t;

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

      if(iy1 >= h) { return; }
      if(iy2 <= 0) { return; }
      if(iy1 < 0) { iy1 = 0; }
      if(iy2 > h) { iy2 = 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));
        z = (int)(4*4096*65536/(z1 + (z2-z1)*(y-y1)/(y2-y1)));
        if(x > w) { x = w; }  //  TODO:  Clip z to edge of screen too
        if(x < 0) { x = 0; }  //  TODO:  Clip z to edge of screen too
        poly_xmin[y] = x;
        poly_zmin[y] = z;
      }

    }

  }

  public void fill_col(int c)
  {
    int x, y, z, zd=0;
    int o, oo;
    
    ////////
    c = 0x00FFFFFF;
    ////////

    oo = w*poly_ymin;

    //- Solid fill

    if((c & 0xFF000000) == 0)
    {
      for(y=poly_ymin; y<poly_ymax; y++, oo += w)
      {
        z = poly_zmin[y]; if(poly_xmax[y] > poly_xmin[y]) zd = (poly_zmax[y]-z)/(poly_xmax[y]-poly_xmin[y]);
        for(x=poly_xmin[y], o = oo + poly_xmin[y]; x<poly_xmax[y]; x++, o++, z += zd) if(z > zbuf[o]) { buf[o] = c; zbuf[o] = z; }
      }
    }
    
    //- Alpha fill

    else
    {
      for(y=poly_ymin; y<poly_ymax; y++, oo += w)
      {
        z = poly_zmin[y]; if(poly_xmax[y] > poly_xmin[y]) zd = (poly_zmax[y]-z)/(poly_xmax[y]-poly_xmin[y]);
        for(x=poly_xmin[y], o = oo + poly_xmin[y]; x<poly_xmax[y]; x++, o++, z += zd) if(z > zbuf[o])
        {
          if(zbuf[o] > 0) buf[o] = ((buf[o]>>1)&0x7F7F7F)+((c>>1)&0x7F7F7F);
                     else buf[o] = c;
          zbuf[o] = z;
        }
      }
    }

  }

}



//---- 3D Renderer -----------------------------------------------------------//



//---- Surface

class Surface
{

  public Surface()
  {
  
  }

}



//---- Scene

class Scene
{
  //---- Data

  //- Camera

  private double camera_x=0, camera_y=0, camera_z=-11;  //  -17 to fit whole scene in, -11 for close-up
  private double camera_rx=0, camera_ry=0;

  //- Output buffer

  private int[] buf = null;
  private int[] zbuf = null;
  private int w = 0, h = 0;

  private Poly poly;

  int bg_col = 0x00FFFFFF, bg_r=0, bg_g=0, bg_b=0;

  //- Shapes in this scene

  private Vector shapes;

  //- Transformation stack

  private Transform[] trans;
  private int numTransforms;

  protected double time = 0;

  //---- Main code

  public Scene()
  {
    shapes = new Vector();

    trans = new Transform[32];
    numTransforms = 0;

    setBackground(bg_col);
  }

  //---- addShape

  public void addShape(Shape s)
  {
    shapes.addElement(s);
  }

  //---- setBuffer

  public void setBuffer(int[] buf, int w, int h)
  {
    this.buf = buf; this.w = w; this.h = h;
    this.zbuf = new int[w*h];

    poly = new Poly(buf, zbuf, w, h);
  }

  //---- setBackground
  
  public void setBackground(int col)
  {
    bg_col = col;
    bg_r = (col>>16) & 0xFF;  
    bg_g = (col>> 8) & 0xFF;  
    bg_b = (col    ) & 0xFF;  
  }

  //---- setCamera

  public void setCamera(double x, double y, double z, double rx, double ry)
  {
    camera_x = x; camera_y = y; camera_z = z;
    camera_rx = rx; camera_ry = ry;
  }

  //---- transformPoint
  
  public void transformPoint(double p[], double t[], double s[])
  {
    //  Transform point

    trans[numTransforms-1].multiply(p, t);

    //  Screen space point

    if(s != null)
    {
      s[0] = w*0.5 + 250.0*t[0]/(t[2]);
      s[1] = h*0.5 - 250.0*t[1]/(t[2]);
      s[2] = t[2];
    }
  }

  //---- plotPoint

  public void plotPoint(double sx, double sy)
  {
//    if(sx >= 0 && sx < w && sy >= 0 && sy < h) buf[(int)sx + w*(int)sy] = 0xFFFFFF;

    if(sx >= 0 && sx < w-1 && sy >= 0 && sy < h-1)
    {
      double dx = sx-(int)sx;
      double dy = sy-(int)sy;

      int o = (int)sx + w*(int)sy;
      
      int c00 = (int)(255*Math.sqrt((1-dx)*(1-dy)));
      int c01 = (int)(255*Math.sqrt((  dx)*(1-dy)));
      int c10 = (int)(255*Math.sqrt((1-dx)*(  dy)));
      int c11 = (int)(255*Math.sqrt((  dx)*(  dy)));

      buf[o    ] = c00 + (c00<<8) + (c00 << 16);
      buf[o+1  ] = c01 + (c01<<8) + (c01 << 16);
      buf[o+w  ] = c10 + (c10<<8) + (c10 << 16);
      buf[o+w+1] = c11 + (c11<<8) + (c11 << 16);
    }
  }

  //---- drawLine
  
  public void drawLine(double x1, double y1, double x2, double y2)
  {
    double dx = x2-x1;
    double dy = y2-y1;
    
    if(dx < 0) dx =- dx;
    if(dy < 0) dy =- dy;
    
    if(dx > dy)
    {
      dx = 1/dx;
      if(x2 < x1)
      {
        double y = y1; y1 = y2; y2 = y;
        double x = x1; x1 = x2; x2 = x;
      }
      for(int i=(int)(x1+1); i<=(int)x2; i++) plotPoint(i, y1 + (y2-y1)*(i-x1)*dx);
    }
    else
    {
      dy = 1/dy;
      if(y2 < y1)
      {
        double y = y1; y1 = y2; y2 = y;
        double x = x1; x1 = x2; x2 = x;
      }
      for(int i=(int)(y1+1); i<=(int)y2; i++) plotPoint(x1 + (x2-x1)*(i-y1)*dy, i);
    }
    

  }

  //---- drawFace
  
  public void drawFace(double[] s1, double[] s2, double[] s3, int c)
  {
    poly.clear();
    poly.line(s1[0], s1[1], s1[2], s2[0], s2[1], s2[2]);
    poly.line(s2[0], s2[1], s2[2], s3[0], s3[1], s3[2]);
    poly.line(s3[0], s3[1], s3[2], s1[0], s1[1], s1[2]);
    poly.fill_col(c);
  }

  //---- transform

  public void transform(Transform t)
  {
    trans[numTransforms] = new Transform(trans[numTransforms-1]);
    trans[numTransforms].transform(t);
    numTransforms ++;
  }

  //---- untransform

  public void untransform()
  {
    numTransforms --;
  }

  //---- render

  public void render()
  {
    //- Sort out time
    
    time += 0.05;  //  Assume 20 fps

    //- Set up transformation stack

    trans[0] = new Transform();

    trans[0].translate(-camera_x, -camera_y, -camera_z);
    trans[0].rotateY(-camera_ry);
    trans[0].rotateX(-camera_rx);

    numTransforms = 1;

    //- Clear buffer

    int size = w*h;
    int i, j;

/* */
    for(i=0; i<size; i++) buf[i] = bg_col;
/* */

/* * /
    for(i = 1+w; i < size-w-1; i++)
    {
      buf[i] = 0;
      buf[i] += (buf[i-1]>>2)&0x003F3F3F;
      buf[i] += (buf[i+1]>>2)&0x003F3F3F;
      buf[i] += (buf[i-w]>>2)&0x003F3F3F;
      buf[i] += (buf[i+w]>>2)&0x003F3F3F;
    }
/* */
    
    poly.clearZBuf();

    //- Render shapes

    for(i=0; i<shapes.size(); i++) ((Shape)shapes.elementAt(i)).render();
    
    //- Post-processing effects
    
    int o;
    
    int d, dd, ddd;
    int r, g, b;

    int[][] gauss =
    {
      { 1,  5,  9,  5,  1 },
      { 5, 16, 27, 16,  5 },
      { 9, 27, 44, 27,  9 },
      { 5, 16, 27, 16,  5 },
      { 1,  5,  9,  5,  1 }
    };

    int m, n;

    int rad = 4;
    
    for(j=rad; j<h-rad; j++) for(i=rad, o=j*w+i; i<w-rad; i++, o++)
    {
      //  Calculate difference
      
      d  = (zbuf[o-1]>>2) + (zbuf[o+1]>>2) + (zbuf[o-w]>>2) + (zbuf[o+w]>>2);

      d -= zbuf[o];
      if(d < 0) d = 0;
      
      d = (d)>>15;
      if(d > 256) d = 256;
      
      d = 256-d;
      
      //  Calculate rate of change
/*
      dd  = (zbuf[o-1]>>3) + (zbuf[o+1]>>3) + (zbuf[o-w]>>3) + (zbuf[o+w]>>3);
      dd += (zbuf[o-2]>>3) + (zbuf[o+2]>>3) + (zbuf[o-w-w]>>3) + (zbuf[o+w+w]>>3);

      dd -= zbuf[o];
      if(dd < 0) dd = 0;
      
      dd = (dd)>>17;
      if(dd > 256) dd = 256;
      
      dd = 256-dd;
*/     
      //  Calculate 3rd order
/*
      ddd  = (zbuf[o-1]>>4) + (zbuf[o+1]>>4) + (zbuf[o-w]>>4) + (zbuf[o+w]>>4);
      ddd += (zbuf[o-2]>>4) + (zbuf[o+2]>>4) + (zbuf[o-w-w]>>4) + (zbuf[o+w+w]>>4);
      ddd += (zbuf[o-3]>>4) + (zbuf[o+3]>>4) + (zbuf[o-w-w-w]>>4) + (zbuf[o+w+w+w]>>4);
      ddd += (zbuf[o-4]>>4) + (zbuf[o+4]>>4) + (zbuf[o-w-w-w-w]>>4) + (zbuf[o+w+w+w+w]>>4);

      ddd -= zbuf[o];
      if(ddd < 0) ddd = 0;
      
      ddd = (ddd)>>16;
      if(dd > 256) ddd = 256;
      
      ddd = 256-ddd;
*/
      //  Apply filter

//      if(dd < d) d = dd;
//      d = (d>>1)+(dd>>1);

      if(d < 256)
      {
        r = (buf[o]>>16)&255;
        g = (buf[o]>> 8)&255;
        b = (buf[o]    )&255;

        r = (r*d)>>8;
        g = (g*d)>>8;
        b = (b*d)>>8;
      
        buf[o] = (r<<16)+(g<<8)+b;
      }
    }

    // System.out.println(zbuf[w*h/2]);

    //- Done
  }
}



//---- Shape

class Shape
{
  //---- Data

  Scene scene = null;
  public Transform trans = null;

  int numPoints = 0;
  int numLines = 0;
  int numFaces = 0;

  double[][] p;         //  Points
  int[] lp1, lp2;       //  Lines
  int[] fp1, fp2, fp3;  //  Faces
  int[] col;            //  Face colours

  double[][] t;  //  Translated points
  double[][] s;  //  Screen points

  Vector shapes;

  //- Center point
  
  double[] op;
  double[] ot;
  double[] os;

  double fog;
  double fog_start = 100, fog_dist = 100;

  //---- Main code

  public Shape(Scene sc)
  {
    scene = sc;
    shapes = new Vector();

    trans = new Transform();

    p = new double[64][4];

    lp1 = new int[64];
    lp2 = new int[64];

    fp1 = new int[64];
    fp2 = new int[64];
    fp3 = new int[64];
    col = new int[64];

    t = new double[64][4];
    s = new double[64][4];
    
    op = new double[4];
    ot = new double[4];
    os = new double[4];

    op[0] = 0;
    op[1] = 0;
    op[2] = 0;
    op[3] = 1;
  }

  //---- addPoint

  public void addPoint(double x, double y, double z)
  {
    p[numPoints][0] = x;
    p[numPoints][1] = y;
    p[numPoints][2] = z;
    p[numPoints][3] = 1;

    numPoints ++;
  }

  public void addLine(int p1, int p2)
  {
    if(p1 == p2) return;
    if(p1 > p2) { int p = p1; p1 = p2; p2 = p; }

    lp1[numLines] = p1;
    lp2[numLines] = p2;
    
    numLines ++;
  }

  public void addFace(int p1, int p2, int p3)
  {
    fp1[numFaces] = p1;
    fp2[numFaces] = p2;
    fp3[numFaces] = p3;
    col[numFaces] = (p1*0x76D761A9 + p2*0x78421687 + p3*0xF76DE1A7) & 0x00FFFFFF;  //  Random colour if none specified
    
    numFaces ++;
  }

  public void addFace(int p1, int p2, int p3, int c)
  {
    fp1[numFaces] = p1;
    fp2[numFaces] = p2;
    fp3[numFaces] = p3;
    col[numFaces] = c;

    numFaces ++;
  }

  //---- addShape

  public void addShape(Shape s)
  {
    shapes.addElement(s);
  }

  //---- setTransform

  public void setTransform(Transform newtrans)
  {
    this.trans = null;
    this.trans = new Transform(newtrans);
  }

  //---- drawFace
  
  public void drawFace(double[] p1, double[] p2, double[] p3, int col)
  {
    if(p1[2] < 0.1 || p2[2] < 0.1 || p3[2] < 0.1) return;
    
    int a = (col>>24) & 0xFF;
    int r = (col>>16) & 0xFF;
    int g = (col>> 8) & 0xFF;
    int b = (col    ) & 0xFF;
    
    r = (int)(r*fog + scene.bg_r*(1-fog));
    g = (int)(g*fog + scene.bg_g*(1-fog));
    b = (int)(b*fog + scene.bg_b*(1-fog));
    
    col = (a<<24)+(r<<16)+(g<<8)+b;

    scene.drawFace(p1, p2, p3, col);
  }

  //---- render

  public void render()
  {
    int i;

    //  Set up transformation

    scene.transform(trans);

    //  Calculate position of shape origin
    
    scene.transformPoint(op, ot, os);

    //  Calculate fogging for this object

    fog = 1 - (os[2]-fog_start)/fog_dist;
    
    if(fog < 0) fog = 0;
    if(fog > 1) fog = 1;

    //  Render sub-shapes
    
    for(i=0; i<shapes.size(); i++) ((Shape)shapes.elementAt(i)).render();
    
    //  Transform points
    
    for(i=0; i<numPoints; i++) scene.transformPoint(p[i], t[i], s[i]);

    //  Render this shape

    if(numLines+numFaces == 0) for(i=0; i<numPoints; i++) scene.plotPoint(s[i][0], s[i][1]);
    if(numFaces == 0)          for(i=0; i<numLines ; i++) scene.drawLine (s[lp1[i]][0], s[lp1[i]][1], s[lp2[i]][0], s[lp2[i]][1]);
                               for(i=0; i<numFaces ; i++) drawFace (s[fp1[i]], s[fp2[i]], s[fp3[i]], col[i]);
    
    //  Revert transformation
    
    scene.untransform();
  }
  
}



//---- This scene ------------------------------------------------------------//



//---- Square

class Square extends Shape
{
  public Square(Scene sc)
  {
    super(sc);

    addPoint(-1, 0, -1);
    addPoint(-1, 0,  1);
    addPoint( 1, 0,  1);
    addPoint( 1, 0, -1);
    
    addLine(0, 1);
    addLine(1, 2);
    addLine(2, 3);
    addLine(3, 4);

    addFace(0, 1, 2);
    addFace(2, 3, 0);
  }
}



//---- Cube

class Cube extends Shape
{
  public Cube(Scene sc)
  {
    super(sc);
    makeCube(sc, 0x999999);  
  }

  public Cube(Scene sc, int col)
  {
    super(sc);
    makeCube(sc, col);
  }
  
  private void makeCube(Scene sc, int col)
  {
//    System.out.println("Creating Cube");

    addPoint(-1, -1, -1);  //  0
    addPoint(-1, -1,  1);  //  1
    addPoint(-1,  1, -1);  //  2
    addPoint(-1,  1,  1);  //  3
    addPoint( 1, -1, -1);  //  4
    addPoint( 1, -1,  1);  //  5
    addPoint( 1,  1, -1);  //  6
    addPoint( 1,  1,  1);  //  7
    
    addLine(0, 1);
    addLine(0, 2);
    addLine(0, 4);
    addLine(1, 3);
    addLine(1, 5);
    addLine(2, 3);
    addLine(2, 6);
    addLine(3, 7);
    addLine(4, 5);
    addLine(4, 6);
    addLine(5, 7);
    addLine(6, 7);

    addFace(0, 1, 3, col);  //  0 1 3
    addFace(0, 5, 1, col);  //  0 1 5 !
    addFace(0, 3, 2, col);  //  0 2 3 !
    addFace(0, 2, 6, col);  //  0 2 6
    addFace(0, 4, 5, col);  //  0 4 5
    addFace(0, 6, 4, col);  //  0 4 6 !
    addFace(1, 7, 3, col);  //  1 3 7 !
    addFace(1, 5, 7, col);  //  1 5 7
    addFace(2, 3, 7, col);  //  2 3 7
    addFace(2, 7, 6, col);  //  2 6 7 !
    addFace(4, 7, 5, col);  //  4 5 7 !
    addFace(4, 6, 7, col);  //  4 6 7

  }
}



//---- Checks

class Checks extends Shape
{
  int size = 4;

  public Checks(Scene sc, int col1, int col2)
  {
    super(sc);
    
    int i, j;
    
    for(j=0; j<size+1; j++) for(i=0; i<size+1; i++)
    {
      addPoint(i-0.5*size, 0, j-0.5*size);
//      System.out.println(numPoints+" "+i+" "+j);
    }

    for(j=0; j<size; j++) for(i=0; i<size; i++)
    {
      int p = i+j*(size+1);
      int col = ((i^j)&1)==1 ? col1 : col2;
      addFace(p+size+2, p+1, p, col);
      addFace(p, p+size+1, p+size+2, col);
    }
  }

}



//---- Pillar

class Pillar extends Shape
{
  public Pillar(Scene sc)
  {
    super(sc);
//    System.out.println("Creating Pillar");

    Shape s;

    s = new Cube(sc, 0x336699);
    s.trans.scale(1.0, 0.2, 1.0);
    s.trans.translate(0.0, -2.0, 0.0);
    addShape(s);

    s = new Cube(sc, 0x003366);
    s.trans.scale(0.6, 1.8, 0.6);
    addShape(s);

    s = new Cube(sc, 0x336699);
    s.trans.scale(1.0, 0.2, 1.0);
    s.trans.translate(0.0, 2.0, 0.0);
    addShape(s);
  }
}



//---- LinkShape1

class LinkShape1 extends Shape
{
  public LinkShape1(Scene sc)
  {
    super(sc);
    
    Shape s;

    s = new Cube(sc, 0xFFCC00);
    s.trans.scale(0.3, 0.3, 0.3);
    s.trans.translate(-0.7, 0, 0);
    addShape(s);  

    s = new Cube(sc, 0xFFCC00);
    s.trans.scale(0.3, 0.3, 0.3);
    s.trans.translate( 0.7, 0, 0);
    addShape(s);  

    s = new Cube(sc, 0xFFFF00);
    s.trans.scale(0.3, 0.3, 0.3);
    s.trans.translate(0, -0.7, 0);
    addShape(s);  

    s = new Cube(sc, 0xFFFF00);
    s.trans.scale(0.3, 0.3, 0.3);
    s.trans.translate(0,  0.7, 0);
    addShape(s);  

    s = new Cube(sc, 0xFF9900);
    s.trans.scale(0.3, 0.3, 0.3);
    s.trans.translate(0, 0, -0.7);
    addShape(s);  

    s = new Cube(sc, 0xFF9900);
    s.trans.scale(0.3, 0.3, 0.3);
    s.trans.translate(0, 0,  0.7);
    addShape(s);  

    s = new Cube(sc, 0xCC6600);
    s.trans.scale(0.3, 0.3, 0.3);
    s.trans.translate(0, 0, 0);
    addShape(s);  
  }

}



//---- LinkShape2

class LinkShape2Corner extends Shape
{
  public LinkShape2Corner(Scene sc, int col)
  {
    super(sc);
    
    addPoint(1, 1, 1);
    addPoint(1, 0, 0);
    addPoint(0, 1, 0);
    addPoint(0, 0, 1);
    
    addFace(0, 1, 2, col);
    addFace(0, 2, 3, col);
    addFace(0, 3, 1, col);
  }

}

class LinkShape2 extends Shape
{
  public LinkShape2(Scene sc)
  {
    super(sc);
    
    int col1 = 0xFFFF00;
    int col2 = 0x0000FF;

    Shape s;

    s = new LinkShape2Corner(sc, col1);
    addShape(s);  

    s = new LinkShape2Corner(sc, col2);
    s.trans.rotateY(3.1415926*0.5);
    addShape(s);  

    s = new LinkShape2Corner(sc, col1);
    s.trans.rotateY(3.1415926*1.0);
    addShape(s);  

    s = new LinkShape2Corner(sc, col2);
    s.trans.rotateY(3.1415926*1.5);
    addShape(s);  

    s = new LinkShape2Corner(sc, col2);
    s.trans.rotateX(3.1415926*0.5);
    addShape(s);  

    s = new LinkShape2Corner(sc, col1);
    s.trans.rotateX(3.1415926*0.5);
    s.trans.rotateY(3.1415926*0.5);
    addShape(s);  

    s = new LinkShape2Corner(sc, col2);
    s.trans.rotateX(3.1415926*0.5);
    s.trans.rotateY(3.1415926*1.0);
    addShape(s);  

    s = new LinkShape2Corner(sc, col1);
    s.trans.rotateX(3.1415926*0.5);
    s.trans.rotateY(3.1415926*1.5);
    addShape(s);  

  }

}



//---- LinkShape3

class LinkShape3 extends Shape
{
  public LinkShape3(Scene sc)
  {
    super(sc);
    
    Shape s;

    for(int i=0; i<8; i++)
    {
      s = new Cube(sc, 0xFF0000 + i*0x001F1F);
      s.trans.scale(0.2, 0.4, 0.2);
      s.trans.translate(-0.8, 0, 0);
      s.trans.rotateY(i*2*3.1415926/8);
      addShape(s);
    }
  }
}



//---- LinkShape4

class LinkShape4 extends Shape
{
  public LinkShape4(Scene sc)
  {
    super(sc);
    
    Shape s;

    for(int i=0; i<8; i++)
    {
      int b0 = (i   )&1;
      int b1 = (i>>1)&1;
      int b2 = (i>>2)&1;

      s = new Cube(sc, b0*0xFF0000 + b1*0x00FF00 + b2*0x0000FF);
      s.trans.scale(0.4, 0.4, 0.4);
      s.trans.translate(b0*1.2-0.6, b1*1.2-0.6, b2*1.2-0.6);
      addShape(s);
    }
  }
}



//---- LinkShape5

class LinkShape5 extends Shape
{
  public LinkShape5(Scene sc)
  {
    super(sc);
    
    Shape s;

    for(int i=0; i<12; i++)
    {
      s = new Cube(sc, 0x660099 + i*0x001100);
      s.trans.scale(0.2, 0.2, 0.2);
      s.trans.translate(-0.8, (i-6)*0.14, 0);
      s.trans.rotateY(i*2*3.1415926/7);
      addShape(s);
    }
  }
}



//---- LinkShape6

class LinkShape6 extends Shape
{
  public LinkShape6(Scene sc)
  {
    super(sc);
    
    Shape s;

    for(int i=0; i<64; i+=5)
    {
      s = new LinkShape1(sc);//Cube(sc, 0x660099 + i*0x001100);

      s.trans.rotateZ( i*3.14159/52);
      s.trans.rotateX( i*3.14159/41);
      
      double scale = 0.9+0.6*Math.sin(i*3.14159/7);
      s.trans.scale(scale, scale, scale);      

      s.trans.translate(3+2*Math.sin(i*3.14159/12), 0*(i-32.0)/32.0, 0);
      s.trans.rotateY( i*3.14159/37);
      addShape(s);
    }
  }
}



//---- MainShape

class MainShape extends Shape
{
  Shape[] st;  
  boolean reflections = false;
  boolean rocky_floor = false;

  public MainShape(Scene sc)
  {
    super(sc);

    Shape s;
    int i, j;

    //- Reflected pillars

    if(reflections)
    {
      for(i=0; i<5; i++)
      {
        s = new Pillar(sc);
        s.trans.translate(-4.5, -4.4, 0.0);
        s.trans.rotateY(3.14159*2.0*i/5.0);
        addShape(s);
      }
    }
  
    //- Floor

    int col1 = 0xFFFFFF;
    int col2 = 0x000000;
    
    if(reflections) { col1 += 0x80000000; }
    if(reflections) { col2 += 0x80000000; }

    for(j=0; j<2; j++) for(i=0; i<2; i++)
    {
      if(rocky_floor)
      {
        s = new LinkShape6(sc);//Checks(sc, col1, col2);
        s.trans.scale(1, 0.3, 1);
        s.trans.translate(8*i-4, -2.0, 8*j-4);
        addShape(s);
      }

      s = new Checks(sc, col1, col2);
      s.trans.scale(2, 1, 2);
      s.trans.translate(8*i-4, -2.2, 8*j-4);
      addShape(s);
    }

    //- Pillars with things on them

    st = new Shape[5];

    st[0] = new LinkShape1(sc);
    st[1] = new LinkShape2(sc);
    st[2] = new LinkShape3(sc);
    st[3] = new LinkShape4(sc);
    st[4] = new LinkShape5(sc);

    for(i=0; i<5; i++)
    {
      s = new Pillar(sc);
      s.trans.translate(-4.5, 0.0, 0.0);
      s.trans.rotateY(3.14159*2.0*i/5.0);
      addShape(s);
      
      st[i].trans.scale(0.5, 0.5, 0.5);
      st[i].trans.translate(-4.5, 0.0, 0.0);
      st[i].trans.rotateY(3.14159*2.0*i/5.0);
      st[i].trans.translate(0.0, 2.95, 0.0);
      addShape(st[i]);
    }
    
  }

  int frameno = 0;

  public void render()
  {
    int i;

    double[] scale = {1, 1, 1, 1, 1};
    i = (frameno / 25)%5;

    scale[i] = 1 + Math.exp(-(frameno%25)*0.1)*Math.sin(frameno*3.14159/5);
    
    frameno ++;
    
    for(i=0; i<5; i++)
    {
      st[i].trans.identity();

      if(i != 0) st[i].trans.rotateX(scene.time * 0.18428);
      if(i != 1) st[i].trans.rotateY(scene.time * 0.38291);
      if(i != 2) st[i].trans.rotateZ(scene.time * 0.29219);
      if(i != 3) st[i].trans.rotateY(scene.time * 0.50291);
      if(i != 4) st[i].trans.rotateX(scene.time * 0.18428);

      st[i].trans.scale(1, scale[i], 1);

      st[i].trans.scale(0.5, 0.5, 0.5);
      st[i].trans.translate(-4.5, 0.0, 0.0);
      st[i].trans.rotateY(3.14159*2.0*i/5.0);
      st[i].trans.translate(0.0, 2.85, 0.0);
    }
    
    super.render();
  }

}



//---- MyScene

class MyScene extends Scene
{
  Shape s;
  Transform t;
  
  double rx=-0.6, ry=0, rz=0;
  
  public MyScene()
  {
//    System.out.println("Creating MyScene");

    s = new MainShape(this);
    addShape(s);
  }

  public void render()
  {
    t = new Transform();

    rx = -0.45 + 0.15 * Math.sin(time*3.14159/7.391);
    ry = 0.257 * time;
    rz = 0.157 * time;

/*
    t.rotateX(rx);
    t.rotateY(ry);
    t.rotateZ(rz);
    t.rotateX(rx-ry);
    t.rotateZ(rz-rx);
    t.rotateX(3.1415926*0.5);
*/

    t.rotateY(ry);
    t.rotateX(rx);    
    s.setTransform(t);

    super.render();  
  }

}



//---- Applet ----------------------------------------------------------------//

public class Site3D extends tinyptc
{
  MyScene scene;

  public void main(int width,int height)
  {
    int size = width * height;
    int buf[] = new int[size];

    scene = new MyScene();

    scene.setBuffer(buf, width, height);
    
    while (true)
    {
      scene.render();
      update(buf);
    }
  }

  int mouse_x = 0, mouse_y = 0;
  int mouse_oldx = 0, mouse_oldy = 0;
  boolean mouse_drag = true;

  public boolean mouseDown(Event evt, int x, int y)
  {
    mouse_oldx = x;
    mouse_oldy = y;
    mouse_x = x;
    mouse_y = y;
    mouse_drag = true;
    return true;
  }

  public boolean mouseDrag(Event e, int x, int y)
  {
    scene.ry += (x-mouse_x)*0.01;
    scene.rx -= (y-mouse_y)*0.01;
    
    if(scene.rx > -0.1) scene.rx = -0.1;
    if(scene.rx < -1.2) scene.rx = -1.2;

    mouse_x = x;
    mouse_y = y;
    return true;
  }

  public boolean mouseUp(Event evt, int x, int y)
  {
    mouse_drag = false;
    return true;
  }

}






class Site3D123 extends java.applet.Applet implements Runnable
{
  //- Data

  Thread runThread = null;

  static int imw = 320, imh = 240;
  Image im = null;
  Graphics img = null;
  int[][] buffer;

  MyScene myScene;

  //- Main code

  public void init()
  {
    System.out.println("LKS 3D Site v0.01");

    myScene = new MyScene();
  }

  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()
  {
    imw = size().width;
    imh = size().height;

    im = createImage(imw, imh);
    img = im.getGraphics();

    img.setColor(Color.black);
    img.fillRect(0, 0, imw, imh);

//    myScene.setBuffer(img, imw, imh);
    
    while(true)
    {
      repaint();
      try { runThread.sleep(10); } catch (InterruptedException e) { return; }
    }
  }

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

  int k = 0;

  public void paint(Graphics g)
  {
    if(im != null)
    {
      img.setColor(Color.black);
      img.fillRect(0, 0, imw, imh);
      
      myScene.render();

      g.drawImage(im, 0, 0, this);
      g.setColor((k++&1) == 0 ? Color.blue : Color.yellow);
      g.fillRect(0, 0, 10, 10);
    }
  }



  int mouse_x = 0, mouse_y = 0;
  int mouse_oldx = 0, mouse_oldy = 0;
  boolean mouse_drag = true;



  public boolean mouseDown(Event evt, int x, int y)
  {
    mouse_oldx = x;
    mouse_oldy = y;
    mouse_x = x;
    mouse_y = y;
    mouse_drag = true;
    return true;
  }


  
  public boolean mouseDrag(Event e, int x, int y)
  {
    mouse_x = x;
    mouse_y = y;

    repaint();
    return true;
  }



  public boolean mouseUp(Event evt, int x, int y)
  {
    mouse_drag = false;
    return true;
  }
  

}

Comments

Related Articles

Demonstration scene with reflection

Sponsored Links

Toys & Games:

Doggie Doo
Nerf Vortex
Monster High
Lagoona Hydration Station
Milky Bunny
Moshling Tree House
Lego Ninja Go Fire Temple
Fireman Sam Pontypandy Rescue
Rock Elmo
Star Wars Ultimate Force Lightsaber

Games

The Dodge Game
Flatspace

2-Player Games:

Quake 2D
Meteora

Puzzle Games:

Mini Tetris
Sudoku Solver

Development

3D Graphics:

3D Graphics Articles
WebGL Examples
Flash 3D Engine
Java 3D Engine

Development:

Programming Articles
Animation Demos
Game Development Examples

Links

iBuddy Social Network
Local Legends Football
PHP Charts & Graphs
CubeLogix Studios