/*
* This file is part of the Illarion project.
*
* Copyright © 2015 - Illarion e.V.
*
* Illarion is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Illarion 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.
*/
package org.illarion.engine.backend.gdx;
import com.badlogic.gdx.Files.FileType;
import com.badlogic.gdx.Graphics.DisplayMode;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import org.illarion.engine.DesktopGameContainer;
import org.illarion.engine.GameListener;
import org.illarion.engine.MouseCursor;
import org.illarion.engine.graphic.GraphicResolution;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* The game container that is using the libGDX backend to handle the game.
*
* @author Martin Karing <nitram@illarion.org>
*/
public class ApplicationGameContainer implements DesktopGameContainer {
/**
* The configuration used to create the application.
*/
@Nonnull
private final LwjglApplicationConfiguration config;
/**
* The game listener that receives the updates regarding the game.
*/
private final GameListener gameListener;
/**
* The libGDX application that contains the game.
*/
@Nullable
private GdxLwjglApplication gdxApplication;
/**
* The graphic resolutions that can be applied to the game.
*/
@Nullable
private GraphicResolution[] graphicResolutions;
/**
* Get the engine that drives the game in this container.
*/
@Nullable
private GdxEngine engine;
/**
* The count of render calls that were performed during the rendering of the last frame.
*/
private int lastFrameRenderCalls;
/**
* The width of the application in windowed mode.
*/
private int windowWidth;
/**
* The height of the application in windowed mode.
*/
private int windowHeight;
/**
* The graphic resolution that applied in full screen mode.
*/
@Nullable
private GraphicResolution fullScreenResolution;
/**
* Create a new desktop game that is drawn using libGDX.
*
* @param gameListener the game listener that receives the updates regarding the game
* @param width the width of the game container
* @param height the height of the game container
* @param fullScreen the full screen flag of the container
* @throws GdxEngineException in case the initialization goes wrong
*/
public ApplicationGameContainer(
GameListener gameListener, int width, int height, boolean fullScreen) throws GdxEngineException {
this.gameListener = gameListener;
config = new LwjglApplicationConfiguration();
config.forceExit = false;
config.vSyncEnabled = true;
config.backgroundFPS = 10;
config.foregroundFPS = 60;
config.useGL30 = false;
config.depth = 0;
windowHeight = height;
windowWidth = width;
fullScreenResolution = getFittingFullScreenResolution(width, height);
if (fullScreen) {
config.height = fullScreenResolution.getHeight();
config.width = fullScreenResolution.getWidth();
} else {
config.height = height;
config.width = width;
}
config.fullscreen = fullScreen;
}
@Nonnull
private GraphicResolution getFittingFullScreenResolution(int width, int height) {
GraphicResolution[] resolutions = getFullScreenResolutions();
int freq = 0;
@Nullable GraphicResolution targetDisplayMode = null;
for (@Nonnull GraphicResolution current : resolutions) {
if ((current.getWidth() == width) && (current.getHeight() == height)) {
if ((targetDisplayMode == null) ||
((current.getRefreshRate() >= freq) && (current.getBPP() > targetDisplayMode.getBPP()))) {
targetDisplayMode = current;
freq = targetDisplayMode.getRefreshRate();
}
if ((current.getBPP() == LwjglApplicationConfiguration.getDesktopDisplayMode().bitsPerPixel) &&
(current.getRefreshRate() ==
LwjglApplicationConfiguration.getDesktopDisplayMode().refreshRate)) {
targetDisplayMode = current;
break;
}
}
}
if (targetDisplayMode == null) {
DisplayMode mode = LwjglApplicationConfiguration.getDesktopDisplayMode();
return new GraphicResolution(mode.width, mode.height, mode.bitsPerPixel, mode.refreshRate);
}
return targetDisplayMode;
}
@Override
public int getHeight() {
if (gdxApplication == null) {
return config.height;
}
return gdxApplication.getGraphics().getHeight();
}
@Override
public int getWidth() {
if (gdxApplication == null) {
return config.width;
}
return gdxApplication.getGraphics().getWidth();
}
@Nonnull
@Override
public GdxEngine getEngine() {
if (engine == null) {
throw new IllegalStateException("Game is not launched yet.");
}
return engine;
}
@Override
public void setMouseCursor(@Nullable MouseCursor cursor) {
if ((engine != null) && (cursor instanceof GdxCursor)) {
engine.getGraphics().setCursor((GdxCursor) cursor);
}else if(engine != null && cursor == null){
engine.getGraphics().setCursor(null);
}
}
@Override
public void startGame() throws GdxEngineException {
gdxApplication = new GdxLwjglApplication(new ListenerApplication(gameListener, this), config);
}
@Override
public void exitGame() {
if (gdxApplication != null) {
gdxApplication.shutdownGame();
}
}
@Nonnull
@Override
public String getTitle() {
return config.title;
}
@Override
public int getFPS() {
if (gdxApplication == null) {
return 0;
}
return gdxApplication.getGraphics().getFramesPerSecond();
}
@Nonnull
@Override
public CharSequence[] getDiagnosticLines() {
return new CharSequence[]{"Render calls: " + lastFrameRenderCalls};
}
@Override
public void setTitle(@Nonnull String title) {
config.title = title;
if (gdxApplication != null) {
gdxApplication.getGraphics().setTitle(title);
}
}
@Override
public void setWindowSize(int width, int height) throws GdxEngineException {
windowWidth = width;
windowHeight = height;
if (!isFullScreen() && (gdxApplication != null)) {
gdxApplication.getGraphics().setDisplayMode(width, height, false);
}
}
@Override
public void setFullScreenResolution(@Nonnull GraphicResolution resolution) throws GdxEngineException {
fullScreenResolution = resolution;
if (isFullScreen() && (gdxApplication != null)) {
gdxApplication.getGraphics().setDisplayMode(resolution.getWidth(), resolution.getHeight(), true);
}
}
@Nonnull
@Override
public GraphicResolution[] getFullScreenResolutions() {
if (graphicResolutions == null) {
List<GraphicResolution> resultResolutions = new ArrayList<>();
DisplayMode[] displayModes;
boolean ignoreRefreshRate;
if (gdxApplication == null) {
displayModes = LwjglApplicationConfiguration.getDisplayModes();
ignoreRefreshRate = true;
} else {
displayModes = gdxApplication.getGraphics().getDisplayModes();
ignoreRefreshRate = false;
}
for (@Nullable DisplayMode mode : displayModes) {
if (mode == null) {
continue;
}
if ((mode.width < 800) || (mode.height < 600) || (mode.bitsPerPixel < 24)) {
continue;
}
if (ignoreRefreshRate) {
resultResolutions.add(new GraphicResolution(mode.width, mode.height, mode.bitsPerPixel, -1));
} else {
if (mode.refreshRate >= 50) {
resultResolutions.add(new GraphicResolution(mode.width, mode.height, mode.bitsPerPixel,
mode.refreshRate));
}
}
}
graphicResolutions = resultResolutions.toArray(new GraphicResolution[resultResolutions.size()]);
}
return graphicResolutions;
}
@Override
public boolean isResizeable() {
return config.resizable;
}
@Override
public void setResizeable(boolean resizeable) throws GdxEngineException {
if (gdxApplication == null) {
config.resizable = resizeable;
}
}
@Override
public boolean isFullScreen() {
return config.fullscreen;
}
@Override
public void setFullScreen(boolean fullScreen) throws GdxEngineException {
config.fullscreen = fullScreen;
if (gdxApplication != null) {
if (fullScreen) {
setFullScreenResolution(fullScreenResolution);
} else {
setWindowSize(windowWidth, windowHeight);
}
}
}
void createEngine() {
assert gdxApplication != null;
engine = new GdxEngine(gdxApplication, this);
}
void setLastFrameRenderCalls(int calls) {
lastFrameRenderCalls = calls;
}
@Override
public void setIcons(@Nonnull String... icons) {
for (@Nullable String icon : icons) {
config.addIcon(icon, FileType.Internal);
}
}
}