diff --git a/src/mightypork/gamecore/render/Render.java b/src/mightypork/gamecore/render/Render.java index 9ca8354..efd37c3 100644 --- a/src/mightypork/gamecore/render/Render.java +++ b/src/mightypork/gamecore/render/Render.java @@ -6,6 +6,7 @@ import static org.lwjgl.opengl.GL11.*; import java.io.IOException; import mightypork.gamecore.render.textures.FilterMode; +import mightypork.gamecore.render.textures.GLTexture; import mightypork.gamecore.render.textures.TxQuad; import mightypork.util.constraints.rect.Rect; import mightypork.util.constraints.rect.caching.RectDigest; @@ -327,7 +328,7 @@ public class Render { * @param texture the texture * @throws RuntimeException if not loaded yet */ - private static void bindTexture(Texture texture) throws RuntimeException + private static void bindTexture(GLTexture texture) throws RuntimeException { texture.bind(); } @@ -486,7 +487,7 @@ public class Render { * @param texture texture instance * @param tint color tint */ - public static void quadTextured(Rect quad, Rect uvs, Texture texture, Color tint) + public static void quadTextured(Rect quad, Rect uvs, GLTexture texture, Color tint) { bindTexture(texture); setColor(tint); @@ -502,7 +503,7 @@ public class Render { * @param uvs texture coords rectangle (px) * @param texture texture instance */ - public static void quadTextured(Rect quad, Rect uvs, Texture texture) + public static void quadTextured(Rect quad, Rect uvs, GLTexture texture) { quadTextured(quad, uvs, texture, Color.WHITE); } @@ -514,7 +515,7 @@ public class Render { * @param quad rectangle (px) * @param texture texture instance */ - public static void quadTextured(Rect quad, Texture texture) + public static void quadTextured(Rect quad, GLTexture texture) { quadTextured(quad, Rect.ONE, texture, Color.WHITE); } diff --git a/src/mightypork/gamecore/render/textures/DeferredTexture.java b/src/mightypork/gamecore/render/textures/DeferredTexture.java index 0b108db..035a95e 100644 --- a/src/mightypork/gamecore/render/textures/DeferredTexture.java +++ b/src/mightypork/gamecore/render/textures/DeferredTexture.java @@ -8,7 +8,6 @@ import mightypork.util.constraints.rect.Rect; import mightypork.util.logging.LogAlias; import org.lwjgl.opengl.GL11; -import org.newdawn.slick.opengl.Texture; /** @@ -18,9 +17,9 @@ import org.newdawn.slick.opengl.Texture; */ @MustLoadInMainThread @LogAlias(name = "Texture") -public class DeferredTexture extends DeferredResource implements FilteredTexture { +public class DeferredTexture extends DeferredResource implements GLTexture { - private Texture backingTexture; + private org.newdawn.slick.opengl.Texture backingTexture; private FilterMode filter = FilterMode.NEAREST; private WrapMode wrap = WrapMode.CLAMP; @@ -36,12 +35,13 @@ public class DeferredTexture extends DeferredResource implements FilteredTexture /** * Get a quad from this texture of given position/size * - * @param rect quad rect + * @param uvs quad rect * @return the quad */ - public TxQuad getQuad(Rect rect) + @Override + public TxQuad makeQuad(Rect uvs) { - return new TxQuad(this, rect); + return new TxQuad(this, uvs); } @@ -64,6 +64,7 @@ public class DeferredTexture extends DeferredResource implements FilteredTexture /** * Bind without adjusting parameters */ + @Override public void bindRaw() { if (!ensureLoaded()) return; @@ -211,4 +212,9 @@ public class DeferredTexture extends DeferredResource implements FilteredTexture this.wrap = wrapping; } + @Override + public QuadGrid grid(int x, int y) + { + return new QuadGrid(this, x, y); + } } diff --git a/src/mightypork/gamecore/render/textures/FilteredTexture.java b/src/mightypork/gamecore/render/textures/FilteredTexture.java deleted file mode 100644 index a686255..0000000 --- a/src/mightypork/gamecore/render/textures/FilteredTexture.java +++ /dev/null @@ -1,26 +0,0 @@ -package mightypork.gamecore.render.textures; - - -import org.newdawn.slick.opengl.Texture; - - -/** - * Texture with filter and wrap mode - * - * @author MightyPork - */ -public interface FilteredTexture extends Texture { - - /** - * Set filter for scaling - * - * @param filter filter - */ - void setFilter(FilterMode filter); - - - /** - * @param wrapping wrap mode - */ - void setWrap(WrapMode wrapping); -} diff --git a/src/mightypork/gamecore/render/textures/GLTexture.java b/src/mightypork/gamecore/render/textures/GLTexture.java new file mode 100644 index 0000000..b0de888 --- /dev/null +++ b/src/mightypork/gamecore/render/textures/GLTexture.java @@ -0,0 +1,53 @@ +package mightypork.gamecore.render.textures; + + +import org.newdawn.slick.opengl.Texture; + +import mightypork.util.constraints.rect.Rect; + + +/** + * Texture with filter and wrap mode + * + * @author MightyPork + */ +public interface GLTexture extends Texture { + + /** + * Set filter for scaling + * + * @param filter filter + */ + void setFilter(FilterMode filter); + + + /** + * @param wrapping wrap mode + */ + void setWrap(WrapMode wrapping); + + + /** + * Get a quad from this texture of given position/size + * + * @param uvs quad rect + * @return the quad + */ + TxQuad makeQuad(Rect uvs); + + + /** + * Bind without adjusting parameters + */ + void bindRaw(); + + + /** + * Get a grid for given number of tiles + * + * @param x horizontal tile count + * @param y vertical tile count + * @return grid + */ + QuadGrid grid(int x, int y); +} diff --git a/src/mightypork/gamecore/render/textures/QuadGrid.java b/src/mightypork/gamecore/render/textures/QuadGrid.java new file mode 100644 index 0000000..d8c756a --- /dev/null +++ b/src/mightypork/gamecore/render/textures/QuadGrid.java @@ -0,0 +1,92 @@ +package mightypork.gamecore.render.textures; + + +import mightypork.util.constraints.rect.Rect; + + +/** + * {@link TxQuad} and {@link TxSheet} building utility + * + * @author MightyPork + */ +public class QuadGrid { + + private final GLTexture tx; + private int txHeight; + private int txWidth; + private double tileW; + private double tileH; + + + public QuadGrid(GLTexture tx, int tilesX, int tilesY) { + this.tx = tx; + this.txWidth = tilesX; + this.txHeight = tilesY; + this.tileW = 1D / tilesX; + this.tileH = 1D / tilesY; + } + + + /** + * Make square quad at given coords (one grid cell) + * + * @param x x coordinate (cells) + * @param y y coordinate (cells) + * @return the quad + */ + public TxQuad makeQuad(int x, int y) + { + if (x < 0 || x >= txWidth || y < 0 || y >= txHeight) { + throw new IndexOutOfBoundsException("Requested invalid txquad coordinates."); + } + + return makeQuad(x, y, 1, 1); + } + + + /** + * Make square quad at given coords, with arbitrary size. Coordinates are + * multiples of cell size. + * + * @param x x coordinate (cells) + * @param y y coordinate (cells) + * @param width width (cells) + * @param height height (cells) + * @return the quad + */ + public TxQuad makeQuad(double x, double y, double width, double height) + { + if (x < 0 || x >= txWidth || y < 0 || y >= txHeight) { + throw new IndexOutOfBoundsException("Requested invalid txquad coordinates."); + } + + if (x + width > txWidth || y + height > txHeight) { + throw new IndexOutOfBoundsException("Requested invalid txquad size (would go beyond texture size)."); + } + + return tx.makeQuad(Rect.make(tileW * x, tileH * y, tileW * width, tileH * height)); + } + + + /** + * Make a sheet of given cells. + * + * @param x x origin coordinate (cells) + * @param y y origin coordinate (cells) + * @param width width (cells) + * @param height height (cells) + * @return the sheet + */ + public TxSheet makeSheet(int x, int y, int width, int height) + { + if (x < 0 || x >= txWidth || y < 0 || y >= txHeight) { + throw new IndexOutOfBoundsException("Requested invalid txquad coordinates."); + } + + if (x + width > txWidth || y + height > txHeight) { + throw new IndexOutOfBoundsException("Requested invalid txsheet size (would go beyond texture size)."); + } + + return makeQuad(x, y).makeSheet(width, height); + } +} diff --git a/src/mightypork/gamecore/render/textures/TextureBank.java b/src/mightypork/gamecore/render/textures/TextureBank.java index d220bce..71c745a 100644 --- a/src/mightypork/gamecore/render/textures/TextureBank.java +++ b/src/mightypork/gamecore/render/textures/TextureBank.java @@ -2,13 +2,13 @@ package mightypork.gamecore.render.textures; import java.util.HashMap; +import java.util.Map; import mightypork.gamecore.control.AppAccess; import mightypork.gamecore.control.AppAdapter; import mightypork.gamecore.control.events.ResourceLoadRequest; import mightypork.util.constraints.rect.Rect; - -import org.newdawn.slick.opengl.Texture; +import mightypork.util.error.KeyAlreadyExistsException; /** @@ -25,80 +25,113 @@ public class TextureBank extends AppAdapter { super(app); } - private final HashMap textures = new HashMap<>(); + private final Map textures = new HashMap<>(); - private final HashMap quads = new HashMap<>(); + private final Map quads = new HashMap<>(); - private DeferredTexture lastTx; + private final Map sheets = new HashMap<>(); /** - * Load a {@link Texture} + * Load a texture from resource. A full-sized quad with the same key will be + * automatically added. * * @param key texture key - * @param texture texture to load + * @param resourcePath texture resource path + * @param filter filter + * @param wrap texture wrapping + * @return the loaded texture. + */ + public GLTexture loadTexture(String key, String resourcePath, FilterMode filter, WrapMode wrap) + { + if (textures.containsKey(key)) throw new KeyAlreadyExistsException(); + + final DeferredTexture texture = new DeferredTexture(resourcePath); + texture.setFilter(filter); + texture.setWrap(wrap); + + loadTexture(key, texture); + + return texture; + } + + + /** + * Add an already initialized deferred texture to textures registry + * + * @param key + * @param texture */ public void loadTexture(String key, DeferredTexture texture) { getEventBus().send(new ResourceLoadRequest(texture)); textures.put(key, texture); - lastTx = texture; - makeQuad(key, Rect.ONE); + addQuad(key, texture.makeQuad(Rect.ONE)); } /** - * Load a {@link Texture} from resource + * Make a quad from texture, and add it to quads registry. * - * @param key texture key - * @param resourcePath texture resource path - * @param filter filter - * @param wrap texture wrapping + * @param quadKey key + * @param texture source texture + * @param uvs rect + * @return the created quad */ - public void loadTexture(String key, String resourcePath, FilterMode filter, WrapMode wrap) + public TxQuad makeQuad(String quadKey, GLTexture texture, Rect uvs) { - final DeferredTexture texture = new DeferredTexture(resourcePath); - texture.setFilter(filter); - texture.setWrap(wrap); + TxQuad quad = texture.makeQuad(uvs); + addQuad(quadKey, quad); + return quad; + } + + + /** + * Add already created quad to the quad registry + * + * @param quadKey key + * @param quad quad to add + */ + public void addQuad(String quadKey, TxQuad quad) + { + if (quads.containsKey(quadKey)) throw new KeyAlreadyExistsException(); - loadTexture(key, texture); + quads.put(quadKey, quad); } /** - * Create a {@link TxQuad} in a texture + * make a sprite sheet originating at given quad, spanning right and down. * - * @param quadKey quad key - * @param textureKey texture key - * @param quad quad rectangle (absolute pixel coordinates) * + * @param sheetKey key + * @param origin starting quad + * @param width sheet width (multiplies of origin width) + * @param height sheet height (multiplies of origin height) + * @return the created sheet */ - public void makeQuad(String quadKey, String textureKey, Rect quad) + public TxSheet makeSheet(String sheetKey, TxQuad origin, int width, int height) { - final DeferredTexture tx = textures.get(textureKey); - if (tx == null) throw new RuntimeException("Texture with key " + textureKey + " not defined!"); + TxSheet sheet = origin.makeSheet(width, height); - final TxQuad txquad = tx.getQuad(quad); + addSheet(sheetKey, sheet); - quads.put(quadKey, txquad); + return sheet; } /** - * Create a {@link TxQuad} in the last loaded texture + * Add an already created sheet * - * @param quadKey quad key - * @param quad quad rectangle (0-1) + * @param sheetKey key + * @param sheet sheet to add */ - public void makeQuad(String quadKey, Rect quad) + public void addSheet(String sheetKey, TxSheet sheet) { - final DeferredTexture tx = lastTx; - if (tx == null) throw new RuntimeException("There's no texture loaded yet, can't define quads!"); - - final TxQuad txquad = tx.getQuad(quad); + if (sheets.containsKey(sheetKey)) throw new KeyAlreadyExistsException(); - quads.put(quadKey, txquad); + sheets.put(sheetKey, sheet); } @@ -108,7 +141,7 @@ public class TextureBank extends AppAdapter { * @param key quad key * @return the quad */ - public TxQuad getTxQuad(String key) + public TxQuad getQuad(String key) { final TxQuad q = quads.get(key); @@ -119,14 +152,14 @@ public class TextureBank extends AppAdapter { /** - * Get a loaded {@link Texture} + * Get a loaded {@link GLTexture} * * @param key texture key * @return the texture */ - public Texture getTexture(String key) + public GLTexture getTexture(String key) { - final Texture t = textures.get(key); + final GLTexture t = textures.get(key); if (t == null) throw new RuntimeException("There's no texture called " + key + "!"); diff --git a/src/mightypork/gamecore/render/textures/TxQuad.java b/src/mightypork/gamecore/render/textures/TxQuad.java index 8611dbf..a00250d 100644 --- a/src/mightypork/gamecore/render/textures/TxQuad.java +++ b/src/mightypork/gamecore/render/textures/TxQuad.java @@ -2,8 +2,7 @@ package mightypork.gamecore.render.textures; import mightypork.util.constraints.rect.Rect; - -import org.newdawn.slick.opengl.Texture; +import mightypork.util.constraints.rect.RectConst; /** @@ -14,9 +13,9 @@ import org.newdawn.slick.opengl.Texture; public class TxQuad { /** The texture */ - public final Texture tx; + public final GLTexture tx; /** Coords in texture (0-1) */ - public final Rect uvs; + public final RectConst uvs; /** @@ -29,7 +28,7 @@ public class TxQuad { * @param heightPx area height (0-1) * @return new TxQuad */ - public static TxQuad fromSizePx(Texture tx, double xPx, double yPx, double widthPx, double heightPx) + public static TxQuad fromSizePx(GLTexture tx, double xPx, double yPx, double widthPx, double heightPx) { final double w = tx.getImageWidth(); final double h = tx.getImageHeight(); @@ -48,7 +47,7 @@ public class TxQuad { * @param height area height (0-1) * @return new TxQuad */ - public static TxQuad fromSize(Texture tx, double x1, double y1, double width, double height) + public static TxQuad fromSize(GLTexture tx, double x1, double y1, double width, double height) { return new TxQuad(tx, x1, y1, x1 + width, y1 + height); } @@ -63,18 +62,18 @@ public class TxQuad { * @param x2 right bottom X (0-1) * @param y2 right bottom Y (0-1) */ - public TxQuad(Texture tx, double x1, double y1, double x2, double y2) { + public TxQuad(GLTexture tx, double x1, double y1, double x2, double y2) { this(tx, Rect.make(x1, y1, x2, y2)); } /** * @param tx Texture - * @param uvs Rect of texture UVs (0-1); will be stored as is. + * @param uvs Rect of texture UVs (0-1); will be frozen. */ - public TxQuad(Texture tx, Rect uvs) { + public TxQuad(GLTexture tx, Rect uvs) { this.tx = tx; - this.uvs = uvs; + this.uvs = uvs.freeze(); } @@ -98,4 +97,17 @@ public class TxQuad { { return new TxQuad(this); } + + + /** + * Make a sheet starting with this quad, spannign to right and down. + * + * @param width sheet width + * @param height sheet height + * @return sheet + */ + public TxSheet makeSheet(int width, int height) + { + return new TxSheet(this, width, height); + } } diff --git a/src/mightypork/gamecore/render/textures/TxSheet.java b/src/mightypork/gamecore/render/textures/TxSheet.java new file mode 100644 index 0000000..8ff6cea --- /dev/null +++ b/src/mightypork/gamecore/render/textures/TxSheet.java @@ -0,0 +1,90 @@ +package mightypork.gamecore.render.textures; + + +import java.util.Random; + + +/** + * Basic sprite sheet + * + * @author MightyPork + */ +public class TxSheet { + + private final TxQuad original; + private final TxQuad[] sprites; + private final int width; + + private final Random rand = new Random(); + private final Random randForSeed = new Random(); + private int count; + + + public TxSheet(TxQuad tx, int width, int height) { + this.original = tx; + this.width = width; + this.count = width * height; + + this.sprites = new TxQuad[count]; + } + + + /** + * @return number of quads + */ + public int getQuadCount() + { + return count; + } + + + /** + * Get quad of index + * + * @param index index + * @return the quad + */ + public TxQuad getQuad(int index) + { + if (index < 0 || index > count) { + throw new IndexOutOfBoundsException("Index out of bounds: " + index + ", allowed: 0.." + count); + } + + // lazy - init only when needed + if (sprites[index] == null) { + final int x = index % width; + final int y = index / width; + + final double origW = original.uvs.width().value(); + final double origH = original.uvs.height().value(); + + sprites[index] = new TxQuad(original.tx, original.uvs.move(x * origW, y * origH)); + } + + return sprites[index]; + } + + + /** + * Get entirely random TxQuad from this sheet + * + * @return the picked quad + */ + public TxQuad getRandomQuad() + { + return getQuad(rand.nextInt(count)); + } + + + /** + * Get random TxQuad from this sheet + * + * @param seed random number generator seed + * @return the picked quad + */ + public TxQuad getRandomQuad(long seed) + { + randForSeed.setSeed(seed); + return getQuad(randForSeed.nextInt(count)); + } +} diff --git a/src/mightypork/rogue/App.java b/src/mightypork/rogue/App.java index 0428f9c..4830170 100644 --- a/src/mightypork/rogue/App.java +++ b/src/mightypork/rogue/App.java @@ -132,7 +132,7 @@ public final class App extends BaseApp { // this will work only with reusable events (such as requests) bindToKey(new ActionRequest(RequestType.FULLSCREEN), Keys.F11); bindToKey(new ActionRequest(RequestType.SCREENSHOT), Keys.F2); - bindToKey(new ActionRequest(RequestType.SHUTDOWN), Keys.L_CONTROL, Keys.Q); + bindToKey(new CrossfadeRequest(null), Keys.L_CONTROL, Keys.Q); bindToKey(new CrossfadeRequest("main_menu"), Keys.L_CONTROL, Keys.M); } diff --git a/src/mightypork/rogue/Res.java b/src/mightypork/rogue/Res.java index 39b6d6a..f2bc232 100644 --- a/src/mightypork/rogue/Res.java +++ b/src/mightypork/rogue/Res.java @@ -10,11 +10,7 @@ import mightypork.gamecore.render.fonts.FontBank; import mightypork.gamecore.render.fonts.GLFont; import mightypork.gamecore.render.fonts.Glyphs; import mightypork.gamecore.render.fonts.impl.DeferredFont; -import mightypork.gamecore.render.textures.DeferredTexture; -import mightypork.gamecore.render.textures.FilterMode; -import mightypork.gamecore.render.textures.TextureBank; -import mightypork.gamecore.render.textures.TxQuad; -import mightypork.gamecore.render.textures.WrapMode; +import mightypork.gamecore.render.textures.*; import mightypork.util.constraints.rect.Rect; import org.newdawn.slick.opengl.Texture; @@ -83,22 +79,20 @@ public final class Res { texture.setFilter(FilterMode.NEAREST); texture.setWrap(WrapMode.CLAMP); textures.loadTexture("gui1", texture); - - final double p16 = 0.25D; - final double p8 = 0.125D; + QuadGrid guiGrid = texture.grid(4, 4); //@formatter:off - textures.makeQuad("item_frame", Rect.make(0, 0, p16, p16)); - textures.makeQuad("sword", Rect.make(p16, 0, p16, p16)); - textures.makeQuad("meat", Rect.make(p16*2, 0, p16, p16)); + textures.addQuad("item_frame", guiGrid.makeQuad(0, 0)); + textures.addQuad("sword", guiGrid.makeQuad(1, 0)); + textures.addQuad("meat", guiGrid.makeQuad(2, 0)); - textures.makeQuad("heart_on", Rect.make(0, p16, p8, p8)); - textures.makeQuad("heart_off", Rect.make(p8, p16, p8, p8)); + textures.addQuad("heart_on", guiGrid.makeQuad(.0, 1, .5, .5)); + textures.addQuad("heart_off", guiGrid.makeQuad(.5, 1, .5, .5)); - textures.makeQuad("xp_on", Rect.make(0, p16+p8, p8, p8)); - textures.makeQuad("xp_off", Rect.make(p8, p16+p8, p8, p8)); + textures.addQuad("xp_on", guiGrid.makeQuad(0, 1.5, .5, .5)); + textures.addQuad("xp_off", guiGrid.makeQuad(.5, 1.5, .5, .5)); - textures.makeQuad("panel", Rect.make(0, p16*4-p8/2, p16*4, p8/2)); + textures.addQuad("panel", guiGrid.makeQuad(0, 3.75, 4, .25)); //@formatter:off } @@ -113,7 +107,7 @@ public final class Res { public static TxQuad getTxQuad(String key) { - return textures.getTxQuad(key); + return textures.getQuad(key); } diff --git a/src/mightypork/rogue/screens/CrossfadeOverlay.java b/src/mightypork/rogue/screens/CrossfadeOverlay.java index caf3457..c7554a0 100644 --- a/src/mightypork/rogue/screens/CrossfadeOverlay.java +++ b/src/mightypork/rogue/screens/CrossfadeOverlay.java @@ -5,6 +5,8 @@ import mightypork.gamecore.control.AppAccess; import mightypork.gamecore.control.events.ScreenRequestEvent; import mightypork.gamecore.gui.components.painters.QuadPainter; import mightypork.gamecore.gui.screens.Overlay; +import mightypork.rogue.events.ActionRequest; +import mightypork.rogue.events.ActionRequest.RequestType; import mightypork.util.constraints.num.mutable.NumAnimated; import mightypork.util.control.timing.TimedTask; import mightypork.util.math.Easing; @@ -26,8 +28,11 @@ public class CrossfadeOverlay extends Overlay implements CrossfadeRequest.Listen @Override public void run() { - if (requestedScreenName == null) shutdown(); - getEventBus().send(new ScreenRequestEvent(requestedScreenName)); + if (requestedScreenName == null) { + getEventBus().send(new ActionRequest(RequestType.SHUTDOWN)); + } else { + getEventBus().send(new ScreenRequestEvent(requestedScreenName)); + } } }; diff --git a/src/mightypork/test/TestRandomSeed.java b/src/mightypork/test/TestRandomSeed.java new file mode 100644 index 0000000..bbf762a --- /dev/null +++ b/src/mightypork/test/TestRandomSeed.java @@ -0,0 +1,35 @@ +package mightypork.test; + + +import java.util.Random; + + +public class TestRandomSeed { + + public static void main(String[] args) + { + + { + Random rand = new Random(); + long begin = System.currentTimeMillis(); + + for (int i = 0; i < 1000000; i++) { + rand.setSeed(1000); + } + + System.out.println((System.currentTimeMillis() - begin) / 1000D); + } + + { + long begin = System.currentTimeMillis(); + + for (int i = 0; i < 1000000; i++) { + Random rand = new Random(); + rand.setSeed(1000); + } + + System.out.println((System.currentTimeMillis() - begin) / 1000D); + } + + } +} diff --git a/src/mightypork/util/error/KeyAlreadyExistsException.java b/src/mightypork/util/error/KeyAlreadyExistsException.java new file mode 100644 index 0000000..dc432c4 --- /dev/null +++ b/src/mightypork/util/error/KeyAlreadyExistsException.java @@ -0,0 +1,22 @@ +package mightypork.util.error; + + +public class KeyAlreadyExistsException extends RuntimeException { + + public KeyAlreadyExistsException() { + super(); + } + + public KeyAlreadyExistsException(String message, Throwable cause) { + super(message, cause); + } + + public KeyAlreadyExistsException(String message) { + super(message); + } + + public KeyAlreadyExistsException(Throwable cause) { + super(cause); + } + +}