diff --git a/buildNumber.properties b/buildNumber.properties index cbe49a67..f1df9a01 100644 --- a/buildNumber.properties +++ b/buildNumber.properties @@ -1,3 +1,3 @@ #maven.buildNumber.plugin properties file -#Wed Sep 04 09:33:29 EDT 2024 -buildNumber=320 +#Sun Sep 08 21:41:56 EDT 2024 +buildNumber=325 diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index d1ede71e..8db04bec 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -693,6 +693,16 @@ Update human collidable data (09/05/2024) Fix AI tracking deleted entity +(09/06/2024) +work on debugging framebuffer bug + +(09/07/2024) +par_shapes integration + +(09/08/2024) +Directed graph datastructure +Framebuffer + RenderingEngine tests + # TODO diff --git a/pom.xml b/pom.xml index b8683460..23653a4a 100644 --- a/pom.xml +++ b/pom.xml @@ -125,6 +125,20 @@ ${lwjgl.version} ${lwjgl.natives} + + + + + org.lwjgl + lwjgl-par + ${lwjgl.version} + + + org.lwjgl + lwjgl-par + ${lwjgl.version} + ${lwjgl.natives} + diff --git a/src/main/java/electrosphere/engine/Globals.java b/src/main/java/electrosphere/engine/Globals.java index 718189a5..7c1e0522 100644 --- a/src/main/java/electrosphere/engine/Globals.java +++ b/src/main/java/electrosphere/engine/Globals.java @@ -602,13 +602,9 @@ public class Globals { //init fluid shader program FluidChunkModelGeneration.fluidChunkShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/entities/fluid2/fluid2.vs", "/Shaders/entities/fluid2/fluid2.fs"); //init models - assetManager.addModelPathToQueue("Models/basic/geometry/unitsphere.glb"); - assetManager.addModelPathToQueue("Models/basic/geometry/unitsphere.fbx"); - assetManager.addModelPathToQueue("Models/basic/geometry/unitsphere_1.fbx"); - assetManager.addModelPathToQueue("Models/basic/geometry/unitsphere_grey.fbx"); + assetManager.registerModelToSpecificString(RenderUtils.createUnitsphere(), AssetDataStrings.UNITSPHERE); + assetManager.registerModelToSpecificString(RenderUtils.createUnitCylinder(), AssetDataStrings.UNITCYLINDER); assetManager.addModelPathToQueue("Models/basic/geometry/SmallCube.fbx"); - assetManager.addModelPathToQueue("Models/basic/geometry/unitcylinder.fbx"); - assetManager.addModelPathToQueue("Models/basic/geometry/unitcylinder.glb"); assetManager.addModelPathToQueue("Models/basic/geometry/unitcapsule.glb"); assetManager.addModelPathToQueue("Models/basic/geometry/unitplane.fbx"); assetManager.addModelPathToQueue("Models/basic/geometry/unitcube.fbx"); @@ -673,6 +669,12 @@ public class Globals { Globals.server = null; Globals.serverSynchronizationManager = null; Globals.javaPID = null; + Globals.RENDER_FLAG_RENDER_SHADOW_MAP = true; + Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER_CONTENT = false; + Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER = false; + Globals.RENDER_FLAG_RENDER_UI = false; + Globals.RENDER_FLAG_RENDER_BLACK_BACKGROUND = false; + Globals.RENDER_FLAG_RENDER_WHITE_BACKGROUND = false; LoggerInterface.destroyLoggers(); } diff --git a/src/main/java/electrosphere/engine/Main.java b/src/main/java/electrosphere/engine/Main.java index aae16c1a..70c7149f 100644 --- a/src/main/java/electrosphere/engine/Main.java +++ b/src/main/java/electrosphere/engine/Main.java @@ -156,7 +156,7 @@ public class Main { Globals.controlHandler.hintUpdateControlState(ControlsState.TITLE_MENU); //start initial asset loading - Globals.threadManager.start(ThreadLabel.ASSET_LOADING, new Thread(Globals.initialAssetLoadingThread)); + Globals.threadManager.start(new LoadingThread(LoadingThreadType.INIT_ASSETS)); } //Sets a hook that fires when the engine process stops @@ -434,8 +434,6 @@ public class Main { // //Terminate the program. if(Globals.renderingEngine != null){ - glfwTerminate(); - Globals.renderingEngine.clearGlobalState(); Globals.renderingEngine.destroy(); } //used to signal threads to stop diff --git a/src/main/java/electrosphere/engine/assetmanager/AssetDataStrings.java b/src/main/java/electrosphere/engine/assetmanager/AssetDataStrings.java index 921e7003..97e9b5ac 100644 --- a/src/main/java/electrosphere/engine/assetmanager/AssetDataStrings.java +++ b/src/main/java/electrosphere/engine/assetmanager/AssetDataStrings.java @@ -8,4 +8,12 @@ public class AssetDataStrings { public static final String ASSET_STRING_SKYBOX_BASIC = "skyboxBasic"; public static final String BITMAP_CHARACTER_MODEL = "bitmapCharacterModel"; public static final String LEAVES_MODEL = "leaves"; + + /** + * The basic geometry of the engine + */ + public static final String UNITSPHERE = "unitSphere"; + public static final String UNITCYLINDER = "unitCylinder"; + + } diff --git a/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java b/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java index 383ab5f8..d74ced6b 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java +++ b/src/main/java/electrosphere/engine/loadingthreads/ClientLoading.java @@ -11,6 +11,7 @@ import electrosphere.client.targeting.crosshair.Crosshair; import electrosphere.client.terrain.cells.DrawCellManager; import electrosphere.controls.ControlHandler; import electrosphere.engine.Globals; +import electrosphere.engine.assetmanager.AssetDataStrings; import electrosphere.engine.signal.Signal.SignalType; import electrosphere.engine.threads.LabeledThread.ThreadLabel; import electrosphere.entity.DrawableUtils; @@ -244,7 +245,7 @@ public class ClientLoading { //player's cursor Globals.playerCursor = EntityCreationUtils.createClientSpatialEntity(); - EntityCreationUtils.makeEntityDrawable(Globals.playerCursor, "Models/basic/geometry/unitsphere_1.fbx"); + EntityCreationUtils.makeEntityDrawable(Globals.playerCursor, AssetDataStrings.UNITSPHERE); DrawableUtils.makeEntityTransparent(Globals.playerCursor); EntityUtils.getScale(Globals.playerCursor).set(30f); } @@ -257,7 +258,7 @@ public class ClientLoading { */ static void initDrawCellManager(boolean blockForInit){ int iterations = 0; - while(blockForInit && (Globals.clientWorldData == null || Globals.initialAssetLoadingThread.isLoading())){ + while(blockForInit && (Globals.clientWorldData == null || InitialAssetLoading.atlasQueuedTexture == null || !InitialAssetLoading.atlasQueuedTexture.hasLoaded())){ try { TimeUnit.MILLISECONDS.sleep(10); iterations++; @@ -267,7 +268,7 @@ public class ClientLoading { if(iterations > MAX_DRAW_CELL_WAIT){ String message = "Draw cell took too long to init!\n" + Globals.clientWorldData + "\n" + - Globals.initialAssetLoadingThread.isLoading(); + InitialAssetLoading.atlasQueuedTexture.hasLoaded(); throw new IllegalStateException(message); } } diff --git a/src/main/java/electrosphere/engine/loadingthreads/InitialAssetLoading.java b/src/main/java/electrosphere/engine/loadingthreads/InitialAssetLoading.java index e65871d1..dffff3ae 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/InitialAssetLoading.java +++ b/src/main/java/electrosphere/engine/loadingthreads/InitialAssetLoading.java @@ -21,35 +21,27 @@ import electrosphere.util.FileUtils; * - Texture Atlas for terrain * - Icons for items */ -public class InitialAssetLoading implements Runnable { - - //tracks whether this thread is still doing work or not - boolean loading = true; +public class InitialAssetLoading { + /** + * The queued atlas texture + */ + static QueuedTexture atlasQueuedTexture = null; + /** * Loads basic data */ - public void LoadData(){ - + protected static void loadData(){ loadTextureAtlas(); LoggerInterface.loggerEngine.INFO("Finished loading texture atlas"); - - loading = false; - } - /** - * Gets whether the thread is still loading or not - * @return true if loading, false otherwise - */ - public boolean isLoading(){ - return loading; } /** * Loads the texture atlas */ - private void loadTextureAtlas(){ + private static void loadTextureAtlas(){ //terrain texture atlas Globals.profiler.beginCpuSample("createVoxelTextureAtlas"); VoxelData data = Globals.gameConfigCurrent.getVoxelData(); @@ -78,7 +70,7 @@ public class InitialAssetLoading implements Runnable { Globals.profiler.endCpuSample(); //queue to asset manager - QueuedTexture atlasQueuedTexture = new QueuedTexture(image); + atlasQueuedTexture = new QueuedTexture(image); Globals.assetManager.queuedAsset(atlasQueuedTexture); @@ -97,10 +89,12 @@ public class InitialAssetLoading implements Runnable { Globals.voxelTextureAtlas.setNormal(atlasQueuedTexture.getTexture()); } - @Override - public void run(){ - this.LoadData(); + /** + * Gets the queued texture + */ + protected static QueuedTexture getQueuedTexture(){ + return atlasQueuedTexture; } - + } diff --git a/src/main/java/electrosphere/engine/loadingthreads/LoadingThread.java b/src/main/java/electrosphere/engine/loadingthreads/LoadingThread.java index 25a91c3d..5dae7f1d 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/LoadingThread.java +++ b/src/main/java/electrosphere/engine/loadingthreads/LoadingThread.java @@ -65,6 +65,11 @@ public class LoadingThread extends Thread { */ LOAD_VIEWPORT, + /** + * Loads initial assets + */ + INIT_ASSETS, + } /** @@ -152,6 +157,11 @@ public class LoadingThread extends Thread { case LOAD_VIEWPORT: { ViewportLoading.loadViewport(params); } break; + + //Load the initial assets + case INIT_ASSETS: { + InitialAssetLoading.loadData(); + } break; } } diff --git a/src/main/java/electrosphere/engine/profiler/Profiler.java b/src/main/java/electrosphere/engine/profiler/Profiler.java index d74a289a..3d4822f2 100644 --- a/src/main/java/electrosphere/engine/profiler/Profiler.java +++ b/src/main/java/electrosphere/engine/profiler/Profiler.java @@ -10,7 +10,8 @@ import org.lwjgl.util.remotery.Remotery; public class Profiler { //controls whether to profile or not - public static boolean PROFILE = true; + //!!WARNING!!: when this is turned on, testing can behave weirdly!! IE GET STUCK! + public static boolean PROFILE = false; //pointer to the global instance long pointer = -1; diff --git a/src/main/java/electrosphere/engine/threads/ThreadManager.java b/src/main/java/electrosphere/engine/threads/ThreadManager.java index 9f40ee37..4d643f33 100644 --- a/src/main/java/electrosphere/engine/threads/ThreadManager.java +++ b/src/main/java/electrosphere/engine/threads/ThreadManager.java @@ -111,8 +111,7 @@ public class ThreadManager { if(thread.getThread().isAlive()){ String errorMessage = "Failed to interrupt thread! " + thread.getLabel(); System.err.println(errorMessage); - new IllegalStateException().printStackTrace(); - System.exit(1); + throw new IllegalStateException(); } } catch (InterruptedException e) { CodeUtils.todo(e, "Think about how to handle this"); diff --git a/src/main/java/electrosphere/renderer/OpenGLState.java b/src/main/java/electrosphere/renderer/OpenGLState.java index eacd4e36..8ff45a65 100644 --- a/src/main/java/electrosphere/renderer/OpenGLState.java +++ b/src/main/java/electrosphere/renderer/OpenGLState.java @@ -21,44 +21,62 @@ public class OpenGLState { private static final boolean DISABLE_CACHING = false; //the max texture allowed by the current environment - int MAX_TEXTURE_WIDTH = 0; + int MAX_TEXTURE_WIDTH; //the current viewport dimensions - private Vector2i viewport = new Vector2i(0,0); + private Vector2i viewport; //whether depth test is enabled or not - boolean depthTest = false; + boolean depthTest; //the current depth function - int depthFunction = -1; + int depthFunction; //whether to blend or nit - boolean blendTest = false; + boolean blendTest; //the current blend func //map is (texture unit) -> [sfactor,dfactor] - Map blendFuncMap = new HashMap(); + Map blendFuncMap; //the key that contains the value of glBlendFunc (which would affect all buffers) static final int ALL_BUFFERS_KEY = -1; //the currently active texture - int activeTexture = 0; + int activeTexture; //the currently bound framebuffer - int framebufferType = 0; - int framebufferPointer = 0; + int framebufferType; + int framebufferPointer; //active shader - ShaderProgram activeShader = null; + ShaderProgram activeShader; //map of texture units and their corresponding texture pointers - Map unitToPointerMap = new HashMap(); + Map unitToPointerMap; + /** + * Initializes the opengl state + */ + public void init(){ + this.MAX_TEXTURE_WIDTH = 0; + this.viewport = new Vector2i(0,0); + this.depthTest = false; + this.depthFunction = -1; + this.blendTest = false; + this.blendFuncMap = new HashMap(); + activeTexture = 0; + framebufferType = 0; + framebufferPointer = 0; + activeShader = null; + unitToPointerMap = new HashMap(); + this.storeCurrentEnvironmentContraints(); + } + /** * Gets the constraints of the current environment (ie how large can the max texture be) */ - public void storeCurrentEnvironmentContraints(){ + private void storeCurrentEnvironmentContraints(){ //the array used to store values fetched from opengl int[] intFetchArray = new int[1]; @@ -67,6 +85,9 @@ public class OpenGLState { GL40.glGetIntegerv(GL40.GL_MAX_TEXTURE_SIZE, intFetchArray); MAX_TEXTURE_WIDTH = intFetchArray[0]; + //get current framebuffer data + GL40.glGetIntegerv(GL40.GL_DRAW_FRAMEBUFFER_BINDING, intFetchArray); + this.framebufferPointer = intFetchArray[0]; } @@ -153,6 +174,21 @@ public class OpenGLState { } } + /** + * Binds a texture to a given texture unit if the texture hasn't already been bound to that unit + * @param textureUnit The texture unit + * @param texturePointer The texture pointer + * @param textureType the type of texture (2d, 3d, etc) + */ + public void glBindTextureUnitForce(int textureUnit, int texturePointer, int textureType){ + unitToPointerMap.put(textureUnit,texturePointer); + this.activeTexture = textureUnit; + GL40.glActiveTexture(this.activeTexture); + Globals.renderingEngine.checkError(); + GL40.glBindTexture(textureType,texturePointer); + Globals.renderingEngine.checkError(); + } + /** * Binds a framebuffer * @param framebufferType the type of framebuffer (vanilla, renderbuffer, etc) diff --git a/src/main/java/electrosphere/renderer/RenderUtils.java b/src/main/java/electrosphere/renderer/RenderUtils.java index 5dbcbd70..98e6e020 100644 --- a/src/main/java/electrosphere/renderer/RenderUtils.java +++ b/src/main/java/electrosphere/renderer/RenderUtils.java @@ -20,6 +20,8 @@ import org.lwjgl.BufferUtils; import static org.lwjgl.opengl.GL11.GL_FLOAT; import org.lwjgl.opengl.GL40; +import org.lwjgl.util.par.ParShapes; +import org.lwjgl.util.par.ParShapesMesh; /** * Utilities to assist with rendering @@ -339,6 +341,64 @@ public class RenderUtils { return rVal; } + /** + * Generates a unit sphere model + * @return The model + */ + public static Model createUnitsphere(){ + Model model = new Model(); + Mesh sphereMesh = new Mesh("sphere"); + sphereMesh.generateVAO(); + + //buffer coords + ParShapesMesh data = ParShapes.par_shapes_create_parametric_sphere(10, 5); + int numPoints = data.npoints(); + FloatBuffer verts = data.points(numPoints * 3); + sphereMesh.bufferVertices(verts, 3); + FloatBuffer texCoords = data.tcoords(numPoints * 3); + sphereMesh.bufferTextureCoords(texCoords, 2); + + //setup extra structures + Material mat = new Material(); + mat.set_diffuse("Textures/color/transparent_teal.png"); + sphereMesh.setMaterial(mat); + sphereMesh.setShader(ShaderProgram.smart_assemble_shader(false, true)); + GL40.glBindVertexArray(0); + sphereMesh.setParent(model); + model.getMeshes().add(sphereMesh); + + return model; + } + + /** + * Creates a unit cylinder model + * @return The model + */ + public static Model createUnitCylinder(){ + Model model = new Model(); + Mesh sphereMesh = new Mesh("cylinder"); + sphereMesh.generateVAO(); + + //buffer coords + ParShapesMesh data = ParShapes.par_shapes_create_cylinder(10, 2); + int numPoints = data.npoints(); + FloatBuffer verts = data.points(numPoints * 3); + sphereMesh.bufferVertices(verts, 3); + FloatBuffer texCoords = data.tcoords(numPoints * 2); + sphereMesh.bufferTextureCoords(texCoords, 2); + + //setup extra structures + Material mat = new Material(); + mat.set_diffuse("Textures/color/transparent_teal.png"); + sphereMesh.setMaterial(mat); + sphereMesh.setShader(ShaderProgram.smart_assemble_shader(false, true)); + GL40.glBindVertexArray(0); + sphereMesh.setParent(model); + model.getMeshes().add(sphereMesh); + + return model; + } + @Deprecated public static Model createBitmapDisplay(){ diff --git a/src/main/java/electrosphere/renderer/RenderingEngine.java b/src/main/java/electrosphere/renderer/RenderingEngine.java index 36d18a9a..2c1f3bf9 100644 --- a/src/main/java/electrosphere/renderer/RenderingEngine.java +++ b/src/main/java/electrosphere/renderer/RenderingEngine.java @@ -38,6 +38,10 @@ import org.lwjgl.glfw.GLFWErrorCallback; import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL30; +import org.lwjgl.opengl.GL45; +import org.lwjgl.opengl.GLCapabilities; +import org.lwjgl.opengl.GLDebugMessageCallback; +import org.lwjgl.system.MemoryStack; import electrosphere.engine.Globals; import electrosphere.logger.LoggerInterface; @@ -186,12 +190,18 @@ public class RenderingEngine { // //set error callback - GLFW.glfwSetErrorCallback(GLFWErrorCallback.createThrow()); + // GLFWErrorCallback + GLFW.glfwSetErrorCallback((int error, long descriptionPtr) -> { + String description = GLFWErrorCallback.getDescription(descriptionPtr); + System.err.println(description); + }); //Initializes opengl boolean glfwInited = glfwInit(); if(!glfwInited){ - throw new IllegalStateException("Failed to initialize glfw!"); + String message = "Failed to initialize glfw!\n" + + "Error code: " + this.getGLFWErrorMessage(this.getGLFWError()); + throw new IllegalStateException(message); } //Gives hints to glfw to control how opengl will be used glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); @@ -216,9 +226,13 @@ public class RenderingEngine { } // Errors for failure to create window (IE: No GUI mode on linux ?) if (Globals.window == NULL) { - LoggerInterface.loggerEngine.ERROR("Failed to make window.", new Exception("Renderer Creation Failure")); + String message = "Failed to create window!\n" + + "Error code: " + this.getGLFWErrorMessage(this.getGLFWError()); + ; + LoggerInterface.loggerEngine.ERROR(new Exception(message)); glfwTerminate(); } + //set resize callback GLFW.glfwSetWindowSizeCallback(Globals.window, (long window, int width, int height) -> { Globals.WINDOW_HEIGHT = height; @@ -228,6 +242,7 @@ public class RenderingEngine { glfwMakeContextCurrent(Globals.window); //Maximize it glfwMaximizeWindow(Globals.window); + GLFW.glfwPollEvents(); //grab actual framebuffer IntBuffer xBuffer = BufferUtils.createIntBuffer(1); IntBuffer yBuffer = BufferUtils.createIntBuffer(1); @@ -255,11 +270,20 @@ public class RenderingEngine { //get title bar dimensions // setTitleBarDimensions(); - //Creates the OpenGL capabilities for the program. - GL.createCapabilities(); + //Creates the OpenGL capabilities for the program.) + GLCapabilities glCapabilities = GL.createCapabilities(); + + GL45.glEnable(GL45.GL_DEBUG_OUTPUT); + //register error callback + GL45.glDebugMessageCallback((int source, int type, int id, int severity, int length, long messagePtr, long userParam) -> { + if(type == GL45.GL_DEBUG_TYPE_ERROR){ + String message = GLDebugMessageCallback.getMessage(length, messagePtr); + System.err.println(message); + } + }, bufferHeight); //get environment constraints - openGLState.storeCurrentEnvironmentContraints(); + openGLState.init(); //init imgui pipeline imGuiPipeline = new ImGuiPipeline(Globals.window, glslVersion); @@ -290,11 +314,20 @@ public class RenderingEngine { //default framebuffer defaultFramebuffer = new Framebuffer(GL_DEFAULT_FRAMEBUFFER); + defaultFramebuffer.bind(openGLState); //generate framebuffers - screenTextureColor = FramebufferUtils.generateScreenTextureColorAlpha(openGLState, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT); - screenTextureDepth = FramebufferUtils.generateScreenTextureDepth(openGLState, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT); - screenFramebuffer = FramebufferUtils.generateScreenTextureFramebuffer(openGLState, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT, screenTextureColor, screenTextureDepth); + Texture screenTextureColor = FramebufferUtils.generateScreenTextureColorAlpha(openGLState, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT); + RenderingEngine.screenTextureColor = screenTextureColor; + Texture screenTextureDepth = FramebufferUtils.generateScreenTextureDepth(openGLState, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT); + RenderingEngine.screenTextureDepth = screenTextureDepth; + try { + Framebuffer screenFramebuffer = FramebufferUtils.generateScreenTextureFramebuffer(openGLState, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT, screenTextureColor, screenTextureDepth); + RenderingEngine.screenFramebuffer = screenFramebuffer; + } catch (Exception e){ + LoggerInterface.loggerRenderer.ERROR(e); + } + defaultFramebuffer.bind(openGLState); glBindRenderbuffer(GL_RENDERBUFFER, GL_DEFAULT_RENDERBUFFER); Globals.renderingEngine.checkError(); @@ -309,18 +342,28 @@ public class RenderingEngine { // lightDepthShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/core/lightDepth/lightDepth.vs", "/Shaders/core/lightDepth/lightDepth.fs"); Globals.depthMapShaderProgramLoc = lightDepthShaderProgram.getShaderId(); - lightDepthBuffer = FramebufferUtils.generateDepthBuffer(openGLState); - lightBufferDepthTexture = lightDepthBuffer.getDepthTexture(); + try { + Framebuffer lightDepthBuffer = FramebufferUtils.generateDepthBuffer(openGLState); + RenderingEngine.lightDepthBuffer = lightDepthBuffer; + } catch(Exception e){ + LoggerInterface.loggerRenderer.ERROR(e); + } + Texture lightBufferDepthTexture = lightDepthBuffer.getDepthTexture(); + RenderingEngine.lightBufferDepthTexture = lightBufferDepthTexture; // glEnable(GL_CULL_FACE); // enabled for shadow mapping // //create volume depth framebuffer/shader for volumetric rendering // - volumeDepthShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/core/volumeBuffer/volumetric.vs", "/Shaders/core/volumeBuffer/volumetric.fs"); - volumeDepthBackfaceTexture = FramebufferUtils.generateDepthBufferTexture(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY()); - volumeDepthBackfaceFramebuffer = FramebufferUtils.generateDepthBuffer(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY(), volumeDepthBackfaceTexture); - volumeDepthFrontfaceTexture = FramebufferUtils.generateDepthBufferTexture(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY()); - volumeDepthFrontfaceFramebuffer = FramebufferUtils.generateDepthBuffer(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY(), volumeDepthFrontfaceTexture); + try { + volumeDepthShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/core/volumeBuffer/volumetric.vs", "/Shaders/core/volumeBuffer/volumetric.fs"); + volumeDepthBackfaceTexture = FramebufferUtils.generateDepthBufferTexture(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY()); + volumeDepthBackfaceFramebuffer = FramebufferUtils.generateDepthBuffer(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY(), volumeDepthBackfaceTexture); + volumeDepthFrontfaceTexture = FramebufferUtils.generateDepthBufferTexture(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY()); + volumeDepthFrontfaceFramebuffer = FramebufferUtils.generateDepthBuffer(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY(), volumeDepthFrontfaceTexture); + } catch(Exception e){ + LoggerInterface.loggerRenderer.ERROR(e); + } // //Game normals @@ -330,20 +373,28 @@ public class RenderingEngine { static Framebuffer gameImageNormalsFramebuffer; static ShaderProgram renderNormalsShader; */ - gameImageNormalsTexture = FramebufferUtils.generateScreenTextureColorAlpha(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY()); - Texture gameImageNormalsDepthTexture = FramebufferUtils.generateScreenTextureDepth(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY()); - gameImageNormalsFramebuffer = FramebufferUtils.generateScreenTextureFramebuffer(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY(), gameImageNormalsTexture, gameImageNormalsDepthTexture); - renderNormalsShader = ShaderProgram.loadSpecificShader("Shaders/core/anime/renderNormals.vs", "Shaders/core/anime/renderNormals.fs"); + try { + gameImageNormalsTexture = FramebufferUtils.generateScreenTextureColorAlpha(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY()); + Texture gameImageNormalsDepthTexture = FramebufferUtils.generateScreenTextureDepth(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY()); + gameImageNormalsFramebuffer = FramebufferUtils.generateScreenTextureFramebuffer(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY(), gameImageNormalsTexture, gameImageNormalsDepthTexture); + renderNormalsShader = ShaderProgram.loadSpecificShader("Shaders/core/anime/renderNormals.vs", "Shaders/core/anime/renderNormals.fs"); + } catch(Exception e){ + LoggerInterface.loggerRenderer.ERROR(e); + } // //Transparency framebuffers // - transparencyAccumulatorClear = new float[]{0.0f, 0.0f, 0.0f, 0.0f}; - transparencyAccumulatorTexture = FramebufferUtils.generateOITAccumulatorTexture(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY()); - transparencyRevealageClear = new float[]{1.0f, 1.0f, 1.0f, 1.0f}; - transparencyRevealageTexture = FramebufferUtils.generateOITRevealageTexture(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY()); - transparencyBuffer = FramebufferUtils.generateOITFramebuffer(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY(), transparencyAccumulatorTexture, transparencyRevealageTexture, screenTextureDepth); - oitCompositeProgram = ShaderProgram.loadSpecificShader("Shaders/core/oit/composite.vs", "Shaders/core/oit/composite.fs"); + try { + transparencyAccumulatorClear = new float[]{0.0f, 0.0f, 0.0f, 0.0f}; + transparencyAccumulatorTexture = FramebufferUtils.generateOITAccumulatorTexture(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY()); + transparencyRevealageClear = new float[]{1.0f, 1.0f, 1.0f, 1.0f}; + transparencyRevealageTexture = FramebufferUtils.generateOITRevealageTexture(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY()); + transparencyBuffer = FramebufferUtils.generateOITFramebuffer(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY(), transparencyAccumulatorTexture, transparencyRevealageTexture, screenTextureDepth); + oitCompositeProgram = ShaderProgram.loadSpecificShader("Shaders/core/oit/composite.vs", "Shaders/core/oit/composite.fs"); + } catch(Exception e){ + LoggerInterface.loggerRenderer.ERROR(e); + } //projection matrices nearVolumeProjectionMatrix.setPerspective((float)(Globals.verticalFOV * Math.PI /180.0f), (float)Globals.WINDOW_WIDTH / (float)Globals.WINDOW_HEIGHT, 0.1f, 100); @@ -356,10 +407,14 @@ public class RenderingEngine { static Framebuffer normalsOutlineFrambuffer; static ShaderProgram normalsOutlineShader; */ - normalsOutlineTexture = FramebufferUtils.generateScreenTextureColorAlpha(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY()); - normalsOutlineFrambuffer = FramebufferUtils.generateScreenTextureFramebuffer(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY(), normalsOutlineTexture); - // normalsOutlineShader = ShaderProgram.loadSpecificShader("Shaders/anime/outlineNormals.vs", "Shaders/anime/outlineNormals.fs"); - Globals.assetManager.addShaderToQueue("Shaders/core/anime/outlineNormals.vs", "Shaders/core/anime/outlineNormals.fs"); + try { + normalsOutlineTexture = FramebufferUtils.generateScreenTextureColorAlpha(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY()); + normalsOutlineFrambuffer = FramebufferUtils.generateScreenTextureFramebuffer(openGLState, Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY(), normalsOutlineTexture); + // normalsOutlineShader = ShaderProgram.loadSpecificShader("Shaders/anime/outlineNormals.vs", "Shaders/anime/outlineNormals.fs"); + Globals.assetManager.addShaderToQueue("Shaders/core/anime/outlineNormals.vs", "Shaders/core/anime/outlineNormals.fs"); + } catch(Exception e){ + LoggerInterface.loggerRenderer.ERROR(e); + } // //Compositing shaders @@ -409,15 +464,6 @@ public class RenderingEngine { Globals.projectionMatrix.setPerspective(verticalFOV, Globals.aspectRatio, nearClip, Globals.userSettings.getGraphicsViewDistance()); Globals.viewMatrix.translation(new Vector3f(0.0f,0.0f,-3.0f)); } - - /** - * Clears global, static state - */ - public void clearGlobalState(){ - screenTextureColor = null; - screenTextureDepth = null; - screenFramebuffer = null; - } /** @@ -637,7 +683,7 @@ public class RenderingEngine { public boolean checkError(){ int error = this.getError(); if(error != GL11.GL_NO_ERROR){ - LoggerInterface.loggerRenderer.ERROR("checkError - " + getErrorInEnglish(error), new IllegalStateException("OpenGL Error")); + LoggerInterface.loggerRenderer.ERROR("checkError - " + getErrorInEnglish(error), new Exception("OpenGL Error")); return true; } return false; @@ -656,6 +702,34 @@ public class RenderingEngine { return lastCode; } + /** + * Gets the most recent GLFW Error + * @return The most recent GLFW error + */ + public int getGLFWError(){ + int lastCode = 0; + try (MemoryStack stack = MemoryStack.stackPush()){ + lastCode = GLFW.glfwGetError(stack.callocPointer(1)); + } + return lastCode; + } + + /** + * Decodes the glfw error code + * @param code The code + * @return The decoded message + */ + public String getGLFWErrorMessage(int code){ + switch(code){ + case GLFW.GLFW_INVALID_ENUM: + return "GLFW_INVALID_ENUM"; + case GLFW.GLFW_INVALID_VALUE: + return "GLFW_INVALID_VALUE"; + default: + return "Unhandled value!"; + } + } + /** * Checks if pipelines should run * @return true if should render, false otherwise @@ -671,7 +745,83 @@ public class RenderingEngine { * Destroys the rendering engine */ public void destroy(){ + + //free framebuffers + if(screenFramebuffer != null){ + screenFramebuffer.free(); + } + if(screenRenderbuffer != null){ + screenRenderbuffer.free(); + } + if(gameImageNormalsFramebuffer != null){ + gameImageNormalsFramebuffer.free(); + } + if(lightDepthBuffer != null){ + lightDepthBuffer.free(); + } + if(transparencyBuffer != null){ + transparencyBuffer.free(); + } + if(volumeDepthBackfaceFramebuffer != null){ + volumeDepthBackfaceFramebuffer.free(); + } + if(volumeDepthFrontfaceFramebuffer != null){ + volumeDepthFrontfaceFramebuffer.free(); + } + if(normalsOutlineFrambuffer != null){ + normalsOutlineFrambuffer.free(); + } + + //null out + screenFramebuffer = null; + screenRenderbuffer = null; + gameImageNormalsFramebuffer = null; + lightDepthBuffer = null; + transparencyBuffer = null; + volumeDepthBackfaceFramebuffer = null; + volumeDepthFrontfaceFramebuffer = null; + normalsOutlineFrambuffer = null; + + + //free textures + if(screenTextureColor != null){ + screenTextureColor.free(); + } + if(screenTextureDepth != null){ + screenTextureDepth.free(); + } + if(gameImageNormalsTexture != null){ + gameImageNormalsTexture.free(); + } + if(lightBufferDepthTexture != null){ + lightBufferDepthTexture.free(); + } + if(transparencyRevealageTexture != null){ + transparencyRevealageTexture.free(); + } + if(volumeDepthBackfaceTexture != null){ + volumeDepthBackfaceTexture.free(); + } + if(volumeDepthFrontfaceTexture != null){ + volumeDepthFrontfaceTexture.free(); + } + if(normalsOutlineTexture != null){ + normalsOutlineTexture.free(); + } + //null out + screenTextureColor = null; + screenTextureDepth = null; + gameImageNormalsTexture = null; + lightBufferDepthTexture = null; + transparencyRevealageTexture = null; + volumeDepthBackfaceTexture = null; + volumeDepthFrontfaceTexture = null; + normalsOutlineTexture = null; + + //end glfw + GLFW.glfwDestroyWindow(Globals.window); + GLFW.glfwTerminate(); } /** diff --git a/src/main/java/electrosphere/renderer/framebuffer/Framebuffer.java b/src/main/java/electrosphere/renderer/framebuffer/Framebuffer.java index 371b4d1d..22f1f560 100644 --- a/src/main/java/electrosphere/renderer/framebuffer/Framebuffer.java +++ b/src/main/java/electrosphere/renderer/framebuffer/Framebuffer.java @@ -14,8 +14,12 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import org.lwjgl.opengl.GL40; +import org.lwjgl.opengl.GL45; import org.lwjgl.system.MemoryUtil; +import static org.lwjgl.opengl.GL11.GL_NONE; +import static org.lwjgl.opengl.GL11.GL_TEXTURE; +import static org.lwjgl.opengl.GL30.GL_FRAMEBUFFER; import static org.lwjgl.opengl.GL45.glCheckNamedFramebufferStatus; /** @@ -91,29 +95,47 @@ public class Framebuffer { * Checks if the framebuffer compiled correctly * @return true if compiled correctly, false otherwise */ - public boolean isComplete(){ - return glCheckNamedFramebufferStatus(framebufferPointer,GL40.GL_FRAMEBUFFER) == GL40.GL_FRAMEBUFFER_COMPLETE; + public boolean isComplete(OpenGLState openGLState){ + if(this.framebufferPointer == DEFAULT_FRAMEBUFFER_POINTER){ + throw new Error("Pointer is the default framebuffer!"); + } + return glCheckNamedFramebufferStatus(this.framebufferPointer,GL40.GL_FRAMEBUFFER) == GL40.GL_FRAMEBUFFER_COMPLETE; } - /** - * Checks if the framebuffer has an error - * @return true if error, false otherwise - */ - public boolean isError(){ - return glCheckNamedFramebufferStatus(framebufferPointer,GL40.GL_FRAMEBUFFER) == 0; - } /** * Checks the status of the framebuffer + * @param openGLState The opengl state + * @throws Exception */ - public void checkStatus(){ - if(this.isError()){ - LoggerInterface.loggerRenderer.WARNING("Framebuffer [glError] - " + RenderingEngine.getErrorInEnglish(Globals.renderingEngine.getError())); - LoggerInterface.loggerRenderer.WARNING("Framebuffer [status] - " + this.getStatus()); - } else if(!this.isComplete()){ - LoggerInterface.loggerRenderer.WARNING("Framebuffer [glError] - " + RenderingEngine.getErrorInEnglish(Globals.renderingEngine.getError())); - LoggerInterface.loggerRenderer.WARNING("Framebuffer [status] - " + this.getStatus()); - LoggerInterface.loggerRenderer.ERROR("Failed to build framebuffer", new IllegalStateException("Framebuffer failed to build.")); + public void shouldBeComplete(OpenGLState openGLState) throws Exception{ + if(!this.isComplete(openGLState)){ + int colorAttach0 = GL45.glGetFramebufferAttachmentParameteri(GL_FRAMEBUFFER, GL45.GL_COLOR_ATTACHMENT0, GL45.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE); + String attach0Type = ""; + switch(colorAttach0){ + case GL_NONE: { + attach0Type = "GL_NONE"; + } break; + case GL_TEXTURE: { + attach0Type = " GL_TEXTURE"; + } break; + } + this.texture.bind(openGLState); + int[] storage = new int[1]; + int width = 0; + int height = 0; + GL45.glGetTexLevelParameteriv(GL45.GL_TEXTURE_2D, 0, GL45.GL_TEXTURE_WIDTH, storage); + width = storage[0]; + GL45.glGetTexLevelParameteriv(GL45.GL_TEXTURE_2D, 0, GL45.GL_TEXTURE_HEIGHT, storage); + height = storage[0]; + String message = "Framebuffer failed to build.\n" + + "Framebuffer [status] - " + this.getStatus() + "\n" + + "Texture: " + this.texture + "\n" + + "attach0Type: " + attach0Type + "\n" + + "attach0 Dims: " + width + "," + height + "\n" + + "Depth: " + this.depthTexture + "\n" + ; + throw new Exception(message); } } @@ -136,7 +158,7 @@ public class Framebuffer { * Blocks the thread until the framebuffer has compiled */ public void blockUntilCompiled(){ - while(glCheckNamedFramebufferStatus(framebufferPointer,GL40.GL_FRAMEBUFFER) != GL40.GL_FRAMEBUFFER_UNDEFINED){ + while(glCheckNamedFramebufferStatus(this.framebufferPointer,GL40.GL_FRAMEBUFFER) != GL40.GL_FRAMEBUFFER_UNDEFINED){ try { TimeUnit.MILLISECONDS.sleep(1); } catch (InterruptedException ex) { @@ -150,7 +172,7 @@ public class Framebuffer { * @return The status */ public String getStatus(){ - switch(glCheckNamedFramebufferStatus(framebufferPointer,GL40.GL_FRAMEBUFFER)){ + switch(glCheckNamedFramebufferStatus(this.framebufferPointer,GL40.GL_FRAMEBUFFER)){ case GL40.GL_FRAMEBUFFER_UNDEFINED: { return "The specified framebuffer is the default read or draw framebuffer, but the default framebuffer does not exist."; } @@ -220,6 +242,15 @@ public class Framebuffer { openGLState.glBindFramebuffer(GL40.GL_FRAMEBUFFER, this.framebufferPointer); GL40.glFramebufferTexture2D(GL40.GL_FRAMEBUFFER, GL40.GL_COLOR_ATTACHMENT0 + attachmentNum, GL40.GL_TEXTURE_2D, texture.getTexturePointer(), 0); Globals.renderingEngine.checkError(); + // check the attachment slot + int colorAttach0 = GL45.glGetFramebufferAttachmentParameteri(GL40.GL_FRAMEBUFFER, GL45.GL_COLOR_ATTACHMENT0 + attachmentNum, GL45.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE); + switch(colorAttach0){ + case GL_NONE: { + throw new Error("Failed to attach!"); + } + case GL_TEXTURE: { + } break; + } Globals.renderingEngine.defaultFramebuffer.bind(openGLState); } diff --git a/src/main/java/electrosphere/renderer/framebuffer/FramebufferUtils.java b/src/main/java/electrosphere/renderer/framebuffer/FramebufferUtils.java index 5f7fd18c..5899a8ac 100644 --- a/src/main/java/electrosphere/renderer/framebuffer/FramebufferUtils.java +++ b/src/main/java/electrosphere/renderer/framebuffer/FramebufferUtils.java @@ -9,6 +9,7 @@ import java.nio.IntBuffer; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL40; +import org.lwjgl.opengl.GL45; import static org.lwjgl.opengl.GL11.GL_DEPTH_COMPONENT; import static org.lwjgl.opengl.GL11.GL_FLOAT; @@ -46,6 +47,7 @@ public class FramebufferUtils { public static Texture generateScreenTextureColor(OpenGLState openGLState, int width, int height){ Texture texture = new Texture(); + texture.bind(openGLState); texture.glTexImage2D(openGLState, width, height, GL_RGB, GL_UNSIGNED_BYTE); texture.setMinFilter(openGLState, GL_LINEAR); texture.setMagFilter(openGLState, GL_LINEAR); @@ -64,7 +66,8 @@ public class FramebufferUtils { public static Texture generateScreenTextureColorAlpha(OpenGLState openGLState, int width, int height){ Texture texture = new Texture(); - texture.glTexImage2D(openGLState, width, height, GL_RGBA, GL_UNSIGNED_BYTE); + texture.bind(openGLState); + texture.glTexImage2D(openGLState, width, height, GL_RGBA, GL45.GL_UNSIGNED_INT_8_8_8_8); texture.setMinFilter(openGLState, GL_LINEAR); texture.setMagFilter(openGLState, GL_LINEAR); //these make sure the texture actually clamps to the borders of the quad @@ -74,7 +77,7 @@ public class FramebufferUtils { //guarantees that the texture object has actually been created (calling gen buffers does not guarantee object creation) texture.bind(openGLState); - openGLState.glBindTexture(GL40.GL_TEXTURE_2D, Texture.DEFAULT_TEXTURE); + openGLState.glBindTextureUnitForce(GL45.GL_TEXTURE0, Texture.DEFAULT_TEXTURE, GL40.GL_TEXTURE_2D); texture.checkStatus(openGLState); return texture; @@ -82,6 +85,7 @@ public class FramebufferUtils { public static Texture generateScreenTextureDepth(OpenGLState openGLState, int width, int height){ Texture texture = new Texture(); + texture.bind(openGLState); texture.glTexImage2D(openGLState, width, height, GL_DEPTH_COMPONENT, GL_FLOAT); texture.setMinFilter(openGLState, GL_LINEAR); @@ -93,36 +97,37 @@ public class FramebufferUtils { //guarantees that the texture object has actually been created (calling gen buffers does not guarantee object creation) texture.bind(openGLState); - openGLState.glBindTexture(GL40.GL_TEXTURE_2D, Texture.DEFAULT_TEXTURE); + openGLState.glBindTextureUnitForce(GL45.GL_TEXTURE0, Texture.DEFAULT_TEXTURE, GL40.GL_TEXTURE_2D); texture.checkStatus(openGLState); return texture; } - public static Framebuffer generateScreenTextureFramebuffer(OpenGLState openGLState, int width, int height, Texture colorTexture, Texture depthTexture){ + public static Framebuffer generateScreenTextureFramebuffer(OpenGLState openGLState, int width, int height, Texture colorTexture, Texture depthTexture) throws Exception { Framebuffer buffer = new Framebuffer(); //bind texture to fbo buffer.setMipMapLevel(0); buffer.attachTexture(openGLState,colorTexture); buffer.setDepthAttachment(openGLState,depthTexture); + buffer.bind(openGLState); //check make sure compiled - buffer.checkStatus(); + buffer.shouldBeComplete(openGLState); Globals.renderingEngine.defaultFramebuffer.bind(openGLState); return buffer; } - public static Framebuffer generateScreenTextureFramebuffer(OpenGLState openGLState, int width, int height, Texture colorTexture){ + public static Framebuffer generateScreenTextureFramebuffer(OpenGLState openGLState, int width, int height, Texture colorTexture) throws Exception { Framebuffer buffer = new Framebuffer(); //bind texture to fbo buffer.setMipMapLevel(0); buffer.attachTexture(openGLState,colorTexture); //check make sure compiled - buffer.checkStatus(); + buffer.shouldBeComplete(openGLState); return buffer; } - public static Framebuffer generateScreensizeTextureFramebuffer(OpenGLState openGLState){ + public static Framebuffer generateScreensizeTextureFramebuffer(OpenGLState openGLState) throws Exception { Framebuffer buffer = new Framebuffer(); buffer.bind(openGLState); //texture @@ -147,12 +152,12 @@ public class FramebufferUtils { glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderBuffer); Globals.renderingEngine.checkError(); //check make sure compiled - buffer.checkStatus(); + buffer.shouldBeComplete(openGLState); Globals.renderingEngine.defaultFramebuffer.bind(openGLState); return buffer; } - public static Framebuffer generateTextureFramebuffer(OpenGLState openGLState, int width, int height){ + public static Framebuffer generateTextureFramebuffer(OpenGLState openGLState, int width, int height) throws Exception { Framebuffer buffer = new Framebuffer(); buffer.bind(openGLState); //texture @@ -178,7 +183,7 @@ public class FramebufferUtils { GL40.glFramebufferRenderbuffer(GL40.GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderBuffer); Globals.renderingEngine.checkError(); //check make sure compiled - buffer.checkStatus(); + buffer.shouldBeComplete(openGLState); //re-bind default buffer Globals.renderingEngine.defaultFramebuffer.bind(openGLState); return buffer; @@ -197,7 +202,7 @@ public class FramebufferUtils { } - public static Framebuffer generateDepthBuffer(OpenGLState openGLState){ + public static Framebuffer generateDepthBuffer(OpenGLState openGLState) throws Exception { Framebuffer buffer = new Framebuffer(); buffer.bind(openGLState); @@ -223,7 +228,7 @@ public class FramebufferUtils { //check make sure compiled - buffer.checkStatus(); + buffer.shouldBeComplete(openGLState); Globals.renderingEngine.defaultFramebuffer.bind(openGLState); return buffer; } @@ -241,7 +246,7 @@ public class FramebufferUtils { return texture; } - public static Framebuffer generateDepthBuffer(OpenGLState openGLState, int width, int height, Texture texture){ + public static Framebuffer generateDepthBuffer(OpenGLState openGLState, int width, int height, Texture texture) throws Exception { Framebuffer buffer = new Framebuffer(); buffer.bind(openGLState); @@ -257,7 +262,7 @@ public class FramebufferUtils { //check make sure compiled - buffer.checkStatus(); + buffer.shouldBeComplete(openGLState); Globals.renderingEngine.defaultFramebuffer.bind(openGLState); return buffer; } @@ -280,7 +285,7 @@ public class FramebufferUtils { return texture; } - public static Framebuffer generateOITFramebuffer(OpenGLState openGLState, int width, int height, Texture accumulatorTex, Texture revealageTex, Texture depthTexture){ + public static Framebuffer generateOITFramebuffer(OpenGLState openGLState, int width, int height, Texture accumulatorTex, Texture revealageTex, Texture depthTexture) throws Exception { Framebuffer buffer = new Framebuffer(); buffer.bind(openGLState); @@ -301,7 +306,7 @@ public class FramebufferUtils { Globals.renderingEngine.checkError(); //check make sure compiled - buffer.checkStatus(); + buffer.shouldBeComplete(openGLState); Globals.renderingEngine.defaultFramebuffer.bind(openGLState); return buffer; } diff --git a/src/main/java/electrosphere/renderer/pipelines/debug/DebugBonesPipeline.java b/src/main/java/electrosphere/renderer/pipelines/debug/DebugBonesPipeline.java index c699d208..0ea79ff9 100644 --- a/src/main/java/electrosphere/renderer/pipelines/debug/DebugBonesPipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/debug/DebugBonesPipeline.java @@ -7,6 +7,7 @@ import org.joml.Vector3f; import org.lwjgl.opengl.GL40; import electrosphere.engine.Globals; +import electrosphere.engine.assetmanager.AssetDataStrings; import electrosphere.entity.Entity; import electrosphere.entity.EntityUtils; import electrosphere.entity.types.camera.CameraEntityUtils; @@ -68,7 +69,7 @@ public class DebugBonesPipeline implements RenderPipeline { //Get target data // Actor targetActor = EntityUtils.getActor(targetEntity); - Model boneModel = Globals.assetManager.fetchModel("Models/basic/geometry/unitcylinder.fbx"); + Model boneModel = Globals.assetManager.fetchModel(AssetDataStrings.UNITCYLINDER); boneModel.getMaterials().get(0).set_diffuse(Globals.textureDiffuseDefault); for(Bone bone : targetActor.getBoneValues()){ Vector3d bonePos = MathBones.getBoneWorldPosition(targetEntity, bone.boneID); diff --git a/src/main/java/electrosphere/renderer/pipelines/debug/DebugContentPipeline.java b/src/main/java/electrosphere/renderer/pipelines/debug/DebugContentPipeline.java index 37947f84..c0ab8724 100644 --- a/src/main/java/electrosphere/renderer/pipelines/debug/DebugContentPipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/debug/DebugContentPipeline.java @@ -13,6 +13,7 @@ import org.ode4j.ode.DSphere; import electrosphere.collision.PhysicsUtils; import electrosphere.collision.collidable.Collidable; import electrosphere.engine.Globals; +import electrosphere.engine.assetmanager.AssetDataStrings; import electrosphere.entity.Entity; import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; @@ -78,7 +79,7 @@ public class DebugContentPipeline implements RenderPipeline { if(geom instanceof DSphere){ DSphere sphereView = (DSphere)geom; HitboxState shapeStatus = hitboxState.getShapeStatus(geom); - if((hitboxModel = Globals.assetManager.fetchModel("Models/basic/geometry/unitsphere.glb")) != null){ + if((hitboxModel = Globals.assetManager.fetchModel(AssetDataStrings.UNITSPHERE)) != null){ //set color based on collision status, type, etc Texture texture = Globals.assetManager.fetchTexture(getHitboxColor(shapeStatus,shapeStatus.getHitboxData())); if(texture != null){ @@ -133,7 +134,7 @@ public class DebugContentPipeline implements RenderPipeline { if(geom instanceof DSphere){ DSphere sphereView = (DSphere)geom; HitboxState shapeStatus = hitboxState.getShapeStatus(geom); - if((hitboxModel = Globals.assetManager.fetchModel("Models/basic/geometry/unitsphere.glb")) != null){ + if((hitboxModel = Globals.assetManager.fetchModel(AssetDataStrings.UNITSPHERE)) != null){ //set color based on collision status, type, etc Texture texture = Globals.assetManager.fetchTexture(getHitboxColor(shapeStatus,shapeStatus.getHitboxData())); if(texture != null){ diff --git a/src/main/java/electrosphere/renderer/texture/Texture.java b/src/main/java/electrosphere/renderer/texture/Texture.java index df0c659f..92f943a3 100644 --- a/src/main/java/electrosphere/renderer/texture/Texture.java +++ b/src/main/java/electrosphere/renderer/texture/Texture.java @@ -390,13 +390,27 @@ public class Texture { this.height = height; this.pixelFormat = format; this.datatype = datatype; + int internalFormat = format; + if(internalFormat == GL45.GL_DEPTH_COMPONENT){ + internalFormat = GL45.GL_DEPTH_COMPONENT24; + } //static values going into call int level = 0; int border = 0; //this must be 0 according to docs openGLState.glBindTexture(GL_TEXTURE_2D,texturePointer); Globals.renderingEngine.checkError(); - GL40.glTexImage2D(GL_TEXTURE_2D, level, format, width, height, border, format, datatype, MemoryUtil.NULL); + GL40.glTexImage2D(GL_TEXTURE_2D, level, internalFormat, width, height, border, format, datatype, MemoryUtil.NULL); Globals.renderingEngine.checkError(); + int[] storage = new int[1]; + int discoveredWidth = 0; + int discoveredHeight = 0; + GL45.glGetTexLevelParameteriv(GL45.GL_TEXTURE_2D, 0, GL45.GL_TEXTURE_WIDTH, storage); + discoveredWidth = storage[0]; + GL45.glGetTexLevelParameteriv(GL45.GL_TEXTURE_2D, 0, GL45.GL_TEXTURE_HEIGHT, storage); + discoveredHeight = storage[0]; + if(width != discoveredWidth || height != discoveredHeight){ + throw new Error("Found dims aren't the same! " + width + "," + height + " vs " + discoveredWidth + "," + discoveredHeight); + } } /** @@ -528,6 +542,13 @@ public class Texture { } } + /** + * Frees the texture + */ + public void free(){ + GL40.glDeleteTextures(this.texturePointer); + } + @Override public String toString(){ String rVal = "" + diff --git a/src/main/java/electrosphere/renderer/ui/elements/ActorPanel.java b/src/main/java/electrosphere/renderer/ui/elements/ActorPanel.java index 9a5f023f..ec8630ff 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/ActorPanel.java +++ b/src/main/java/electrosphere/renderer/ui/elements/ActorPanel.java @@ -58,7 +58,11 @@ public class ActorPanel extends StandardElement implements DrawableElement, Drag public ActorPanel(OpenGLState openGLState, int x, int y, int width, int height, Actor actor){ super(); - elementBuffer = FramebufferUtils.generateTextureFramebuffer(openGLState, width, height); + try { + elementBuffer = FramebufferUtils.generateTextureFramebuffer(openGLState, width, height); + } catch(Exception e){ + LoggerInterface.loggerRenderer.ERROR(e); + } customMat.setTexturePointer(elementBuffer.getTexture().getTexturePointer()); this.actor = actor; this.internalPositionX = x; diff --git a/src/main/java/electrosphere/renderer/ui/elements/ScrollableContainer.java b/src/main/java/electrosphere/renderer/ui/elements/ScrollableContainer.java index e35cdb3c..a4ac508f 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/ScrollableContainer.java +++ b/src/main/java/electrosphere/renderer/ui/elements/ScrollableContainer.java @@ -37,7 +37,11 @@ public class ScrollableContainer extends StandardContainerElement implements Dra public ScrollableContainer(OpenGLState openGLState, int positionX, int positionY, int width, int height){ super(); - widgetBuffer = FramebufferUtils.generateTextureFramebuffer(openGLState, width, height); + try { + widgetBuffer = FramebufferUtils.generateTextureFramebuffer(openGLState, width, height); + } catch(Exception e){ + LoggerInterface.loggerRenderer.ERROR(e); + } // widgetBuffer = FramebufferUtils.generateScreensizeTextureFramebuffer(); customMat.setTexturePointer(widgetBuffer.getTexture().getTexturePointer()); // customMat.setTexturePointer(Globals.assetManager.fetchTexture("Textures/Testing1.png").getTexturePointer()); diff --git a/src/main/java/electrosphere/renderer/ui/elements/Window.java b/src/main/java/electrosphere/renderer/ui/elements/Window.java index fc7f89df..daf54db8 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/Window.java +++ b/src/main/java/electrosphere/renderer/ui/elements/Window.java @@ -71,7 +71,11 @@ public class Window implements DrawableElement, ContainerElement, NavigableEleme * @param height */ public Window(OpenGLState openGLState, int positionX, int positionY, int width, int height, boolean showDecorations){ - widgetBuffer = FramebufferUtils.generateTextureFramebuffer(openGLState, width, height); + try { + widgetBuffer = FramebufferUtils.generateTextureFramebuffer(openGLState, width, height); + } catch(Exception e){ + LoggerInterface.loggerRenderer.ERROR(e); + } customMat.setTexturePointer(widgetBuffer.getTexture().getTexturePointer()); float ndcWidth = (float)width/Globals.WINDOW_WIDTH; float ndcHeight = (float)height/Globals.WINDOW_HEIGHT; diff --git a/src/main/java/electrosphere/server/gen/DungeonGen.java b/src/main/java/electrosphere/server/gen/DungeonGen.java new file mode 100644 index 00000000..80dd9c6c --- /dev/null +++ b/src/main/java/electrosphere/server/gen/DungeonGen.java @@ -0,0 +1,10 @@ +package electrosphere.server.gen; + +/** + * Generates dungeons + */ +public class DungeonGen { + + + +} \ No newline at end of file diff --git a/src/main/java/electrosphere/util/ds/DirectedGraph.java b/src/main/java/electrosphere/util/ds/DirectedGraph.java new file mode 100644 index 00000000..4384d9bc --- /dev/null +++ b/src/main/java/electrosphere/util/ds/DirectedGraph.java @@ -0,0 +1,161 @@ +package electrosphere.util.ds; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * A directed graph + */ +public class DirectedGraph { + + /** + * The list of nodes in the graph + */ + List nodes; + + /** + * Constructor + */ + public DirectedGraph(){ + this.nodes = new LinkedList(); + } + + /** + * Creates a node in the graph + * @param data The data in the node + * @return The node + */ + public GraphNode createNode(Object data){ + GraphNode rVal = new GraphNode(); + this.nodes.add(rVal); + return rVal; + } + + /** + * Adds direction as a neighbor of source. Does not create a connection from direction to source + * @param source The source node + * @param direction The destination node + */ + public void pointNode(GraphNode source, GraphNode direction){ + if(!source.containsNeighbor(direction)){ + source.addNeighbor(direction); + } + } + + /** + * Mutually connects two nodes + * @param node1 Node 1 + * @param node2 Node 2 + */ + public void connectNodes(GraphNode node1, GraphNode node2){ + if(!node1.containsNeighbor(node2)){ + node1.addNeighbor(node2); + } + if(!node2.containsNeighbor(node1)){ + node2.addNeighbor(node1); + } + } + + /** + * Destroys a node + * @param node The node to destroy + */ + public void destroyNode(GraphNode node){ + for(GraphNode toEval : this.nodes){ + if(toEval != node){ + if(toEval.containsNeighbor(node)){ + toEval.removeNeighbor(node); + } + } + } + this.nodes.remove(node); + } + + /** + * Gets the nodes in the graph + * @return The list of all nodes in the graph + */ + public List getNodes(){ + return Collections.unmodifiableList(this.nodes); + } + + + + /** + * A node in a graph + */ + public static class GraphNode { + + /** + * The data at the node + */ + Object data; + + /** + * The neighbors of this graph node + */ + List neighbors; + + /** + * Creates a graph node + * @param data The data to put in the node + */ + public GraphNode(Object data){ + this.data = data; + this.neighbors = new LinkedList(); + } + + /** + * Creates an empty graph node + */ + public GraphNode(){ + this.data = null; + this.neighbors = new LinkedList(); + } + + /** + * Gets the data at this node + * @return The data + */ + public Object getData(){ + return this.data; + } + + /** + * Gets the neighbors of this node + * @return The list of neighbors + */ + public List getNeighbors(){ + return this.neighbors; + } + + /** + * Adds a neighbor to this node + * @param neighbor The neighbor + */ + public void addNeighbor(GraphNode neighbor){ + this.neighbors.add(neighbor); + } + + /** + * Removes a neighbor from this node + * @param neighbor THe neighbor + */ + public void removeNeighbor(GraphNode neighbor){ + this.neighbors.remove(neighbor); + } + + /** + * Checks if this node contains a given node as a neighbor + * @param node The potential neighbor to check + * @return true if the node is a neighbor, false otherwise + */ + public boolean containsNeighbor(GraphNode node){ + return this.neighbors.contains(node); + } + + } + + +} diff --git a/src/test/java/electrosphere/engine/StartupExtensionTests.java b/src/test/java/electrosphere/engine/StartupExtensionTests.java new file mode 100644 index 00000000..fef796b5 --- /dev/null +++ b/src/test/java/electrosphere/engine/StartupExtensionTests.java @@ -0,0 +1,38 @@ +package electrosphere.engine; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.extension.ExtendWith; + +import electrosphere.test.annotations.IntegrationTest; +import electrosphere.test.template.extensions.EntityExtension; +import electrosphere.test.template.extensions.StateCleanupCheckerExtension; + +/** + * Tests for startup extensions + */ +@ExtendWith(StateCleanupCheckerExtension.class) +public class StartupExtensionTests { + + @IntegrationTest + public void test_EntityExtension_Startup(){ + assertDoesNotThrow(() -> { + EntityExtension ext = new EntityExtension(); + ext.beforeEach(null); + ext.afterEach(null); + }); + } + + @IntegrationTest + public void test_EntityExtension_RepeatStartup(){ + assertDoesNotThrow(() -> { + EntityExtension ext = new EntityExtension(); + int someNumberOfTests = 3; + for(int i = 0; i < someNumberOfTests; i++){ + ext.beforeEach(null); + ext.afterEach(null); + } + }); + } + +} diff --git a/src/test/java/electrosphere/renderer/RenderingEngineTests.java b/src/test/java/electrosphere/renderer/RenderingEngineTests.java new file mode 100644 index 00000000..fa55bd58 --- /dev/null +++ b/src/test/java/electrosphere/renderer/RenderingEngineTests.java @@ -0,0 +1,30 @@ +package electrosphere.renderer; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import electrosphere.engine.Globals; +import electrosphere.test.template.extensions.StateCleanupCheckerExtension; + +/** + * Tests for the core rendering engine + */ +@ExtendWith(StateCleanupCheckerExtension.class) +public class RenderingEngineTests { + + @Test + public void testRenderingEngineResetsAllState(){ + assertDoesNotThrow(() -> { + for(int i = 0; i < 5; i++){ + Globals.initGlobals(); + Globals.renderingEngine = new RenderingEngine(); + Globals.renderingEngine.createOpenglContext(); + Globals.renderingEngine.destroy(); + Globals.resetGlobals(); + } + }); + } + +} \ No newline at end of file diff --git a/src/test/java/electrosphere/renderer/framebuffer/FramebufferUtilsTests.java b/src/test/java/electrosphere/renderer/framebuffer/FramebufferUtilsTests.java new file mode 100644 index 00000000..f873d14e --- /dev/null +++ b/src/test/java/electrosphere/renderer/framebuffer/FramebufferUtilsTests.java @@ -0,0 +1,59 @@ +package electrosphere.renderer.framebuffer; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import electrosphere.engine.Globals; +import electrosphere.renderer.RenderingEngine; +import electrosphere.renderer.texture.Texture; +import electrosphere.test.template.extensions.StateCleanupCheckerExtension; + +/** + * Tests for framebuffer creation utilities + */ +@ExtendWith(StateCleanupCheckerExtension.class) +public class FramebufferUtilsTests { + + @Test + public void testCreateScreenFramebuffer(){ + Globals.initGlobals(); + Globals.renderingEngine = new RenderingEngine(); + Globals.renderingEngine.createOpenglContext(); + assertDoesNotThrow(() -> { + Texture screenTextureColor = FramebufferUtils.generateScreenTextureColorAlpha(Globals.renderingEngine.getOpenGLState(), Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT); + RenderingEngine.screenTextureColor = screenTextureColor; + Texture screenTextureDepth = FramebufferUtils.generateScreenTextureDepth(Globals.renderingEngine.getOpenGLState(), Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT); + RenderingEngine.screenTextureDepth = screenTextureDepth; + Framebuffer screenFramebuffer = FramebufferUtils.generateScreenTextureFramebuffer(Globals.renderingEngine.getOpenGLState(), Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT, screenTextureColor, screenTextureDepth); + RenderingEngine.screenFramebuffer = screenFramebuffer; + }); + Globals.renderingEngine.destroy(); + Globals.resetGlobals(); + } + + @Test + public void testCreateScreenFramebufferRepeat(){ + assertDoesNotThrow(() -> { + Globals.initGlobals(); + Globals.renderingEngine = new RenderingEngine(); + Globals.renderingEngine.createOpenglContext(); + Texture screenTextureColor = FramebufferUtils.generateScreenTextureColorAlpha(Globals.renderingEngine.getOpenGLState(), Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT); + Texture screenTextureDepth = FramebufferUtils.generateScreenTextureDepth(Globals.renderingEngine.getOpenGLState(), Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT); + FramebufferUtils.generateScreenTextureFramebuffer(Globals.renderingEngine.getOpenGLState(), Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT, screenTextureColor, screenTextureDepth); + Globals.renderingEngine.destroy(); + Globals.resetGlobals(); + + Globals.initGlobals(); + Globals.renderingEngine = new RenderingEngine(); + Globals.renderingEngine.createOpenglContext(); + screenTextureColor = FramebufferUtils.generateScreenTextureColorAlpha(Globals.renderingEngine.getOpenGLState(), Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT); + screenTextureDepth = FramebufferUtils.generateScreenTextureDepth(Globals.renderingEngine.getOpenGLState(), Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT); + FramebufferUtils.generateScreenTextureFramebuffer(Globals.renderingEngine.getOpenGLState(), Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT, screenTextureColor, screenTextureDepth); + Globals.renderingEngine.destroy(); + Globals.resetGlobals(); + }); + } + +} diff --git a/src/test/java/electrosphere/test/testutils/EngineInit.java b/src/test/java/electrosphere/test/testutils/EngineInit.java index ad3b5986..79d94c5c 100644 --- a/src/test/java/electrosphere/test/testutils/EngineInit.java +++ b/src/test/java/electrosphere/test/testutils/EngineInit.java @@ -10,8 +10,6 @@ import electrosphere.engine.loadingthreads.LoadingThread; import electrosphere.engine.loadingthreads.LoadingThread.LoadingThreadType; import electrosphere.engine.profiler.Profiler; import electrosphere.net.NetUtils; -import electrosphere.renderer.ui.elementtypes.DrawableElement; -import electrosphere.renderer.ui.elementtypes.Element; public class EngineInit { @@ -85,28 +83,6 @@ public class EngineInit { LoadingThread loadingThread = new LoadingThread(LoadingThreadType.LOAD_VIEWPORT); Globals.threadManager.start(loadingThread); - // - //wait for client to be fully init'd - int frames = 0; - while(Globals.threadManager.isLoading()){ - TestEngineUtils.simulateFrames(1); - try { - TimeUnit.MILLISECONDS.sleep(1); - } catch (InterruptedException e) { - e.printStackTrace(); - } - frames++; - if(frames > MAX_FRAMES_TO_WAIT){ - String errorMessage = "Failed to setup connected test scene!\n" + - "Still running threads are:\n" - ; - for(LoadingThread thread : Globals.threadManager.getLoadingThreads()){ - errorMessage = errorMessage + thread.getType() + "\n"; - } - Assertions.fail("Failed to startup"); - } - } - TestEngineUtils.flush(); } diff --git a/src/test/java/electrosphere/test/testutils/TestEngineUtils.java b/src/test/java/electrosphere/test/testutils/TestEngineUtils.java index 018f27c6..6eb2f4f7 100644 --- a/src/test/java/electrosphere/test/testutils/TestEngineUtils.java +++ b/src/test/java/electrosphere/test/testutils/TestEngineUtils.java @@ -2,10 +2,15 @@ package electrosphere.test.testutils; import static org.junit.jupiter.api.Assertions.*; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Assertions; + import java.util.function.Supplier; import electrosphere.engine.Globals; import electrosphere.engine.Main; +import electrosphere.engine.loadingthreads.LoadingThread; import electrosphere.entity.Entity; /** @@ -87,6 +92,28 @@ public class TestEngineUtils { * Flushes any signals that haven't been processed yet */ public static void flush(){ + // + //wait for client to be fully init'd + int frames = 0; + while(Globals.threadManager.isLoading()){ + TestEngineUtils.simulateFrames(1); + try { + TimeUnit.MILLISECONDS.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + frames++; + if(frames > EngineInit.MAX_FRAMES_TO_WAIT){ + String errorMessage = "Failed to setup connected test scene!\n" + + "Still running threads are:\n" + ; + for(LoadingThread thread : Globals.threadManager.getLoadingThreads()){ + errorMessage = errorMessage + thread.getType() + "\n"; + } + Assertions.fail("Failed to startup"); + } + } + while( Globals.elementService.getSignalQueueCount() > 0 ){ diff --git a/src/test/java/electrosphere/util/ds/DirectedGraphUnitTests.java b/src/test/java/electrosphere/util/ds/DirectedGraphUnitTests.java new file mode 100644 index 00000000..d552af3c --- /dev/null +++ b/src/test/java/electrosphere/util/ds/DirectedGraphUnitTests.java @@ -0,0 +1,115 @@ +package electrosphere.util.ds; + +import static org.junit.jupiter.api.Assertions.*; + +import electrosphere.test.annotations.FastTest; +import electrosphere.test.annotations.UnitTest; +import electrosphere.util.ds.DirectedGraph.GraphNode; + +/** + * Unit tests for the directed graph implementation + */ +public class DirectedGraphUnitTests { + + + // + //Graph-specific tests + // + + @UnitTest + @FastTest + public void testCreateGraph(){ + DirectedGraph graph = new DirectedGraph(); + assertNotNull(graph); + } + + @UnitTest + @FastTest + public void testCreateEntry(){ + DirectedGraph graph = new DirectedGraph(); + GraphNode node = graph.createNode(null); + assertNotNull(node); + } + + @UnitTest + @FastTest + public void testContainsEntryNotNull(){ + DirectedGraph graph = new DirectedGraph(); + graph.createNode(null); + assertNotNull(graph.getNodes()); + } + + @UnitTest + @FastTest + public void testContainsEntryHasEntry(){ + DirectedGraph graph = new DirectedGraph(); + graph.createNode(null); + assertEquals(1,graph.getNodes().size()); + } + + @UnitTest + @FastTest + public void testDeleteNode(){ + DirectedGraph graph = new DirectedGraph(); + GraphNode node = graph.createNode(null); + graph.destroyNode(node); + assertEquals(0,graph.getNodes().size()); + } + + + // + //Node-specific tests + // + + @UnitTest + @FastTest + public void testCreateNode(){ + GraphNode node = new GraphNode(); + assertNotNull(node); + } + + @UnitTest + @FastTest + public void testNodeGetData(){ + GraphNode node = new GraphNode("some data"); + assertEquals("some data", node.getData()); + } + + @UnitTest + @FastTest + public void testAddNeighbor(){ + GraphNode node = new GraphNode(); + GraphNode neighbor = new GraphNode(); + node.addNeighbor(neighbor); + assertEquals(neighbor, node.getNeighbors().get(0)); + } + + @UnitTest + @FastTest + public void testRemoveNeighbor(){ + GraphNode node = new GraphNode(); + GraphNode neighbor = new GraphNode(); + node.addNeighbor(neighbor); + node.removeNeighbor(neighbor); + assertEquals(0, node.getNeighbors().size()); + } + + @UnitTest + @FastTest + public void testGetNeighbors(){ + GraphNode node = new GraphNode(); + GraphNode neighbor = new GraphNode(); + node.addNeighbor(neighbor); + assertEquals(1, node.getNeighbors().size()); + } + + @UnitTest + @FastTest + public void testContainsNeighbor(){ + GraphNode node = new GraphNode(); + GraphNode neighbor = new GraphNode(); + node.addNeighbor(neighbor); + assertEquals(true, node.containsNeighbor(neighbor)); + } + +}