/* Copyright 2010-2012 Christian Matt
*
* This file is part of PonkOut.
*
* PonkOut 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.
*
* PonkOut 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 PonkOut. If not, see <http://www.gnu.org/licenses/>.
*/
package ponkOut;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.ARBMultisample;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.PixelFormat;
import org.lwjgl.util.glu.GLU;
import org.lwjgl.util.vector.Vector2f;
import ponkOut.Menu.MenuState;
import ponkOut.graphics.Font;
import ponkOut.graphics.GameGraphicsObjectsManager;
import ponkOut.graphics.GraphicsObjectsManager;
import ponkOut.graphics.HUDText;
import ponkOut.graphics.MenuGraphicsObjectsManager;
import ponkOut.graphics.Skybox;
import ponkOut.graphics.resources.Texture;
import ponkOut.input.InputManager;
/**
* the main class
*/
public final class PonkOut implements StateChangeListener {
public static final float COMMON_RADIUS = 0.2f;
/** game will leave game loop if true */
private boolean exit;
/** number of old elapsed time values included for smoothing */
private static final int SMOOTHING_COUNT = 10;
/** weight value i as smoothingWeight[i] (oldest first) */
private float smoothingWeight[] = new float[SMOOTHING_COUNT];
private LinkedList<Float> elapsedHistory = new LinkedList<Float>();
private boolean showFPS;
private static Settings settings = Settings.getInstance();
private HUDText fpsText;
private GraphicsObjectsManager goManager;
private InputManager inputManager;
private State currentState;
public PonkOut() {
exit = false;
showFPS = false;
initDisplay();
initGL();
MenuGraphicsObjectsManager.init();
GameGraphicsObjectsManager.init();
Font.init();
Skybox.init();
goManager = new GraphicsObjectsManager();
inputManager = InputManager.getInstance();
currentState = new MenuState(this, null);
currentState.init();
fpsText = new HUDText("", 10.0f, 0.8f, 0.8f, 1.0f, 0.9f, new Vector2f(1.0f, 1.0f), showFPS, goManager);
// use Gaussian function for weighting
float sum = 0.0f;
for (int i = 0; i < SMOOTHING_COUNT; ++i) {
double x = i / (SMOOTHING_COUNT - 1.0);
smoothingWeight[i] = (float) Math.exp(-4.0 * (x - 1.0) * (x - 1.0));
sum += smoothingWeight[i];
}
// normalize weights
for (int i = 0; i < SMOOTHING_COUNT; ++i)
smoothingWeight[i] /= sum;
// init history
for (int i = 0; i < SMOOTHING_COUNT; ++i)
elapsedHistory.add(0.0167f);
}
public static void initDisplay() {
int x = settings.getResolutionX();
int y = settings.getResolutionY();
int frequency = settings.getFrequency();
try {
DisplayMode modes[] = org.lwjgl.util.Display.getAvailableDisplayModes(x, y, x, y, 32, 32, frequency,
frequency);
DisplayMode mode = null;
if (modes.length > 0)
mode = modes[0];
if (mode == null) {
Sys.alert("Error", "Display mode not supported. Switchting to desktop display mode.");
// use desktop display mode
mode = Display.getDesktopDisplayMode();
x = mode.getWidth();
y = mode.getHeight();
frequency = mode.getFrequency();
modes = org.lwjgl.util.Display.getAvailableDisplayModes(x, y, x, y, 32, 32, frequency, frequency);
if (modes.length > 0)
mode = modes[0];
else {
Sys.alert("Fatal Error", "No valid display mode found.");
System.exit(1);
}
// set fullscreen because we use full resolution now
settings.setFullscreen(true);
}
Display.setDisplayMode(mode);
Display.setVSyncEnabled(settings.getVSync());
Display.setFullscreen(settings.getFullscreen());
Display.setTitle("PonkOut");
// load icons
ByteBuffer[] icons = new ByteBuffer[5];
icons[0] = Texture.getBytes("icon16.tga");
icons[1] = Texture.getBytes("icon32.tga");
icons[2] = Texture.getBytes("icon48.tga");
icons[3] = Texture.getBytes("icon64.tga");
icons[4] = Texture.getBytes("icon128.tga");
// set icons
Display.setIcon(icons);
// update settings
settings.setResolution(mode.getWidth(), mode.getHeight());
settings.setFrequency(mode.getFrequency());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// create display with at least 8 bits alpha, 24 bits color, 1 bit
// stencil buffer and desired samples
try {
Display.create(new PixelFormat(8, 24, 1, settings.getSamples()));
} catch (LWJGLException e) {
if (settings.getSamples() > 0 || !settings.getFullscreen()) {
// probably the desired multisampling value is not supported
// or desktop uses 16 bit colour and therefore cannot run in
// windowed mode
Sys.alert("Error", "Failed creating display. Now trying without anti-aliasing and in fullscreen mode.");
settings.setSamples(0);
settings.setFullscreen(true);
try {
Display.setFullscreen(true);
Display.create(new PixelFormat(8, 24, 1, settings.getSamples()));
} catch (LWJGLException e1) {
Sys.alert("Fatal Error", "Failed creating display.");
System.exit(1);
}
} else {
Sys.alert("Fatal Error", "Failed creating display.");
System.exit(1);
}
}
}
public static void initGL() {
// enable depth buffer
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glDepthFunc(GL11.GL_LEQUAL);
// enable backface culling
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glCullFace(GL11.GL_BACK);
// enable alpha blending
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
// enable textures
GL11.glEnable(GL11.GL_TEXTURE_2D);
// set multisampling
if (settings.getSamples() > 0)
GL11.glEnable(ARBMultisample.GL_MULTISAMPLE_ARB);
// init projection matrix
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
DisplayMode displayMode = Display.getDisplayMode();
GLU.gluPerspective(45.0f, (float) displayMode.getWidth() / (float) displayMode.getHeight(), 0.1f, 2000.0f);
// select modelview matrix
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glLoadIdentity();
}
private void gameLoop() {
// nanoTime() much better on Windows 7 when frame rate is high
// long lastTime = Sys.getTime();
long lastTime = System.nanoTime();
long lastFpsTime = 0;
int frames = 0;
while (!exit) {
// long time = Sys.getTime();
// long ticksPerSecond = Sys.getTimerResolution();
long time = System.nanoTime();
long ticksPerSecond = 1000000000;
float cur = (float) (time - lastTime) / ticksPerSecond;
// remove oldest element from list and add current
elapsedHistory.remove();
elapsedHistory.add(cur);
// computed smoothed value
float elapsedTime = 0.0f;
int i = 0;
for (float f : elapsedHistory) {
elapsedTime += f * smoothingWeight[i];
++i;
}
lastTime = time;
++frames;
// update fps every second
if (time - lastFpsTime >= ticksPerSecond) {
long fps = frames * ticksPerSecond / (time - lastFpsTime);
fpsText.setText(fps + " fps");
lastFpsTime = time;
frames = 0;
}
currentState.render();
// render global stuff
goManager.drawScene();
// update window contents
Display.update();
inputManager.newFrame();
// process keyboard events
while (Keyboard.next()) {
if (Keyboard.getEventKeyState()) { // key down
int key = Keyboard.getEventKey();
// toggle showFPS when F1 is pressed
if (key == Keyboard.KEY_F1) {
showFPS = !showFPS;
fpsText.setVisibility(showFPS);
}
// delegate to current state
currentState.keyDown(key);
}
}
currentState.update(elapsedTime);
// closing the window exits the game
if (Display.isCloseRequested())
exit = true;
}
}
/**
* the main method
*/
public static void main(String argv[]) {
new PonkOut().run();
System.exit(0);
}
/**
* starts game loop
*/
private void run() {
gameLoop();
Display.destroy();
}
@Override
public void changeState(State state) {
currentState.cleanup();
state.init();
currentState = state;
}
@Override
public void exit() {
exit = true;
}
}