//======================================================== // Transforms - a simple applet demonstrating transformations // Written by Chris DeCoro - cdecoro@cat.nyu.edu // Sept. 9, 2002 //======================================================== import java.util.*; import java.net.*; import java.io.*; import java.awt.*; import java.awt.event.*; import Geometry.*; class Vertex { Vector3 pos=null; Vector3 relPos=null; Vector3 nowPos=null; Vector3 normal=null; Color color=null; double[] texCoord = new double[2]; int bone=-1; } class Triangle { int texID; Vertex vtx[] = new Vertex[3]; } class Texture { int id; String name; } class Bitmap { int fileSize; int width; int height; int bitsPerPixel; int compression; int[] colorTable=null; int[] indices=null; int[] colors=null; } public class Particles extends ThreeDimensionalApplet { //vertices[] and faces[] contain the currently loaded geometry private Vector3[] vertices; //private Face[] faces; //Given a stream, downloads the model, parses it, and fills in vertices[]/faces[] //This will traverse through the bone structure, and for each vertex connected to the current bone, // it will determine the relative position of that vertex in the bone's coordinate system //We currently do an exhaustive search, while we should really sort all the stuff int tris = 0; private boolean initialized = false; //Changes the current model //Called from outside of the Java applet (by the scripting in the page) public void setModel(String model) { System.out.println(model); //readModel(openModel(model), theModel, 0); setDamage(); } static final double INCREMENT = 0.2; class Vertex { Vector3 coords; Vector3 normal; } Vertex[][][] vertexCache; Matrix4x4[][] geometryMatrices; double lastTime = 0; ParticleSystem psystem = new ParticleSystem(500); Bitmap flare = null; //Renders the scene public void render() { //On the first run, initialize using the following code if(!initialized) { ambientCoeff(0.8); //0.3 , 0.7 also looked good diffuseCoeff(0.75); ambientAndDiffuseMaterial(201, 125, 73); ambientAndDiffuseColor(255, 255, 255); System.out.println(getDocumentBase()); //flare = readTexture(openModel("http://www.cat.nyu.edu/~cdecoro/HWFinal/flare.bmp")); flare = readTexture(openModel(getDocumentBase() + "/flare.bmp")); texture(flare); initialized = true; lastTime = System.currentTimeMillis() / 1000.0; } //Whenever the display is damaged, redraw using this code if (damage) { //Clear off the screen by drawing a big white box (this is glClear()) identity(); //if(cullFace) // enable(RENDER_CULL_FACE); disable(RENDER_CULL_FACE); //Stop gap measure until we fix that clear(); //Transform the model based on the current settings double hw=bounds().width/2, hh=bounds().height/2; identity(); //graphics.setColor(new Color(255, 255, 255)); //graphics.drawString("This is a test", 50, 50); transform( (new Matrix4x4()) .translate(translateX, translateY,0) .rotateX(angleX) .rotateY(angleY) // .translate(translateX+hw, translateY+hh,0) //.scale(scaleX, scaleY, 1) //.mul(rotationMatrix) //.scale(100, 100, /*-*/ 100) ); //int anim = theAnimation; //long fr = (long)(System.currentTimeMillis() / 50) % theModel.frameCount[anim]; /* begin(RENDER_QUAD); color4i(10, 200, 20, 180); vertex(-200, -200, 0); vertex(+200, -200, 0); vertex(+200, +200, -50); vertex(-200, +200, -50); end(); */ cullFace = false; if(cullFace) enable(RENDER_CULL_FACE); else disable(RENDER_CULL_FACE); disable(RENDER_LIGHTING); color(new Color(0, 255, 0)); begin(RENDER_QUAD); vertex(-40, 0, -40); vertex(40, 0, -40); vertex(40, 0, 40); vertex(-40, 0, 40); end(); if(doLighting) enable(RENDER_LIGHTING); else disable(RENDER_LIGHTING); //enable(RENDER_TEXTURE); double thisTime = ((double)System.currentTimeMillis() / 1000.0); double timeDelta = thisTime - lastTime; lastTime = thisTime; System.out.print("\rFPS: " + 1/timeDelta + "\t"); psystem.update(timeDelta); psystem.render(this); //Clear out the context, ending the frame setContext(null); damage = true; } } boolean modified = false; int selectedK = -1; int selectedI = -1; int selectedJ = -1; //Stores the "old" values for each of the transformation factors, and sets the mouse-down pos //These old values are used to allow for dynamic updates while we drag the mouse public boolean mouseDown(Event e, int x, int y) { boolean leftButton, centerButton, rightButton; centerButton = ((e.modifiers & e.ALT_MASK) > 0); rightButton = ((e.modifiers & e.META_MASK) > 0); leftButton = !centerButton && !rightButton; downX = x; downY = y; oldAngleX = angleX; oldAngleY = angleY; oldTranslateX = translateX; oldTranslateY = translateY; oldScaleX = scaleX; oldScaleY = scaleY; oldTheta = theta; //I dont think we use this anymore oldRotationMatrix = rotationMatrix; setDamage(); return true; } //Here is where we actually perform the transformations public boolean mouseDrag(Event e, int x, int y) { //See what buttons are down //This is a real mess, but I think its JDK1.0 compilant boolean leftButton, centerButton, rightButton, shiftDown; centerButton = ((e.modifiers & e.ALT_MASK) > 0); rightButton = ((e.modifiers & e.META_MASK) > 0); leftButton = !centerButton && !rightButton; shiftDown = ((e.modifiers & e.SHIFT_MASK) > 0); double hw=bounds().width/2, hh=bounds().height/2; //Virtual trackball rotation if(leftButton && !shiftDown) { int dY = downX - x; int dX = downY - y; angleX = oldAngleX + dX; angleY = oldAngleY + dY; if(angleX < 5) angleX = 5; if(angleX > 90) angleX = 90; } else if(leftButton && shiftDown) { //We have the vector from the center to the mouse-down position //We can compute the vector to the Vector3 center = new Vector3(translateX+hw,translateY+hh,0); Vector3 handle = (new Vector3(x, y, 0)).sub(center); Vector3 start = (new Vector3(downX,downY,0)).sub(center); //Get the angle from the dot product, dividing by norms and takign arccos //If we wanted to be clever, we could keep the cos as it is, find sin // using sin = sqrt(1-cos^2) and plug sin,cos into the rotatematrix directly //This is a 2d operation double dot = handle.dot(start); double angle = Math.acos(dot/handle.norm()/start.norm())*180/Math.PI; //If we went left (i think its left), take the negative angle //We will know if the crossproduct is pointing back at us if( handle.cross(start).z() > 0 ) angle = -angle; //Update the old rotation matrix //rotationMatrix = (oldRotationMatrix).rotateZ(angle); rotationMatrix = (new Matrix4x4()).rotateZ(angle).mul(oldRotationMatrix); } else if(centerButton) { //Translate the object based on mouse pos. translateX = oldTranslateX + (x - downX); translateY = oldTranslateY + (y - downY); } else if(rightButton) { //Rescale the object based on the mouse position scaleX = oldScaleX + (x - downX)/50.0; scaleY = oldScaleY + -(y - downY)/50.0; } setDamage(); return true; } //Just reset the mouse positions to their old values public boolean mouseUp(Event e, int x, int y) { boolean leftButton, centerButton, rightButton; centerButton = ((e.modifiers & e.ALT_MASK) > 0); rightButton = ((e.modifiers & e.META_MASK) > 0); leftButton = !centerButton && !rightButton; if( downX == -1 ) return true; selectedK = -1; selectedI = -1; selectedJ = -1; downX = -1; downY = -1; setDamage(); return true; } boolean cullFace=true; boolean drawAxis=true; boolean wire=false; boolean doLighting=true; int selectedMode = 0; int theAnimation = 0; boolean skeletonOnly = false; boolean drawControl = true; static final int ANIMATION_COUNT = 3; public boolean keyUp(Event evt, int key) { switch(key) { case Event.TAB: modified = false; selectedK = -1; selectedJ = -1; selectedI = -1; break; } return true; } //Toggle a couple of settings, based on input public boolean keyDown(Event evt, int key) { switch(key) { case 'b': cullFace = !cullFace; break; case 'x': drawAxis = !drawAxis; break; case 'w': wire = !wire; wireframe(wire); break; case 's': skeletonOnly = !skeletonOnly; break; case 'l': doLighting = !doLighting; break; case ' ': theAnimation = (theAnimation+1) % ANIMATION_COUNT; switch (theAnimation) { case 0: psystem.setFountain(); System.out.println("Fountain"); break; case 1: psystem.setExplosion(); System.out.println("Explosion"); break; case 2: psystem.setRings(); System.out.println("Rings"); break; } break; case Event.ESCAPE: translateX = translateY = oldTranslateX = oldTranslateY = theta = oldTheta = rotationTheta = 0; scaleX = scaleY = oldScaleX = oldScaleY = 1; rotationMatrix = oldRotationMatrix = new Matrix4x4(); break; case 'c': drawControl = !drawControl; break; case Event.TAB: modified = true; break; } setDamage(); return true; } //Settings for transformations int downX=-1, downY=-1; double translateX=0, translateY=0; double oldTranslateX=0, oldTranslateY=0; //double scaleX=1, scaleY=1; //double scaleX=0.32, scaleY=0.32; double scaleX=0.2, scaleY=0.2; double oldScaleX=1, oldScaleY=1; double theta=0, oldTheta=0; Vector3 rotationAxis=new Vector3(1, 0, 0); double rotationTheta=0; double angleY=0, oldAngleY=0, angleX=10, oldAngleX=10; //Matrix4x4 rotationMatrix=new Matrix4x4(); Matrix4x4 rotationMatrix=new Matrix4x4(0.208, .997, -.009, 0, .0026, -.0101, -.999, 0, -.978, .208, -.0046, 0, 0, 0, 0, 1); Matrix4x4 oldRotationMatrix=new Matrix4x4(); public static void main(String[] args) { Matrix4x4 mat1 = (new Matrix4x4()).rotateX(18).translate(1, 1, 1); Matrix4x4 mat2 = (new Matrix4x4()).translate(1, 1, 1).rotateX(18); Frame frame = new Frame("Kinematics"); Particles pnl = new Particles(); pnl.setSize(900, 900); frame.setSize(900, 900); frame.add(pnl); frame.show(); frame.addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent e) { e.getWindow().dispose(); System.exit(0); } } ); pnl.init(); pnl.start(); } }