From ee14ac9b2d8644fb9acae32b126959141732bb2d Mon Sep 17 00:00:00 2001 From: yugecin Date: Tue, 23 Oct 2018 00:43:10 +0200 Subject: [PATCH] attempt to make a better cursortrail for replay stuff --- src/itdelatrisu/opsu/states/Game.java | 15 +- src/yugecin/opsudance/ReplayCursor.java | 267 ++++++++++++++++++++++ src/yugecin/opsudance/ReplayPlayback.java | 17 +- 3 files changed, 288 insertions(+), 11 deletions(-) create mode 100644 src/yugecin/opsudance/ReplayCursor.java diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index 4bf15158..2f29b582 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -51,7 +51,6 @@ import java.util.*; import org.lwjgl.input.Keyboard; import org.lwjgl.opengl.Display; -import org.lwjgl.opengl.GL11; import org.newdawn.slick.Animation; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; @@ -1500,6 +1499,11 @@ public class Game extends ComplexOpsuState { } }); + if (replays != null) { + for (ReplayPlayback r : replays) { + r.cursor.destroy(); + } + } replays = new LinkedList<>(); float hueshift = 360f / files.length + 18f; float hue = 0; @@ -1533,7 +1537,7 @@ public class Game extends ComplexOpsuState { for (ReplayPlayback r : replays) { Color c = new Color(java.awt.Color.getHSBColor((hue) / 360f, 1.0f, 1.0f).getRGB()); r.color = c; - r.cursor = new Cursor(c); + r.cursor.filter = c; hue += hueshift; } @@ -1807,6 +1811,13 @@ public class Game extends ComplexOpsuState { knorkesliders = null; + if (replays != null) { + for (ReplayPlayback r : replays) { + r.cursor.destroy(); + } + replays.clear(); + } + Dancer.instance.setGameObjects(null); Cursor.lastObjColor = Color.white; diff --git a/src/yugecin/opsudance/ReplayCursor.java b/src/yugecin/opsudance/ReplayCursor.java new file mode 100644 index 00000000..e9123019 --- /dev/null +++ b/src/yugecin/opsudance/ReplayCursor.java @@ -0,0 +1,267 @@ +/* + * opsu! - an open-source osu! client + * Copyright (C) 2014, 2015 Jeffrey Han + * + * opsu! is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * opsu! is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with opsu!. If not, see . + */ + +package yugecin.opsudance; + +import java.awt.Point; +import java.nio.IntBuffer; +import java.util.Iterator; + +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.*; +import org.newdawn.slick.*; +import org.newdawn.slick.opengl.Texture; + +import itdelatrisu.opsu.render.Rendertarget; + +import static itdelatrisu.opsu.GameImage.*; +import static yugecin.opsudance.core.InstanceContainer.*; + +public class ReplayCursor +{ + private final Point lastPosition; + private TrailList trail = new TrailList(); + + public Color filter; + + private Rendertarget fbo; + + private static long nowtime; + + public ReplayCursor(Color filter) { + this.filter = filter; + this.lastPosition = new Point(width2, 0); + this.fbo = Rendertarget.createRTTFramebuffer(width, height); + } + + public void draw() + { + nowtime = System.currentTimeMillis(); + + // stuff copied from CurveRenderState and stuff, I don't know what I'm doing + int oldFb = GL11.glGetInteger(EXTFramebufferObject.GL_FRAMEBUFFER_BINDING_EXT); + int oldTex = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D); + //glGetInteger requires a buffer of size 16, even though just 4 + //values are returned in this specific case + IntBuffer oldViewport = BufferUtils.createIntBuffer(16); + GL11.glGetInteger(GL11.GL_VIEWPORT, oldViewport); + EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, fbo.getID()); + GL11.glViewport(0, 0, fbo.width, fbo.height); + // render + GL11.glClearColor(0f, 0f, 0f, 0f); + GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); + //GL14.glBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); + // render2 + + renderstuff(); + + //GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE); + GL11.glBindTexture(GL11.GL_TEXTURE_2D, oldTex); + EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, oldFb); + GL11.glViewport(oldViewport.get(0), oldViewport.get(1), oldViewport.get(2), oldViewport.get(3)); + + //GL14.glBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE); + GL11.glEnable(GL11.GL_TEXTURE_2D); + GL11.glDisable(GL11.GL_TEXTURE_1D); + GL11.glBindTexture(GL11.GL_TEXTURE_2D, fbo.getTextureID()); + GL11.glBegin(GL11.GL_QUADS); + GL11.glColor4f(1f, 1f, 1f, 1f); + GL11.glTexCoord2f(1f, 1f); + GL11.glVertex2i(fbo.width, 0); + GL11.glTexCoord2f(0f, 1f); + GL11.glVertex2i(0, 0); + GL11.glTexCoord2f(0f, 0f); + GL11.glVertex2i(0, fbo.height); + GL11.glTexCoord2f(1f, 0f); + GL11.glVertex2i(fbo.width, fbo.height); + GL11.glEnd(); + GL14.glBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); + + CURSOR.getImage().drawCentered(lastPosition.x, lastPosition.y, filter); + CURSOR_MIDDLE.getImage().drawCentered(lastPosition.x, lastPosition.y, filter); + } + + private void renderstuff() + { + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, fbo.getVbo()); + GL11.glDisable(GL11.GL_TEXTURE_2D); + GL11.glEnable(GL11.GL_TEXTURE_1D); + + final Image img = CURSOR_TRAIL.getImage(); + final Texture txt = img.getTexture(); + Color.white.bind(); + txt.bind(); + + float alpha = 0f; + float alphaIncrease = .5f / trail.size; + float trailwidth2 = img.getWidth() / 2f; + float trailheight2 = img.getHeight() / 2f; + float txtwidth = txt.getWidth(); + float txtheight = txt.getHeight(); + GL11.glBegin(GL11.GL_QUADS); + final Point lastpoint = new Point(-1, -1); + for (Trailpart p : trail) { + alpha += alphaIncrease; + GL11.glColor4f(filter.r, filter.g, filter.b, alpha); + GL11.glTexCoord2f(0f, 0f); + GL11.glVertex3f(p.x - trailwidth2, p.y - trailheight2, 0f); + GL11.glTexCoord2f(txtwidth, 0); + GL11.glVertex3f(p.x + trailwidth2, p.y - trailheight2, 0f); + GL11.glTexCoord2f(txtwidth, txtheight); + GL11.glVertex3f(p.x + trailwidth2, p.y + trailheight2, 0f); + GL11.glTexCoord2f(0f, txtheight); + GL11.glVertex3f(p.x - trailwidth2, p.y + trailheight2, 0f); + lastpoint.x = p.x; + lastpoint.y = p.y; + } + GL11.glEnd(); + } + + /** + * Sets the cursor position to given point and updates trail. + * @param mouseX x coordinate to set position to + * @param mouseY y coordinate to set position to + */ + public void setCursorPosition(int delta, int mouseX, int mouseY) { + nowtime = System.currentTimeMillis(); + + if ((lastPosition.x == 0 && lastPosition.y == 0) || !addCursorPoints(lastPosition.x, lastPosition.y, mouseX, mouseY)) { + //trail.add(new Trailpart(mouseX, mouseY)); + } + lastPosition.move(mouseX, mouseY); + + int removecount = 0; + TrailNode newfirst = trail.first; + while (newfirst != null && newfirst.value.time < nowtime - 400) { + newfirst = newfirst.next; + removecount++; + } + trail.first = newfirst; + if (newfirst == null) { + trail.last = null; + } + trail.size -= removecount; + } + + /** + * Adds all points between (x1, y1) and (x2, y2) to the cursor point lists. + * @author http://rosettacode.org/wiki/Bitmap/Bresenham's_line_algorithm#Java + */ + private boolean addCursorPoints(int x1, int y1, int x2, int y2) { + int size = trail.size; + // delta of exact value and rounded value of the dependent variable + int d = 0; + int dy = Math.abs(y2 - y1); + int dx = Math.abs(x2 - x1); + + int dy2 = (dy << 1); // slope scaling factors to avoid floating + int dx2 = (dx << 1); // point + int ix = x1 < x2 ? 1 : -1; // increment direction + int iy = y1 < y2 ? 1 : -1; + + if (dy <= dx) { + for (;;) { + if (x1 == x2) + break; + trail.add(new Trailpart(x1, y1)); + x1 += ix; + d += dy2; + if (d > dx) { + y1 += iy; + d -= dx2; + } + } + } else { + for (;;) { + if (y1 == y2) + break; + trail.add(new Trailpart(x1, y1)); + y1 += iy; + d += dx2; + if (d > dy) { + x1 += ix; + d -= dy2; + } + } + } + return trail.size != size; + } + + public void destroy() + { + this.fbo.destroyRTT(); + } + + private static class TrailList implements Iterable + { + TrailNode first; + TrailNode last; + int size; + + public void add(Trailpart t) + { + if (last == null) { + last = first = new TrailNode(t); + } else { + TrailNode n = new TrailNode(t); + last.next = n; + last = n; + } + size++; + } + + @Override + public Iterator iterator() + { + return new Iterator() { + TrailNode node = first; + @Override + public boolean hasNext() { + return node != null; + } + + @Override + public Trailpart next() { + Trailpart v = node.value; + node = node.next; + return v; + } + }; + } + } + + private static class TrailNode + { + TrailNode next; + Trailpart value; + TrailNode(Trailpart t) { + value = t; + } + } + + private static class Trailpart { + int x, y; + long time; + Trailpart(int x, int y) { + this.x = x; + this.y = y; + this.time = nowtime; + } + } +} diff --git a/src/yugecin/opsudance/ReplayPlayback.java b/src/yugecin/opsudance/ReplayPlayback.java index 7e6147bc..6374e449 100644 --- a/src/yugecin/opsudance/ReplayPlayback.java +++ b/src/yugecin/opsudance/ReplayPlayback.java @@ -20,7 +20,6 @@ package yugecin.opsudance; import itdelatrisu.opsu.GameData; import itdelatrisu.opsu.replay.Replay; import itdelatrisu.opsu.replay.ReplayFrame; -import itdelatrisu.opsu.ui.Cursor; import itdelatrisu.opsu.ui.Fonts; import itdelatrisu.opsu.ui.animations.AnimationEquation; import org.newdawn.slick.Color; @@ -48,7 +47,7 @@ public class ReplayPlayback { private int frameIndex; public Color color; public Color originalcolor; - public Cursor cursor; + public final ReplayCursor cursor; private int keydelay[]; public static final int SQSIZE = 15; public static final int UNITHEIGHT = SQSIZE + 5; @@ -80,7 +79,7 @@ public class ReplayPlayback { this.originalcolor = new Color(color); Color cursorcolor = new Color(color); //cursorcolor.a = 0.5f; - cursor = new Cursor(cursorcolor); + cursor = new ReplayCursor(cursorcolor); keydelay = new int[4]; this.player = replay.playerName; this.playerwidth = Fonts.SMALLBOLD.getWidth(this.player); @@ -299,7 +298,7 @@ public class ReplayPlayback { y = height - y; } cursor.setCursorPosition(renderdelta, currentFrame.getScaledX(), y); - cursor.draw(false); + cursor.draw(); } private void processKeys() { @@ -322,11 +321,11 @@ public class ReplayPlayback { public static class HitData { int combobreaktime = -1; - LinkedList time300 = new LinkedList(); - LinkedList time100 = new LinkedList(); - LinkedList time50 = new LinkedList(); - LinkedList acc = new LinkedList(); - LinkedList combo = new LinkedList(); + LinkedList time300 = new LinkedList<>(); + LinkedList time100 = new LinkedList<>(); + LinkedList time50 = new LinkedList<>(); + LinkedList acc = new LinkedList<>(); + LinkedList combo = new LinkedList<>(); public HitData(File file) { try (InputStream in = new FileInputStream(file)) {