diff --git a/src/itdelatrisu/opsu/Options.java b/src/itdelatrisu/opsu/Options.java index 0c7e42b1..eb5aeccd 100644 --- a/src/itdelatrisu/opsu/Options.java +++ b/src/itdelatrisu/opsu/Options.java @@ -485,9 +485,6 @@ public class Options { /** The current skin. */ private static Skin skin; - /** Resolution that the latest Container passed to "setDisplayMode" was set to. */ - private static Resolution lastSetResolution = Resolution.RES_1024_768; - /** Frame limiters. */ private static final int[] targetFPS = { 60, 120, 240 }; @@ -597,8 +594,6 @@ public class Options { // set borderless window if dimensions match screen size boolean borderless = (screenWidth == resolution.getWidth() && screenHeight == resolution.getHeight()); System.setProperty("org.lwjgl.opengl.Window.undecorated", Boolean.toString(borderless)); - - lastSetResolution = resolution; } // /** @@ -893,24 +888,6 @@ public class Options { return replayDir; } - /** - * Returns the horizontal Resolution. - * If no game has been started then a default value is returned. - * @return the horizontal resolution of the latest game instance to be started. - */ - public static int getLatestResolutionWidth() { - return lastSetResolution.getWidth(); - } - - /** - * Returns the vertical Resolution. - * If no game has been started then a default value is returned. - * @return the vertical resolution of the latest game instance to be started. - */ - public static int getLatestResolutionHeight() { - return lastSetResolution.getHeight(); - } - /** * Returns the current skin directory. * If invalid, this will create a "Skins" folder in the root directory. diff --git a/src/itdelatrisu/opsu/objects/Slider.java b/src/itdelatrisu/opsu/objects/Slider.java index bcf3f0ab..78b96011 100644 --- a/src/itdelatrisu/opsu/objects/Slider.java +++ b/src/itdelatrisu/opsu/objects/Slider.java @@ -30,20 +30,12 @@ import itdelatrisu.opsu.objects.curves.CatmullCurve; import itdelatrisu.opsu.objects.curves.CircumscribedCircle; import itdelatrisu.opsu.objects.curves.Curve; import itdelatrisu.opsu.objects.curves.LinearBezier; -import itdelatrisu.opsu.render.Rendertarget; import itdelatrisu.opsu.states.Game; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL14; -import org.lwjgl.opengl.GL30; import org.newdawn.slick.Color; import org.newdawn.slick.GameContainer; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; -import org.newdawn.slick.util.Log; /** * Data type representing a slider object. @@ -58,9 +50,6 @@ public class Slider implements GameObject { /** Rate at which slider ticks are placed. */ private static float sliderTickRate = 1.0f; - /** Scaling factor for display elements */ - private static int diameter = 1; - /** The amount of time, in milliseconds, to fade in the slider. */ private static final int FADE_IN_TIME = 375; @@ -122,7 +111,7 @@ public class Slider implements GameObject { containerWidth = container.getWidth(); containerHeight = container.getHeight(); - diameter = (int) (104 - (circleSize * 8)); + int diameter = (int) (104 - (circleSize * 8)); diameter = (int) (diameter * HitObject.getXMultiplier()); // convert from Osupixels (640x480) // slider ball @@ -399,7 +388,6 @@ public class Slider implements GameObject { // calculate and send slider result hitResult(); - return true; } @@ -481,8 +469,6 @@ public class Slider implements GameObject { this.curve = new CatmullCurve(hitObject, color); else this.curve = new LinearBezier(hitObject, color, hitObject.getSliderType() == HitObject.SLIDER_LINEAR); - - this.curve.setScale(diameter );//* 118 / 128); } @Override diff --git a/src/itdelatrisu/opsu/objects/curves/Curve.java b/src/itdelatrisu/opsu/objects/curves/Curve.java index 0b96abc6..cddbd4b7 100644 --- a/src/itdelatrisu/opsu/objects/curves/Curve.java +++ b/src/itdelatrisu/opsu/objects/curves/Curve.java @@ -37,6 +37,9 @@ public abstract class Curve { /** Points generated along the curve should be spaced this far apart. */ protected static float CURVE_POINTS_SEPERATION = 5; + /** The width and height of the display container this curve gets drawn into */ + protected static int containerWidth = 0, containerHeight = 0; + /** The associated HitObject. */ protected HitObject hitObject; @@ -46,9 +49,6 @@ public abstract class Curve { /** The scaled slider x, y coordinate lists. */ protected float[] sliderX, sliderY; - /** scaling factor for drawing. */ - protected static float scale; - /** Per-curve render-state used for the new style curve renders*/ private CurveRenderState renderState; @@ -66,10 +66,22 @@ public abstract class Curve { this.y = hitObject.getScaledY(); this.sliderX = hitObject.getScaledSliderX(); this.sliderY = hitObject.getScaledSliderY(); - this.scale = 100; this.renderState = null; } + /** + * Set the width and height of the container that Curves get drawn into + * Should be called before any curves are drawn. + * @param width + * @param height + */ + public static void init(int width, int height, float circleSize) + { + containerWidth = width; + containerHeight = height; + CurveRenderState.init(width, height, circleSize); + } + /** * Returns the point on the curve at a value t. * @param t the t value [0, 1] @@ -82,26 +94,24 @@ public abstract class Curve { * @param color the color filter */ public void draw(Color color) { - if ( curve == null){ - Log.error("draw curve"+this); - return; + if (curve == null) { + return; + } + if (Options.GameOption.NEW_SLIDER.getBooleanValue()) { + if (renderState == null) { + renderState = new CurveRenderState(hitObject); } - if (Options.GameOption.NEW_SLIDER.getBooleanValue()) { - if(renderState == null) - { - renderState = new CurveRenderState(scale,hitObject); - } - renderState.draw(color,curve); - } else { - Image hitCircle = GameImage.HITCIRCLE.getImage(); - Image hitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage(); - for (int i = 0; i < curve.length; i++) { - hitCircleOverlay.drawCentered(curve[i].x, curve[i].y, Utils.COLOR_WHITE_FADE); - } - for (int i = 0; i < curve.length; i++){ - hitCircle.drawCentered(curve[i].x, curve[i].y, color); - } + renderState.draw(color, curve); + } else { + Image hitCircle = GameImage.HITCIRCLE.getImage(); + Image hitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage(); + for (int i = 0; i < curve.length; i++) { + hitCircleOverlay.drawCentered(curve[i].x, curve[i].y, Utils.COLOR_WHITE_FADE); } + for (int i = 0; i < curve.length; i++) { + hitCircle.drawCentered(curve[i].x, curve[i].y, color); + } + } } /** @@ -126,14 +136,6 @@ public abstract class Curve { */ public float getY(int i) { return (i == 0) ? y : sliderY[i - 1]; } - /** - * Set the scaling factor. - * @param factor the new scaling factor for the UI representation - */ - public static void setScale(float factor) { - scale = factor; - } - /** * Linear interpolation of a and b at t. */ diff --git a/src/itdelatrisu/opsu/render/CurveRenderState.java b/src/itdelatrisu/opsu/render/CurveRenderState.java index 9afda458..e45c1515 100644 --- a/src/itdelatrisu/opsu/render/CurveRenderState.java +++ b/src/itdelatrisu/opsu/render/CurveRenderState.java @@ -41,24 +41,55 @@ import org.newdawn.slick.util.Log; */ public class CurveRenderState { + /** The width and height of the display container this curve gets drawn into */ + protected static int containerWidth, containerHeight; + /** cached drawn slider, only used if new style sliders are activated */ public Rendertarget fbo; /** thickness of the curve */ - float scale; + protected static int scale; /** The HitObject associated with the curve to be drawn */ HitObject hitObject; /** Static state that's needed to draw the new style curves */ private static final NewCurveStyleState staticState = new NewCurveStyleState(); - - public CurveRenderState(float scale, HitObject hitObject) { + + /** + * Set the width and height of the container that Curves get drawn into + * Should be called before any curves are drawn. + * @param width + * @param height + */ + public static void init(int width, int height, float circleSize) + { + containerWidth = width; + containerHeight = height; + //equivalent to what happens in Slider.init + scale = (int) (104 - (circleSize * 8)); + scale = (int) (scale * HitObject.getXMultiplier()); // convert from Osupixels (640x480) + //scale = scale * 118 / 128; //for curves exaclty as big as the sliderball + FrameBufferCache.init(width, height); + } + + /** + * Creates an object to hold the render state that's necessary to draw a curve. + * @param hitObject the HitObject that represents this curve, just used as a + * unique ID + */ + public CurveRenderState(HitObject hitObject) { fbo = null; - this.scale = scale; this.hitObject = hitObject; } + /** + * Draw a curve to the screen that's tinted with `color`. The first time + * this is called this caches the image result of the curve and on subsequent + * runs it just draws the cached copy to the screen. + * @param color tint of the curve + * @param curve the points along the curve to be drawn + */ public void draw(Color color, Vec2f[] curve) { float alpha = color.a; //if this curve hasn't been drawn, draw it and cache the result @@ -108,6 +139,11 @@ public class CurveRenderState { FrameBufferCache.getInstance().freeMappingFor(hitObject); } + /** + * Just a structure to hold all the important OpenGL state that needs to be + * changed to draw the curve. This is used to backup and restore the state + * so that the code outside of this (mainly Slick2D) doesn't break + */ private class RenderState { boolean smoothedPoly; boolean blendEnabled; @@ -119,6 +155,11 @@ public class CurveRenderState { int oldArrayBuffer; } + /** + * Backup the current state of the relevant OpenGL state and change it to + * what's needed to draw the curve + * @return + */ private RenderState startRender() { RenderState state = new RenderState(); state.smoothedPoly = GL11.glGetBoolean(GL11.GL_POLYGON_SMOOTH); @@ -154,6 +195,10 @@ public class CurveRenderState { return state; } + /** + * Restore the old OpenGL state that's backed up in {@code state} + * @param state the old state to restore + */ private void endRender(RenderState state) { GL11.glMatrixMode(GL11.GL_PROJECTION); GL11.glPopMatrix(); @@ -186,12 +231,12 @@ public class CurveRenderState { * @param curve the points along the curve */ private void draw_curve(Color color, Vec2f[] curve) { + staticState.initGradient(); RenderState state = startRender(); int vtx_buf; - //floatsize * (position + texture coordinates) * (number of cones) * (vertices in a cone) + //The size is: floatsize * (position + texture coordinates) * (number of cones) * (vertices in a cone) FloatBuffer buff = BufferUtils.createByteBuffer(4 * (4 + 2) * (2 * curve.length - 1) * (NewCurveStyleState.DIVIDES + 2)).asFloatBuffer(); staticState.initShaderProgram(); - staticState.initGradient(); vtx_buf = GL15.glGenBuffers(); for (int i = 0; i < curve.length; ++i) { float x = curve[i].x; @@ -211,7 +256,20 @@ public class CurveRenderState { buff.flip(); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vtx_buf); GL15.glBufferData(GL15.GL_ARRAY_BUFFER, buff, GL15.GL_STATIC_DRAW); - staticState.render(color, curve.length * 2 - 1, vtx_buf); + GL20.glUseProgram(staticState.program); + GL20.glEnableVertexAttribArray(staticState.attribLoc); + GL20.glEnableVertexAttribArray(staticState.texCoordLoc); + GL20.glUniform1i(staticState.texLoc, 0); + GL20.glUniform3f(staticState.colLoc, color.r, color.g, color.b); + //stride is 6*4 for the floats (4 bytes) (u,v)(x,y,z,w) + //2*4 is for skipping the first 2 floats (u,v) + GL20.glVertexAttribPointer(staticState.attribLoc, 4, GL11.GL_FLOAT, false, 6 * 4, 2 * 4); + GL20.glVertexAttribPointer(staticState.texCoordLoc, 2, GL11.GL_FLOAT, false, 6 * 4, 0); + for (int i = 0; i < curve.length*2-1; ++i) { + GL11.glDrawArrays(GL11.GL_TRIANGLE_FAN, i * (NewCurveStyleState.DIVIDES + 2), NewCurveStyleState.DIVIDES + 2); + } + GL20.glDisableVertexAttribArray(staticState.texCoordLoc); + GL20.glDisableVertexAttribArray(staticState.attribLoc); GL15.glDeleteBuffers(vtx_buf); endRender(state); } @@ -226,8 +284,8 @@ public class CurveRenderState { * @param DIVIDES the base of the cone is a regular polygon with this many sides */ protected void fillCone(FloatBuffer buff, float x1, float y1, final int DIVIDES) { - float divx = Options.getLatestResolutionWidth() / 2.0f; - float divy = Options.getLatestResolutionHeight() / 2.0f; + float divx = containerWidth / 2.0f; + float divy = containerHeight / 2.0f; float offx = -1.0f; float offy = 1.0f; float x, y; @@ -271,7 +329,7 @@ public class CurveRenderState { * Contains all the necessary state that needs to be tracked to draw curves * in the new style and not re-create the shader each time. * - * @author Bigpet {@literal } + * @author Bigpet {@literal } */ private static class NewCurveStyleState { @@ -281,29 +339,22 @@ public class CurveRenderState { */ protected static final int DIVIDES = 30; - /** - * OpenGL shader program ID used to draw and recolor the curve - */ + /** OpenGL shader program ID used to draw and recolor the curve */ protected int program = 0; - /** - * OpenGL shader attribute location of the vertex position attribute - */ + /** OpenGL shader attribute location of the vertex position attribute */ protected int attribLoc = 0; - /** - * OpenGL shader attribute location of the texture coordinate attribute - */ + /** OpenGL shader attribute location of the texture coordinate attribute */ protected int texCoordLoc = 0; - /** - * OpenGL shader uniform location of the color attribute - */ + /** OpenGL shader uniform location of the color attribute */ protected int colLoc = 0; - /** - * OpenGL texture id for the gradient texture for the curve - */ + /** OpenGL shader uniform location of the texture sampler attribute */ + protected int texLoc = 0; + + /** OpenGL texture id for the gradient texture for the curve */ protected int gradientTexture = 0; /** @@ -324,7 +375,6 @@ public class CurveRenderState { buff.put((byte)(255*col.g)); buff.put((byte)(255*col.b)); buff.put((byte)(255*col.a)); - } buff.flip(); GL11.glBindTexture(GL11.GL_TEXTURE_1D, gradientTexture); @@ -391,38 +441,9 @@ public class CurveRenderState { } attribLoc = GL20.glGetAttribLocation(program, "in_position"); texCoordLoc = GL20.glGetAttribLocation(program, "in_tex_coord"); - int texLoc = GL20.glGetUniformLocation(program, "tex"); + texLoc = GL20.glGetUniformLocation(program, "tex"); colLoc = GL20.glGetUniformLocation(program, "col_tint"); - GL20.glUniform1i(texLoc, 0); } } - - /** - * Make the drawcalls for the curve. This requires the {@code vertexBufferObject} to be filled with - * {@code numCones * (NewCurveStyleState.DIVIDES+2)} sets of (u,v,x,y,z,w) floats - * @param color the color of the curve to be drawn - * @param numCones the number of points in the {code vertexBufferObject} - * @param vertexBufferObject the OpenGL vertex buffer object ID that - * contains the positions and texture coordinates of the cones - */ - public void render(Color color, int numCones, int vertexBufferObject) { - GL20.glUseProgram(program); - GL20.glEnableVertexAttribArray(attribLoc); - GL20.glEnableVertexAttribArray(texCoordLoc); - //stride is 6*4 for the floats (4 bytes) (u,v)(x,y,z,w) - //2*4 is for skipping the first 2 floats (u,v) - GL20.glVertexAttribPointer(attribLoc, 4, GL11.GL_FLOAT, false, 6 * 4, 2 * 4); - GL20.glVertexAttribPointer(texCoordLoc, 2, GL11.GL_FLOAT, false, 6 * 4, 0); - - GL20.glUniform3f(colLoc, color.r, color.g, color.b); - for (int i = 0; i < numCones; ++i) { - GL11.glDrawArrays(GL11.GL_TRIANGLE_FAN, i * (NewCurveStyleState.DIVIDES + 2), NewCurveStyleState.DIVIDES + 2); - } - - GL20.glDisableVertexAttribArray(texCoordLoc); - GL20.glDisableVertexAttribArray(attribLoc); - } - } - } diff --git a/src/itdelatrisu/opsu/render/FrameBufferCache.java b/src/itdelatrisu/opsu/render/FrameBufferCache.java index f3a447b4..43d0c72b 100644 --- a/src/itdelatrisu/opsu/render/FrameBufferCache.java +++ b/src/itdelatrisu/opsu/render/FrameBufferCache.java @@ -17,6 +17,7 @@ */ package itdelatrisu.opsu.render; +import itdelatrisu.opsu.Opsu; import itdelatrisu.opsu.Options; import itdelatrisu.opsu.beatmap.HitObject; import java.util.ArrayList; @@ -28,24 +29,31 @@ import org.newdawn.slick.util.Log; * This is cache for OpenGL FrameBufferObjects. This is currently only used * to draw curve objects of the new slider style. Does currently not integrate * well and requires some manual OpenGL state manipulation to use it. - * @author Bigpet {@literal } + * @author Bigpet {@literal } */ public class FrameBufferCache { private static final int INITIAL_CACHE_SIZE = 4; private static FrameBufferCache instance = null; private Map cacheMap; private ArrayList cache; - public final int width; - public final int height; + public static int width; + public static int height; - private FrameBufferCache(int width, int height) { - cache = new ArrayList<>(INITIAL_CACHE_SIZE); + /** + * Set the width and height of the framebuffers in this cache. + * Should be called before anything is inserted into the map. + * @param width + * @param height + */ + public static void init(int width, int height) + { + FrameBufferCache.width = width; + FrameBufferCache.height = height; + } + + private FrameBufferCache() { + cache = new ArrayList<>(); cacheMap = new HashMap<>(); - this.width = width; - this.height = height; - for (int i = 0; i < INITIAL_CACHE_SIZE; ++i) { - cache.add(Rendertarget.createRTTFramebuffer(width, height)); - } } /** @@ -115,7 +123,7 @@ public class FrameBufferCache { */ public static FrameBufferCache getInstance() { if (instance == null) { - instance = new FrameBufferCache(Options.getLatestResolutionWidth(), Options.getLatestResolutionHeight()); + instance = new FrameBufferCache(); } return instance; } diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index ff6d14cb..69d2c057 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -41,6 +41,7 @@ import itdelatrisu.opsu.objects.DummyObject; import itdelatrisu.opsu.objects.GameObject; import itdelatrisu.opsu.objects.Slider; import itdelatrisu.opsu.objects.Spinner; +import itdelatrisu.opsu.objects.curves.Curve; import itdelatrisu.opsu.render.FrameBufferCache; import itdelatrisu.opsu.replay.PlaybackSpeed; import itdelatrisu.opsu.replay.Replay; @@ -1024,7 +1025,7 @@ public class Game extends BasicGameState { if (beatmap == null || beatmap.objects == null) throw new RuntimeException("Running game with no beatmap loaded."); - //@TODO: find a better place to clean the SliderCache + //free all previously cached hitobject to framebuffer mappings if some still exist FrameBufferCache.getInstance().freeMap(); // grab the mouse (not working for touchscreen) @@ -1392,6 +1393,7 @@ public class Game extends BasicGameState { Circle.init(container, circleSize); Slider.init(container, circleSize, beatmap); Spinner.init(container); + Curve.init(container.getWidth(),container.getHeight(),circleSize); // approachRate (hit object approach time) if (approachRate < 5)