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 }