Follow-up to #64.

- Removed NEW_SLIDER option, and use the skin "SliderStyle" instead.  Uses the new style by default, unless STYLE_PEPPYSLIDER is specified.
- Check if OpenGL 3.0 is supported before trying to draw new style sliders.
- Fixed compilation warnings; removed unneeded fields and imports.
- Filled in some missing Javadocs.
- Style changes.

Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
Jeffrey Han 2015-06-08 15:02:28 -04:00
parent 9c8a8f24c6
commit b6f208a47d
10 changed files with 192 additions and 188 deletions

View File

@ -374,16 +374,10 @@ public class GameData {
health = 100f; health = 100f;
healthDisplay = 100f; healthDisplay = 100f;
hitResultCount = new int[HIT_MAX]; hitResultCount = new int[HIT_MAX];
if(hitResultList != null) if (hitResultList != null) {
{ for (OsuHitObjectResult hitResult : hitResultList) {
Iterator<OsuHitObjectResult> iter =hitResultList.iterator(); if (hitResult.curve != null)
while(iter.hasNext()) hitResult.curve.discardCache();
{
OsuHitObjectResult hitObj = iter.next();
if(hitObj != null && hitObj.curve != null)
{
hitObj.curve.discardCache();
}
} }
} }
hitResultList = new LinkedBlockingDeque<OsuHitObjectResult>(); hitResultList = new LinkedBlockingDeque<OsuHitObjectResult>();
@ -951,11 +945,9 @@ public class GameData {
} }
hitResult.alpha = 1 - ((float) (trackPosition - hitResult.time) / HITRESULT_FADE_TIME); hitResult.alpha = 1 - ((float) (trackPosition - hitResult.time) / HITRESULT_FADE_TIME);
} else{ } else {
if (hitResult.curve !=null) if (hitResult.curve != null)
{
hitResult.curve.discardCache(); hitResult.curve.discardCache();
}
iter.remove(); iter.remove();
} }
} }

View File

@ -228,7 +228,6 @@ public class Options {
UI.getCursor().reset(); UI.getCursor().reset();
} }
}, },
NEW_SLIDER("Enable New Slider", "Use the new Slider style (requires OpenGL 3.0).",false),
DYNAMIC_BACKGROUND ("Enable Dynamic Backgrounds", "The song background will be used as the main menu background.", true), DYNAMIC_BACKGROUND ("Enable Dynamic Backgrounds", "The song background will be used as the main menu background.", true),
BACKGROUND_DIM ("Background Dim", "Percentage to dim the background image during gameplay.", 50, 0, 100), BACKGROUND_DIM ("Background Dim", "Percentage to dim the background image during gameplay.", 50, 0, 100),
FORCE_DEFAULT_PLAYFIELD ("Force Default Playfield", "Override the song background with the default playfield background.", false), FORCE_DEFAULT_PLAYFIELD ("Force Default Playfield", "Override the song background with the default playfield background.", false),
@ -1072,9 +1071,6 @@ public class Options {
case "NewCursor": case "NewCursor":
GameOption.NEW_CURSOR.setValue(Boolean.parseBoolean(value)); GameOption.NEW_CURSOR.setValue(Boolean.parseBoolean(value));
break; break;
case "NewSlider":
GameOption.NEW_SLIDER.setValue(Boolean.parseBoolean(value));
break;
case "DynamicBackground": case "DynamicBackground":
GameOption.DYNAMIC_BACKGROUND.setValue(Boolean.parseBoolean(value)); GameOption.DYNAMIC_BACKGROUND.setValue(Boolean.parseBoolean(value));
break; break;
@ -1226,8 +1222,6 @@ public class Options {
writer.newLine(); writer.newLine();
writer.write(String.format("NewCursor = %b", isNewCursorEnabled())); writer.write(String.format("NewCursor = %b", isNewCursorEnabled()));
writer.newLine(); writer.newLine();
writer.write(String.format("NewSlider = %b", GameOption.NEW_SLIDER.getBooleanValue()));
writer.newLine();
writer.write(String.format("DynamicBackground = %b", isDynamicBackgroundEnabled())); writer.write(String.format("DynamicBackground = %b", isDynamicBackgroundEnabled()));
writer.newLine(); writer.newLine();
writer.write(String.format("LoadVerbose = %b", isLoadVerbose())); writer.write(String.format("LoadVerbose = %b", isLoadVerbose()));

View File

@ -187,9 +187,8 @@ public class Slider implements GameObject {
// start circle // start circle
hitCircle.drawCentered(x, y, color); hitCircle.drawCentered(x, y, color);
if (!overlayAboveNumber) { if (!overlayAboveNumber)
hitCircleOverlay.drawCentered(x, y, Utils.COLOR_WHITE_FADE); hitCircleOverlay.drawCentered(x, y, Utils.COLOR_WHITE_FADE);
}
// ticks // ticks
if (ticksT != null) { if (ticksT != null) {

View File

@ -18,12 +18,14 @@
package itdelatrisu.opsu.objects.curves; package itdelatrisu.opsu.objects.curves;
import itdelatrisu.opsu.render.CurveRenderState;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.HitObject; import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.render.CurveRenderState;
import itdelatrisu.opsu.skins.Skin;
import org.lwjgl.opengl.GLContext;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.Image; import org.newdawn.slick.Image;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
@ -37,9 +39,9 @@ public abstract class Curve {
/** Points generated along the curve should be spaced this far apart. */ /** Points generated along the curve should be spaced this far apart. */
protected static float CURVE_POINTS_SEPERATION = 5; protected static float CURVE_POINTS_SEPERATION = 5;
/** The width and height of the display container this curve gets drawn into */ /** Whether OpenGL 3.0 is supported. */
protected static int containerWidth = 0, containerHeight = 0; private static boolean openGL30 = false;
/** The associated HitObject. */ /** The associated HitObject. */
protected HitObject hitObject; protected HitObject hitObject;
@ -48,8 +50,8 @@ public abstract class Curve {
/** The scaled slider x, y coordinate lists. */ /** The scaled slider x, y coordinate lists. */
protected float[] sliderX, sliderY; protected float[] sliderX, sliderY;
/** Per-curve render-state used for the new style curve renders*/ /** Per-curve render-state used for the new style curve renders. */
private CurveRenderState renderState; private CurveRenderState renderState;
/** Points along the curve (set by inherited classes). */ /** Points along the curve (set by inherited classes). */
@ -70,18 +72,22 @@ public abstract class Curve {
} }
/** /**
* Set the width and height of the container that Curves get drawn into * Set the width and height of the container that Curves get drawn into.
* Should be called before any curves are drawn. * Should be called before any curves are drawn.
* @param width * @param width the container width
* @param height * @param height the container height
* @param circleSize the circle size
*/ */
public static void init(int width, int height, float circleSize) public static void init(int width, int height, float circleSize) {
{ openGL30 = GLContext.getCapabilities().OpenGL30;
containerWidth = width; if (openGL30)
containerHeight = height; CurveRenderState.init(width, height, circleSize);
CurveRenderState.init(width, height, circleSize); else {
if (Options.getSkin().getSliderStyle() != Skin.STYLE_PEPPYSLIDER)
Log.warn("New slider style requires OpenGL 3.0, which is not supported.");
}
} }
/** /**
* Returns the point on the curve at a value t. * Returns the point on the curve at a value t.
* @param t the t value [0, 1] * @param t the t value [0, 1]
@ -94,23 +100,24 @@ public abstract class Curve {
* @param color the color filter * @param color the color filter
*/ */
public void draw(Color color) { public void draw(Color color) {
if (curve == null) { if (curve == null)
return; return;
}
if (Options.GameOption.NEW_SLIDER.getBooleanValue()) { // peppysliders
if (renderState == null) { if (Options.getSkin().getSliderStyle() == Skin.STYLE_PEPPYSLIDER || !openGL30) {
renderState = new CurveRenderState(hitObject);
}
renderState.draw(color, curve);
} else {
Image hitCircle = GameImage.HITCIRCLE.getImage(); Image hitCircle = GameImage.HITCIRCLE.getImage();
Image hitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage(); Image hitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage();
for (int i = 0; i < curve.length; i++) { for (int i = 0; i < curve.length; i++)
hitCircleOverlay.drawCentered(curve[i].x, curve[i].y, Utils.COLOR_WHITE_FADE); hitCircleOverlay.drawCentered(curve[i].x, curve[i].y, Utils.COLOR_WHITE_FADE);
} for (int i = 0; i < curve.length; i++)
for (int i = 0; i < curve.length; i++) {
hitCircle.drawCentered(curve[i].x, curve[i].y, color); hitCircle.drawCentered(curve[i].x, curve[i].y, color);
} }
// mmsliders
else {
if (renderState == null)
renderState = new CurveRenderState(hitObject);
renderState.draw(color, curve);
} }
} }
@ -143,8 +150,11 @@ public abstract class Curve {
return a * (1 - t) + b * t; return a * (1 - t) + b * t;
} }
/**
* Discards the slider cache (only used for mmsliders).
*/
public void discardCache() { public void discardCache() {
if(renderState != null) if (renderState != null)
renderState.discardCache(); renderState.discardCache();
} }
} }

View File

@ -18,12 +18,13 @@
package itdelatrisu.opsu.render; package itdelatrisu.opsu.render;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils; import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.HitObject; import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.objects.curves.Vec2f; import itdelatrisu.opsu.objects.curves.Vec2f;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import org.lwjgl.BufferUtils; import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL13;
@ -38,45 +39,46 @@ import org.newdawn.slick.util.Log;
/** /**
* Hold the temporary render state that needs to be restored again after the new * Hold the temporary render state that needs to be restored again after the new
* style curves are drawn. * style curves are drawn.
*
* @author Bigpet {@literal <dravorek (at) gmail.com>}
*/ */
public class CurveRenderState { public class CurveRenderState {
/** The width and height of the display container this curve gets drawn into. */
/** The width and height of the display container this curve gets drawn into */
protected static int containerWidth, containerHeight; protected static int containerWidth, containerHeight;
/** cached drawn slider, only used if new style sliders are activated */
public Rendertarget fbo;
/** thickness of the curve */ /** Thickness of the curve. */
protected static int 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 */ /** Static state that's needed to draw the new style curves. */
private static final NewCurveStyleState staticState = new NewCurveStyleState(); private static final NewCurveStyleState staticState = new NewCurveStyleState();
/** Cached drawn slider, only used if new style sliders are activated. */
public Rendertarget fbo;
/** The HitObject associated with the curve to be drawn. */
protected HitObject hitObject;
/** /**
* Set the width and height of the container that Curves get drawn into * Set the width and height of the container that Curves get drawn into.
* Should be called before any curves are drawn. * Should be called before any curves are drawn.
* @param width * @param width the container width
* @param height * @param height the container height
* @param circleSize the circle size
*/ */
public static void init(int width, int height, float circleSize) public static void init(int width, int height, float circleSize) {
{
containerWidth = width; containerWidth = width;
containerHeight = height; containerHeight = height;
//equivalent to what happens in Slider.init
// equivalent to what happens in Slider.init()
scale = (int) (104 - (circleSize * 8)); scale = (int) (104 - (circleSize * 8));
scale = (int) (scale * HitObject.getXMultiplier()); // convert from Osupixels (640x480) scale = (int) (scale * HitObject.getXMultiplier()); // convert from Osupixels (640x480)
//scale = scale * 118 / 128; //for curves exaclty as big as the sliderball //scale = scale * 118 / 128; //for curves exactly as big as the sliderball
FrameBufferCache.init(width, height); FrameBufferCache.init(width, height);
} }
/** /**
* Creates an object to hold the render state that's necessary to draw a curve. * 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 * @param hitObject the HitObject that represents this curve, just used as a unique ID
* unique ID
*/ */
public CurveRenderState(HitObject hitObject) { public CurveRenderState(HitObject hitObject) {
fbo = null; fbo = null;
@ -92,13 +94,13 @@ public class CurveRenderState {
*/ */
public void draw(Color color, Vec2f[] curve) { public void draw(Color color, Vec2f[] curve) {
float alpha = color.a; float alpha = color.a;
//if this curve hasn't been drawn, draw it and cache the result
// if this curve hasn't been drawn, draw it and cache the result
if (fbo == null) { if (fbo == null) {
FrameBufferCache cache = FrameBufferCache.getInstance(); FrameBufferCache cache = FrameBufferCache.getInstance();
Rendertarget mapping = cache.get(hitObject); Rendertarget mapping = cache.get(hitObject);
if (mapping == null) { if (mapping == null)
mapping = cache.insert(hitObject); mapping = cache.insert(hitObject);
}
fbo = mapping; fbo = mapping;
int old_fb = GL11.glGetInteger(GL30.GL_FRAMEBUFFER_BINDING); int old_fb = GL11.glGetInteger(GL30.GL_FRAMEBUFFER_BINDING);
@ -116,8 +118,8 @@ public class CurveRenderState {
GL30.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, old_fb); GL30.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, old_fb);
Utils.COLOR_WHITE_FADE.a = alpha; Utils.COLOR_WHITE_FADE.a = alpha;
} }
//draw a fullscreen quad with the texture that contains the curve // draw a fullscreen quad with the texture that contains the curve
GL11.glEnable(GL11.GL_TEXTURE_2D); GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glDisable(GL11.GL_TEXTURE_1D); GL11.glDisable(GL11.GL_TEXTURE_1D);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, fbo.getTextureID()); GL11.glBindTexture(GL11.GL_TEXTURE_2D, fbo.getTextureID());
@ -134,15 +136,18 @@ public class CurveRenderState {
GL11.glEnd(); GL11.glEnd();
} }
/**
* Discard the cache.
*/
public void discardCache() { public void discardCache() {
fbo = null; fbo = null;
FrameBufferCache.getInstance().freeMappingFor(hitObject); FrameBufferCache.getInstance().freeMappingFor(hitObject);
} }
/** /**
* Just a structure to hold all the important OpenGL state that needs to be * 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 * 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 * so that the code outside of this (mainly Slick2D) doesn't break.
*/ */
private class RenderState { private class RenderState {
boolean smoothedPoly; boolean smoothedPoly;
@ -157,8 +162,7 @@ public class CurveRenderState {
/** /**
* Backup the current state of the relevant OpenGL state and change it to * Backup the current state of the relevant OpenGL state and change it to
* what's needed to draw the curve * what's needed to draw the curve.
* @return
*/ */
private RenderState startRender() { private RenderState startRender() {
RenderState state = new RenderState(); RenderState state = new RenderState();
@ -182,7 +186,7 @@ public class CurveRenderState {
GL11.glTexParameteri(GL11.GL_TEXTURE_1D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_LINEAR); GL11.glTexParameteri(GL11.GL_TEXTURE_1D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_1D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); GL11.glTexParameteri(GL11.GL_TEXTURE_1D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_1D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP); GL11.glTexParameteri(GL11.GL_TEXTURE_1D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
GL20.glUseProgram(0); GL20.glUseProgram(0);
GL11.glMatrixMode(GL11.GL_PROJECTION); GL11.glMatrixMode(GL11.GL_PROJECTION);
@ -196,7 +200,7 @@ public class CurveRenderState {
} }
/** /**
* Restore the old OpenGL state that's backed up in {@code state} * Restore the old OpenGL state that's backed up in {@code state}.
* @param state the old state to restore * @param state the old state to restore
*/ */
private void endRender(RenderState state) { private void endRender(RenderState state) {
@ -208,21 +212,16 @@ public class CurveRenderState {
GL20.glUseProgram(state.oldProgram); GL20.glUseProgram(state.oldProgram);
GL13.glActiveTexture(state.texUnit); GL13.glActiveTexture(state.texUnit);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, state.oldArrayBuffer); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, state.oldArrayBuffer);
if (!state.depthWriteEnabled) { if (!state.depthWriteEnabled)
GL11.glDepthMask(false); GL11.glDepthMask(false);
} if (!state.depthEnabled)
if (!state.depthEnabled) {
GL11.glDisable(GL11.GL_DEPTH_TEST); GL11.glDisable(GL11.GL_DEPTH_TEST);
} if (state.texEnabled)
if (state.texEnabled) {
GL11.glEnable(GL11.GL_TEXTURE_2D); GL11.glEnable(GL11.GL_TEXTURE_2D);
} if (state.smoothedPoly)
if (state.smoothedPoly) {
GL11.glEnable(GL11.GL_POLYGON_SMOOTH); GL11.glEnable(GL11.GL_POLYGON_SMOOTH);
} if (!state.blendEnabled)
if (!state.blendEnabled) {
GL11.glDisable(GL11.GL_BLEND); GL11.glDisable(GL11.GL_BLEND);
}
} }
/** /**
@ -234,14 +233,14 @@ public class CurveRenderState {
staticState.initGradient(); staticState.initGradient();
RenderState state = startRender(); RenderState state = startRender();
int vtx_buf; int vtx_buf;
//The size is: 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(); FloatBuffer buff = BufferUtils.createByteBuffer(4 * (4 + 2) * (2 * curve.length - 1) * (NewCurveStyleState.DIVIDES + 2)).asFloatBuffer();
staticState.initShaderProgram(); staticState.initShaderProgram();
vtx_buf = GL15.glGenBuffers(); vtx_buf = GL15.glGenBuffers();
for (int i = 0; i < curve.length; ++i) { for (int i = 0; i < curve.length; ++i) {
float x = curve[i].x; float x = curve[i].x;
float y = curve[i].y; float y = curve[i].y;
//if(i == 0 || i==curve.length-1){ //if (i == 0 || i == curve.length - 1){
fillCone(buff, x, y, NewCurveStyleState.DIVIDES); fillCone(buff, x, y, NewCurveStyleState.DIVIDES);
if (i != 0) { if (i != 0) {
float last_x = curve[i - 1].x; float last_x = curve[i - 1].x;
@ -265,9 +264,8 @@ public class CurveRenderState {
//2*4 is for skipping the first 2 floats (u,v) //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.attribLoc, 4, GL11.GL_FLOAT, false, 6 * 4, 2 * 4);
GL20.glVertexAttribPointer(staticState.texCoordLoc, 2, GL11.GL_FLOAT, false, 6 * 4, 0); GL20.glVertexAttribPointer(staticState.texCoordLoc, 2, GL11.GL_FLOAT, false, 6 * 4, 0);
for (int i = 0; i < curve.length*2-1; ++i) { for (int i = 0; i < curve.length * 2 - 1; ++i)
GL11.glDrawArrays(GL11.GL_TRIANGLE_FAN, i * (NewCurveStyleState.DIVIDES + 2), NewCurveStyleState.DIVIDES + 2); GL11.glDrawArrays(GL11.GL_TRIANGLE_FAN, i * (NewCurveStyleState.DIVIDES + 2), NewCurveStyleState.DIVIDES + 2);
}
GL20.glDisableVertexAttribArray(staticState.texCoordLoc); GL20.glDisableVertexAttribArray(staticState.texCoordLoc);
GL20.glDisableVertexAttribArray(staticState.attribLoc); GL20.glDisableVertexAttribArray(staticState.attribLoc);
GL15.glDeleteBuffers(vtx_buf); GL15.glDeleteBuffers(vtx_buf);
@ -277,7 +275,7 @@ public class CurveRenderState {
/** /**
* Fill {@code buff} with the texture coordinates and positions for a cone * Fill {@code buff} with the texture coordinates and positions for a cone
* with {@code DIVIDES} ground corners that has its center at the coordinates * with {@code DIVIDES} ground corners that has its center at the coordinates
* {@code (x1,y1)} * {@code (x1,y1)}.
* @param buff the buffer to be filled * @param buff the buffer to be filled
* @param x1 x-coordinate of the cone * @param x1 x-coordinate of the cone
* @param y1 y-coordinate of the cone * @param y1 y-coordinate of the cone
@ -332,57 +330,53 @@ public class CurveRenderState {
* @author Bigpet {@literal <dravorek (at) gmail.com>} * @author Bigpet {@literal <dravorek (at) gmail.com>}
*/ */
private static class NewCurveStyleState { private static class NewCurveStyleState {
/** /**
* used for new style Slider rendering, defines how many vertices the * Used for new style Slider rendering, defines how many vertices the
* base of the cone has that is used to draw the curve * base of the cone has that is used to draw the curve.
*/ */
protected static final int DIVIDES = 30; 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; 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; 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; protected int texCoordLoc = 0;
/** OpenGL shader uniform location of the color attribute */ /** OpenGL shader uniform location of the color attribute. */
protected int colLoc = 0; protected int colLoc = 0;
/** OpenGL shader uniform location of the texture sampler attribute */ /** OpenGL shader uniform location of the texture sampler attribute. */
protected int texLoc = 0; protected int texLoc = 0;
/** OpenGL texture id for the gradient texture for the curve */ /** OpenGL texture id for the gradient texture for the curve. */
protected int gradientTexture = 0; protected int gradientTexture = 0;
/** /**
* Reads the first row of the slider gradient texture and upload it as * Reads the first row of the slider gradient texture and upload it as
* a 1D texture to OpenGL if it hasn't already been done * a 1D texture to OpenGL if it hasn't already been done.
*/ */
public void initGradient() public void initGradient() {
{ if (gradientTexture == 0) {
if(gradientTexture == 0) Image slider = GameImage.SLIDER_GRADIENT.getImage().getScaledCopy(1.0f / GameImage.getUIscale());
{
Image slider = GameImage.SLIDER_GRADIENT.getImage().getScaledCopy(1.0f/GameImage.getUIscale());
staticState.gradientTexture = GL11.glGenTextures(); staticState.gradientTexture = GL11.glGenTextures();
ByteBuffer buff = BufferUtils.createByteBuffer(slider.getWidth()*4); ByteBuffer buff = BufferUtils.createByteBuffer(slider.getWidth() * 4);
for(int i=0 ; i< slider.getWidth(); ++i) for (int i = 0; i < slider.getWidth(); ++i) {
{
Color col = slider.getColor(i, 0); Color col = slider.getColor(i, 0);
buff.put((byte)(255*col.r)); buff.put((byte) (255 * col.r));
buff.put((byte)(255*col.g)); buff.put((byte) (255 * col.g));
buff.put((byte)(255*col.b)); buff.put((byte) (255 * col.b));
buff.put((byte)(255*col.a)); buff.put((byte) (255 * col.a));
} }
buff.flip(); buff.flip();
GL11.glBindTexture(GL11.GL_TEXTURE_1D, gradientTexture); GL11.glBindTexture(GL11.GL_TEXTURE_1D, gradientTexture);
GL11.glTexImage1D(GL11.GL_TEXTURE_1D,0,GL11.GL_RGBA,slider.getWidth(),0,GL11.GL_RGBA,GL11.GL_UNSIGNED_BYTE,buff); GL11.glTexImage1D(GL11.GL_TEXTURE_1D, 0, GL11.GL_RGBA, slider.getWidth(), 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buff);
GL30.glGenerateMipmap(GL11.GL_TEXTURE_1D); GL30.glGenerateMipmap(GL11.GL_TEXTURE_1D);
} }
} }
/** /**
* Compiles and links the shader program for the new style curve objects * Compiles and links the shader program for the new style curve objects
* if it hasn't already been compiled and linked. * if it hasn't already been compiled and linked.
@ -407,7 +401,7 @@ public class CurveRenderState {
int res = GL20.glGetShaderi(vtxShdr, GL20.GL_COMPILE_STATUS); int res = GL20.glGetShaderi(vtxShdr, GL20.GL_COMPILE_STATUS);
if (res != GL11.GL_TRUE) { if (res != GL11.GL_TRUE) {
String error = GL20.glGetShaderInfoLog(vtxShdr, 1024); String error = GL20.glGetShaderInfoLog(vtxShdr, 1024);
Log.error("Vertex Shader compilation failed", new Exception(error)); Log.error("Vertex Shader compilation failed.", new Exception(error));
} }
GL20.glShaderSource(frgShdr, "#version 330\n" GL20.glShaderSource(frgShdr, "#version 330\n"
+ "\n" + "\n"
@ -429,7 +423,7 @@ public class CurveRenderState {
res = GL20.glGetShaderi(frgShdr, GL20.GL_COMPILE_STATUS); res = GL20.glGetShaderi(frgShdr, GL20.GL_COMPILE_STATUS);
if (res != GL11.GL_TRUE) { if (res != GL11.GL_TRUE) {
String error = GL20.glGetShaderInfoLog(frgShdr, 1024); String error = GL20.glGetShaderInfoLog(frgShdr, 1024);
Log.error("Fragment Shader compilation failed", new Exception(error)); Log.error("Fragment Shader compilation failed.", new Exception(error));
} }
GL20.glAttachShader(program, vtxShdr); GL20.glAttachShader(program, vtxShdr);
GL20.glAttachShader(program, frgShdr); GL20.glAttachShader(program, frgShdr);
@ -437,7 +431,7 @@ public class CurveRenderState {
res = GL20.glGetProgrami(program, GL20.GL_LINK_STATUS); res = GL20.glGetProgrami(program, GL20.GL_LINK_STATUS);
if (res != GL11.GL_TRUE) { if (res != GL11.GL_TRUE) {
String error = GL20.glGetProgramInfoLog(program, 1024); String error = GL20.glGetProgramInfoLog(program, 1024);
Log.error("Program linking failed", new Exception(error)); Log.error("Program linking failed.", new Exception(error));
} }
attribLoc = GL20.glGetAttribLocation(program, "in_position"); attribLoc = GL20.glGetAttribLocation(program, "in_position");
texCoordLoc = GL20.glGetAttribLocation(program, "in_tex_coord"); texCoordLoc = GL20.glGetAttribLocation(program, "in_tex_coord");

View File

@ -17,57 +17,68 @@
*/ */
package itdelatrisu.opsu.render; package itdelatrisu.opsu.render;
import itdelatrisu.opsu.Opsu;
import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.beatmap.HitObject; import itdelatrisu.opsu.beatmap.HitObject;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.newdawn.slick.util.Log; import org.newdawn.slick.util.Log;
/** /**
* This is cache for OpenGL FrameBufferObjects. This is currently only used * This is cache for OpenGL FrameBufferObjects. This is currently only used
* to draw curve objects of the new slider style. Does currently not integrate * to draw curve objects of the new slider style. Does currently not integrate
* well and requires some manual OpenGL state manipulation to use it. * well and requires some manual OpenGL state manipulation to use it.
*
* @author Bigpet {@literal <dravorek (at) gmail.com>} * @author Bigpet {@literal <dravorek (at) gmail.com>}
*/ */
public class FrameBufferCache { public class FrameBufferCache {
private static final int INITIAL_CACHE_SIZE = 4; /** The initial cache size. */
//private static final int INITIAL_CACHE_SIZE = 4;
/** The single framebuffer cache instance. */
private static FrameBufferCache instance = null; private static FrameBufferCache instance = null;
/** The mapping from hit objects to framebuffers. */
private Map<HitObject, Rendertarget> cacheMap; private Map<HitObject, Rendertarget> cacheMap;
/** */
private ArrayList<Rendertarget> cache; private ArrayList<Rendertarget> cache;
public static int width;
public static int height; /** Container dimensions. */
public static int width, height;
/** /**
* Set the width and height of the framebuffers in this cache. * Set the width and height of the framebuffers in this cache.
* Should be called before anything is inserted into the map. * Should be called before anything is inserted into the map.
* @param width * @param width the container width
* @param height * @param height the container height
*/ */
public static void init(int width, int height) public static void init(int width, int height) {
{
FrameBufferCache.width = width; FrameBufferCache.width = width;
FrameBufferCache.height = height; FrameBufferCache.height = height;
} }
/**
* Constructor.
*/
private FrameBufferCache() { private FrameBufferCache() {
cache = new ArrayList<>(); cache = new ArrayList<Rendertarget>();
cacheMap = new HashMap<>(); cacheMap = new HashMap<HitObject, Rendertarget>();
} }
/** /**
* Check if there is a framebuffer object mapped to the {@code HitObject obj} * Check if there is a framebuffer object mapped to {@code obj}.
* @param obj * @param obj the hit object
* @return true if there is a framebuffer mapped for this HitObject, else false * @return true if there is a framebuffer mapped for this {@code HitObject}, else false
*/ */
public boolean contains(HitObject obj) { public boolean contains(HitObject obj) {
return cacheMap.containsKey(obj); return cacheMap.containsKey(obj);
} }
/** /**
* Get the {@code Rendertarget} mapped to {@code obj} * Get the {@code Rendertarget} mapped to {@code obj}.
* @param obj * @param obj the hit object
* @return the {@code Rendertarget} if there's one mapped to {@code obj}, otherwise null * @return the {@code Rendertarget} if there's one mapped to {@code obj}, otherwise null
*/ */
public Rendertarget get(HitObject obj) { public Rendertarget get(HitObject obj) {
@ -75,8 +86,8 @@ public class FrameBufferCache {
} }
/** /**
* Clear the mapping for {@code obj} to free it up to get used by another {@code HitObject} * Clear the mapping for {@code obj} to free it up to get used by another {@code HitObject}.
* @param obj * @param obj the hit object
* @return true if there was a mapping for {@code obj} and false if there was no mapping for it. * @return true if there was a mapping for {@code obj} and false if there was no mapping for it.
*/ */
public boolean freeMappingFor(HitObject obj) { public boolean freeMappingFor(HitObject obj) {
@ -95,11 +106,11 @@ public class FrameBufferCache {
* Create a mapping from {@code obj} to a framebuffer. If there was already * Create a mapping from {@code obj} to a framebuffer. If there was already
* a mapping from {@code obj} this will associate another framebuffer with it * a mapping from {@code obj} this will associate another framebuffer with it
* (thereby freeing up the previously mapped framebuffer). * (thereby freeing up the previously mapped framebuffer).
* @param obj * @param obj the hit object
* @return the {@code Rendertarget} newly mapped to {@code obj} * @return the {@code Rendertarget} newly mapped to {@code obj}
*/ */
public Rendertarget insert(HitObject obj) { public Rendertarget insert(HitObject obj) {
//find first RTTFramebuffer that's not mapped to anything and return it // find first RTTFramebuffer that's not mapped to anything and return it
Rendertarget buffer; Rendertarget buffer;
for (int i = 0; i < cache.size(); ++i) { for (int i = 0; i < cache.size(); ++i) {
buffer = cache.get(i); buffer = cache.get(i);
@ -108,7 +119,8 @@ public class FrameBufferCache {
return buffer; return buffer;
} }
} }
//no unmapped RTTFramebuffer found, create a new one
// no unmapped RTTFramebuffer found, create a new one
buffer = Rendertarget.createRTTFramebuffer(width, height); buffer = Rendertarget.createRTTFramebuffer(width, height);
cache.add(buffer); cache.add(buffer);
Log.warn("Framebuffer cache was not large enough, possible resource leak."); Log.warn("Framebuffer cache was not large enough, possible resource leak.");
@ -117,15 +129,14 @@ public class FrameBufferCache {
} }
/** /**
* There should only ever be one framebuffer cache, this function returns that one framebuffer cache instance. * There should only ever be one framebuffer cache, this function returns
* that one framebuffer cache instance.
* If there was no instance created already then this function creates it. * If there was no instance created already then this function creates it.
* @return the instance of FrameBufferCache * @return the instance of FrameBufferCache
*/ */
public static FrameBufferCache getInstance() { public static FrameBufferCache getInstance() {
if (instance == null) { if (instance == null)
instance = new FrameBufferCache(); instance = new FrameBufferCache();
}
return instance; return instance;
} }
} }

View File

@ -24,64 +24,70 @@ import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
/** /**
* Represents a rendertarget. For now this maps to an OpenGL FBO via LWJGL.
* *
* Represents a rendertarget. For now this maps to an OpenGL FBO via LWJGL * @author Bigpet {@literal <dravorek (at) gmail.com>}
*/ */
public class Rendertarget { public class Rendertarget {
public final int width; /** The dimensions. */
public final int height; public final int width, height;
/** The FBO ID. */
private final int fboID; private final int fboID;
/** The texture ID. */
private final int textureID; private final int textureID;
/** /**
* Create a new FBO * Create a new FBO.
* * @param width the width
* @param width * @param height the height
* @param height
*/ */
private Rendertarget(int width, int height) private Rendertarget(int width, int height) {
{
this.width = width; this.width = width;
this.height = height; this.height = height;
fboID = GL30.glGenFramebuffers(); fboID = GL30.glGenFramebuffers();
textureID = GL11.glGenTextures(); textureID = GL11.glGenTextures();
} }
/** /**
* Bind this rendertarget as the primary framebuffer. * Bind this rendertarget as the primary framebuffer.
*/ */
public void bind() public void bind() {
{
GL30.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, fboID); GL30.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, fboID);
} }
//use judiciously, try to avoid if possible and consider adding a method to /**
//this class if you find yourself calling this repeatedly * Returns the FBO ID.
public int getID() */
{ // NOTE: use judiciously, try to avoid if possible and consider adding a
// method to this class if you find yourself calling this repeatedly.
public int getID() {
return fboID; return fboID;
} }
//try not to use, could be moved into seperate class /**
public int getTextureID() * Returns the texture ID.
{ */
// NOTE: try not to use, could be moved into separate class.
public int getTextureID() {
return textureID; return textureID;
} }
/** /**
* Bind the default framebuffer * Bind the default framebuffer.
*/ */
public static void unbind() public static void unbind() {
{
GL30.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, 0); GL30.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, 0);
} }
/** /**
* Creates a Rendertarget with a Texture that it renders the color buffer in * Creates a Rendertarget with a Texture that it renders the color buffer in
* and a renderbuffer that it renders the depth to. * and a renderbuffer that it renders the depth to.
* @param width the width
* @param height the height
*/ */
public static Rendertarget createRTTFramebuffer(int width, int height) public static Rendertarget createRTTFramebuffer(int width, int height) {
{
int old_framebuffer = GL11.glGetInteger(GL30.GL_READ_FRAMEBUFFER_BINDING); int old_framebuffer = GL11.glGetInteger(GL30.GL_READ_FRAMEBUFFER_BINDING);
int old_texture = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D); int old_texture = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
Rendertarget buffer = new Rendertarget(width,height); Rendertarget buffer = new Rendertarget(width,height);
@ -104,7 +110,6 @@ public class Rendertarget {
GL11.glBindTexture(GL11.GL_TEXTURE_2D, old_texture); GL11.glBindTexture(GL11.GL_TEXTURE_2D, old_texture);
GL30.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, old_framebuffer); GL30.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, old_framebuffer);
return buffer; return buffer;
} }
} }

View File

@ -31,10 +31,10 @@ public class Skin {
/** Slider styles. */ /** Slider styles. */
public static final byte public static final byte
STYLE_PEPPYSLIDER = 1, STYLE_PEPPYSLIDER = 1, // fallback
STYLE_MMSLIDER = 2, STYLE_MMSLIDER = 2, // default (requires OpenGL 3.0)
STYLE_TOONSLIDER = 3, STYLE_TOONSLIDER = 3, // not implemented
STYLE_OPENGLSLIDER = 4; STYLE_OPENGLSLIDER = 4; // not implemented
/** The latest skin version. */ /** The latest skin version. */
protected static final int LATEST_VERSION = 2; protected static final int LATEST_VERSION = 2;
@ -133,7 +133,7 @@ public class Skin {
protected boolean comboBurstRandom = false; protected boolean comboBurstRandom = false;
/** The slider style to use (see STYLE_* constants). */ /** The slider style to use (see STYLE_* constants). */
protected byte sliderStyle = 2; protected byte sliderStyle = STYLE_MMSLIDER;
/** /**
* [Colours] * [Colours]

View File

@ -31,8 +31,8 @@ import itdelatrisu.opsu.audio.MusicController;
import itdelatrisu.opsu.audio.SoundController; import itdelatrisu.opsu.audio.SoundController;
import itdelatrisu.opsu.audio.SoundEffect; import itdelatrisu.opsu.audio.SoundEffect;
import itdelatrisu.opsu.beatmap.Beatmap; import itdelatrisu.opsu.beatmap.Beatmap;
import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.beatmap.BeatmapParser; import itdelatrisu.opsu.beatmap.BeatmapParser;
import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.beatmap.TimingPoint; import itdelatrisu.opsu.beatmap.TimingPoint;
import itdelatrisu.opsu.db.BeatmapDB; import itdelatrisu.opsu.db.BeatmapDB;
import itdelatrisu.opsu.db.ScoreDB; import itdelatrisu.opsu.db.ScoreDB;
@ -1025,7 +1025,7 @@ public class Game extends BasicGameState {
if (beatmap == null || beatmap.objects == null) if (beatmap == null || beatmap.objects == null)
throw new RuntimeException("Running game with no beatmap loaded."); throw new RuntimeException("Running game with no beatmap loaded.");
//free all previously cached hitobject to framebuffer mappings if some still exist // free all previously cached hitobject to framebuffer mappings if some still exist
FrameBufferCache.getInstance().freeMap(); FrameBufferCache.getInstance().freeMap();
// grab the mouse (not working for touchscreen) // grab the mouse (not working for touchscreen)
@ -1393,7 +1393,7 @@ public class Game extends BasicGameState {
Circle.init(container, circleSize); Circle.init(container, circleSize);
Slider.init(container, circleSize, beatmap); Slider.init(container, circleSize, beatmap);
Spinner.init(container); Spinner.init(container);
Curve.init(container.getWidth(),container.getHeight(),circleSize); Curve.init(container.getWidth(), container.getHeight(), circleSize);
// approachRate (hit object approach time) // approachRate (hit object approach time)
if (approachRate < 5) if (approachRate < 5)

View File

@ -59,8 +59,7 @@ public class OptionsMenu extends BasicGameState {
GameOption.SCREENSHOT_FORMAT, GameOption.SCREENSHOT_FORMAT,
GameOption.NEW_CURSOR, GameOption.NEW_CURSOR,
GameOption.DYNAMIC_BACKGROUND, GameOption.DYNAMIC_BACKGROUND,
GameOption.LOAD_VERBOSE, GameOption.LOAD_VERBOSE
GameOption.NEW_SLIDER
}), }),
MUSIC ("Music", new GameOption[] { MUSIC ("Music", new GameOption[] {
GameOption.MASTER_VOLUME, GameOption.MASTER_VOLUME,