Cleaned PropertyManager, refactored GameLoop to external package.
This commit is contained in:
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user