package BulletGame$1; import java.awt.Dimension; import java.awt.Point; import java.awt.Toolkit; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import javax.media.opengl.GL; import javax.media.opengl.GL2; import javax.media.opengl.glu.GLU; import javax.media.opengl.glu.GLUtessellatorCallback; import javax.swing.JFrame; import processing.core.PApplet; import processing.core.PFont; import processing.core.PImage; import processing.opengl.GLPersist; import processing.opengl.PGraphicsOpenGL; import processing.opengl.GLPersist.GLPersistCB; import TaiGameCore.GameSprite; import TaiGameCore.GameVirtualFS; import TaiGameCore.P5GLExtend; import TaiGameCore.SinCosLUT; import TaiGameCore.GameSprite.GameGraphic; import TaiGameCore.RelativelyTimed.RelativeTimeNode; import TaiGameCore.RelativelyTimed.TimeSource; import ddf.minim.AudioPlayer; import ddf.minim.Minim; /** * NOTE: If you instantiate THIS CLASS, you will get just that: no screens. * Instantiate a subclass to provide more and more screens. (See * bulletGame$1Engine$L{N}, so that N is large.) */ public abstract class BulletGame$1Engine$ABasicEngine extends P5GLExtend implements SinCosLUT { /** * Game genesis follows */ public BulletGame$1Engine$ABasicEngine(JFrame holder, PApplet hold) { super(hold); /* AllocationRecorder.addSampler(new Sampler() { private boolean inStackTrace = false; public void sampleAllocation(int count, String desc, Object newObj, long size) { if (!g.mousePressed){ return; } if (g.frameCount<2){ return; } if (inStackTrace){ return; } inStackTrace = true; StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); inStackTrace = false; for(int i = 3; i < stackTrace.length && i < 10; i++){ System.out.println(stackTrace[i]); } System.out.println("I just allocated the object " + newObj + " of type " + desc + " whose size is " + size); if (count != -1) { System.out.println("It's an array of size " + count); } } }); */ this.holder = holder; try { AUDIO = new Minim(hold); } catch (Throwable e) { //Note to readers: I'm explicitly //catching any "ClassNotFound" exceptions with this. e.printStackTrace(); System.err.println("WARNING: Audio unsupported."); AUDIO = null; } FILE_SYSTEM = new GameVirtualFS(hold); GAME_TIME = new TimeSource() { public double time() { return System.nanoTime() / 1e9; } }; firstFrameGoToDefaultScreen = true; System.gc(); sinLUT = new float[SC_PERIOD]; cosLUT = new float[SC_PERIOD]; initSinCosLookup(); //GO! if (hold!=null){ hold.registerDraw(this); hold.registerKeyEvent(this); } } // TO BE IMPLEMENTED AT THE BASE LAYER! public abstract BulletGameScreen SCREEN(int num); public final TimeSource GAME_TIME; public Minim AUDIO; public GameVirtualFS FILE_SYSTEM; public boolean isResized = false; public boolean wantsClearImages = false; /** * Frames seem to be more reliable. Hmm.... * @deprecated */ public boolean TIME_BASED_INTEGRATION = false; private JFrame holder; private Dimension restoreToDim = null; // Before maximizing. private Point oldLocation = null; private ArrayList<KeyEvent> toDeque = new ArrayList<KeyEvent>(); private int needsCallMaximizeGame = 0; private Dimension lastSize = new Dimension(); private boolean firstFrameGoToDefaultScreen = false; private Dimension lastDim = new Dimension(); private BulletGameScreen currentScreen; private int[] dialogStoleThePreciousMouseCoord = new int[2]; private ModalDialog currentDialog; public boolean hasCurrentDialog() { return currentDialog != null; } private BulletGameScreen shoeingInScreen; private TransitioningScreen midMan; // TEXT SHADER ATTRIBUTE MAP public static enum Shader1VertShader { // short corner : ATTR2, float4 data : ATTR3, float2 texSize : ATTR4 Center_X("Center_X", GL.GL_FLOAT, 3, 0), // Center_Y("Center_Y", GL.GL_FLOAT, 3, 1), // RectWidth("RectWidth", GL.GL_FLOAT, 4, 0), // TODO // nonsquare RectHeight("RectHeight", GL.GL_FLOAT, 4, 1), // Rotation("Rotation", GL.GL_FLOAT, 3, 3), // X_TexOffset("X_TexOffset", GL.GL_FLOAT, 5, 0), // Y_TexOffset("Y_TexOffset", GL.GL_FLOAT, 5, 1), // TexScaleX("TexScaleX", GL.GL_FLOAT, 5, 2), // TexScaleY("TexScaleY", GL.GL_FLOAT, 5, 3), // // DO NOT USE ATTRIBUTE 6. // tint("tint",GL.GL_FLOAT,) ; public final String name; public final int Type; public final int attribNum; public final int attribOff; private Shader1VertShader(String name, int Type, int attribNum, int off) { this.name = name; this.Type = Type; this.attribNum = attribNum; this.attribOff = off; } } /***************************************************************** *****************************************************************/ /***************************************************************** *****************************************************************/ /***************************************************************** *****************************************************************/ /***************************************************************** *****************************************************************/ /***************************************************************** *****************************************************************/ /***************************************************************** *****************************************************************/ /***************************************************************** *****************************************************************/ /***************************************************************** *****************************************************************/ /***************************************************************** *****************************************************************/ /***************************************************************** *****************************************************************/ // END SUPER GLOBALS. public class TimeRenderer { { dFont = FILE_SYSTEM.getFont("Comfortaa-Thin-48.vlw"); } PFont dFont; public void draw() { g.textMode(g.MODEL); g.textFont(dFont); g.textAlign(g.LEFT, g.TOP); g.fill(255, 238, 85); g.pushMatrix(); g.scale(1f / currentViewPortWidth, 1f / currentViewPortHeight); g.text(String.format("%.2f",g.frameRate), -7, 5); g.popMatrix(); // screen2D(); /* * screen2D4GL(640,480); GL gl = ((PGraphicsOpenGL)g.g).gl; * gl.glPushMatrix(); gl.glColor3f(0,0,0); gl.glBegin(GL.GL_QUADS); * gl.glVertex3f(0,0,0); gl.glVertex3f(0,.5f,0); * gl.glVertex3f(.5f,.5f,0); gl.glVertex3f(.5f,0,0); gl.glEnd(); * gl.glPopMatrix(); */ } } public static abstract class Transitioner { public abstract double getLength(); public abstract void drawTrans(float lerpr); public static int WIPE = -1, SHUTTERS = -1, FLASH = -1, FADE_WITH_BLACK = -1; } public Transitioner[] transitions = new Transitioner[] { new Transitioner() { { Transitioner.SHUTTERS = 0; } public double getLength() { return .8; } public void drawTrans(final float lerpr) { final int[] numberings = new int[] { 0 }; int numW = 6; int numH = 6; float bright = g.constrain(lerpr * 5, 0, 1); for (int x = 0; x < numW; x++) { for (int y = 0; y < numH; y++) { GLPersistCB xFormer = new GLPersistCB() { private int number = numberings[0]++; public void transform(GL2 gl) { gl.glTranslatef(.5f, .5f, 0f); float rot = g.constrain(1.05f - lerpr * (number / 40f + .95f), 0, 1) * 90; gl.glRotatef(rot, 1f, .8f, 0f); gl.glTranslatef(-.5f, -.5f, 0f); } }; Rectangle2D.Float useRect = new Rectangle2D.Float( 1f / numW * x, 1f / numH * y, 1f / numW, 1f / numH); currentScreen.frozen.beginPersist(1, (int) (useRect.x * g.width), (int) (useRect.y * g.height), (int) (useRect.getMaxX() * g.width), (int) (useRect.getMaxY() * g.height)); currentScreen.frozen.setSetColor(bright, bright, bright); currentScreen.frozen .set( -(int) (useRect.x * g.width), -(int) Math .ceil((1f - useRect.height - useRect.y) * g.height), bright, xFormer); currentScreen.frozen.endPersist(); } } } }, new Transitioner() { { Transitioner.WIPE = 1; } public double getLength() { return .4; } public void drawTrans(final float lerpr) { currentScreen.frozen.beginPersist(1, 0, 0, (int) (g.width * lerpr), g.height); currentScreen.frozen.set(0, 0, 1, null); currentScreen.frozen.endPersist(); } }, new Transitioner() { { Transitioner.FLASH = 2; } public double getLength() { return .4; } public void drawTrans(final float lerpr) { currentScreen.frozen.beginPersist(1, 0, 0, g.width, g.height); float bright = g.cos((1 - lerpr) * 1.5f * g.TWO_PI) * .5f + .5f; currentScreen.frozen.setSetColor(bright, bright, bright); currentScreen.frozen.set(0, 0, 1, null); currentScreen.frozen.endPersist(); } }, new Transitioner() { { Transitioner.FADE_WITH_BLACK = 3; } public double getLength() { return .6; } public void drawTrans(final float lerpr) { if (lerpr > .5f) { // Fade to black. currentScreen.frozen.beginPersist(1, 0, 0, g.width, g.height); float bright = g.cos((1 - lerpr) * g.TWO_PI) * .5f + .5f; currentScreen.frozen .setSetColor(bright, bright, bright); currentScreen.frozen.set(0, 0, 1, null); currentScreen.frozen.endPersist(); } else { // Remove a black screen float alpha = lerpr * 2f; g.fill(0, 0, 0, alpha * 255); g.rect(0, 0, 1, 1); } } }, }; public class TransitioningScreen { private RelativeTimeNode clock; private Transitioner currentTrans; private int nowScreen; private double outTime; public TransitioningScreen(int whichTrans, int now) { currentTrans = transitions[whichTrans]; // Logic to decide behavior... outTime = currentTrans.getLength(); nowScreen = now; } private Snapshotter snapshotter = new Snapshotter(); private OverlayRender overlayer = new OverlayRender(); public boolean warned = false; //Ignore this. Not important! public class Snapshotter { public void draw() { viewport(0, 0, 1, 1); screen2D(); if (currentScreen.wantsFrozen && currentScreen.frozen == null) { ((PGraphicsOpenGL) g.g).MAKE_MIPMAPS = false; // TODO this thing sucks. currentScreen.frozen = new GLPersist(g); currentScreen.frozen.beginPersist(1, GL.GL_LINEAR, 0, 0, g.width, g.height); // Copies on first time. currentScreen.frozen.endPersist(); try { currentScreen.cleanup(); } catch (Throwable e){ e.printStackTrace(); } FILE_SYSTEM.clearImages(); // Trigger the next screen shoeingInScreen = SCREEN(nowScreen); shoeingInScreen.denyInput(true); g.registerDraw(midMan.overlayer); } } } public class OverlayRender { int frameSkips = 0; public void draw() { viewport(0, 0, 1, 1); screen2D(); if (currentScreen.frozen != null) { if (clock != null && clock.time() > outTime) { currentScreen.frozen.cleanup(); currentScreen.frozen = null; currentScreen = shoeingInScreen; // Note: this method // makes problems if // you have > 1 // transition going! // watch out. shoeingInScreen = null; midMan = null; currentScreen.denyInput(false); g.unregisterDraw(snapshotter); g.unregisterDraw(overlayer); } else { javax.media.opengl.GL gl = ((PGraphicsOpenGL) g.g).gl; gl.glClear(javax.media.opengl.GL.GL_DEPTH_BUFFER_BIT); final float lerpr = 1 - (float) ((clock == null ? 0 : clock.time()) / outTime); // == // useWidth // Begin transformation currentTrans.drawTrans(lerpr); // End transformation if (frameSkips++ == 2) { if (clock == null) { clock = new RelativeTimeNode(GAME_TIME);// Reset } } } } else if (clock.time() > 10) { System.err .println("Whoa, snapshotting this screen for the Transition took > 10 seconds!!!"); } } } } public abstract class BulletGameScreen implements KeyListener { public BulletGameScreen() { } public void keyPressed(KeyEvent e) { }; public void keyReleased(KeyEvent e) { }; public void keyTyped(KeyEvent e) { }; private long lastIgnoringInput = -1; public final void draw() { if (this != currentScreen && currentScreen != null) { // Then, if the currentScreen isn't ready to be disposed... if (currentScreen.frozen == null) { return; } } if (ignoringInput && currentDialog == null) { if (lastIgnoringInput == -1) { lastIgnoringInput = System.nanoTime(); } // Fix the escape key freezeing feature if (g.mousePressed) { ignoringInput = false; } if ((System.nanoTime() - lastIgnoringInput) > .2e9) { ignoringInput = false; } } else { lastIgnoringInput = -1; } drawScreen(); } public abstract void drawScreen(); private boolean wantsFrozen = false; public GLPersist frozen; public void freeze() { wantsFrozen = true; } public abstract void cleanup(); public boolean ignoringInput = false; private boolean specialModalDialogInputIgnore = false; public void denyInput(boolean b) { ignoringInput = b; } public boolean isInputBlocked() { return ignoringInput; } } public interface ModalDialogCallback<E extends ModalDialog> { public void dialogFinished(E self); } public abstract class ModalDialog implements KeyListener { private ModalDialogCallback cb; /* * public ModalDialog(ModalDialogCallback onReturn){ * this(null,onReturn); } */ private BulletGameScreen parent; public ModalDialog(final BulletGameScreen parent, ModalDialogCallback onReturn) { this.parent = parent; if (currentDialog != null) { throw new RuntimeException( "Only one modal dialog allowed at a time."); } currentDialog = this; cb = onReturn; dialogStoleThePreciousMouseCoord[0] = -1; // invalidate old state // this is a problem when modaldialogs get added mid-frame-draw... dialogStoleThePreciousMouseCoord[1] = -1; parent.denyInput(true); parent.specialModalDialogInputIgnore = true; g.registerDraw(this); addSubKeyListener(this); closeOnEscape = new KeyAdapter() { public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { if (parent != null && !parent.specialModalDialogInputIgnore) { modalDialogEscapePressed = true; parent.specialModalDialogInputIgnore = true; } } } }; addSubKeyListener(closeOnEscape); } private boolean modalDialogEscapePressed = false; private KeyListener closeOnEscape; public void keyPressed(KeyEvent e) { } public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { } private boolean first = false; public final void draw() { try { drawUnprotected(); } catch (OutOfMemoryError e) { crashCleanup(); } } private void drawUnprotected() { // Fix the escape key freezing feature if (g.mousePressed) { parent.specialModalDialogInputIgnore = false; } if (dialogStoleThePreciousMouseCoord[0] != -1) g.mouseX = dialogStoleThePreciousMouseCoord[0]; if (dialogStoleThePreciousMouseCoord[1] != -1) g.mouseY = dialogStoleThePreciousMouseCoord[1]; if (drawDialog() || modalDialogEscapePressed) { currentDialog = null; // we have to get rid of it FIRST. g.unregisterDraw(this); removeSubKeyListener(closeOnEscape); removeSubKeyListener(this); if (cb != null) cb.dialogFinished(this); // then call the callback (may set // up a // new modal dialog to replace this // one). g.mousePressed = false; // Usability problem, if the next dialog // has // a button in the same place as the last one (require a new // click) } } /** * This does nothing. The actual components of the modaldialog are * already cleaned up; just worry about your subclass code please. */ public void cleanup() { } /** * Return TRUE if this dialog is done! */ public abstract boolean drawDialog(); } /** * An emergency strategy which is able to return the application * to a good-state after an OOM exception. * Caveat: Ability to recover is dependent on programmer properly * implementing the cleanup() method to produce memory-freeing effects. */ public void crashCleanup() { if (currentDialog != null) { currentDialog.cleanup(); g.unregisterDraw(currentDialog); currentDialog = null; } currentScreen.cleanup(); currentScreen = null; firstFrameGoToDefaultScreen = true; wantsClearImages = true; } public boolean SceneChange(int trans, int now) { if (midMan != null) { if (!midMan.warned){//Debug printout at most once. System.err.println("Transition not finished yet! Wait!"); } midMan.warned = true; return false; } // Ok, if we're doing a scene change, get RID of the modaldialog. currentDialog = null; currentScreen.denyInput(true); currentScreen.freeze(); midMan = new TransitioningScreen(trans, now); // The trans overlays the // frozen on TOP g.registerDraw(midMan.snapshotter); return true; } public void maximizeGame() { if (restoreToDim != null) { // Return to restore. holder.setSize(restoreToDim); holder.setLocation(oldLocation); restoreToDim = null; return; } oldLocation = holder.getLocation(); restoreToDim = holder.getSize(); Toolkit toolkit = Toolkit.getDefaultToolkit(); Dimension dim = toolkit.getScreenSize(); holder.setSize(dim); holder.setLocation(new Point(0, 0)); g.resize(dim); viewport(0, 0, 1, 1); } public void keyEvent(KeyEvent e) { toDeque.add(e); } public void draw() { try { drawUnprotected(); } catch (OutOfMemoryError e) { crashCleanup(); } } private void drawUnprotected() { if (firstFrameGoToDefaultScreen) { currentScreen = SCREEN(0); // At this point, we can be sure our // context is valid. firstFrameGoToDefaultScreen = false; } // Do we need to flush images? if (wantsClearImages) { wantsClearImages = false; FILE_SYSTEM.clearImages(); } // Ok, if we have a dialog, temporarily hide the mouse coords until it // draws: if (currentDialog != null) { dialogStoleThePreciousMouseCoord[0] = g.mouseX; dialogStoleThePreciousMouseCoord[1] = g.mouseY; g.mouseX = -1; g.mouseY = -1; } while (toDeque.size() > 0) { g.keyEvent = toDeque.remove(0); if (toDeque.size() > 0) handleInputAtBeginningOfDraw(); } handleInputAtBeginningOfDraw(); if (needsCallMaximizeGame > 0) { needsCallMaximizeGame = 0; maximizeGame(); } Dimension newDim = new Dimension(g.width, g.height); if (!lastDim.equals(lastDim = newDim)) { isResized = true; } else { isResized = false; } // Just the basics: g.background(255); viewport(0, 0, 1, 1); screen2D(); // Game native resolution. Some pages may use less / more or // other random things. g.noSmooth(); ((PGraphicsOpenGL) g.g).gl.glDisable(GL.GL_MULTISAMPLE); } public boolean keyEventSyncSuper() { if (g.keyEvent.isAltDown() && g.keyEvent.paramString().contains("keyChar=Enter")) { needsCallMaximizeGame = 1; return false; } return true; } public void drawGameGraphic(GameGraphic gfx) { drawGameGraphic(gfx, .5f, .5f, 1, 1, 0); } public void drawGameGraphic(GameSprite gfx, int frame, float a, float b, float c, float d, float rotation) { GameGraphic toDraw = null; if (gfx.frame.length >= frame + 1) { toDraw = gfx.frame[frame]; } drawGameGraphic(toDraw, a, b, c, d, rotation); } public GameGraphic getGameGraphicG(GameSprite gfx, int frame) { GameGraphic toDraw = null; if (gfx.frame.length >= frame + 1) { toDraw = gfx.frame[frame]; } return toDraw; } public PImage getGameGraphic(GameSprite gfx, int frame) { GameGraphic toDraw = getGameGraphicG(gfx, frame); return getGameGraphic(toDraw); } public PImage getGameGraphic(GameGraphic toDraw) { String fname = getImgUrlFromGraphic(toDraw); PImage got = FILE_SYSTEM.getImg(fname); return got; } public String getImgUrlFromGraphic(GameGraphic toDraw) { String fname = "[default]"; if (toDraw != null) { fname = toDraw.filename; } return fname; } public void drawGameGraphic(GameGraphic gfx, float a, float b, float c, float d, float rotation) { PImage got = getGameGraphic(gfx); int[] r = getGameGraphicPixels(gfx, got); drawGameGraphic(r, got, a, b, c, d, rotation); } public int[] getGameGraphicPixels(GameGraphic gfx, PImage got) { int[] r = null; if (gfx != null) { r = gfx.rect; } if (r == null) { r = new int[] { -1, -1, -101, -101 }; } for (int k = 0; k <= 2; k += 2) { if (r[k] < 0) { r[k] = (int) ((-r[k] - 1) * got.width / 100.); } } for (int k = 1; k <= 4; k += 2) { if (r[k] < 0) { r[k] = (int) ((-r[k] - 1) * got.height / 100.); } } return r; } public int[] getGameGraphicPixelsGL(GameGraphic gfx, PImage got) { int[] r = null; if (gfx != null) { r = gfx.rect; } if (r == null) { r = new int[] { -1, -1, -101, -101 }; } for (int k = 0; k <= 2; k += 2) { if (r[k] < 0) { r[k] = (int) ((-r[k] - 1) * got.width / 100.); } } for (int k = 1; k <= 4; k += 2) { if (r[k] < 0) { r[k] = (int) ((-r[k] - 1) * got.height / 100.); } } return r; } public void drawGameGraphicGL(GameSprite gfx, int frame, float a, float b, float c, float d, float rotation) { GameGraphic toDraw = null; if (gfx.frame.length >= frame + 1) { toDraw = gfx.frame[frame]; } drawGameGraphicGL(toDraw, a, b, c, d, rotation); } public void drawGameGraphicGL(GameGraphic gfx, float a, float b, float c, float d, float rotation) { PImage got = getGameGraphic(gfx); int[] r = getGameGraphicPixelsGL(gfx, got); drawGameGraphicGL(r, got, a, b, c, d, rotation); } /** * Use screen2D4GL(wantedWidth, wantedHeight)(); first. */ public void drawGameGraphicGL(int[] r, PImage got, float a, float b, float c, float d, float rotation) { // rotation is in degrees float[] t = predrawGameGraphicGL(got, r); GL gl = ((PGraphicsOpenGL) g.g).gl; duringDrawGameGraphicGL(t, a, b, c, d, rotation, 0); gl.glDisable(GL.GL_TEXTURE_2D); } /** * When you're done, use gl.glDisable(GL.GL_TEXTURE_2D); * * @param r */ public float[] predrawGameGraphicGL(PImage got, int[] r) { GL gl = ((PGraphicsOpenGL) g.g).gl; gl.glEnable(GL.GL_TEXTURE_2D); int[] t = ((PGraphicsOpenGL) g.g).bindTexture(got); float[] t2 = new float[] { t[0], t[1] }; /** * We need these texcoords: gl.glTexCoord2d(r[0]/t[0], * (r[1]+r[3])/t[1]); gl.glTexCoord2d(r[0]/t[0], (r[1])/t[1]); * gl.glTexCoord2d((r[0]+r[2])/t[0], (r[1])/t[1]); * gl.glTexCoord2d((r[0]+r[2])/t[0], (r[1]+r[3])/t[1]); **/ float[] texCoords = new float[] { r[0] / t2[0], (r[1] + r[3]) / t2[1], r[0] / t2[0], (r[1]) / t2[1], (r[0] + r[2]) / t2[0], (r[1]) / t2[1], (r[0] + r[2]) / t2[0], (r[1] + r[3]) / t2[1], }; return texCoords; } public final void duringDrawGameGraphicGL(float[] allTexCoords, float a, float b, float c, float d, float rotation, float z) { GL2 gl = ((PGraphicsOpenGL) g.g).gl; //Ok, we have pixels. int ind = getLUTLocus(rotation); float cX = cosLUT[ind]*c/2; float sX = sinLUT[ind]*c/2; float cY = cosLUT[ind]*d/2; float sY = sinLUT[ind]*d/2; int x, y; gl.glBegin(GL2.GL_QUADS); gl.glTexCoord2f(allTexCoords[0], allTexCoords[1]); x = -1; y = 1; gl.glVertex3f(cX*x-sY*y+a,sX*x+cY*y+b, z); // V3 gl.glTexCoord2f(allTexCoords[2], allTexCoords[3]); x = -1; y = -1; gl.glVertex3f(cX*x-sY*y+a,sX*x+cY*y+b, z); // V3 gl.glTexCoord2f(allTexCoords[4], allTexCoords[5]); x = 1; y = -1; gl.glVertex3f(cX*x-sY*y+a,sX*x+cY*y+b, z); // V3 gl.glTexCoord2f(allTexCoords[6], allTexCoords[7]); x = 1; y = 1; gl.glVertex3f(cX*x-sY*y+a,sX*x+cY*y+b, z); // V3 // g.image(got,-c/2,-d/2,c,d, r[0], r[1], r[2]+ r[0], r[3]+ r[1]) gl.glEnd(); } public void draw3DPolygonGL(float[][] corners) { GL2 gl = ((PGraphicsOpenGL) g.g).gl; gl.glBegin(GL2.GL_POLYGON); for (float[] corner : corners) { gl.glVertex3f(corner[0], corner[1], corner[2]); } gl.glEnd(); } /** * a,b is the CENTER of the rectangle c,d, are width/height rotation is * rotation */ public void drawGameGraphic(int[] r, PImage got, float a, float b, float c, float d, float rotation) { // Ok, we have pixels. g.translate(a, b); g.rotate(rotation); g .image(got, -c / 2, -d / 2, c, d, r[0], r[1], r[2] + r[0], r[3] + r[1]); g.rotate(-rotation); g.translate(-a, -b); } public class GameSpriteAnimator { public GameSpriteAnimator(GameSprite on) { this.on = on; } private GameSprite on; private boolean musicPlaying = false; private AudioPlayer currentMusic = null; private long now = -1; private int cFrame = -1; public boolean done = false; private GameSprite.GameGraphic currentFrame; /** * Draws the sprite in the current viewport. * * Returs true if the rendering is complete. */ public boolean draw() { if (!musicPlaying) { musicPlaying = true; if (on.sound != null && on.sound.filename.length() > 0) { currentMusic = FILE_SYSTEM.loadAudioFile(on.sound.filename, AUDIO); currentMusic.play(); } } advanceFrame(); try { drawGameGraphic(currentFrame); } catch (Throwable e) { // Do nothing. } return done; } public void advanceFrame() { if (now == -1) { now = System.nanoTime(); } long tdiff = System.nanoTime() - now; int potentialNext = cFrame + 1; try { if (potentialNext >= on.keyFrames.length) { if (on.loopAfter != -1) { potentialNext = 0; } else { done = true; return; } } long required = on.keyFrames[potentialNext]; required *= on.frameResolution; if (tdiff >= required) { cFrame = potentialNext; if (cFrame >= on.frame.length) { done = true; return; } currentFrame = on.frame[cFrame]; } } catch (Throwable e) { e.printStackTrace(); return; } } public void cleanup() { if (currentMusic != null) { currentMusic.pause(); currentMusic.close(); } } } public void dispose() { try { AUDIO.stop(); } catch (Throwable e) { e.printStackTrace(); } try { if (currentDialog != null) currentDialog.cleanup(); } catch (Throwable e) { e.printStackTrace(); } try { if (currentScreen != null) currentScreen.cleanup(); } catch (Throwable e) { e.printStackTrace(); } } /* * Tessellator callback implemenation with all the callback routines. YOu * could use GLUtesselatorCallBackAdapter instead. But */ public class tessellCallBack implements GLUtessellatorCallback { private GL2 gl; private GLU glu; private PImage texture; private PGraphicsOpenGL g; private int[] texData; double wAS, hAS; public tessellCallBack(GL2 gl, GLU glu, PImage texture, PGraphicsOpenGL g) { this.gl = gl; this.glu = glu; this.texture = texture; this.g = g; } public void begin(int type) { gl.glEnable(GL.GL_TEXTURE_2D); texData = g.bindTexture(texture); wAS = texture.width / (double) texData[0]; hAS = texture.height / (double) texData[1]; gl.glBegin(type); } public void end() { gl.glEnd(); gl.glDisable(GL.GL_TEXTURE_2D); } private boolean darken = false; public void setDarken(boolean darken) { this.darken = darken; } public void vertex(Object vertexData) { double[] pointer; if (vertexData instanceof double[]) { pointer = (double[]) vertexData; /* * if (pointer.length == 6) gl.glColor4d(1,1,1,pointer[5]); */ if (darken) { gl.glColor4d(.2f, .2f, .2f, 1f); } else { gl.glColor4d(1, 1, 1, 1f - pointer[5]); } gl.glTexCoord2f(PApplet.constrain((float) (pointer[0] * wAS), 0, 1), PApplet.constrain((float) (pointer[1] * hAS), 0, 1)); gl.glVertex3dv(pointer, 0); } } public void vertexData(Object vertexData, Object polygonData) { } /* * combineCallback is used to create a new vertex when edges intersect. * coordinate location is trivial to calculate, but weight[4] may be * used to average color, normal, or texture coordinate data. In this * program, color is weighted. */ public void combine(double[] coords, Object[] data, // float[] weight, Object[] outData) { double[] vertex = new double[6]; int i; vertex[0] = coords[0]; vertex[1] = coords[1]; vertex[2] = coords[2]; for (i = 3; i < 6/* 7OutOfBounds from C! */; i++) { if(data[0] !=null) vertex[i] = weight[0] * ((double[]) data[0])[i]; if (data[1]!=null) vertex[i] += weight[1] * ((double[]) data[1])[i]; if (data[2]!=null) vertex[i] += weight[2] * ((double[]) data[2])[i]; if (data[3]!=null) vertex[i] += weight[3] * ((double[]) data[3])[i]; } outData[0] = vertex; } public void combineData(double[] coords, Object[] data, // float[] weight, Object[] outData, Object polygonData) { } public void error(int errnum) { String estring; estring = glu.gluErrorString(errnum); System.err.println("Tessellation Error: " + estring); } public void beginData(int type, Object polygonData) { } public void endData(Object polygonData) { } public void edgeFlag(boolean boundaryEdge) { } public void edgeFlagData(boolean boundaryEdge, Object polygonData) { } public void errorData(int errnum, Object polygonData) { } }// tessellCallBack // SIN COS LOOKUP // declare arrays and params for storing sin/cos values public final float sinLUT[]; public final float cosLUT[]; // set table precision to radians private static final float SC_PRECISION = 1f / 360f * .13805f * 2; // caculate reciprocal for conversions private static final float SC_INV_PREC = 1 / SC_PRECISION; // compute required table length private static final int SC_PERIOD = (int) (PApplet.TWO_PI * SC_INV_PREC); // init sin/cos tables with values // should be called from setup() private final void initSinCosLookup() { for (int i = 0; i < SC_PERIOD; i++) { sinLUT[i] = (float) Math.sin(i * SC_PRECISION); cosLUT[i] = (float) Math.cos(i * SC_PRECISION); } } public final int getLUTLocus(float rad) { int locus = ((int) (rad * SC_INV_PREC)) & (SC_PERIOD-1); if (locus < 0) { locus += SC_PERIOD; } return locus; } public final float CosLUT(float rad) { return cosLUT[getLUTLocus(rad)]; } public final float SinLUT(float rad) { return sinLUT[getLUTLocus(rad)]; } }