Tuesday April 16, 2024
 home | contact | Support | Slice of Java. Its simple and its powerful...

 Slice of Java. Its simple and its powerful...

Pretty 3D Cube

by Ben Kenwright

This is a demonstration of the code that we have achieved up to now - using relatively simple techniques of perspective and Euler's rotation equations, we can create a cool 3D cube java demo.  Now if you click the mouse, you'll be able to rotate the cube.  Here is a screenshot of the applet:

To run the applet click <here>

Below is the code.  The main addition to this code is culling.  Culling is all about not rendering triangle faces that are facing away from us.  Culling is achieved by by using the normal of the triangle, and taking the dot product of our viewing direction with it.  As the dot product returns the cosine of the angle between the two directional vectors.  So if the angle is less than 90 degrees the cosine will be positive - else it will be negative.

I've cut and paste the code which is responsible for determining if we cull the triangle.

 Code Snippet - Culling Code ....  float DotProduct(float x0, float y0, float z0,                   float x1, float y1, float z1 )  {     return( x0*x1 + y0*y1 + z0*z1 );  }// End of DotProduct(..)     void CullTriangles( Triangle t[], int iNumTris )   {      // Our default camera is at 0,0,0 and faces in the positive z      // direction           for(int i=0; i

Its a lot of code now..hehehe.  But if you look at the applet code, you can basically break it down into separate pieces.  For example the render code and the triangle code and then there's small code functions for the various operations, such as Culling and Perspective.

 Applet code: cube.java (Download Source Code) /*********************************************************************************************/ /*                                                                                           */ /*  Java 3D Engine Basics Tutorials - Tut Cube                                               */ /*  Auth: bkenwright@xbdev.net                                                               */ /*                                                                                           */ /*                                                                                           */ /*********************************************************************************************/   import java.awt.image.*; import java.awt.*; import java.applet.*;   /*********************************************************************************************/ /*                                                                                           */ /*  class Triangle                                                                           */ /*  We need a way of representing our triangles...so that we can have a number of them       */ /*  and not have to deal with large numbers of arrays of values and things.  By putting      */ /*  them in a class each triangle is all nice and tidy.                                      */ /*                                                                                           */ /*********************************************************************************************/   class Triangle {    float x0, y0, z0;    float x1, y1, z1;    float x2, y2, z2;    Color c;       // Average z    float avg_z;       // Transformed coords    float tx0, ty0, tz0;    float tx1, ty1, tz1;    float tx2, ty2, tz2;    Color tc;       // Triangle Normal    float tnx, tny, tnz;       // Cull our triangle if we can't see it    boolean bCull;       public Triangle(){} // default constructor    public Triangle( float x0_, float y0_, float z0_,                     float x1_, float y1_, float z1_,                     float x2_, float y2_, float z2_,                     Color c_  )    {        tx0=x0=x0_; ty0=y0=y0_; tz0=z0=z0_;        tx1=x1=x1_; ty1=y1=y1_; tz1=z1=z1_;        tx2=x2=x2_; ty2=y2=y2_; tz2=z2=z2_;        tc = c = c_;        bCull = false;    }// End of Triangle(..) constructor      public void Translate( float x, float y, float z )    {                   tx0 += x;  ty0 += y;  tz0 += z;                   tx1 += x;  ty1 += y;  tz1 += z;                   tx2 += x;  ty2 += y;  tz2 += z;    }// End of Translate(..)       public void ResetCoords()    {      tx0=x0; ty0=y0; tz0=z0;      tx1=x1; ty1=y1; tz1=z1;      tx2=x2; ty2=y2; tz2=z2;      bCull = false;         }// End ResetCoords()       public void RotateX( float angle )    {       float cosA = (float)Math.cos(angle);       float sinA = (float)Math.sin(angle);             float temp_x, temp_y, temp_z;             temp_x =  tx0;       temp_y =  ty0*cosA - tz0*sinA;       temp_z =  ty0*sinA + tz0*cosA;       tx0 = temp_x;  ty0 = temp_y;  tz0 = temp_z;             temp_x =  tx1;       temp_y =  ty1*cosA - tz1*sinA;       temp_z =  ty1*sinA + tz1*cosA;       tx1 = temp_x;  ty1 = temp_y;  tz1 = temp_z;             temp_x =  tx2;       temp_y =  ty2*cosA - tz2*sinA;       temp_z =  ty2*sinA + tz2*cosA;       tx2 = temp_x;  ty2 = temp_y;  tz2 = temp_z;          }// End RotateX(..)      public void RotateY( float angle )    {       float cosA = (float)Math.cos(angle);       float sinA = (float)Math.sin(angle);             float temp_x, temp_y, temp_z;             temp_x =   tx0*cosA + tz0*sinA;       temp_y =   ty0;       temp_z =  -tx0*sinA + tz0*cosA;       tx0 = temp_x;  ty0 = temp_y;  tz0 = temp_z;             temp_x =   tx1*cosA + tz1*sinA;       temp_y =   ty1;       temp_z =  -tx1*sinA + tz1*cosA;       tx1 = temp_x;  ty1 = temp_y;  tz1 = temp_z;             temp_x =   tx2*cosA + tz2*sinA;       temp_y =   ty2;       temp_z =  -tx2*sinA + tz2*cosA;       tx2 = temp_x;  ty2 = temp_y;  tz2 = temp_z;                        }// End RotateY(..)       public void RotateZ( float angle )    {       float cosA = (float)Math.cos(angle);       float sinA = (float)Math.sin(angle);             float temp_x =  tx0*cosA - ty0*sinA;       float temp_y =  tx0*sinA + ty0*cosA;       float temp_z =  tz0;       tx0 = temp_x;  ty0 = temp_y;  tz0 = temp_z;             temp_x =  tx1*cosA - ty1*sinA;       temp_y =  tx1*sinA + ty1*cosA;       temp_z =  tz1;       tx1 = temp_x;  ty1 = temp_y;  tz1 = temp_z;             temp_x =  tx2*cosA - ty2*sinA;       temp_y =  tx2*sinA + ty2*cosA;       temp_z =  tz2;       tx2 = temp_x;  ty2 = temp_y;  tz2 = temp_z;    }// End RotateZ(..)       public void CalculateNormal()    {       // First we need to find the direction of our triangle       // using the cross product :)             // Lets calculate the two direction vectors       float vx0, vy0, vz0;       float vx1, vy1, vz1;             vx0 = tx2 - tx0;       vy0 = ty2 - ty0;       vz0 = tz2 - tz0;           vx1 = tx1 - tx0;       vy1 = ty1 - ty0;       vz1 = tz1 - tz0;             // Do the cross product       float nx, ny, nz;       nx =  vy0*vz1 - vz0*vy1;       ny = -vx0*vz1 + vz0*vx1;       nz =  vx0*vy1 - vy0*vx1;             // And we normalize it so its magnitude is 1       float length = nx*nx + ny*ny + nz*nz;                   length = (float)Math.sqrt(length);                                     nx /= length;                   ny /= length;                   nz /= length;                                     tnx = nx;                   tny = ny;                   tnz = nz;       }// End CalculateNormal(..)       }// End of class triangle       /*********************************************************************************************/ /*                                                                                           */ /*  Program Entry Point                                                                      */ /*                                                                                           */ /*********************************************************************************************/   public class cube extends Applet {    Image myImage;    Graphics offScreen;      int mouse_x_left=0;    int mouse_y_left=0;      int mouse_x_right=0;    int mouse_y_right=0;            int m_NumTris = 12;    Triangle[] m_tri;      public void init()    {       Dimension appletSize = this.getSize();       myImage = createImage(appletSize.width,appletSize.height);       offScreen = myImage.getGraphics();               m_tri = new Triangle[m_NumTris];       // Front       m_tri[0] = new Triangle(-1.0f, -1.0f, -1.0f,  -1.0f, 1.0f, -1.0f,  1.0f,  1.0f, -1.0f,  Color.red);       m_tri[1] = new Triangle(-1.0f, -1.0f, -1.0f,   1.0f, 1.0f,-1.0f,   1.0f, -1.0f, -1.0f,  Color.red);                               // Back       m_tri[2] = new Triangle(-1.0f, -1.0f,  1.0f,  1.0f,  1.0f, 1.0f,  -1.0f, 1.0f,  1.0f,  Color.green);       m_tri[3] = new Triangle(-1.0f, -1.0f,  1.0f,  1.0f, -1.0f, 1.0f,   1.0f, 1.0f,  1.0f,  Color.green);                                                                  // Bottom       m_tri[4] = new Triangle(-1.0f, -1.0f,  -1.0f,   1.0f, -1.0f, 1.0f,   -1.0f, -1.0f, 1.0f,   Color.blue);       m_tri[5] = new Triangle(-1.0f, -1.0f,  -1.0f,   1.0f, -1.0f, -1.0f,   1.0f, -1.0f,  1.0f,  Color.blue);              // Top       m_tri[6] = new Triangle(-1.0f,  1.0f,  -1.0f,  -1.0f, 1.0f, 1.0f,   1.0f,  1.0f, 1.0f,  Color.orange);       m_tri[7] = new Triangle(-1.0f,  1.0f,  -1.0f,   1.0f, 1.0f,  1.0f,  1.0f, 1.0f, -1.0f,  Color.orange);           // Left       m_tri[8] = new Triangle(-1.0f, -1.0f,  -1.0f,   -1.0f,  1.0f, 1.0f,   -1.0f, 1.0f, -1.0f,  Color.yellow);       m_tri[9] = new Triangle(-1.0f, -1.0f,  -1.0f,   -1.0f, -1.0f, 1.0f,   -1.0f, 1.0f,  1.0f,  Color.yellow);           // Right       m_tri[10] = new Triangle(1.0f, -1.0f,  -1.0f,   1.0f, 1.0f, -1.0f,   1.0f,  1.0f, 1.0f,  Color.pink);       m_tri[11] = new Triangle(1.0f, -1.0f,  -1.0f,   1.0f, 1.0f,  1.0f,   1.0f, -1.0f, 1.0f,  Color.pink);            }// End of init(..)     public void RenderTriangle(Triangle t)   {       ScreenPerspective( myImage,                          t.tx0, t.ty0, t.tz0,                          t.tx1, t.ty1, t.tz1,                          t.tx2, t.ty2, t.tz2,                          t.tc);   }     void SortRenderOrder(Triangle t[], int iNumTris)   {      // First lets generate an average z for each triangle      float average_z;      for( int i=0; i t[inner].avg_z )             {                Triangle temp = t[outer];                t[outer] = t[inner];                t[inner] = temp;             }// End if(..)         }// End inner for loop      }// End outer for loop        }// SortRenderOrder(..)     void RenderAllTriangles(Triangle t[], int iNumTris)   {      for(int i=0; i dx_right )          {             t = dx_left;             dx_left = dx_right;             dx_right = t;             s=1;          }//End if                   for(float y=y0; y 0 ? 1 : -1;             int ydir = dy > 0 ? 1 : -1;                         dx = dx*xdir;             dy = dy*ydir;                                     float incx =  dx / dy;             float incy =  dy / dx;                         incx *= xdir;             incy *= ydir;                         if( dy > dx )             {                         for( int y=0; y