/*
* 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.lwjgl.opengl.GL11;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.opengl.InternalTextureLoader;
import org.newdawn.slick.opengl.renderer.Renderer;
import org.newdawn.slick.opengl.renderer.SGL;
import org.newdawn.slick.util.Log;
import yugecin.opsudance.core.state.OpsuState;
import yugecin.opsudance.core.state.transitions.EmptyTransitionState;
import yugecin.opsudance.core.state.transitions.TransitionState;
import yugecin.opsudance.errorhandling.ErrorDumpable;
import yugecin.opsudance.utils.GLHelper;
import java.io.StringWriter;
import java.util.LinkedList;
import java.util.List;
import static yugecin.opsudance.kernel.Entrypoint.sout;
/**
* based on org.newdawn.slick.AppGameContainer
*/
public class DisplayContainer implements ErrorDumpable {
private static SGL GL = Renderer.get();
private final Demux demux;
private final DisplayMode nativeDisplayMode;
private final List resolutionChangeListeners;
private Graphics graphics;
private Input input;
public int width;
public int height;
public int targetRenderInterval;
public int targetBackgroundRenderInterval;
public int realRenderInterval;
public int delta;
public int timeSinceLastRender;
private long lastFrame;
private String glVersion;
private String glVendor;
@Inject
public DisplayContainer(Demux demux) {
this.demux = demux;
this.nativeDisplayMode = Display.getDisplayMode();
this.resolutionChangeListeners = new LinkedList<>();
targetRenderInterval = 16; // ~60 fps
targetBackgroundRenderInterval = 41; // ~24 fps
lastFrame = getTime();
}
public void addResolutionChangeListener(ResolutionChangeListener listener) {
resolutionChangeListeners.add(listener);
}
public void switchState(Class extends OpsuState> newState) {
demux.switchState(newState);
}
public void switchStateNow(Class extends OpsuState> newState) {
demux.switchState(newState, EmptyTransitionState.class, 0, EmptyTransitionState.class, 0);
}
public void switchState(Class extends OpsuState> newState, Class extends TransitionState> outTransition, int outTime, Class extends TransitionState> inTransition, int inTime) {
demux.switchState(newState, outTransition, outTime, inTransition, inTime);
}
public void run() throws LWJGLException {
while(!(Display.isCloseRequested() && demux.onCloseRequest())) {
delta = getDelta();
timeSinceLastRender += delta;
input.poll(width, height);
demux.update(delta);
int maxRenderInterval;
if (Display.isVisible() && Display.isActive()) {
maxRenderInterval = targetRenderInterval;
} else {
maxRenderInterval = targetBackgroundRenderInterval;
}
if (timeSinceLastRender >= maxRenderInterval) {
GL.glClear(SGL.GL_COLOR_BUFFER_BIT);
/*
graphics.resetTransform();
graphics.resetFont();
graphics.resetLineWidth();
graphics.resetTransform();
*/
demux.preRenderUpdate(timeSinceLastRender);
demux.render(graphics);
realRenderInterval = timeSinceLastRender;
timeSinceLastRender = 0;
Display.update(false);
}
Display.processMessages();
Display.sync(1000); // TODO add option to change this, to not eat CPUs
}
teardown();
}
public void setup() throws LWJGLException {
width = height = -1;
Input.disableControllers();
Display.setTitle("opsu!dance");
// temp displaymode to not flash the screen with a 1ms black window
Display.setDisplayMode(new DisplayMode(100, 100));
Display.create();
GLHelper.setIcons(new String[] { "icon16.png", "icon32.png" });
setDisplayMode(640, 480, false);
sout("GL ready");
glVersion = GL11.glGetString(GL11.GL_VERSION);
glVendor = GL11.glGetString(GL11.GL_VENDOR);
}
public 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();
}
}
private void initGL() {
GL.initDisplay(width, height);
GL.enterOrtho(width, height);
graphics = new Graphics(width, height);
graphics.setAntiAlias(false);
input = new Input(height);
input.addKeyListener(demux);
input.addMouseListener(demux);
}
private int getDelta() {
long time = getTime();
int delta = (int) (time - lastFrame);
lastFrame = time;
return delta;
}
public long getTime() {
return (Sys.getTime() * 1000) / Sys.getTimerResolution();
}
@Override
public void writeErrorDump(StringWriter dump) {
dump.append("> DisplayContainer dump").append('\n');
dump.append("OpenGL version: ").append(glVersion).append( "(").append(glVendor).append(")").append('\n');
}
}