From e8cac712fc51d507e3279f53c34510d9957d53f9 Mon Sep 17 00:00:00 2001 From: yugecin Date: Mon, 9 Jan 2017 22:42:59 +0100 Subject: [PATCH] getting rid of the slick (App)GameContainer --- src/yugecin/opsudance/OpsuDance.java | 16 +- src/yugecin/opsudance/core/Container.java | 68 ------ src/yugecin/opsudance/core/Demux.java | 29 +-- .../opsudance/core/DisplayContainer.java | 209 ++++++++++++++++++ .../core/ResolutionChangeListener.java | 24 ++ .../opsudance/kernel/OpsuDanceModule.java | 6 +- .../transitions/FadeInTransitionState.java | 4 +- .../transitions/FadeOutTransitionState.java | 4 +- .../transitions/FadeTransitionState.java | 8 +- src/yugecin/opsudance/utils/GLHelper.java | 53 +++++ 10 files changed, 315 insertions(+), 106 deletions(-) delete mode 100644 src/yugecin/opsudance/core/Container.java create mode 100644 src/yugecin/opsudance/core/DisplayContainer.java create mode 100644 src/yugecin/opsudance/core/ResolutionChangeListener.java create mode 100644 src/yugecin/opsudance/utils/GLHelper.java diff --git a/src/yugecin/opsudance/OpsuDance.java b/src/yugecin/opsudance/OpsuDance.java index 1ce075c4..3ae341ea 100644 --- a/src/yugecin/opsudance/OpsuDance.java +++ b/src/yugecin/opsudance/OpsuDance.java @@ -18,25 +18,23 @@ package yugecin.opsudance; import com.google.inject.Inject; -import org.newdawn.slick.SlickException; -import yugecin.opsudance.core.Container; -import yugecin.opsudance.core.Demux; +import org.lwjgl.LWJGLException; +import yugecin.opsudance.core.DisplayContainer; public class OpsuDance { - private final Demux stateDemultiplexer; - private final Container container; + private final DisplayContainer container; @Inject - public OpsuDance(Demux stateDemultiplexer, Container container) { - this.stateDemultiplexer = stateDemultiplexer; + public OpsuDance(DisplayContainer container) { this.container = container; } public void start() { try { - container.start(); - } catch (SlickException e) { + container.setIcons(new String[] { "icon16.png", "icon32.png" }); + container.run(); + } catch (LWJGLException e) { e.printStackTrace(); } } diff --git a/src/yugecin/opsudance/core/Container.java b/src/yugecin/opsudance/core/Container.java deleted file mode 100644 index 847e087e..00000000 --- a/src/yugecin/opsudance/core/Container.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2017 yugecin - * - * opsu!dance 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!dance 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!dance. If not, see . - */ -package yugecin.opsudance.core; - -import com.google.inject.Inject; -import org.lwjgl.opengl.Display; -import org.newdawn.slick.AppGameContainer; -import org.newdawn.slick.SlickException; - -/** - * based on itdelatrisu.opsu.Container - */ -public class Container extends AppGameContainer { - - @Inject - public Container(Demux demux) throws SlickException { - super(demux); - setShowFPS(false); - } - - @Override - public void start() throws SlickException { - try { - setup(); - getDelta(); - while (running()) - gameLoop(); - } catch (Exception e) { - } - destroy(); - } - - @Override - protected void gameLoop() throws SlickException { - int delta = getDelta(); - if (!Display.isVisible() && updateOnlyOnVisible) { - try { Thread.sleep(100); } catch (Exception e) {} - } else { - try { - updateAndRender(delta); - } catch (SlickException e) { - running = false; - return; - } - } - updateFPS(); - Display.update(); - if (Display.isCloseRequested()) { - if (game.closeRequested()) - running = false; - } - } -} diff --git a/src/yugecin/opsudance/core/Demux.java b/src/yugecin/opsudance/core/Demux.java index 464daf34..2bd1c65e 100644 --- a/src/yugecin/opsudance/core/Demux.java +++ b/src/yugecin/opsudance/core/Demux.java @@ -18,7 +18,7 @@ package yugecin.opsudance.core; import com.google.inject.Inject; -import org.newdawn.slick.*; +import org.newdawn.slick.Graphics; import yugecin.opsudance.kernel.InstanceContainer; import yugecin.opsudance.states.EmptyState; import yugecin.opsudance.states.GameState; @@ -26,7 +26,10 @@ import yugecin.opsudance.states.transitions.FadeInTransitionState; import yugecin.opsudance.states.transitions.FadeOutTransitionState; import yugecin.opsudance.states.transitions.TransitionState; -public class Demux implements Game { +/** + * state demultiplexer, sends events to current state + */ +public class Demux { private final InstanceContainer instanceContainer; @@ -40,35 +43,25 @@ public class Demux implements Game { this.instanceContainer = instanceContainer; } - @Override - public void init(GameContainer container) throws SlickException { + // cannot do this in constructor, would cause circular dependency + public void init() { + state = instanceContainer.provide(EmptyState.class); fadeOutTransitionState = instanceContainer.provide(FadeOutTransitionState.class); fadeInTransitionState = instanceContainer.provide(FadeInTransitionState.class); - state = instanceContainer.provide(EmptyState.class); - state.enter(); } - @Override - public void update(GameContainer container, int delta) throws SlickException { + public void update(int delta) { state.update(delta); } - @Override - public void render(GameContainer container, Graphics g) throws SlickException { + public void render(Graphics g) { state.render(g); } - @Override - public boolean closeRequested() { - // TODO for what is this used + public boolean onCloseRequest() { return !isTransitioning(); } - @Override - public String getTitle() { - return "opsu!dance"; - } - public boolean isTransitioning() { return state == fadeInTransitionState || state == fadeOutTransitionState; } diff --git a/src/yugecin/opsudance/core/DisplayContainer.java b/src/yugecin/opsudance/core/DisplayContainer.java new file mode 100644 index 00000000..215eea1d --- /dev/null +++ b/src/yugecin/opsudance/core/DisplayContainer.java @@ -0,0 +1,209 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 yugecin + * + * opsu!dance 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!dance 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!dance. If not, see . + */ +package yugecin.opsudance.core; + +import com.google.inject.Inject; +import org.lwjgl.LWJGLException; +import org.lwjgl.Sys; +import org.lwjgl.openal.AL; +import org.lwjgl.opengl.Display; +import org.lwjgl.opengl.DisplayMode; +import org.newdawn.slick.Graphics; +import org.newdawn.slick.opengl.ImageIOImageData; +import org.newdawn.slick.opengl.InternalTextureLoader; +import org.newdawn.slick.opengl.LoadableImageData; +import org.newdawn.slick.opengl.TGAImageData; +import org.newdawn.slick.opengl.renderer.Renderer; +import org.newdawn.slick.opengl.renderer.SGL; +import org.newdawn.slick.util.Log; +import org.newdawn.slick.util.ResourceLoader; +import yugecin.opsudance.states.EmptyRedState; +import yugecin.opsudance.utils.GLHelper; + +import java.nio.ByteBuffer; +import java.util.LinkedList; +import java.util.List; + +/** + * based on org.newdawn.slick.AppGameContainer + */ +public class DisplayContainer { + + private static SGL GL = Renderer.get(); + + private final Demux demux; + private final DisplayMode nativeDisplayMode; + private final List resolutionChangeListeners; + + private Graphics graphics; + + public int width; + public int height; + + private long lastFrame; + + @Inject + public DisplayContainer(Demux demux) { + this.demux = demux; + this.nativeDisplayMode = Display.getDisplayMode(); + this.resolutionChangeListeners = new LinkedList<>(); + lastFrame = getTime(); + } + + public void addResolutionChangeListener(ResolutionChangeListener listener) { + resolutionChangeListeners.add(listener); + } + + public void run() throws LWJGLException { + demux.init(); + demux.switchStateNow(new EmptyRedState(null, null)); + setup(); + while(!(Display.isCloseRequested() && demux.onCloseRequest())) { + // TODO: lower fps when not visible Display.isVisible + int delta = getDelta(); + GL.glClear(SGL.GL_COLOR_BUFFER_BIT); + /* + graphics.resetTransform(); + graphics.resetFont(); + graphics.resetLineWidth(); + graphics.resetTransform(); + */ + demux.update(delta); + demux.render(graphics); + Display.update(true); + Display.sync(60); + } + teardown(); + } + + private void setup() { + Display.setTitle("opsu!dance"); + try { + // temp displaymode to not flash the screen with a 1ms black window + Display.setDisplayMode(new DisplayMode(100, 100)); + Display.create(); + setDisplayMode(640, 480, false); + } catch (LWJGLException e) { + e.printStackTrace(); + // TODO errorhandler dialog here + Log.error("could not initialize GL", e); + } + } + + private void teardown() { + Display.destroy(); + AL.destroy(); + } + + public void setDisplayMode(int width, int height, boolean fullscreen) throws LWJGLException { + if (this.width == width && this.height == height) { + Display.setFullscreen(fullscreen); + return; + } + + DisplayMode displayMode = null; + if (fullscreen) { + displayMode = GLHelper.findFullscreenDisplayMode(nativeDisplayMode.getBitsPerPixel(), nativeDisplayMode.getFrequency(), width, height); + } + + if (displayMode == null) { + displayMode = new DisplayMode(width,height); + if (fullscreen) { + fullscreen = false; + Log.warn("could not find fullscreen displaymode for " + width + "x" + height); + } + } + + this.width = displayMode.getWidth(); + this.height = displayMode.getHeight(); + + Display.setDisplayMode(displayMode); + Display.setFullscreen(fullscreen); + + initGL(); + + for (ResolutionChangeListener resolutionChangeListener : resolutionChangeListeners) { + resolutionChangeListener.onDisplayResolutionChanged(width, height); + } + + if (displayMode.getBitsPerPixel() == 16) { + InternalTextureLoader.get().set16BitMode(); + } + + getDelta(); + } + + private void initGL() { + GL.initDisplay(width, height); + GL.enterOrtho(width, height); + + graphics = new Graphics(width, height); + graphics.setAntiAlias(false); + + /* + if (input == null) { + input = new Input(height); + } + input.init(height); + // no need to remove listeners? + //input.removeAllListeners(); + if (game instanceof InputListener) { + input.removeListener((InputListener) game); + input.addListener((InputListener) game); + } + */ + + } + + private int getDelta() { + long time = getTime(); + int delta = (int) (time - lastFrame); + lastFrame = time; + return delta; + } + + public long getTime() { + return (Sys.getTime() * 1000) / Sys.getTimerResolution(); + } + + public void setIcons(String[] refs) { + ByteBuffer[] bufs = new ByteBuffer[refs.length]; + + for (int i = 0; i < refs.length; i++) { + LoadableImageData data; + boolean flip = true; + + if (refs[i].endsWith(".tga")) { + data = new TGAImageData(); + } else { + flip = false; + data = new ImageIOImageData(); + } + + try { + bufs[i] = data.loadImage(ResourceLoader.getResourceAsStream(refs[i]), flip, false, null); + } catch (Exception e) { + Log.error("failed to set the icon", e); + return; + } + } + + Display.setIcon(bufs); + } + +} diff --git a/src/yugecin/opsudance/core/ResolutionChangeListener.java b/src/yugecin/opsudance/core/ResolutionChangeListener.java new file mode 100644 index 00000000..9c9b30f8 --- /dev/null +++ b/src/yugecin/opsudance/core/ResolutionChangeListener.java @@ -0,0 +1,24 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 yugecin + * + * opsu!dance 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!dance 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!dance. If not, see . + */ +package yugecin.opsudance.core; + +public interface ResolutionChangeListener { + + void onDisplayResolutionChanged(int width, int height); + +} diff --git a/src/yugecin/opsudance/kernel/OpsuDanceModule.java b/src/yugecin/opsudance/kernel/OpsuDanceModule.java index 71a473b8..c7962d66 100644 --- a/src/yugecin/opsudance/kernel/OpsuDanceModule.java +++ b/src/yugecin/opsudance/kernel/OpsuDanceModule.java @@ -19,7 +19,7 @@ package yugecin.opsudance.kernel; import com.google.inject.AbstractModule; import yugecin.opsudance.PreStartupInitializer; -import yugecin.opsudance.core.Container; +import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.core.Demux; import yugecin.opsudance.states.EmptyRedState; import yugecin.opsudance.states.EmptyState; @@ -31,12 +31,12 @@ public class OpsuDanceModule extends AbstractModule { protected void configure() { bind(InstanceContainer.class).to(InstanceResolver.class); bind(PreStartupInitializer.class).asEagerSingleton(); - bind(Demux.class).asEagerSingleton(); - bind(Container.class).asEagerSingleton(); + bind(DisplayContainer.class).asEagerSingleton(); bind(FadeInTransitionState.class).asEagerSingleton(); bind(FadeOutTransitionState.class).asEagerSingleton(); bind(EmptyRedState.class).asEagerSingleton(); bind(EmptyState.class).asEagerSingleton(); + bind(Demux.class).asEagerSingleton(); } } diff --git a/src/yugecin/opsudance/states/transitions/FadeInTransitionState.java b/src/yugecin/opsudance/states/transitions/FadeInTransitionState.java index d832e414..ce71cfe5 100644 --- a/src/yugecin/opsudance/states/transitions/FadeInTransitionState.java +++ b/src/yugecin/opsudance/states/transitions/FadeInTransitionState.java @@ -18,15 +18,15 @@ package yugecin.opsudance.states.transitions; import com.google.inject.Inject; -import yugecin.opsudance.core.Container; import yugecin.opsudance.core.Demux; +import yugecin.opsudance.core.DisplayContainer; public class FadeInTransitionState extends FadeTransitionState { private final Demux demux; @Inject - public FadeInTransitionState(Container container, Demux demux) { + public FadeInTransitionState(DisplayContainer container, Demux demux) { super(container, 300); this.demux = demux; } diff --git a/src/yugecin/opsudance/states/transitions/FadeOutTransitionState.java b/src/yugecin/opsudance/states/transitions/FadeOutTransitionState.java index 6d83e4d9..5303d18c 100644 --- a/src/yugecin/opsudance/states/transitions/FadeOutTransitionState.java +++ b/src/yugecin/opsudance/states/transitions/FadeOutTransitionState.java @@ -18,8 +18,8 @@ package yugecin.opsudance.states.transitions; import com.google.inject.Inject; -import yugecin.opsudance.core.Container; import yugecin.opsudance.core.Demux; +import yugecin.opsudance.core.DisplayContainer; public class FadeOutTransitionState extends FadeTransitionState { @@ -27,7 +27,7 @@ public class FadeOutTransitionState extends FadeTransitionState { private final FadeInTransitionState fadeInTransitionState; @Inject - public FadeOutTransitionState(Container container, Demux demux, FadeInTransitionState fadeInTransitionState) { + public FadeOutTransitionState(DisplayContainer container, Demux demux, FadeInTransitionState fadeInTransitionState) { super(container, 200); this.demux = demux; this.fadeInTransitionState = fadeInTransitionState; diff --git a/src/yugecin/opsudance/states/transitions/FadeTransitionState.java b/src/yugecin/opsudance/states/transitions/FadeTransitionState.java index 3870b0b3..918ed4eb 100644 --- a/src/yugecin/opsudance/states/transitions/FadeTransitionState.java +++ b/src/yugecin/opsudance/states/transitions/FadeTransitionState.java @@ -19,21 +19,21 @@ package yugecin.opsudance.states.transitions; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; -import yugecin.opsudance.core.Container; +import yugecin.opsudance.core.DisplayContainer; import yugecin.opsudance.states.GameState; public abstract class FadeTransitionState extends TransitionState { protected GameState applicableState; - private final Container container; + private final DisplayContainer container; protected final int fadeTargetTime; protected int fadeTime; private final Color black; - public FadeTransitionState(Container container, int fadeTargetTime) { + public FadeTransitionState(DisplayContainer container, int fadeTargetTime) { super(fadeTargetTime); this.container = container; this.fadeTargetTime = fadeTargetTime; @@ -58,7 +58,7 @@ public abstract class FadeTransitionState extends TransitionState { applicableState.render(g); black.a = getMaskAlphaLevel((float) fadeTime / fadeTargetTime); g.setColor(black); - g.fillRect(0, 0, container.getWidth(), container.getHeight()); + g.fillRect(0, 0, container.width, container.height); } @Override diff --git a/src/yugecin/opsudance/utils/GLHelper.java b/src/yugecin/opsudance/utils/GLHelper.java new file mode 100644 index 00000000..e0c48f46 --- /dev/null +++ b/src/yugecin/opsudance/utils/GLHelper.java @@ -0,0 +1,53 @@ +/* + * opsu!dance - fork of opsu! with cursordance auto + * Copyright (C) 2017 yugecin + * + * opsu!dance 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!dance 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!dance. If not, see . + */ +package yugecin.opsudance.utils; + +import org.lwjgl.LWJGLException; +import org.lwjgl.opengl.Display; +import org.lwjgl.opengl.DisplayMode; + +public class GLHelper { + + /** + * from org.newdawn.slick.AppGameContainer#setDisplayMode + */ + public static DisplayMode findFullscreenDisplayMode(int targetBPP, int targetFrequency, int width, int height) throws LWJGLException { + DisplayMode[] modes = Display.getAvailableDisplayModes(); + DisplayMode foundMode = null; + int freq = 0; + int bpp = 0; + + for (DisplayMode current : modes) { + if (current.getWidth() != width || current.getHeight() != height) { + continue; + } + + if (current.getBitsPerPixel() == targetBPP && current.getFrequency() == targetFrequency) { + return current; + } + + if (current.getFrequency() >= freq && (foundMode == null || current.getBitsPerPixel() >= bpp)) { + foundMode = current; + freq = foundMode.getFrequency(); + bpp = foundMode.getBitsPerPixel(); + } + } + return foundMode; + } + +}