A 3D Applet
gob
The Applet is implemented in class gob
public class gob extends Object
{
//----------------------------------------------------------------
// defining the minimalistic graphical interface:
// clearAll, drawBox, flush, pBegin, pEnd, popMatrix, pushMatrix,
// rotateX, rotateY, rotateZ, scale, translate,
// setBackground, setColor, setIdentityMatrix,
// setOrtho, setperspective, setViewport, vertex
//-----------------------------------------------------------------
//-----------------------------------------------------------------
// classes : p3D, matrix, polygon defined at end of file
//-----------------------------------------------------------------
// fill or outline polygons
public final static int FILL=0;
public final static int FRAME=1;
// the list of polygons to draw
Vector drawList=new Vector(20,6);
// matrix stack, for push and pop
Vector matrixStack=new Vector(5,5);
// current model(view) matrix
matrix curMatrix=null;
// current polygon, while we are adding vertexes
polygon curPoly=null;
// current drawing color
Color curColor=null;
// Background color, last before flush yields
Color BKColor=null;
// buffer to draw in to avoid flicker
Image Buffer;
// viewport
float Vxl,Vyt,Vw,Vh;
// window, model coordinates
float Wxl,Wyt,Ww,Wh;
// perspective angles, in degrees
float Pvx,Pvy;
// use perspective projection or not
boolean isPerspective;
// the actual component that gob will work on
Component theComp;
// possible background image
Image theImage=null;
public gob(Component comp)
{
Buffer=comp.createImage(comp.getSize().width,comp.getSize().height);
setViewport(0,0,comp.getSize().width,comp.getSize().height);
setOrtho(0,0,comp.getSize().width,comp.getSize().height);
theComp=comp;
curColor=new Color(0.9f,0.9f,0.9f);
BKColor=new Color(1.0f,1.0f,1.0f);
}
public void clearAll()
{
drawList.removeAllElements();
curPoly=null;
curMatrix=new matrix();
matrixStack.removeAllElements();
}
public void setIdentityMatrix()
{
curMatrix=new matrix();
}
public void pushMatrix()
{
if(curMatrix==null)
curMatrix=new matrix();
matrixStack.addElement(curMatrix.copyMatrix(curMatrix));
}
public void popMatrix()
{
if(matrixStack.size()<1)
{
//underflow
curMatrix=new matrix();
System.out.println("Matrix stack undeflow, too many pops");
return;
}
curMatrix=(matrix)matrixStack.lastElement();
matrixStack.removeElement(matrixStack.lastElement());
}
public void translate(float dx,float dy,float dz)
{
float[] values={
1.0f,0.0f,0.0f,dx,
0.0f,1.0f,0.0f,dy,
0.0f,0.0f,1.0f,dz,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void scale(float sx,float sy,float sz)
{
float[] values={
sx,0.0f,0.0f,0.0f,
0.0f,sy,0.0f,0.0f,
0.0f,0.0f,sz,0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateX(float v)
{
float[] values={
1.0f,0.0f,0.0f,0.0f,
0.0f,(float)Math.cos(v),(float)-Math.sin(v),0.0f,
0.0f,(float)Math.sin(v),(float)Math.cos(v),0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateY(float v)
{
float[] values={
(float)Math.cos(v),0.0f,(float)Math.sin(v),0.0f,
0.0f,1.0f,0.0f,0.0f,
(float)-Math.sin(v),0.0f,(float)Math.cos(v),0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateZ(float v)
{
float[] values={
(float)Math.cos(v),(float)-Math.sin(v),0.0f,0.0f,
(float)Math.sin(v),(float)Math.cos(v),0.0f,0.0f,
0.0f,0.0f,1.0f,0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void pBegin(int mode)
{
if(mode==FILL)
curPoly=new polygon(curColor,mode);
else
curPoly=new polygon(curColor,FRAME);
}
public void vertex(float nx, float ny, float nz)
{
p3D npnt=curMatrix.transform(new p3D(nx,ny,nz));
curPoly.appendVertex(npnt);
}
public void pEnd()
{
// calculate the angle for lighting
curPoly.calculateAngle();
// put it into the the drawList according to depth
// lowest minZ comes first in list
for(int ix=0;ix<drawList.size();ix++)
if(((polygon)drawList.elementAt(ix)).getMinZ()>curPoly.getMinZ())
{
drawList.insertElementAt(curPoly,ix);
return;
}
drawList.addElement(curPoly);
}
public void flush(Graphics g)
{
// project the polygons
for(int ix=0;ix<drawList.size();ix++)
{
// project it
if(isPerspective)
((polygon)drawList.elementAt(ix)).perspectiveProject(Pvx,Pvy, Wxl,Wyt,Ww,Wh, Vxl,Vyt,Vw,Vh);
else
((polygon)drawList.elementAt(ix)).paralellProject(Wxl,Wyt,Ww,Wh, Vxl,Vyt,Vw,Vh);
}
// draw drawList from bottom on an offscreen buffer
Graphics tmpg=Buffer.getGraphics();
// clear only the specified viewport
// tmpg.clearRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
tmpg.setColor(BKColor);
// fills with color or set out image
tmpg.fillRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
if(theImage!=null)
tmpg.drawImage(theImage,Math.round(Vxl),Math.round(Vyt),BKColor, null);
// draw it
for(int ix=0;ix<drawList.size();ix++)
((polygon)drawList.elementAt(ix)).render(tmpg);
// copy to viewport
g.clipRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
g.drawImage(Buffer,0,0,null);
}
public void setColor(Color c)
{
curColor=c;
}
public void setBackground(Color c)
{
BKColor=c;
//theImage=null;
}
public void setImage(Image im)
{
if(im==null)
theImage=null;
else
theImage=im;
}
public void setViewport(float xl,float yt,float w, float h)
{
Vxl=xl;Vyt=yt;Vw=w;Vh=h;
}
public void setOrtho(float xl,float yt,float w, float h)
{
Wxl=xl;Wyt=yt;Ww=w;Wh=h;
isPerspective=false;
}
public void setPerspective(float vx,float vy)
{
if(vx < 0.5)
Pvx=0.5f;
else if(vx > 89.5)
Pvx=89.5f;
else
Pvx=vx;
if(vy<0.5)
Pvy=0.5f;
else if(vy > 89.5)
Pvy=89.5f;
else
Pvy=vy;
isPerspective=true;
}
//----------------------------------------------
// draw a box, is handy since this is about all we
// would be tempted to draw in these surroundings
public void drawBox(float dx,float dy, float dz,int mode)
{
// bottom
float x=dx/2.0f;
float y=dy/2.0f;
float z=dz/2.0f;
pBegin(mode);
vertex(-x,y,-z); vertex(-x,-y,-z); vertex(x,-y,-z); vertex(x,y,-z);
pEnd();
// top
pBegin(mode);
vertex(-x,y,z); vertex(x,y,z); vertex(x,-y,z); vertex(-x,-y,z);
pEnd();
// front
pBegin(mode);
vertex(-x,y,-z); vertex(x,y,-z); vertex(x,y,z); vertex(-x,y,z);
pEnd();
// back
pBegin(mode);
vertex(-x,-y,-z); vertex(-x,-y,z); vertex(x,-y,z); vertex(x,-y,-z);
pEnd();
// left
pBegin(mode);
vertex(-x,y,-z); vertex(-x,y,z); vertex(-x,-y,z); vertex(-x,-y,-z);
pEnd();
// right
pBegin(mode);
vertex(x,-y,-z); vertex(x,-y,z); vertex(x,y,z); vertex(x,y,-z);
pEnd();
}
}
//----------------- end of gob
//------------------ start of p3D
class p3D extends Object
{
float x,y,z;
public p3D(float nx, float ny, float nz)
{
x=nx; y=ny; z=nz;
}
}
//----------------- end of p3D
//------------------ start of matrix
class matrix extends Object
{
float[][] m=null;
public matrix()
{
m=new float[][]{
{1.0f,0.0f,0.0f,0.0f},
{0.0f,1.0f,0.0f,0.0f},
{0.0f,0.0f,1.0f,0.0f},
{0.0f,0.0f,0.0f,1.0f}};
}
public matrix(float[] values)
{
m=new float[][]{
{1.0f,0.0f,0.0f,0.0f},
{0.0f,1.0f,0.0f,0.0f},
{0.0f,0.0f,1.0f,0.0f},
{0.0f,0.0f,0.0f,1.0f}};
if(values.length!=16)
return;
m[0][0]=values[0]; m[0][1]=values[1]; m[0][2]=values[2]; m[0][3]=values[3];
m[1][0]=values[4]; m[1][1]=values[5]; m[1][2]=values[6]; m[1][3]=values[7];
m[2][0]=values[8]; m[2][1]=values[9]; m[2][2]=values[10]; m[2][3]=values[11];
m[3][0]=values[12]; m[3][1]=values[13]; m[3][2]=values[14]; m[3][3]=values[15];
}
matrix copyMatrix(matrix sm)
{
float v[]={
sm.m[0][0],sm.m[0][1],sm.m[0][2],sm.m[0][3],
sm.m[1][0],sm.m[1][1],sm.m[1][2],sm.m[1][3],
sm.m[2][0],sm.m[2][1],sm.m[2][2],sm.m[2][3],
sm.m[3][0],sm.m[3][1],sm.m[3][2],sm.m[3][3]};
return new matrix(v);
}
p3D transform(p3D p)
{
return new p3D(
p.x*m[0][0]+p.y*m[0][1]+p.z*m[0][2]+m[0][3],
p.x*m[1][0]+p.y*m[1][1]+p.z*m[1][2]+m[1][3],
p.x*m[2][0]+p.y*m[2][1]+p.z*m[2][2]+m[2][3]);
}
void postMult(matrix tm)
{
// do: m = m X tm.m
float [][] oldm={
{m[0][0],m[0][1],m[0][2],m[0][3]},
{m[1][0],m[1][1],m[1][2],m[1][3]},
{m[2][0],m[2][1],m[2][2],m[2][3]},
{m[3][0],m[3][1],m[3][2],m[3][3]}};
for(int r=0;r<4;r++)
for(int k=0;k<4;k++)
m[r][k]=oldm[r][0]*tm.m[0][k]+
oldm[r][1]*tm.m[1][k]+
oldm[r][2]*tm.m[2][k]+
oldm[r][3]*tm.m[3][k];
}
}
//----------------- end of matrix
//------------------ start of polygon
class polygon extends Object
{
Vector p=new Vector(20,6);
float minZ=100000.0f; // higher than any point
Color pcolor=null;
float cosangle=0.0f;
Polygon pol=null;
int pmode;
public polygon(Color c,int mode)
{
pmode=mode;
pcolor=c;
}
void setColor(float r,float g,float b)
{
pcolor=new Color(r,g,b);
}
float getMinZ()
{
return minZ;
}
int getPCount()
{
return p.size();
}
p3D getPAt(int index)
{
if(index>=p.size() || index<0)
return new p3D(0.0f,0.0f,0.0f);
return (p3D)p.elementAt(index);
}
void setPAt(p3D np,int index)
{
if(index>=p.size() || index<0)
return;
((p3D)p.elementAt(index)).x=np.x;
((p3D)p.elementAt(index)).y=np.y;
((p3D)p.elementAt(index)).z=np.z;
}
void appendVertex(float x,float y, float z)
{
appendVertex(new p3D(x,y,z));
}
void appendVertex(p3D pnt)
{
if(pnt.z<minZ)
minZ=pnt.z;
p.addElement(pnt);
}
void calculateAngle()
{
// calculate normal and angle twds z-axis
if(p.size()<3)
{
cosangle=1.0f;
return;
}
// normal,normalV, from triangle: p0 - p1 - p2
// c=(a2b3-a3b2, a3b1-a1b3, a1b2-a2b1)
p3D p0=(p3D)p.elementAt(0);
p3D p1=(p3D)p.elementAt(1);
p3D p2=(p3D)p.elementAt(2);
p3D normalV=new p3D((p2.y-p1.y)*(p0.z-p1.z)-(p2.z-p1.z)*(p0.y-p1.y),
(p2.z-p1.z)*(p0.x-p1.x)-(p2.x-p1.x)*(p0.z-p1.z),
(p2.x-p1.x)*(p0.y-p1.y)-(p2.y-p1.y)*(p0.x-p1.x));
float len=(float)Math.sqrt(normalV.x*normalV.x+normalV.y*normalV.y+normalV.z*normalV.z);
// normalize. need only z-component
// dont care about direction
// big cosangle -> light face, since light is -z
cosangle=Math.abs(normalV.z/len);
}
//public void paralellProject(float xfactor,float yfactor)
public void paralellProject(
float Wxl, float Wyt, float Ww, float Wh,
float Vxl, float Vyt, float Vw, float Vh)
{
if(p.size()<2)
return;
pol=new Polygon();
float xfactor=Vw/Ww;
float yfactor=Vh/Wh;
for(int ix=0;ix<p.size();ix++)
pol.addPoint(
Math.round( (((p3D)p.elementAt(ix)).x-Wxl)*xfactor+Vxl),
Math.round( (((p3D)p.elementAt(ix)).y-Wyt)*yfactor+Vyt) );
}
public void perspectiveProject(
float vx,float vy,
float Wxl,float Wyt, float Ww, float Wh,
float Vxl, float Vyt, float Vw, float Vh)
{
// W is not used, assumes Rectangle(-0.5,-0.5,1,1)
if(p.size()<2)
return;
pol=new Polygon();
float xf=(float)(0.5f/Math.tan(Math.toRadians(vx)));
float yf=(float)(0.5f/Math.tan(Math.toRadians(vy)));
for(int ix=0;ix<p.size();ix++)
{
float x=((p3D)p.elementAt(ix)).x/Math.abs(((p3D)p.elementAt(ix)).z)*xf;
float y=((p3D)p.elementAt(ix)).y/Math.abs(((p3D)p.elementAt(ix)).z)*xf;
pol.addPoint(Math.round( (x+0.5f)*Vw+Vxl ),Math.round( (y+0.5f)*Vh+Vyt )
);
}
}
public void render(Graphics g)
{
if(pol==null)
return;
if(pmode==gob.FILL)
{
Color c=new Color(
(pcolor.getRed()*(cosangle/255.0f)),
(pcolor.getGreen()*(cosangle/255.0f)),
(pcolor.getBlue()*(cosangle)/255.0f));
g.setColor(c);
g.fillPolygon(pol);
}
else
{
g.setColor(pcolor);
g.drawPolygon(pol);
}
}
}
Methods
Methods in gob are:
- gob(Component comp)
Sets up a gob-object against a component. Sets default values for polygon color and background color. Sets projection to parallel projection and viewport like the components dimensions. - clearAll()
Clears the model matrix and removes list of polygons to render. - drawBox(float dx, float dy, float dz, int mode)
helper to draw a bok with sides dx,dy og dz, with origo in centrum. mode may have values gob.FILL or gob.FRAME. - flush()
Render all polygons to screen. - pBegin(int mode)
Establish a polygon and prepares for receiving corners. mode may have values gob.FILL or gob.FRAME. - pEnd()
Ends a polygon. - popMatrix()
Pop last pushed matrix from the stack and use it as model matrix. - pushMatrix()
Push model matrix to the stack - rotateX(float v)
Rotate v radians around x-axis. - rotateY(float v)
Rotate v radians around y-axis. - rotateZ(float v)
Rotate v radians around z-axis. - scale(float sx,float sy,float sz)
Scaling with factors sx, sy, sz, relative to origo. - translate(float tx,float ty,float tz)
translates tx, ty, tz. - setBackground(Color c)
Set background color - setImage(Image im)
put an image in background, anchored at upper left in viewport - setColor(Color c)
sets current rendering color. - setIdentityMatrix()
Set modelmatrix to identity matrix - setOrtho(float xl, float yt, float dx, float dy)
Paralell projection to i xy-plane. The part limited by parameters are mapped to viewport Den delen av xy-planet som er - setPerspective(float xv, float yv)
Sets projection to perspective, giving the "lens". xv og yv oppgis given in degrees, looking from origo down z. No clipping - setViewport(float xl,float yt,float w, float h)
Where to map the projection. parameters relative to gob-objects component. - vertex(float x, float y, float z)
One vertex.
Light
There is only one light source. This light supplies diffuse light along negative z. Since the eye is placed in origo, looking down z, the light appears as a "headlight". The light is white.
A better light model might be a sensible extension
Hidden surfaces
The list of polygons are sorted according to the point with longest distance from the eye, and the list is rendered accordingly. With the simple eye-model we have, the negative z-value is the distance from the eye.
This is a simplified version of Painters algorithm.
This depth sorting is not waterproof, but it works for most convex, closed forms.
A better surface depth-sorting might be a sensible extension
Clipping
There is no clipping in gob. The only clipping that goes on is when the projected image is mapped to the viewport. Viewport is clipped by java.awt.
Projection
There are two alternative projections: parallel (setOrtho) and perspective (setPerspective).
The parallel projection is strait forward: The z-coordinates is removed and we are left with 2D-polygon which ia mapped from model to viewport.
The perspective projection is a little more complicated. The distance from the eye must be taken into consideration.

We decide to project onto a plane with a distance from the eye,
and we decide that the projection plane has dimension 1 x 1.
We want to find Q.
We set up the following equations:
- tg(Vx)=0.5/d
- P.x/P.z=Q.x/d.
We eliminate d and gets Q.x=(P.x*0.5)/(tg(Vx)*P.z). We can do the same operation for y.
Then we are ready to map the square (-0.5,-0.5) - (0.5,0.5) to viewport.
Viewport
Viewport can either be set to cover the component that gob is attached to, or we can specify viewport as a part of this component. The latter solution is chosen. It gives us greater freedom to design the component.
Double Buffering
The method flush render all polygons to a background buffer, and then copy this background buffer to the component gob is attached to
//Image Buffer.. .. Graphics tmpg=Buffer.getGraphics(); tmpg.setColor(BKColor); // fill background tmpg.fillRect(< viewport>); // possible background image if(theImage!=null) tmpg.drawImage(< viewports uppeleft>,BKColor, null); // polygons in list for(int ix=0;ix< drawList.size();ix++) ((polygon)drawList.elementAt(ix)).render(tmpg); // copy to viewport g.clipRect(< viewport>); g.drawImage(Buffer,0,0,null);
Note that we avoid flickering by implementing:
public void update(Graphics g)
{
paint(g);
}
This shortcuts the blanking that awt initiates.
Boxes
It is up to you how you organize the use of gob. A typical applet is the applet that runs on the top of this page. Source:
import java.awt.Polygon;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Component;
import java.awt.event.*;
import java.applet.Applet;
import java.util.Vector;
import java.awt.geom.AffineTransform;
import java.awt.Graphics2D;
public class a3dboxes extends Applet implements Runnable
{
// the 3D object
gob hb=null;
// a thread that we can start and stop by mouseclick
Thread painter=null;
// running ?
boolean halted=false;
// use perspective or not
//---------- to make the demo spin ----------
float vx=0.0f;
float vy=0.0f;
float vz=0.0f;
float dvx=0.02f;
float dvy=0.01f;
float dvz=0.02f;
int lastx=0;
int lasty=0;
//--------------------------------------
//Construct the applet
public a3dboxes()
{
}
//Initialize the applet
public void init()
{
try { jbInit(); }
catch(Exception e){e.printStackTrace(); }
}
//Component initialization
private void jbInit() throws Exception
{
this.addMouseMotionListener(new java.awt.event.MouseMotionAdapter()
{
public void mouseDragged(MouseEvent e)
{ this_mouseDragged(e); }
});
this.addMouseListener(new java.awt.event.MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{ this_mouseClicked(e); }
public void mousePressed(MouseEvent e)
{ this_mousePressed(e); }
public void mouseReleased(MouseEvent e)
{ this_mouseReleased(e); }
});
this.setBackground(Color.white);
this.setLayout(null);
hb=new gob(this);
}
//Start the applet
public void start()
{
painter=new Thread(this);
halted=false;
painter.start();
}
//Stop the applet
public void stop()
{
if(painter!=null)
halted=true;
}
//Destroy the applet
public void destroy()
{
}
//Get Applet information
public String getAppletInfo()
{
return "Name: App3D, Author: Børre Stenseth";
}
public void paint(Graphics g)
{
// a demo with some spinning boxes
hb.clearAll();
drawSpinningBoxes();
hb.flush(g);
}
public void run()
{
while (!halted)
{
try
{
vx+=dvx;
vx+=dvy;
vz+=dvz;
repaint();
Thread.sleep(20);
}
catch (InterruptedException e)
{
stop();
}
}
}
// override update to avois flickering
public void update(Graphics g)
{
paint(g);
}
void drawSpinningBoxes()
{
// a demo with some spinning boxes
hb.setBackground(new Color(1.0f,1.0f,1.0f));
hb.setOrtho(-200,-200,400,400);
hb.setViewport(0,0,200,200);
hb.translate(0.0f,0.0f,-200.0f);
hb.rotateZ(vz);
hb.rotateX(vx);
hb.rotateY(vy);
hb.pushMatrix();
hb.setColor(new Color(1.0f,0.0f,0.0f));
hb.drawBox(50.0f,50.0f,50.0f,gob.FILL);
hb.popMatrix();
hb.pushMatrix();
hb.translate(60.0f,60.0f,-10.0f);
hb.pushMatrix();
hb.rotateZ(vz);
hb.setColor(new Color(0.0f,1.0f,0.0f));
hb.drawBox(50.0f,50.0f,50.0f,gob.FILL);
hb.popMatrix();
hb.popMatrix();
hb.pushMatrix();
hb.translate(60.0f,-60.0f,10.0f);
hb.pushMatrix();
hb.rotateX(-2*vx);
hb.setColor(new Color(1.0f,1.0f,0.0f));
hb.drawBox(50.0f,50.0f,50.0f,gob.FILL);
hb.popMatrix();
hb.popMatrix();
hb.pushMatrix();
hb.rotateY(-2*vy);
hb.rotateX(-2*vy);
hb.translate(-60.0f,60.0f,30.0f);
hb.setColor(new Color(0.0f,1.0f,1.0f));
hb.drawBox(50.0f,50.0f,50.0f,gob.FILL);
}
//----------------------------------------------
// mousing around
void this_mouseClicked(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
{
if(halted)
start();
else
stop();
}
}
void this_mousePressed(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK)
{
lastx=e.getX();
lasty=e.getY();
halted=true;
stop();
}
}
void this_mouseReleased(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK)
{
int x=e.getX();
int y=e.getY();
halted=false;
start();
}
}
void this_mouseDragged(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK)
{
int x=e.getX();
int y=e.getY();
dvx=5.0f*(x-lastx)/this.getSize().width;
dvy=5.0f*(y-lasty)/this.getSize().height;
dvz=dvx-dvy;
vx+=dvx;
vx+=dvy;
vz+=dvz;
repaint();
lastx=x;
lasty=y;
}
}
//--------------------------------------------------
// start of inner classes: gob, p3D, matrix, polygon
public class gob extends Object
{
//----------------------------------------------------------------
// defining the minimalistic graphical interface:
// clearAll, drawBox, flush, pBegin, pEnd, popMatrix, pushMatrix,
// rotateX, rotateY, rotateZ, scale, translate,
// setBackground, setColor, setIdentityMatrix,
// setOrtho, setperspective, setViewport, vertex
//-----------------------------------------------------------------
//-----------------------------------------------------------------
// intenal classes : p3D, matrix, polygon defined at end of file
//-----------------------------------------------------------------
// fill or outline polygons
public final static int FILL=0;
public final static int FRAME=1;
// the list of polygons to draw
Vector drawList=new Vector(20,6);
// matrix stack, for push and pop
Vector matrixStack=new Vector(5,5);
// current model(view) matrix
matrix curMatrix=null;
// current polygon, while we are adding vertexes
polygon curPoly=null;
// current drawing color
Color curColor=null;
// Background color, last before flush yields
Color BKColor=null;
// buffer to draw in to avoid flicker
Image Buffer;
// viewport
float Vxl,Vyt,Vw,Vh;
// window, model coordinates
float Wxl,Wyt,Ww,Wh;
// perspective angles, in degrees
float Pvx,Pvy;
// use perspective projection or not
boolean isPerspective;
// the actual component that gob will work on
Component theComp;
// possible background image
Image theImage=null;
public gob(Component comp)
{
Buffer=comp.createImage(comp.getSize().width,comp.getSize().height);
setViewport(0,0,comp.getSize().width,comp.getSize().height);
setOrtho(0,0,comp.getSize().width,comp.getSize().height);
theComp=comp;
curColor=new Color(0.9f,0.9f,0.9f);
BKColor=new Color(1.0f,1.0f,1.0f);
}
public void clearAll()
{
drawList.removeAllElements();
curPoly=null;
curMatrix=new matrix();
matrixStack.removeAllElements();
}
public void setIdentityMatrix()
{
curMatrix=new matrix();
}
public void pushMatrix()
{
if(curMatrix==null)
curMatrix=new matrix();
matrixStack.addElement(curMatrix.copyMatrix(curMatrix));
}
public void popMatrix()
{
if(matrixStack.size()<1)
{
//underflow
curMatrix=new matrix();
System.out.println("Matrix stack undeflow, too many pops");
return;
}
curMatrix=(matrix)matrixStack.lastElement();
matrixStack.removeElement(matrixStack.lastElement());
}
public void translate(float dx,float dy,float dz)
{
float[] values={
1.0f,0.0f,0.0f,dx,
0.0f,1.0f,0.0f,dy,
0.0f,0.0f,1.0f,dz,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void scale(float sx,float sy,float sz)
{
float[] values={
sx,0.0f,0.0f,0.0f,
0.0f,sy,0.0f,0.0f,
0.0f,0.0f,sz,0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateX(float v)
{
float[] values={
1.0f,0.0f,0.0f,0.0f,
0.0f,(float)Math.cos(v),(float)-Math.sin(v),0.0f,
0.0f,(float)Math.sin(v),(float)Math.cos(v),0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateY(float v)
{
float[] values={
(float)Math.cos(v),0.0f,(float)Math.sin(v),0.0f,
0.0f,1.0f,0.0f,0.0f,
(float)-Math.sin(v),0.0f,(float)Math.cos(v),0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateZ(float v)
{
float[] values={
(float)Math.cos(v),(float)-Math.sin(v),0.0f,0.0f,
(float)Math.sin(v),(float)Math.cos(v),0.0f,0.0f,
0.0f,0.0f,1.0f,0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void pBegin(int mode)
{
if(mode==FILL)
curPoly=new polygon(curColor,mode);
else
curPoly=new polygon(curColor,FRAME);
}
public void vertex(float nx, float ny, float nz)
{
p3D npnt=curMatrix.transform(new p3D(nx,ny,nz));
curPoly.appendVertex(npnt);
}
public void pEnd()
{
// calculate the angle for lighting
curPoly.calculateAngle();
// put it into the the drawList according to depth
// lowest minZ comes first in list
for(int ix=0;ix<drawList.size();ix++)
if(((polygon)drawList.elementAt(ix)).getMinZ()>curPoly.getMinZ())
{
drawList.insertElementAt(curPoly,ix);
return;
}
drawList.addElement(curPoly);
}
public void flush(Graphics g)
{
// project the polygons
for(int ix=0;ix<drawList.size();ix++)
{
// project it
if(isPerspective)
((polygon)drawList.elementAt(ix)).perspectiveProject(Pvx,Pvy, Wxl,Wyt,Ww,Wh, Vxl,Vyt,Vw,Vh);
else
((polygon)drawList.elementAt(ix)).paralellProject(Wxl,Wyt,Ww,Wh, Vxl,Vyt,Vw,Vh);
}
// draw drawList from bottom on an offscreen buffer
Graphics tmpg=Buffer.getGraphics();
// clear only the specified viewport
// tmpg.clearRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
tmpg.setColor(BKColor);
// fills with color or set out image
tmpg.fillRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
if(theImage!=null)
tmpg.drawImage(theImage,Math.round(Vxl),Math.round(Vyt),BKColor, null);
// draw it
for(int ix=0;ix<drawList.size();ix++)
((polygon)drawList.elementAt(ix)).render(tmpg);
// copy to viewport
g.clipRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
g.drawImage(Buffer,0,0,null);
}
public void setColor(Color c)
{
curColor=c;
}
public void setBackground(Color c)
{
BKColor=c;
//theImage=null;
}
public void setImage(Image im)
{
if(im==null)
theImage=null;
else
theImage=im;
}
public void setViewport(float xl,float yt,float w, float h)
{
Vxl=xl;Vyt=yt;Vw=w;Vh=h;
}
public void setOrtho(float xl,float yt,float w, float h)
{
Wxl=xl;Wyt=yt;Ww=w;Wh=h;
isPerspective=false;
}
public void setPerspective(float vx,float vy)
{
if(vx < 0.5)
Pvx=0.5f;
else if(vx > 89.5)
Pvx=89.5f;
else
Pvx=vx;
if(vy<0.5)
Pvy=0.5f;
else if(vy > 89.5)
Pvy=89.5f;
else
Pvy=vy;
isPerspective=true;
}
//----------------------------------------------
// draw a box, is handy since this is about all we
// would be tempted to draw in these surroundings
public void drawBox(float dx,float dy, float dz,int mode)
{
// bottom
float x=dx/2.0f;
float y=dy/2.0f;
float z=dz/2.0f;
pBegin(mode);
vertex(-x,y,-z); vertex(-x,-y,-z); vertex(x,-y,-z); vertex(x,y,-z);
pEnd();
// top
pBegin(mode);
vertex(-x,y,z); vertex(x,y,z); vertex(x,-y,z); vertex(-x,-y,z);
pEnd();
// front
pBegin(mode);
vertex(-x,y,-z); vertex(x,y,-z); vertex(x,y,z); vertex(-x,y,z);
pEnd();
// back
pBegin(mode);
vertex(-x,-y,-z); vertex(-x,-y,z); vertex(x,-y,z); vertex(x,-y,-z);
pEnd();
// left
pBegin(mode);
vertex(-x,y,-z); vertex(-x,y,z); vertex(-x,-y,z); vertex(-x,-y,-z);
pEnd();
// right
pBegin(mode);
vertex(x,-y,-z); vertex(x,-y,z); vertex(x,y,z); vertex(x,y,-z);
pEnd();
}
}
//----------------- end of gob
//------------------ start of p3D
class p3D extends Object
{
float x,y,z;
public p3D(float nx, float ny, float nz)
{
x=nx; y=ny; z=nz;
}
}
//----------------- end of p3D
//------------------ start of matrix
class matrix extends Object
{
float[][] m=null;
public matrix()
{
m=new float[][]{
{1.0f,0.0f,0.0f,0.0f},
{0.0f,1.0f,0.0f,0.0f},
{0.0f,0.0f,1.0f,0.0f},
{0.0f,0.0f,0.0f,1.0f}};
}
public matrix(float[] values)
{
m=new float[][]{
{1.0f,0.0f,0.0f,0.0f},
{0.0f,1.0f,0.0f,0.0f},
{0.0f,0.0f,1.0f,0.0f},
{0.0f,0.0f,0.0f,1.0f}};
if(values.length!=16)
return;
m[0][0]=values[0]; m[0][1]=values[1]; m[0][2]=values[2]; m[0][3]=values[3];
m[1][0]=values[4]; m[1][1]=values[5]; m[1][2]=values[6]; m[1][3]=values[7];
m[2][0]=values[8]; m[2][1]=values[9]; m[2][2]=values[10]; m[2][3]=values[11];
m[3][0]=values[12]; m[3][1]=values[13]; m[3][2]=values[14]; m[3][3]=values[15];
}
matrix copyMatrix(matrix sm)
{
float v[]={
sm.m[0][0],sm.m[0][1],sm.m[0][2],sm.m[0][3],
sm.m[1][0],sm.m[1][1],sm.m[1][2],sm.m[1][3],
sm.m[2][0],sm.m[2][1],sm.m[2][2],sm.m[2][3],
sm.m[3][0],sm.m[3][1],sm.m[3][2],sm.m[3][3]};
return new matrix(v);
}
p3D transform(p3D p)
{
return new p3D(
p.x*m[0][0]+p.y*m[0][1]+p.z*m[0][2]+m[0][3],
p.x*m[1][0]+p.y*m[1][1]+p.z*m[1][2]+m[1][3],
p.x*m[2][0]+p.y*m[2][1]+p.z*m[2][2]+m[2][3]);
}
void postMult(matrix tm)
{
// do: m = m X tm.m
float [][] oldm={
{m[0][0],m[0][1],m[0][2],m[0][3]},
{m[1][0],m[1][1],m[1][2],m[1][3]},
{m[2][0],m[2][1],m[2][2],m[2][3]},
{m[3][0],m[3][1],m[3][2],m[3][3]}};
for(int r=0;r<4;r++)
for(int k=0;k<4;k++)
m[r][k]=oldm[r][0]*tm.m[0][k]+
oldm[r][1]*tm.m[1][k]+
oldm[r][2]*tm.m[2][k]+
oldm[r][3]*tm.m[3][k];
}
}
//----------------- end of matrix
//------------------ start of polygon
class polygon extends Object
{
Vector p=new Vector(20,6);
float minZ=100000.0f; // higher than any point
Color pcolor=null;
float cosangle=0.0f;
Polygon pol=null;
int pmode;
public polygon(Color c,int mode)
{
pmode=mode;
pcolor=c;
}
void setColor(float r,float g,float b)
{
pcolor=new Color(r,g,b);
}
float getMinZ()
{
return minZ;
}
int getPCount()
{
return p.size();
}
p3D getPAt(int index)
{
if(index>=p.size() || index<0)
return new p3D(0.0f,0.0f,0.0f);
return (p3D)p.elementAt(index);
}
void setPAt(p3D np,int index)
{
if(index>=p.size() || index<0)
return;
((p3D)p.elementAt(index)).x=np.x;
((p3D)p.elementAt(index)).y=np.y;
((p3D)p.elementAt(index)).z=np.z;
}
void appendVertex(float x,float y, float z)
{
appendVertex(new p3D(x,y,z));
}
void appendVertex(p3D pnt)
{
if(pnt.z<minZ)
minZ=pnt.z;
p.addElement(pnt);
}
void calculateAngle()
{
// calculate normal and angle twds z-axis
if(p.size()<3)
{
cosangle=1.0f;
return;
}
// normal,normalV, from triangle: p0 - p1 - p2
// c=(a2b3-a3b2, a3b1-a1b3, a1b2-a2b1)
p3D p0=(p3D)p.elementAt(0);
p3D p1=(p3D)p.elementAt(1);
p3D p2=(p3D)p.elementAt(2);
p3D normalV=new p3D((p2.y-p1.y)*(p0.z-p1.z)-(p2.z-p1.z)*(p0.y-p1.y),
(p2.z-p1.z)*(p0.x-p1.x)-(p2.x-p1.x)*(p0.z-p1.z),
(p2.x-p1.x)*(p0.y-p1.y)-(p2.y-p1.y)*(p0.x-p1.x));
float len=(float)Math.sqrt(normalV.x*normalV.x+normalV.y*normalV.y+normalV.z*normalV.z);
// normalize. need only z-component
// dont care about direction
// big cosangle -> light face, since light is -z
cosangle=Math.abs(normalV.z/len);
}
//public void paralellProject(float xfactor,float yfactor)
public void paralellProject(
float Wxl, float Wyt, float Ww, float Wh,
float Vxl, float Vyt, float Vw, float Vh)
{
if(p.size()<2)
return;
pol=new Polygon();
float xfactor=Vw/Ww;
float yfactor=Vh/Wh;
for(int ix=0;ix<p.size();ix++)
pol.addPoint(
Math.round( (((p3D)p.elementAt(ix)).x-Wxl)*xfactor+Vxl),
Math.round( (((p3D)p.elementAt(ix)).y-Wyt)*yfactor+Vyt) );
}
public void perspectiveProject(
float vx,float vy,
float Wxl,float Wyt, float Ww, float Wh,
float Vxl, float Vyt, float Vw, float Vh)
{
// W is not used, assumes Rectangle(-0.5,-0.5,1,1)
if(p.size()<2)
return;
pol=new Polygon();
float xf=(float)(0.5f/Math.tan(Math.toRadians(vx)));
float yf=(float)(0.5f/Math.tan(Math.toRadians(vy)));
for(int ix=0;ix<p.size();ix++)
{
float x=((p3D)p.elementAt(ix)).x/Math.abs(((p3D)p.elementAt(ix)).z)*xf;
float y=((p3D)p.elementAt(ix)).y/Math.abs(((p3D)p.elementAt(ix)).z)*xf;
pol.addPoint(Math.round( (x+0.5f)*Vw+Vxl ),Math.round( (y+0.5f)*Vh+Vyt )
);
}
}
public void render(Graphics g)
{
if(pol==null)
return;
if(pmode==gob.FILL)
{
Color c=new Color(
(pcolor.getRed()*(cosangle/255.0f)),
(pcolor.getGreen()*(cosangle/255.0f)),
(pcolor.getBlue()*(cosangle)/255.0f));
g.setColor(c);
g.fillPolygon(pol);
}
else
{
g.setColor(pcolor);
g.drawPolygon(pol);
}
}
}
//------------- end of polygon
// -------------- end of inner classes
}
Falling
|
A simple demo of gob. Falling boxes.
void drawFallingBoxes()
{
// a demo with some falling boxes
hb.setBackground(new Color(1.0f,1.0f,1.0f));
// use default viewport and ortho, full applet
// boxes are "falling" in the direction of pos y
hb.translate(FALLWIDTH/2.0f+3.0f,0.0f,0.0f);
for(int ix=0;ix< BOX_COUNT;ix++)
{
boxpos[ix]+=FALLSTEP;
if(boxpos[ix] > FALLHEIGHT)
boxpos[ix]-=FALLHEIGHT;
hb.pushMatrix();
hb.translate(0.0f,boxpos[ix],0.0f);
if(ix%2==0)
hb.rotateZ(vz);
else
hb.rotateZ(-2*vz);
if(ix%3==0)
hb.rotateX(vx);
else
hb.rotateX(-vx);
if(ix%2==0)
hb.rotateY(2*vy);
else
hb.rotateY(vy);
hb.setColor(boxcolor[ix]);
hb.drawBox(BOX_SIDE,BOX_SIDE,BOX_SIDE,gob.FILL);
hb.popMatrix();
}
Rotations vx,vy,vz is updated in the Applets run method. Stop it with right-click, change rotation with left-drag. Full source: _a3dfalling.java import java.awt.Polygon;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Component;
import java.awt.Label;
import java.awt.Rectangle;
import java.awt.event.*;
import java.applet.Applet;
import java.util.Vector;
import java.awt.geom.AffineTransform;
import java.awt.Graphics2D;
public class a3dfalling extends Applet implements Runnable
{
//boolean isStandalone = false;
gob hb=null;
// a thread that we can start and stop by mouseclick
Thread painter=null;
// running ?
boolean halted=false;
// use perspective or not
boolean isPerspective;
//---------- to make the demo spin ----------
float vx=0.0f;
float vy=0.0f;
float vz=0.0f;
float dvx=0.02f;
float dvy=0.03f;
float dvz=0.02f;
int lastx=0;
int lasty=0;
//-------------------------------
Label label1 = new Label();
//------------------------
//-------- keep track of boxes -------
final int BOX_COUNT=8;
final float BOX_SIDE=20.0f;
final float FALLSTEP=2.0f;
final float FALLHEIGHT=600.0f;
final float FALLWIDTH=40.0f;
float boxpos[]=null;
Color boxcolor[]=null;
//Construct the applet
public a3dfalling()
{
}
//Initialize the applet
public void init()
{
try
{
jbInit();
}
catch(Exception e)
{
e.printStackTrace();
}
}
//Component initialization
private void jbInit() throws Exception
{
label1.setBounds(new Rectangle(4, 170, 195, 23));
label1.setFont(new java.awt.Font("Dialog", 0, 9));
label1.setBackground(Color.white);
label1.setForeground(Color.blue);
label1.setText("click right: stop/start drag left: spin");
this.addMouseMotionListener(new java.awt.event.MouseMotionAdapter()
{
public void mouseDragged(MouseEvent e)
{
this_mouseDragged(e);
}
});
this.addMouseListener(new java.awt.event.MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
this_mouseClicked(e);
}
public void mousePressed(MouseEvent e)
{
this_mousePressed(e);
}
public void mouseReleased(MouseEvent e)
{
this_mouseReleased(e);
}
});
this.setBackground(Color.white);
this.setLayout(null);
//this.add(label1, null);
this.setBounds(0,0,(int)FALLWIDTH,(int)FALLHEIGHT);
hb=new gob(this);
boxpos=new float[BOX_COUNT];
boxcolor=new Color[BOX_COUNT];
for(int ix=0;ix<BOX_COUNT;ix++)
{
boxpos[ix]=(ix-1)*FALLHEIGHT/BOX_COUNT;
if(ix%2==0)
boxcolor[ix]=new Color(1.0f-1.0f*(ix+1)/BOX_COUNT,0.0f+1.0f*(ix+1)/BOX_COUNT,1.0f);
else
boxcolor[ix]=new Color(0.0f+1.0f*(ix+1)/BOX_COUNT,1.0f-1.0f*(ix+1)/BOX_COUNT,0.0f);
}
}
//Start the applet
public void start()
{
painter=new Thread(this);
halted=false;
painter.start();
}
//Stop the applet
public void stop()
{
if(painter!=null)
halted=true;
}
//Destroy the applet
public void destroy()
{
}
//Get Applet information
public String getAppletInfo()
{
return "Name: App3D," +
"Author: Børre Stenseth";
}
public void paint(Graphics g)
{
//-----------------
// a demo with some spinning boxes
hb.clearAll();
hb.setIdentityMatrix();
drawFallingBoxes();
hb.flush(g);
}
public void run()
{
int count=0;
while (!halted)
{
try
{
vx+=dvx;
vx+=dvy;
vz+=dvz;
repaint();
Thread.sleep(20);
}
catch (InterruptedException e)
{
stop();
}
}
}
// override update to avois flickering
public void update(Graphics g)
{
paint(g);
}
void drawFallingBoxes()
{
// a demo with some falling boxes
hb.setBackground(new Color(1.0f,1.0f,1.0f));
//hb.setBackground(new Color(0.0f,0.0f,0.0f));
// use default viewport and ortho, full applet
// boxes are "falling" in the direction of pos y
hb.translate(FALLWIDTH/2.0f+3.0f,0.0f,0.0f);
for(int ix=0;ix<BOX_COUNT;ix++)
{
boxpos[ix]+=FALLSTEP;
if(boxpos[ix] > FALLHEIGHT)
boxpos[ix]-=FALLHEIGHT;
hb.pushMatrix();
hb.translate(0.0f,boxpos[ix],0.0f);
if(ix%2==0) hb.rotateZ(vz); else hb.rotateZ(-2*vz);
if(ix%3==0) hb.rotateX(vx); else hb.rotateX(-vx);
if(ix%2==0) hb.rotateY(2*vy); else hb.rotateY(vy);
hb.setColor(boxcolor[ix]);
hb.drawBox(BOX_SIDE,BOX_SIDE,BOX_SIDE,gob.FILL);
hb.popMatrix();
}
}
//----------------------------------------------
// mousing around
void this_mouseClicked(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
{
if(halted)
start();
else
stop();
}
}
void this_mousePressed(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK)
{
lastx=e.getX();
lasty=e.getY();
halted=true;
stop();
}
}
void this_mouseReleased(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK)
{
int x=e.getX();
int y=e.getY();
/*
if((dvx!=0.0f)||(dvy!=0.0f))
{
*/
halted=false;
start();
/*
}
*/
}
}
void this_mouseDragged(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK)
{
int x=e.getX();
int y=e.getY();
dvx=5.0f*(x-lastx)/this.getSize().width;
dvy=5.0f*(y-lasty)/this.getSize().height;
dvz=dvx-dvy;
vx+=dvx;
vx+=dvy;
vz+=dvz;
repaint();
lastx=x;
lasty=y;
}
}
//--------------------------------------------------
// start of inner classes: gob, p3D, matrix, polygon
public class gob extends Object
{
//----------------------------------------------------------------
// defining the minimalistic graphical interface:
// clearAll, drawBox, flush, pBegin, pEnd, popMatrix, pushMatrix,
// rotateX, rotateY, rotateZ, scale, translate,
// setBackground, setColor, setIdentityMatrix,
// setOrtho, setperspective, setViewport, vertex
//-----------------------------------------------------------------
//-----------------------------------------------------------------
// intenal classes : p3D, matrix, polygon defined at end of file
//-----------------------------------------------------------------
// fill or outline polygons
public final static int FILL=0;
public final static int FRAME=1;
// the list of polygons to draw
Vector drawList=new Vector(20,6);
// matrix stack, for push and pop
Vector matrixStack=new Vector(5,5);
// current model(view) matrix
matrix curMatrix=null;
// current polygon, while we are adding vertexes
polygon curPoly=null;
// current drawing color
Color curColor=null;
// Background color, last before flush yields
Color BKColor=null;
// buffer to draw in to avoid flicker
Image Buffer;
// viewport
float Vxl,Vyt,Vw,Vh;
// window, model coordinates
float Wxl,Wyt,Ww,Wh;
// perspective angles, in degrees
float Pvx,Pvy;
// use perspective projection or not
boolean isPerspective;
// the actual component that gob will work on
Component theComp;
// possible background image
Image theImage=null;
public gob(Component comp)
{
Buffer=comp.createImage(comp.getSize().width,comp.getSize().height);
setViewport(0,0,comp.getSize().width,comp.getSize().height);
setOrtho(0,0,comp.getSize().width,comp.getSize().height);
theComp=comp;
curColor=new Color(0.9f,0.9f,0.9f);
BKColor=new Color(1.0f,1.0f,1.0f);
}
public void clearAll()
{
drawList.removeAllElements();
curPoly=null;
curMatrix=new matrix();
matrixStack.removeAllElements();
}
public void setIdentityMatrix()
{
curMatrix=new matrix();
}
public void pushMatrix()
{
if(curMatrix==null)
curMatrix=new matrix();
matrixStack.addElement(curMatrix.copyMatrix(curMatrix));
}
public void popMatrix()
{
if(matrixStack.size()<1)
{
//underflow
curMatrix=new matrix();
System.out.println("Matrix stack undeflow, too many pops");
return;
}
curMatrix=(matrix)matrixStack.lastElement();
matrixStack.removeElement(matrixStack.lastElement());
}
public void translate(float dx,float dy,float dz)
{
float[] values={
1.0f,0.0f,0.0f,dx,
0.0f,1.0f,0.0f,dy,
0.0f,0.0f,1.0f,dz,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void scale(float sx,float sy,float sz)
{
float[] values={
sx,0.0f,0.0f,0.0f,
0.0f,sy,0.0f,0.0f,
0.0f,0.0f,sz,0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateX(float v)
{
float[] values={
1.0f,0.0f,0.0f,0.0f,
0.0f,(float)Math.cos(v),(float)-Math.sin(v),0.0f,
0.0f,(float)Math.sin(v),(float)Math.cos(v),0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateY(float v)
{
float[] values={
(float)Math.cos(v),0.0f,(float)Math.sin(v),0.0f,
0.0f,1.0f,0.0f,0.0f,
(float)-Math.sin(v),0.0f,(float)Math.cos(v),0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateZ(float v)
{
float[] values={
(float)Math.cos(v),(float)-Math.sin(v),0.0f,0.0f,
(float)Math.sin(v),(float)Math.cos(v),0.0f,0.0f,
0.0f,0.0f,1.0f,0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void pBegin(int mode)
{
if(mode==FILL)
curPoly=new polygon(curColor,mode);
else
curPoly=new polygon(curColor,FRAME);
}
public void vertex(float nx, float ny, float nz)
{
p3D npnt=curMatrix.transform(new p3D(nx,ny,nz));
curPoly.appendVertex(npnt);
}
public void pEnd()
{
// calculate the angle for lighting
curPoly.calculateAngle();
// put it into the the drawList according to depth
// lowest minZ comes first in list
for(int ix=0;ix<drawList.size();ix++)
if(((polygon)drawList.elementAt(ix)).getMinZ()>curPoly.getMinZ())
{
drawList.insertElementAt(curPoly,ix);
return;
}
drawList.addElement(curPoly);
}
public void flush(Graphics g)
{
// project the polygons
for(int ix=0;ix<drawList.size();ix++)
{
// project it
if(isPerspective)
((polygon)drawList.elementAt(ix)).perspectiveProject(Pvx,Pvy, Wxl,Wyt,Ww,Wh, Vxl,Vyt,Vw,Vh);
else
((polygon)drawList.elementAt(ix)).paralellProject(Wxl,Wyt,Ww,Wh, Vxl,Vyt,Vw,Vh);
}
// draw drawList from bottom on an offscreen buffer
Graphics tmpg=Buffer.getGraphics();
// clear only the specified viewport
// tmpg.clearRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
tmpg.setColor(BKColor);
// fills with color or set out image
tmpg.fillRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
if(theImage!=null)
tmpg.drawImage(theImage,Math.round(Vxl),Math.round(Vyt),BKColor, null);
// draw it
for(int ix=0;ix<drawList.size();ix++)
((polygon)drawList.elementAt(ix)).render(tmpg);
// copy to viewport
g.clipRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
g.drawImage(Buffer,0,0,null);
}
public void setColor(Color c)
{
curColor=c;
}
public void setBackground(Color c)
{
BKColor=c;
//theImage=null;
}
public void setImage(Image im)
{
if(im==null)
theImage=null;
else
theImage=im;
}
public void setViewport(float xl,float yt,float w, float h)
{
Vxl=xl;Vyt=yt;Vw=w;Vh=h;
}
public void setOrtho(float xl,float yt,float w, float h)
{
Wxl=xl;Wyt=yt;Ww=w;Wh=h;
isPerspective=false;
}
public void setPerspective(float vx,float vy)
{
if(vx < 0.5)
Pvx=0.5f;
else if(vx > 89.5)
Pvx=89.5f;
else
Pvx=vx;
if(vy<0.5)
Pvy=0.5f;
else if(vy > 89.5)
Pvy=89.5f;
else
Pvy=vy;
isPerspective=true;
}
//----------------------------------------------
// draw a box, is handy since this is about all we
// would be tempted to draw in these surroundings
public void drawBox(float dx,float dy, float dz,int mode)
{
// bottom
float x=dx/2.0f;
float y=dy/2.0f;
float z=dz/2.0f;
pBegin(mode);
vertex(-x,y,-z); vertex(-x,-y,-z); vertex(x,-y,-z); vertex(x,y,-z);
pEnd();
// top
pBegin(mode);
vertex(-x,y,z); vertex(x,y,z); vertex(x,-y,z); vertex(-x,-y,z);
pEnd();
// front
pBegin(mode);
vertex(-x,y,-z); vertex(x,y,-z); vertex(x,y,z); vertex(-x,y,z);
pEnd();
// back
pBegin(mode);
vertex(-x,-y,-z); vertex(-x,-y,z); vertex(x,-y,z); vertex(x,-y,-z);
pEnd();
// left
pBegin(mode);
vertex(-x,y,-z); vertex(-x,y,z); vertex(-x,-y,z); vertex(-x,-y,-z);
pEnd();
// right
pBegin(mode);
vertex(x,-y,-z); vertex(x,-y,z); vertex(x,y,z); vertex(x,y,-z);
pEnd();
}
}
//----------------- end of gob
//------------------ start of p3D
class p3D extends Object
{
float x,y,z;
public p3D(float nx, float ny, float nz)
{
x=nx; y=ny; z=nz;
}
}
//----------------- end of p3D
//------------------ start of matrix
class matrix extends Object
{
float[][] m=null;
public matrix()
{
m=new float[][]{
{1.0f,0.0f,0.0f,0.0f},
{0.0f,1.0f,0.0f,0.0f},
{0.0f,0.0f,1.0f,0.0f},
{0.0f,0.0f,0.0f,1.0f}};
}
public matrix(float[] values)
{
m=new float[][]{
{1.0f,0.0f,0.0f,0.0f},
{0.0f,1.0f,0.0f,0.0f},
{0.0f,0.0f,1.0f,0.0f},
{0.0f,0.0f,0.0f,1.0f}};
if(values.length!=16)
return;
m[0][0]=values[0]; m[0][1]=values[1]; m[0][2]=values[2]; m[0][3]=values[3];
m[1][0]=values[4]; m[1][1]=values[5]; m[1][2]=values[6]; m[1][3]=values[7];
m[2][0]=values[8]; m[2][1]=values[9]; m[2][2]=values[10]; m[2][3]=values[11];
m[3][0]=values[12]; m[3][1]=values[13]; m[3][2]=values[14]; m[3][3]=values[15];
}
matrix copyMatrix(matrix sm)
{
float v[]={
sm.m[0][0],sm.m[0][1],sm.m[0][2],sm.m[0][3],
sm.m[1][0],sm.m[1][1],sm.m[1][2],sm.m[1][3],
sm.m[2][0],sm.m[2][1],sm.m[2][2],sm.m[2][3],
sm.m[3][0],sm.m[3][1],sm.m[3][2],sm.m[3][3]};
return new matrix(v);
}
p3D transform(p3D p)
{
return new p3D(
p.x*m[0][0]+p.y*m[0][1]+p.z*m[0][2]+m[0][3],
p.x*m[1][0]+p.y*m[1][1]+p.z*m[1][2]+m[1][3],
p.x*m[2][0]+p.y*m[2][1]+p.z*m[2][2]+m[2][3]);
}
void postMult(matrix tm)
{
// do: m = m X tm.m
float [][] oldm={
{m[0][0],m[0][1],m[0][2],m[0][3]},
{m[1][0],m[1][1],m[1][2],m[1][3]},
{m[2][0],m[2][1],m[2][2],m[2][3]},
{m[3][0],m[3][1],m[3][2],m[3][3]}};
for(int r=0;r<4;r++)
for(int k=0;k<4;k++)
m[r][k]=oldm[r][0]*tm.m[0][k]+
oldm[r][1]*tm.m[1][k]+
oldm[r][2]*tm.m[2][k]+
oldm[r][3]*tm.m[3][k];
}
}
//----------------- end of matrix
//------------------ start of polygon
class polygon extends Object
{
Vector p=new Vector(20,6);
float minZ=100000.0f; // higher than any point
Color pcolor=null;
float cosangle=0.0f;
Polygon pol=null;
int pmode;
public polygon(Color c,int mode)
{
pmode=mode;
pcolor=c;
}
void setColor(float r,float g,float b)
{
pcolor=new Color(r,g,b);
}
float getMinZ()
{
return minZ;
}
int getPCount()
{
return p.size();
}
p3D getPAt(int index)
{
if(index>=p.size() || index<0)
return new p3D(0.0f,0.0f,0.0f);
return (p3D)p.elementAt(index);
}
void setPAt(p3D np,int index)
{
if(index>=p.size() || index<0)
return;
((p3D)p.elementAt(index)).x=np.x;
((p3D)p.elementAt(index)).y=np.y;
((p3D)p.elementAt(index)).z=np.z;
}
void appendVertex(float x,float y, float z)
{
appendVertex(new p3D(x,y,z));
}
void appendVertex(p3D pnt)
{
if(pnt.z<minZ)
minZ=pnt.z;
p.addElement(pnt);
}
void calculateAngle()
{
// calculate normal and angle twds z-axis
if(p.size()<3)
{
cosangle=1.0f;
return;
}
// normal,normalV, from triangle: p0 - p1 - p2
// c=(a2b3-a3b2, a3b1-a1b3, a1b2-a2b1)
p3D p0=(p3D)p.elementAt(0);
p3D p1=(p3D)p.elementAt(1);
p3D p2=(p3D)p.elementAt(2);
p3D normalV=new p3D((p2.y-p1.y)*(p0.z-p1.z)-(p2.z-p1.z)*(p0.y-p1.y),
(p2.z-p1.z)*(p0.x-p1.x)-(p2.x-p1.x)*(p0.z-p1.z),
(p2.x-p1.x)*(p0.y-p1.y)-(p2.y-p1.y)*(p0.x-p1.x));
float len=(float)Math.sqrt(normalV.x*normalV.x+normalV.y*normalV.y+normalV.z*normalV.z);
// normalize. need only z-component
// dont care about direction
// big cosangle -> light face, since light is -z
cosangle=Math.abs(normalV.z/len);
}
//public void paralellProject(float xfactor,float yfactor)
public void paralellProject(
float Wxl, float Wyt, float Ww, float Wh,
float Vxl, float Vyt, float Vw, float Vh)
{
if(p.size()<2)
return;
pol=new Polygon();
float xfactor=Vw/Ww;
float yfactor=Vh/Wh;
for(int ix=0;ix<p.size();ix++)
pol.addPoint(
Math.round( (((p3D)p.elementAt(ix)).x-Wxl)*xfactor+Vxl),
Math.round( (((p3D)p.elementAt(ix)).y-Wyt)*yfactor+Vyt) );
}
public void perspectiveProject(
float vx,float vy,
float Wxl,float Wyt, float Ww, float Wh,
float Vxl, float Vyt, float Vw, float Vh)
{
// W is not used, assumes Rectangle(-0.5,-0.5,1,1)
if(p.size()<2)
return;
pol=new Polygon();
float xf=(float)(0.5f/Math.tan(Math.toRadians(vx)));
float yf=(float)(0.5f/Math.tan(Math.toRadians(vy)));
for(int ix=0;ix<p.size();ix++)
{
float x=((p3D)p.elementAt(ix)).x/Math.abs(((p3D)p.elementAt(ix)).z)*xf;
float y=((p3D)p.elementAt(ix)).y/Math.abs(((p3D)p.elementAt(ix)).z)*xf;
pol.addPoint(Math.round( (x+0.5f)*Vw+Vxl ),Math.round( (y+0.5f)*Vh+Vyt )
);
}
}
public void render(Graphics g)
{
if(pol==null)
return;
if(pmode==gob.FILL)
{
Color c=new Color(
(pcolor.getRed()*(cosangle/255.0f)),
(pcolor.getGreen()*(cosangle/255.0f)),
(pcolor.getBlue()*(cosangle)/255.0f));
g.setColor(c);
g.fillPolygon(pol);
}
else
{
g.setColor(pcolor);
g.drawPolygon(pol);
}
}
}
//------------- end of polygon
// -------------- end of inner classes
}
|
Runner
Source:
import java.awt.Polygon;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Component;
import java.awt.event.*;
import java.applet.Applet;
import java.util.Vector;
import java.awt.geom.AffineTransform;
import java.awt.Graphics2D;
public class a3dwalker extends Applet implements Runnable
{
// the 3D object
gob hb=null;
// a thread that we can start and stop by mouseclick
Thread painter=null;
// running ?
boolean halted=false;
// a walking man
theMan man=null;
//--------- make the man walk -------------
float dvt=0.08f;
//------------------------------------------
//---------- to make the demo spin ----------
float vx=0.0f;
float vy=0.0f;
float vz=0.0f;
float dvx=0.02f;
float dvy=0.01f;
float dvz=0.02f;
int lastx=0;
int lasty=0;
//--------------------------------------
//Construct the applet
public a3dwalker()
{
}
//Initialize the applet
public void init()
{
try { jbInit(); }
catch(Exception e){e.printStackTrace(); }
}
//Component initialization
private void jbInit() throws Exception
{
this.addMouseMotionListener(new java.awt.event.MouseMotionAdapter()
{
public void mouseDragged(MouseEvent e)
{
this_mouseDragged(e);
}
});
this.addMouseListener(new java.awt.event.MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
this_mouseClicked(e);
}
public void mousePressed(MouseEvent e)
{
this_mousePressed(e);
}
public void mouseReleased(MouseEvent e)
{
this_mouseReleased(e);
}
});
this.setBackground(Color.white);
this.setLayout(null);
hb=new gob(this);
man=new theMan();
}
//Start the applet
public void start()
{
painter=new Thread(this);
halted=false;
painter.start();
}
//Stop the applet
public void stop()
{
if(painter!=null)
halted=true;
}
//Destroy the applet
public void destroy()
{
}
//Get Applet information
public String getAppletInfo()
{
return "Name: App3D," +
"Author: Børre Stenseth";
}
public void paint(Graphics g)
{
//-----------------
// a demo with some spinning boxes
hb.clearAll();
drawAMan();
hb.flush(g);
}
public void run()
{
while (!halted)
{
try
{
vx+=dvx;
vx+=dvy;
vz+=dvz;
repaint();
Thread.sleep(20);
}
catch (InterruptedException e)
{
stop();
}
}
}
// override update to avois flickering
public void update(Graphics g)
{
paint(g);
}
void drawAMan()
{
hb.setBackground(new Color(1.0f,1.0f,1.0f));
hb.setOrtho(-1.5f,-1.5f,3.0f,3.0f);
hb.setViewport(0,0,200,200);
hb.rotateY(0.34906f);//(float)Math.toRadians(20.0));
hb.rotateX(1.57079f);//(float)Math.toRadians(90.0));
hb.translate(0.0f,-2.0f,0.0f);
hb.rotateZ(vz);
hb.rotateX(vx);
hb.rotateY(vy);
man.walkmove(dvt);
hb.setColor(new Color(1.0f,1.0f,0.0f));
man.drawMan(hb,gob.FILL);
}
//----------------------------------------------
// mousing around
void this_mouseClicked(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
{
if(halted)
start();
else
stop();
}
}
void this_mousePressed(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK)
{
lastx=e.getX();
lasty=e.getY();
halted=true;
stop();
}
}
void this_mouseReleased(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK)
{
int x=e.getX();
int y=e.getY();
halted=false;
start();
}
}
void this_mouseDragged(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK)
{
int x=e.getX();
int y=e.getY();
dvx=5.0f*(x-lastx)/this.getSize().width;
dvy=5.0f*(y-lasty)/this.getSize().height;
dvz=dvx-dvy;
vx+=dvx;
vx+=dvy;
vz+=dvz;
repaint();
lastx=x;
lasty=y;
}
}
//--------------------------------------------------
// start of inner classes: gob, p3D, matrix, polygon
public class gob extends Object
{
//----------------------------------------------------------------
// defining the minimalistic graphical interface:
// clearAll, drawBox, flush, pBegin, pEnd, popMatrix, pushMatrix,
// rotateX, rotateY, rotateZ, scale, translate,
// setBackground, setColor, setIdentityMatrix,
// setOrtho, setperspective, setViewport, vertex
//-----------------------------------------------------------------
//-----------------------------------------------------------------
// intenal classes : p3D, matrix, polygon defined at end of file
//-----------------------------------------------------------------
// fill or outline polygons
public final static int FILL=0;
public final static int FRAME=1;
// the list of polygons to draw
Vector drawList=new Vector(20,6);
// matrix stack, for push and pop
Vector matrixStack=new Vector(5,5);
// current model(view) matrix
matrix curMatrix=null;
// current polygon, while we are adding vertexes
polygon curPoly=null;
// current drawing color
Color curColor=null;
// Background color, last before flush yields
Color BKColor=null;
// buffer to draw in to avoid flicker
Image Buffer;
// viewport
float Vxl,Vyt,Vw,Vh;
// window, model coordinates
float Wxl,Wyt,Ww,Wh;
// perspective angles, in degrees
float Pvx,Pvy;
// use perspective projection or not
boolean isPerspective;
// the actual component that gob will work on
Component theComp;
// possible background image
Image theImage=null;
public gob(Component comp)
{
Buffer=comp.createImage(comp.getSize().width,comp.getSize().height);
setViewport(0,0,comp.getSize().width,comp.getSize().height);
setOrtho(0,0,comp.getSize().width,comp.getSize().height);
theComp=comp;
curColor=new Color(0.9f,0.9f,0.9f);
BKColor=new Color(1.0f,1.0f,1.0f);
}
public void clearAll()
{
drawList.removeAllElements();
curPoly=null;
curMatrix=new matrix();
matrixStack.removeAllElements();
}
public void setIdentityMatrix()
{
curMatrix=new matrix();
}
public void pushMatrix()
{
if(curMatrix==null)
curMatrix=new matrix();
matrixStack.addElement(curMatrix.copyMatrix(curMatrix));
}
public void popMatrix()
{
if(matrixStack.size()<1)
{
//underflow
curMatrix=new matrix();
System.out.println("Matrix stack undeflow, too many pops");
return;
}
curMatrix=(matrix)matrixStack.lastElement();
matrixStack.removeElement(matrixStack.lastElement());
}
public void translate(float dx,float dy,float dz)
{
float[] values={
1.0f,0.0f,0.0f,dx,
0.0f,1.0f,0.0f,dy,
0.0f,0.0f,1.0f,dz,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void scale(float sx,float sy,float sz)
{
float[] values={
sx,0.0f,0.0f,0.0f,
0.0f,sy,0.0f,0.0f,
0.0f,0.0f,sz,0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateX(float v)
{
float[] values={
1.0f,0.0f,0.0f,0.0f,
0.0f,(float)Math.cos(v),(float)-Math.sin(v),0.0f,
0.0f,(float)Math.sin(v),(float)Math.cos(v),0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateY(float v)
{
float[] values={
(float)Math.cos(v),0.0f,(float)Math.sin(v),0.0f,
0.0f,1.0f,0.0f,0.0f,
(float)-Math.sin(v),0.0f,(float)Math.cos(v),0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateZ(float v)
{
float[] values={
(float)Math.cos(v),(float)-Math.sin(v),0.0f,0.0f,
(float)Math.sin(v),(float)Math.cos(v),0.0f,0.0f,
0.0f,0.0f,1.0f,0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void pBegin(int mode)
{
if(mode==FILL)
curPoly=new polygon(curColor,mode);
else
curPoly=new polygon(curColor,FRAME);
}
public void vertex(float nx, float ny, float nz)
{
p3D npnt=curMatrix.transform(new p3D(nx,ny,nz));
curPoly.appendVertex(npnt);
}
public void pEnd()
{
// calculate the angle for lighting
curPoly.calculateAngle();
// put it into the the drawList according to depth
// lowest minZ comes first in list
for(int ix=0;ix<drawList.size();ix++)
if(((polygon)drawList.elementAt(ix)).getMinZ()>curPoly.getMinZ())
{
drawList.insertElementAt(curPoly,ix);
return;
}
drawList.addElement(curPoly);
}
public void flush(Graphics g)
{
// project the polygons
for(int ix=0;ix<drawList.size();ix++)
{
// project it
if(isPerspective)
((polygon)drawList.elementAt(ix)).perspectiveProject(Pvx,Pvy, Wxl,Wyt,Ww,Wh, Vxl,Vyt,Vw,Vh);
else
((polygon)drawList.elementAt(ix)).paralellProject(Wxl,Wyt,Ww,Wh, Vxl,Vyt,Vw,Vh);
}
// draw drawList from bottom on an offscreen buffer
Graphics tmpg=Buffer.getGraphics();
// clear only the specified viewport
// tmpg.clearRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
tmpg.setColor(BKColor);
// fills with color or set out image
tmpg.fillRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
if(theImage!=null)
tmpg.drawImage(theImage,Math.round(Vxl),Math.round(Vyt),BKColor, null);
// draw it
for(int ix=0;ix<drawList.size();ix++)
((polygon)drawList.elementAt(ix)).render(tmpg);
// copy to viewport
g.clipRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
g.drawImage(Buffer,0,0,null);
}
public void setColor(Color c)
{
curColor=c;
}
public void setBackground(Color c)
{
BKColor=c;
//theImage=null;
}
public void setImage(Image im)
{
if(im==null)
theImage=null;
else
theImage=im;
}
public void setViewport(float xl,float yt,float w, float h)
{
Vxl=xl;Vyt=yt;Vw=w;Vh=h;
}
public void setOrtho(float xl,float yt,float w, float h)
{
Wxl=xl;Wyt=yt;Ww=w;Wh=h;
isPerspective=false;
}
public void setPerspective(float vx,float vy)
{
if(vx < 0.5)
Pvx=0.5f;
else if(vx > 89.5)
Pvx=89.5f;
else
Pvx=vx;
if(vy<0.5)
Pvy=0.5f;
else if(vy > 89.5)
Pvy=89.5f;
else
Pvy=vy;
isPerspective=true;
}
//----------------------------------------------
// draw a box, is handy since this is about all we
// would be tempted to draw in these surroundings
public void drawBox(float dx,float dy, float dz,int mode)
{
// bottom
float x=dx/2.0f;
float y=dy/2.0f;
float z=dz/2.0f;
pBegin(mode);
vertex(-x,y,-z); vertex(-x,-y,-z); vertex(x,-y,-z); vertex(x,y,-z);
pEnd();
// top
pBegin(mode);
vertex(-x,y,z); vertex(x,y,z); vertex(x,-y,z); vertex(-x,-y,z);
pEnd();
// front
pBegin(mode);
vertex(-x,y,-z); vertex(x,y,-z); vertex(x,y,z); vertex(-x,y,z);
pEnd();
// back
pBegin(mode);
vertex(-x,-y,-z); vertex(-x,-y,z); vertex(x,-y,z); vertex(x,-y,-z);
pEnd();
// left
pBegin(mode);
vertex(-x,y,-z); vertex(-x,y,z); vertex(-x,-y,z); vertex(-x,-y,-z);
pEnd();
// right
pBegin(mode);
vertex(x,-y,-z); vertex(x,-y,z); vertex(x,y,z); vertex(x,y,-z);
pEnd();
}
}
//----------------- end of gob
//------------------ start of p3D
class p3D extends Object
{
float x,y,z;
public p3D(float nx, float ny, float nz)
{
x=nx; y=ny; z=nz;
}
}
//----------------- end of p3D
//------------------ start of matrix
class matrix extends Object
{
float[][] m=null;
public matrix()
{
m=new float[][]{
{1.0f,0.0f,0.0f,0.0f},
{0.0f,1.0f,0.0f,0.0f},
{0.0f,0.0f,1.0f,0.0f},
{0.0f,0.0f,0.0f,1.0f}};
}
public matrix(float[] values)
{
m=new float[][]{
{1.0f,0.0f,0.0f,0.0f},
{0.0f,1.0f,0.0f,0.0f},
{0.0f,0.0f,1.0f,0.0f},
{0.0f,0.0f,0.0f,1.0f}};
if(values.length!=16)
return;
m[0][0]=values[0]; m[0][1]=values[1]; m[0][2]=values[2]; m[0][3]=values[3];
m[1][0]=values[4]; m[1][1]=values[5]; m[1][2]=values[6]; m[1][3]=values[7];
m[2][0]=values[8]; m[2][1]=values[9]; m[2][2]=values[10]; m[2][3]=values[11];
m[3][0]=values[12]; m[3][1]=values[13]; m[3][2]=values[14]; m[3][3]=values[15];
}
matrix copyMatrix(matrix sm)
{
float v[]={
sm.m[0][0],sm.m[0][1],sm.m[0][2],sm.m[0][3],
sm.m[1][0],sm.m[1][1],sm.m[1][2],sm.m[1][3],
sm.m[2][0],sm.m[2][1],sm.m[2][2],sm.m[2][3],
sm.m[3][0],sm.m[3][1],sm.m[3][2],sm.m[3][3]};
return new matrix(v);
}
p3D transform(p3D p)
{
return new p3D(
p.x*m[0][0]+p.y*m[0][1]+p.z*m[0][2]+m[0][3],
p.x*m[1][0]+p.y*m[1][1]+p.z*m[1][2]+m[1][3],
p.x*m[2][0]+p.y*m[2][1]+p.z*m[2][2]+m[2][3]);
}
void postMult(matrix tm)
{
// do: m = m X tm.m
float [][] oldm={
{m[0][0],m[0][1],m[0][2],m[0][3]},
{m[1][0],m[1][1],m[1][2],m[1][3]},
{m[2][0],m[2][1],m[2][2],m[2][3]},
{m[3][0],m[3][1],m[3][2],m[3][3]}};
for(int r=0;r<4;r++)
for(int k=0;k<4;k++)
m[r][k]=oldm[r][0]*tm.m[0][k]+
oldm[r][1]*tm.m[1][k]+
oldm[r][2]*tm.m[2][k]+
oldm[r][3]*tm.m[3][k];
}
}
//----------------- end of matrix
//------------------ start of polygon
class polygon extends Object
{
Vector p=new Vector(20,6);
float minZ=100000.0f; // higher than any point
Color pcolor=null;
float cosangle=0.0f;
Polygon pol=null;
int pmode;
public polygon(Color c,int mode)
{
pmode=mode;
pcolor=c;
}
void setColor(float r,float g,float b)
{
pcolor=new Color(r,g,b);
}
float getMinZ()
{
return minZ;
}
int getPCount()
{
return p.size();
}
p3D getPAt(int index)
{
if(index>=p.size() || index<0)
return new p3D(0.0f,0.0f,0.0f);
return (p3D)p.elementAt(index);
}
void setPAt(p3D np,int index)
{
if(index>=p.size() || index<0)
return;
((p3D)p.elementAt(index)).x=np.x;
((p3D)p.elementAt(index)).y=np.y;
((p3D)p.elementAt(index)).z=np.z;
}
void appendVertex(float x,float y, float z)
{
appendVertex(new p3D(x,y,z));
}
void appendVertex(p3D pnt)
{
if(pnt.z<minZ)
minZ=pnt.z;
p.addElement(pnt);
}
void calculateAngle()
{
// calculate normal and angle twds z-axis
if(p.size()<3)
{
cosangle=1.0f;
return;
}
// normal,normalV, from triangle: p0 - p1 - p2
// c=(a2b3-a3b2, a3b1-a1b3, a1b2-a2b1)
p3D p0=(p3D)p.elementAt(0);
p3D p1=(p3D)p.elementAt(1);
p3D p2=(p3D)p.elementAt(2);
p3D normalV=new p3D((p2.y-p1.y)*(p0.z-p1.z)-(p2.z-p1.z)*(p0.y-p1.y),
(p2.z-p1.z)*(p0.x-p1.x)-(p2.x-p1.x)*(p0.z-p1.z),
(p2.x-p1.x)*(p0.y-p1.y)-(p2.y-p1.y)*(p0.x-p1.x));
float len=(float)Math.sqrt(normalV.x*normalV.x+normalV.y*normalV.y+normalV.z*normalV.z);
// normalize. need only z-component
// dont care about direction
// big cosangle -> light face, since light is -z
cosangle=Math.abs(normalV.z/len);
}
//public void paralellProject(float xfactor,float yfactor)
public void paralellProject(
float Wxl, float Wyt, float Ww, float Wh,
float Vxl, float Vyt, float Vw, float Vh)
{
if(p.size()<2)
return;
pol=new Polygon();
float xfactor=Vw/Ww;
float yfactor=Vh/Wh;
for(int ix=0;ix<p.size();ix++)
pol.addPoint(
Math.round( (((p3D)p.elementAt(ix)).x-Wxl)*xfactor+Vxl),
Math.round( (((p3D)p.elementAt(ix)).y-Wyt)*yfactor+Vyt) );
}
public void perspectiveProject(
float vx,float vy,
float Wxl,float Wyt, float Ww, float Wh,
float Vxl, float Vyt, float Vw, float Vh)
{
// W is not used, assumes Rectangle(-0.5,-0.5,1,1)
if(p.size()<2)
return;
pol=new Polygon();
float xf=(float)(0.5f/Math.tan(Math.toRadians(vx)));
float yf=(float)(0.5f/Math.tan(Math.toRadians(vy)));
for(int ix=0;ix<p.size();ix++)
{
float x=((p3D)p.elementAt(ix)).x/Math.abs(((p3D)p.elementAt(ix)).z)*xf;
float y=((p3D)p.elementAt(ix)).y/Math.abs(((p3D)p.elementAt(ix)).z)*xf;
pol.addPoint(Math.round( (x+0.5f)*Vw+Vxl ),Math.round( (y+0.5f)*Vh+Vyt )
);
}
}
public void render(Graphics g)
{
if(pol==null)
return;
if(pmode==gob.FILL)
{
Color c=new Color(
(pcolor.getRed()*(cosangle/255.0f)),
(pcolor.getGreen()*(cosangle/255.0f)),
(pcolor.getBlue()*(cosangle)/255.0f));
g.setColor(c);
g.fillPolygon(pol);
}
else
{
g.setColor(pcolor);
g.drawPolygon(pol);
}
}
}
//------------- end of polygon
// -----------------------------
// describing a simple robot:
// origo in middle of torso
// head up in z, facing x
//-----------------------------
//------------------------------
// the robot can only do a silly run,
// see method: walkmove
// if you want other functionality
// you must write other methods which
// updates the robots angles
//------------------------------
public class theMan
{
// dimensions: x,y,z
final float torso[]= {0.2f, 0.4f, 0.6f};
final float head[]= {0.1f, 0.2f, 0.2f};
final float overarm[]= {0.1f, 0.1f, 0.25f};
final float underarm[]={0.1f, 0.1f, 0.30f};
final float overleg[]= {0.1f, 0.1f, 0.3f};
final float underleg[]={0.1f, 0.1f, 0.35f};
// angles: around x,y,z
float v_head[]= {0.0f,0.0f,0.0f};
float left_shoulder[]= {0.0f,0.0f,0.0f};
float left_elboe[]= {0.0f,0.0f,0.0f};
float right_shoulder[]={0.0f,0.0f,0.0f};
float right_elboe[]= {0.0f,0.0f,0.0f};
float left_hip[]= {0.0f,0.0f,0.0f};
float left_knee[]= {0.0f,0.0f,0.0f};
float right_hip[]= {0.0f,0.0f,0.0f};
float right_knee[]= {0.0f,0.0f,0.0f};
// joint space
final float joint=0.05f;
//-----------------------------------------
// describing the walk/run
// state in movement, argument to sinus function
float state=0.0f;
// max and min hip angles, refers to rotateY
final float minhip=-0.52359f;//(float)Math.toRadians(-30.0f);
final float maxhip=1.22173f;//(float)Math.toRadians(70.0f);
// max and min knee angles, refers to rotateY
final float minknee=0.34906f;//(float)Math.toRadians(20.0f);
final float maxknee=1.57079f;//(float)Math.toRadians(90.0f);
// max and min shoulder angles, refers to rotateY
final float minshoulder=-1.04719f;//(float)Math.toRadians(-60.0f);
final float maxshoulder=1.04719f;//((float)Math.toRadians(60.0f);
// max and min elboe angles, refers to rotateY
final float minelboe=0.174532f;//(float)Math.toRadians(10.0f);
final float maxelboe=1.57079f;//(float)Math.toRadians(90.0f);
public theMan()
{
}
public void walkmove(float incr)
{
// walking according to a sinus description
// inr is an increment on a sinus function
// increments state
state+=incr;
float sx=(float)Math.sin(state);
if(sx>=0.0f)
{
// calculate left arm - forward
left_shoulder[1]=-maxshoulder*sx;
left_elboe[1]=-maxelboe*sx;
// calculate right arm - backward
right_shoulder[1]=-minshoulder*sx;
right_elboe[1]=-minelboe*sx;
// calculate right leg - forward
right_hip[1]=-maxhip*sx;
right_knee[1]=maxknee*sx;
// calculate left leg - backward
left_hip[1]=-minhip*sx;
left_knee[1]=minknee*sx;
}
else
{
// calculate left arm - backward
left_shoulder[1]=minshoulder*sx;
left_elboe[1]=minelboe*sx;
// calculate right arm - forward
right_shoulder[1]=maxshoulder*sx;
right_elboe[1]=maxelboe*sx;
// calculate right leg - backward
right_hip[1]=minhip*sx;
right_knee[1]=-minknee*sx;
// calculate left leg - forward
left_hip[1]=maxhip*sx;
left_knee[1]=-maxknee*sx;
}
}
public void drawMan(gob gb,int mode)
{
// origo in middle of torso
gb.drawBox(torso[0],torso[1],torso[2],mode);
gb.pushMatrix();
// head
gb.translate(0.0f,0.0f,torso[2]/2.0f+joint/2.0f);
gb.rotateX(v_head[0]);
gb.rotateY(v_head[1]);
gb.rotateZ(v_head[2]);
gb.translate(0.0f,0.0f,head[2]/2.0f);
gb.drawBox(head[0],head[1],head[2],mode);
gb.popMatrix();
gb.pushMatrix();
// left arm
gb.translate(0.0f,torso[1]/2.0f+joint/2.0f+overarm[1]/2.0f,torso[2]/2.0f);
gb.rotateX(left_shoulder[0]);
gb.rotateY(left_shoulder[1]);
gb.rotateZ(left_shoulder[2]);
gb.translate(0.0f,0.0f,-overarm[2]/2.0f);
gb.drawBox(overarm[0],overarm[1],overarm[2],mode);
//---------------
gb.translate(0.0f,0.0f,-overarm[2]/2.0f-joint/2.0f);
gb.rotateX(left_elboe[0]);
gb.rotateY(left_elboe[1]);
gb.rotateZ(left_elboe[2]);
gb.translate(0.0f,0.0f,-underarm[2]/2.0f-joint/2.0f);
gb.drawBox(underarm[0],underarm[1],underarm[2],mode);
gb.popMatrix();
gb.pushMatrix();
// right arm
gb.translate(0.0f,-torso[1]/2.0f-joint/2.0f-overarm[1]/2.0f,torso[2]/2.0f);
gb.rotateX(right_shoulder[0]);
gb.rotateY(right_shoulder[1]);
gb.rotateZ(right_shoulder[2]);
gb.translate(0.0f,0.0f,-overarm[2]/2.0f);
gb.drawBox(overarm[0],overarm[1],overarm[2],mode);
//---------------
gb.translate(0.0f,0.0f,-overarm[2]/2.0f-joint/2.0f);
gb.rotateX(right_elboe[0]);
gb.rotateY(right_elboe[1]);
gb.rotateZ(right_elboe[2]);
gb.translate(0.0f,0.0f,-underarm[2]/2.0f-joint/2.0f);
gb.drawBox(underarm[0],underarm[1],underarm[2],mode);
gb.popMatrix();
gb.pushMatrix();
// left leg
gb.translate(0.0f,torso[1]/2.0f-overleg[1]/2.0f,-torso[2]/2.0f-joint/2.0f);
gb.rotateX(left_hip[0]);
gb.rotateY(left_hip[1]);
gb.rotateZ(left_hip[2]);
gb.translate(0.0f,0.0f,-overleg[2]/2.0f);
gb.drawBox(overleg[0],overleg[1],overleg[2],mode);
//---------------
gb.translate(0.0f,0.0f,-overleg[2]/2.0f-joint/2.0f);
gb.rotateX(left_knee[0]);
gb.rotateY(left_knee[1]);
gb.rotateZ(left_knee[2]);
gb.translate(0.0f,0.0f,-underleg[2]/2.0f-joint/2.0f);
gb.drawBox(underleg[0],underleg[1],underleg[2],mode);
gb.popMatrix();
gb.pushMatrix();
// right leg
gb.translate(0.0f,-torso[1]/2.0f+overleg[1]/2.0f,-torso[2]/2.0f-joint/2.0f);
gb.rotateX(right_hip[0]);
gb.rotateY(right_hip[1]);
gb.rotateZ(right_hip[2]);
gb.translate(0.0f,0.0f,-overleg[2]/2.0f);
gb.drawBox(overleg[0],overleg[1],overleg[2],mode);
//---------------
gb.translate(0.0f,0.0f,-overleg[2]/2.0f-joint/2.0f);
gb.rotateX(right_knee[0]);
gb.rotateY(right_knee[1]);
gb.rotateZ(right_knee[2]);
gb.translate(0.0f,0.0f,-underleg[2]/2.0f-joint/2.0f);
gb.drawBox(underleg[0],underleg[1],underleg[2],mode);
gb.popMatrix();
}
}
// -------------- end of inner classes
}
Movie
The Applet combines the 3d-robot, theMan, from Applet "Runner" with a scrolling of images. The Applet takes the following parameters:
- piccount value=2
- picext value="jpg"
- sleep value=20
- imagepath value="images"
which means it takes two jpg-images in folder images. The Applet updates the background each 20 milliseconds. The two images are very simle:
A better solution will of course be to construct a complete surrounding scene.
Source:
import java.awt.Polygon;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Component;
import java.awt.MediaTracker;
import java.awt.event.*;
import java.applet.Applet;
import java.util.Vector;
import java.awt.geom.AffineTransform;
import java.awt.Graphics2D;
public class a3dmovie extends Applet implements Runnable
{
// the 3D object
gob hb=null;
// a thread that we can start and stop by mouseclick
Thread painter=null;
// running ?
boolean halted=false;
// use perspective or not
boolean isPerspective;
// a walking man
theMan man=null;
//--------- make the man walk -------------
float dvt=0.08f;
//------------------------------------------
//--------- a background movie --------
// !!!assume all images of same size!!!
Image Images[]=null;
boolean AllLoaded=false;
int curMovieLen=0;
//---------- parameters --------------
private int m_piccount = 0;
private String m_picext = "";
private long m_sleep = 100;
private String m_imagePath="images";
private int NUM_IMAGES = 1;
//---------- parameter names ----------
private final String PARAM_piccount = "piccount";
private final String PARAM_picext = "picext";
private final String PARAM_sleep = "sleep";
private final String PARAM_imagepath = "imagepath";
//Construct the applet
public a3dmovie()
{
}
public String[][] getParameterInfo()
{
String[][] info =
{
{ PARAM_piccount, "int", "Number of pictures" },
{ PARAM_picext, "String", "Pictype: gif,jpg" },
{ PARAM_sleep, "long", "Delay in ms" },
{ PARAM_imagepath, "String", "Path to images" },
};
return info;
}
//Initialize the applet
public void init()
{
try { jbInit(); }
catch(Exception e){e.printStackTrace(); }
}
//Component initialization
private void jbInit() throws Exception
{
//------- params --------------
String param;
// number of images
param = getParameter(PARAM_piccount);
if (param != null)
m_piccount = Integer.parseInt(param);
NUM_IMAGES=m_piccount;
// type of images
param = getParameter(PARAM_picext);
if (param != null)
m_picext = param;
// timeslot
param = getParameter(PARAM_sleep);
if (param != null)
m_sleep = Long.parseLong(param);
// relative imagepath
param = getParameter(PARAM_imagepath);
if(param != null)
m_imagePath = param;
//----------------------------------------------
this.addMouseListener(new java.awt.event.MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
this_mouseClicked(e);
}
});
this.setBackground(Color.white);
this.setLayout(null);
hb=new gob(this);
man=new theMan();
}
//Start the applet
public void start()
{
painter=new Thread(this);
halted=false;
painter.start();
}
//Stop the applet
public void stop()
{
if(painter!=null)
halted=true;
}
//Destroy the applet
public void destroy()
{
}
//Get Applet information
public String getAppletInfo()
{
return "Name: App3D, Author: Børre Stenseth";
}
public void paint(Graphics g)
{
if(!AllLoaded)
g.drawString("Loading images. Please wait...", 10, 20);
else
{
hb.clearAll();
hb.setIdentityMatrix();
drawAMan();
hb.flush(g);
}
}
public void run()
{
if(!AllLoaded)
{
Images = new Image[NUM_IMAGES];
repaint();
MediaTracker tracker = new MediaTracker(this);
for (int i = 1; i <= NUM_IMAGES; i++)
{
String strImage = m_imagePath+"/img00" + ((i < 10) ? "0" : "") + i + "."+m_picext;
Images[i-1] = getImage(getDocumentBase(), strImage);
tracker.addImage(Images[i-1], 0);
}
try
{
tracker.waitForAll();
AllLoaded = !tracker.isErrorAny();
}
catch (InterruptedException e)
{
}
}
int count=0;
while (!halted)
{
try
{
curMovieLen+=2;
repaint();
Thread.sleep(m_sleep);
}
catch (InterruptedException e)
{
stop();
}
}
}
// override update to avois flickering
public void update(Graphics g)
{
paint(g);
}
void drawAMan()
{
hb.setBackground(new Color(1.0f,1.0f,1.0f));
hb.setOrtho(-1.0f,-1.0f,4.0f,2.0f);
hb.rotateY(0.34906f);//(float)Math.toRadians(20.0));
hb.rotateX(1.57079f);//(float)Math.toRadians(90.0));
hb.translate(0.0f,-2.0f,0.0f);
hb.setViewport(0,0,600,164);
//-------------
if(AllLoaded)
{
Image im=ProduceImage(Math.abs(curMovieLen),600);
hb.setImage(im);
}
man.walkmove(dvt);
hb.setColor(new Color(1.0f,1.0f,0.0f));
man.drawMan(hb,gob.FILL);
}
//-------------- moviestuff -----------------
public Image ProduceImage(int L,int Vw)
{
// all images same width and height
int ImHeigth=Images[0].getHeight(this);
int ImWidth=Images[0].getWidth(this);
int ix;
int imCount=Images.length;
int lastimIx=imCount-1;
// length of all images
int movielen=imCount*ImWidth;
while(L>movielen) L-=movielen;
// find first image and offset of this image
int picix=L/ImWidth;
int firstoffset=L%ImWidth;
if(picix>lastimIx)
{
picix=0;
}
// production
Image Buffer=this.createImage(Vw,ImHeigth);
Graphics tmpg=Buffer.getGraphics();
// partly first image
tmpg.drawImage(
Images[picix],
0,0,ImWidth-firstoffset,ImHeigth, //destination
firstoffset,0,ImWidth,ImHeigth,
new Color(1.0f,1.0f,1.0f),
null);
int offset=ImWidth-firstoffset;
while(offset < Vw)
{
if(picix<imCount-1) picix++; else picix=0;
tmpg.drawImage(
Images[picix],
offset,0,offset+ImWidth,ImHeigth, //destination
0,0,ImWidth,ImHeigth,
new Color(1.0f,1.0f,1.0f),
null);
offset+=ImWidth;
}
return Buffer;
}
//----------------------------------------------
// mousing
void this_mouseClicked(MouseEvent e)
{
if(halted)
start();
else
stop();
}
//--------------------------------------------------
// start of inner classes: gob, p3D, matrix, polygon
public class gob extends Object
{
//----------------------------------------------------------------
// defining the minimalistic graphical interface:
// clearAll, drawBox, flush, pBegin, pEnd, popMatrix, pushMatrix,
// rotateX, rotateY, rotateZ, scale, translate,
// setBackground, setColor, setIdentityMatrix,
// setOrtho, setperspective, setViewport, vertex
//-----------------------------------------------------------------
//-----------------------------------------------------------------
// intenal classes : p3D, matrix, polygon defined at end of file
//-----------------------------------------------------------------
// fill or outline polygons
public final static int FILL=0;
public final static int FRAME=1;
// the list of polygons to draw
Vector drawList=new Vector(20,6);
// matrix stack, for push and pop
Vector matrixStack=new Vector(5,5);
// current model(view) matrix
matrix curMatrix=null;
// current polygon, while we are adding vertexes
polygon curPoly=null;
// current drawing color
Color curColor=null;
// Background color, last before flush yields
Color BKColor=null;
// buffer to draw in to avoid flicker
Image Buffer;
// viewport
float Vxl,Vyt,Vw,Vh;
// window, model coordinates
float Wxl,Wyt,Ww,Wh;
// perspective angles, in degrees
float Pvx,Pvy;
// use perspective projection or not
boolean isPerspective;
// the actual component that gob will work on
Component theComp;
// possible background image
Image theImage=null;
public gob(Component comp)
{
Buffer=comp.createImage(comp.getSize().width,comp.getSize().height);
setViewport(0,0,comp.getSize().width,comp.getSize().height);
setOrtho(0,0,comp.getSize().width,comp.getSize().height);
theComp=comp;
curColor=new Color(0.9f,0.9f,0.9f);
BKColor=new Color(1.0f,1.0f,1.0f);
}
public void clearAll()
{
drawList.removeAllElements();
curPoly=null;
curMatrix=new matrix();
matrixStack.removeAllElements();
}
public void setIdentityMatrix()
{
curMatrix=new matrix();
}
public void pushMatrix()
{
if(curMatrix==null)
curMatrix=new matrix();
matrixStack.addElement(curMatrix.copyMatrix(curMatrix));
}
public void popMatrix()
{
if(matrixStack.size()<1)
{
//underflow
curMatrix=new matrix();
System.out.println("Matrix stack undeflow, too many pops");
return;
}
curMatrix=(matrix)matrixStack.lastElement();
matrixStack.removeElement(matrixStack.lastElement());
}
public void translate(float dx,float dy,float dz)
{
float[] values={
1.0f,0.0f,0.0f,dx,
0.0f,1.0f,0.0f,dy,
0.0f,0.0f,1.0f,dz,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void scale(float sx,float sy,float sz)
{
float[] values={
sx,0.0f,0.0f,0.0f,
0.0f,sy,0.0f,0.0f,
0.0f,0.0f,sz,0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateX(float v)
{
float[] values={
1.0f,0.0f,0.0f,0.0f,
0.0f,(float)Math.cos(v),(float)-Math.sin(v),0.0f,
0.0f,(float)Math.sin(v),(float)Math.cos(v),0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateY(float v)
{
float[] values={
(float)Math.cos(v),0.0f,(float)Math.sin(v),0.0f,
0.0f,1.0f,0.0f,0.0f,
(float)-Math.sin(v),0.0f,(float)Math.cos(v),0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateZ(float v)
{
float[] values={
(float)Math.cos(v),(float)-Math.sin(v),0.0f,0.0f,
(float)Math.sin(v),(float)Math.cos(v),0.0f,0.0f,
0.0f,0.0f,1.0f,0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void pBegin(int mode)
{
if(mode==FILL)
curPoly=new polygon(curColor,mode);
else
curPoly=new polygon(curColor,FRAME);
}
public void vertex(float nx, float ny, float nz)
{
p3D npnt=curMatrix.transform(new p3D(nx,ny,nz));
curPoly.appendVertex(npnt);
}
public void pEnd()
{
// calculate the angle for lighting
curPoly.calculateAngle();
// put it into the the drawList according to depth
// lowest minZ comes first in list
for(int ix=0;ix<drawList.size();ix++)
if(((polygon)drawList.elementAt(ix)).getMinZ()>curPoly.getMinZ())
{
drawList.insertElementAt(curPoly,ix);
return;
}
drawList.addElement(curPoly);
}
public void flush(Graphics g)
{
// project the polygons
for(int ix=0;ix<drawList.size();ix++)
{
// project it
if(isPerspective)
((polygon)drawList.elementAt(ix)).perspectiveProject(Pvx,Pvy, Wxl,Wyt,Ww,Wh, Vxl,Vyt,Vw,Vh);
else
((polygon)drawList.elementAt(ix)).paralellProject(Wxl,Wyt,Ww,Wh, Vxl,Vyt,Vw,Vh);
}
// draw drawList from bottom on an offscreen buffer
Graphics tmpg=Buffer.getGraphics();
// clear only the specified viewport
// tmpg.clearRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
tmpg.setColor(BKColor);
// fills with color or set out image
tmpg.fillRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
if(theImage!=null)
tmpg.drawImage(theImage,Math.round(Vxl),Math.round(Vyt),BKColor, null);
// draw it
for(int ix=0;ix<drawList.size();ix++)
((polygon)drawList.elementAt(ix)).render(tmpg);
// copy to viewport
g.clipRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
g.drawImage(Buffer,0,0,null);
}
public void setColor(Color c)
{
curColor=c;
}
public void setBackground(Color c)
{
BKColor=c;
//theImage=null;
}
public void setImage(Image im)
{
if(im==null)
theImage=null;
else
theImage=im;
}
public void setViewport(float xl,float yt,float w, float h)
{
Vxl=xl;Vyt=yt;Vw=w;Vh=h;
}
public void setOrtho(float xl,float yt,float w, float h)
{
Wxl=xl;Wyt=yt;Ww=w;Wh=h;
isPerspective=false;
}
public void setPerspective(float vx,float vy)
{
if(vx < 0.5)
Pvx=0.5f;
else if(vx > 89.5)
Pvx=89.5f;
else
Pvx=vx;
if(vy<0.5)
Pvy=0.5f;
else if(vy > 89.5)
Pvy=89.5f;
else
Pvy=vy;
isPerspective=true;
}
//----------------------------------------------
// draw a box, is handy since this is about all we
// would be tempted to draw in these surroundings
public void drawBox(float dx,float dy, float dz,int mode)
{
// bottom
float x=dx/2.0f;
float y=dy/2.0f;
float z=dz/2.0f;
pBegin(mode);
vertex(-x,y,-z); vertex(-x,-y,-z); vertex(x,-y,-z); vertex(x,y,-z);
pEnd();
// top
pBegin(mode);
vertex(-x,y,z); vertex(x,y,z); vertex(x,-y,z); vertex(-x,-y,z);
pEnd();
// front
pBegin(mode);
vertex(-x,y,-z); vertex(x,y,-z); vertex(x,y,z); vertex(-x,y,z);
pEnd();
// back
pBegin(mode);
vertex(-x,-y,-z); vertex(-x,-y,z); vertex(x,-y,z); vertex(x,-y,-z);
pEnd();
// left
pBegin(mode);
vertex(-x,y,-z); vertex(-x,y,z); vertex(-x,-y,z); vertex(-x,-y,-z);
pEnd();
// right
pBegin(mode);
vertex(x,-y,-z); vertex(x,-y,z); vertex(x,y,z); vertex(x,y,-z);
pEnd();
}
}
//----------------- end of gob
//------------------ start of p3D
class p3D extends Object
{
float x,y,z;
public p3D(float nx, float ny, float nz)
{
x=nx; y=ny; z=nz;
}
}
//----------------- end of p3D
//------------------ start of matrix
class matrix extends Object
{
float[][] m=null;
public matrix()
{
m=new float[][]{
{1.0f,0.0f,0.0f,0.0f},
{0.0f,1.0f,0.0f,0.0f},
{0.0f,0.0f,1.0f,0.0f},
{0.0f,0.0f,0.0f,1.0f}};
}
public matrix(float[] values)
{
m=new float[][]{
{1.0f,0.0f,0.0f,0.0f},
{0.0f,1.0f,0.0f,0.0f},
{0.0f,0.0f,1.0f,0.0f},
{0.0f,0.0f,0.0f,1.0f}};
if(values.length!=16)
return;
m[0][0]=values[0]; m[0][1]=values[1]; m[0][2]=values[2]; m[0][3]=values[3];
m[1][0]=values[4]; m[1][1]=values[5]; m[1][2]=values[6]; m[1][3]=values[7];
m[2][0]=values[8]; m[2][1]=values[9]; m[2][2]=values[10]; m[2][3]=values[11];
m[3][0]=values[12]; m[3][1]=values[13]; m[3][2]=values[14]; m[3][3]=values[15];
}
matrix copyMatrix(matrix sm)
{
float v[]={
sm.m[0][0],sm.m[0][1],sm.m[0][2],sm.m[0][3],
sm.m[1][0],sm.m[1][1],sm.m[1][2],sm.m[1][3],
sm.m[2][0],sm.m[2][1],sm.m[2][2],sm.m[2][3],
sm.m[3][0],sm.m[3][1],sm.m[3][2],sm.m[3][3]};
return new matrix(v);
}
p3D transform(p3D p)
{
return new p3D(
p.x*m[0][0]+p.y*m[0][1]+p.z*m[0][2]+m[0][3],
p.x*m[1][0]+p.y*m[1][1]+p.z*m[1][2]+m[1][3],
p.x*m[2][0]+p.y*m[2][1]+p.z*m[2][2]+m[2][3]);
}
void postMult(matrix tm)
{
// do: m = m X tm.m
float [][] oldm={
{m[0][0],m[0][1],m[0][2],m[0][3]},
{m[1][0],m[1][1],m[1][2],m[1][3]},
{m[2][0],m[2][1],m[2][2],m[2][3]},
{m[3][0],m[3][1],m[3][2],m[3][3]}};
for(int r=0;r<4;r++)
for(int k=0;k<4;k++)
m[r][k]=oldm[r][0]*tm.m[0][k]+
oldm[r][1]*tm.m[1][k]+
oldm[r][2]*tm.m[2][k]+
oldm[r][3]*tm.m[3][k];
}
}
//----------------- end of matrix
//------------------ start of polygon
class polygon extends Object
{
Vector p=new Vector(20,6);
float minZ=100000.0f; // higher than any point
Color pcolor=null;
float cosangle=0.0f;
Polygon pol=null;
int pmode;
public polygon(Color c,int mode)
{
pmode=mode;
pcolor=c;
}
void setColor(float r,float g,float b)
{
pcolor=new Color(r,g,b);
}
float getMinZ()
{
return minZ;
}
int getPCount()
{
return p.size();
}
p3D getPAt(int index)
{
if(index>=p.size() || index<0)
return new p3D(0.0f,0.0f,0.0f);
return (p3D)p.elementAt(index);
}
void setPAt(p3D np,int index)
{
if(index>=p.size() || index<0)
return;
((p3D)p.elementAt(index)).x=np.x;
((p3D)p.elementAt(index)).y=np.y;
((p3D)p.elementAt(index)).z=np.z;
}
void appendVertex(float x,float y, float z)
{
appendVertex(new p3D(x,y,z));
}
void appendVertex(p3D pnt)
{
if(pnt.z<minZ)
minZ=pnt.z;
p.addElement(pnt);
}
void calculateAngle()
{
// calculate normal and angle twds z-axis
if(p.size()<3)
{
cosangle=1.0f;
return;
}
// normal,normalV, from triangle: p0 - p1 - p2
// c=(a2b3-a3b2, a3b1-a1b3, a1b2-a2b1)
p3D p0=(p3D)p.elementAt(0);
p3D p1=(p3D)p.elementAt(1);
p3D p2=(p3D)p.elementAt(2);
p3D normalV=new p3D((p2.y-p1.y)*(p0.z-p1.z)-(p2.z-p1.z)*(p0.y-p1.y),
(p2.z-p1.z)*(p0.x-p1.x)-(p2.x-p1.x)*(p0.z-p1.z),
(p2.x-p1.x)*(p0.y-p1.y)-(p2.y-p1.y)*(p0.x-p1.x));
float len=(float)Math.sqrt(normalV.x*normalV.x+normalV.y*normalV.y+normalV.z*normalV.z);
// normalize. need only z-component
// dont care about direction
// big cosangle -> light face, since light is -z
cosangle=Math.abs(normalV.z/len);
}
//public void paralellProject(float xfactor,float yfactor)
public void paralellProject(
float Wxl, float Wyt, float Ww, float Wh,
float Vxl, float Vyt, float Vw, float Vh)
{
if(p.size()<2)
return;
pol=new Polygon();
float xfactor=Vw/Ww;
float yfactor=Vh/Wh;
for(int ix=0;ix<p.size();ix++)
pol.addPoint(
Math.round( (((p3D)p.elementAt(ix)).x-Wxl)*xfactor+Vxl),
Math.round( (((p3D)p.elementAt(ix)).y-Wyt)*yfactor+Vyt) );
}
public void perspectiveProject(
float vx,float vy,
float Wxl,float Wyt, float Ww, float Wh,
float Vxl, float Vyt, float Vw, float Vh)
{
// W is not used, assumes Rectangle(-0.5,-0.5,1,1)
if(p.size()<2)
return;
pol=new Polygon();
float xf=(float)(0.5f/Math.tan(Math.toRadians(vx)));
float yf=(float)(0.5f/Math.tan(Math.toRadians(vy)));
for(int ix=0;ix<p.size();ix++)
{
float x=((p3D)p.elementAt(ix)).x/Math.abs(((p3D)p.elementAt(ix)).z)*xf;
float y=((p3D)p.elementAt(ix)).y/Math.abs(((p3D)p.elementAt(ix)).z)*xf;
pol.addPoint(Math.round( (x+0.5f)*Vw+Vxl ),Math.round( (y+0.5f)*Vh+Vyt )
);
}
}
public void render(Graphics g)
{
if(pol==null)
return;
if(pmode==gob.FILL)
{
Color c=new Color(
(pcolor.getRed()*(cosangle/255.0f)),
(pcolor.getGreen()*(cosangle/255.0f)),
(pcolor.getBlue()*(cosangle)/255.0f));
g.setColor(c);
g.fillPolygon(pol);
}
else
{
g.setColor(pcolor);
g.drawPolygon(pol);
}
}
}
//------------- end of polygon
// -----------------------------
// describing a simple robot:
// origo in middle of torso
// head up in z, facing x
//-----------------------------
//------------------------------
// the robot can only do a silly run,
// see method: walkmove
// if you want other functionality
// you must write other methods which
// updates the robots angles
//------------------------------
public class theMan
{
// dimensions: x,y,z
final float torso[]= {0.2f, 0.4f, 0.6f};
final float head[]= {0.1f, 0.2f, 0.2f};
final float overarm[]= {0.1f, 0.1f, 0.25f};
final float underarm[]={0.1f, 0.1f, 0.30f};
final float overleg[]= {0.1f, 0.1f, 0.3f};
final float underleg[]={0.1f, 0.1f, 0.35f};
// angles: around x,y,z
float v_head[]= {0.0f,0.0f,0.0f};
float left_shoulder[]= {0.0f,0.0f,0.0f};
float left_elboe[]= {0.0f,0.0f,0.0f};
float right_shoulder[]={0.0f,0.0f,0.0f};
float right_elboe[]= {0.0f,0.0f,0.0f};
float left_hip[]= {0.0f,0.0f,0.0f};
float left_knee[]= {0.0f,0.0f,0.0f};
float right_hip[]= {0.0f,0.0f,0.0f};
float right_knee[]= {0.0f,0.0f,0.0f};
// joint space
final float joint=0.05f;
//-----------------------------------------
// describing the walk/run
// state in movement, argument to sinus function
float state=0.0f;
// max and min hip angles, refers to rotateY
final float minhip=-0.52359f;//(float)Math.toRadians(-30.0f);
final float maxhip=1.22173f;//(float)Math.toRadians(70.0f);
// max and min knee angles, refers to rotateY
final float minknee=0.34906f;//(float)Math.toRadians(20.0f);
final float maxknee=1.57079f;//(float)Math.toRadians(90.0f);
// max and min shoulder angles, refers to rotateY
final float minshoulder=-1.04719f;//(float)Math.toRadians(-60.0f);
final float maxshoulder=1.04719f;//((float)Math.toRadians(60.0f);
// max and min elboe angles, refers to rotateY
final float minelboe=0.174532f;//(float)Math.toRadians(10.0f);
final float maxelboe=1.57079f;//(float)Math.toRadians(90.0f);
public theMan()
{
}
public void walkmove(float incr)
{
// walking according to a sinus description
// inr is an increment on a sinus function
// increments state
state+=incr;
float sx=(float)Math.sin(state);
if(sx>=0.0f)
{
// calculate left arm - forward
left_shoulder[1]=-maxshoulder*sx;
left_elboe[1]=-maxelboe*sx;
// calculate right arm - backward
right_shoulder[1]=-minshoulder*sx;
right_elboe[1]=-minelboe*sx;
// calculate right leg - forward
right_hip[1]=-maxhip*sx;
right_knee[1]=maxknee*sx;
// calculate left leg - backward
left_hip[1]=-minhip*sx;
left_knee[1]=minknee*sx;
}
else
{
// calculate left arm - backward
left_shoulder[1]=minshoulder*sx;
left_elboe[1]=minelboe*sx;
// calculate right arm - forward
right_shoulder[1]=maxshoulder*sx;
right_elboe[1]=maxelboe*sx;
// calculate right leg - backward
right_hip[1]=minhip*sx;
right_knee[1]=-minknee*sx;
// calculate left leg - forward
left_hip[1]=maxhip*sx;
left_knee[1]=-maxknee*sx;
}
}
public void drawMan(gob gb,int mode)
{
// origo in middle of torso
gb.drawBox(torso[0],torso[1],torso[2],mode);
gb.pushMatrix();
// head
gb.translate(0.0f,0.0f,torso[2]/2.0f+joint/2.0f);
gb.rotateX(v_head[0]);
gb.rotateY(v_head[1]);
gb.rotateZ(v_head[2]);
gb.translate(0.0f,0.0f,head[2]/2.0f);
gb.drawBox(head[0],head[1],head[2],mode);
gb.popMatrix();
gb.pushMatrix();
// left arm
gb.translate(0.0f,torso[1]/2.0f+joint/2.0f+overarm[1]/2.0f,torso[2]/2.0f);
gb.rotateX(left_shoulder[0]);
gb.rotateY(left_shoulder[1]);
gb.rotateZ(left_shoulder[2]);
gb.translate(0.0f,0.0f,-overarm[2]/2.0f);
gb.drawBox(overarm[0],overarm[1],overarm[2],mode);
//---------------
gb.translate(0.0f,0.0f,-overarm[2]/2.0f-joint/2.0f);
gb.rotateX(left_elboe[0]);
gb.rotateY(left_elboe[1]);
gb.rotateZ(left_elboe[2]);
gb.translate(0.0f,0.0f,-underarm[2]/2.0f-joint/2.0f);
gb.drawBox(underarm[0],underarm[1],underarm[2],mode);
gb.popMatrix();
gb.pushMatrix();
// right arm
gb.translate(0.0f,-torso[1]/2.0f-joint/2.0f-overarm[1]/2.0f,torso[2]/2.0f);
gb.rotateX(right_shoulder[0]);
gb.rotateY(right_shoulder[1]);
gb.rotateZ(right_shoulder[2]);
gb.translate(0.0f,0.0f,-overarm[2]/2.0f);
gb.drawBox(overarm[0],overarm[1],overarm[2],mode);
//---------------
gb.translate(0.0f,0.0f,-overarm[2]/2.0f-joint/2.0f);
gb.rotateX(right_elboe[0]);
gb.rotateY(right_elboe[1]);
gb.rotateZ(right_elboe[2]);
gb.translate(0.0f,0.0f,-underarm[2]/2.0f-joint/2.0f);
gb.drawBox(underarm[0],underarm[1],underarm[2],mode);
gb.popMatrix();
gb.pushMatrix();
// left leg
gb.translate(0.0f,torso[1]/2.0f-overleg[1]/2.0f,-torso[2]/2.0f-joint/2.0f);
gb.rotateX(left_hip[0]);
gb.rotateY(left_hip[1]);
gb.rotateZ(left_hip[2]);
gb.translate(0.0f,0.0f,-overleg[2]/2.0f);
gb.drawBox(overleg[0],overleg[1],overleg[2],mode);
//---------------
gb.translate(0.0f,0.0f,-overleg[2]/2.0f-joint/2.0f);
gb.rotateX(left_knee[0]);
gb.rotateY(left_knee[1]);
gb.rotateZ(left_knee[2]);
gb.translate(0.0f,0.0f,-underleg[2]/2.0f-joint/2.0f);
gb.drawBox(underleg[0],underleg[1],underleg[2],mode);
gb.popMatrix();
gb.pushMatrix();
// right leg
gb.translate(0.0f,-torso[1]/2.0f+overleg[1]/2.0f,-torso[2]/2.0f-joint/2.0f);
gb.rotateX(right_hip[0]);
gb.rotateY(right_hip[1]);
gb.rotateZ(right_hip[2]);
gb.translate(0.0f,0.0f,-overleg[2]/2.0f);
gb.drawBox(overleg[0],overleg[1],overleg[2],mode);
//---------------
gb.translate(0.0f,0.0f,-overleg[2]/2.0f-joint/2.0f);
gb.rotateX(right_knee[0]);
gb.rotateY(right_knee[1]);
gb.rotateZ(right_knee[2]);
gb.translate(0.0f,0.0f,-underleg[2]/2.0f-joint/2.0f);
gb.drawBox(underleg[0],underleg[1],underleg[2],mode);
gb.popMatrix();
}
}
// -------------- end of inner classes
}
Football
A form with 20 hexagons and 12 pentagons. The Buckball is explained in Ball.
Source: https://svn.hiof.no/svn/webnew/sites/j3d/gobApplets/src/a3dbucky.java
Source:
import java.awt.Polygon;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Component;
import java.awt.event.*;
import java.applet.Applet;
import java.util.Vector;
import java.awt.geom.AffineTransform;
import java.awt.Graphics2D;
public class a3dbucky extends Applet implements Runnable
{
boolean isStandalone = false;
gob hb=null;
Thread painter=null;
// running ?
boolean halted=false;
// use perspective or not
boolean isPerspective;
// a buckyball object
buckyball theBall=null;
// fill or not
boolean bFill=false;
//---------- to make the demo spin ----------
float vx=0.0f;
float vy=0.0f;
float vz=0.0f;
float dvx=0.02f;
float dvy=0.01f;
float dvz=0.02f;
int lastx=0;
int lasty=0;
//-------------------------------
//Construct the applet
public a3dbucky()
{
}
//Initialize the applet
public void init()
{
try
{
jbInit();
}
catch(Exception e)
{
e.printStackTrace();
}
}
private void jbInit() throws Exception
{
this.addMouseMotionListener(new java.awt.event.MouseMotionAdapter()
{
public void mouseDragged(MouseEvent e)
{
this_mouseDragged(e);
}
});
this.addMouseListener(new java.awt.event.MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
this_mouseClicked(e);
}
public void mousePressed(MouseEvent e)
{
this_mousePressed(e);
}
public void mouseReleased(MouseEvent e)
{
this_mouseReleased(e);
}
});
hb=new gob(this);
theBall=new buckyball();
this.setLayout(null);
}
//Start the applet
public void start()
{
painter=new Thread(this);
halted=false;
painter.start();
}
//Stop the applet
public void stop()
{
if(painter!=null)
halted=true;
}
//Destroy the applet
public void destroy()
{
}
//Get Applet information
public String getAppletInfo()
{
return "Name: App3D," + "Author: Børre Stenseth";
}
public void paint(Graphics g)
{
hb.clearAll();
hb.setIdentityMatrix();
drawBall();
hb.flush(g);
}
public void run()
{
int count=0;
while (!halted)
{
try
{
vx+=dvx;
vx+=dvy;
vz+=dvz;
repaint();
Thread.sleep(40);
}
catch (InterruptedException e)
{
stop();
}
}
}
// override update to avois flickering
// ok, since we take care of the whole clientarea
// in paint
public void update(Graphics g)
{
paint(g);
}
void drawBall()
{
// viewport is set in gob-constructor
hb.setOrtho(-2,-2,4,4);
hb.translate(0.0f,0.0f,-200.0f);
hb.rotateZ(vz);
hb.rotateX(vx);
hb.rotateY(vy);
if(bFill)
{
hb.setColor(new Color(0.9f,0.9f,0.2f));
theBall.DrawIt(hb,gob.FILL);
}
hb.setColor(new Color(0.0f,0.0f,0.0f));
theBall.DrawIt(hb,gob.FRAME);
}
void this_mouseClicked(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
{
if(halted)
start();
else
stop();
bFill=!bFill;
}
}
void this_mousePressed(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK)
{
lastx=e.getX();
lasty=e.getY();
halted=true;
stop();
}
}
void this_mouseReleased(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK)
{
int x=e.getX();
int y=e.getY();
if((dvx!=0.0f)||(dvy!=0.0f))
{
halted=false;
start();
}
}
}
void this_mouseDragged(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK)
{
int x=e.getX();
int y=e.getY();
dvx=5.0f*(x-lastx)/this.getSize().width;
dvy=5.0f*(y-lasty)/this.getSize().height;
dvz=dvx-dvy;
vx+=dvx;
vx+=dvy;
vz+=dvz;
repaint();
lastx=x;
lasty=y;
}
}
//--------------------------------------------------
// start of inner classes: gob, p3D, matrix, polygon
public class gob extends Object
{
//----------------------------------------------------------------
// defining the minimalistic graphical interface:
// clearAll, drawBox, flush, pBegin, pEnd, popMatrix, pushMatrix,
// rotateX, rotateY, rotateZ, scale, translate,
// setBackground, setColor, setIdentityMatrix,
// setOrtho, setperspective, setViewport, vertex
//-----------------------------------------------------------------
//-----------------------------------------------------------------
// intenal classes : p3D, matrix, polygon defined at end of file
//-----------------------------------------------------------------
// fill or outline polygons
public final static int FILL=0;
public final static int FRAME=1;
// the list of polygons to draw
Vector drawList=new Vector(20,6);
// matrix stack, for push and pop
Vector matrixStack=new Vector(5,5);
// current model(view) matrix
matrix curMatrix=null;
// current polygon, while we are adding vertexes
polygon curPoly=null;
// current drawing color
Color curColor=null;
// Background color, last before flush yields
Color BKColor=null;
// buffer to draw in to avoid flicker
Image Buffer;
// viewport
float Vxl,Vyt,Vw,Vh;
// window, model coordinates
float Wxl,Wyt,Ww,Wh;
// perspective angles, in degrees
float Pvx,Pvy;
// use perspective projection or not
boolean isPerspective;
// the actual component that gob will work on
Component theComp;
// possible background image
Image theImage=null;
public gob(Component comp)
{
Buffer=comp.createImage(comp.getSize().width,comp.getSize().height);
setViewport(0,0,comp.getSize().width,comp.getSize().height);
setOrtho(0,0,comp.getSize().width,comp.getSize().height);
theComp=comp;
curColor=new Color(0.9f,0.9f,0.9f);
BKColor=new Color(1.0f,1.0f,1.0f);
}
public void clearAll()
{
drawList.removeAllElements();
curPoly=null;
curMatrix=new matrix();
matrixStack.removeAllElements();
}
public void setIdentityMatrix()
{
curMatrix=new matrix();
}
public void pushMatrix()
{
if(curMatrix==null)
curMatrix=new matrix();
matrixStack.addElement(curMatrix.copyMatrix(curMatrix));
}
public void popMatrix()
{
if(matrixStack.size()<1)
{
//underflow
curMatrix=new matrix();
System.out.println("Matrix stack undeflow, too many pops");
return;
}
curMatrix=(matrix)matrixStack.lastElement();
matrixStack.removeElement(matrixStack.lastElement());
}
public void translate(float dx,float dy,float dz)
{
float[] values={
1.0f,0.0f,0.0f,dx,
0.0f,1.0f,0.0f,dy,
0.0f,0.0f,1.0f,dz,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void scale(float sx,float sy,float sz)
{
float[] values={
sx,0.0f,0.0f,0.0f,
0.0f,sy,0.0f,0.0f,
0.0f,0.0f,sz,0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateX(float v)
{
float[] values={
1.0f,0.0f,0.0f,0.0f,
0.0f,(float)Math.cos(v),(float)-Math.sin(v),0.0f,
0.0f,(float)Math.sin(v),(float)Math.cos(v),0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateY(float v)
{
float[] values={
(float)Math.cos(v),0.0f,(float)Math.sin(v),0.0f,
0.0f,1.0f,0.0f,0.0f,
(float)-Math.sin(v),0.0f,(float)Math.cos(v),0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateZ(float v)
{
float[] values={
(float)Math.cos(v),(float)-Math.sin(v),0.0f,0.0f,
(float)Math.sin(v),(float)Math.cos(v),0.0f,0.0f,
0.0f,0.0f,1.0f,0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void pBegin(int mode)
{
if(mode==FILL)
curPoly=new polygon(curColor,mode);
else
curPoly=new polygon(curColor,FRAME);
}
public void vertex(float nx, float ny, float nz)
{
p3D npnt=curMatrix.transform(new p3D(nx,ny,nz));
curPoly.appendVertex(npnt);
}
public void pEnd()
{
// calculate the angle for lighting
curPoly.calculateAngle();
// put it into the the drawList according to depth
// lowest minZ comes first in list
for(int ix=0;ix<drawList.size();ix++)
if(((polygon)drawList.elementAt(ix)).getMinZ()>curPoly.getMinZ())
{
drawList.insertElementAt(curPoly,ix);
return;
}
drawList.addElement(curPoly);
}
public void flush(Graphics g)
{
// project the polygons
for(int ix=0;ix<drawList.size();ix++)
{
// project it
if(isPerspective)
((polygon)drawList.elementAt(ix)).perspectiveProject(Pvx,Pvy, Wxl,Wyt,Ww,Wh, Vxl,Vyt,Vw,Vh);
else
((polygon)drawList.elementAt(ix)).paralellProject(Wxl,Wyt,Ww,Wh, Vxl,Vyt,Vw,Vh);
}
// draw drawList from bottom on an offscreen buffer
Graphics tmpg=Buffer.getGraphics();
// clear only the specified viewport
// tmpg.clearRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
tmpg.setColor(BKColor);
// fills with color or set out image
tmpg.fillRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
if(theImage!=null)
tmpg.drawImage(theImage,Math.round(Vxl),Math.round(Vyt),BKColor, null);
// draw it
for(int ix=0;ix<drawList.size();ix++)
((polygon)drawList.elementAt(ix)).render(tmpg);
// copy to viewport
g.clipRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
g.drawImage(Buffer,0,0,null);
}
public void setColor(Color c)
{
curColor=c;
}
public void setBackground(Color c)
{
BKColor=c;
//theImage=null;
}
public void setImage(Image im)
{
if(im==null)
theImage=null;
else
theImage=im;
}
public void setViewport(float xl,float yt,float w, float h)
{
Vxl=xl;Vyt=yt;Vw=w;Vh=h;
}
public void setOrtho(float xl,float yt,float w, float h)
{
Wxl=xl;Wyt=yt;Ww=w;Wh=h;
isPerspective=false;
}
public void setPerspective(float vx,float vy)
{
if(vx < 0.5)
Pvx=0.5f;
else if(vx > 89.5)
Pvx=89.5f;
else
Pvx=vx;
if(vy<0.5)
Pvy=0.5f;
else if(vy > 89.5)
Pvy=89.5f;
else
Pvy=vy;
isPerspective=true;
}
//----------------------------------------------
// draw a box, is handy since this is about all we
// would be tempted to draw in these surroundings
public void drawBox(float dx,float dy, float dz,int mode)
{
// bottom
float x=dx/2.0f;
float y=dy/2.0f;
float z=dz/2.0f;
pBegin(mode);
vertex(-x,y,-z); vertex(-x,-y,-z); vertex(x,-y,-z); vertex(x,y,-z);
pEnd();
// top
pBegin(mode);
vertex(-x,y,z); vertex(x,y,z); vertex(x,-y,z); vertex(-x,-y,z);
pEnd();
// front
pBegin(mode);
vertex(-x,y,-z); vertex(x,y,-z); vertex(x,y,z); vertex(-x,y,z);
pEnd();
// back
pBegin(mode);
vertex(-x,-y,-z); vertex(-x,-y,z); vertex(x,-y,z); vertex(x,-y,-z);
pEnd();
// left
pBegin(mode);
vertex(-x,y,-z); vertex(-x,y,z); vertex(-x,-y,z); vertex(-x,-y,-z);
pEnd();
// right
pBegin(mode);
vertex(x,-y,-z); vertex(x,-y,z); vertex(x,y,z); vertex(x,y,-z);
pEnd();
}
}
//----------------- end of gob
//------------------ start of p3D
class p3D extends Object
{
float x,y,z;
public p3D(float nx, float ny, float nz)
{
x=nx; y=ny; z=nz;
}
}
//----------------- end of p3D
//------------------ start of matrix
class matrix extends Object
{
float[][] m=null;
public matrix()
{
m=new float[][]{
{1.0f,0.0f,0.0f,0.0f},
{0.0f,1.0f,0.0f,0.0f},
{0.0f,0.0f,1.0f,0.0f},
{0.0f,0.0f,0.0f,1.0f}};
}
public matrix(float[] values)
{
m=new float[][]{
{1.0f,0.0f,0.0f,0.0f},
{0.0f,1.0f,0.0f,0.0f},
{0.0f,0.0f,1.0f,0.0f},
{0.0f,0.0f,0.0f,1.0f}};
if(values.length!=16)
return;
m[0][0]=values[0]; m[0][1]=values[1]; m[0][2]=values[2]; m[0][3]=values[3];
m[1][0]=values[4]; m[1][1]=values[5]; m[1][2]=values[6]; m[1][3]=values[7];
m[2][0]=values[8]; m[2][1]=values[9]; m[2][2]=values[10]; m[2][3]=values[11];
m[3][0]=values[12]; m[3][1]=values[13]; m[3][2]=values[14]; m[3][3]=values[15];
}
matrix copyMatrix(matrix sm)
{
float v[]={
sm.m[0][0],sm.m[0][1],sm.m[0][2],sm.m[0][3],
sm.m[1][0],sm.m[1][1],sm.m[1][2],sm.m[1][3],
sm.m[2][0],sm.m[2][1],sm.m[2][2],sm.m[2][3],
sm.m[3][0],sm.m[3][1],sm.m[3][2],sm.m[3][3]};
return new matrix(v);
}
p3D transform(p3D p)
{
return new p3D(
p.x*m[0][0]+p.y*m[0][1]+p.z*m[0][2]+m[0][3],
p.x*m[1][0]+p.y*m[1][1]+p.z*m[1][2]+m[1][3],
p.x*m[2][0]+p.y*m[2][1]+p.z*m[2][2]+m[2][3]);
}
void postMult(matrix tm)
{
// do: m = m X tm.m
float [][] oldm={
{m[0][0],m[0][1],m[0][2],m[0][3]},
{m[1][0],m[1][1],m[1][2],m[1][3]},
{m[2][0],m[2][1],m[2][2],m[2][3]},
{m[3][0],m[3][1],m[3][2],m[3][3]}};
for(int r=0;r<4;r++)
for(int k=0;k<4;k++)
m[r][k]=oldm[r][0]*tm.m[0][k]+
oldm[r][1]*tm.m[1][k]+
oldm[r][2]*tm.m[2][k]+
oldm[r][3]*tm.m[3][k];
}
}
//----------------- end of matrix
//------------------ start of polygon
class polygon extends Object
{
Vector p=new Vector(20,6);
float minZ=100000.0f; // higher than any point
Color pcolor=null;
float cosangle=0.0f;
Polygon pol=null;
int pmode;
public polygon(Color c,int mode)
{
pmode=mode;
pcolor=c;
}
void setColor(float r,float g,float b)
{
pcolor=new Color(r,g,b);
}
float getMinZ()
{
return minZ;
}
int getPCount()
{
return p.size();
}
p3D getPAt(int index)
{
if(index>=p.size() || index<0)
return new p3D(0.0f,0.0f,0.0f);
return (p3D)p.elementAt(index);
}
void setPAt(p3D np,int index)
{
if(index>=p.size() || index<0)
return;
((p3D)p.elementAt(index)).x=np.x;
((p3D)p.elementAt(index)).y=np.y;
((p3D)p.elementAt(index)).z=np.z;
}
void appendVertex(float x,float y, float z)
{
appendVertex(new p3D(x,y,z));
}
void appendVertex(p3D pnt)
{
if(pnt.z<minZ)
minZ=pnt.z;
p.addElement(pnt);
}
void calculateAngle()
{
// calculate normal and angle twds z-axis
if(p.size()<3)
{
cosangle=1.0f;
return;
}
// normal,normalV, from triangle: p0 - p1 - p2
// c=(a2b3-a3b2, a3b1-a1b3, a1b2-a2b1)
p3D p0=(p3D)p.elementAt(0);
p3D p1=(p3D)p.elementAt(1);
p3D p2=(p3D)p.elementAt(2);
p3D normalV=new p3D((p2.y-p1.y)*(p0.z-p1.z)-(p2.z-p1.z)*(p0.y-p1.y),
(p2.z-p1.z)*(p0.x-p1.x)-(p2.x-p1.x)*(p0.z-p1.z),
(p2.x-p1.x)*(p0.y-p1.y)-(p2.y-p1.y)*(p0.x-p1.x));
float len=(float)Math.sqrt(normalV.x*normalV.x+normalV.y*normalV.y+normalV.z*normalV.z);
// normalize. need only z-component
// dont care about direction
// big cosangle -> light face, since light is -z
cosangle=Math.abs(normalV.z/len);
}
//public void paralellProject(float xfactor,float yfactor)
public void paralellProject(
float Wxl, float Wyt, float Ww, float Wh,
float Vxl, float Vyt, float Vw, float Vh)
{
if(p.size()<2)
return;
pol=new Polygon();
float xfactor=Vw/Ww;
float yfactor=Vh/Wh;
for(int ix=0;ix<p.size();ix++)
pol.addPoint(
Math.round( (((p3D)p.elementAt(ix)).x-Wxl)*xfactor+Vxl),
Math.round( (((p3D)p.elementAt(ix)).y-Wyt)*yfactor+Vyt) );
}
public void perspectiveProject(
float vx,float vy,
float Wxl,float Wyt, float Ww, float Wh,
float Vxl, float Vyt, float Vw, float Vh)
{
// W is not used, assumes Rectangle(-0.5,-0.5,1,1)
if(p.size()<2)
return;
pol=new Polygon();
float xf=(float)(0.5f/Math.tan(Math.toRadians(vx)));
float yf=(float)(0.5f/Math.tan(Math.toRadians(vy)));
for(int ix=0;ix<p.size();ix++)
{
float x=((p3D)p.elementAt(ix)).x/Math.abs(((p3D)p.elementAt(ix)).z)*xf;
float y=((p3D)p.elementAt(ix)).y/Math.abs(((p3D)p.elementAt(ix)).z)*xf;
pol.addPoint(Math.round( (x+0.5f)*Vw+Vxl ),Math.round( (y+0.5f)*Vh+Vyt )
);
}
}
public void render(Graphics g)
{
if(pol==null)
return;
if(pmode==gob.FILL)
{
Color c=new Color(
(pcolor.getRed()*(cosangle/255.0f)),
(pcolor.getGreen()*(cosangle/255.0f)),
(pcolor.getBlue()*(cosangle)/255.0f));
g.setColor(c);
g.fillPolygon(pol);
}
else
{
g.setColor(pcolor);
g.drawPolygon(pol);
}
}
}
//------------- end of polygon
public class buckyball
{
// we could do more here, but leave it with a simple drawing
void DrawIt(gob gb,int mode)
{
float t = 1.61803398f;//Golden ratio
gb.pBegin(mode); //1
gb.vertex(-2/3.0f, -1/3.0f-2*t/3.0f, -t/3.0f);
gb.vertex(-1/3.0f, -2/3.0f-t/3.0f, -2*t/3.0f);
gb.vertex(1/3.0f, -2/3.0f-t/3.0f, -2*t/3.0f);
gb.vertex(2/3.0f, -1/3.0f-2*t/3.0f, -t/3.0f);
gb.vertex(1/3.0f, -t, 0.0f);
gb.vertex(-1/3.0f, -t, 0.0f);
gb.pEnd();
gb.pBegin(mode); //2
gb.vertex(-2/3.0f-t/3.0f, -2*t/3.0f, -1/3.0f);
gb.vertex(-1/3.0f-2*t/3.0f, -t/3.0f, -2/3.0f);
gb.vertex(-2*t/3.0f, -1/3.0f, -2/3.0f-t/3.0f);
gb.vertex(-t/3.0f, -2/3.0f, -1/3.0f-2*t/3.0f);
gb.vertex(-1/3.0f, -2/3.0f-t/3.0f, -2*t/3.0f);
gb.vertex(-2/3.0f, -1/3.0f-2*t/3.0f, -t/3.0f);
gb.pEnd();
gb.pBegin(mode); //3
gb.vertex(-1/3.0f, -t, 0.0f);
gb.vertex(1/3.0f, -t, 0.0f);
gb.vertex(2/3.0f, -1/3.0f-2*t/3.0f, t/3.0f);
gb.vertex(1/3.0f, -2/3.0f-t/3.0f, 2*t/3.0f);
gb.vertex(-1/3.0f, -2/3.0f-t/3.0f, 2*t/3.0f);
gb.vertex(-2/3.0f, -1/3.0f-2*t/3.0f, t/3.0f);
gb.pEnd();
gb.pBegin(mode); //4
gb.vertex(-2/3.0f, -1/3.0f-2*t/3.0f, t/3.0f);
gb.vertex(-1/3.0f, -2/3.0f-t/3.0f, 2*t/3.0f);
gb.vertex(-t/3.0f, -2/3.0f, 1/3.0f+2*t/3.0f);
gb.vertex(-2*t/3.0f, -1/3.0f, 2/3.0f+t/3.0f);
gb.vertex(-1/3.0f-2*t/3.0f, -t/3.0f, 2/3.0f);
gb.vertex(-2/3.0f-t/3.0f, -2*t/3.0f, 1/3.0f);
gb.pEnd();
gb.pBegin(mode); //5
gb.vertex(-2/3.0f-t/3.0f, -2*t/3.0f, 1/3.0f);
gb.vertex(-1/3.0f-2*t/3.0f, -t/3.0f, 2/3.0f);
gb.vertex(-t, 0.0f, 1/3.0f);
gb.vertex(-t, 0.0f, -1/3.0f);
gb.vertex(-1/3.0f-2*t/3.0f, -t/3.0f, -2/3.0f);
gb.vertex(-2/3.0f-t/3.0f, -2*t/3.0f, -1/3.0f);
gb.pEnd();
gb.pBegin(mode); //6
gb.vertex(-1/3.0f, t, 0.0f);
gb.vertex(1/3.0f, t, 0.0f);
gb.vertex(2/3.0f, 1/3.0f+2*t/3.0f, -t/3.0f);
gb.vertex(1/3.0f, 2/3.0f+t/3.0f, -2*t/3.0f);
gb.vertex(-1/3.0f, 2/3.0f+t/3.0f, -2*t/3.0f);
gb.vertex(-2/3.0f, 1/3.0f+2*t/3.0f, -t/3.0f);
gb.pEnd();
gb.pBegin(mode); //7
gb.vertex(-2/3.0f, 1/3.0f+2*t/3.0f, -t/3.0f);
gb.vertex(-1/3.0f, 2/3.0f+t/3.0f, -2*t/3.0f);
gb.vertex(-t/3.0f, 2/3.0f, -1/3.0f-2*t/3.0f);
gb.vertex(-2*t/3.0f, 1/3.0f, -2/3.0f-t/3.0f);
gb.vertex(-1/3.0f-2*t/3.0f, t/3.0f, -2/3.0f);
gb.vertex(-2/3.0f-t/3.0f, 2*t/3.0f, -1/3.0f);
gb.pEnd();
gb.pBegin(mode); //8
gb.vertex(-2/3.0f, 1/3.0f+2*t/3.0f, t/3.0f);
gb.vertex(-1/3.0f, 2/3.0f+t/3.0f, 2*t/3.0f);
gb.vertex(1/3.0f, 2/3.0f+t/3.0f, 2*t/3.0f);
gb.vertex(2/3.0f, 1/3.0f+2*t/3.0f, t/3.0f);
gb.vertex(1/3.0f, t, 0.0f);
gb.vertex(-1/3.0f, t, 0.0f);
gb.pEnd();
gb.pBegin(mode); //9
gb.vertex(-2/3.0f-t/3.0f, 2*t/3.0f, 1/3.0f);
gb.vertex(-1/3.0f-2*t/3.0f, t/3.0f, 2/3.0f);
gb.vertex(-2*t/3.0f, 1/3.0f, 2/3.0f+t/3.0f);
gb.vertex(-t/3.0f, 2/3.0f, 1/3.0f+2*t/3.0f);
gb.vertex(-1/3.0f, 2/3.0f+t/3.0f, 2*t/3.0f);
gb.vertex(-2/3.0f, 1/3.0f+2*t/3.0f, t/3.0f);
gb.pEnd();
gb.pBegin(mode); //10
gb.vertex(-2/3.0f-t/3.0f, 2*t/3.0f, -1/3.0f);
gb.vertex(-1/3.0f-2*t/3.0f, t/3.0f, -2/3.0f);
gb.vertex(-t, 0.0f, -1/3.0f);
gb.vertex(-t, 0.0f, 1/3.0f);
gb.vertex(-1/3.0f-2*t/3.0f, t/3.0f, 2/3.0f);
gb.vertex(-2/3.0f-t/3.0f, 2*t/3.0f, 1/3.0f);
gb.pEnd();
gb.pBegin(mode); //11
gb.vertex(-t/3.0f, -2/3.0f, -1/3.0f-2*t/3.0f);
gb.vertex(-2*t/3.0f, -1/3.0f, -2/3.0f-t/3.0f);
gb.vertex(-2*t/3.0f, 1/3.0f, -2/3.0f-t/3.0f);
gb.vertex(-t/3.0f, 2/3.0f, -1/3.0f-2*t/3.0f);
gb.vertex(0.0f, 1/3.0f, -t);
gb.vertex(0.0f, -1/3.0f, -t);
gb.pEnd();
gb.pBegin(mode); //12
gb.vertex(0.0f, -1/3.0f, -t);
gb.vertex(0.0f, 1/3.0f, -t);
gb.vertex(t/3.0f, 2/3.0f, -1/3.0f-2*t/3.0f);
gb.vertex(2*t/3.0f, 1/3.0f, -2/3.0f-t/3.0f);
gb.vertex(2*t/3.0f, -1/3.0f, -2/3.0f-t/3.0f);
gb.vertex(t/3.0f, -2/3.0f, -1/3.0f-2*t/3.0f);
gb.pEnd();
gb.pBegin(mode); //13
gb.vertex(t/3.0f, -2/3.0f, -1/3.0f-2*t/3.0f);
gb.vertex(2*t/3.0f, -1/3.0f, -2/3.0f-t/3.0f);
gb.vertex(1/3.0f+2*t/3.0f, -t/3.0f, -2/3.0f);
gb.vertex(2/3.0f+t/3.0f, -2*t/3.0f, -1/3.0f);
gb.vertex(2/3.0f, -1/3.0f-2*t/3.0f, -t/3.0f);
gb.vertex(1/3.0f, -2/3.0f-t/3.0f, -2*t/3.0f);
gb.pEnd();
gb.pBegin(mode); //14
gb.vertex(0.0f, -1/3.0f, t);
gb.vertex(0.0f, 1/3.0f, t);
gb.vertex(-t/3.0f, 2/3.0f, 1/3.0f+2*t/3.0f);
gb.vertex(-2*t/3.0f, 1/3.0f, 2/3.0f+t/3.0f);
gb.vertex(-2*t/3.0f, -1/3.0f, 2/3.0f+t/3.0f);
gb.vertex(-t/3.0f, -2/3.0f, 1/3.0f+2*t/3.0f);
gb.pEnd();
gb.pBegin(mode); //15
gb.vertex(t/3.0f, -2/3.0f, 1/3.0f+2*t/3.0f);
gb.vertex(2*t/3.0f, -1/3.0f, 2/3.0f+t/3.0f);
gb.vertex(2*t/3.0f, 1/3.0f, 2/3.0f+t/3.0f);
gb.vertex(t/3.0f, 2/3.0f, 1/3.0f+2*t/3.0f);
gb.vertex(0.0f, 1/3.0f, t);
gb.vertex(0.0f, -1/3.0f, t);
gb.pEnd();
gb.pBegin(mode); //16
gb.vertex(1/3.0f, -2/3.0f-t/3.0f, 2*t/3.0f);
gb.vertex(2/3.0f, -1/3.0f-2*t/3.0f, t/3.0f);
gb.vertex(2/3.0f+t/3.0f, -2*t/3.0f, 1/3.0f);
gb.vertex(1/3.0f+2*t/3.0f, -t/3.0f, 2/3.0f);
gb.vertex(2*t/3.0f, -1/3.0f, 2/3.0f+t/3.0f);
gb.vertex(t/3.0f, -2/3.0f, 1/3.0f+2*t/3.0f);
gb.pEnd();
gb.pBegin(mode); //17
gb.vertex(1/3.0f, 2/3.0f+t/3.0f, -2*t/3.0f);
gb.vertex(2/3.0f, 1/3.0f+2*t/3.0f, -t/3.0f);
gb.vertex(2/3.0f+t/3.0f, 2*t/3.0f, -1/3.0f);
gb.vertex(1/3.0f+2*t/3.0f, t/3.0f, -2/3.0f);
gb.vertex(2*t/3.0f, 1/3.0f, -2/3.0f-t/3.0f);
gb.vertex(t/3.0f, 2/3.0f, -1/3.0f-2*t/3.0f);
gb.pEnd();
gb.pBegin(mode); //18
gb.vertex(t/3.0f, 2/3.0f, 1/3.0f+2*t/3.0f);
gb.vertex(2*t/3.0f, 1/3.0f, 2/3.0f+t/3.0f);
gb.vertex(1/3.0f+2*t/3.0f, t/3.0f, 2/3.0f);
gb.vertex(2/3.0f+t/3.0f, 2*t/3.0f, 1/3.0f);
gb.vertex(2/3.0f, 1/3.0f+2*t/3.0f, t/3.0f);
gb.vertex(1/3.0f, 2/3.0f+t/3.0f, 2*t/3.0f);
gb.pEnd();
gb.pBegin(mode); //19
gb.vertex(2/3.0f+t/3.0f, -2*t/3.0f, -1/3.0f);
gb.vertex(1/3.0f+2*t/3.0f, -t/3.0f, -2/3.0f);
gb.vertex(t, 0.0f, -1/3.0f);
gb.vertex(t, 0.0f, 1/3.0f);
gb.vertex(1/3.0f+2*t/3.0f, -t/3.0f, 2/3.0f);
gb.vertex(2/3.0f+t/3.0f, -2*t/3.0f, 1/3.0f);
gb.pEnd();
gb.pBegin(mode); //20
gb.vertex(2/3.0f+t/3.0f, 2*t/3.0f, 1/3.0f);
gb.vertex(1/3.0f+2*t/3.0f, t/3.0f, 2/3.0f);
gb.vertex(t, 0.0f, 1/3.0f);
gb.vertex(t, 0.0f, -1/3.0f);
gb.vertex(1/3.0f+2*t/3.0f, t/3.0f, -2/3.0f);
gb.vertex(2/3.0f+t/3.0f, 2*t/3.0f, -1/3.0f);
gb.pEnd();
gb.pBegin(mode); //21
gb.vertex(-2/3.0f, -1/3.0f-2*t/3.0f, -t/3.0f);
gb.vertex(-1/3.0f, -t, 0.0f);
gb.vertex(-2/3.0f, -1/3.0f-2*t/3.0f, t/3.0f);
gb.vertex(-2/3.0f-t/3.0f, -2*t/3.0f, 1/3.0f);
gb.vertex(-2/3.0f-t/3.0f, -2*t/3.0f, -1/3.0f);
gb.pEnd();
gb.pBegin(mode); //22
gb.vertex(-2/3.0f-t/3.0f, 2*t/3.0f, -1/3.0f);
gb.vertex(-2/3.0f-t/3.0f, 2*t/3.0f, 1/3.0f);
gb.vertex(-2/3.0f, 1/3.0f+2*t/3.0f, t/3.0f);
gb.vertex(-1/3.0f, t, 0.0f);
gb.vertex(-2/3.0f, 1/3.0f+2*t/3.0f, -t/3.0f);
gb.pEnd();
gb.pBegin(mode); //23
gb.vertex(-t/3.0f, -2/3.0f, -1/3.0f-2*t/3.0f);
gb.vertex(0.0f, -1/3.0f, -t);
gb.vertex(t/3.0f, -2/3.0f, -1/3.0f-2*t/3.0f);
gb.vertex(1/3.0f, -2/3.0f-t/3.0f, -2*t/3.0f);
gb.vertex(-1/3.0f, -2/3.0f-t/3.0f, -2*t/3.0f);
gb.pEnd();
gb.pBegin(mode); //24
gb.vertex(-1 / 3.0f, -2 / 3.0f - t / 3.0f, 2 * t / 3.0f);
gb.vertex(1 / 3.0f, -2 / 3.0f - t / 3.0f, 2 * t / 3.0f);
gb.vertex(t / 3.0f, -2 / 3.0f, 1 / 3.0f + 2 * t / 3.0f);
gb.vertex(0.0f, -1 / 3.0f, t);
gb.vertex(-t / 3.0f, -2 / 3.0f, 1 / 3.0f + 2 * t / 3.0f);
gb.pEnd();
gb.pBegin(mode); //25
gb.vertex(-1/3.0f, 2/3.0f+t/3.0f, -2*t/3.0f);
gb.vertex(1/3.0f, 2/3.0f+t/3.0f, -2*t/3.0f);
gb.vertex(t/3.0f, 2/3.0f, -1/3.0f-2*t/3.0f);
gb.vertex(0.0f, 1/3.0f, -t);
gb.vertex(-t/3.0f, 2/3.0f, -1/3.0f-2*t/3.0f);
gb.pEnd();
gb.pBegin(mode); //26
gb.vertex(-t/3.0f, 2/3.0f, 1/3.0f+2*t/3.0f);
gb.vertex(0.0f, 1/3.0f, t);
gb.vertex(t/3.0f, 2/3.0f, 1/3.0f+2*t/3.0f);
gb.vertex(1/3.0f, 2/3.0f+t/3.0f, 2*t/3.0f);
gb.vertex(-1/3.0f, 2/3.0f+t/3.0f, 2*t/3.0f);
gb.pEnd();
gb.pBegin(mode); //27
gb.vertex(1/3.0f, -t, 0.0f);
gb.vertex(2/3.0f, -1/3.0f-2*t/3.0f, -t/3.0f);
gb.vertex(2/3.0f+t/3.0f, -2*t/3.0f, -1/3.0f);
gb.vertex(2/3.0f+t/3.0f, -2*t/3.0f, 1/3.0f);
gb.vertex(2/3.0f, -1/3.0f-2*t/3.0f, t/3.0f);
gb.pEnd();
gb.pBegin(mode); //28
gb.vertex(2/3.0f, 1/3.0f+2*t/3.0f, t/3.0f);
gb.vertex(2/3.0f+t/3.0f, 2*t/3.0f, 1/3.0f);
gb.vertex(2/3.0f+t/3.0f, 2*t/3.0f, -1/3.0f);
gb.vertex(2/3.0f, 1/3.0f+2*t/3.0f, -t/3.0f);
gb.vertex(1/3.0f, t, 0.0f);
gb.pEnd();
gb.pBegin(mode); //29
gb.vertex(-t, 0.0f, -1/3.0f);
gb.vertex(-1/3.0f-2*t/3.0f, t/3.0f, -2/3.0f);
gb.vertex(-2*t/3.0f, 1/3.0f, -2/3.0f-t/3.0f);
gb.vertex(-2*t/3.0f, -1/3.0f, -2/3.0f-t/3.0f);
gb.vertex(-1/3.0f-2*t/3.0f, -t/3.0f, -2/3.0f);
gb.pEnd();
gb.pBegin(mode); //30
gb.vertex(-1/3.0f-2*t/3.0f, -t/3.0f, 2/3.0f);
gb.vertex(-2*t/3.0f, -1/3.0f, 2/3.0f+t/3.0f);
gb.vertex(-2*t/3.0f, 1/3.0f, 2/3.0f+t/3.0f);
gb.vertex(-1/3.0f-2*t/3.0f, t/3.0f, 2/3.0f);
gb.vertex(-t, 0.0f, 1/3.0f);
gb.pEnd();
gb.pBegin(mode); //31
gb.vertex(2*t/3.0f, -1/3.0f, -2/3.0f-t/3.0f);
gb.vertex(2*t/3.0f, 1/3.0f, -2/3.0f-t/3.0f);
gb.vertex(1/3.0f+2*t/3.0f, t/3.0f, -2/3.0f);
gb.vertex(t, 0.0f, -1/3.0f);
gb.vertex(1/3.0f+2*t/3.0f, -t/3.0f, -2/3.0f);
gb.pEnd();
gb.pBegin(mode); //32
gb.vertex(1/3.0f+2*t/3.0f, -t/3.0f, 2/3.0f);
gb.vertex(t, 0.0f, 1/3.0f);
gb.vertex(1/3.0f+2*t/3.0f, t/3.0f, 2/3.0f);
gb.vertex(2*t/3.0f, 1/3.0f, 2/3.0f+t/3.0f);
gb.vertex(2*t/3.0f, -1/3.0f, 2/3.0f+t/3.0f);
gb.pEnd();
}
public buckyball()
{
//we could do more here, but leave it with a hardcoded
//list of polygons, as in DrawIt
}
}
// --------- end of buckyball
// -------------- end of inner classes
}
Logo
This sample reveals shortcomings of the solution for depth sorting of surfaces. We also see that we do not smooth the transition from one surface to another.
Why not improve it ?
Source:
import java.awt.Polygon;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Component;
import java.awt.event.*;
import java.applet.Applet;
import java.util.Vector;
import java.awt.geom.AffineTransform;
import java.awt.Graphics2D;
public class a3dlogo extends Applet implements Runnable
{
boolean isStandalone = false;
gob hb=null;
Thread painter=null;
// running ?
boolean halted=false;
// use perspective or not
boolean isPerspective;
// a logo object
hiologo HiofLogo=null;
//---------- to make the demo spin ----------
float vx=0.0f;
float vy=0.0f;
float vz=0.0f;
float dvx=0.02f;
float dvy=0.01f;
float dvz=0.02f;
int lastx=0;
int lasty=0;
//-------------------------------
//Construct the applet
public a3dlogo()
{
}
//Initialize the applet
public void init()
{
try
{
jbInit();
}
catch(Exception e)
{
e.printStackTrace();
}
}
private void jbInit() throws Exception
{
this.addMouseMotionListener(new java.awt.event.MouseMotionAdapter()
{
public void mouseDragged(MouseEvent e)
{
this_mouseDragged(e);
}
});
this.addMouseListener(new java.awt.event.MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
this_mouseClicked(e);
}
public void mousePressed(MouseEvent e)
{
this_mousePressed(e);
}
public void mouseReleased(MouseEvent e)
{
this_mouseReleased(e);
}
});
hb=new gob(this);
HiofLogo=new hiologo();
this.setLayout(null);
}
//Start the applet
public void start()
{
painter=new Thread(this);
halted=false;
painter.start();
}
//Stop the applet
public void stop()
{
if(painter!=null)
halted=true;
}
//Destroy the applet
public void destroy()
{
}
//Get Applet information
public String getAppletInfo()
{
return "Name: App3D," + "Author: Børre Stenseth";
}
public void paint(Graphics g)
{
hb.clearAll();
hb.setIdentityMatrix();
drawLogo();
hb.flush(g);
}
public void run()
{
int count=0;
while (!halted)
{
try
{
vx+=dvx;
vx+=dvy;
vz+=dvz;
repaint();
Thread.sleep(40);
}
catch (InterruptedException e)
{
stop();
}
}
}
// override update to avois flickering
// ok, since we take care of the whole clientarea
// in paint
public void update(Graphics g)
{
paint(g);
}
void drawLogo()
{
hb.setColor(new Color(0.9f,0.9f,0.2f));
hb.setOrtho(-2,-2,4,4);
hb.setViewport(0,0,300,300);
hb.translate(0.0f,0.0f,-200.0f);
hb.rotateZ(vz);
hb.rotateX(vx);
hb.rotateY(vy);
HiofLogo.DrawIt(hb);
}
void this_mouseClicked(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
{
if(halted)
start();
else
stop();
}
}
void this_mousePressed(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK)
{
lastx=e.getX();
lasty=e.getY();
halted=true;
stop();
}
}
void this_mouseReleased(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK)
{
int x=e.getX();
int y=e.getY();
if((dvx!=0.0f)||(dvy!=0.0f))
{
halted=false;
start();
}
}
}
void this_mouseDragged(MouseEvent e)
{
if((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK)
{
int x=e.getX();
int y=e.getY();
dvx=5.0f*(x-lastx)/this.getSize().width;
dvy=5.0f*(y-lasty)/this.getSize().height;
dvz=dvx-dvy;
vx+=dvx;
vx+=dvy;
vz+=dvz;
repaint();
lastx=x;
lasty=y;
}
}
//--------------------------------------------------
// start of inner classes: gob, p3D, matrix, polygon
public class gob extends Object
{
//----------------------------------------------------------------
// defining the minimalistic graphical interface:
// clearAll, drawBox, flush, pBegin, pEnd, popMatrix, pushMatrix,
// rotateX, rotateY, rotateZ, scale, translate,
// setBackground, setColor, setIdentityMatrix,
// setOrtho, setperspective, setViewport, vertex
//-----------------------------------------------------------------
//-----------------------------------------------------------------
// intenal classes : p3D, matrix, polygon defined at end of file
//-----------------------------------------------------------------
// fill or outline polygons
public final static int FILL=0;
public final static int FRAME=1;
// the list of polygons to draw
Vector drawList=new Vector(20,6);
// matrix stack, for push and pop
Vector matrixStack=new Vector(5,5);
// current model(view) matrix
matrix curMatrix=null;
// current polygon, while we are adding vertexes
polygon curPoly=null;
// current drawing color
Color curColor=null;
// Background color, last before flush yields
Color BKColor=null;
// buffer to draw in to avoid flicker
Image Buffer;
// viewport
float Vxl,Vyt,Vw,Vh;
// window, model coordinates
float Wxl,Wyt,Ww,Wh;
// perspective angles, in degrees
float Pvx,Pvy;
// use perspective projection or not
boolean isPerspective;
// the actual component that gob will work on
Component theComp;
// possible background image
Image theImage=null;
public gob(Component comp)
{
Buffer=comp.createImage(comp.getSize().width,comp.getSize().height);
setViewport(0,0,comp.getSize().width,comp.getSize().height);
setOrtho(0,0,comp.getSize().width,comp.getSize().height);
theComp=comp;
curColor=new Color(0.9f,0.9f,0.9f);
BKColor=new Color(1.0f,1.0f,1.0f);
}
public void clearAll()
{
drawList.removeAllElements();
curPoly=null;
curMatrix=new matrix();
matrixStack.removeAllElements();
}
public void setIdentityMatrix()
{
curMatrix=new matrix();
}
public void pushMatrix()
{
if(curMatrix==null)
curMatrix=new matrix();
matrixStack.addElement(curMatrix.copyMatrix(curMatrix));
}
public void popMatrix()
{
if(matrixStack.size()<1)
{
//underflow
curMatrix=new matrix();
System.out.println("Matrix stack undeflow, too many pops");
return;
}
curMatrix=(matrix)matrixStack.lastElement();
matrixStack.removeElement(matrixStack.lastElement());
}
public void translate(float dx,float dy,float dz)
{
float[] values={
1.0f,0.0f,0.0f,dx,
0.0f,1.0f,0.0f,dy,
0.0f,0.0f,1.0f,dz,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void scale(float sx,float sy,float sz)
{
float[] values={
sx,0.0f,0.0f,0.0f,
0.0f,sy,0.0f,0.0f,
0.0f,0.0f,sz,0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateX(float v)
{
float[] values={
1.0f,0.0f,0.0f,0.0f,
0.0f,(float)Math.cos(v),(float)-Math.sin(v),0.0f,
0.0f,(float)Math.sin(v),(float)Math.cos(v),0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateY(float v)
{
float[] values={
(float)Math.cos(v),0.0f,(float)Math.sin(v),0.0f,
0.0f,1.0f,0.0f,0.0f,
(float)-Math.sin(v),0.0f,(float)Math.cos(v),0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void rotateZ(float v)
{
float[] values={
(float)Math.cos(v),(float)-Math.sin(v),0.0f,0.0f,
(float)Math.sin(v),(float)Math.cos(v),0.0f,0.0f,
0.0f,0.0f,1.0f,0.0f,
0.0f,0.0f,0.0f,1.0f};
curMatrix.postMult(new matrix(values));
}
public void pBegin(int mode)
{
if(mode==FILL)
curPoly=new polygon(curColor,mode);
else
curPoly=new polygon(curColor,FRAME);
}
public void vertex(float nx, float ny, float nz)
{
p3D npnt=curMatrix.transform(new p3D(nx,ny,nz));
curPoly.appendVertex(npnt);
}
public void pEnd()
{
// calculate the angle for lighting
curPoly.calculateAngle();
// put it into the the drawList according to depth
// lowest minZ comes first in list
for(int ix=0;ix<drawList.size();ix++)
if(((polygon)drawList.elementAt(ix)).getMinZ()>curPoly.getMinZ())
{
drawList.insertElementAt(curPoly,ix);
return;
}
drawList.addElement(curPoly);
}
public void flush(Graphics g)
{
// project the polygons
for(int ix=0;ix<drawList.size();ix++)
{
// project it
if(isPerspective)
((polygon)drawList.elementAt(ix)).perspectiveProject(Pvx,Pvy, Wxl,Wyt,Ww,Wh, Vxl,Vyt,Vw,Vh);
else
((polygon)drawList.elementAt(ix)).paralellProject(Wxl,Wyt,Ww,Wh, Vxl,Vyt,Vw,Vh);
}
// draw drawList from bottom on an offscreen buffer
Graphics tmpg=Buffer.getGraphics();
// clear only the specified viewport
// tmpg.clearRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
tmpg.setColor(BKColor);
// fills with color or set out image
tmpg.fillRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
if(theImage!=null)
tmpg.drawImage(theImage,Math.round(Vxl),Math.round(Vyt),BKColor, null);
// draw it
for(int ix=0;ix<drawList.size();ix++)
((polygon)drawList.elementAt(ix)).render(tmpg);
// copy to viewport
g.clipRect(Math.round(Vxl),Math.round(Vyt),Math.round(Vw),Math.round(Vh));
g.drawImage(Buffer,0,0,null);
}
public void setColor(Color c)
{
curColor=c;
}
public void setBackground(Color c)
{
BKColor=c;
//theImage=null;
}
public void setImage(Image im)
{
if(im==null)
theImage=null;
else
theImage=im;
}
public void setViewport(float xl,float yt,float w, float h)
{
Vxl=xl;Vyt=yt;Vw=w;Vh=h;
}
public void setOrtho(float xl,float yt,float w, float h)
{
Wxl=xl;Wyt=yt;Ww=w;Wh=h;
isPerspective=false;
}
public void setPerspective(float vx,float vy)
{
if(vx < 0.5)
Pvx=0.5f;
else if(vx > 89.5)
Pvx=89.5f;
else
Pvx=vx;
if(vy<0.5)
Pvy=0.5f;
else if(vy > 89.5)
Pvy=89.5f;
else
Pvy=vy;
isPerspective=true;
}
//----------------------------------------------
// draw a box, is handy since this is about all we
// would be tempted to draw in these surroundings
public void drawBox(float dx,float dy, float dz,int mode)
{
// bottom
float x=dx/2.0f;
float y=dy/2.0f;
float z=dz/2.0f;
pBegin(mode);
vertex(-x,y,-z); vertex(-x,-y,-z); vertex(x,-y,-z); vertex(x,y,-z);
pEnd();
// top
pBegin(mode);
vertex(-x,y,z); vertex(x,y,z); vertex(x,-y,z); vertex(-x,-y,z);
pEnd();
// front
pBegin(mode);
vertex(-x,y,-z); vertex(x,y,-z); vertex(x,y,z); vertex(-x,y,z);
pEnd();
// back
pBegin(mode);
vertex(-x,-y,-z); vertex(-x,-y,z); vertex(x,-y,z); vertex(x,-y,-z);
pEnd();
// left
pBegin(mode);
vertex(-x,y,-z); vertex(-x,y,z); vertex(-x,-y,z); vertex(-x,-y,-z);
pEnd();
// right
pBegin(mode);
vertex(x,-y,-z); vertex(x,-y,z); vertex(x,y,z); vertex(x,y,-z);
pEnd();
}
}
//----------------- end of gob
//------------------ start of p3D
class p3D extends Object
{
float x,y,z;
public p3D(float nx, float ny, float nz)
{
x=nx; y=ny; z=nz;
}
}
//----------------- end of p3D
//------------------ start of matrix
class matrix extends Object
{
float[][] m=null;
public matrix()
{
m=new float[][]{
{1.0f,0.0f,0.0f,0.0f},
{0.0f,1.0f,0.0f,0.0f},
{0.0f,0.0f,1.0f,0.0f},
{0.0f,0.0f,0.0f,1.0f}};
}
public matrix(float[] values)
{
m=new float[][]{
{1.0f,0.0f,0.0f,0.0f},
{0.0f,1.0f,0.0f,0.0f},
{0.0f,0.0f,1.0f,0.0f},
{0.0f,0.0f,0.0f,1.0f}};
if(values.length!=16)
return;
m[0][0]=values[0]; m[0][1]=values[1]; m[0][2]=values[2]; m[0][3]=values[3];
m[1][0]=values[4]; m[1][1]=values[5]; m[1][2]=values[6]; m[1][3]=values[7];
m[2][0]=values[8]; m[2][1]=values[9]; m[2][2]=values[10]; m[2][3]=values[11];
m[3][0]=values[12]; m[3][1]=values[13]; m[3][2]=values[14]; m[3][3]=values[15];
}
matrix copyMatrix(matrix sm)
{
float v[]={
sm.m[0][0],sm.m[0][1],sm.m[0][2],sm.m[0][3],
sm.m[1][0],sm.m[1][1],sm.m[1][2],sm.m[1][3],
sm.m[2][0],sm.m[2][1],sm.m[2][2],sm.m[2][3],
sm.m[3][0],sm.m[3][1],sm.m[3][2],sm.m[3][3]};
return new matrix(v);
}
p3D transform(p3D p)
{
return new p3D(
p.x*m[0][0]+p.y*m[0][1]+p.z*m[0][2]+m[0][3],
p.x*m[1][0]+p.y*m[1][1]+p.z*m[1][2]+m[1][3],
p.x*m[2][0]+p.y*m[2][1]+p.z*m[2][2]+m[2][3]);
}
void postMult(matrix tm)
{
// do: m = m X tm.m
float [][] oldm={
{m[0][0],m[0][1],m[0][2],m[0][3]},
{m[1][0],m[1][1],m[1][2],m[1][3]},
{m[2][0],m[2][1],m[2][2],m[2][3]},
{m[3][0],m[3][1],m[3][2],m[3][3]}};
for(int r=0;r<4;r++)
for(int k=0;k<4;k++)
m[r][k]=oldm[r][0]*tm.m[0][k]+
oldm[r][1]*tm.m[1][k]+
oldm[r][2]*tm.m[2][k]+
oldm[r][3]*tm.m[3][k];
}
}
//----------------- end of matrix
//------------------ start of polygon
class polygon extends Object
{
Vector p=new Vector(20,6);
float minZ=100000.0f; // higher than any point
Color pcolor=null;
float cosangle=0.0f;
Polygon pol=null;
int pmode;
public polygon(Color c,int mode)
{
pmode=mode;
pcolor=c;
}
void setColor(float r,float g,float b)
{
pcolor=new Color(r,g,b);
}
float getMinZ()
{
return minZ;
}
int getPCount()
{
return p.size();
}
p3D getPAt(int index)
{
if(index>=p.size() || index<0)
return new p3D(0.0f,0.0f,0.0f);
return (p3D)p.elementAt(index);
}
void setPAt(p3D np,int index)
{
if(index>=p.size() || index<0)
return;
((p3D)p.elementAt(index)).x=np.x;
((p3D)p.elementAt(index)).y=np.y;
((p3D)p.elementAt(index)).z=np.z;
}
void appendVertex(float x,float y, float z)
{
appendVertex(new p3D(x,y,z));
}
void appendVertex(p3D pnt)
{
if(pnt.z<minZ)
minZ=pnt.z;
p.addElement(pnt);
}
void calculateAngle()
{
// calculate normal and angle twds z-axis
if(p.size()<3)
{
cosangle=1.0f;
return;
}
// normal,normalV, from triangle: p0 - p1 - p2
// c=(a2b3-a3b2, a3b1-a1b3, a1b2-a2b1)
p3D p0=(p3D)p.elementAt(0);
p3D p1=(p3D)p.elementAt(1);
p3D p2=(p3D)p.elementAt(2);
p3D normalV=new p3D((p2.y-p1.y)*(p0.z-p1.z)-(p2.z-p1.z)*(p0.y-p1.y),
(p2.z-p1.z)*(p0.x-p1.x)-(p2.x-p1.x)*(p0.z-p1.z),
(p2.x-p1.x)*(p0.y-p1.y)-(p2.y-p1.y)*(p0.x-p1.x));
float len=(float)Math.sqrt(normalV.x*normalV.x+normalV.y*normalV.y+normalV.z*normalV.z);
// normalize. need only z-component
// dont care about direction
// big cosangle -> light face, since light is -z
cosangle=Math.abs(normalV.z/len);
}
//public void paralellProject(float xfactor,float yfactor)
public void paralellProject(
float Wxl, float Wyt, float Ww, float Wh,
float Vxl, float Vyt, float Vw, float Vh)
{
if(p.size()<2)
return;
pol=new Polygon();
float xfactor=Vw/Ww;
float yfactor=Vh/Wh;
for(int ix=0;ix<p.size();ix++)
pol.addPoint(
Math.round( (((p3D)p.elementAt(ix)).x-Wxl)*xfactor+Vxl),
Math.round( (((p3D)p.elementAt(ix)).y-Wyt)*yfactor+Vyt) );
}
public void perspectiveProject(
float vx,float vy,
float Wxl,float Wyt, float Ww, float Wh,
float Vxl, float Vyt, float Vw, float Vh)
{
// W is not used, assumes Rectangle(-0.5,-0.5,1,1)
if(p.size()<2)
return;
pol=new Polygon();
float xf=(float)(0.5f/Math.tan(Math.toRadians(vx)));
float yf=(float)(0.5f/Math.tan(Math.toRadians(vy)));
for(int ix=0;ix<p.size();ix++)
{
float x=((p3D)p.elementAt(ix)).x/Math.abs(((p3D)p.elementAt(ix)).z)*xf;
float y=((p3D)p.elementAt(ix)).y/Math.abs(((p3D)p.elementAt(ix)).z)*xf;
pol.addPoint(Math.round( (x+0.5f)*Vw+Vxl ),Math.round( (y+0.5f)*Vh+Vyt )
);
}
}
public void render(Graphics g)
{
if(pol==null)
return;
if(pmode==gob.FILL)
{
Color c=new Color(
(pcolor.getRed()*(cosangle/255.0f)),
(pcolor.getGreen()*(cosangle/255.0f)),
(pcolor.getBlue()*(cosangle)/255.0f));
g.setColor(c);
g.fillPolygon(pol);
}
else
{
g.setColor(pcolor);
g.drawPolygon(pol);
}
}
}
//------------- end of polygon
public class hiologo
{
final float GtR= 0.01745329251994f; // degrees to radians
final float RO=1.0f;// outer radius
final float D=0.2f;// stroke thickness
final float H=0.2f;// symbol "height", z-extension
final float BOTTOM= -0.26f;// bottom of lines
final float LTOP=0.61f; // top left line
final float RTOP=0.25f; // top right line
final float SHEARV=20.0f; // degrees shear
final int m_nP=40; // precision
// degrees to radians
float rSHEARV=SHEARV*GtR;
// derive geometry from key-constants
// list for outer circlesegment
float CoList[]=new float[3*m_nP];
// list for inner circle segment
float CiList[]=new float[3*m_nP];
// list for left line
float LL_List[]=new float[12];
// list for right line
float RL_List[]=new float[12];
// angles
float rVO1,rVO2,rVI1,rVI2,DVO,DVI;
// for instance R(ight line)T(op)L(eft)
float LTL,LBL,RTL,RBL;
// stroke as measured with VSHEAR
float sD=D/(float)Math.cos(rSHEARV);
void MakeIt()
{
//////////////////////////////////////////////
// This is some dirty piece of code
// Calculate the basic geometry, by producing
// the pointlists: CoList,CiList,LL_List,RL_List
//////////////////////////////////////////////
//////////////////////////////////////////////
// Calculate the geometry
// left line low-left
// same as -fabs(BOTTOM), since BOTTOM is negative
LBL=BOTTOM*(float)Math.tan(rSHEARV)-1.5f*sD;
// right line low-left
RBL=LBL+2.0f*sD;
// left line top-left
LTL=LTOP*(float)Math.tan(rSHEARV)-1.5f*sD;
// right line top-left
RTL=RTOP*(float)Math.tan(rSHEARV)+0.5f*sD;
// set up the lines(strait strokes)
LL_List[0]=LTL; LL_List[1]=LTOP; LL_List[2]=0.0f;
LL_List[3]=LBL; LL_List[4]=BOTTOM; LL_List[5]=0.0f;
LL_List[6]=LBL+sD; LL_List[7]=BOTTOM; LL_List[8]=0.0f;
LL_List[9]=LTL+sD; LL_List[10]=LTOP; LL_List[11]=0.0f;
RL_List[0]=RTL; RL_List[1]=RTOP; RL_List[2]=0.0f;
RL_List[3]=RBL; RL_List[4]=BOTTOM; RL_List[5]=0.0f;
RL_List[6]=RBL+sD; RL_List[7]=BOTTOM; RL_List[8]=0.0f;
RL_List[9]=RTL+sD; RL_List[10]=RTOP; RL_List[11]=0.0f;
// Then we consentrate on the circle-arcs
// set params for outer circle
// startangle outer circle
rVO1=(float)Math.atan2(
(double)RO*(float)Math.sin((90*GtR-rSHEARV)),
(double)RO*(float)Math.cos((90*GtR-rSHEARV))-0.5*sD);
// stoppangle outer circle
rVO2=(float)Math.atan2(
(double)RO*(float)Math.sin((90*GtR-rSHEARV)),
(double)RO*(float)Math.cos((90*GtR-rSHEARV))+0.5*sD);
// sector
DVO=360.0f*GtR-(float)Math.abs(rVO1-rVO2);
// set params for inner circle
// startangle inner circle
rVI1=(float)Math.atan2(
(RO-D)*(float)Math.sin((90-SHEARV)*GtR),
(RO-D)*(float)Math.cos((90-SHEARV)*GtR)-0.5f*sD);
// stopangle inner circle
rVI2=(float)Math.atan2(
(RO-D)*(float)Math.sin((90-SHEARV)*GtR),
(RO-D)*(float)Math.cos((90-SHEARV)*GtR)+0.5f*sD);
DVI=360.0f*GtR-(float)Math.abs(rVI1-rVI2);
// establish a list of points,nPnts, for
// outer and inner pointlist
// make lists, same noof pnts, m_nP, in each
int ix;
float v,dv;
float r;
// outer circle segment
v=rVO1;
r=RO;
CoList[0]=r*(float)Math.cos( v);
CoList[1]=r*(float)Math.sin( v);
CoList[2]=(float)0.0f;
dv=DVO/(m_nP-1);
for(ix=1;ix<m_nP;ix++)
{
v+=dv;
CoList[ix*3+0]=r*(float)Math.cos( v);
CoList[ix*3+1]=r*(float)Math.sin( v);
CoList[ix*3+2]=(float)0.0f;
}
// inner circle segment
v=rVI1;
r=RO-D;
CiList[0]=r*(float)Math.cos( v);
CiList[1]=r*(float)Math.sin( v);
CiList[2]=(float)0.0f;
dv=DVI/(m_nP-1);
for(ix=1;ix<m_nP;ix++)
{
v+=dv;
CiList[ix*3+0]=r*(float)Math.cos( v);
CiList[ix*3+1]=r*(float)Math.sin( v);
CiList[ix*3+2]=(float)0.0f;
}
}
void DrawIt(gob gb)
{
// draw circle-polygons top (z-wise)
int ix;
for(ix=1;ix<m_nP;ix++)
{
gb.pBegin(gob.FILL);
gb.vertex(CoList[3*(ix-1)+0],CoList[3*(ix-1)+1],CoList[3*(ix-1)+2]);
gb.vertex(CiList[3*(ix-1)+0],CiList[3*(ix-1)+1],CiList[3*(ix-1)+2]);
gb.vertex(CiList[3*ix+0],CiList[3*ix+1],CiList[3*ix+2]);
gb.vertex(CoList[3*ix+0],CoList[3*ix+1],CoList[3*ix+2]);
gb.pEnd();
}
// draw circle-polygons bottom
for(ix=1;ix<m_nP;ix++)
{
gb.pBegin(gob.FILL);
gb.vertex(CoList[3*(ix-1)+0],CoList[3*(ix-1)+1],CoList[3*(ix-1)+2]-H);
gb.vertex(CiList[3*(ix-1)+0],CiList[3*(ix-1)+1],CiList[3*(ix-1)+2]-H);
gb.vertex(CiList[3*ix+0],CiList[3*ix+1],CiList[3*ix+2]-H);
gb.vertex(CoList[3*ix+0],CoList[3*ix+1],CoList[3*ix+2]-H);
gb.pEnd();
}
// draw circle-polygons inner
for(ix=1;ix<m_nP;ix++)
{
gb.pBegin(gob.FILL);
gb.vertex(CiList[3*(ix-1)+0],CiList[3*(ix-1)+1],CiList[3*(ix-1)+2]-H);
gb.vertex(CiList[3*ix+0],CiList[3*ix+1],CiList[3*ix+2]-H);
gb.vertex(CiList[3*ix+0],CiList[3*ix+1],CiList[3*ix+2]);
gb.vertex(CiList[3*(ix-1)+0],CiList[3*(ix-1)+1],CiList[3*(ix-1)+2]);
gb.pEnd();
}
// draw circle-polygons outer
for(ix=1;ix<m_nP;ix++)
{
gb.pBegin(gob.FILL);
gb.vertex(CoList[3*(ix-1)+0],CoList[3*(ix-1)+1],CoList[3*(ix-1)+2]-H);
gb.vertex(CoList[3*ix+0],CoList[3*ix+1],CoList[3*ix+2]-H);
gb.vertex(CoList[3*ix+0],CoList[3*ix+1],CoList[3*ix+2]);
gb.vertex(CoList[3*(ix-1)+0],CoList[3*(ix-1)+1],CoList[3*(ix-1)+2]);
gb.pEnd();
}
// draw circle-plygon start-end
gb.pBegin(gob.FILL);
gb.vertex(CoList[0],CoList[1],CoList[2]);
gb.vertex(CiList[0],CiList[1],CiList[2]);
gb.vertex(CiList[0],CiList[1],CiList[2]-H);
gb.vertex(CoList[0],CoList[1],CoList[2]-H);
gb.pEnd();
// draw circle-plygon stop-end
gb.pBegin(gob.FILL);
gb.vertex(CoList[3*(m_nP-1)],CoList[3*(m_nP-1)+1],CoList[3*(m_nP-1)+2]);
gb.vertex(CiList[3*(m_nP-1)],CiList[3*(m_nP-1)+1],CiList[3*(m_nP-1)+2]);
gb.vertex(CiList[3*(m_nP-1)],CiList[3*(m_nP-1)+1],CiList[3*(m_nP-1)+2]-H);
gb.vertex(CoList[3*(m_nP-1)],CoList[3*(m_nP-1)+1],CoList[3*(m_nP-1)+2]-H);
gb.pEnd();
// leftmost stroke
// draw lefmost stroke top
gb.pBegin(gob.FILL);
gb.vertex(LL_List[0],LL_List[1],LL_List[2]);
gb.vertex(LL_List[3],LL_List[4],LL_List[5]);
gb.vertex(LL_List[6],LL_List[7],LL_List[8]);
gb.vertex(LL_List[9],LL_List[10],LL_List[11]);
gb.pEnd();
// draw lefmost stroke bottom
gb.pBegin(gob.FILL);
gb.vertex(LL_List[0],LL_List[1],LL_List[2]-H);
gb.vertex(LL_List[3],LL_List[4],LL_List[5]-H);
gb.vertex(LL_List[6],LL_List[7],LL_List[8]-H);
gb.vertex(LL_List[9],LL_List[10],LL_List[11]-H);
gb.pEnd();
// draw lefmost stroke right
gb.pBegin(gob.FILL);
gb.vertex(LL_List[6],LL_List[7],LL_List[8]);
gb.vertex(LL_List[6],LL_List[7],LL_List[8]-H);
gb.vertex(LL_List[9],LL_List[10],LL_List[11]-H);
gb.vertex(LL_List[9],LL_List[10],LL_List[11]);
gb.pEnd();
// draw lefmost stroke left
gb.pBegin(gob.FILL);
gb.vertex(LL_List[3],LL_List[4],LL_List[5]);
gb.vertex(LL_List[3],LL_List[4],LL_List[5]-H);
gb.vertex(LL_List[0],LL_List[1],LL_List[2]-H);
gb.vertex(LL_List[0],LL_List[1],LL_List[2]);
gb.pEnd();
// draw lefmost stroke upper
gb.pBegin(gob.FILL);
gb.vertex(LL_List[0],LL_List[1],LL_List[2]);
gb.vertex(LL_List[9],LL_List[10],LL_List[11]);
gb.vertex(LL_List[9],LL_List[10],LL_List[11]-H);
gb.vertex(LL_List[0],LL_List[1],LL_List[2]-H);
gb.pEnd();
// draw lefmost stroke lower
gb.pBegin(gob.FILL);
gb.vertex(LL_List[3],LL_List[4],LL_List[5]);
gb.vertex(LL_List[6],LL_List[7],LL_List[8]);
gb.vertex(LL_List[6],LL_List[7],LL_List[8]-H);
gb.vertex(LL_List[3],LL_List[4],LL_List[5]-H);
gb.pEnd();
// rightmost stroke
// draw rightmost stroke top
gb.pBegin(gob.FILL);
gb.vertex(RL_List[0],RL_List[1],RL_List[2]);
gb.vertex(RL_List[3],RL_List[4],RL_List[5]);
gb.vertex(RL_List[6],RL_List[7],RL_List[8]);
gb.vertex(RL_List[9],RL_List[10],RL_List[11]);
gb.pEnd();
// draw rightmost stroke bottom
gb.pBegin(gob.FILL);
gb.vertex(RL_List[0],RL_List[1],RL_List[2]-H);
gb.vertex(RL_List[3],RL_List[4],RL_List[5]-H);
gb.vertex(RL_List[6],RL_List[7],RL_List[8]-H);
gb.vertex(RL_List[9],RL_List[10],RL_List[11]-H);
gb.pEnd();
// draw rightmost stroke left
gb.pBegin(gob.FILL);
gb.vertex(RL_List[0],RL_List[1],RL_List[2]);
gb.vertex(RL_List[0],RL_List[1],RL_List[2]-H);
gb.vertex(RL_List[3],RL_List[4],RL_List[5]-H);
gb.vertex(RL_List[3],RL_List[4],RL_List[5]);
gb.pEnd();
// draw rightmost stroke right
gb.pBegin(gob.FILL);
gb.vertex(RL_List[6],RL_List[7],RL_List[8]);
gb.vertex(RL_List[6],RL_List[7],RL_List[8]-H);
gb.vertex(RL_List[9],RL_List[10],RL_List[11]-H);
gb.vertex(RL_List[9],RL_List[10],RL_List[11]);
gb.pEnd();
// draw rightmost stroke upper
gb.pBegin(gob.FILL);
gb.vertex(RL_List[0],RL_List[1],RL_List[2]);
gb.vertex(RL_List[9],RL_List[10],RL_List[11]);
gb.vertex(RL_List[9],RL_List[10],RL_List[11]-H);
gb.vertex(RL_List[0],RL_List[1],RL_List[2]-H);
gb.pEnd();
// draw rightmost stroke lower
gb.pBegin(gob.FILL);
gb.vertex(RL_List[3],RL_List[4],RL_List[5]);
gb.vertex(RL_List[6],RL_List[7],RL_List[8]);
gb.vertex(RL_List[6],RL_List[7],RL_List[8]-H);
gb.vertex(RL_List[3],RL_List[4],RL_List[5]-H);
gb.pEnd();
}
public hiologo()
{
MakeIt();
}
}
// --------- end of hiologo
// -------------- end of inner classes
}



