Merge pull request #134 from Bigpet/snakeslider

Snaking sliders implementation.
This commit is contained in:
Jeffrey Han 2015-09-18 15:27:54 -04:00
commit daf3a2aad3
7 changed files with 203 additions and 104 deletions

View File

@ -907,7 +907,7 @@ public class GameData {
float oldColorAlpha = hitResult.color.a; float oldColorAlpha = hitResult.color.a;
Colors.WHITE_FADE.a = alpha; Colors.WHITE_FADE.a = alpha;
hitResult.color.a = alpha; hitResult.color.a = alpha;
hitResult.curve.draw(hitResult.color); hitResult.curve.draw(hitResult.color,1.0f);
Colors.WHITE_FADE.a = oldWhiteAlpha; Colors.WHITE_FADE.a = oldWhiteAlpha;
hitResult.color.a = oldColorAlpha; hitResult.color.a = oldColorAlpha;
} }

View File

@ -417,6 +417,7 @@ public class Options {
BACKGROUND_DIM ("Background Dim", "DimLevel", "Percentage to dim the background image during gameplay.", 50, 0, 100), BACKGROUND_DIM ("Background Dim", "DimLevel", "Percentage to dim the background image during gameplay.", 50, 0, 100),
FORCE_DEFAULT_PLAYFIELD ("Force Default Playfield", "ForceDefaultPlayfield", "Override the song background with the default playfield background.", false), FORCE_DEFAULT_PLAYFIELD ("Force Default Playfield", "ForceDefaultPlayfield", "Override the song background with the default playfield background.", false),
IGNORE_BEATMAP_SKINS ("Ignore All Beatmap Skins", "IgnoreBeatmapSkins", "Never use skin element overrides provided by beatmaps.", false), IGNORE_BEATMAP_SKINS ("Ignore All Beatmap Skins", "IgnoreBeatmapSkins", "Never use skin element overrides provided by beatmaps.", false),
SNAKING_SLIDERS ("Snaking sliders", "SnakingSliders", "Sliders gradually snake out from their starting point.", true),
SHOW_HIT_LIGHTING ("Show Hit Lighting", "HitLighting", "Adds an effect behind hit explosions.", true), SHOW_HIT_LIGHTING ("Show Hit Lighting", "HitLighting", "Adds an effect behind hit explosions.", true),
SHOW_COMBO_BURSTS ("Show Combo Bursts", "ComboBurst", "A character image is displayed at combo milestones.", true), SHOW_COMBO_BURSTS ("Show Combo Bursts", "ComboBurst", "A character image is displayed at combo milestones.", true),
SHOW_PERFECT_HIT ("Show Perfect Hits", "PerfectHit", "Whether to show perfect hit result bursts (300s, slider ticks).", true), SHOW_PERFECT_HIT ("Show Perfect Hits", "PerfectHit", "Whether to show perfect hit result bursts (300s, slider ticks).", true),
@ -941,6 +942,12 @@ public class Options {
*/ */
public static boolean isBeatmapSkinIgnored() { return GameOption.IGNORE_BEATMAP_SKINS.getBooleanValue(); } public static boolean isBeatmapSkinIgnored() { return GameOption.IGNORE_BEATMAP_SKINS.getBooleanValue(); }
/**
* Returns whether or not sliders should snake in or just appear fully at once.
* @return true if sliders should snake in
*/
public static boolean isSliderSnaking() { return GameOption.SNAKING_SLIDERS.getBooleanValue(); }
/** /**
* Returns the fixed circle size override, if any. * Returns the fixed circle size override, if any.
* @return the CS value (0, 10], 0f if disabled * @return the CS value (0, 10], 0f if disabled

View File

@ -30,6 +30,7 @@ import itdelatrisu.opsu.objects.curves.Curve;
import itdelatrisu.opsu.objects.curves.Vec2f; import itdelatrisu.opsu.objects.curves.Vec2f;
import itdelatrisu.opsu.states.Game; import itdelatrisu.opsu.states.Game;
import itdelatrisu.opsu.ui.Colors; import itdelatrisu.opsu.ui.Colors;
import itdelatrisu.opsu.ui.animations.AnimationEquation;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer; import org.newdawn.slick.GameContainer;
@ -179,20 +180,27 @@ public class Slider implements GameObject {
float approachScale = 1 + scale * 3; float approachScale = 1 + scale * 3;
float fadeinScale = (timeDiff - approachTime + fadeInTime) / (float) fadeInTime; float fadeinScale = (timeDiff - approachTime + fadeInTime) / (float) fadeInTime;
float alpha = Utils.clamp(1 - fadeinScale, 0, 1); float alpha = Utils.clamp(1 - fadeinScale, 0, 1);
float decorationsAlpha = Utils.clamp(-2.0f * fadeinScale, 0, 1);
boolean overlayAboveNumber = Options.getSkin().isHitCircleOverlayAboveNumber(); boolean overlayAboveNumber = Options.getSkin().isHitCircleOverlayAboveNumber();
float oldAlpha = Colors.WHITE_FADE.a; float oldAlpha = Colors.WHITE_FADE.a;
Colors.WHITE_FADE.a = color.a = alpha; Colors.WHITE_FADE.a = color.a = alpha;
Image hitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage(); Image hitCircleOverlay = GameImage.HITCIRCLE_OVERLAY.getImage();
Image hitCircle = GameImage.HITCIRCLE.getImage(); Image hitCircle = GameImage.HITCIRCLE.getImage();
Vec2f endPos = curve.pointAt(1); Vec2f endPos = curve.pointAt(1);
curve.draw(color); float curveInterval;
if(Options.isSliderSnaking()){
curveInterval = alpha;
} else {
curveInterval = 1.0f;
}
curve.draw(color,curveInterval);
color.a = alpha; color.a = alpha;
// end circle // end circle
hitCircle.drawCentered(endPos.x, endPos.y, color); Vec2f endCircPos = curve.pointAt(curveInterval);
hitCircleOverlay.drawCentered(endPos.x, endPos.y, Colors.WHITE_FADE); hitCircle.drawCentered(endCircPos.x, endCircPos.y, color);
hitCircleOverlay.drawCentered(endCircPos.x, endCircPos.y, Colors.WHITE_FADE);
// start circle // start circle
hitCircle.drawCentered(x, y, color); hitCircle.drawCentered(x, y, color);
@ -201,10 +209,13 @@ public class Slider implements GameObject {
// ticks // ticks
if (ticksT != null) { if (ticksT != null) {
Image tick = GameImage.SLIDER_TICK.getImage(); float tickScale = 0.5f + 0.5f*AnimationEquation.OUT_BACK.calc(decorationsAlpha);
Image tick = GameImage.SLIDER_TICK.getImage().getScaledCopy(tickScale);
for (int i = 0; i < ticksT.length; i++) { for (int i = 0; i < ticksT.length; i++) {
Vec2f c = curve.pointAt(ticksT[i]); Vec2f c = curve.pointAt(ticksT[i]);
Colors.WHITE_FADE.a = decorationsAlpha;
tick.drawCentered(c.x , c.y , Colors.WHITE_FADE); tick.drawCentered(c.x , c.y , Colors.WHITE_FADE);
Colors.WHITE_FADE.a = alpha;
} }
} }
if (GameMod.HIDDEN.isActive()) { if (GameMod.HIDDEN.isActive()) {
@ -224,16 +235,23 @@ public class Slider implements GameObject {
hitCircleOverlay.drawCentered(x, y, Colors.WHITE_FADE); hitCircleOverlay.drawCentered(x, y, Colors.WHITE_FADE);
// repeats // repeats
if (curveInterval == 1.0f) {
for (int tcurRepeat = currentRepeats; tcurRepeat <= currentRepeats + 1; tcurRepeat++) { for (int tcurRepeat = currentRepeats; tcurRepeat <= currentRepeats + 1; tcurRepeat++) {
if (hitObject.getRepeatCount() - 1 > tcurRepeat) { if (hitObject.getRepeatCount() - 1 > tcurRepeat) {
Image arrow = GameImage.REVERSEARROW.getImage(); Image arrow = GameImage.REVERSEARROW.getImage();
if (tcurRepeat != currentRepeats) { if (tcurRepeat != currentRepeats) {
if (sliderTime == 0) if (sliderTime == 0) {
continue; continue;
}
float t = Math.max(getT(trackPosition, true), 0); float t = Math.max(getT(trackPosition, true), 0);
arrow.setAlpha((float) (t - Math.floor(t))); arrow.setAlpha((float) (t - Math.floor(t)));
} else } else {
if(Options.isSliderSnaking()){
arrow.setAlpha(decorationsAlpha);
} else {
arrow.setAlpha(1f); arrow.setAlpha(1f);
}
}
if (tcurRepeat % 2 == 0) { if (tcurRepeat % 2 == 0) {
// last circle // last circle
arrow.setRotation(curve.getEndAngle()); arrow.setRotation(curve.getEndAngle());
@ -245,6 +263,7 @@ public class Slider implements GameObject {
} }
} }
} }
}
if (timeDiff >= 0) { if (timeDiff >= 0) {
// approach circle // approach circle

View File

@ -20,6 +20,7 @@ package itdelatrisu.opsu.objects.curves;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.beatmap.HitObject; import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.render.CurveRenderState; import itdelatrisu.opsu.render.CurveRenderState;
import itdelatrisu.opsu.skins.Skin; import itdelatrisu.opsu.skins.Skin;
@ -111,28 +112,31 @@ public abstract class Curve {
public abstract Vec2f pointAt(float t); public abstract Vec2f pointAt(float t);
/** /**
* Draws the full curve to the graphics context. * Draws the curve in the range [0, t] (where the full range is [0, 1]) to the graphics context.
* @param color the color filter * @param color the color filter
* @param t set the curve interval to [0, t]
*/ */
public void draw(Color color) { public void draw(Color color, float t) {
if (curve == null) if (curve == null)
return; return;
t = Utils.clamp(t, 0.0f, 1.0f);
// peppysliders // peppysliders
if (Options.getSkin().getSliderStyle() == Skin.STYLE_PEPPYSLIDER || !mmsliderSupported) { if (Options.getSkin().getSliderStyle() == Skin.STYLE_PEPPYSLIDER || !mmsliderSupported) {
int drawUpTo = (int)(curve.length*t);
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 < drawUpTo; i++)
hitCircleOverlay.drawCentered(curve[i].x, curve[i].y, Colors.WHITE_FADE); hitCircleOverlay.drawCentered(curve[i].x, curve[i].y, Colors.WHITE_FADE);
for (int i = 0; i < curve.length; i++) for (int i = 0; i < drawUpTo; i++)
hitCircle.drawCentered(curve[i].x, curve[i].y, color); hitCircle.drawCentered(curve[i].x, curve[i].y, color);
} }
// mmsliders // mmsliders
else { else {
if (renderState == null) if (renderState == null)
renderState = new CurveRenderState(hitObject); renderState = new CurveRenderState(hitObject,curve);
renderState.draw(color, borderColor, curve); renderState.draw(color, borderColor, t);
} }
} }

View File

@ -18,9 +18,9 @@
package itdelatrisu.opsu.render; package itdelatrisu.opsu.render;
import itdelatrisu.opsu.GameImage; import itdelatrisu.opsu.GameImage;
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 itdelatrisu.opsu.ui.Colors;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
@ -59,6 +59,12 @@ public class CurveRenderState {
/** The HitObject associated with the curve to be drawn. */ /** The HitObject associated with the curve to be drawn. */
protected HitObject hitObject; protected HitObject hitObject;
/** the points along the curve to be drawn */
protected Vec2f[] curve;
/** The point to which the curve has last been rendered into the texture (as an index into {@code curve}) */
private int lastPointDrawn;
/** /**
* 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.
@ -74,11 +80,12 @@ public class CurveRenderState {
scale = (int) (circleDiameter * HitObject.getXMultiplier()); // convert from Osupixels (640x480) scale = (int) (circleDiameter * HitObject.getXMultiplier()); // convert from Osupixels (640x480)
//scale = scale * 118 / 128; //for curves exactly 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);
NewCurveStyleState.initUnitCone();
} }
/** /**
* Undo the static state. Static state setup caused by calls to * Undo the static state. Static state setup caused by calls to
* {@link #draw(org.newdawn.slick.Color, org.newdawn.slick.Color, itdelatrisu.opsu.objects.curves.Vec2f[])} * {@link #draw(org.newdawn.slick.Color, org.newdawn.slick.Color, float)}
* are undone. * are undone.
*/ */
public static void shutdown() { public static void shutdown() {
@ -89,10 +96,12 @@ public class CurveRenderState {
/** /**
* 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 unique ID * @param hitObject the HitObject that represents this curve, just used as a unique ID
* @param curve the points along the curve to be drawn
*/ */
public CurveRenderState(HitObject hitObject) { public CurveRenderState(HitObject hitObject, Vec2f[] curve) {
fbo = null; fbo = null;
this.hitObject = hitObject; this.hitObject = hitObject;
this.curve = curve;
} }
/** /**
@ -101,9 +110,10 @@ public class CurveRenderState {
* runs it just draws the cached copy to the screen. * runs it just draws the cached copy to the screen.
* @param color tint of the curve * @param color tint of the curve
* @param borderColor the curve border color * @param borderColor the curve border color
* @param curve the points along the curve to be drawn * @param t the point up to which the curve should be drawn (in the interval [0, 1])
*/ */
public void draw(Color color, Color borderColor, Vec2f[] curve) { public void draw(Color color, Color borderColor, float t) {
t = Utils.clamp(t, 0.0f, 1.0f);
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
@ -113,26 +123,40 @@ public class CurveRenderState {
if (mapping == null) if (mapping == null)
mapping = cache.insert(hitObject); mapping = cache.insert(hitObject);
fbo = mapping; fbo = mapping;
createVertexBuffer(fbo.getVbo());
//write impossible value to make sure the fbo is cleared
lastPointDrawn = -1;
}
int drawUpTo = (int) (t * curve.length);
if (lastPointDrawn != drawUpTo) {
if (drawUpTo == lastPointDrawn) {
return;
}
int oldFb = GL11.glGetInteger(EXTFramebufferObject.GL_FRAMEBUFFER_BINDING_EXT); int oldFb = GL11.glGetInteger(EXTFramebufferObject.GL_FRAMEBUFFER_BINDING_EXT);
int oldTex = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D); int oldTex = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
//glGetInteger requires a buffer of size 16, even though just 4 //glGetInteger requires a buffer of size 16, even though just 4
//values are returned in this specific case //values are returned in this specific case
IntBuffer oldViewport = BufferUtils.createIntBuffer(16); IntBuffer oldViewport = BufferUtils.createIntBuffer(16);
GL11.glGetInteger(GL11.GL_VIEWPORT, oldViewport); GL11.glGetInteger(GL11.GL_VIEWPORT, oldViewport);
EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, fbo.getID()); EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, fbo.getID());
GL11.glViewport(0, 0, fbo.width, fbo.height); GL11.glViewport(0, 0, fbo.width, fbo.height);
if (lastPointDrawn <= 0 || lastPointDrawn > drawUpTo) {
lastPointDrawn = 0;
GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
Colors.WHITE_FADE.a = 1.0f; }
this.draw_curve(color, borderColor, curve);
this.renderCurve(color, borderColor, lastPointDrawn, drawUpTo);
lastPointDrawn = drawUpTo;
color.a = 1f; color.a = 1f;
GL11.glBindTexture(GL11.GL_TEXTURE_2D, oldTex); GL11.glBindTexture(GL11.GL_TEXTURE_2D, oldTex);
EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, oldFb); EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, oldFb);
GL11.glViewport(oldViewport.get(0), oldViewport.get(1), oldViewport.get(2), oldViewport.get(3)); GL11.glViewport(oldViewport.get(0), oldViewport.get(1), oldViewport.get(2), oldViewport.get(3));
Colors.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
@ -180,7 +204,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.
*/ */
private RenderState startRender() { private RenderState saveRenderState() {
RenderState state = new RenderState(); RenderState state = new RenderState();
state.smoothedPoly = GL11.glGetBoolean(GL11.GL_POLYGON_SMOOTH); state.smoothedPoly = GL11.glGetBoolean(GL11.GL_POLYGON_SMOOTH);
state.blendEnabled = GL11.glGetBoolean(GL11.GL_BLEND); state.blendEnabled = GL11.glGetBoolean(GL11.GL_BLEND);
@ -219,7 +243,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 restoreRenderState(RenderState state) {
GL11.glMatrixMode(GL11.GL_PROJECTION); GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glPopMatrix(); GL11.glPopMatrix();
GL11.glMatrixMode(GL11.GL_MODELVIEW); GL11.glMatrixMode(GL11.GL_MODELVIEW);
@ -241,24 +265,18 @@ public class CurveRenderState {
} }
/** /**
* Do the actual drawing of the curve into the currently bound framebuffer. * Write the vertices and (with position and texture coordinates) for the full
* @param color the color of the curve * curve into the OpenGL buffer with the ID specified by {@code bufferID}
* @param borderColor the curve border color * @param bufferID the buffer ID for the OpenGL buffer the vertices should be written into
* @param curve the points along the curve
*/ */
private void draw_curve(Color color, Color borderColor, Vec2f[] curve) { private void createVertexBuffer(int bufferID)
staticState.initGradient(); {
RenderState state = startRender(); int arrayBufferBinding = GL11.glGetInteger(GL15.GL_ARRAY_BUFFER_BINDING);
int vtx_buf;
// 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();
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){ fillCone(buff, x, y);
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;
float last_y = curve[i - 1].y; float last_y = curve[i - 1].y;
@ -266,12 +284,25 @@ public class CurveRenderState {
double diff_y = y - last_y; double diff_y = y - last_y;
x = (float) (x - diff_x / 2); x = (float) (x - diff_x / 2);
y = (float) (y - diff_y / 2); y = (float) (y - diff_y / 2);
fillCone(buff, x, y, NewCurveStyleState.DIVIDES); fillCone(buff, x, y);
} }
} }
buff.flip(); buff.flip();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vtx_buf); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferID);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, buff, GL15.GL_STATIC_DRAW); GL15.glBufferData(GL15.GL_ARRAY_BUFFER, buff, GL15.GL_STATIC_DRAW);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, arrayBufferBinding);
}
/**
* Do the actual drawing of the curve into the currently bound framebuffer.
* @param color the color of the curve
* @param borderColor the curve border color
*/
private void renderCurve(Color color, Color borderColor, int from, int to) {
staticState.initGradient();
RenderState state = saveRenderState();
staticState.initShaderProgram();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, fbo.getVbo());
GL20.glUseProgram(staticState.program); GL20.glUseProgram(staticState.program);
GL20.glEnableVertexAttribArray(staticState.attribLoc); GL20.glEnableVertexAttribArray(staticState.attribLoc);
GL20.glEnableVertexAttribArray(staticState.texCoordLoc); GL20.glEnableVertexAttribArray(staticState.texCoordLoc);
@ -282,63 +313,37 @@ 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 = from*2; i < to * 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);
GL11.glFlush();
GL20.glDisableVertexAttribArray(staticState.texCoordLoc); GL20.glDisableVertexAttribArray(staticState.texCoordLoc);
GL20.glDisableVertexAttribArray(staticState.attribLoc); GL20.glDisableVertexAttribArray(staticState.attribLoc);
GL15.glDeleteBuffers(vtx_buf); restoreRenderState(state);
endRender(state);
} }
/** /**
* 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 * 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
* @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) { protected void fillCone(FloatBuffer buff, float x1, float y1) {
float divx = containerWidth / 2.0f; float divx = containerWidth / 2.0f;
float divy = containerHeight / 2.0f; float divy = containerHeight / 2.0f;
float offx = -1.0f; float offx = -1.0f;
float offy = 1.0f; float offy = 1.0f;
float x, y;
float radius = scale / 2; float radius = scale / 2;
buff.put(1.0f);
buff.put(0.5f); for(int i = 0; i<NewCurveStyleState.unitCone.length/6; ++i)
//GL11.glTexCoord2d(1.0, 0.5); {
x = offx + x1 / divx; buff.put(NewCurveStyleState.unitCone[i*6+0]);
y = offy - y1 / divy; buff.put(NewCurveStyleState.unitCone[i*6+1]);
buff.put(x); buff.put(offx + (x1 + radius * NewCurveStyleState.unitCone[i*6+2])/divx);
buff.put(y); buff.put(offy - (y1 + radius * NewCurveStyleState.unitCone[i*6+3])/divy);
buff.put(0f); buff.put(NewCurveStyleState.unitCone[i*6+4]);
buff.put(1f); buff.put(NewCurveStyleState.unitCone[i*6+5]);
//GL11.glVertex4f(x, y, 0.0f, 1.0f);
for (int j = 0; j < DIVIDES; ++j) {
double phase = j * (float) Math.PI * 2 / DIVIDES;
buff.put(0.0f);
buff.put(0.5f);
//GL11.glTexCoord2d(0.0, 0.5);
x = (x1 + radius * (float) Math.sin(phase)) / divx;
y = (y1 + radius * (float) Math.cos(phase)) / divy;
buff.put((offx + x));
buff.put((offy - y));
buff.put(1f);
buff.put(1f);
//GL11.glVertex4f(x + 90 * (float) Math.sin(phase), y + 90 * (float) Math.cos(phase), 1.0f, 1.0f);
} }
buff.put(0.0f);
buff.put(0.5f);
//GL11.glTexCoord2d(0.0, 0.5);
x = (x1 + radius * (float) Math.sin(0.0)) / divx;
y = (y1 + radius * (float) Math.cos(0.0)) / divy;
buff.put((offx + x));
buff.put((offy - y));
buff.put(1f);
buff.put(1f);
//GL11.glVertex4f(x + 90 * (float) Math.sin(0.0), y + 90 * (float) Math.cos(0.0), 1.0f, 1.0f);
} }
/** /**
@ -354,6 +359,13 @@ public class CurveRenderState {
*/ */
protected static final int DIVIDES = 30; protected static final int DIVIDES = 30;
/**
* Array to hold the dummy vertex data (texture coordinates and position)
* of a cone with DIVIDES vertices at its base, that is centered around
* (0,0) and has a radius of 1 (so that it can be translated and scaled easily).
*/
protected static float[] unitCone = new float[(DIVIDES+2)*6];
/** 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;
@ -398,6 +410,47 @@ public class CurveRenderState {
} }
} }
/**
* Write the data into {@code unitCone} if it hasn't already been initialized.
*/
public static void initUnitCone() {
int index = 0;
//check if initialization has already happened
if (unitCone[0] == 0.0f) {
//tip of the cone
//vec2 texture coordinates
unitCone[index++] = 1.0f;
unitCone[index++] = 0.5f;
//vec4 position
unitCone[index++] = 0.0f;
unitCone[index++] = 0.0f;
unitCone[index++] = 0.0f;
unitCone[index++] = 1.0f;
for (int j = 0; j < NewCurveStyleState.DIVIDES; ++j) {
double phase = j * (float) Math.PI * 2 / NewCurveStyleState.DIVIDES;
//vec2 texture coordinates
unitCone[index++] = 0.0f;
unitCone[index++] = 0.5f;
//vec4 positon
unitCone[index++] = (float) Math.sin(phase);
unitCone[index++] = (float) Math.cos(phase);
unitCone[index++] = 1.0f;
unitCone[index++] = 1.0f;
}
//vec2 texture coordinates
unitCone[index++] = 0.0f;
unitCone[index++] = 0.5f;
//vec4 positon
unitCone[index++] = (float) Math.sin(0.0f);
unitCone[index++] = (float) Math.cos(0.0f);
unitCone[index++] = 1.0f;
unitCone[index++] = 1.0f;
}
}
/** /**
* 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.

View File

@ -21,6 +21,7 @@ import java.nio.ByteBuffer;
import org.lwjgl.opengl.EXTFramebufferObject; import org.lwjgl.opengl.EXTFramebufferObject;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
/** /**
* 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.
@ -31,6 +32,9 @@ public class Rendertarget {
/** The dimensions. */ /** The dimensions. */
public final int width, height; public final int width, height;
/** ID of the vertex buffer associated with this rendertarget*/
private final int vboID;
/** The FBO ID. */ /** The FBO ID. */
private final int fboID; private final int fboID;
@ -49,6 +53,7 @@ public class Rendertarget {
this.width = width; this.width = width;
this.height = height; this.height = height;
fboID = EXTFramebufferObject.glGenFramebuffersEXT(); fboID = EXTFramebufferObject.glGenFramebuffersEXT();
vboID = GL15.glGenBuffers();
textureID = GL11.glGenTextures(); textureID = GL11.glGenTextures();
depthBufferID = EXTFramebufferObject.glGenRenderbuffersEXT(); depthBufferID = EXTFramebufferObject.glGenRenderbuffersEXT();
} }
@ -60,19 +65,27 @@ public class Rendertarget {
EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, fboID); EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, fboID);
} }
/** /**
* Returns the FBO ID. * Get the ID of the VBO associated with this Rendertarget.
* @return OpenGL buffer ID for the VBO
*/
public int getVbo() {
return vboID;
}
/**
* Get the FBO ID.
* @return the OpenGL FBO ID
*/ */
// 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() { public int getID() {
return fboID; return fboID;
} }
/** /**
* Returns the texture ID. * Get the texture ID of the texture this rendertarget renders into.
* @return the OpenGL texture ID
*/ */
// NOTE: try not to use, could be moved into separate class.
public int getTextureID() { public int getTextureID() {
return textureID; return textureID;
} }
@ -89,6 +102,7 @@ public class Rendertarget {
* and a renderbuffer that it renders the depth to. * and a renderbuffer that it renders the depth to.
* @param width the width * @param width the width
* @param height the height * @param height the height
* @return the newly created Rendertarget instance
*/ */
public static Rendertarget createRTTFramebuffer(int width, int height) { public static Rendertarget createRTTFramebuffer(int width, int height) {
int old_framebuffer = GL11.glGetInteger(EXTFramebufferObject.GL_FRAMEBUFFER_BINDING_EXT); int old_framebuffer = GL11.glGetInteger(EXTFramebufferObject.GL_FRAMEBUFFER_BINDING_EXT);
@ -122,5 +136,6 @@ public class Rendertarget {
EXTFramebufferObject.glDeleteFramebuffersEXT(fboID); EXTFramebufferObject.glDeleteFramebuffersEXT(fboID);
EXTFramebufferObject.glDeleteRenderbuffersEXT(depthBufferID); EXTFramebufferObject.glDeleteRenderbuffersEXT(depthBufferID);
GL11.glDeleteTextures(textureID); GL11.glDeleteTextures(textureID);
GL15.glDeleteBuffers(vboID);
} }
} }

View File

@ -76,6 +76,7 @@ public class OptionsMenu extends BasicGameState {
GameOption.BACKGROUND_DIM, GameOption.BACKGROUND_DIM,
GameOption.FORCE_DEFAULT_PLAYFIELD, GameOption.FORCE_DEFAULT_PLAYFIELD,
GameOption.IGNORE_BEATMAP_SKINS, GameOption.IGNORE_BEATMAP_SKINS,
GameOption.SNAKING_SLIDERS,
GameOption.SHOW_HIT_LIGHTING, GameOption.SHOW_HIT_LIGHTING,
GameOption.SHOW_COMBO_BURSTS, GameOption.SHOW_COMBO_BURSTS,
GameOption.SHOW_PERFECT_HIT, GameOption.SHOW_PERFECT_HIT,