Cleaned PropertyManager, refactored GameLoop to external package.

This commit is contained in:
ondra
2014-04-08 17:53:23 +02:00
parent 5e35bfe958
commit cc85d8a9d3
25 changed files with 1004 additions and 1156 deletions
@@ -46,7 +46,7 @@ public abstract class AppModule extends RootBusNode implements AppAccess {
@Override
public void shutdown()
public final void shutdown()
{
app.shutdown();
}
@@ -46,7 +46,7 @@ public abstract class AppSubModule extends BusNode implements AppAccess {
@Override
public void shutdown()
public final void shutdown()
{
app.shutdown();
}
@@ -9,6 +9,6 @@ public interface BusAccess {
/**
* @return event bus
*/
public abstract EventBus bus();
EventBus bus();
}
+3 -3
View File
@@ -18,7 +18,7 @@ import mightypork.gamecore.control.bus.clients.ToggleableClient;
*/
public abstract class BusNode implements BusAccess, DelegatingClient, ToggleableClient {
private BusAccess busAccess;
private final BusAccess busAccess;
private final Set<Object> clients = new LinkedHashSet<Object>();
private boolean listening = true;
@@ -45,7 +45,7 @@ public abstract class BusNode implements BusAccess, DelegatingClient, Toggleable
@Override
public boolean isListening()
public final boolean isListening()
{
return listening;
}
@@ -104,7 +104,7 @@ public abstract class BusNode implements BusAccess, DelegatingClient, Toggleable
@Override
public EventBus bus()
public final EventBus bus()
{
return busAccess.bus();
}
+38 -7
View File
@@ -6,19 +6,38 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import mightypork.gamecore.control.bus.events.MainLoopTaskRequest;
import mightypork.gamecore.control.bus.events.UpdateEvent;
import mightypork.gamecore.control.interf.NoImpl;
import mightypork.gamecore.control.timing.TimerDelta;
import mightypork.gamecore.gui.renderers.Renderable;
import mightypork.gamecore.gui.screens.ScreenRegistry;
/**
* Delta-timed game loop with task queue etc.
*
* @author MightyPork
*/
public abstract class GameLoop extends AppModule implements MainLoopTaskRequest.Listener {
private final Queue<Runnable> taskQueue = new ConcurrentLinkedQueue<Runnable>();
/** timer */
private TimerDelta timer;
private Renderable mainRenderable;
private boolean running = true;
public GameLoop(AppAccess app) {
/**
* @param app {@link AppAccess} instance
* @param rootRenderable main {@link Renderable}, typically a
* {@link ScreenRegistry}
*/
public GameLoop(AppAccess app, Renderable rootRenderable) {
super(app);
if (rootRenderable == null) {
throw new NullPointerException("Master renderable must not be null.");
}
mainRenderable = rootRenderable;
}
@@ -36,17 +55,29 @@ public abstract class GameLoop extends AppModule implements MainLoopTaskRequest.
r.run();
}
tick();
beforeRender();
mainRenderable.render();
afterRender();
disp().endFrame();
}
}
/**
* Called each frame, in rendering context.
*/
protected abstract void tick();
@NoImpl
protected void beforeRender()
{
//
}
@NoImpl
protected void afterRender()
{
//
}
@Override
@@ -12,8 +12,9 @@ import mightypork.gamecore.control.interf.Updateable;
* @author MightyPork
*/
// sending via queue would hog the bus
@ImmediateEvent
@UnloggedEvent
@ImmediateEvent
public class UpdateEvent implements Event<Updateable> {
private final double deltaTime;
@@ -0,0 +1,11 @@
package mightypork.gamecore.control.interf;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Documented
@Retention(RetentionPolicy.SOURCE)
public @interface NoImpl {
}
@@ -10,10 +10,8 @@ package mightypork.gamecore.control.timing;
public class FpsMeter {
private long frames = 0;
private long drops = 0;
private long lastTimeMillis = System.currentTimeMillis();
private long lastSecFPS = 0;
private long lastSecDrop = 0;
/**
@@ -32,31 +30,10 @@ public class FpsMeter {
{
if (System.currentTimeMillis() - lastTimeMillis > 1000) {
lastSecFPS = frames;
lastSecDrop = drops;
frames = 0;
drops = 0;
lastTimeMillis = System.currentTimeMillis();
long over = System.currentTimeMillis() - lastTimeMillis - 1000;
lastTimeMillis = System.currentTimeMillis() - over;
}
frames++;
}
/**
* Notification that some frames have been dropped
*
* @param dropped dropped frames
*/
public void drop(int dropped)
{
drops += dropped;
}
/**
* @return current second's dropped frames
*/
public long getDropped()
{
return lastSecDrop;
}
}
@@ -20,6 +20,11 @@ public class TextRenderer extends PluggableRenderer {
private Align align;
public TextRenderer(GLFont font, RGB color, Align align) {
this(font, "MISSINGNO", color, align);
}
public TextRenderer(GLFont font, String text, RGB color, Align align) {
this.font = new FontRenderer(font);
this.text = text;
@@ -52,8 +57,19 @@ public class TextRenderer extends PluggableRenderer {
}
public String getText()
{
return text;
}
@Override
public void render()
{
render(getText());
}
public void render(String text)
{
final double h = getRect().getHeight();
@@ -1,7 +1,6 @@
package mightypork.gamecore.gui.screens;
import static org.lwjgl.opengl.GL11.*;
import mightypork.gamecore.control.AppAccess;
import mightypork.gamecore.control.AppSubModule;
import mightypork.gamecore.control.bus.events.ScreenChangeEvent;
@@ -9,7 +8,7 @@ import mightypork.gamecore.gui.renderers.Renderable;
import mightypork.gamecore.input.KeyBinder;
import mightypork.gamecore.input.KeyBindingPool;
import mightypork.gamecore.input.KeyStroke;
import mightypork.gamecore.render.DisplaySystem;
import mightypork.gamecore.render.Render;
import mightypork.utils.math.constraints.RectConstraint;
import mightypork.utils.math.coord.Coord;
import mightypork.utils.math.coord.Rect;
@@ -148,7 +147,7 @@ public abstract class Screen extends AppSubModule implements Renderable, KeyBind
if (!isActive()) return;
if (needSetupViewport) {
DisplaySystem.setupOrtho();
Render.setupOrtho();
}
renderScreen();
@@ -3,16 +3,12 @@ package mightypork.gamecore.render;
import static org.lwjgl.opengl.GL11.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.imageio.ImageIO;
import mightypork.gamecore.control.AppAccess;
import mightypork.gamecore.control.RootBusNode;
import mightypork.gamecore.control.bus.events.ScreenChangeEvent;
import mightypork.gamecore.control.timing.FpsMeter;
import mightypork.utils.logging.Log;
import mightypork.utils.math.constraints.RectConstraint;
import mightypork.utils.math.coord.Coord;
@@ -29,6 +25,7 @@ public class DisplaySystem extends RootBusNode implements RectConstraint {
private DisplayMode windowDisplayMode;
private int targetFps;
public static boolean yAxisDown = true;
private FpsMeter fpsMeter;
public DisplaySystem(AppAccess app) {
@@ -58,13 +55,13 @@ public class DisplaySystem extends RootBusNode implements RectConstraint {
Display.setTitle(title);
Display.create();
fpsMeter = new FpsMeter();
if (fullscreen) {
switchFullscreen();
Display.update();
}
Render.init();
} catch (final LWJGLException e) {
throw new RuntimeException("Could not initialize screen", e);
}
@@ -106,13 +103,18 @@ public class DisplaySystem extends RootBusNode implements RectConstraint {
}
/**
* Take screenshot (expensive processing is done on-demand when screenshot
* is processed).
*
* @return screenshot object
*/
public static Screenshot takeScreenshot()
{
glReadBuffer(GL_FRONT);
final int width = Display.getDisplayMode().getWidth();
final int height = Display.getDisplayMode().getHeight();
final int bpp = 4; // Assuming a 32-bit display with a byte each for red,
// green, blue, and alpha.
final int bpp = 4;
final ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * bpp);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
@@ -177,6 +179,7 @@ public class DisplaySystem extends RootBusNode implements RectConstraint {
glLoadIdentity();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
fpsMeter.frame();
}
@@ -196,62 +199,11 @@ public class DisplaySystem extends RootBusNode implements RectConstraint {
return new Rect(getSize());
}
public static class Screenshot {
private int width;
private int height;
private int bpp;
private ByteBuffer bytes;
private BufferedImage image;
public Screenshot(int width, int height, int bpp, ByteBuffer buffer) {
this.width = width;
this.height = height;
this.bpp = bpp;
this.bytes = buffer;
}
public BufferedImage getImage()
{
if (image != null) return image;
image = new BufferedImage(this.width, this.height, BufferedImage.TYPE_INT_RGB);
// convert to a buffered image
for (int x = 0; x < this.width; x++) {
for (int y = 0; y < this.height; y++) {
final int i = (x + (this.width * y)) * this.bpp;
final int r = this.bytes.get(i) & 0xFF;
final int g = this.bytes.get(i + 1) & 0xFF;
final int b = this.bytes.get(i + 2) & 0xFF;
image.setRGB(x, this.height - (y + 1), (0xFF << 24) | (r << 16) | (g << 8) | b);
}
}
return image;
}
public void save(File file) throws IOException
{
ImageIO.write(getImage(), "PNG", file);
}
}
public static void setupOrtho()
/**
* @return current FPS
*/
public final long getFps()
{
// fix projection for changed size
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
final Coord s = getSize();
glViewport(0, 0, s.xi(), s.yi());
glOrtho(0, s.x, (yAxisDown ? 1 : -1) * s.y, 0, -1000, 1000);
// back to modelview
glMatrixMode(GL_MODELVIEW);
return fpsMeter.getFPS();
}
}
+33 -26
View File
@@ -30,32 +30,6 @@ public class Render {
private static final Coord AXIS_Y = new Coord(0, 1, 0);
private static final Coord AXIS_Z = new Coord(0, 0, 1);
public static void init()
{
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable(GL_LIGHTING);
glClearDepth(1f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_NORMALIZE);
glShadeModel(GL_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
/**
* Bind GL color
*
@@ -490,5 +464,38 @@ public class Render {
{
quadTextured(quad, txquad.uvs, txquad.tx, tint);
}
/**
* Setup Ortho projection for 2D graphics
*/
public static void setupOrtho()
{
// fix projection for changed size
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
final Coord s = DisplaySystem.getSize();
glViewport(0, 0, s.xi(), s.yi());
glOrtho(0, s.x, (DisplaySystem.yAxisDown ? 1 : -1) * s.y, 0, -1000, 1000);
// back to modelview
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable(GL_LIGHTING);
glClearDepth(1f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_NORMALIZE);
glShadeModel(GL_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
}
@@ -0,0 +1,60 @@
package mightypork.gamecore.render;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.imageio.ImageIO;
/**
* Screenshot object, can be used to extract image or write to file.<br>
* Screenshot, once taken, can be safely processed in separate thread.
*
* @author MightyPork
*/
public class Screenshot {
private int width;
private int height;
private int bpp;
private ByteBuffer bytes;
private BufferedImage image;
public Screenshot(int width, int height, int bpp, ByteBuffer buffer) {
this.width = width;
this.height = height;
this.bpp = bpp;
this.bytes = buffer;
}
public BufferedImage getImage()
{
if (image != null) return image;
image = new BufferedImage(this.width, this.height, BufferedImage.TYPE_INT_RGB);
// convert to a buffered image
for (int x = 0; x < this.width; x++) {
for (int y = 0; y < this.height; y++) {
final int i = (x + (this.width * y)) * this.bpp;
final int r = this.bytes.get(i) & 0xFF;
final int g = this.bytes.get(i + 1) & 0xFF;
final int b = this.bytes.get(i + 2) & 0xFF;
image.setRGB(x, this.height - (y + 1), (0xFF << 24) | (r << 16) | (g << 8) | b);
}
}
return image;
}
public void save(File file) throws IOException
{
ImageIO.write(getImage(), "PNG", file);
}
}