diff --git a/docs/src/highlevel-design/creatures/individual/skeletonprogress.md b/docs/src/highlevel-design/creatures/individual/skeletonprogress.md index 46f72939..dfb90898 100644 --- a/docs/src/highlevel-design/creatures/individual/skeletonprogress.md +++ b/docs/src/highlevel-design/creatures/individual/skeletonprogress.md @@ -1,6 +1,6 @@ -@page humanprogress Human +@page skeletonprogres Skeleton -Progress on the human creature +Progress on the skeleton creature ## Third Person Model - [X] Meshed diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index adfe928f..1fe18ae7 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -765,6 +765,7 @@ Fix movement packet timing bug Fix all items spawning above player head Fix items falling below terrain Fix gridded data cell manager saving attached items on realm save +Fix render signals caching between frames (not reseting global flags per usual) # TODO diff --git a/src/main/java/electrosphere/engine/Globals.java b/src/main/java/electrosphere/engine/Globals.java index 1edade3e..5058fcff 100644 --- a/src/main/java/electrosphere/engine/Globals.java +++ b/src/main/java/electrosphere/engine/Globals.java @@ -240,8 +240,8 @@ public class Globals { //OpenGL - Other // - public static int WINDOW_WIDTH = 1920; - public static int WINDOW_HEIGHT = 1080; + public static int WINDOW_WIDTH; + public static int WINDOW_HEIGHT; public static boolean WINDOW_DECORATED = true; //used to control whether the window is created with decorations or not (ie for testing) public static boolean WINDOW_FULLSCREEN = false; //used to control whether the window is created fullscreen or not (ie for testing) @@ -444,10 +444,22 @@ public class Globals { Globals.javaPID = ManagementFactory.getRuntimeMXBean().getName(); } //load user settings + Globals.WINDOW_WIDTH = 1920; + Globals.WINDOW_HEIGHT = 1080; UserSettings.loadUserSettings(); //timekeeper timekeeper = new Timekeeper(); - threadManager = new ThreadManager(); + Globals.threadManager = new ThreadManager(); + Globals.threadManager.init(); + + //render flags + RENDER_FLAG_RENDER_SHADOW_MAP = false; + RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER_CONTENT = false; + RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER = false; + RENDER_FLAG_RENDER_BLACK_BACKGROUND = true; + RENDER_FLAG_RENDER_WHITE_BACKGROUND = false; + RENDER_FLAG_RENDER_UI = true; + RENDER_FLAG_RENDER_UI_BOUNDS = false; //load in default texture map textureMapDefault = TextureMap.construct("Textures/default_texture_map.json"); //load model pretransforms @@ -667,6 +679,7 @@ public class Globals { Globals.clientScene = null; Globals.audioEngine = null; Globals.renderingEngine = null; + Globals.threadManager = null; Globals.signalSystem = null; Globals.serviceManager = null; Globals.clientConnection = null; @@ -681,6 +694,7 @@ public class Globals { Globals.RENDER_FLAG_RENDER_UI = false; Globals.RENDER_FLAG_RENDER_BLACK_BACKGROUND = false; Globals.RENDER_FLAG_RENDER_WHITE_BACKGROUND = false; + Globals.window = -1; LoggerInterface.destroyLoggers(); } diff --git a/src/main/java/electrosphere/engine/threads/ThreadManager.java b/src/main/java/electrosphere/engine/threads/ThreadManager.java index 4d643f33..23a189b3 100644 --- a/src/main/java/electrosphere/engine/threads/ThreadManager.java +++ b/src/main/java/electrosphere/engine/threads/ThreadManager.java @@ -17,18 +17,27 @@ import electrosphere.util.CodeUtils; public class ThreadManager { //Threadsafes the manager - Semaphore threadLock = new Semaphore(1); + Semaphore threadLock; //All threads that are actively running - private List activeThreads = new LinkedList(); + private List activeThreads; //All loading threads that are actively running - private List loadingThreads = new LinkedList(); + private List loadingThreads; //Used by main thread to alert other threads whether they should keep running or not - private boolean shouldKeepRunning = true; + private boolean shouldKeepRunning; + /** + * Initializes the thread manager + */ + public void init(){ + threadLock = new Semaphore(1); + activeThreads = new LinkedList(); + loadingThreads = new LinkedList(); + shouldKeepRunning = true; + } /** * Updates what threads are being tracked diff --git a/src/main/java/electrosphere/renderer/OpenGLState.java b/src/main/java/electrosphere/renderer/OpenGLState.java index 15adc76c..3d0ab4f8 100644 --- a/src/main/java/electrosphere/renderer/OpenGLState.java +++ b/src/main/java/electrosphere/renderer/OpenGLState.java @@ -20,7 +20,7 @@ import electrosphere.renderer.shader.ShaderProgram; public class OpenGLState { //tracks whether caching should be used or not (to deduplicate opengl calls) - private static final boolean DISABLE_CACHING = false; + private static final boolean DISABLE_CACHING = true; //the max texture allowed by the current environment int MAX_TEXTURE_WIDTH; @@ -67,14 +67,18 @@ public class OpenGLState { */ public void init(){ this.MAX_TEXTURE_WIDTH = 0; - this.viewport = new Vector2i(0,0); + this.viewport = new Vector2i(Globals.WINDOW_WIDTH,Globals.WINDOW_HEIGHT); + GL45.glViewport(0, 0, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT); this.depthTest = false; + GL45.glDisable(GL45.GL_DEPTH_TEST); this.depthFunction = -1; this.blendTest = false; + GL45.glDisable(GL45.GL_BLEND); this.blendFuncMap = new HashMap(); - activeTexture = 0; - framebufferType = 0; + activeTexture = -1; + framebufferType = GL45.GL_FRAMEBUFFER; framebufferPointer = 0; + GL45.glBindFramebuffer(this.framebufferType, this.framebufferPointer); activeShader = null; this.unitToPointerMap = new HashMap(); this.indexBlockMap = new HashMap(); diff --git a/src/main/java/electrosphere/renderer/RenderingEngine.java b/src/main/java/electrosphere/renderer/RenderingEngine.java index 36b6eb7d..f5c70533 100644 --- a/src/main/java/electrosphere/renderer/RenderingEngine.java +++ b/src/main/java/electrosphere/renderer/RenderingEngine.java @@ -1,20 +1,6 @@ package electrosphere.renderer; import static electrosphere.renderer.RenderUtils.createScreenTextureVAO; -import static org.lwjgl.glfw.GLFW.GLFW_CONTEXT_VERSION_MAJOR; -import static org.lwjgl.glfw.GLFW.GLFW_CONTEXT_VERSION_MINOR; -import static org.lwjgl.glfw.GLFW.GLFW_FALSE; -import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_CORE_PROFILE; -import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_PROFILE; -import static org.lwjgl.glfw.GLFW.glfwCreateWindow; -import static org.lwjgl.glfw.GLFW.glfwGetPrimaryMonitor; -import static org.lwjgl.glfw.GLFW.glfwInit; -import static org.lwjgl.glfw.GLFW.glfwMakeContextCurrent; -import static org.lwjgl.glfw.GLFW.glfwMaximizeWindow; -import static org.lwjgl.glfw.GLFW.glfwPollEvents; -import static org.lwjgl.glfw.GLFW.glfwSwapBuffers; -import static org.lwjgl.glfw.GLFW.glfwTerminate; -import static org.lwjgl.glfw.GLFW.glfwWindowHint; import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST; import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_ALPHA; @@ -190,47 +176,52 @@ public class RenderingEngine { // //set error callback - // GLFWErrorCallback + // GLFW.glfwSetErrorCallback((int error, long descriptionPtr) -> { String description = GLFWErrorCallback.getDescription(descriptionPtr); System.err.println(description); }); //Initializes opengl - boolean glfwInited = glfwInit(); + boolean glfwInited = GLFW.glfwInit(); if(!glfwInited){ 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); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glslVersion = "#version 430"; - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 4); + GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 5); + glslVersion = "#version 450"; + GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE); //headless option if(Globals.RUN_HIDDEN){ - glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE); + GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE); } if(!Globals.WINDOW_DECORATED){ - glfwWindowHint(GLFW.GLFW_DECORATED, GLFW_FALSE); + GLFW.glfwWindowHint(GLFW.GLFW_DECORATED, GLFW.GLFW_FALSE); + } + if(Globals.ENGINE_DEBUG){ + GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_DEBUG_CONTEXT, GLFW.GLFW_TRUE); + } + + if(Globals.userSettings.getDisplayWidth() <= 0 || Globals.userSettings.getDisplayHeight() <= 0){ + throw new Error("Trying to create window with width or height less than 1! " + Globals.userSettings.getDisplayWidth() + " " + Globals.userSettings.getDisplayHeight()); } -// glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); Allows you to make the background transparent -// glfwWindowHint(GLFW_OPACITY, 23); //Creates the window reference object if(Globals.userSettings.displayFullscreen() || Globals.WINDOW_FULLSCREEN){ //below line is for fullscreen - Globals.window = glfwCreateWindow(Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT, "ORPG", glfwGetPrimaryMonitor(), NULL); + Globals.window = GLFW.glfwCreateWindow(Globals.userSettings.getDisplayWidth(), Globals.userSettings.getDisplayHeight(), "ORPG", GLFW.glfwGetPrimaryMonitor(), NULL); } else { - Globals.window = glfwCreateWindow(Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT, "ORPG", NULL, NULL); + Globals.window = GLFW.glfwCreateWindow(Globals.userSettings.getDisplayWidth(), Globals.userSettings.getDisplayHeight(), "ORPG", NULL, NULL); } // Errors for failure to create window (IE: No GUI mode on linux ?) if (Globals.window == NULL) { String message = "Failed to create window!\n" + "Error code: " + this.getGLFWErrorMessage(this.getGLFWError()); ; - LoggerInterface.loggerEngine.ERROR(new Exception(message)); - glfwTerminate(); + GLFW.glfwTerminate(); + throw new Error(message); } //set resize callback @@ -239,9 +230,9 @@ public class RenderingEngine { Globals.WINDOW_WIDTH = width; }); //Makes the window that was just created the current OS-level window context - glfwMakeContextCurrent(Globals.window); + GLFW.glfwMakeContextCurrent(Globals.window); //Maximize it - glfwMaximizeWindow(Globals.window); + GLFW.glfwMaximizeWindow(Globals.window); GLFW.glfwPollEvents(); //grab actual framebuffer IntBuffer xBuffer = BufferUtils.createIntBuffer(1); @@ -254,10 +245,11 @@ public class RenderingEngine { //get title bar size Globals.WINDOW_TITLE_BAR_HEIGHT = Globals.WINDOW_HEIGHT - bufferHeight; -// System.out.println(Globals.WINDOW_TITLE_BAR_HEIGHT); - Globals.WINDOW_WIDTH = bufferWidth; Globals.WINDOW_HEIGHT = bufferHeight; + if(bufferWidth == 0 || bufferHeight == 0){ + throw new Error("Failed to get width or height! " + Globals.WINDOW_WIDTH + " " + Globals.WINDOW_HEIGHT); + } // // Attach controls callbacks @@ -289,7 +281,7 @@ public class RenderingEngine { imGuiPipeline = new ImGuiPipeline(Globals.window, glslVersion); //This enables Z-buffering so that farther-back polygons are not drawn over nearer ones - glEnable(GL_DEPTH_TEST); + openGLState.glDepthTest(true); // Support for transparency openGLState.glBlend(true); @@ -300,6 +292,10 @@ public class RenderingEngine { if(!Globals.userSettings.graphicsPerformanceEnableVSync()){ GLFW.glfwSwapInterval(0); } + + //clear screen + GL45.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + GL45.glClear(GL45.GL_COLOR_BUFFER_BIT | GL45.GL_DEPTH_BUFFER_BIT); // //Hide the cursor and capture it // glfwSetInputMode(Globals.window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); @@ -314,7 +310,6 @@ public class RenderingEngine { //default framebuffer defaultFramebuffer = new Framebuffer(GL_DEFAULT_FRAMEBUFFER); - defaultFramebuffer.bind(openGLState); //generate framebuffers Texture screenTextureColor = FramebufferUtils.generateScreenTextureColorAlpha(openGLState, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT); @@ -557,9 +552,9 @@ public class RenderingEngine { //check and call events and swap the buffers LoggerInterface.loggerRenderer.DEBUG_LOOP("GLFW Swap buffers"); - glfwSwapBuffers(Globals.window); + GLFW.glfwSwapBuffers(Globals.window); LoggerInterface.loggerRenderer.DEBUG_LOOP("GLFW Poll Events"); - glfwPollEvents(); + GLFW.glfwPollEvents(); LoggerInterface.loggerRenderer.DEBUG_LOOP("Check OpenGL Errors"); checkError(); } @@ -668,9 +663,9 @@ public class RenderingEngine { public static void recaptureIfNecessary(){ if(Globals.controlHandler.shouldRecapture()){ //Makes the window that was just created the current OS-level window context - glfwMakeContextCurrent(Globals.window); + GLFW.glfwMakeContextCurrent(Globals.window); // //Maximize it - glfwMaximizeWindow(Globals.window); + GLFW.glfwMaximizeWindow(Globals.window); //grab focus GLFW.glfwFocusWindow(Globals.window); //apply mouse controls state diff --git a/src/main/java/electrosphere/renderer/framebuffer/Framebuffer.java b/src/main/java/electrosphere/renderer/framebuffer/Framebuffer.java index 499d5d68..e8b1b08e 100644 --- a/src/main/java/electrosphere/renderer/framebuffer/Framebuffer.java +++ b/src/main/java/electrosphere/renderer/framebuffer/Framebuffer.java @@ -13,9 +13,9 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; +import org.lwjgl.BufferUtils; 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; @@ -291,7 +291,7 @@ public class Framebuffer { int type = GL40.GL_UNSIGNED_BYTE; - bind(openGLState); + this.bind(openGLState); GL40.glReadBuffer(GL40.GL_COLOR_ATTACHMENT0); if(this.framebufferPointer == 0){ //this is the default framebuffer, read from backbuffer because it is default @@ -303,11 +303,23 @@ public class Framebuffer { } else { LoggerInterface.loggerRenderer.ERROR(new IllegalStateException("Tried to get pixels from a framebuffer that does not have a texture attached to attachment point 0.")); } + + //error check + if(width < 1){ + throw new Error("Invalid width! " + width); + } + if(height < 1){ + throw new Error("Invalid height! " + height); + } + //get pixel data try { int bytesPerPixel = pixelFormatToBytes(pixelFormat,type); int bufferSize = width * height * bytesPerPixel; - ByteBuffer buffer = MemoryUtil.memAlloc(bufferSize); + ByteBuffer buffer = BufferUtils.createByteBuffer(bufferSize); + if(buffer == null || buffer.limit() < bufferSize){ + throw new Error("Failed to create buffer!"); + } openGLState.glViewport(width, height); GL40.glReadPixels(offsetX, offsetY, width, height, pixelFormat, type, buffer); Globals.renderingEngine.checkError(); @@ -326,8 +338,6 @@ public class Framebuffer { rVal.setRGB(x, height - (y + 1), (alpha << 24) | (red << 16) | (green << 8) | blue); } } - //memory management - MemoryUtil.memFree(buffer); } catch (OutOfMemoryError e){ LoggerInterface.loggerRenderer.ERROR(new IllegalStateException(e.getMessage())); } diff --git a/src/main/java/electrosphere/renderer/texture/Texture.java b/src/main/java/electrosphere/renderer/texture/Texture.java index 899bf669..3d8f986d 100644 --- a/src/main/java/electrosphere/renderer/texture/Texture.java +++ b/src/main/java/electrosphere/renderer/texture/Texture.java @@ -385,6 +385,12 @@ public class Texture { * @param datatype The data type of a single component of a pixel (ie GL_BYTE, GL_UNSIGNED_INT, etc) */ public void glTexImage2D(OpenGLState openGLState, int width, int height, int format, int datatype){ + if(width < 1){ + throw new Error("Invalid texture width " + width); + } + if(height < 1){ + throw new Error("Invalid texture height " + height); + } //store provided values this.width = width; this.height = height; @@ -420,6 +426,12 @@ public class Texture { * @param datatype The data type of a single component of a pixel (ie GL_BYTE, GL_UNSIGNED_INT, etc) */ public void glTextureStorage2D(OpenGLState openGLState, int width, int height, int format, int datatype){ + if(width < 1){ + throw new Error("Invalid texture width " + width); + } + if(height < 1){ + throw new Error("Invalid texture height " + height); + } //store provided values this.width = width; this.height = height; @@ -437,6 +449,12 @@ public class Texture { * @param data The data to populate the image with */ public void glTexImage2D(OpenGLState openGLState, int width, int height, int format, int datatype, ByteBuffer data){ + if(width < 1){ + throw new Error("Invalid texture width " + width); + } + if(height < 1){ + throw new Error("Invalid texture height " + height); + } //store provided values this.width = width; this.height = height; @@ -461,6 +479,12 @@ public class Texture { * @param data The data to populate the image with */ public void glTexImage2D(OpenGLState openGLState, int internalFormat, int width, int height, int format, int datatype, ByteBuffer data){ + if(width < 1){ + throw new Error("Invalid texture width " + width); + } + if(height < 1){ + throw new Error("Invalid texture height " + height); + } //store provided values this.width = width; this.height = height; @@ -480,7 +504,7 @@ public class Texture { * @return The width */ public int getWidth(){ - if(pixelFormat == -1){ + if(width == -1){ throw new IllegalStateException( "The width of the texture you are trying to query from has not been set yet." + " The texture was likely constructed by passing the opengl texture pointer into the texture object." @@ -494,7 +518,7 @@ public class Texture { * @return The height */ public int getHeight(){ - if(pixelFormat == -1){ + if(height == -1){ throw new IllegalStateException( "The height of the texture you are trying to query from has not been set yet." + " The texture was likely constructed by passing the opengl texture pointer into the texture object." diff --git a/src/test/java/electrosphere/renderer/ui/UIExtensionTests.java b/src/test/java/electrosphere/renderer/ui/UIExtensionTests.java new file mode 100644 index 00000000..ce577b8b --- /dev/null +++ b/src/test/java/electrosphere/renderer/ui/UIExtensionTests.java @@ -0,0 +1,100 @@ +package electrosphere.renderer.ui; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.File; + +import org.junit.jupiter.api.extension.ExtendWith; + +import static electrosphere.test.testutils.Assertions.*; + +import electrosphere.engine.Globals; +import electrosphere.engine.Main; +import electrosphere.menu.WindowUtils; +import electrosphere.renderer.ui.elements.Div; +import electrosphere.test.annotations.IntegrationTest; +import electrosphere.test.template.extensions.StateCleanupCheckerExtension; +import electrosphere.test.testutils.EngineInit; +import electrosphere.test.testutils.TestEngineUtils; +import electrosphere.test.testutils.TestRenderingUtils; + +/** + * Tests to verify the ui test template (we're testing our own testing framework woooooo) + */ +@ExtendWith(StateCleanupCheckerExtension.class) +public class UIExtensionTests { + + @IntegrationTest + public void test_StartupShutdown_NoThrow(){ + assertDoesNotThrow(() -> { + Globals.WINDOW_DECORATED = false; + Globals.WINDOW_FULLSCREEN = true; + Globals.RUN_AUDIO = false; + Globals.RUN_SCRIPTS = false; + Globals.WINDOW_WIDTH = 1920; + Globals.WINDOW_HEIGHT = 1080; + EngineInit.initGraphicalEngine(); + TestEngineUtils.flush(); + + Main.shutdown(); + }); + } + + @IntegrationTest + public void test_Screencapture_Match(){ + Globals.WINDOW_DECORATED = false; + Globals.WINDOW_FULLSCREEN = true; + Globals.RUN_AUDIO = false; + Globals.RUN_SCRIPTS = false; + Globals.WINDOW_WIDTH = 1920; + Globals.WINDOW_HEIGHT = 1080; + EngineInit.initGraphicalEngine(); + TestEngineUtils.flush(); + + TestEngineUtils.simulateFrames(3); + + String canonicalName = this.getClass().getCanonicalName(); + //check the render + assertEqualsRender("./test/java/renderer/ui/test_Screencapture_Match.png", () -> { + + //on failure, save the failed render + String failureSavePath = "./.testcache/" + canonicalName + "-test_Screencapture_Match.png"; + File saveFile = new File(failureSavePath); + System.err.println("[[ATTACHMENT|" + saveFile.getAbsolutePath() + "]]"); + TestRenderingUtils.saveTestRender(failureSavePath); + }); + Main.shutdown(); + } + + @IntegrationTest + public void test_Screencapture_Blank_Match(){ + Globals.WINDOW_DECORATED = false; + Globals.WINDOW_FULLSCREEN = true; + Globals.RUN_AUDIO = false; + Globals.RUN_SCRIPTS = false; + Globals.WINDOW_WIDTH = 1920; + Globals.WINDOW_HEIGHT = 1080; + EngineInit.initGraphicalEngine(); + TestEngineUtils.flush(); + + TestEngineUtils.simulateFrames(3); + + WindowUtils.replaceMainMenuContents(Div.createDiv()); + TestEngineUtils.flush(); + TestEngineUtils.simulateFrames(2); + + String canonicalName = this.getClass().getCanonicalName(); + //check the render + assertEqualsRender("./test/java/renderer/ui/test_Screencapture_Blank.png", () -> { + + //on failure, save the failed render + String failureSavePath = "./.testcache/" + canonicalName + "-test_Screencapture_Blank.png"; + File saveFile = new File(failureSavePath); + System.err.println("[[ATTACHMENT|" + saveFile.getAbsolutePath() + "]]"); + TestRenderingUtils.saveTestRender(failureSavePath); + }); + + Main.shutdown(); + } + +} diff --git a/src/test/java/electrosphere/renderer/ui/elements/WindowTest.java b/src/test/java/electrosphere/renderer/ui/elements/WindowTest.java index 40c49e27..44c52feb 100644 --- a/src/test/java/electrosphere/renderer/ui/elements/WindowTest.java +++ b/src/test/java/electrosphere/renderer/ui/elements/WindowTest.java @@ -2,8 +2,6 @@ package electrosphere.renderer.ui.elements; import electrosphere.test.annotations.IntegrationTest; -import org.junit.jupiter.api.Disabled; - import electrosphere.menu.WindowUtils; import electrosphere.menu.mainmenu.MenuGeneratorsUITesting; import electrosphere.test.template.UITestTemplate; @@ -17,7 +15,6 @@ public class WindowTest extends UITestTemplate { /** * Tests creating a window */ - @Disabled @IntegrationTest public void testCreateWindow(){ //create ui testing window @@ -29,8 +26,8 @@ public class WindowTest extends UITestTemplate { TestEngineUtils.simulateFrames(60); - // TestRenderingUtils.saveTestRender("./test/java/electrosphere/renderer/ui/elements/window.png"); - this.checkRender("Basic", "./test/java/electrosphere/renderer/ui/elements/ui-test.png"); + // TestRenderingUtils.saveTestRender("./test/java/renderer/ui/elements/window.png"); + this.checkRender("Basic", "./test/java/renderer/ui/elements/ui-test.png"); } } diff --git a/src/test/java/electrosphere/test/template/UITestTemplate.java b/src/test/java/electrosphere/test/template/UITestTemplate.java index 5b833b82..487e7aca 100644 --- a/src/test/java/electrosphere/test/template/UITestTemplate.java +++ b/src/test/java/electrosphere/test/template/UITestTemplate.java @@ -5,10 +5,14 @@ import java.io.File; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.extension.ExtendWith; +import electrosphere.menu.WindowUtils; +import electrosphere.renderer.ui.elements.Div; import electrosphere.test.template.extensions.StateCleanupCheckerExtension; import electrosphere.test.template.extensions.UIExtension; import static electrosphere.test.testutils.Assertions.*; + +import electrosphere.test.testutils.TestEngineUtils; import electrosphere.test.testutils.TestRenderingUtils; /** @@ -40,4 +44,13 @@ public class UITestTemplate { }); } + /** + * Sets up a blank view + */ + public void setupBlankView(){ + WindowUtils.replaceMainMenuContents(Div.createDiv()); + TestEngineUtils.flush(); + TestEngineUtils.simulateFrames(2); + } + } diff --git a/src/test/java/electrosphere/test/template/extensions/StateCleanupCheckerExtension.java b/src/test/java/electrosphere/test/template/extensions/StateCleanupCheckerExtension.java index 8eaf674d..44777fde 100644 --- a/src/test/java/electrosphere/test/template/extensions/StateCleanupCheckerExtension.java +++ b/src/test/java/electrosphere/test/template/extensions/StateCleanupCheckerExtension.java @@ -19,6 +19,7 @@ public class StateCleanupCheckerExtension implements AfterEachCallback { Globals.clientSceneWrapper, Globals.clientScene, Globals.signalSystem, + Globals.threadManager, Globals.renderingEngine, Globals.audioEngine, Globals.javaPID, @@ -35,6 +36,9 @@ public class StateCleanupCheckerExtension implements AfterEachCallback { throw new Exception("Failed to cleanup state after test! " + object.toString()); } } + if(Globals.window != -1){ + throw new Exception("Failed to cleanup global window pointer!"); + } } } diff --git a/src/test/java/electrosphere/test/template/extensions/UIExtension.java b/src/test/java/electrosphere/test/template/extensions/UIExtension.java index b7cd9738..bce2cfff 100644 --- a/src/test/java/electrosphere/test/template/extensions/UIExtension.java +++ b/src/test/java/electrosphere/test/template/extensions/UIExtension.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.extension.ExtensionContext; import electrosphere.engine.Globals; import electrosphere.engine.Main; import electrosphere.test.testutils.EngineInit; +import electrosphere.test.testutils.TestEngineUtils; /** * Spins up an tears down generic ui environment @@ -18,9 +19,11 @@ public class UIExtension implements BeforeEachCallback, AfterEachCallback { Globals.WINDOW_DECORATED = false; Globals.WINDOW_FULLSCREEN = true; Globals.RUN_AUDIO = false; + Globals.RUN_SCRIPTS = false; Globals.WINDOW_WIDTH = 1920; Globals.WINDOW_HEIGHT = 1080; EngineInit.initGraphicalEngine(); + TestEngineUtils.flush(); } @Override diff --git a/src/test/java/electrosphere/test/testutils/Assertions.java b/src/test/java/electrosphere/test/testutils/Assertions.java index 4285c872..b82a536a 100644 --- a/src/test/java/electrosphere/test/testutils/Assertions.java +++ b/src/test/java/electrosphere/test/testutils/Assertions.java @@ -10,6 +10,7 @@ import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import electrosphere.engine.Globals; +import electrosphere.engine.Main; /** * Custom assertion macros @@ -35,6 +36,7 @@ public class Assertions { try { testData = ImageIO.read(new File(existingRenderPath)); } catch (IOException e){ + Main.shutdown(); fail("Failed to read existing image path " + existingRenderPath); } BufferedImage screenshot = Globals.renderingEngine.defaultFramebuffer.getPixels(Globals.renderingEngine.getOpenGLState()); @@ -43,6 +45,7 @@ public class Assertions { //width if(testData.getWidth() != screenshot.getWidth()){ onFailure.run(); + Main.shutdown(); } assertEquals(testData.getWidth(), screenshot.getWidth()); @@ -50,6 +53,7 @@ public class Assertions { //height if(testData.getHeight() != screenshot.getHeight()){ onFailure.run(); + Main.shutdown(); } assertEquals(testData.getHeight(), screenshot.getHeight()); @@ -79,6 +83,7 @@ public class Assertions { ){ onFailure.run(); + Main.shutdown(); String failMessage = "Colors aren't approximately the same!\n" + "Color from disk: " + sourceRed + "," + sourceGreen + "," + sourceBlue + "," + sourceAlpha + "\n" + "Color from render: " + renderRed + "," + renderGreen + "," + renderBlue + "," + renderAlpha + "\n" diff --git a/src/test/java/electrosphere/test/testutils/TestEngineUtils.java b/src/test/java/electrosphere/test/testutils/TestEngineUtils.java index 6eb2f4f7..4f7b3381 100644 --- a/src/test/java/electrosphere/test/testutils/TestEngineUtils.java +++ b/src/test/java/electrosphere/test/testutils/TestEngineUtils.java @@ -114,6 +114,10 @@ public class TestEngineUtils { } } + if(frames == 0){ + TestEngineUtils.simulateFrames(1); + } + while( Globals.elementService.getSignalQueueCount() > 0 ){ diff --git a/test/java/electrosphere/renderer/ui/elements/ui-test.png b/test/java/renderer/ui/elements/ui-test.png similarity index 100% rename from test/java/electrosphere/renderer/ui/elements/ui-test.png rename to test/java/renderer/ui/elements/ui-test.png diff --git a/test/java/renderer/ui/test_Screencapture_Blank.png b/test/java/renderer/ui/test_Screencapture_Blank.png new file mode 100644 index 00000000..de9dd9a4 Binary files /dev/null and b/test/java/renderer/ui/test_Screencapture_Blank.png differ diff --git a/test/java/renderer/ui/test_Screencapture_Match.png b/test/java/renderer/ui/test_Screencapture_Match.png new file mode 100644 index 00000000..1055fc38 Binary files /dev/null and b/test/java/renderer/ui/test_Screencapture_Match.png differ