diff --git a/buildNumber.properties b/buildNumber.properties index 66bab587..55471199 100644 --- a/buildNumber.properties +++ b/buildNumber.properties @@ -1,3 +1,3 @@ #maven.buildNumber.plugin properties file -#Thu Feb 29 18:57:13 EST 2024 -buildNumber=30 +#Wed Mar 06 19:14:23 EST 2024 +buildNumber=31 diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 0c330e02..ab0bde9e 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -125,7 +125,8 @@ Implement proper Frustum Culling - Regular Actors - Instanced Actors - +(03/06/2024) +Bake in imgui @@ -142,7 +143,6 @@ Implement proper Frustum Culling -Bake in imgui Build a lod system - Could potentially be held at actor level diff --git a/imgui.ini b/imgui.ini new file mode 100644 index 00000000..d7bef56a --- /dev/null +++ b/imgui.ini @@ -0,0 +1,20 @@ +[Window][Debug##Default] +Pos=60,60 +Size=400,400 +Collapsed=0 + +[Window][test window] +Pos=791,428 +Size=328,189 +Collapsed=1 + +[Window][FPS Graph] +Pos=1148,36 +Size=775,335 +Collapsed=0 + +[Window][Frametime Graph] +Pos=1345,20 +Size=566,324 +Collapsed=0 + diff --git a/pom.xml b/pom.xml index 4b953e48..8bf0bb19 100644 --- a/pom.xml +++ b/pom.xml @@ -12,6 +12,7 @@ 3.2.3 1.9.19 1.5.7 + 1.86.11 @@ -215,6 +216,33 @@ + + + + + io.github.spair + imgui-java-binding + ${imgui.version} + + + io.github.spair + imgui-java-lwjgl3 + ${imgui.version} + + + io.github.spair + imgui-java-natives-windows + ${imgui.version} + runtime + + + io.github.spair + imgui-java-natives-linux + ${imgui.version} + runtime + + + diff --git a/src/main/java/electrosphere/engine/Main.java b/src/main/java/electrosphere/engine/Main.java index c01dec05..22d9998a 100644 --- a/src/main/java/electrosphere/engine/Main.java +++ b/src/main/java/electrosphere/engine/Main.java @@ -17,6 +17,10 @@ import electrosphere.game.config.UserSettings; import electrosphere.game.server.world.MacroData; import electrosphere.logger.LoggerInterface; import electrosphere.renderer.RenderingEngine; +import electrosphere.renderer.ui.imgui.ImGuiLinePlot; +import electrosphere.renderer.ui.imgui.ImGuiWindow; +import electrosphere.renderer.ui.imgui.ImGuiWindowMacros; +import electrosphere.renderer.ui.imgui.ImGuiLinePlot.ImGuiLinePlotDataset; import electrosphere.server.simulation.MacroSimulation; @@ -174,6 +178,11 @@ public class Main { Globals.initDefaultGraphicalResources(); Globals.initDefaultAudioResources(); } + + //init imgui debug windows + if(Globals.RUN_CLIENT && !Globals.HEADLESS){ + ImGuiWindowMacros.createFramerateGraph(); + } //fire off a loading thread for the title menus/screen LoggerInterface.loggerStartup.INFO("Fire off loading thread"); @@ -208,8 +217,13 @@ public class Main { */ public static void mainLoop(long maxFrames){ + double functionTrackTimeStart = 0; + boolean captureFramerate = false; //main loop while (running) { + + //sets whether to capture framerates of current frame + captureFramerate = frameCount % 10 == 0; /* Frame calculation @@ -223,6 +237,15 @@ public class Main { deltaTime = currentTime - lastFrame; deltaFrames = targetFrameRate * (float)deltaTime; lastFrame = currentTime; + + + + // + // track total frametiime in debug graph + // + if(captureFramerate){ + ImGuiWindowMacros.addFramerateDatapoint("totalframerate",deltaTime); + } /// @@ -264,6 +287,9 @@ public class Main { /// /// C L I E N T S I M U L A T I O N S T U F F /// + if(!Globals.HEADLESS && captureFramerate){ + functionTrackTimeStart = glfwGetTime(); + } if(Globals.clientSimulation != null && Globals.clientSimulation.isReady() && framestep > 0){ ClientFunctions.runBeforeSimulationFunctions(); } @@ -276,10 +302,26 @@ public class Main { if(Globals.clientSimulation != null && Globals.clientSimulation.isReady() && framestep > 0){ ClientFunctions.runClientFunctions(); } + if(!Globals.HEADLESS && captureFramerate){ + ImGuiWindowMacros.addFramerateDatapoint("clientsim",glfwGetTime()-functionTrackTimeStart); + } + + + + + + + + + + /// /// S E R V E R M I C R O S I M U L A T I O N /// + if(!!Globals.HEADLESS && captureFramerate){ + functionTrackTimeStart = glfwGetTime(); + } if(Globals.realmManager != null){ Globals.realmManager.simulate(); } @@ -290,11 +332,38 @@ public class Main { if(Globals.macroSimulation != null && Globals.macroSimulation.isReady() && framestep > 0){ Globals.macroSimulation.simulate(); } + if(!Globals.HEADLESS && captureFramerate){ + ImGuiWindowMacros.addFramerateDatapoint("serversim",glfwGetTime()-functionTrackTimeStart); + } - + + + + /// + /// M A I N R E N D E R F U N C T I O N + /// + if(!Globals.HEADLESS && captureFramerate){ + functionTrackTimeStart = glfwGetTime(); + } if(Globals.RUN_CLIENT && !Globals.HEADLESS){ Globals.renderingEngine.drawScreen(); } + if(!Globals.HEADLESS && captureFramerate){ + ImGuiWindowMacros.addFramerateDatapoint("render",glfwGetTime()-functionTrackTimeStart); + } + + + + + + + + + + + /// + /// S H U T D O W N C H E C K + /// if(Globals.HEADLESS){ if(Globals.ENGINE_SHUTDOWN_FLAG){ @@ -306,6 +375,14 @@ public class Main { } } + + + + + + // + // C L E A N U P T I M E V A R I A B L E S + // if(deltaTime < targetFramePeriod){ sleep((int)(1000.0 * (targetFramePeriod - deltaTime))); } else { diff --git a/src/main/java/electrosphere/renderer/RenderingEngine.java b/src/main/java/electrosphere/renderer/RenderingEngine.java index 15c73ae0..630d8e1c 100644 --- a/src/main/java/electrosphere/renderer/RenderingEngine.java +++ b/src/main/java/electrosphere/renderer/RenderingEngine.java @@ -60,6 +60,8 @@ import static org.lwjgl.opengl.GL40.glBlendFunci; import static org.lwjgl.system.MemoryUtil.NULL; import java.nio.IntBuffer; +import java.util.LinkedList; +import java.util.List; import org.joml.Matrix4d; import org.joml.Matrix4f; @@ -99,9 +101,17 @@ import electrosphere.renderer.light.LightManager; import electrosphere.renderer.texture.Texture; import electrosphere.renderer.ui.DrawableElement; import electrosphere.renderer.ui.Element; +import electrosphere.renderer.ui.imgui.ImGuiLinePlot; +import electrosphere.renderer.ui.imgui.ImGuiWindow; +import electrosphere.renderer.ui.imgui.ImGuiLinePlot.ImGuiLinePlotDataset; import electrosphere.server.pathfinding.navmesh.NavCube; import electrosphere.server.pathfinding.navmesh.NavMesh; import electrosphere.server.pathfinding.navmesh.NavShape; +import imgui.ImGui; +import imgui.extension.implot.ImPlot; +import imgui.gl3.ImGuiImplGl3; +import imgui.glfw.ImGuiImplGlfw; +import imgui.internal.ImGuiContext; public class RenderingEngine { @@ -115,8 +125,29 @@ public class RenderingEngine { static Renderbuffer screenRenderbuffer; static int screenTextureVAO; static ShaderProgram screenTextureShaders; - static ShaderProgram drawChannel; + + + + + // + //imgui related + // + //imgui internal objects + private static final ImGuiImplGlfw imGuiGlfw = new ImGuiImplGlfw(); + private static final ImGuiImplGl3 imGuiGl13 = new ImGuiImplGl3(); + //the version of glsl to init imgui with + private static String glslVersion = null; + //the context pointer for the core imgui objects + private static ImGuiContext imGuiContext = null; + //if set to true, will render imgui windows + private static boolean imGuiShouldRender = true; + //All imgui windows that should be displayed + private static List imGuiWindows = new LinkedList(); + + + + //depth framebuffer/shader for shadow mapping static ShaderProgram lightDepthShaderProgram; @@ -202,6 +233,7 @@ public class RenderingEngine { //Gives hints to glfw to control how opengl will be used glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); + glslVersion = "#version 410"; glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); Allows you to make the background transparent // glfwWindowHint(GLFW_OPACITY, 23); @@ -260,6 +292,12 @@ public class RenderingEngine { //Creates the OpenGL capabilities for the program. GL.createCapabilities(); + //init imgui (must happen after gl.createCapabilities) + imGuiContext = ImGui.createContext(); + ImPlot.createContext(); + imGuiGlfw.init(Globals.window,true); + imGuiGl13.init(glslVersion); + //This enables Z-buffering so that farther-back polygons are not drawn over nearer ones glEnable(GL_DEPTH_TEST); @@ -505,6 +543,19 @@ public class RenderingEngine { if(Globals.RENDER_FLAG_RENDER_UI_BOUNDS){ DebugRendering.drawUIBoundsWireframe(); } + + /** + * Render imgui + */ + if(imGuiShouldRender){ + imGuiGlfw.newFrame(); + ImGui.newFrame(); + for(ImGuiWindow window : imGuiWindows){ + window.draw(); + } + ImGui.render(); + imGuiGl13.renderDrawData(ImGui.getDrawData()); + } //check for errors @@ -1545,6 +1596,22 @@ public class RenderingEngine { Globals.projectionMatrix.setPerspective(radVerticalFOV, RenderingEngine.aspectRatio, nearClip, Globals.userSettings.getGraphicsViewDistance()); } + /** + * Adds a window to the rendering engine + * @param window The window + */ + public static void addImGuiWindow(ImGuiWindow window){ + imGuiWindows.add(window); + } + + /** + * Removes an imgui window from the rendering engine + * @param window The window + */ + public static void removeImGuiWindow(ImGuiWindow window){ + imGuiWindows.remove(window); + } + /** * Gets the current render pipeline state * @return The current render pipeline state diff --git a/src/main/java/electrosphere/renderer/ui/imgui/ImGuiElement.java b/src/main/java/electrosphere/renderer/ui/imgui/ImGuiElement.java new file mode 100644 index 00000000..38f3b545 --- /dev/null +++ b/src/main/java/electrosphere/renderer/ui/imgui/ImGuiElement.java @@ -0,0 +1,13 @@ +package electrosphere.renderer.ui.imgui; + +/** + * Generic interface for something inside an imgui window + */ +public interface ImGuiElement { + + /** + * Draws the ui for this element + */ + public void draw(); + +} diff --git a/src/main/java/electrosphere/renderer/ui/imgui/ImGuiLinePlot.java b/src/main/java/electrosphere/renderer/ui/imgui/ImGuiLinePlot.java new file mode 100644 index 00000000..74adff72 --- /dev/null +++ b/src/main/java/electrosphere/renderer/ui/imgui/ImGuiLinePlot.java @@ -0,0 +1,108 @@ +package electrosphere.renderer.ui.imgui; + +import java.util.LinkedList; +import java.util.List; + +import imgui.ImVec2; +import imgui.extension.implot.ImPlot; +import imgui.extension.implot.flag.ImPlotAxisFlags; + +/** + * A line plot of a given set of data + */ +public class ImGuiLinePlot implements ImGuiElement { + + //the title of the plot + String plotTitle; + + //the data sets to draw + List dataSets = new LinkedList(); + + /** + * Creates an im gui line plot + */ + public ImGuiLinePlot(String plotTitle){ + this.plotTitle = plotTitle; + } + + @Override + public void draw() { + if(ImPlot.beginPlot(plotTitle,"","",new ImVec2(-1,-1),0,ImPlotAxisFlags.AutoFit,ImPlotAxisFlags.AutoFit)){ + for(ImGuiLinePlotDataset dataSet : dataSets){ + double[] xs = dataSet.xData.stream().mapToDouble(Double::doubleValue).toArray();//(Double[])dataSet.xData.toArray(new Double[dataSet.xData.size()]); + double[] ys = dataSet.yData.stream().mapToDouble(Double::doubleValue).toArray();//(Double[])dataSet.yData.toArray(new Double[dataSet.yData.size()]); + ImPlot.plotLine(dataSet.label, xs, ys, xs.length, 0); + } + ImPlot.endPlot(); + } + } + + /** + * Adds a dataset to the line plot + * @param dataset The dataset + */ + public void addDataset(ImGuiLinePlotDataset dataset){ + this.dataSets.add(dataset); + } + + /** + * A single set of data to be plotted in this graph + */ + public static class ImGuiLinePlotDataset { + + //x data + List xData = new LinkedList(); + + //y data + List yData = new LinkedList(); + + //the label of the line + String label; + + //the max number of points + int limit; + + /** + * Creates a dataset object + * @param x the x data + * @param y the y data + * @param label the label for the data + * @param limit the maximum number of objects to keep in the dataset + */ + public ImGuiLinePlotDataset(String label, int limit){ + this.label = label; + this.limit = limit; + } + + /** + * Adds data to the data set. If the amount of data is greater than the limit, it will remove the oldest datapoint + * @param x the x value + * @param y the y value + */ + public void addPoint(double x, double y){ + xData.add(x); + yData.add(y); + while(xData.size() > limit){ + xData.remove(0); + } + while(yData.size() > limit){ + yData.remove(0); + } + } + + /** + * Adds data to the data set. If the amount of data is greater than the limit, it will remove the oldest datapoint. + * Does not clear the x axis so can constantly receive data and update without axis freaking out. + * @param y the y value + */ + public void addPoint(double y){ + yData.add(y); + while(yData.size() > limit){ + yData.remove(0); + } + } + + + } + +} diff --git a/src/main/java/electrosphere/renderer/ui/imgui/ImGuiWindow.java b/src/main/java/electrosphere/renderer/ui/imgui/ImGuiWindow.java new file mode 100644 index 00000000..90e2eabb --- /dev/null +++ b/src/main/java/electrosphere/renderer/ui/imgui/ImGuiWindow.java @@ -0,0 +1,54 @@ +package electrosphere.renderer.ui.imgui; + +import java.util.LinkedList; +import java.util.List; + +import imgui.ImGui; + +/** + * A window in ImGui. The window can contain any number of controls and information. + */ +public class ImGuiWindow { + + //the name of the window + String windowName; + + //The elements housed within this window + List elements = new LinkedList(); + + /** + * Creates the window + */ + public ImGuiWindow(String windowName){ + this.windowName = windowName; + } + + /** + * Adds an imgui element to the window + */ + public void addElement(ImGuiElement element){ + elements.add(element); + } + + /** + * Removes an element from this window + * @param element The element + */ + public void removeElement(ImGuiElement element){ + this.elements.remove(element); + } + + /** + * Draws this window + */ + public void draw(){ + ImGui.begin(windowName); + + for(ImGuiElement element : elements){ + element.draw(); + } + + ImGui.end(); + } + +} diff --git a/src/main/java/electrosphere/renderer/ui/imgui/ImGuiWindowMacros.java b/src/main/java/electrosphere/renderer/ui/imgui/ImGuiWindowMacros.java new file mode 100644 index 00000000..dfb62b08 --- /dev/null +++ b/src/main/java/electrosphere/renderer/ui/imgui/ImGuiWindowMacros.java @@ -0,0 +1,58 @@ +package electrosphere.renderer.ui.imgui; + +import java.util.HashMap; +import java.util.Map; + +import electrosphere.renderer.RenderingEngine; +import electrosphere.renderer.ui.imgui.ImGuiLinePlot.ImGuiLinePlotDataset; + +/** + * Various methods for creating specific imgui windows in engine + */ +public class ImGuiWindowMacros { + + //Framerate graph + private static ImGuiWindow imGuiWindow; + private static ImGuiLinePlot plot; + private static Map dataSetMap; + + /** + * Creates a framerate graph + */ + public static void createFramerateGraph(){ + imGuiWindow = new ImGuiWindow("Frametime Graph"); + plot = new ImGuiLinePlot("Frametime plot"); + dataSetMap = new HashMap(); + initFramerateGraphSeries("totalframerate"); + initFramerateGraphSeries("serversim"); + initFramerateGraphSeries("clientsim"); + initFramerateGraphSeries("render"); + imGuiWindow.addElement(plot); + RenderingEngine.addImGuiWindow(imGuiWindow); + } + + /** + * Inits a series for the framerate graph + * @param seriesName The name of the series + */ + public static void initFramerateGraphSeries(String seriesName){ + ImGuiLinePlotDataset dataSet = new ImGuiLinePlotDataset(seriesName, 50); + dataSetMap.put(seriesName,dataSet); + for(int x = 0; x < 50; x++){ + dataSet.addPoint(x, 0); + } + plot.addDataset(dataSet); + } + + /** + * Adds a datapoint to the framerate graph + * @param seriesName The name of the series to add a datapoint for + * @param y the y coord + */ + public static void addFramerateDatapoint(String seriesName, double y){ + if(dataSetMap != null && dataSetMap.containsKey(seriesName)){ + dataSetMap.get(seriesName).addPoint(y); + } + } + +}