// Copyright 2001 Ken Perlin package render; import java.applet.*; import java.awt.*; import java.awt.image.*; public class RenderApplet extends Applet implements Runnable { private String notice = "Copyright 2001 Ken Perlin. All rights reserved."; //--- PUBLIC DATA FIELDS public Renderer renderer; // THE RENDERER OBJECT public Geometry world; // ROOT OF SCENE GEOMETRY public boolean showFPS = false; // SHOWING FRAME RATE? FLAG public boolean enableLod = false; public double theta = 0, phi = 0; public static boolean noViewElevation = false; //--- PUBLIC METHODS public void animate(double time) { isDamage = false; } // OVERRIDE TO ANIMATE public void damage() { renderer.refresh(); isDamage = true; } // FORCE A RERENDER public void setFOV(double value) { renderer.setFOV(value); }// SET F.O.VIEW public void setFL(double value) { renderer.setFL(value); }// SET FOCAL LENGTH public void setBgColor(double r, double g, double b) { // SET BACKGD COLOR renderer.setBgColor(r, g, b); } public void addLight(double x,double y,double z, // ADD A LIGHT SOURCE double r,double g,double b) { renderer.addLight(x, y, z, r, g, b); } // PUBLIC METHODS TO LET THE PROGRAMMER MANIPULATE A MATRIX STACK public void identity() { Matrix.identity(m()); } public double[][] m() { return m[top]; } public void pop() { top--; } public void push() { Matrix.copy(m[top],m[top+1]); top++; } public void rotateX(double t) { Matrix.rotateX(m(), t); } public void rotateY(double t) { Matrix.rotateY(m(), t); } public void rotateZ(double t) { Matrix.rotateZ(m(), t); } public void scale(double x,double y,double z) { Matrix.scale(m(),x,y,z); } public void transform(Geometry s) { s.setMatrix(m()); } public void translate(double x,double y,double z) { Matrix.translate(m(), x, y, z); } // PUBLIC METHODS TO LET THE PROGRAMMER DEFORM AN OBJECT public int pull(Geometry s, double x0,double x1,double x2, double y0,double y1,double y2, double z0,double z1,double z2) { return s.pull(m(), x0,x1,x2, y0,y1,y2, z0,z1,z2); } //--- SYSTEM LEVEL PUBLIC METHODS --- Image bufferIm; int pix[]; public void init() { W = bounds().width; H = bounds().height; renderer = new Renderer(); pix = renderer.init(W,H); mis = new MemoryImageSource(W, H, pix, 0, W); mis.setAnimated(true); im = createImage(mis); bufferIm = createImage(W, H); startTime = getCurrentTime(); world = renderer.getWorld(); // GET ROOT OF GEOMETRY identity(); initialize(); } public void initialize() { } // APPLICATION PROGRAM OVERRIDES THIS public void start() { if (t == null) { t = new Thread(this); t.start(); } } public void stop() { if (t != null) { t.stop(); t = null; } } public void run() { int o = 0; while(true) { // MEASURE ELAPSED TIME AND FRAMERATE elapsed += getCurrentTime() - currentTime; currentTime = getCurrentTime(); if (isDamage) { frameRate = .9*frameRate + .1/elapsed; elapsed = 0; // LET THE APPLICATION PROGRAMMER MOVE THINGS INTO PLACE identity(); // APPLIC. MATRIX STARTS UNTRANSFORMED isDamage = true; if (noViewElevation) phi = 0; renderer.rotateView(theta, phi); theta = phi = 0; animate(currentTime-startTime); // APPLICATION ANIMATES THINGS // SHADE AND SCAN CONVERT GEOMETRY INTO FRAME BUFFER renderer.render(); // KEEP REFINING LEVEL OF DETAIL UNTIL PERFECT (WHEN LOD=1) if (renderer.lod > 1) { isDamage = true; renderer.lod--; } // WRITE RESULTS TO THE SCREEN if (seeMaterial) { int n = 0; for ( ; n < world.child.length ; n++) if (world.child[n].material != null) break; Material m = world.child[n].material; for (int x = 0 ; x < 128 ; x++) for (int y = 0 ; y < 128 ; y++) { int i = y*W+x; pix[i] = m.table[(x< H-14; return true; } // MOUSE DOWN STARTS A VIEW ROTATION public boolean mouseDown(Event event, int x, int y) { if (event.controlDown() == false && event.metaDown() == false) { // ROBBINS - Added test to ensure LEFT mouse button was pressed renderer.setDragging(true); mx = x; my = y; } return true; } // MOUSE DRAG MAKES VIEW ANGLE GRADUALLY ROTATE public boolean mouseDrag(Event event, int x, int y) { if (event.controlDown() == false && event.metaDown() == false) { // ROBBINS - Added test to ensure LEFT mouse button was pressed theta += .03 * (double) (x - mx); // HORIZONTAL VIEW ROTATION phi += .03 * (double) (y - my); // VERTICAL VIEW ROTATION //System.out.println("theta = " + theta + " phi = " + phi); mx = x; my = y; if (frameRate < 10 && renderer.lod < 4) if (enableLod) renderer.lod++; isDamage = true; } return true; } public boolean mouseUp(Event event, int x, int y) { renderer.setDragging(false); if (x < 35 && y < 35) Renderer.tableMode = ! Renderer.tableMode; if (x > W-35 && y < 35) { seeMaterial = ! seeMaterial; if (!seeMaterial) renderer.refresh(); } if (x > W-35 && y > H-35) renderer.showMesh = ! renderer.showMesh; return true; } //--- PRIVATE METHODS // GET THE CURRENT TIME IN SECONDS private double getCurrentTime() { return System.currentTimeMillis() / 1000.; } //--- PRIVATE DATA FIELDS private int mx, my; // CURRENT MOUSE POSITION private MemoryImageSource mis; // THE IMAGE MEMORY SOURCE OBJECT private Thread t; // RENDERING THREAD private int W, H; // IMAGE WIDTH,HEIGHT,FRAMEBUFFER private Image im; // IMAGE OF MEMORY SOURCE OBJECT private boolean isDamage = true; // WHETHER WE NEED TO RECOMPUTE IMAGE private double startTime = 0, currentTime = 0, elapsed = 0, frameRate = 10; private double m[][][] = new double[10][4][4]; // THE MATRIX STACK private int top = 0; // MATRIX STACK POINTER private boolean seeMaterial = false; }