Sunday March 26, 2023
 home | about | contact | Donations

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

Render objects in order of Z

by Ben Kenwright

When we move to 3D, we have objects with different z values - now assuming where using a left handed drawing system, where positive z is into the screen.  This means that objects or points with lower z values, should get drawn before one's with higher z values.  Our solution is to use an ordered render - sometimes referred to as the 'Painters Algorithm'....as when a painter paints a scenery, he starts by painting the background, then working his way forwards...painting on top of his previous work when he needs to.

We'll use a similar approach - we'll sort our z values into order, then we'll render all our triangles with the highest average z value first and working backwards till we reach the triangle with the lowest average z value.  I've said average z value - as we will use an average z value for each triangle, which is nothing more than the 'mean' average.

To make managing the data a little less messy, I've created a 'Triangle' class, which holds the points and colour for our triangles.  This way its easier to keep track of our values and modify them.....I also added a RotateX, RotateY and RotateZ member functions, so that we an rotate the values stored in the triangle class.  We keep track of two sets of data - firstly the original data we put in the triangle class (x,y and z), and also the translated or rotated values (tx,ty and tz).  Here is what the class looks like:

 Code Snippet: 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;          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_;    }// 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;         }// 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(..)        }// End of class triangle

We create an array of 'Triangles' called m_tri, and then we are render to go into our update(..) loop.  First we reset our 'translated values' so that there back to there original state.  Then we follow by any rotation or maths operations we want to carry out on our translated values in our triangle array.  And finally, we call 'SortRenderOrder(..)' with the array of trangles, and it will sort the triangles in the array into order, so that when we render them...as I've said above...it will render in order highest z first.

I've done a cut out of the 'SortRenderOrder(..)' function to show you exactly what it does.  As this is what where concerned with here.  And its not very efficient, and you could improve it a bit more - but its simple to follow for this example, and won't slow our simple demo down to much.

 Code Snippet: ... 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(..) ....

Here is the whole body of the applet - I've commented out some of the larger code area's which I've already shown above - such as Triangle Class...and code such as drawTriangle(..) which basically draws a triangle using pixels.

You can download the full source code to look at it and compile it if you want...possibly tweak a few of the values.

 Applet Code: (render_order.java) Download Source Code /*********************************************************************************************/ /*                                                                                           */ /*  Java 3D Engine Basics Tutorials - Tut Render Order                                       */ /*  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 {    ....// Commented out }// End of class triangle       /*********************************************************************************************/ /*                                                                                           */ /*  Program Entry Point                                                                      */ /*                                                                                           */ /*********************************************************************************************/   public class render_order 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 = 2;   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];        m_tri[0] = new Triangle(-1.0f, -1.0f, -0.5f,                                0.0f,  1.0f,  -0.5f,                                1.0f, -1.0f,  -0.5f,                                Color.red);                                                                                        m_tri[1] = new Triangle(-1.0f, -1.0f, 0.5f,                                 0.0f,  1.0f, 0.5f,                                 1.0f, -1.0f, 0.5f,                                 Color.blue);   }// 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);   }// End of RenderTriangles(..)         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