/*********************************************************************************************/
/*
*/
/* 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<iNumTris; i++ )
{
average_z = t[i].tz0 + t[i].tz1 + t[i].tz2;
average_z /= 3.0f;
t[i].avg_z = average_z;
}//
End for loop
// Now lets do a bruit force sort, so each triangle is in
order
// of our average z
for(
int
outer=0; outer<iNumTris; outer++ )
{
for(
int
inner=0; inner<iNumTris; inner++ )
{
if(
t[outer].avg_z > 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<iNumTris; i++)
{
if(t[i].bCull
==
false
)
RenderTriangle( t[i] );
}//
End for loop
}//
End of RenderAllTriangles(..)
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<iNumTris; i++)
{
float
cosA = DotProduct( t[i].tnx, t[i].tny, t[i].tnz,
0.0f, 0.0f, 1.0f );
if(
cosA <= 0.0f )
t[i].bCull =
true;
else
t[i].bCull =
false;
}//End
for loop
}//
End CullTriangles(..)
void
CalculateNormals(Triangle t[],
int
iNumTris)
{
for(
int
i=0; i<iNumTris; i++ )
{
t[i].CalculateNormal();
}//
End for loop
}//
End of CalculateNormals(..)
void
ResetAllCoords(Triangle t[],
int
iNumTris)
{
for(int
i=0; i<iNumTris; i++)
{
t[i].ResetCoords();
}//
End for loop
}//
End of ResetAllCoords(..)
float
m_AngleX = 0.0f;
float
m_AngleY = 0.0f;
float
m_AngleZ = 0.0f;
public
void
paint(Graphics g)
{
// Clear screen
offScreen.setColor(Color.white);
offScreen.fillRect(0,0,this.getSize().width,this.getSize().height);
Dimension appletSize =
this.getSize();
int
width = appletSize.width;
int
height = appletSize.height;
ResetAllCoords( m_tri, m_NumTris );
// Lets rotate all of the triangles and translate them back into the horizon
for(int
i=0; i<m_NumTris; i++)
{
m_tri[i].RotateY(m_AngleY);
m_tri[i].RotateZ(m_AngleZ);
m_tri[i].RotateX(m_AngleX);
m_tri[i].Translate(0.0f, 0.0f, 3.0f);
}
m_AngleY += 0.09f;
m_AngleZ += 0.12f;
m_AngleX += 0.06f;
CalculateNormals( m_tri, m_NumTris );
CullTriangles( m_tri, m_NumTris );
SortRenderOrder( m_tri, m_NumTris );
RenderAllTriangles( m_tri, m_NumTris );
//offScreen.drawLine( mouse_x_left+10, mouse_y_left+10,
// mouse_x_right+10, mouse_y_right+10);
g.drawImage(myImage,0,0,this);
}//
End of paint(..)
public
void
update(Graphics g){ paint(g); };
public
void
start(){ repaint(); };
public
void
ScreenPerspective( Image image,
float
x0,
float
y0,
float
z0,
float
x1,
float
y1,
float
z1,
float
x2,
float
y2,
float
z2,
Color c )
{
// This is where we'll put our final values that we calculate
// we only have an x and a y, as we'll convert our x,y and
z into
// a screen coordinate square which has all the sizing and
things
// done to it already
// Get the screen size of our applet
Dimension appletSize =
this.getSize();
int
width = appletSize.width;
int
height = appletSize.height;
//if( (z0<1) && (z1<1) && (z2<1) )
// return;
//if( z0 < 1 ) z0 = 1;
//if( z1 < 1 ) z1 = 1;
//if( z2 < 1 ) z2 = 1;
// Something worth noting - we have a pole at z=0...as when we
// create a perspective ..which is x_per = d*x/z for
example, if
// z is 0 or very close to zero, we'll get an infinit
number...so
// if we have a z value less than 1 we'll just round z to
1 and it
// will get clipped later down the line.
// Whats this pTan stuff for?...well we can't just clip the z value
// to 1...as what if its on a really steep angle...so what
we do, is
// we work out a new value for x and z and then clip it to
the near
// clip plane :)
// Add some clipping code so Z is clipped to the near view plane
// ++ //
// From x,y,z to x,y for the screen.
// -1-Perspective Conversion
// Focul point of 1 (Normalised view plane 90 degrees)
float
d = 1.0f;
float
Perspective_x0 = -d*x0 / z0;
float
Perspective_x1 = -d*x1 / z1;
float
Perspective_x2 = -d*x2 / z2;
// its between -1 to 1...so we need to scale it to 0 to 1...and scale to
// the size of the screen.
Perspective_x0 += 1;
Perspective_x1 += 1;
Perspective_x2 += 1;
// So now its between 0 and 2 and not -1 to 1;
Perspective_x0 *= 0.5;
Perspective_x1 *= 0.5;
Perspective_x2 *= 0.5;
// -2- Now we scale it to the size of the screen
float
Screen_x0 = (width-1)*Perspective_x0;
float
Screen_x1 = (width-1)*Perspective_x1;
float
Screen_x2 = (width-1)*Perspective_x2;
// Add some clipping to make sure its on the screen here
// ++ //
// Now we do the same for the y value to convert it
// to screen coordinates … its exactly the same as for the
x above,
// but I've combined all the stages into a single line for
each
// one
// Note y1 is the top of square, and y2 is the bottom of
our square
float
Screen_y0 = ((-d*y0 / z0)+1)*0.5f *(height-1);
float
Screen_y1 = ((-d*y1 / z1)+1)*0.5f *(height-1);
float
Screen_y2 = ((-d*y2 / z2)+1)*0.5f *(height-1);
// Debug information
/*
System.out.println( "Color: " + c );
System.out.println( "x0: " + Screen_x0 + " y0: " +
Screen_y0 );
System.out.println( "x1: " + Screen_x1 + " y1: " +
Screen_y1 );
System.out.println( "x2: " + Screen_x2 + " y2: " +
Screen_y2 );
System.out.println("");
*/
// Render our data
drawTriangle( image,
Screen_x0, Screen_y0,
Screen_x1, Screen_y1,
Screen_x2, Screen_y2,c );
}//
End ScreenPerspective(...)
public
void
drawTriangle( Image image,
float
x0,
float
y0,
float
x1,
float
y1,
float
x2,
float
y2,
Color c )
{
//First we sort our points out
into y order...where it goes
// 0
// 2
// 1
float
t;
// temp variable
if( y1 < y0 )
{
t = y0;
y0 = y1;
y1 = t;
t = x0;
x0 = x1;
x1 = t;
}
if( y2 < y0 )
{
t = y0;
y0 = y2;
y2 = t;
t = x0;
x0 = x2;
x2 = t;
}
if(
y1 < y2 )
{
t = y1;
y1 = y2;
y2 = t;
t = x1;
x1 = x2;
x2 = t;
}
// Next part...
// Render top of triangle
float
x_left = x0;
float
x_right = x0;
float
dy_top = y2 - y0;
float
dy_bottom = y1 - y2;
float
dx_left = x2 - x0;
// to the middle then we change it
float
dx_right = x1 - x0;
// all the way to the bottom :)
//int dx_left_dir = dx_left <
0 ? -1 : 1;
//int dx_right_dir = dx_right <
0 ? -1 : 1;
//dx_left *= dx_left_dir;
//dx_right *= dx_right_dir;
//dx_left = dx_left / dy_top;
//dx_right = dx_right / dy_top;
dx_left = (x2 - x0) / (y2 -
y0);
dx_right = (x1 - x0) / (y1 -
y0);
int
s = 0;
if( dx_left > dx_right )
{
t = dx_left;
dx_left = dx_right;
dx_right
= t;
s=1;
}//End if
for(float
y=y0; y<y2; y++)
{
for(float
x=x_left; x<x_right; x++)
{
setPixel(image, (int)x,
(int)y,
c );
}//
End inner for loop
x_left += dx_left;
x_right += dx_right;
}// End outer for loop
// **IMPORTANT UPDATE **
// Its important to check that
the top of the triangle exists...as its af flat top
// then we have to make sure our
bottom is okay :) Not the added lines of code
// so its not x0 for the
starting point.
// Now for the bottom of the
triangle
if( s == 0 )
{
dx_left = (x1 - x2) / (y1 -
y2);
x_left = x2;
}
else
{
dx_right = (x1 - x2) / (y1 -
y2);
x_right = x2;
}
for(float
y=y2; y<y1; y++)
{
for(float
x=x_left; x<x_right; x++)
{
setPixel(image, (int)x,
(int)y,
c);
}
x_left += dx_left;
x_right += dx_right;
}// End outer for loop
}//End
of drawTriangle(..)
public
void
drawLine(Image image,
int
x0,
int
y0,
int
x1,
int
y1, Color c)
{
float
sx = (float)x0;
float
sy = (float)y0;
float
ex = (float)x1;
float
ey = (float)y1;
float
dx = ex-sx;
float
dy = ey-sy;
int
xdir = dx > 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<dy; y++)
{
setPixel(image, (int)sx,
(int)sy,
Color.blue );
sx += incx;
sy += ydir;
}//
End for loop(..)
}
else
{
for(
int
x=0; x<dx; x++)
{
setPixel(image, (int)sx,
(int)sy,
Color.blue );
sy += incy;
sx += xdir;
}//
End for loop(..)
}
}//End
of drawline(..)
public
void
setPixel(Image image,
int
x,
int
y, Color color )
{
Graphics g = image.getGraphics( );
g.setColor( color );
g.fillRect( x, y, 1, 1 );
g.dispose( );
}//
End of setPixel(..)
/*********************************************************************************************/
/*
*/
/* Capture event when mouse is
pressed */
/* Parameters:
*/
/* evt is The
event...
*/
/* x is the x mouse
position */
/* y is the y mouse position
*/
/* return whether or not we handled the
event */
/*
*/
/*********************************************************************************************/
public
boolean
mouseDown(Event evt,
int
x,
int
y)
{
if(evt.id
== Event.MOUSE_DOWN)
{
// Right mouse button
if(
evt.metaDown() ==
true
)
{
System.out.println(
"Right MouseDown"
);
mouse_x_right = x;
mouse_y_right = y;
//m_x0 = x;
//m_y0 = y;
}
else
// right mouse button
{
System.out.println(
"Left MouseDown"
);
mouse_x_left = x;
mouse_y_left = y;
//m_x1 = x;
//m_y1 = y;
}
}//
End if
repaint();
return
true;
}//
End of mouseDown(..)
/*********************************************************************************************/
/*
*/
/* Capture mouse
release
*/
/*
Parameters:
*/
/* evt is The
event...
*/
/* x is the x mouse position
*/
/* y is the y mouse
position */
/* return whether or not we handled the
event */
/*
*/
/*********************************************************************************************/
public
boolean
mouseUp(Event evt,
int
x,
int
y)
{
return
false;
}//
End of mouseUp(..)
/*********************************************************************************************/
/*
*/
/* Capture mouse
drags
*/
/*
Parameters:
*/
/* evt is The event...
*/
/* x is the x mouse
position */
/* y is the y mouse
position */
/* return whether or not we handled the
event */
/*
*/
/*********************************************************************************************/
public
boolean
mouseDrag(Event evt,
int
x,
int
y)
{
return true;
}//
End of mouseDrag(..)
}// End of our Applet |