Java tutorial
/******************************************************************************* * Copyright 2012 Martijn Courteaux <martijn.courteaux@skynet.be> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package net.kubin.game; import de.matthiasmann.twl.GUI; import de.matthiasmann.twl.renderer.lwjgl.LWJGLRenderer; import de.matthiasmann.twl.theme.ThemeManager; import static org.lwjgl.opengl.GL11.*; import java.io.File; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import net.kubin.Kubin; import net.kubin.blocks.BlockJSONLoader; import net.kubin.blocks.BlockManager; import net.kubin.game.PerformanceMonitor.Operation; import net.kubin.items.ItemXMLLoader; import net.kubin.math.MathHelper; import net.kubin.math.Vec3f; import net.kubin.rendering.BufferManager; import net.kubin.rendering.GLFont; import net.kubin.rendering.GLUtils; import net.kubin.world.World; import org.lwjgl.Sys; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.Display; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.NVFogDistance; public class Game { private World _world; private int _fps = Kubin._fps; private float _step; private int _sleepTimeMillis; private static Configuration _configuration = Kubin.getConfiguration(); private static Game __instance; private int[] _fpsDataBuffer = Kubin._fpsDataBuffer; private float _averageFPS = Kubin._averageFPS; public static boolean RENDER_INFORMATION_OVERLAY = false; public static Game getInstance() { if (__instance == null) { __instance = new Game(); } return __instance; } public GUI gui; public LWJGLRenderer renderer; public ThemeManager theme; public void init() throws IOException { System.out.println("LWJGL Version: " + Sys.getVersion()); System.out.println("GPU: " + Display.getAdapter()); initOpenGL(); loadTextures(); loadFonts(); //loadItems(); loadBlocks(); //loadSpecialStuff(); Mouse.setGrabbed(true); } public void initOpenGL() { // init OpenGL glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, 800, 600, 0, 1, 300); glMatrixMode(GL_MODELVIEW); GL11.glShadeModel(GL11.GL_SMOOTH); Vec3f fog = Kubin.getConfiguration().getFogColor(); glClearColor(fog.x(), fog.y(), fog.z(), 1.0f); glEnable(GL_TEXTURE_2D); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // glEnable(GL_DEPTH_TEST); // glDepthFunc(GL_ALWAYS); glEnable(GL_CULL_FACE); glEnable(GL_FOG); glFog(GL_FOG_COLOR, GLUtils.wrapDirect(fog.x(), fog.y(), fog.z(), 1.0f)); glFogi(GL_FOG_MODE, GL11.GL_LINEAR); glFogf(GL_FOG_START, Kubin.getConfiguration().getViewingDistance() * 0.55f); glFogf(GL_FOG_END, Kubin.getConfiguration().getViewingDistance()); glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, NVFogDistance.GL_EYE_RADIAL_NV); glHint(GL_FOG_HINT, GL_NICEST); System.out.println("VBO Supported: " + GLUtils.isVBOSupported()); /* Instantiate the BufferManager */ BufferManager.getInstance(); } private void loadSpecialStuff() { try { Class.forName("net.kubin.rendering.particles.Smoke"); } catch (Exception e) { // TODO: handle exception } } public float getFPS() { return _fps; } public float getStep() { return _step; } public void update() { if (_world != null) { _world.update(); } BufferManager.getInstance().deleteQueuedBuffers(); } public void render() { // Clear the screen and depth buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (_world != null) { _world.render(); } if (RENDER_INFORMATION_OVERLAY) { renderOnScreenInfo(); } } private void renderOnScreenInfo() { GLFont infoFont = FontStorage.getFont("Monospaced_20"); glColor3f(1, 1, 1); /* Top Left Info */ infoFont.print(4, _configuration.getHeight() - 20, String.format("FPS: %5.1f", getAverageFPS())); infoFont.print(4, _configuration.getHeight() - 20 - 15, "Sleeping: " + String.format("%4d", Game.getInstance().getSleepTime())); infoFont.print(4, _configuration.getHeight() - 20 - 30, "Heap Size: " + MathHelper.bytesToMagaBytes(Runtime.getRuntime().totalMemory()) + " MB"); infoFont.print(4, _configuration.getHeight() - 20 - 45, "Heap Use: " + MathHelper .bytesToMagaBytes(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) + " MB"); infoFont.print(4, _configuration.getHeight() - 20 - 60, "Buffers: " + BufferManager.getInstance().getAliveBuffers()); Operation[] ops = Operation.values(); for (int i = 0; i < ops.length; ++i) { String name = ops[i].name(); infoFont.print(_configuration.getWidth() - 200, _configuration.getHeight() - 20 - 15 * i, String.format("%-20s %7.3f", name, PerformanceMonitor.getInstance().get(ops[i]))); } infoFont.print(Kubin.getConfiguration().getWidth() - 260, 0, _world.getActivePlayer().getLookDir().toString()); infoFont.print(Kubin.getConfiguration().getWidth() - 100, 0, "Colliding: " + _world.getActivePlayer().isColliding); } public void initOverlayRendering() { Configuration conf = Kubin.getConfiguration(); glDisable(GL_FOG); glClear(GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, conf.getWidth(), 0, conf.getHeight(), -100, 100); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_COLOR_MATERIAL); glEnable(GL_ALPHA_TEST); glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); } public void initSceneRendering() { glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glEnable(GL_CULL_FACE); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glEnable(GL_FOG); } public void renderTransculentOverlayLayer() { glColor4f(0.0f, 0.0f, 0.0f, 0.4f); glDisable(GL_TEXTURE_2D); glBegin(GL_QUADS); glVertex2i(0, 0); glVertex2i(_configuration.getWidth(), 0); glVertex2i(_configuration.getWidth(), _configuration.getHeight()); glVertex2i(0, _configuration.getWidth()); glEnd(); } public void startGameLoop() { Thread.currentThread().setPriority(Thread.MAX_PRIORITY); while (!Display.isCloseRequested()) { if (Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) { BufferManager.getInstance().deleteQueuedBuffers(); BlockManager.getInstance().release(); TextureStorage.release(); FontStorage.release(); Display.destroy(); } if (Display.wasResized()) { handleResize(); } long startTiming = System.nanoTime(); _step = 1.0f / _fps; /* Update */ PerformanceMonitor.getInstance().start(Operation.UPDATE); update(); PerformanceMonitor.getInstance().stop(Operation.UPDATE); /* Render */ PerformanceMonitor.getInstance().start(Operation.RENDER_ALL); render(); //gui.update(); Display.update(); PerformanceMonitor.getInstance().stop(Operation.RENDER_ALL); long stopTiming = System.nanoTime(); long frameTimeNanos = (stopTiming - startTiming); long desiredFrameTimeNanos = 1000000000L; if (_configuration.getVSync()) { // desiredFrameTimeNanos /= 1; } else { desiredFrameTimeNanos /= _configuration.getFPS(); } long diff = desiredFrameTimeNanos - frameTimeNanos; if (frameTimeNanos < desiredFrameTimeNanos) { if (!_configuration.getVSync()) { try { _sleepTimeMillis = (int) (diff / 1000000L); Thread.sleep(_sleepTimeMillis, (int) (diff % 1000000L)); } catch (Exception e) { e.printStackTrace(); } } } else { _sleepTimeMillis = 0; } _fps = (int) (1000000000.0f / (frameTimeNanos + (_sleepTimeMillis * 1000000L))); /* Average FPS System */ float fpsSum = _fps; for (int i = 0; i < _fpsDataBuffer.length - 1; ++i) { _fpsDataBuffer[i] = _fpsDataBuffer[i + 1]; fpsSum += _fpsDataBuffer[i]; } _fpsDataBuffer[_fpsDataBuffer.length - 1] = _fps; fpsSum /= _fpsDataBuffer.length; _averageFPS = fpsSum; } if (_world != null) { try { getWorld().save(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } BufferManager.getInstance().deleteQueuedBuffers(); BlockManager.getInstance().release(); TextureStorage.release(); FontStorage.release(); //gui.destroy(); //theme.destroy(); Display.destroy(); } public void handleResize() { //Prevent negative UI sizes if (Display.getWidth() < 250) { return; } if (Display.getHeight() < 250) { return; } //Resize the UI and Viewport _configuration.setDisplaySettings(Display.getWidth(), Display.getHeight(), Display.isFullscreen()); GL11.glViewport(0, 0, Display.getWidth(), Display.getHeight()); getWorld().handleDisplayResize(); } private void loadTextures() throws IOException { TextureStorage.setTexturePack(Kubin.getConfiguration().getTexturePack()); TextureStorage.loadTexture("blocks", "PNG", "blocks.png"); TextureStorage.loadTexture("environment.clouds", "PNG", "environment/clouds.png"); TextureStorage.loadTexture("gui.gui", "PNG", "gui/gui.png"); TextureStorage.loadTexture("gui.inventory", "PNG", "gui/inventory.png"); } private void loadFonts() throws IOException { FontStorage.loadFont("Monospaced_20", "novamono.ttf", 22); FontStorage.loadFont("InventoryAmount", "visitor1.ttf", 14); } private void loadItems() { try { ItemXMLLoader.parseXML(); } catch (Exception ex) { Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex); } } private void loadBlocks() { try { //BlockXMLLoader.parseXML(); BlockJSONLoader.parseJSON(); } catch (Exception e) { e.printStackTrace(); } } public void setWorld(World world) { this._world = world; } public World getWorld() { return this._world; } /** * Returns the sleeptime in milliseconds for the last frame. * * @return */ public int getSleepTime() { return _sleepTimeMillis; } public float getAverageFPS() { return _averageFPS; } public static final int FILE_BASE_APPLICATION = 0x01; public static final int FILE_BASE_USER_DATA = 0x02; public File getRelativeFile(int fileBase, String string) { if (string.contains("${world}")) { string = string.replace("${world}", getWorld().getWorldName()); } switch (fileBase) { case FILE_BASE_USER_DATA: return new File(getUserDataFolder(), string); case FILE_BASE_APPLICATION: default: return new File(string); } } private File getUserHome() { return new File(System.getProperty("user.home")); } public File getUserDataFolder() { String os = System.getProperty("os.name").toLowerCase(); File f = null; if (!Kubin.USE_USER_DATA_DIR) { f = new File(".kubin"); } else { if (os.contains("mac")) { f = new File(getUserHome(), "Library/Application Support/kubin"); } else if (os.contains("inux") || os.contains("nix")) { f = new File(getUserHome(), ".kubin"); } else if (os.contains("win")) { f = new File(new File(System.getenv("APPDATA")), ".kubin"); } } f.mkdir(); return f; } }