From ec64ad21fc3c5b4720924ee747226bbbe7bc1f2e Mon Sep 17 00:00:00 2001 From: MightyPork Date: Wed, 23 Jul 2014 17:30:07 +0200 Subject: [PATCH] More refactoring towards backend system --- src/mightypork/gamecore/backend/Backend.java | 34 + .../gamecore/backend/BackendModule.java | 32 + .../gamecore/backend/lwjgl/AwtScreenshot.java | 85 +++ .../gamecore/backend/lwjgl/LwjglBackend.java | 37 + .../backend/lwjgl/LwjglRenderModule.java | 641 ++++++++++++++++++ .../backend/lwjgl/SlickLazyTexture.java | 142 ++++ src/mightypork/gamecore/core/modules/App.java | 85 +++ .../gamecore/core/modules/BaseApp.java | 10 +- .../gui/components/painters/ImagePainter.java | 4 +- .../gui/components/painters/QuadPainter.java | 34 +- .../gui/components/painters/TextPainter.java | 25 +- .../gamecore/gui/screens/Screen.java | 11 +- .../gamecore/render/DisplaySystem.java | 6 +- src/mightypork/gamecore/render/Grad.java | 31 + src/mightypork/gamecore/render/GradH.java | 17 + src/mightypork/gamecore/render/GradV.java | 17 + src/mightypork/gamecore/render/Render.java | 5 +- .../gamecore/render/RenderModule.java | 420 ++++++++++++ .../gamecore/render/Screenshot.java | 80 +-- .../gamecore/render/TaskTakeScreenshot.java | 14 +- src/mightypork/gamecore/resources/Res.java | 4 +- .../resources/fonts/FontRenderer.java | 10 +- .../fonts/impl/TextureBackedFont.java | 2 - .../{GLTexture.java => ITexture.java} | 2 +- .../resources/textures/LazyTexture.java | 118 +--- .../gamecore/resources/textures/QuadGrid.java | 4 +- .../resources/textures/TextureRegistry.java | 15 +- .../gamecore/resources/textures/TxQuad.java | 10 +- src/mightypork/rogue/RogueApp.java | 3 + src/mightypork/rogue/RogueResources.java | 4 +- 30 files changed, 1640 insertions(+), 262 deletions(-) create mode 100644 src/mightypork/gamecore/backend/Backend.java create mode 100644 src/mightypork/gamecore/backend/BackendModule.java create mode 100644 src/mightypork/gamecore/backend/lwjgl/AwtScreenshot.java create mode 100644 src/mightypork/gamecore/backend/lwjgl/LwjglBackend.java create mode 100644 src/mightypork/gamecore/backend/lwjgl/LwjglRenderModule.java create mode 100644 src/mightypork/gamecore/backend/lwjgl/SlickLazyTexture.java create mode 100644 src/mightypork/gamecore/core/modules/App.java create mode 100644 src/mightypork/gamecore/render/Grad.java create mode 100644 src/mightypork/gamecore/render/GradH.java create mode 100644 src/mightypork/gamecore/render/GradV.java create mode 100644 src/mightypork/gamecore/render/RenderModule.java rename src/mightypork/gamecore/resources/textures/{GLTexture.java => ITexture.java} (96%) diff --git a/src/mightypork/gamecore/backend/Backend.java b/src/mightypork/gamecore/backend/Backend.java new file mode 100644 index 0000000..e347dce --- /dev/null +++ b/src/mightypork/gamecore/backend/Backend.java @@ -0,0 +1,34 @@ +package mightypork.gamecore.backend; + + +import mightypork.gamecore.render.RenderModule; +import mightypork.utils.eventbus.BusAccess; +import mightypork.utils.eventbus.clients.RootBusNode; + + +/** + * Application backend interface (set of core modules) + * + * @author MightyPork + */ +public abstract class Backend extends RootBusNode { + + public Backend(BusAccess busAccess) { + super(busAccess); + } + + + /** + * Initialize backend modules, add them to event bus.
+ * Event bus must already be available in the BusAccess. + */ + public abstract void initialize(); + + + /** + * Get graphics module (renderer) + * + * @return graphics module + */ + public abstract RenderModule getRenderer(); +} diff --git a/src/mightypork/gamecore/backend/BackendModule.java b/src/mightypork/gamecore/backend/BackendModule.java new file mode 100644 index 0000000..f9d65e8 --- /dev/null +++ b/src/mightypork/gamecore/backend/BackendModule.java @@ -0,0 +1,32 @@ +package mightypork.gamecore.backend; + + +import mightypork.utils.annotations.DefaultImpl; +import mightypork.utils.eventbus.BusAccess; +import mightypork.utils.eventbus.clients.BusNode; +import mightypork.utils.interfaces.Destroyable; + + +/** + * Abstract application backend module. + * + * @author MightyPork + */ +public abstract class BackendModule extends BusNode implements Destroyable { + + /** + * Create a module with bus access + * + * @param busAccess + */ + public BackendModule(BusAccess busAccess) { + super(busAccess); + } + + @Override + @DefaultImpl + public void destroy() + { + } + +} diff --git a/src/mightypork/gamecore/backend/lwjgl/AwtScreenshot.java b/src/mightypork/gamecore/backend/lwjgl/AwtScreenshot.java new file mode 100644 index 0000000..0609da6 --- /dev/null +++ b/src/mightypork/gamecore/backend/lwjgl/AwtScreenshot.java @@ -0,0 +1,85 @@ +package mightypork.gamecore.backend.lwjgl; + + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; + +import javax.imageio.ImageIO; + +import mightypork.gamecore.render.Screenshot; + + +/** + * Screenshot object, can be used to extract image or write to file.
+ * Screenshot, once taken, can be safely processed in separate thread.
+ * Based on {@link BufferedImage} and {@link ImageIO}. + * + * @author Ondřej Hruška (MightyPork) + */ +public class AwtScreenshot implements Screenshot { + + private final int width; + private final int height; + private final int bpp; + private final ByteBuffer bytes; + private BufferedImage image; + + + /** + * @param width image width + * @param height image height + * @param bpp bits per pixel (typically 4) + * @param buffer + */ + public AwtScreenshot(int width, int height, int bpp, ByteBuffer buffer) + { + this.width = width; + this.height = height; + this.bpp = bpp; + this.bytes = buffer; + } + + + /** + * Extract to an image.
+ * Subsequent calls will use a cached value. + * + * @return image + */ + 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; + } + + + /** + * Save to a file.
+ * Cached value is used if any. + * + * @param file target file + * @throws IOException on error writing to file + */ + @Override + public void save(File file) throws IOException + { + file.getParentFile().mkdirs(); + ImageIO.write(getImage(), "PNG", file); + } +} diff --git a/src/mightypork/gamecore/backend/lwjgl/LwjglBackend.java b/src/mightypork/gamecore/backend/lwjgl/LwjglBackend.java new file mode 100644 index 0000000..cc00bb2 --- /dev/null +++ b/src/mightypork/gamecore/backend/lwjgl/LwjglBackend.java @@ -0,0 +1,37 @@ +package mightypork.gamecore.backend.lwjgl; + + +import mightypork.gamecore.backend.Backend; +import mightypork.gamecore.render.RenderModule; +import mightypork.utils.eventbus.BusAccess; + + +/** + * Game backend using LWJGL + * + * @author MightyPork + */ +public class LwjglBackend extends Backend { + + public LwjglBackend(BusAccess busAccess) { + super(busAccess); + } + + + private LwjglRenderModule renderer; + + + @Override + public void initialize() + { + addChildClient(renderer = new LwjglRenderModule(this)); + } + + + @Override + public RenderModule getRenderer() + { + return renderer; + } + +} diff --git a/src/mightypork/gamecore/backend/lwjgl/LwjglRenderModule.java b/src/mightypork/gamecore/backend/lwjgl/LwjglRenderModule.java new file mode 100644 index 0000000..7141cb1 --- /dev/null +++ b/src/mightypork/gamecore/backend/lwjgl/LwjglRenderModule.java @@ -0,0 +1,641 @@ +package mightypork.gamecore.backend.lwjgl; + + +import static org.lwjgl.opengl.GL11.*; + +import java.nio.ByteBuffer; +import java.util.Stack; + +import org.lwjgl.BufferUtils; +import org.lwjgl.LWJGLException; +import org.lwjgl.opengl.Display; +import org.lwjgl.opengl.DisplayMode; +import org.lwjgl.opengl.GL11; + +import mightypork.gamecore.render.Grad; +import mightypork.gamecore.render.RenderModule; +import mightypork.gamecore.render.Screenshot; +import mightypork.gamecore.render.events.DisplayReadyEvent; +import mightypork.gamecore.render.events.ViewportChangeEvent; +import mightypork.gamecore.resources.textures.ITexture; +import mightypork.gamecore.resources.textures.LazyTexture; +import mightypork.gamecore.resources.textures.TxQuad; +import mightypork.utils.eventbus.BusAccess; +import mightypork.utils.logging.Log; +import mightypork.utils.math.color.Color; +import mightypork.utils.math.color.pal.RGB; +import mightypork.utils.math.constraints.rect.Rect; +import mightypork.utils.math.constraints.rect.caching.RectDigest; +import mightypork.utils.math.constraints.vect.Vect; +import mightypork.utils.math.timing.FpsMeter; + + +/** + * LWJGL rendering module + * + * @author MightyPork + */ +public class LwjglRenderModule extends RenderModule { + + public LwjglRenderModule(BusAccess busAccess) { + super(busAccess); + } + + /** Currently binded color */ + private Color activeColor = null; + /** Currently binded color's alpha multiplier */ + private double activeColorAlpha = 1; + /** Stack of pushed colors */ + private Stack colorPushStack = new Stack<>(); + /** Currently binded texture */ + private ITexture activeTexture; + + /** Display mode used currently for the window */ + private DisplayMode windowDisplayMode; + /** FPS the user wants */ + private int targetFps; + /** FPS meter used for measuring actual FPS */ + private FpsMeter fpsMeter; + + /** Flag that at the end of frame, fullscreen should be toggled. */ + private boolean fullscreenToggleRequested; + /** Flag that at the end of frame, fullscreen should be set. */ + private boolean fullscreenSetRequested; + /** State to which fullscreen should be set. */ + private boolean fullscreenSetState; + + /** Current screen size */ + private static final Vect screenSize = new Vect() { + + @Override + public double y() + { + return Display.getHeight(); + } + + + @Override + public double x() + { + return Display.getWidth(); + } + }; + + /** Current screen rectangle */ + private static final Rect rect = Rect.make(screenSize); + + + @Override + public void setColor(Color color) + { + setColor(color, 1); + } + + + @Override + public void setColor(Color color, double alpha) + { + if (color == null) color = RGB.WHITE; + + // color components can change over time - must use equals() + if (activeColorAlpha == alpha && color.equals(activeColor)) return; + + activeColor = color; + activeColorAlpha = alpha; + GL11.glColor4d(color.r(), color.g(), color.b(), alpha * color.a()); + } + + + @Override + public void translate(double x, double y) + { + glTranslated(x, y, 0); + } + + + @Override + public void translate(double x, double y, double z) + { + glTranslated(x, y, z); + } + + + @Override + public void translate(Vect offset) + { + glTranslated(offset.x(), offset.y(), offset.z()); + } + + + @Override + public void translateXY(Vect offset) + { + glTranslated(offset.x(), offset.y(), 0); + } + + + @Override + public void scale(double x, double y) + { + glScaled(x, y, 0); + } + + + @Override + public void scale(double x, double y, double z) + { + glScaled(x, y, z); + } + + + @Override + public void scale(Vect scale) + { + glScaled(scale.x(), scale.y(), scale.z()); + } + + + @Override + public void scaleXY(double scale) + { + glScaled(scale, scale, 1); + } + + + @Override + public void scaleX(double scale) + { + glScaled(scale, 1, 1); + } + + + @Override + public void scaleY(double scale) + { + glScaled(1, scale, 1); + } + + + @Override + public void scaleZ(double scale) + { + glScaled(1, 1, scale); + } + + + @Override + public void rotateX(double angle) + { + rotate(angle, AXIS_X); + } + + + @Override + public void rotateY(double angle) + { + rotate(angle, AXIS_Y); + } + + + @Override + public void rotateZ(double angle) + { + rotate(angle, AXIS_Z); + } + + + @Override + public void rotate(double angle, Vect axis) + { + final Vect vec = axis.norm(1); + glRotated(angle, vec.x(), vec.y(), vec.z()); + } + + + @Override + public void pushState() + { + GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); + GL11.glPushClientAttrib(GL11.GL_ALL_CLIENT_ATTRIB_BITS); + GL11.glPushMatrix(); + } + + + @Override + public void popState() + { + GL11.glPopMatrix(); + GL11.glPopClientAttrib(); + GL11.glPopAttrib(); + } + + + @Override + public void pushGeometry() + { + GL11.glPushMatrix(); + } + + + @Override + public void popGeometry() + { + GL11.glPopMatrix(); + } + + + @Override + public void pushColor() + { + colorPushStack.push(activeColor); + } + + + @Override + public void popColor() + { + setColor(colorPushStack.pop()); + } + + + @Override + public void quad(Rect rect) + { + final RectDigest q = rect.digest(); + + // disable texture + if (activeTexture != null) { + activeTexture = null; + glDisable(GL_TEXTURE_2D); + } + + // quad + glBegin(GL_QUADS); + glVertex2d(q.left, q.bottom); + glVertex2d(q.right, q.bottom); + glVertex2d(q.right, q.top); + glVertex2d(q.left, q.top); + glEnd(); + } + + + @Override + public void quad(Rect rect, Color color) + { + setColor(color); + quad(rect); + } + + + @Override + public void quad(Rect rect, Grad grad) + { + final RectDigest r = rect.digest(); + + // disable texture + if (activeTexture != null) { + activeTexture = null; + glDisable(GL_TEXTURE_2D); + } + + // quad + glBegin(GL_QUADS); + setColor(grad.leftBottom); + glVertex2d(r.left, r.bottom); + + setColor(grad.rightBottom); + glVertex2d(r.right, r.bottom); + + setColor(grad.rightTop); + glVertex2d(r.right, r.top); + + setColor(grad.leftTop); + glVertex2d(r.left, r.top); + glEnd(); + } + + + @Override + public void quad(Rect rect, TxQuad txquad) + { + quad(rect, txquad, RGB.WHITE); + } + + + @Override + public void quad(Rect rect, TxQuad txquad, Color color) + { + // texture is loaded uniquely -> can compare with == + if (activeTexture != txquad.tx) { + glEnable(GL_TEXTURE_2D); + activeTexture = txquad.tx; + activeTexture.bind(); + } + + glBegin(GL_QUADS); + setColor(color); + + final RectDigest q = rect.digest(); + final RectDigest u = txquad.uvs.digest(); + + final double offs = 0.0001;// hack to avoid white stitching + + double tL = u.left + offs, tR = u.right - offs, tT = u.top + offs, tB = u.bottom - offs; + + // handle flip + if (txquad.isFlippedY()) { + final double swap = tT; + tT = tB; + tB = swap; + } + + if (txquad.isFlippedX()) { + final double swap = tL; + tL = tR; + tR = swap; + } + + final double w = txquad.tx.getWidth01(); + final double h = txquad.tx.getHeight01(); + + // quad with texture + glTexCoord2d(tL * w, tB * h); + glVertex2d(q.left, q.bottom); + + glTexCoord2d(tR * w, tB * h); + glVertex2d(q.right, q.bottom); + + glTexCoord2d(tR * w, tT * h); + glVertex2d(q.right, q.top); + + glTexCoord2d(tL * w, tT * h); + glVertex2d(q.left, q.top); + + glEnd(); + } + + + @Override + public void setupProjection(Vect screenSize) + { + // fix projection for changed size + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glViewport(0, 0, screenSize.xi(), screenSize.yi()); + glOrtho(0, screenSize.xi(), screenSize.yi(), 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); + } + + + @Override + public LazyTexture getLazyTexture(String path) + { + return new SlickLazyTexture(path); + } + + + @Override + public void destroy() + { + Display.destroy(); + } + + + @Override + public void setTargetFps(int fps) + { + this.targetFps = fps; + } + + + @Override + public void createDisplay() + { + if (Display.isCreated()) throw new IllegalStateException("Display already created."); + try { + Display.create(); + + fpsMeter = new FpsMeter(); + + if (fullscreenSetRequested && fullscreenSetState) { + doToggleFullscreen(); + Display.update(); + fullscreenSetRequested = false; + } + + getEventBus().send(new DisplayReadyEvent()); + + } catch (final Exception e) { + throw new RuntimeException("Could not initialize display.", e); + } + } + + + @Override + public void setFullscreen(boolean fs) + { + fullscreenSetRequested = true; + fullscreenSetState = fs; + } + + + @Override + public void switchFullscreen() + { + fullscreenToggleRequested = true; + } + + + @Override + public boolean isFullscreen() + { + return Display.isFullscreen(); + } + + + private void doToggleFullscreen() + { + doSetFullscreen(!Display.isFullscreen()); + } + + + private void doSetFullscreen(boolean fs) + { + try { + + if (Display.isFullscreen() == fs) return; // no work + + if (fs) { + Log.f3("Entering fullscreen."); + // save window resize + windowDisplayMode = new DisplayMode(Display.getWidth(), Display.getHeight()); + + Display.setDisplayMode(Display.getDesktopDisplayMode()); + Display.setFullscreen(true); + Display.update(); + } else { + Log.f3("Leaving fullscreen."); + Display.setDisplayMode(windowDisplayMode); + Display.update(); + } + + getEventBus().send(new ViewportChangeEvent(getSize())); + + } catch (final Throwable t) { + Log.e("Failed to change fullscreen mode.", t); + try { + Display.setDisplayMode(windowDisplayMode); + Display.update(); + } catch (final Throwable t1) { + throw new RuntimeException("Failed to revert failed fullscreen toggle.", t1); + } + } + } + + + @Override + public Screenshot takeScreenshot() + { + glReadBuffer(GL_FRONT); + final int width = Display.getWidth(); + final int height = Display.getHeight(); + final int bpp = 4; + final ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * bpp); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + + final AwtScreenshot sc = new AwtScreenshot(width, height, bpp, buffer); + + return sc; + } + + + @Override + public boolean isCloseRequested() + { + return Display.isCloseRequested(); + } + + + @Override + public void beginFrame() + { + // handle resize + if (Display.wasResized()) { + getEventBus().send(new ViewportChangeEvent(getSize())); + } + + if (fullscreenToggleRequested) { + fullscreenToggleRequested = false; + doToggleFullscreen(); + } + + if (fullscreenSetRequested) { + fullscreenSetRequested = false; + doSetFullscreen(fullscreenSetState); + } + + glLoadIdentity(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + fpsMeter.frame(); + } + + + @Override + public void endFrame() + { + Display.update(false); // don't poll input devices + Display.sync(targetFps); + } + + + @Override + public void setSize(int width, int height) + { + try { + Display.setDisplayMode(windowDisplayMode = new DisplayMode(width, height)); + } catch (LWJGLException e) { + throw new RuntimeException(e); + } + } + + + @Override + public void setTitle(String title) + { + Display.setTitle(title); + } + + + @Override + public void setVSync(boolean vsync) + { + Display.setVSyncEnabled(vsync); + } + + + @Override + public void setResizable(boolean resizable) + { + Display.setResizable(resizable); + } + + + @Override + public Rect getRect() + { + return rect; + } + + + @Override + public long getFps() + { + return fpsMeter.getFPS(); + } + + + @Override + public Vect getCenter() + { + return rect.center(); + } + + + @Override + public int getWidth() + { + return Display.getWidth(); + } + + + @Override + public int getHeight() + { + return Display.getHeight(); + } + + + @Override + public Vect getSize() + { + return screenSize; + } + +} diff --git a/src/mightypork/gamecore/backend/lwjgl/SlickLazyTexture.java b/src/mightypork/gamecore/backend/lwjgl/SlickLazyTexture.java new file mode 100644 index 0000000..90e4203 --- /dev/null +++ b/src/mightypork/gamecore/backend/lwjgl/SlickLazyTexture.java @@ -0,0 +1,142 @@ +package mightypork.gamecore.backend.lwjgl; + + +import java.io.IOException; + +import mightypork.gamecore.resources.TextureBasedResource; +import mightypork.gamecore.resources.textures.*; +import mightypork.utils.annotations.Alias; +import mightypork.utils.files.FileUtils; +import mightypork.utils.logging.Log; +import org.lwjgl.opengl.GL11; +import org.newdawn.slick.opengl.Texture; +import org.newdawn.slick.opengl.TextureLoader; + + +/** + * Deferred texture + * + * @author Ondřej Hruška (MightyPork) + */ +@Alias(name = "Texture") +@TextureBasedResource +public class SlickLazyTexture extends LazyTexture { + + private org.newdawn.slick.opengl.Texture backingTexture; + private boolean alpha; + private boolean alphal; + + /** + * @param resourcePath resource path + */ + public SlickLazyTexture(String resourcePath) { + super(resourcePath); + } + + @Override + protected synchronized void loadResource(String path) + { + try { + final String ext = FileUtils.getExtension(path).toUpperCase(); + + final Texture texture = TextureLoader.getTexture(ext, FileUtils.getResource(path), false, filter.num); + + if (texture == null) { + Log.w("Texture " + path + " could not be loaded."); + } + + backingTexture = texture; + + } catch (final IOException e) { + Log.e("Loading of texture " + path + " failed.", e); + throw new RuntimeException("Could not load texture " + path + ".", e); + } + } + + + @Override + public boolean hasAlpha() + { + if (!ensureLoaded()) return false; + + if (!alphal) { + alphal = true; + alpha = backingTexture.hasAlpha(); + } + + return alpha; + } + + + @Override + public void bind() + { + if (!ensureLoaded()) return; + + //GL11.glEnable(GL11.GL_TEXTURE_2D); + + GL11.glBindTexture(GL11.GL_TEXTURE_2D, getTextureID()); + + GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE); + + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, wrap.num); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, wrap.num); + + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, filter.num); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, filter.num); + } + + + @Override + public int getImageHeight() + { + if (!ensureLoaded()) return 0; + + return backingTexture.getImageHeight(); + } + + + @Override + public int getImageWidth() + { + if (!ensureLoaded()) return 0; + + return backingTexture.getImageWidth(); + } + + + @Override + public float getHeight01() + { + if (!ensureLoaded()) return 0; + + return backingTexture.getHeight(); + } + + + @Override + public float getWidth01() + { + if (!ensureLoaded()) return 0; + + return backingTexture.getWidth(); + } + + + @Override + public void destroy() + { + if (!isLoaded()) return; + + backingTexture.release(); + } + + + @Override + public int getTextureID() + { + if (!ensureLoaded()) return -1; + + return backingTexture.getTextureID(); + } +} diff --git a/src/mightypork/gamecore/core/modules/App.java b/src/mightypork/gamecore/core/modules/App.java new file mode 100644 index 0000000..3a99c17 --- /dev/null +++ b/src/mightypork/gamecore/core/modules/App.java @@ -0,0 +1,85 @@ +package mightypork.gamecore.core.modules; + + +import mightypork.gamecore.backend.Backend; +import mightypork.gamecore.render.RenderModule; + + +/** + * Game base class & static subsystem access + * + * @author MightyPork + */ +public class App { + + private static App runningInstance; + private static Backend backend; + + + public App() { + if (App.isInitialized()) throw new IllegalStateException("App already initialized"); + + // store current instance in static field + App.runningInstance = this; + } + + + /** + * Create app with given backend. + * + * @param backend backend to use + */ + public void setBackend(Backend backend) + { + // store used backend in static field + App.backend = backend; + + // initialize the backend + App.backend.initialize(); + } + + + /** + * Throw error if app is not initialized + */ + protected static void assertInitialized() + { + if (!App.isInitialized()) throw new IllegalStateException("App is not initialized."); + if (backend == null) throw new IllegalStateException("No backend set!"); + } + + + /** + * Check whether the app is initialized (backend assigned). + * + * @return is initialized + */ + public static boolean isInitialized() + { + return runningInstance != null; + } + + + /** + * Get current backend + * + * @return the backend + */ + public static Backend getBackend() + { + assertInitialized(); + return backend; + } + + + /** + * Get renderer instance from the backend + * + * @return backend + */ + public static RenderModule gfx() + { + assertInitialized(); + return backend.getRenderer(); + } +} diff --git a/src/mightypork/gamecore/core/modules/BaseApp.java b/src/mightypork/gamecore/core/modules/BaseApp.java index c4128f6..bf51e46 100644 --- a/src/mightypork/gamecore/core/modules/BaseApp.java +++ b/src/mightypork/gamecore/core/modules/BaseApp.java @@ -10,6 +10,7 @@ import java.util.logging.Level; import javax.swing.JOptionPane; +import mightypork.gamecore.backend.Backend; import mightypork.gamecore.core.WorkDir; import mightypork.gamecore.core.WorkDir.RouteSetup; import mightypork.gamecore.core.config.Config; @@ -45,7 +46,7 @@ import mightypork.utils.math.algo.Move; * * @author Ondřej Hruška (MightyPork) */ -public abstract class BaseApp implements AppAccess, UncaughtExceptionHandler { +public abstract class BaseApp extends App implements AppAccess, UncaughtExceptionHandler { /** * Init options holder class @@ -176,8 +177,8 @@ public abstract class BaseApp implements AppAccess, UncaughtExceptionHandler { } - public BaseApp(File workdir, boolean singleInstance) - { + public BaseApp(File workdir, boolean singleInstance) { + WorkDir.init(workdir); opt.sigleInstance = singleInstance; @@ -240,7 +241,6 @@ public abstract class BaseApp implements AppAccess, UncaughtExceptionHandler { org.newdawn.slick.util.Log.setLogSystem(new SlickLogRedirector(log)); writeLogHeader(); - Log.i("=== Starting initialization sequence ==="); // pre-init hook @@ -367,7 +367,6 @@ public abstract class BaseApp implements AppAccess, UncaughtExceptionHandler { }); - Ion.registerIndirect(254, new IonizerBinary() { @Override @@ -478,7 +477,6 @@ public abstract class BaseApp implements AppAccess, UncaughtExceptionHandler { * Triggered when lock cannot be obtained.
* App should terminate gracefully. */ - protected void onLockError() { Log.e("Could not obtain lock file.\nOnly one instance can run at a time."); diff --git a/src/mightypork/gamecore/gui/components/painters/ImagePainter.java b/src/mightypork/gamecore/gui/components/painters/ImagePainter.java index 2dd558f..c4c87a4 100644 --- a/src/mightypork/gamecore/gui/components/painters/ImagePainter.java +++ b/src/mightypork/gamecore/gui/components/painters/ImagePainter.java @@ -1,9 +1,9 @@ package mightypork.gamecore.gui.components.painters; +import mightypork.gamecore.core.modules.App; import mightypork.gamecore.gui.components.BaseComponent; import mightypork.gamecore.gui.components.DynamicWidthComponent; -import mightypork.gamecore.render.Render; import mightypork.gamecore.resources.textures.TxQuad; @@ -29,7 +29,7 @@ public class ImagePainter extends BaseComponent implements DynamicWidthComponent @Override public void renderComponent() { - Render.quadTextured(this, txQuad); + App.gfx().quad(this, txQuad); } diff --git a/src/mightypork/gamecore/gui/components/painters/QuadPainter.java b/src/mightypork/gamecore/gui/components/painters/QuadPainter.java index 1631ad7..7af47b9 100644 --- a/src/mightypork/gamecore/gui/components/painters/QuadPainter.java +++ b/src/mightypork/gamecore/gui/components/painters/QuadPainter.java @@ -1,8 +1,9 @@ package mightypork.gamecore.gui.components.painters; +import mightypork.gamecore.core.modules.App; import mightypork.gamecore.gui.components.BaseComponent; -import mightypork.gamecore.render.Render; +import mightypork.gamecore.render.Grad; import mightypork.utils.annotations.FactoryMethod; import mightypork.utils.math.color.Color; @@ -27,10 +28,7 @@ public class QuadPainter extends BaseComponent { return new QuadPainter(colorTop, colorTop, colorBottom, colorBottom); } - private final Color colorHMinVMin; - private final Color colorHMaxVMin; - private final Color colorHMaxVMax; - private final Color colorHMinVMax; + private final Grad grad; /** @@ -38,35 +36,27 @@ public class QuadPainter extends BaseComponent { * * @param color */ - public QuadPainter(Color color) - { - this.colorHMinVMin = color; - this.colorHMaxVMin = color; - this.colorHMaxVMax = color; - this.colorHMinVMax = color; + public QuadPainter(Color color) { + this.grad = new Grad(color, color, color, color); } /** * Painter with coloured vertices. * - * @param colorHMinVMin - * @param colorHMaxVMin - * @param colorHMaxVMax - * @param colorHMinVMax + * @param leftTop + * @param rightTop + * @param leftBottom + * @param rightBottom */ - public QuadPainter(Color colorHMinVMin, Color colorHMaxVMin, Color colorHMaxVMax, Color colorHMinVMax) - { - this.colorHMinVMin = colorHMinVMin; - this.colorHMaxVMin = colorHMaxVMin; - this.colorHMaxVMax = colorHMaxVMax; - this.colorHMinVMax = colorHMinVMax; + public QuadPainter(Color leftTop, Color rightTop, Color leftBottom, Color rightBottom) { + this.grad = new Grad(leftTop, rightTop, rightBottom, leftBottom); } @Override public void renderComponent() { - Render.quadColor(getRect(), colorHMinVMin, colorHMaxVMin, colorHMaxVMax, colorHMinVMax); + App.gfx().quad(getRect(), grad); } } diff --git a/src/mightypork/gamecore/gui/components/painters/TextPainter.java b/src/mightypork/gamecore/gui/components/painters/TextPainter.java index d29bb31..58d7a0d 100644 --- a/src/mightypork/gamecore/gui/components/painters/TextPainter.java +++ b/src/mightypork/gamecore/gui/components/painters/TextPainter.java @@ -1,10 +1,10 @@ package mightypork.gamecore.gui.components.painters; +import mightypork.gamecore.core.modules.App; import mightypork.gamecore.gui.AlignX; import mightypork.gamecore.gui.components.BaseComponent; import mightypork.gamecore.gui.components.DynamicWidthComponent; -import mightypork.gamecore.render.Render; import mightypork.gamecore.resources.fonts.FontRenderer; import mightypork.gamecore.resources.fonts.GLFont; import mightypork.utils.math.color.Color; @@ -39,38 +39,32 @@ public class TextPainter extends BaseComponent implements DynamicWidthComponent /** * @param font font to use */ - public TextPainter(GLFont font) - { + public TextPainter(GLFont font) { this(font, AlignX.LEFT, RGB.WHITE); } - public TextPainter(GLFont font, Color color, String text) - { + public TextPainter(GLFont font, Color color, String text) { this(font, AlignX.LEFT, color, new StringWrapper(text)); } - public TextPainter(GLFont font, Color color, StringProvider text) - { + public TextPainter(GLFont font, Color color, StringProvider text) { this(font, AlignX.LEFT, color, text); } - public TextPainter(GLFont font, Color color) - { + public TextPainter(GLFont font, Color color) { this(font, AlignX.LEFT, color, (StringProvider) null); } - public TextPainter(GLFont font, AlignX align, Color color, String text) - { + public TextPainter(GLFont font, AlignX align, Color color, String text) { this(font, align, color, new StringWrapper(text)); } - public TextPainter(GLFont font, AlignX align, Color color, StringProvider text) - { + public TextPainter(GLFont font, AlignX align, Color color, StringProvider text) { this.font = new FontRenderer(font); this.color = color; this.align = align; @@ -78,8 +72,7 @@ public class TextPainter extends BaseComponent implements DynamicWidthComponent } - public TextPainter(GLFont font, AlignX align, Color color) - { + public TextPainter(GLFont font, AlignX align, Color color) { this(font, align, color, (StringProvider) null); } @@ -102,7 +95,7 @@ public class TextPainter extends BaseComponent implements DynamicWidthComponent final Rect r = (shadow ? rect.move(shadowOffset.neg()) : rect).round(); font.draw(str, r, align, color); - if (DEBUG_FONT_RENDER) Render.quadColor(r, RGB.PINK.withAlpha(0.4)); + if (DEBUG_FONT_RENDER) App.gfx().quad(r, RGB.PINK.withAlpha(0.4)); } diff --git a/src/mightypork/gamecore/gui/screens/Screen.java b/src/mightypork/gamecore/gui/screens/Screen.java index 7a8f4f0..dd5da90 100644 --- a/src/mightypork/gamecore/gui/screens/Screen.java +++ b/src/mightypork/gamecore/gui/screens/Screen.java @@ -1,6 +1,7 @@ package mightypork.gamecore.gui.screens; +import mightypork.gamecore.core.modules.App; import mightypork.gamecore.core.modules.AppAccess; import mightypork.gamecore.core.modules.AppSubModule; import mightypork.gamecore.gui.events.LayoutChangeEvent; @@ -10,7 +11,6 @@ import mightypork.gamecore.input.KeyBindingPool; import mightypork.gamecore.input.KeyStroke; import mightypork.gamecore.input.KeyStroke.Edge; import mightypork.gamecore.render.DisplaySystem; -import mightypork.gamecore.render.Render; import mightypork.gamecore.render.Renderable; import mightypork.utils.annotations.DefaultImpl; import mightypork.utils.math.constraints.rect.Rect; @@ -33,8 +33,7 @@ public abstract class Screen extends AppSubModule implements Renderable, RectBou /** * @param app app access */ - public Screen(AppAccess app) - { + public Screen(AppAccess app) { super(app); // disable events initially @@ -123,14 +122,14 @@ public abstract class Screen extends AppSubModule implements Renderable, RectBou if (!isActive()) return; if (needSetupViewport) { - Render.setupOrtho(DisplaySystem.getSize()); + App.gfx().setupProjection(DisplaySystem.getSize()); } - Render.pushState(); + App.gfx().pushState(); renderScreen(); - Render.popState(); + App.gfx().popState(); } diff --git a/src/mightypork/gamecore/render/DisplaySystem.java b/src/mightypork/gamecore/render/DisplaySystem.java index 713a468..53c6e5f 100644 --- a/src/mightypork/gamecore/render/DisplaySystem.java +++ b/src/mightypork/gamecore/render/DisplaySystem.java @@ -5,6 +5,7 @@ import static org.lwjgl.opengl.GL11.*; import java.nio.ByteBuffer; +import mightypork.gamecore.backend.lwjgl.AwtScreenshot; import mightypork.gamecore.core.modules.AppAccess; import mightypork.gamecore.core.modules.AppModule; import mightypork.gamecore.render.events.DisplayReadyEvent; @@ -26,6 +27,7 @@ import org.lwjgl.opengl.DisplayMode; * * @author Ondřej Hruška (MightyPork) */ +@Deprecated public class DisplaySystem extends AppModule implements RectBound { private DisplayMode windowDisplayMode; @@ -161,7 +163,7 @@ public class DisplaySystem extends AppModule implements RectBound { * * @return screenshot object */ - public static Screenshot prepareScreenshot() + public static AwtScreenshot prepareScreenshot() { glReadBuffer(GL_FRONT); final int width = Display.getWidth(); @@ -170,7 +172,7 @@ public class DisplaySystem extends AppModule implements RectBound { final ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * bpp); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer); - final Screenshot sc = new Screenshot(width, height, bpp, buffer); + final AwtScreenshot sc = new AwtScreenshot(width, height, bpp, buffer); return sc; } diff --git a/src/mightypork/gamecore/render/Grad.java b/src/mightypork/gamecore/render/Grad.java new file mode 100644 index 0000000..1356ca6 --- /dev/null +++ b/src/mightypork/gamecore/render/Grad.java @@ -0,0 +1,31 @@ +package mightypork.gamecore.render; + + +import mightypork.utils.math.color.Color; + + +/** + * Linear gradient (each vertex can have different color) + * + * @author MightyPork + */ +public class Grad { + + public final Color leftTop, rightTop, rightBottom, leftBottom; + + + /** + * Create a gradient + * + * @param leftTop left top color + * @param rightTop right top color + * @param rightBottom right bottom color + * @param leftBottom left bottom color + */ + public Grad(Color leftTop, Color rightTop, Color rightBottom, Color leftBottom) { + this.leftTop = leftTop; + this.rightTop = rightTop; + this.rightBottom = rightBottom; + this.leftBottom = leftBottom; + } +} diff --git a/src/mightypork/gamecore/render/GradH.java b/src/mightypork/gamecore/render/GradH.java new file mode 100644 index 0000000..3938582 --- /dev/null +++ b/src/mightypork/gamecore/render/GradH.java @@ -0,0 +1,17 @@ +package mightypork.gamecore.render; + + +import mightypork.utils.math.color.Color; + + +/** + * Linear horizontal gradient + * + * @author MightyPork + */ +public class GradH extends Grad { + + public GradH(Color left, Color right) { + super(left, right, right, left); + } +} diff --git a/src/mightypork/gamecore/render/GradV.java b/src/mightypork/gamecore/render/GradV.java new file mode 100644 index 0000000..40db53f --- /dev/null +++ b/src/mightypork/gamecore/render/GradV.java @@ -0,0 +1,17 @@ +package mightypork.gamecore.render; + + +import mightypork.utils.math.color.Color; + + +/** + * Linear vertical gradient + * + * @author MightyPork + */ +public class GradV extends Grad { + + public GradV(Color top, Color bottom) { + super(top, top, bottom, bottom); + } +} diff --git a/src/mightypork/gamecore/render/Render.java b/src/mightypork/gamecore/render/Render.java index 5f0146f..ca4d781 100644 --- a/src/mightypork/gamecore/render/Render.java +++ b/src/mightypork/gamecore/render/Render.java @@ -6,7 +6,7 @@ import static org.lwjgl.opengl.GL11.*; import java.io.IOException; import mightypork.gamecore.resources.textures.FilterMode; -import mightypork.gamecore.resources.textures.GLTexture; +import mightypork.gamecore.resources.textures.ITexture; import mightypork.gamecore.resources.textures.TxQuad; import mightypork.utils.files.FileUtils; import mightypork.utils.logging.Log; @@ -27,6 +27,7 @@ import org.newdawn.slick.opengl.TextureLoader; * * @author Ondřej Hruška (MightyPork) */ +@Deprecated public class Render { public static final VectConst AXIS_X = Vect.make(1, 0, 0); @@ -525,7 +526,7 @@ public class Render { } - public static void enterBatchTexturedQuadMode(GLTexture texture) + public static void enterBatchTexturedQuadMode(ITexture texture) { texture.bind(); glBegin(GL11.GL_QUADS); diff --git a/src/mightypork/gamecore/render/RenderModule.java b/src/mightypork/gamecore/render/RenderModule.java new file mode 100644 index 0000000..e6dcf28 --- /dev/null +++ b/src/mightypork/gamecore/render/RenderModule.java @@ -0,0 +1,420 @@ +package mightypork.gamecore.render; + + +import mightypork.gamecore.backend.BackendModule; +import mightypork.gamecore.resources.textures.LazyTexture; +import mightypork.gamecore.resources.textures.TxQuad; +import mightypork.utils.eventbus.BusAccess; +import mightypork.utils.math.color.Color; +import mightypork.utils.math.constraints.rect.Rect; +import mightypork.utils.math.constraints.vect.Vect; +import mightypork.utils.math.constraints.vect.VectConst; +import mightypork.utils.math.timing.FpsMeter; + + +/** + * Render and display backend module.
+ * This module takes care of setting and getting screen size and parameters, + * drawing on screen and timing render frames. + * + * @author MightyPork + */ +public abstract class RenderModule extends BackendModule { + + public RenderModule(BusAccess busAccess) { + super(busAccess); + } + + protected static final VectConst AXIS_X = Vect.make(1, 0, 0); + protected static final VectConst AXIS_Y = Vect.make(0, 1, 0); + protected static final VectConst AXIS_Z = Vect.make(0, 0, 1); + + + /** + * Set drawing color + * + * @param color color + */ + public abstract void setColor(Color color); + + + /** + * Set drawing color, adjust alpha + * + * @param color color + * @param alpha alpha multiplier + */ + public abstract void setColor(Color color, double alpha); + + + /** + * Translate by x, y + * + * @param x x offset + * @param y y offset + */ + public abstract void translate(double x, double y); + + + /** + * Translate by x, y, z + * + * @param x x offset + * @param y y offset + * @param z z offset + */ + public abstract void translate(double x, double y, double z); + + + /** + * Translate by offset vector + * + * @param offset offset coordinate + */ + public abstract void translate(Vect offset); + + + /** + * Translate by offset vector, ignore Z + * + * @param offset offset coordinate + */ + public abstract void translateXY(Vect offset); + + + /** + * Set scale for translations and coordinates + * + * @param x x scale + * @param y y scale + */ + public abstract void scale(double x, double y); + + + /** + * Set scale for translations and coordinates + * + * @param x x scale + * @param y y scale + * @param z z scale + */ + public abstract void scale(double x, double y, double z); + + + /** + * Set scale for translations and coordinates + * + * @param scale vector + */ + public abstract void scale(Vect scale); + + + /** + * Set scale for translations and coordinates (same value for X and Y scale) + * + * @param scale scaling factor + */ + public abstract void scaleXY(double scale); + + + /** + * Set X scale for translations and coordinates + * + * @param scale scaling factor + */ + public abstract void scaleX(double scale); + + + /** + * Set Y scale for translations and coordinates + * + * @param scale scaling factor + */ + public abstract void scaleY(double scale); + + + /** + * Set Z scale for translations and coordinates + * + * @param scale scaling factor + */ + public abstract void scaleZ(double scale); + + + /** + * Rotate coordinate system around X axis + * + * @param angle rotation (in degrees) + */ + public abstract void rotateX(double angle); + + + /** + * Rotate coordinate system around Y axis + * + * @param angle rotation (in degrees) + */ + public abstract void rotateY(double angle); + + + /** + * Rotate coordinate system around Z axis + * + * @param angle rotation (in degrees) + */ + public abstract void rotateZ(double angle); + + + /** + * Rotate coordinate system around given axis + * + * @param angle rotation angle + * @param axis rotation axis (unit vector) + */ + public abstract void rotate(double angle, Vect axis); + + + /** + * Store render state on stack
+ * This includes pushGeometry and pushColor. + */ + public abstract void pushState(); + + + /** + * Restore state from stack (must be pushed first)
+ * This includes popColor and popGeometry. + */ + public abstract void popState(); + + + /** + * Store current rotation and translation on stack + */ + public abstract void pushGeometry(); + + + /** + * Restore rotation and translation from stack + */ + public abstract void popGeometry(); + + + /** + * Store color on stack (so it can later be restored) + */ + public abstract void pushColor(); + + + /** + * Restore color from stack (must be pushed first) + */ + public abstract void popColor(); + + + /** + * Render 2D quad with currently set color + * + * @param rect drawn rect + */ + public abstract void quad(Rect rect); + + + /** + * Render 2D quad with given color.
+ * This may change current drawing color. + * + * @param rect rectangle + * @param color draw color + */ + public abstract void quad(Rect rect, Color color); + + + /** + * Render 2D quad with gradient.
+ * This may change current drawing color. + * + * @param rect rectangle + * @param grad gradient + */ + public abstract void quad(Rect rect, Grad grad); + + + /** + * Render textured quad with current color + * + * @param rect rectangle to draw + * @param txquad texture quad + */ + public abstract void quad(Rect rect, TxQuad txquad); + + + /** + * Render textured quad with given color + * + * @param rect rectangle to draw + * @param txquad texture instance + * @param color color tint + */ + public abstract void quad(Rect rect, TxQuad txquad, Color color); + + + /** + * Setup projection for 2D graphics + * + * @param screenSize current viewport size + */ + public abstract void setupProjection(Vect screenSize); + + + /** + * Get backend-flavoured lazy texture + * + * @param path path to texture + * @return the lazy texture + */ + public abstract LazyTexture getLazyTexture(String path); + + + /** + * Set target fps (for syncing in endFrame() call).
+ * With vsync enabled, the target fps may not be met. + * + * @param fps requested fps + */ + public abstract void setTargetFps(int fps); + + + /** + * Create a main window + */ + public abstract void createDisplay(); + + + /** + * Set fullscreen + * + * @param fs true for fullscreen + */ + public abstract void setFullscreen(boolean fs); + + + /** + * Request fullscreen toggle (eg. at the end of render frame) + */ + public abstract void switchFullscreen(); + + + /** + * Get fullscreen state + * + * @return is fullscreen + */ + public abstract boolean isFullscreen(); + + + /** + * Take screenshot (expensive processing should be done in separate thread + * when screenshot is saved). + * + * @return screenshot object + */ + public abstract Screenshot takeScreenshot(); + + + /** + * FIXME This should be moved to inout module + * + * @return true if close was requested recently (i.e. click on cross) + */ + public abstract boolean isCloseRequested(); + + + /** + * Start a render frame - clear buffers, prepare rendering context etc. + */ + public abstract void beginFrame(); + + + /** + * End a render frame: flip buffers, sync to fps... + */ + public abstract void endFrame(); + + + /** + * Set display dimensions + * + * @param width display width (pixels) + * @param height display height (pixels) + */ + public abstract void setSize(int width, int height); + + + /** + * Set titlebar text + * + * @param title titlebar text + */ + public abstract void setTitle(String title); + + + /** + * Enable or disable VSync + * + * @param vsync true for vsync enabled + */ + public abstract void setVSync(boolean vsync); + + + /** + * Set window resizable / fixed + * + * @param resizable true for resizable + */ + public abstract void setResizable(boolean resizable); + + + /** + * Get screen rect. Should always return the same Vect instance. + * + * @return the rect + */ + public abstract Rect getRect(); + + + /** + * Get current FPS (eg. measured by a {@link FpsMeter}) + * + * @return current FPS + */ + public abstract long getFps(); + + + /** + * Get screen center. Should always return the same Vect instance. + * + * @return screen center. + */ + public abstract Vect getCenter(); + + + /** + * @return screen width + */ + public abstract int getWidth(); + + + /** + * @return screen height + */ + public abstract int getHeight(); + + + /** + * Get screen size. Should always return the same Vect instance. + * + * @return size + */ + public abstract Vect getSize(); +} diff --git a/src/mightypork/gamecore/render/Screenshot.java b/src/mightypork/gamecore/render/Screenshot.java index 0229309..ca55016 100644 --- a/src/mightypork/gamecore/render/Screenshot.java +++ b/src/mightypork/gamecore/render/Screenshot.java @@ -1,81 +1,33 @@ 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.
- * Screenshot, once taken, can be safely processed in separate thread. + *

+ * Screenshot object, can be used to save image to file. + *

+ *

+ * Screenshot typically takes a byte buffer and converts it to image before + * saving to file. This image can be cached to speed up repeated saving. + *

+ *

+ * Once created (passing byte buffer in constructor), the Screenshot should be + * safe to process (call the save() method) in separate thread. + *

* - * @author Ondřej Hruška (MightyPork) + * @author MightyPork */ -public class Screenshot { - - private final int width; - private final int height; - private final int bpp; - private final ByteBuffer bytes; - private BufferedImage image; - - - /** - * @param width image width - * @param height image height - * @param bpp bits per pixel (typically 4) - * @param buffer - */ - public Screenshot(int width, int height, int bpp, ByteBuffer buffer) - { - this.width = width; - this.height = height; - this.bpp = bpp; - this.bytes = buffer; - } - - - /** - * Extract to an image.
- * Subsequent calls will use a cached value. - * - * @return image - */ - 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 interface Screenshot { /** - * Save to a file.
- * Cached value is used if any. + * Process byte buffer and write image to a file.
+ * Image can be cached for future save. * * @param file target file * @throws IOException on error writing to file */ - public void save(File file) throws IOException - { - file.getParentFile().mkdirs(); - ImageIO.write(getImage(), "PNG", file); - } + void save(File file) throws IOException; } diff --git a/src/mightypork/gamecore/render/TaskTakeScreenshot.java b/src/mightypork/gamecore/render/TaskTakeScreenshot.java index afdf858..8866743 100644 --- a/src/mightypork/gamecore/render/TaskTakeScreenshot.java +++ b/src/mightypork/gamecore/render/TaskTakeScreenshot.java @@ -4,22 +4,30 @@ package mightypork.gamecore.render; import java.io.File; import java.io.IOException; +import mightypork.gamecore.backend.lwjgl.AwtScreenshot; import mightypork.gamecore.core.WorkDir; +import mightypork.gamecore.core.modules.App; import mightypork.utils.Support; import mightypork.utils.logging.Log; import org.newdawn.slick.opengl.GLUtils; +/** + * Task that takes screenshot and asynchronously saves it to a file.
+ * Can be run in a separate thread, but must be instantiated in the render + * thread. + * + * @author MightyPork + */ public class TaskTakeScreenshot implements Runnable { private final Screenshot scr; - public TaskTakeScreenshot() - { + public TaskTakeScreenshot() { GLUtils.checkGLContext(); - scr = DisplaySystem.prepareScreenshot(); + scr = App.gfx().takeScreenshot(); } diff --git a/src/mightypork/gamecore/resources/Res.java b/src/mightypork/gamecore/resources/Res.java index 8ec936d..c980890 100644 --- a/src/mightypork/gamecore/resources/Res.java +++ b/src/mightypork/gamecore/resources/Res.java @@ -7,7 +7,7 @@ import mightypork.gamecore.resources.audio.players.EffectPlayer; import mightypork.gamecore.resources.audio.players.LoopPlayer; import mightypork.gamecore.resources.fonts.FontRegistry; import mightypork.gamecore.resources.fonts.GLFont; -import mightypork.gamecore.resources.textures.GLTexture; +import mightypork.gamecore.resources.textures.ITexture; import mightypork.gamecore.resources.textures.TextureRegistry; import mightypork.gamecore.resources.textures.TxQuad; import mightypork.gamecore.resources.textures.TxSheet; @@ -43,7 +43,7 @@ public final class Res { } - public static GLTexture getTexture(String key) + public static ITexture getTexture(String key) { return textures.getTexture(key); } diff --git a/src/mightypork/gamecore/resources/fonts/FontRenderer.java b/src/mightypork/gamecore/resources/fonts/FontRenderer.java index 281150c..01d5bc7 100644 --- a/src/mightypork/gamecore/resources/fonts/FontRenderer.java +++ b/src/mightypork/gamecore/resources/fonts/FontRenderer.java @@ -1,8 +1,8 @@ package mightypork.gamecore.resources.fonts; +import mightypork.gamecore.core.modules.App; import mightypork.gamecore.gui.AlignX; -import mightypork.gamecore.render.Render; import mightypork.utils.math.color.Color; import mightypork.utils.math.color.pal.RGB; import mightypork.utils.math.constraints.rect.Rect; @@ -105,16 +105,16 @@ public class FontRenderer { */ public void draw(String text, Vect pos, double height, Color color) { - Render.pushMatrix(); + App.gfx().pushGeometry(); final double sc = getScale(height); - Render.translate(pos.x(), pos.y()); - Render.scaleXY(sc); + App.gfx().translate(pos.x(), pos.y()); + App.gfx().scaleXY(sc); font.draw(text, color); - Render.popMatrix(); + App.gfx().popGeometry(); } diff --git a/src/mightypork/gamecore/resources/fonts/impl/TextureBackedFont.java b/src/mightypork/gamecore/resources/fonts/impl/TextureBackedFont.java index 42a9a77..8fe2084 100644 --- a/src/mightypork/gamecore/resources/fonts/impl/TextureBackedFont.java +++ b/src/mightypork/gamecore/resources/fonts/impl/TextureBackedFont.java @@ -379,8 +379,6 @@ public class TextureBackedFont implements GLFont { { GLUtils.checkGLContext(); - LazyTexture.lastBind = null; // needs rebind. - // PUSH glPushAttrib(GL_ENABLE_BIT); diff --git a/src/mightypork/gamecore/resources/textures/GLTexture.java b/src/mightypork/gamecore/resources/textures/ITexture.java similarity index 96% rename from src/mightypork/gamecore/resources/textures/GLTexture.java rename to src/mightypork/gamecore/resources/textures/ITexture.java index b38c47a..e9fc94c 100644 --- a/src/mightypork/gamecore/resources/textures/GLTexture.java +++ b/src/mightypork/gamecore/resources/textures/ITexture.java @@ -10,7 +10,7 @@ import mightypork.utils.math.constraints.rect.Rect; * * @author Ondřej Hruška (MightyPork) */ -public interface GLTexture extends Destroyable { +public interface ITexture extends Destroyable { /** * Set filter for scaling diff --git a/src/mightypork/gamecore/resources/textures/LazyTexture.java b/src/mightypork/gamecore/resources/textures/LazyTexture.java index fdc06d4..4602910 100644 --- a/src/mightypork/gamecore/resources/textures/LazyTexture.java +++ b/src/mightypork/gamecore/resources/textures/LazyTexture.java @@ -1,38 +1,29 @@ package mightypork.gamecore.resources.textures; -import mightypork.gamecore.render.Render; import mightypork.gamecore.resources.BaseLazyResource; import mightypork.gamecore.resources.TextureBasedResource; import mightypork.utils.annotations.Alias; import mightypork.utils.math.constraints.rect.Rect; -import org.lwjgl.opengl.GL11; - /** - * Deferred texture + * Deferred texture (to be extended by backend texture) * * @author Ondřej Hruška (MightyPork) */ @Alias(name = "Texture") @TextureBasedResource -public class LazyTexture extends BaseLazyResource implements GLTexture { - - public static LazyTexture lastBind = null; +public abstract class LazyTexture extends BaseLazyResource implements ITexture { - private org.newdawn.slick.opengl.Texture backingTexture; - private FilterMode filter = FilterMode.NEAREST; - private WrapMode wrap = WrapMode.CLAMP; - private boolean alpha; - private boolean alphal; + protected FilterMode filter = FilterMode.NEAREST; + protected WrapMode wrap = WrapMode.CLAMP; /** * @param resourcePath resource path */ - public LazyTexture(String resourcePath) - { + public LazyTexture(String resourcePath) { super(resourcePath); } @@ -44,105 +35,6 @@ public class LazyTexture extends BaseLazyResource implements GLTexture { } - @Override - protected synchronized void loadResource(String path) - { - backingTexture = Render.loadSlickTexture(path, filter); - } - - - @Override - public boolean hasAlpha() - { - if (!ensureLoaded()) return false; - - if (!alphal) { - alphal = true; - alpha = backingTexture.hasAlpha(); - } - - return alpha; - } - - - @Override - public void bind() - { - if (!ensureLoaded()) return; - - GL11.glEnable(GL11.GL_TEXTURE_2D); - - if (lastBind != this) { - lastBind = this; - - GL11.glBindTexture(GL11.GL_TEXTURE_2D, getTextureID()); - - GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE); - - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, wrap.num); - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, wrap.num); - - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, filter.num); - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, filter.num); - - } - } - - - @Override - public int getImageHeight() - { - if (!ensureLoaded()) return 0; - - return backingTexture.getImageHeight(); - } - - - @Override - public int getImageWidth() - { - if (!ensureLoaded()) return 0; - - return backingTexture.getImageWidth(); - } - - - @Override - public float getHeight01() - { - if (!ensureLoaded()) return 0; - - return backingTexture.getHeight(); - } - - - @Override - public float getWidth01() - { - if (!ensureLoaded()) return 0; - - return backingTexture.getWidth(); - } - - - @Override - public void destroy() - { - if (!isLoaded()) return; - - backingTexture.release(); - } - - - @Override - public int getTextureID() - { - if (!ensureLoaded()) return -1; - - return backingTexture.getTextureID(); - } - - @Override public void setFilter(FilterMode filterMin) { diff --git a/src/mightypork/gamecore/resources/textures/QuadGrid.java b/src/mightypork/gamecore/resources/textures/QuadGrid.java index 147882b..86c4c04 100644 --- a/src/mightypork/gamecore/resources/textures/QuadGrid.java +++ b/src/mightypork/gamecore/resources/textures/QuadGrid.java @@ -11,14 +11,14 @@ import mightypork.utils.math.constraints.rect.Rect; */ public class QuadGrid { - private final GLTexture tx; + private final ITexture tx; private final int txHeight; private final int txWidth; private final double tileW; private final double tileH; - public QuadGrid(GLTexture tx, int tilesX, int tilesY) + public QuadGrid(ITexture tx, int tilesX, int tilesY) { this.tx = tx; this.txWidth = tilesX; diff --git a/src/mightypork/gamecore/resources/textures/TextureRegistry.java b/src/mightypork/gamecore/resources/textures/TextureRegistry.java index cddfe51..ba67ad1 100644 --- a/src/mightypork/gamecore/resources/textures/TextureRegistry.java +++ b/src/mightypork/gamecore/resources/textures/TextureRegistry.java @@ -4,6 +4,7 @@ package mightypork.gamecore.resources.textures; import java.util.HashMap; import java.util.Map; +import mightypork.gamecore.core.modules.App; import mightypork.gamecore.core.modules.AppAccess; import mightypork.gamecore.core.modules.AppAccessAdapter; import mightypork.gamecore.resources.ResourceLoadRequest; @@ -19,7 +20,7 @@ import mightypork.utils.math.constraints.rect.Rect; */ public class TextureRegistry extends AppAccessAdapter { - private final Map textures = new HashMap<>(); + private final Map textures = new HashMap<>(); private final Map sheets = new HashMap<>(); @@ -41,7 +42,7 @@ public class TextureRegistry extends AppAccessAdapter { * @param wrap * @return texture reference */ - public GLTexture addTexture(String resourcePath, FilterMode filter, WrapMode wrap) + public ITexture addTexture(String resourcePath, FilterMode filter, WrapMode wrap) { return addTexture(resourcePath, resourcePath, filter, wrap); } @@ -57,11 +58,11 @@ public class TextureRegistry extends AppAccessAdapter { * @param wrap * @return texture reference */ - public GLTexture addTexture(String key, String resourcePath, FilterMode filter, WrapMode wrap) + public ITexture addTexture(String key, String resourcePath, FilterMode filter, WrapMode wrap) { if (key != null) if (textures.containsKey(key)) throw new KeyAlreadyExistsException(); - final LazyTexture texture = new LazyTexture(resourcePath); + final LazyTexture texture = App.gfx().getLazyTexture(resourcePath); texture.setFilter(filter); texture.setWrap(wrap); @@ -118,14 +119,14 @@ public class TextureRegistry extends AppAccessAdapter { /** - * Get a loaded {@link GLTexture} + * Get a loaded {@link ITexture} * * @param key texture key * @return the texture */ - public GLTexture getTexture(String key) + public ITexture getTexture(String key) { - final GLTexture tx = textures.get(key); + final ITexture tx = textures.get(key); if (tx == null) throw new RuntimeException("There's no texture called \"" + key + "\"!"); diff --git a/src/mightypork/gamecore/resources/textures/TxQuad.java b/src/mightypork/gamecore/resources/textures/TxQuad.java index 02a0cc5..3b76a48 100644 --- a/src/mightypork/gamecore/resources/textures/TxQuad.java +++ b/src/mightypork/gamecore/resources/textures/TxQuad.java @@ -13,7 +13,7 @@ import mightypork.utils.math.constraints.rect.RectConst; public class TxQuad { /** The texture */ - public final GLTexture tx; + public final ITexture tx; /** Coords in texture (0-1) */ public final RectConst uvs; @@ -31,7 +31,7 @@ public class TxQuad { * @param heightPx area height (0-1) * @return new TxQuad */ - public static TxQuad fromSizePx(GLTexture tx, double xPx, double yPx, double widthPx, double heightPx) + public static TxQuad fromSizePx(ITexture tx, double xPx, double yPx, double widthPx, double heightPx) { final double w = tx.getImageWidth(); final double h = tx.getImageHeight(); @@ -50,7 +50,7 @@ public class TxQuad { * @param height area height (0-1) * @return new TxQuad */ - public static TxQuad fromSize(GLTexture tx, double x1, double y1, double width, double height) + public static TxQuad fromSize(ITexture tx, double x1, double y1, double width, double height) { return new TxQuad(tx, x1, y1, x1 + width, y1 + height); } @@ -65,7 +65,7 @@ public class TxQuad { * @param x2 right bottom X (0-1) * @param y2 right bottom Y (0-1) */ - public TxQuad(GLTexture tx, double x1, double y1, double x2, double y2) + public TxQuad(ITexture tx, double x1, double y1, double x2, double y2) { this(tx, Rect.make(x1, y1, x2, y2)); } @@ -75,7 +75,7 @@ public class TxQuad { * @param tx Texture * @param uvs Rect of texture UVs (0-1); will be frozen. */ - public TxQuad(GLTexture tx, Rect uvs) + public TxQuad(ITexture tx, Rect uvs) { this.tx = tx; this.uvs = uvs.freeze(); diff --git a/src/mightypork/rogue/RogueApp.java b/src/mightypork/rogue/RogueApp.java index 6e7cf98..87d45c9 100644 --- a/src/mightypork/rogue/RogueApp.java +++ b/src/mightypork/rogue/RogueApp.java @@ -3,6 +3,7 @@ package mightypork.rogue; import java.io.File; +import mightypork.gamecore.backend.lwjgl.LwjglBackend; import mightypork.gamecore.core.config.Config; import mightypork.gamecore.core.events.MainLoopRequest; import mightypork.gamecore.core.events.ShudownRequest; @@ -45,6 +46,8 @@ public final class RogueApp extends BaseApp implements ViewportChangeListener, S { super(workdir, singleInstance); + setBackend(new LwjglBackend(this)); + AppInitOptions opt = getInitOptions(); opt.addRoutes(new RogueRoutes()); diff --git a/src/mightypork/rogue/RogueResources.java b/src/mightypork/rogue/RogueResources.java index b160c83..60992a4 100644 --- a/src/mightypork/rogue/RogueResources.java +++ b/src/mightypork/rogue/RogueResources.java @@ -7,7 +7,7 @@ import mightypork.gamecore.resources.fonts.FontRegistry; import mightypork.gamecore.resources.fonts.Glyphs; import mightypork.gamecore.resources.fonts.impl.LazyFont; import mightypork.gamecore.resources.textures.FilterMode; -import mightypork.gamecore.resources.textures.GLTexture; +import mightypork.gamecore.resources.textures.ITexture; import mightypork.gamecore.resources.textures.QuadGrid; import mightypork.gamecore.resources.textures.TextureRegistry; import mightypork.gamecore.resources.textures.WrapMode; @@ -76,7 +76,7 @@ public class RogueResources implements ResourceSetup { @Override public void addTextures(TextureRegistry textures) { - GLTexture texture; + ITexture texture; QuadGrid grid; // gui