package com.nilunder.bdx; import java.util.*; import com.badlogic.gdx.*; import com.badlogic.gdx.controllers.Controllers; import com.badlogic.gdx.files.*; import com.badlogic.gdx.graphics.*; import com.badlogic.gdx.graphics.g2d.*; import com.badlogic.gdx.graphics.g3d.*; import com.badlogic.gdx.graphics.g3d.shaders.DefaultShader; import com.nilunder.bdx.audio.Audio; import com.nilunder.bdx.gl.*; import com.nilunder.bdx.inputs.*; import com.nilunder.bdx.utils.*; import com.nilunder.bdx.utils.Color; import javax.vecmath.Vector2f; public class Bdx{ public static class Display{ public boolean changed; public Color clearColor = new Color(0, 0, 0, 0); public int width(){ return Gdx.graphics.getWidth(); } public void width(int width){ Gdx.graphics.setWindowedMode(width, Gdx.graphics.getHeight()); } public int height(){ return Gdx.graphics.getHeight(); } public void height(int height){ Gdx.graphics.setWindowedMode(Gdx.graphics.getWidth(), height); } public Vector2f size(){ return new Vector2f(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); } public void size(int width, int height){ Gdx.graphics.setWindowedMode(width, height); } public void size(Vector2f size){ size(Math.round(size.x), Math.round(size.y)); } public Vector2f center(){ return new Vector2f(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2); } public void fullscreen(boolean full){ if (full){ Gdx.graphics.setFullscreenMode(Gdx.graphics.getDisplayMode()); }else{ size(size()); } } public boolean fullscreen(){ return Gdx.graphics.isFullscreen(); } public static void advancedLighting(boolean on){ advancedLightingOn = on; shaderProvider.deleteShaders(); } public static boolean advancedLighting(){ return advancedLightingOn; } public static void setMaxLightCount(Light.Type lightType, int count){ DefaultShader.Config config = shaderProvider.config; if (lightType.equals(Light.Type.POINT)) config.numPointLights = count; else if (lightType.equals(Light.Type.SUN)) config.numDirectionalLights = count; else config.numSpotLights = count; shaderProvider.deleteShaders(); // Get rid of the old shaders, as they need to be recreated for the new light count. } public static int getMaxLightCount(Light.Type lightType){ DefaultShader.Config config = shaderProvider.config; if (lightType.equals(Light.Type.POINT)) return config.numPointLights; else if (lightType.equals(Light.Type.SUN)) return config.numDirectionalLights; else return config.numSpotLights; } } public static class ArrayListScenes extends ArrayListNamed<Scene>{ @Override public Object clone(){ // GWT (2.6.0) doesn't provide a default implementation of Object.clone() ArrayListScenes cloned = new ArrayListScenes(); for (Scene scene : this){ cloned.add(scene); } return cloned; } @Override public boolean add(Scene scene){ boolean ret = super.add(scene); if (scene.objects == null) scene.init(); return ret; } @Override public void add(int index, Scene scene){ super.add(index, scene); if (scene.objects == null) scene.init(); } public Scene add(String name){ Scene scene = new Scene(name); add(scene); return scene; } public Scene add(int index, String name){ Scene scene = new Scene(name); add(index, scene); return scene; } public Scene set(int index, Scene scene){ Scene old = remove(index); add(index, scene); return old; } public Scene set(int index, String name){ Scene scene = new Scene(name); set(index, scene); return scene; } public Scene remove(String name){ int index = indexOf(get(name)); return remove(index); } public ArrayList<String> available(){ ArrayList<String> scenes = new ArrayList<String>(); FileHandle[] files = Gdx.files.internal("bdx/scenes/").list("bdx"); for (FileHandle file : files) scenes.add(file.nameWithoutExtension()); return scenes; } } public static final int TICK_RATE = 60; public static final float TICK_TIME = 1f/TICK_RATE; public static final int VERT_STRIDE = 8; public static float time; public static String firstScene; public static Profiler profiler; public static ArrayListScenes scenes; public static Display display; public static Audio audio; public static Mouse mouse; public static ArrayListNamed<Gamepad> gamepads; public static InputMaps imaps; public static Keyboard keyboard; public static ArrayList<Finger> fingers; public static ArrayList<Component> components; public static BDXShaderProvider shaderProvider; public static float physicsSpeed; public static float timeSpeed; public static boolean restartOnExport = false; private static boolean advancedLightingOn; private static ArrayList<Finger> allocatedFingers; private static ModelBatch modelBatch; private static ModelBatch depthBatch; private static RenderBuffer frameBuffer; private static RenderBuffer depthBuffer; private static SpriteBatch spriteBatch; private static BDXDepthShaderProvider depthShaderProvider; private static HashMap<Float, RenderBuffer> availableTempBuffers; private static boolean requestedRestart; private static long startMillis = System.currentTimeMillis(); public static void init(){ time = 0; physicsSpeed = 1; timeSpeed = 1; profiler = new Profiler(); display = new Display(); scenes = new ArrayListScenes(); audio = new Audio(); mouse = new Mouse(); imaps = new InputMaps(); keyboard = new Keyboard(); fingers = new ArrayList<Finger>(); components = new ArrayList<Component>(); allocatedFingers = new ArrayList<Finger>(); for (int i = 0; i < 10; ++i){ allocatedFingers.add(new Finger(i)); } gamepads = new ArrayListNamed<Gamepad>(); for (int i = 0; i < Controllers.getControllers().size; i++) gamepads.add(new Gamepad(i)); com.badlogic.gdx.graphics.glutils.ShaderProgram.pedantic = false; shaderProvider = new BDXShaderProvider(); modelBatch = new ModelBatch(shaderProvider); spriteBatch = new SpriteBatch(); spriteBatch.setBlendFunction(Gdx.gl.GL_SRC_ALPHA, Gdx.gl.GL_ONE_MINUS_SRC_ALPHA); frameBuffer = new RenderBuffer(spriteBatch); depthBuffer = new RenderBuffer(spriteBatch); depthShaderProvider = new BDXDepthShaderProvider(Gdx.files.internal("bdx/shaders/3d/depthExtract.vert"), Gdx.files.internal("bdx/shaders/3d/depthExtract.frag")); depthBatch = new ModelBatch(depthShaderProvider); advancedLightingOn = true; Gdx.input.setInputProcessor(new GdxProcessor(keyboard, mouse, allocatedFingers, gamepads)); availableTempBuffers = new HashMap<Float, RenderBuffer>(); requestedRestart = false; profiler.start("__gpu wait"); } public static void main(){ // --------- Auto reloading -------- // profiler.stop("__gpu wait"); profiler.deltaTimes.put("__gpu wait", (long) Math.max(profiler.deltaTimes.get("__gpu wait") - (TICK_TIME * 1000000000), 0)); if (restartOnExport && Gdx.files.internal("finishedExport").lastModified() > startMillis) { startMillis = System.currentTimeMillis(); restart(); } boolean screenShadersUsed = false; for (Scene scene : scenes) { if (scene.screenShaders.size() > 0) { screenShadersUsed = true; break; } } profiler.start("__render"); Gdx.gl.glClearColor(display.clearColor.r, display.clearColor.g, display.clearColor.b, display.clearColor.a); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); Gdx.gl.glClearColor(0, 0, 0, 0); Gdx.gl.glClear(GL20.GL_DEPTH_BUFFER_BIT); profiler.stop("__render"); // -------- Update Input -------- time += TICK_TIME * timeSpeed; ++GdxProcessor.currentTick; fingers.clear(); for (Finger f : allocatedFingers){ if (f.down() || f.up()) fingers.add(f); } profiler.stop("__scene"); // ------------------------------ for (Component c : components){ if (c.state != null) c.state.main(); } profiler.stop("__logic"); Viewport vp; ArrayListScenes newSceneList = (ArrayListScenes) scenes.clone(); boolean depthBufferCleared = false; boolean colorBufferCleared = false; for (int i = 0; i < newSceneList.size(); i++){ Scene scene = newSceneList.get(i); boolean prevSceneRenderPassthrough = false; boolean nextSceneRenderPassthrough = false; if (i > 0) prevSceneRenderPassthrough = newSceneList.get(i - 1).renderPassthrough; if (i < newSceneList.size() - 1) nextSceneRenderPassthrough = newSceneList.get(i + 1).renderPassthrough; if (!prevSceneRenderPassthrough) { colorBufferCleared = false; depthBufferCleared = false; } scene.update(); profiler.stop("__scene"); if (!scene.valid() || !scene.visible) continue; // ------- Render Scene -------- vp = scene.viewport; vp.apply(); depthShaderProvider.update(scene); shaderProvider.update(scene); boolean frameBufferInUse = false; if (scene.screenShaders.size() > 0 || (screenShadersUsed && scene.renderPassthrough)) { // If the scene is passing its render output, and screen shaders are used, then it needs to use the framebuffer to pass the render on. // If screen shaders aren't used anywhere, there's no need to render to a framebuffer, as OpenGL will correctly blend normally. frameBuffer.begin(); frameBufferInUse = true; if (!colorBufferCleared) { // First rendering scene, or previous scene didn't pass a render, so it needs to clear the framebuffer. Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); // We only need to do this once - the rendered data can accumulate until the last scene renders or colorBufferCleared = true; // Another scene stops passing the render data up the stack } } Gdx.gl.glClear(GL20.GL_DEPTH_BUFFER_BIT); // We have to clear the depth buffer no matter what, because closer things on previous scenes should never // overlap further things on overlay scenes renderWorld(modelBatch, scene, scene.camera); // Render main view for (Camera cam : scene.cameras){ // Render auxiliary cameras if (cam.renderToTexture){ cam.update(); if (cam.renderBuffer == null){ cam.initRenderBuffer(); } cam.renderBuffer.begin(); Gdx.gl.glClear(GL20.GL_DEPTH_BUFFER_BIT | GL20.GL_COLOR_BUFFER_BIT); renderWorld(modelBatch, scene, cam); cam.renderBuffer.end(); } } if (frameBufferInUse) { frameBuffer.end(); boolean usingDepth = false; for (ScreenShader filter : scene.screenShaders) { if (filter.usingDepthTexture()) usingDepth = true; } if (usingDepth || scene.renderPassthrough) { // Render depth texture Gdx.gl.glClearColor(1, 1, 1, 1); depthBuffer.begin(); if (!depthBufferCleared) { Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); depthBufferCleared = true; } Gdx.gl.glClear(GL20.GL_DEPTH_BUFFER_BIT ); renderWorld(depthBatch, scene, scene.camera); depthBuffer.end(); depthBuffer.getColorBufferTexture().bind(2); } scene.lastFrameBuffer.getColorBufferTexture().bind(1); Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0); Gdx.gl.glClearColor(0, 0, 0, 0); for (ScreenShader filter : scene.screenShaders) { if (!filter.active) continue; filter.begin(); filter.setUniformf("time", Bdx.time); filter.setUniformi("lastFrame", 1); filter.setUniformi("depthTexture", 2); filter.setUniformf("screenWidth", vp.w); filter.setUniformf("screenHeight", vp.h); filter.setUniformf("near", scene.camera.near()); filter.setUniformf("far", scene.camera.far()); filter.end(); if (!availableTempBuffers.containsKey(filter.renderScale.x)) availableTempBuffers.put(filter.renderScale.x, new RenderBuffer(spriteBatch, Math.round(vp.size().x * filter.renderScale.x), Math.round(vp.size().y * filter.renderScale.y))); RenderBuffer tempBuffer = availableTempBuffers.get(filter.renderScale.x); tempBuffer.clear(); frameBuffer.drawTo(tempBuffer, filter, 0, 0, frameBuffer.getWidth(), frameBuffer.getHeight()); if (!filter.overlay) frameBuffer.clear(); tempBuffer.drawTo(frameBuffer); } if (!scene.renderPassthrough || scene == newSceneList.get(newSceneList.size() - 1) || !nextSceneRenderPassthrough) frameBuffer.drawTo(null, null, vp.x, vp.y, vp.w, vp.h); // Draw to screen, but only if the scene's not passing the render, or if it's the last scene in the list scene.lastFrameBuffer.clear(); frameBuffer.drawTo(scene.lastFrameBuffer); } scene.executeDrawCommands(); // ------- Render physics debug view -------- Bullet.DebugDrawer debugDrawer = (Bullet.DebugDrawer)scene.world.getDebugDrawer(); debugDrawer.drawWorld(scene.world, scene.camera.data); profiler.stop("__render"); } mouse.wheelMove = 0; Bdx.display.changed = false; profiler.stop("__scene"); profiler.updateVariables(); if (profiler.visible()){ profiler.updateVisible(); // ------- Render profiler scene -------- profiler.scene.update(); Gdx.gl.glClear(GL20.GL_DEPTH_BUFFER_BIT); renderWorld(modelBatch, profiler.scene, profiler.scene.camera); profiler.scene.executeDrawCommands(); } if (profiler.gl.isEnabled()){ profiler.gl.updateFields(); } if (requestedRestart) { requestedRestart = false; Scene.clearColorDefaultSet = false; dispose(); for (Scene scene : new ArrayList<Scene>(scenes)) { scenes.remove(scene); scene.end(); } if (profiler.visible()){ profiler.scene.end(); } init(); scenes.add(firstScene); } profiler.start("__gpu wait"); } private static void renderWorld(ModelBatch batch, Scene scene, Camera camera){ batch.begin(camera.data); for (GameObject g : scene.objects){ if (g.visible() && (g.insideFrustum() || !g.frustumCulling) && !camera.ignoreObjects.contains(g)) batch.render(g.modelInstance, scene.environment); } batch.end(); } public static void dispose(){ modelBatch.dispose(); depthBatch.dispose(); spriteBatch.dispose(); frameBuffer.dispose(); depthBuffer.dispose(); shaderProvider.dispose(); audio.dispose(); for (RenderBuffer b : availableTempBuffers.values()) b.dispose(); for (Scene s : scenes) { s.dispose(); } } public static void end(){ if (profiler.visible()) profiler.gl.disable(); Gdx.app.exit(); } public static void resize(int width, int height) { spriteBatch.getProjectionMatrix().setToOrtho2D(0, 0, width, height); if (frameBuffer != null) frameBuffer.dispose(); if (depthBuffer != null) depthBuffer.dispose(); frameBuffer = new RenderBuffer(spriteBatch); // Have to recreate all render buffers and adjust the projection matrix as the window size has changed depthBuffer = new RenderBuffer(spriteBatch); for (RenderBuffer b : availableTempBuffers.values()) b.dispose(); availableTempBuffers.clear(); for (Scene scene : scenes) { for (Camera cam : scene.cameras) { // Have to do this, as the RenderBuffers need to be resized for the new window size if (cam.renderBuffer != null) cam.renderBuffer.dispose(); cam.renderBuffer = null; } if (scene.lastFrameBuffer != null) scene.lastFrameBuffer.dispose(); scene.lastFrameBuffer = new RenderBuffer(null); scene.viewport.update(width, height); } if (profiler.visible()){ profiler.updateViewport(width, height); } Bdx.display.changed = true; } public static void restart(){ requestedRestart = true; } }