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);
+ }
+ }
+
+}