[Example] Migrate example to the new module
This commit is contained in:
1 parent
46f74381aa
commit
cf9e74b1d0
17 files changed
+489
-822
No files matched your search
+14
-1
@@ -1,12 +1,25 @@
|
||||
plugins {
|
||||
id 'application'
|
||||
id 'checkstyle'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass = 'Main'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':imgui-app')
|
||||
}
|
||||
|
||||
task start(type: JavaExec, dependsOn: build) {
|
||||
main = 'Main'
|
||||
classpath = sourceSets.test.runtimeClasspath
|
||||
jvmArgs = ["-Dimgui.library.path=${System.getProperty('libPath', '../bin')}"]
|
||||
|
||||
if (org.gradle.internal.os.OperatingSystem.current().isMacOsX()) {
|
||||
jvmArgs += ['-XstartOnFirstThread', '-Djava.awt.headless=true']
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import imgui.extension.nodeditor.NodeEditor;
|
||||
import imgui.extension.nodeditor.NodeEditorConfig;
|
||||
import imgui.extension.nodeditor.NodeEditorContext;
|
||||
import imgui.extension.nodeditor.flag.NodeEditorPinKind;
|
||||
import imgui.flag.ImGuiCond;
|
||||
import imgui.internal.ImGui;
|
||||
import imgui.type.ImBoolean;
|
||||
import imgui.type.ImLong;
|
||||
|
||||
import java.awt.Desktop;
|
||||
import java.net.URI;
|
||||
|
||||
public class ExampleImGuiNodeEditor {
|
||||
private static final NodeEditorContext CONTEXT;
|
||||
private static final String URL = "https://github.com/thedmd/imgui-node-editor/tree/687a72f940";
|
||||
|
||||
static {
|
||||
NodeEditorConfig config = new NodeEditorConfig();
|
||||
config.setSettingsFile(null);
|
||||
CONTEXT = new NodeEditorContext(config);
|
||||
}
|
||||
|
||||
public static void show(final ImBoolean showImNodeEditorWindow, final Graph graph) {
|
||||
ImGui.setNextWindowSize(500, 400, ImGuiCond.Once);
|
||||
ImGui.setNextWindowPos(ImGui.getMainViewport().getPosX() + 100, ImGui.getMainViewport().getPosY() + 200, ImGuiCond.Once);
|
||||
if (ImGui.begin("imgui-node-editor Demo", showImNodeEditorWindow)) {
|
||||
ImGui.text("This a demo graph editor for imgui-node-editor");
|
||||
|
||||
ImGui.alignTextToFramePadding();
|
||||
ImGui.text("Repo:");
|
||||
ImGui.sameLine();
|
||||
if (ImGui.button(URL)) {
|
||||
try {
|
||||
Desktop.getDesktop().browse(new URI(URL));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.button("Navigate to content")) {
|
||||
NodeEditor.navigateToContent(1);
|
||||
}
|
||||
|
||||
NodeEditor.setCurrentEditor(CONTEXT);
|
||||
NodeEditor.begin("Node Editor");
|
||||
|
||||
for (Graph.GraphNode node : graph.nodes.values()) {
|
||||
NodeEditor.beginNode(node.nodeId);
|
||||
|
||||
ImGui.text(node.getName());
|
||||
|
||||
NodeEditor.beginPin(node.getInputPinId(), NodeEditorPinKind.Input);
|
||||
ImGui.text("-> In");
|
||||
NodeEditor.endPin();
|
||||
|
||||
ImGui.sameLine();
|
||||
|
||||
NodeEditor.beginPin(node.getOutputPinId(), NodeEditorPinKind.Output);
|
||||
ImGui.text("Out ->");
|
||||
NodeEditor.endPin();
|
||||
|
||||
NodeEditor.endNode();
|
||||
}
|
||||
|
||||
if (NodeEditor.beginCreate()) {
|
||||
final ImLong a = new ImLong();
|
||||
final ImLong b = new ImLong();
|
||||
if (NodeEditor.queryNewLink(a, b)) {
|
||||
final Graph.GraphNode source = graph.findByOutput(a.get());
|
||||
final Graph.GraphNode target = graph.findByInput(b.get());
|
||||
if (source != null && target != null && source.outputNodeId != target.nodeId && NodeEditor.acceptNewItem()) {
|
||||
source.outputNodeId = target.nodeId;
|
||||
}
|
||||
}
|
||||
}
|
||||
NodeEditor.endCreate();
|
||||
|
||||
int uniqueLinkId = 1;
|
||||
for (Graph.GraphNode node : graph.nodes.values()) {
|
||||
if (graph.nodes.containsKey(node.outputNodeId)) {
|
||||
NodeEditor.link(uniqueLinkId++, node.getOutputPinId(), graph.nodes.get(node.outputNodeId).getInputPinId());
|
||||
}
|
||||
}
|
||||
|
||||
NodeEditor.suspend();
|
||||
|
||||
final long nodeWithContextMenu = NodeEditor.getNodeWithContextMenu();
|
||||
if (nodeWithContextMenu != -1) {
|
||||
ImGui.openPopup("node_context");
|
||||
ImGui.getStateStorage().setInt(ImGui.getID("delete_node_id"), (int) nodeWithContextMenu);
|
||||
}
|
||||
|
||||
if (ImGui.isPopupOpen("node_context")) {
|
||||
final int targetNode = ImGui.getStateStorage().getInt(ImGui.getID("delete_node_id"));
|
||||
if (ImGui.beginPopup("node_context")) {
|
||||
if (ImGui.button("Delete " + graph.nodes.get(targetNode).getName())) {
|
||||
graph.nodes.remove(targetNode);
|
||||
ImGui.closeCurrentPopup();
|
||||
}
|
||||
ImGui.endPopup();
|
||||
}
|
||||
}
|
||||
|
||||
if (NodeEditor.showBackgroundContextMenu()) {
|
||||
ImGui.openPopup("node_editor_context");
|
||||
}
|
||||
|
||||
if (ImGui.beginPopup("node_editor_context")) {
|
||||
if (ImGui.button("Create New Node")) {
|
||||
final Graph.GraphNode node = graph.createGraphNode();
|
||||
final float canvasX = NodeEditor.toCanvasX(ImGui.getMousePosX());
|
||||
final float canvasY = NodeEditor.toCanvasY(ImGui.getMousePosY());
|
||||
NodeEditor.setNodePosition(node.nodeId, canvasX, canvasY);
|
||||
ImGui.closeCurrentPopup();
|
||||
}
|
||||
ImGui.endPopup();
|
||||
}
|
||||
|
||||
NodeEditor.resume();
|
||||
NodeEditor.end();
|
||||
}
|
||||
ImGui.end();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
import imgui.extension.imnodes.ImNodes;
|
||||
import imgui.extension.imnodes.ImNodesContext;
|
||||
import imgui.extension.imnodes.flag.ImNodesPinShape;
|
||||
import imgui.flag.ImGuiCond;
|
||||
import imgui.flag.ImGuiMouseButton;
|
||||
import imgui.internal.ImGui;
|
||||
import imgui.type.ImBoolean;
|
||||
import imgui.type.ImInt;
|
||||
|
||||
import java.awt.Desktop;
|
||||
import java.net.URI;
|
||||
|
||||
public class ExampleImNodes {
|
||||
private static final ImNodesContext CONTEXT = new ImNodesContext();
|
||||
private static final String URL = "https://github.com/Nelarius/imnodes/tree/5959729410";
|
||||
|
||||
private static final ImInt LINK_A = new ImInt();
|
||||
private static final ImInt LINK_B = new ImInt();
|
||||
|
||||
public static void show(final ImBoolean showImNodesWindow, final Graph graph) {
|
||||
ImGui.setNextWindowSize(500, 400, ImGuiCond.Once);
|
||||
ImGui.setNextWindowPos(ImGui.getMainViewport().getPosX() + 100, ImGui.getMainViewport().getPosY() + 100, ImGuiCond.Once);
|
||||
if (ImGui.begin("ImNodes Demo", showImNodesWindow)) {
|
||||
ImGui.text("This a demo graph editor for ImNodes");
|
||||
|
||||
ImGui.alignTextToFramePadding();
|
||||
ImGui.text("Repo:");
|
||||
ImGui.sameLine();
|
||||
if (ImGui.button(URL)) {
|
||||
try {
|
||||
Desktop.getDesktop().browse(new URI(URL));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
ImNodes.editorContextSet(CONTEXT);
|
||||
ImNodes.beginNodeEditor();
|
||||
|
||||
for (Graph.GraphNode node : graph.nodes.values()) {
|
||||
ImNodes.beginNode(node.nodeId);
|
||||
|
||||
ImNodes.beginNodeTitleBar();
|
||||
ImGui.text(node.getName());
|
||||
ImNodes.endNodeTitleBar();
|
||||
|
||||
ImNodes.beginInputAttribute(node.getInputPinId(), ImNodesPinShape.CircleFilled);
|
||||
ImGui.text("In");
|
||||
ImNodes.endInputAttribute();
|
||||
|
||||
ImGui.sameLine();
|
||||
|
||||
ImNodes.beginOutputAttribute(node.getOutputPinId());
|
||||
ImGui.text("Out");
|
||||
ImNodes.endOutputAttribute();
|
||||
|
||||
ImNodes.endNode();
|
||||
}
|
||||
|
||||
int uniqueLinkId = 1;
|
||||
for (Graph.GraphNode node : graph.nodes.values()) {
|
||||
if (graph.nodes.containsKey(node.outputNodeId)) {
|
||||
ImNodes.link(uniqueLinkId++, node.getOutputPinId(), graph.nodes.get(node.outputNodeId).getInputPinId());
|
||||
}
|
||||
}
|
||||
|
||||
final boolean isEditorHovered = ImNodes.isEditorHovered();
|
||||
|
||||
ImNodes.endNodeEditor();
|
||||
|
||||
if (ImNodes.isLinkCreated(LINK_A, LINK_B)) {
|
||||
final Graph.GraphNode source = graph.findByOutput(LINK_A.get());
|
||||
final Graph.GraphNode target = graph.findByInput(LINK_B.get());
|
||||
if (source != null && target != null && source.outputNodeId != target.nodeId) {
|
||||
source.outputNodeId = target.nodeId;
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.isMouseClicked(ImGuiMouseButton.Right)) {
|
||||
final int hoveredNode = ImNodes.getHoveredNode();
|
||||
if (hoveredNode != -1) {
|
||||
ImGui.openPopup("node_context");
|
||||
ImGui.getStateStorage().setInt(ImGui.getID("delete_node_id"), hoveredNode);
|
||||
} else if (isEditorHovered) {
|
||||
ImGui.openPopup("node_editor_context");
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.isPopupOpen("node_context")) {
|
||||
final int targetNode = ImGui.getStateStorage().getInt(ImGui.getID("delete_node_id"));
|
||||
if (ImGui.beginPopup("node_context")) {
|
||||
if (ImGui.button("Delete " + graph.nodes.get(targetNode).getName())) {
|
||||
graph.nodes.remove(targetNode);
|
||||
ImGui.closeCurrentPopup();
|
||||
}
|
||||
ImGui.endPopup();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.beginPopup("node_editor_context")) {
|
||||
if (ImGui.button("Create New Node")) {
|
||||
final Graph.GraphNode node = graph.createGraphNode();
|
||||
ImNodes.setNodeScreenSpacePos(node.nodeId, ImGui.getMousePosX(), ImGui.getMousePosY());
|
||||
ImGui.closeCurrentPopup();
|
||||
}
|
||||
ImGui.endPopup();
|
||||
}
|
||||
}
|
||||
ImGui.end();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import imgui.ImGui;
|
||||
import imgui.app.Application;
|
||||
import imgui.type.ImBoolean;
|
||||
|
||||
public class Extra {
|
||||
private static final ImBoolean SHOW_DEMO_WINDOW = new ImBoolean(false);
|
||||
private static final ImBoolean SHOW_IMNODES_DEMO_WINDOW = new ImBoolean(false);
|
||||
private static final ImBoolean SHOW_IMGUI_NODE_EDITOR_DEMO_WINDOW = new ImBoolean(false);
|
||||
|
||||
private static final Graph GRAPH = new Graph();
|
||||
|
||||
public static void show(final Application app) {
|
||||
ImGui.colorEdit3("Background Color", app.getColorBg().getData());
|
||||
ImGui.checkbox("Show Demo Window", SHOW_DEMO_WINDOW);
|
||||
ImGui.checkbox("Show ImNodes Demo Window", SHOW_IMNODES_DEMO_WINDOW);
|
||||
ImGui.checkbox("Show imgui-node-editor Demo Window", SHOW_IMGUI_NODE_EDITOR_DEMO_WINDOW);
|
||||
|
||||
if (SHOW_DEMO_WINDOW.get()) {
|
||||
ImGui.showDemoWindow(SHOW_DEMO_WINDOW);
|
||||
}
|
||||
|
||||
if (SHOW_IMNODES_DEMO_WINDOW.get()) {
|
||||
ExampleImNodes.show(SHOW_IMNODES_DEMO_WINDOW, GRAPH);
|
||||
}
|
||||
|
||||
if (SHOW_IMGUI_NODE_EDITOR_DEMO_WINDOW.get()) {
|
||||
ExampleImGuiNodeEditor.show(SHOW_IMGUI_NODE_EDITOR_DEMO_WINDOW, GRAPH);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class Graph {
|
||||
public int nextNodeId = 1;
|
||||
public int nextPinId = 100;
|
||||
|
||||
public final Map<Integer, GraphNode> nodes = new HashMap<>();
|
||||
|
||||
public Graph() {
|
||||
final GraphNode first = createGraphNode();
|
||||
final GraphNode second = createGraphNode();
|
||||
first.outputNodeId = second.nodeId;
|
||||
}
|
||||
|
||||
public GraphNode createGraphNode() {
|
||||
final GraphNode node = new GraphNode(nextNodeId++, nextPinId++, nextPinId++);
|
||||
this.nodes.put(node.nodeId, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
public GraphNode findByInput(final long inputPinId) {
|
||||
for (GraphNode node : nodes.values()) {
|
||||
if (node.getInputPinId() == inputPinId) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public GraphNode findByOutput(final long outputPinId) {
|
||||
for (GraphNode node : nodes.values()) {
|
||||
if (node.getOutputPinId() == outputPinId) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static final class GraphNode {
|
||||
public final int nodeId;
|
||||
public final int inputPinId;
|
||||
public final int outputPinId;
|
||||
|
||||
public int outputNodeId = -1;
|
||||
|
||||
public GraphNode(final int nodeId, final int inputPinId, final int outputPintId) {
|
||||
this.nodeId = nodeId;
|
||||
this.inputPinId = inputPinId;
|
||||
this.outputPinId = outputPintId;
|
||||
}
|
||||
|
||||
public int getInputPinId() {
|
||||
return inputPinId;
|
||||
}
|
||||
|
||||
public int getOutputPinId() {
|
||||
return outputPinId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return "Node " + (char) (64 + nodeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,16 @@ import imgui.ImGui;
|
||||
import imgui.ImGuiIO;
|
||||
import imgui.app.Application;
|
||||
import imgui.app.Configuration;
|
||||
import imgui.extension.imnodes.ImNodes;
|
||||
import imgui.flag.ImGuiConfigFlags;
|
||||
import imgui.flag.ImGuiInputTextFlags;
|
||||
import imgui.type.ImString;
|
||||
|
||||
public class Main extends Application {
|
||||
private final ImString str = new ImString(5);
|
||||
private final float[] flt = new float[1];
|
||||
private int count = 0;
|
||||
|
||||
public final class Main extends Application {
|
||||
@Override
|
||||
protected void configure(final Configuration config) {
|
||||
config.setTitle("Example Application");
|
||||
@@ -20,11 +27,29 @@ public final class Main extends Application {
|
||||
io.addConfigFlags(ImGuiConfigFlags.DockingEnable); // Enable Docking
|
||||
io.addConfigFlags(ImGuiConfigFlags.ViewportsEnable); // Enable Multi-Viewport / Platform Windows
|
||||
io.setConfigViewportsNoTaskBarIcon(true);
|
||||
|
||||
ImNodes.initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disposeImGui() {
|
||||
ImNodes.shutdown();
|
||||
super.disposeImGui();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
ImGui.text("Hello, World");
|
||||
ImGui.text("Hello, World!");
|
||||
if (ImGui.button("Save")) {
|
||||
count++;
|
||||
}
|
||||
ImGui.sameLine();
|
||||
ImGui.text(String.valueOf(count));
|
||||
ImGui.inputText("string", str, ImGuiInputTextFlags.CallbackResize);
|
||||
ImGui.sliderFloat("float", flt, 0, 1);
|
||||
ImGui.separator();
|
||||
ImGui.text("Extra");
|
||||
Extra.show(this);
|
||||
}
|
||||
|
||||
public static void main(final String[] args) {
|
||||
|
||||
@@ -12,7 +12,11 @@ package imgui.app;
|
||||
* <li>{@link #configure(Configuration)} It's called before window creation, so only basic application setups are expected.</li>
|
||||
* <li>{@link #initWindow(Configuration)} Method creates application window.</li>
|
||||
* <li>{@link #initImGui(Configuration)} Method initializes Dear ImGui context. Could be used to do Dear ImGui setup as well.</li>
|
||||
* <li>{@link #preRun()} Method called once, before application loop.</li>
|
||||
* <li>{@link #preProcess()} Method called every frame, before {@link #process()}.</li>
|
||||
* <li>{@link #process()} Method is meant to be overridden with user application logic.</li>
|
||||
* <li>{@link #postProcess()} Method called every frame, after {@link #process()}.</li>
|
||||
* <li>{@link #postRun()} Method called once, after application loop.</li>
|
||||
* <li>{@link #disposeImGui()} Destroys Dear ImGui context.</li>
|
||||
* <li>{@link #disposeWindow()} Destroys application window.</li>
|
||||
* </ol>
|
||||
@@ -27,17 +31,11 @@ package imgui.app;
|
||||
* <pre>{@code
|
||||
* import imgui.ImGui;
|
||||
* import imgui.app.Application;
|
||||
* import imgui.app.Configuration;
|
||||
*
|
||||
* public class Main extends Application {
|
||||
* @Override
|
||||
* protected void configure(final Configuration config) {
|
||||
* config.setTitle("Example Application");
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public void process() {
|
||||
* ImGui.text("Hello, World");
|
||||
* ImGui.text("Hello, World!");
|
||||
* }
|
||||
*
|
||||
* public static void main(final String[] args) {
|
||||
@@ -55,13 +53,25 @@ package imgui.app;
|
||||
*/
|
||||
public abstract class Application extends Window {
|
||||
/**
|
||||
* Method is called before window creation. Could be used to provide basic window information, like title name etc.
|
||||
* Method called before window creation. Could be used to provide basic window information, like title name etc.
|
||||
*
|
||||
* @param config configuration object with basic window information
|
||||
*/
|
||||
protected void configure(final Configuration config) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called once, before application run loop.
|
||||
*/
|
||||
protected void preRun() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called once, after application run loop.
|
||||
*/
|
||||
protected void postRun() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point of any ImGui application. Use it to start the application loop.
|
||||
*
|
||||
@@ -69,7 +79,9 @@ public abstract class Application extends Window {
|
||||
*/
|
||||
public static void launch(final Application app) {
|
||||
initialize(app);
|
||||
app.preRun();
|
||||
app.run();
|
||||
app.postRun();
|
||||
app.dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package imgui.app;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Object for color data.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class Color implements Cloneable {
|
||||
private final float[] data = new float[4];
|
||||
|
||||
public Color(final float[] color) {
|
||||
set(color);
|
||||
}
|
||||
|
||||
public Color(final float red, final float green, final float blue, final float alpha) {
|
||||
data[0] = red;
|
||||
data[1] = green;
|
||||
data[2] = blue;
|
||||
data[3] = alpha;
|
||||
}
|
||||
|
||||
public final void set(final float red, final float green, final float blue, final float alpha) {
|
||||
data[0] = red;
|
||||
data[1] = green;
|
||||
data[2] = blue;
|
||||
data[3] = alpha;
|
||||
}
|
||||
|
||||
public final void set(final float[] color) {
|
||||
System.arraycopy(color, 0, data, 0, 4);
|
||||
}
|
||||
|
||||
public final float getRed() {
|
||||
return data[0];
|
||||
}
|
||||
|
||||
public final float getGreen() {
|
||||
return data[1];
|
||||
}
|
||||
|
||||
public final float getBlue() {
|
||||
return data[2];
|
||||
}
|
||||
|
||||
public final float getAlpha() {
|
||||
return data[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to clone Color instance.
|
||||
*
|
||||
* @return cloned Color instance
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("MethodDoesntCallSuperMethod")
|
||||
public Color clone() {
|
||||
return new Color(data);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import lombok.Data;
|
||||
* Data class to provide basic information about the window. Like, the title name etc.
|
||||
*/
|
||||
@Data
|
||||
public final class Configuration {
|
||||
public class Configuration {
|
||||
/**
|
||||
* Application title.
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,6 @@ import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -29,13 +28,14 @@ public abstract class Window {
|
||||
private String glslVersion = null;
|
||||
|
||||
/**
|
||||
* Pointer to native GLFW window.
|
||||
* Pointer to the native GLFW window.
|
||||
*/
|
||||
protected long handle;
|
||||
|
||||
/**
|
||||
* Background color of the window.
|
||||
*/
|
||||
protected Color colorBg = Color.GRAY;
|
||||
protected final Color colorBg = new Color(.5f, .5f, .5f, 1);
|
||||
|
||||
/**
|
||||
* Method to initialize application.
|
||||
@@ -126,13 +126,27 @@ public abstract class Window {
|
||||
ImGui.createContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called every frame, before calling {@link #process()} method.
|
||||
*/
|
||||
protected void preProcess() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called every frame, after calling {@link #process()} method.
|
||||
*/
|
||||
protected void postProcess() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Main application loop.
|
||||
*/
|
||||
protected final void run() {
|
||||
while (!GLFW.glfwWindowShouldClose(handle)) {
|
||||
startFrame();
|
||||
preProcess();
|
||||
process();
|
||||
postProcess();
|
||||
endFrame();
|
||||
}
|
||||
}
|
||||
@@ -143,10 +157,7 @@ public abstract class Window {
|
||||
public abstract void process();
|
||||
|
||||
private void startFrame() {
|
||||
if (colorBg != null) {
|
||||
GL32.glClearColor(colorBg.getRed() / 255f, colorBg.getGreen() / 255f, colorBg.getBlue() / 255f, colorBg.getAlpha() / 255f);
|
||||
}
|
||||
|
||||
GL32.glClearColor(colorBg.getRed(), colorBg.getGreen(), colorBg.getBlue(), colorBg.getAlpha());
|
||||
GL32.glClear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT);
|
||||
imGuiGlfw.newFrame();
|
||||
ImGui.newFrame();
|
||||
@@ -183,4 +194,18 @@ public abstract class Window {
|
||||
GLFW.glfwTerminate();
|
||||
Objects.requireNonNull(GLFW.glfwSetErrorCallback(null)).free();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return pointer to the native GLFW window
|
||||
*/
|
||||
public final long getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link Color} instance, which represents background color for the window
|
||||
*/
|
||||
public final Color getColorBg() {
|
||||
return colorBg;
|
||||
}
|
||||
}
|
||||
@@ -45,10 +45,10 @@ public class ImGui {
|
||||
|
||||
final String extractedLibAbsPath = tryLoadFromClasspath(fullLibName);
|
||||
|
||||
if (extractedLibAbsPath != null) {
|
||||
System.load(extractedLibAbsPath);
|
||||
} else if (libPath != null) {
|
||||
if (libPath != null) {
|
||||
System.load(Paths.get(libPath).resolve(fullLibName).toFile().getAbsolutePath());
|
||||
} else if (extractedLibAbsPath != null) {
|
||||
System.load(extractedLibAbsPath);
|
||||
} else {
|
||||
System.loadLibrary(libName);
|
||||
}
|
||||
|
||||
@@ -1,512 +0,0 @@
|
||||
import imgui.ImGuiViewport;
|
||||
import imgui.flag.ImGuiColorEditFlags;
|
||||
import imgui.flag.ImGuiCond;
|
||||
import imgui.flag.ImGuiDir;
|
||||
import imgui.flag.ImGuiInputTextFlags;
|
||||
import imgui.flag.ImGuiStyleVar;
|
||||
import imgui.flag.ImGuiWindowFlags;
|
||||
import imgui.extension.imnodes.ImNodes;
|
||||
import imgui.extension.imnodes.ImNodesContext;
|
||||
import imgui.extension.imnodes.flag.ImNodesPinShape;
|
||||
import imgui.extension.nodeditor.NodeEditor;
|
||||
import imgui.internal.ImGui;
|
||||
import imgui.internal.flag.ImGuiDockNodeFlags;
|
||||
import imgui.extension.nodeditor.NodeEditorConfig;
|
||||
import imgui.extension.nodeditor.NodeEditorContext;
|
||||
import imgui.extension.nodeditor.flag.NodeEditorPinKind;
|
||||
import imgui.type.ImBoolean;
|
||||
import imgui.ImColor;
|
||||
import imgui.type.ImInt;
|
||||
import imgui.type.ImLong;
|
||||
import imgui.type.ImString;
|
||||
import org.lwjgl.BufferUtils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.lwjgl.opengl.GL32.GL_CLAMP_TO_EDGE;
|
||||
import static org.lwjgl.opengl.GL32.GL_LINEAR;
|
||||
import static org.lwjgl.opengl.GL32.GL_RGBA;
|
||||
import static org.lwjgl.opengl.GL32.GL_RGBA8;
|
||||
import static org.lwjgl.opengl.GL32.GL_TEXTURE_2D;
|
||||
import static org.lwjgl.opengl.GL32.GL_TEXTURE_MAG_FILTER;
|
||||
import static org.lwjgl.opengl.GL32.GL_TEXTURE_MIN_FILTER;
|
||||
import static org.lwjgl.opengl.GL32.GL_TEXTURE_WRAP_S;
|
||||
import static org.lwjgl.opengl.GL32.GL_TEXTURE_WRAP_T;
|
||||
import static org.lwjgl.opengl.GL32.GL_UNSIGNED_BYTE;
|
||||
import static org.lwjgl.opengl.GL32.glBindTexture;
|
||||
import static org.lwjgl.opengl.GL32.glGenTextures;
|
||||
import static org.lwjgl.opengl.GL32.glTexImage2D;
|
||||
import static org.lwjgl.opengl.GL32.glTexParameteri;
|
||||
|
||||
@SuppressWarnings({"MagicNumber", "VisibilityModifier"})
|
||||
final class ExampleUi {
|
||||
private static final String IMGUI_DEMO_LINK = "https://raw.githubusercontent.com/ocornut/imgui/05bc204/imgui_demo.cpp";
|
||||
|
||||
private static final int COLOR_DODGERBLUE = ImColor.rgbToColor("#1E90FF");
|
||||
private static final int COLOR_CORAL = ImColor.rgbToColor("#FF7F50");
|
||||
private static final int COLOR_LIMEGREEN = ImColor.rgbToColor("#32CD32");
|
||||
|
||||
// Test data for payload
|
||||
private String dropTargetText = "Drop Here";
|
||||
|
||||
// Resizable input example
|
||||
private final ImString resizableStr = new ImString(5);
|
||||
|
||||
// Toggles
|
||||
private final ImBoolean showBottomDockedWindow = new ImBoolean(true);
|
||||
private final ImBoolean showDemoWindow = new ImBoolean();
|
||||
private final ImBoolean showImNodesWindow = new ImBoolean(false);
|
||||
private final ImBoolean showImNodeEditorWindow = new ImBoolean(false);
|
||||
|
||||
// Attach image example
|
||||
private int dukeTexture = 0;
|
||||
|
||||
private boolean isBottomDockedWindowInit = false;
|
||||
|
||||
// To modify background color dynamically
|
||||
final float[] backgroundColor = new float[]{0.5f, 0, 0};
|
||||
|
||||
// Context for imnodes example
|
||||
private ImNodesContext imNodesContext;
|
||||
|
||||
// Context for imgui-node-editor example
|
||||
private NodeEditorContext nodeEditorContext;
|
||||
|
||||
// Graph used both for imnodes and imgui-node-editor demo
|
||||
private final Graph graph = new Graph();
|
||||
|
||||
void render() throws Exception {
|
||||
if (imNodesContext == null) {
|
||||
imNodesContext = new ImNodesContext();
|
||||
}
|
||||
if (nodeEditorContext == null) {
|
||||
final NodeEditorConfig config = new NodeEditorConfig();
|
||||
config.setSettingsFile(null);
|
||||
nodeEditorContext = new NodeEditorContext(config);
|
||||
}
|
||||
|
||||
final int dockspaceId = ImGui.getID("MyDockSpace");
|
||||
showDockSpace(dockspaceId);
|
||||
setupBottomDockedWindow(dockspaceId);
|
||||
showBottomDockedWindow();
|
||||
|
||||
final ImGuiViewport mainViewport = ImGui.getMainViewport();
|
||||
ImGui.setNextWindowSize(600, 300, ImGuiCond.Once);
|
||||
ImGui.setNextWindowPos(mainViewport.getWorkPosX() + 10, mainViewport.getWorkPosY() + 10, ImGuiCond.Once);
|
||||
|
||||
ImGui.begin("Custom window"); // Start Custom window
|
||||
showWindowImage();
|
||||
showToggles();
|
||||
|
||||
ImGui.separator();
|
||||
|
||||
showDragNDrop();
|
||||
showBackgroundPicker();
|
||||
|
||||
ImGui.separator();
|
||||
|
||||
showResizableInput();
|
||||
|
||||
ImGui.separator();
|
||||
ImGui.newLine();
|
||||
|
||||
showDemoLink();
|
||||
|
||||
ImGui.end(); // End Custom window
|
||||
|
||||
if (showDemoWindow.get()) {
|
||||
ImGui.showDemoWindow(showDemoWindow);
|
||||
}
|
||||
|
||||
if (showImNodesWindow.get()) {
|
||||
showImNodesWindow();
|
||||
}
|
||||
|
||||
if (showImNodeEditorWindow.get()) {
|
||||
showImNodeEditorWindow();
|
||||
}
|
||||
}
|
||||
|
||||
private void showDockSpace(final int dockspaceId) {
|
||||
final ImGuiViewport mainViewport = ImGui.getMainViewport();
|
||||
final int windowFlags = ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoResize
|
||||
| ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoBringToFrontOnFocus | ImGuiWindowFlags.NoNavFocus | ImGuiWindowFlags.NoBackground;
|
||||
|
||||
ImGui.setNextWindowPos(mainViewport.getWorkPosX(), mainViewport.getWorkPosY());
|
||||
ImGui.setNextWindowSize(mainViewport.getWorkSizeX(), mainViewport.getWorkSizeY());
|
||||
ImGui.setNextWindowViewport(mainViewport.getID());
|
||||
ImGui.pushStyleVar(ImGuiStyleVar.WindowRounding, 0);
|
||||
ImGui.pushStyleVar(ImGuiStyleVar.WindowBorderSize, 0);
|
||||
|
||||
ImGui.pushStyleVar(ImGuiStyleVar.WindowPadding, 0, 0);
|
||||
ImGui.begin("Dockspace Demo", windowFlags);
|
||||
ImGui.popStyleVar(3);
|
||||
|
||||
ImGui.dockSpace(dockspaceId, 0, 0, ImGuiDockNodeFlags.PassthruCentralNode);
|
||||
ImGui.end();
|
||||
}
|
||||
|
||||
private void setupBottomDockedWindow(final int dockspaceId) {
|
||||
if (!isBottomDockedWindowInit) {
|
||||
ImGui.dockBuilderRemoveNode(dockspaceId);
|
||||
ImGui.dockBuilderAddNode(dockspaceId, ImGuiDockNodeFlags.DockSpace);
|
||||
|
||||
final int dockIdBottom = ImGui.dockBuilderSplitNode(dockspaceId, ImGuiDir.Down, .25f, null, null);
|
||||
|
||||
ImGui.dockBuilderDockWindow("Bottom Docked Window", dockIdBottom);
|
||||
ImGui.dockBuilderSetNodeSize(dockIdBottom, 150f, 150f);
|
||||
ImGui.dockBuilderFinish(dockspaceId);
|
||||
|
||||
isBottomDockedWindowInit = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void showBottomDockedWindow() {
|
||||
if (showBottomDockedWindow.get()) {
|
||||
ImGui.begin("Bottom Docked Window", showBottomDockedWindow, ImGuiWindowFlags.AlwaysAutoResize);
|
||||
ImGui.text("An example of how to create docked windows.");
|
||||
ImGui.end();
|
||||
}
|
||||
}
|
||||
|
||||
private void showWindowImage() throws IOException {
|
||||
if (dukeTexture == 0) {
|
||||
dukeTexture = loadTexture(ImageIO.read(new File("src/test/resources/Duke_waving.png")));
|
||||
}
|
||||
|
||||
// Draw an image in the bottom-right corner of the window
|
||||
final float xPoint = ImGui.getWindowPosX() + ImGui.getWindowSizeX() - 100;
|
||||
final float yPoint = ImGui.getWindowPosY() + ImGui.getWindowSizeY();
|
||||
ImGui.getWindowDrawList().addImage(dukeTexture, xPoint, yPoint - 180, xPoint + 100, yPoint);
|
||||
}
|
||||
|
||||
private void showToggles() {
|
||||
ImGui.checkbox("Show Demo Window", showDemoWindow);
|
||||
ImGui.checkbox("Show Bottom Docked Window", showBottomDockedWindow);
|
||||
ImGui.checkbox("Show demo for imnodes", showImNodesWindow);
|
||||
ImGui.checkbox("Show demo for imgui-node-editor", showImNodeEditorWindow);
|
||||
if (ImGui.button("Reset Bottom Dock Window")) {
|
||||
isBottomDockedWindowInit = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void showDragNDrop() {
|
||||
ImGui.button("Drag me");
|
||||
if (ImGui.beginDragDropSource()) {
|
||||
ImGui.setDragDropPayloadObject("payload_type", "Test Payload");
|
||||
ImGui.text("Drag started");
|
||||
ImGui.endDragDropSource();
|
||||
}
|
||||
ImGui.sameLine();
|
||||
ImGui.text(dropTargetText);
|
||||
if (ImGui.beginDragDropTarget()) {
|
||||
final Object payload = ImGui.acceptDragDropPayloadObject("payload_type");
|
||||
if (payload != null) {
|
||||
dropTargetText = (String) payload;
|
||||
}
|
||||
ImGui.endDragDropTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private void showBackgroundPicker() {
|
||||
ImGui.alignTextToFramePadding();
|
||||
ImGui.text("Background color:");
|
||||
ImGui.sameLine();
|
||||
ImGui.colorEdit3("##click_counter_col", backgroundColor, ImGuiColorEditFlags.NoInputs | ImGuiColorEditFlags.NoDragDrop);
|
||||
}
|
||||
|
||||
private void showResizableInput() {
|
||||
ImGui.text("You can use text inputs with auto-resizable strings!");
|
||||
ImGui.inputText("Resizable input", resizableStr, ImGuiInputTextFlags.CallbackResize);
|
||||
ImGui.text("text len:");
|
||||
ImGui.sameLine();
|
||||
ImGui.textColored(COLOR_DODGERBLUE, Integer.toString(resizableStr.getLength()));
|
||||
ImGui.sameLine();
|
||||
ImGui.text("| buffer size:");
|
||||
ImGui.sameLine();
|
||||
ImGui.textColored(COLOR_CORAL, Integer.toString(resizableStr.getBufferSize()));
|
||||
}
|
||||
|
||||
private void showDemoLink() {
|
||||
ImGui.text("Consider to look the original ImGui demo: ");
|
||||
ImGui.setNextItemWidth(500);
|
||||
ImGui.textColored(COLOR_LIMEGREEN, IMGUI_DEMO_LINK);
|
||||
ImGui.sameLine();
|
||||
if (ImGui.button("Copy")) {
|
||||
ImGui.setClipboardText(IMGUI_DEMO_LINK);
|
||||
}
|
||||
}
|
||||
|
||||
private void showImNodesWindow() {
|
||||
ImGui.setNextWindowSize(400F, 400F, ImGuiCond.Appearing);
|
||||
ImGui.begin("Demo imnodes", showImNodesWindow);
|
||||
|
||||
ImGui.text("This a demo graph editor for ImNodes");
|
||||
|
||||
ImNodes.editorContextSet(imNodesContext);
|
||||
ImNodes.beginNodeEditor();
|
||||
|
||||
for (Graph.GraphNode node : graph.nodes.values()) {
|
||||
ImNodes.beginNode(node.nodeId);
|
||||
|
||||
ImNodes.beginNodeTitleBar();
|
||||
ImGui.text(node.getName());
|
||||
ImNodes.endNodeTitleBar();
|
||||
|
||||
|
||||
ImNodes.beginInputAttribute(node.getInputPinId(), ImNodesPinShape.CircleFilled);
|
||||
ImGui.text("In");
|
||||
ImNodes.endInputAttribute();
|
||||
|
||||
ImGui.sameLine();
|
||||
|
||||
ImNodes.beginOutputAttribute(node.getOutputPinId());
|
||||
ImGui.text("Out");
|
||||
ImNodes.endOutputAttribute();
|
||||
|
||||
ImNodes.endNode();
|
||||
}
|
||||
|
||||
int uniqueLinkId = 1;
|
||||
for (Graph.GraphNode node : graph.nodes.values()) {
|
||||
if (graph.nodes.containsKey(node.outputNodeId)) {
|
||||
ImNodes.link(uniqueLinkId++, node.getOutputPinId(), graph.nodes.get(node.outputNodeId).getInputPinId());
|
||||
}
|
||||
}
|
||||
|
||||
final boolean isEditorHovered = ImNodes.isEditorHovered();
|
||||
|
||||
ImNodes.endNodeEditor();
|
||||
final ImInt a = new ImInt();
|
||||
final ImInt b = new ImInt();
|
||||
if (ImNodes.isLinkCreated(a, b)) {
|
||||
final Graph.GraphNode source = graph.findByOutput(a.get());
|
||||
final Graph.GraphNode target = graph.findByInput(b.get());
|
||||
if (source != null && target != null && source.outputNodeId != target.nodeId) {
|
||||
source.outputNodeId = target.nodeId;
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.isMouseClicked(1)) {
|
||||
final int hoveredNode = ImNodes.getHoveredNode();
|
||||
if (hoveredNode != -1) {
|
||||
ImGui.openPopup("node-context");
|
||||
ImGui.getStateStorage().setInt(ImGui.getID("delete-node-id"), hoveredNode);
|
||||
} else if (isEditorHovered) {
|
||||
ImGui.openPopup("node-editor-context");
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.isPopupOpen("node-context")) {
|
||||
final int targetNode = ImGui.getStateStorage().getInt(ImGui.getID("delete-node-id"));
|
||||
if (ImGui.beginPopup("node-context")) {
|
||||
if (ImGui.button("Delete " + graph.nodes.get(targetNode).getName())) {
|
||||
graph.nodes.remove(targetNode);
|
||||
ImGui.closeCurrentPopup();
|
||||
}
|
||||
ImGui.endPopup();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.beginPopup("node-editor-context")) {
|
||||
if (ImGui.button("Create new node")) {
|
||||
final Graph.GraphNode node = graph.createGraphNode();
|
||||
ImNodes.setNodeScreenSpacePos(node.nodeId, ImGui.getMousePosX(), ImGui.getMousePosY());
|
||||
ImGui.closeCurrentPopup();
|
||||
}
|
||||
ImGui.endPopup();
|
||||
}
|
||||
|
||||
ImGui.end();
|
||||
}
|
||||
|
||||
private void showImNodeEditorWindow() {
|
||||
ImGui.setNextWindowSize(400F, 400F, ImGuiCond.Appearing);
|
||||
ImGui.begin("Demo imgui-node-editor", showImNodeEditorWindow);
|
||||
|
||||
ImGui.text("This a demo graph editor for imgui-node-editor");
|
||||
|
||||
if (ImGui.button("Navigate to content")) {
|
||||
NodeEditor.navigateToContent(1F);
|
||||
}
|
||||
|
||||
NodeEditor.setCurrentEditor(nodeEditorContext);
|
||||
NodeEditor.begin("Node Editor");
|
||||
|
||||
for (Graph.GraphNode node : graph.nodes.values()) {
|
||||
NodeEditor.beginNode(node.nodeId);
|
||||
|
||||
ImGui.text(node.getName());
|
||||
|
||||
NodeEditor.beginPin(node.getInputPinId(), NodeEditorPinKind.Input);
|
||||
ImGui.text("-> In");
|
||||
NodeEditor.endPin();
|
||||
|
||||
ImGui.sameLine();
|
||||
|
||||
NodeEditor.beginPin(node.getOutputPinId(), NodeEditorPinKind.Output);
|
||||
ImGui.text("Out ->");
|
||||
NodeEditor.endPin();
|
||||
|
||||
NodeEditor.endNode();
|
||||
}
|
||||
|
||||
if (NodeEditor.beginCreate()) {
|
||||
final ImLong a = new ImLong();
|
||||
final ImLong b = new ImLong();
|
||||
if (NodeEditor.queryNewLink(a, b)) {
|
||||
final Graph.GraphNode source = graph.findByOutput(a.get());
|
||||
final Graph.GraphNode target = graph.findByInput(b.get());
|
||||
if (source != null && target != null && source.outputNodeId != target.nodeId && NodeEditor.acceptNewItem()) {
|
||||
source.outputNodeId = target.nodeId;
|
||||
}
|
||||
}
|
||||
}
|
||||
NodeEditor.endCreate();
|
||||
|
||||
int uniqueLinkId = 1;
|
||||
for (Graph.GraphNode node : graph.nodes.values()) {
|
||||
if (graph.nodes.containsKey(node.outputNodeId)) {
|
||||
NodeEditor.link(uniqueLinkId++, node.getOutputPinId(), graph.nodes.get(node.outputNodeId).getInputPinId());
|
||||
}
|
||||
}
|
||||
|
||||
NodeEditor.suspend();
|
||||
final long nodeWithContextMenu = NodeEditor.getNodeWithContextMenu();
|
||||
if (nodeWithContextMenu != -1) {
|
||||
ImGui.openPopup("node-context");
|
||||
ImGui.getStateStorage().setInt(ImGui.getID("delete-node-id"), (int) nodeWithContextMenu);
|
||||
}
|
||||
|
||||
if (ImGui.isPopupOpen("node-context")) {
|
||||
final int targetNode = ImGui.getStateStorage().getInt(ImGui.getID("delete-node-id"));
|
||||
if (ImGui.beginPopup("node-context")) {
|
||||
if (ImGui.button("Delete " + graph.nodes.get(targetNode).getName())) {
|
||||
graph.nodes.remove(targetNode);
|
||||
ImGui.closeCurrentPopup();
|
||||
}
|
||||
ImGui.endPopup();
|
||||
}
|
||||
}
|
||||
|
||||
if (NodeEditor.showBackgroundContextMenu()) {
|
||||
ImGui.openPopup("node-editor-context");
|
||||
}
|
||||
if (ImGui.beginPopup("node-editor-context")) {
|
||||
if (ImGui.button("Create new node")) {
|
||||
final Graph.GraphNode node = graph.createGraphNode();
|
||||
final float canvasX = NodeEditor.toCanvasX(ImGui.getMousePosX());
|
||||
final float canvasY = NodeEditor.toCanvasY(ImGui.getMousePosY());
|
||||
NodeEditor.setNodePosition(node.nodeId, canvasX, canvasY);
|
||||
ImGui.closeCurrentPopup();
|
||||
}
|
||||
ImGui.endPopup();
|
||||
}
|
||||
NodeEditor.resume();
|
||||
|
||||
NodeEditor.end();
|
||||
ImGui.end();
|
||||
}
|
||||
|
||||
private int loadTexture(final BufferedImage image) {
|
||||
final int[] pixels = new int[image.getWidth() * image.getHeight()];
|
||||
image.getRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth());
|
||||
|
||||
final ByteBuffer buffer = BufferUtils.createByteBuffer(image.getWidth() * image.getHeight() * 4); // 4 for RGBA, 3 for RGB
|
||||
for (int y = 0; y < image.getHeight(); y++) {
|
||||
for (int x = 0; x < image.getWidth(); x++) {
|
||||
final int pixel = pixels[y * image.getWidth() + x];
|
||||
buffer.put((byte) ((pixel >> 16) & 0xFF));
|
||||
buffer.put((byte) ((pixel >> 8) & 0xFF));
|
||||
buffer.put((byte) (pixel & 0xFF));
|
||||
buffer.put((byte) ((pixel >> 24) & 0xFF));
|
||||
}
|
||||
}
|
||||
buffer.flip();
|
||||
|
||||
final int textureID = glGenTextures();
|
||||
glBindTexture(GL_TEXTURE_2D, textureID);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, image.getWidth(), image.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
|
||||
|
||||
return textureID;
|
||||
}
|
||||
|
||||
|
||||
// Simple graph struct for both of node editors demos
|
||||
private static final class Graph {
|
||||
|
||||
private int nextNodeId = 1;
|
||||
private int nextPinId = 100;
|
||||
|
||||
final Map<Integer, GraphNode> nodes = new HashMap<>();
|
||||
|
||||
private Graph() {
|
||||
final GraphNode first = createGraphNode();
|
||||
final GraphNode second = createGraphNode();
|
||||
first.outputNodeId = second.nodeId;
|
||||
}
|
||||
|
||||
public GraphNode createGraphNode() {
|
||||
final GraphNode node = new GraphNode(nextNodeId++, nextPinId++, nextPinId++);
|
||||
this.nodes.put(node.nodeId, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
public GraphNode findByInput(final long inputPinId) {
|
||||
for (GraphNode node : nodes.values()) {
|
||||
if (node.getInputPinId() == inputPinId) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public GraphNode findByOutput(final long outputPinId) {
|
||||
for (GraphNode node : nodes.values()) {
|
||||
if (node.getOutputPinId() == outputPinId) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final class GraphNode {
|
||||
private final int nodeId;
|
||||
private final int inputPinId;
|
||||
private final int outputPinId;
|
||||
|
||||
public int outputNodeId = -1;
|
||||
|
||||
private GraphNode(final int nodeId, final int inputPinId, final int outputPintId) {
|
||||
this.nodeId = nodeId;
|
||||
this.inputPinId = inputPinId;
|
||||
this.outputPinId = outputPintId;
|
||||
}
|
||||
|
||||
public int getInputPinId() {
|
||||
return inputPinId;
|
||||
}
|
||||
|
||||
public int getOutputPinId() {
|
||||
return outputPinId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return "Node " + (char) (64 + nodeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,288 +0,0 @@
|
||||
import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks;
|
||||
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_FORWARD_COMPAT;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_OPENGL_PROFILE;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_TRUE;
|
||||
import static org.lwjgl.glfw.GLFW.GLFW_VISIBLE;
|
||||
import static org.lwjgl.glfw.GLFW.glfwCreateWindow;
|
||||
import static org.lwjgl.glfw.GLFW.glfwDefaultWindowHints;
|
||||
import static org.lwjgl.glfw.GLFW.glfwDestroyWindow;
|
||||
import static org.lwjgl.glfw.GLFW.glfwGetCurrentContext;
|
||||
import static org.lwjgl.glfw.GLFW.glfwGetPrimaryMonitor;
|
||||
import static org.lwjgl.glfw.GLFW.glfwGetVideoMode;
|
||||
import static org.lwjgl.glfw.GLFW.glfwGetWindowSize;
|
||||
import static org.lwjgl.glfw.GLFW.glfwInit;
|
||||
import static org.lwjgl.glfw.GLFW.glfwMakeContextCurrent;
|
||||
import static org.lwjgl.glfw.GLFW.glfwPollEvents;
|
||||
import static org.lwjgl.glfw.GLFW.glfwSetErrorCallback;
|
||||
import static org.lwjgl.glfw.GLFW.glfwSetWindowPos;
|
||||
import static org.lwjgl.glfw.GLFW.glfwShowWindow;
|
||||
import static org.lwjgl.glfw.GLFW.glfwSwapBuffers;
|
||||
import static org.lwjgl.glfw.GLFW.glfwSwapInterval;
|
||||
import static org.lwjgl.glfw.GLFW.glfwTerminate;
|
||||
import static org.lwjgl.glfw.GLFW.glfwWindowHint;
|
||||
import static org.lwjgl.glfw.GLFW.glfwWindowShouldClose;
|
||||
import static org.lwjgl.opengl.GL32.GL_COLOR_BUFFER_BIT;
|
||||
import static org.lwjgl.opengl.GL32.GL_TRUE;
|
||||
import static org.lwjgl.opengl.GL32.glClear;
|
||||
import static org.lwjgl.opengl.GL32.glClearColor;
|
||||
import static org.lwjgl.system.MemoryStack.stackPush;
|
||||
import static org.lwjgl.system.MemoryUtil.NULL;
|
||||
|
||||
import imgui.ImFontAtlas;
|
||||
import imgui.ImFontConfig;
|
||||
import imgui.ImGui;
|
||||
import imgui.ImGuiIO;
|
||||
import imgui.ImGuiStyle;
|
||||
import imgui.flag.ImGuiCol;
|
||||
import imgui.flag.ImGuiConfigFlags;
|
||||
import imgui.gl3.ImGuiImplGl3;
|
||||
import imgui.glfw.ImGuiImplGlfw;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.Objects;
|
||||
|
||||
import imgui.extension.imnodes.ImNodes;
|
||||
import org.lwjgl.glfw.GLFWErrorCallback;
|
||||
import org.lwjgl.glfw.GLFWVidMode;
|
||||
import org.lwjgl.opengl.GL;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
|
||||
@SuppressWarnings("MagicNumber")
|
||||
public final class ImGuiGlfwExample {
|
||||
private long windowPtr; // pointer to the current GLFW window
|
||||
|
||||
// LWJGJ3 window backend
|
||||
private final ImGuiImplGlfw imGuiGlfw = new ImGuiImplGlfw();
|
||||
|
||||
// LWJGL3 renderer (SHOULD be initialized)
|
||||
private final ImGuiImplGl3 imGuiGl3 = new ImGuiImplGl3();
|
||||
private String glslVersion = null; // We can initialize our renderer with different versions of the GLSL
|
||||
|
||||
// User UI to render
|
||||
private final ExampleUi exampleUi = new ExampleUi();
|
||||
|
||||
public void run() throws Exception {
|
||||
setupGlfw();
|
||||
setupImGui();
|
||||
|
||||
// Method initializes GLFW backend.
|
||||
// This method SHOULD be called after you've setup GLFW.
|
||||
// ImGui context should be created as well.
|
||||
imGuiGlfw.init(windowPtr, true);
|
||||
// Method initializes LWJGL3 renderer.
|
||||
// This method SHOULD be called after you've initialized your ImGui configuration (fonts and so on).
|
||||
// ImGui context should be created as well.
|
||||
imGuiGl3.init(glslVersion);
|
||||
|
||||
loop();
|
||||
|
||||
// You should clean up after yourself in reverse order.
|
||||
imGuiGl3.dispose();
|
||||
imGuiGlfw.dispose();
|
||||
|
||||
ImNodes.shutdown();
|
||||
ImGui.destroyContext();
|
||||
|
||||
disposeWindow();
|
||||
}
|
||||
|
||||
// Initialize GLFW + create an OpenGL context.
|
||||
// All code is mostly a copy-paste from the official LWJGL3 "Get Started": https://www.lwjgl.org/guide
|
||||
private void setupGlfw() {
|
||||
// Setup an error callback. The default implementation
|
||||
// will print the error message in System.err.
|
||||
GLFWErrorCallback.createPrint(System.err).set();
|
||||
|
||||
// Initialize GLFW. Most GLFW functions will not work before doing this.
|
||||
if (!glfwInit()) {
|
||||
throw new IllegalStateException("Unable to initialize GLFW");
|
||||
}
|
||||
|
||||
// Configure GLFW
|
||||
glfwDefaultWindowHints(); // optional, the current window hints are already the default
|
||||
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation
|
||||
|
||||
decideGlGlslVersions();
|
||||
|
||||
windowPtr = glfwCreateWindow(1280, 768, "Dear ImGui+LWJGL Example", NULL, NULL);
|
||||
|
||||
if (windowPtr == NULL) {
|
||||
throw new RuntimeException("Failed to create the GLFW window");
|
||||
}
|
||||
|
||||
// Get the thread stack and push a new frame
|
||||
try (MemoryStack stack = stackPush()) {
|
||||
final IntBuffer pWidth = stack.mallocInt(1); // int*
|
||||
final IntBuffer pHeight = stack.mallocInt(1); // int*
|
||||
|
||||
// Get the window size passed to glfwCreateWindow
|
||||
glfwGetWindowSize(windowPtr, pWidth, pHeight);
|
||||
|
||||
// Get the resolution of the primary monitor
|
||||
final GLFWVidMode vidmode = Objects.requireNonNull(glfwGetVideoMode(glfwGetPrimaryMonitor()));
|
||||
|
||||
// Center the window
|
||||
glfwSetWindowPos(windowPtr, (vidmode.width() - pWidth.get(0)) / 2, (vidmode.height() - pHeight.get(0)) / 2);
|
||||
} // the stack frame is popped automatically
|
||||
|
||||
glfwMakeContextCurrent(windowPtr); // Make the OpenGL context current
|
||||
glfwSwapInterval(GLFW_TRUE); // Enable v-sync
|
||||
glfwShowWindow(windowPtr); // Make the window visible
|
||||
|
||||
// IMPORTANT!!
|
||||
// This line is critical for LWJGL's interoperation with GLFW's
|
||||
// OpenGL context, or any context that is managed externally.
|
||||
// LWJGL detects the context that is current in the current thread,
|
||||
// creates the GLCapabilities instance and makes the OpenGL
|
||||
// bindings available for use.
|
||||
GL.createCapabilities();
|
||||
}
|
||||
|
||||
private void decideGlGlslVersions() {
|
||||
final boolean isMac = System.getProperty("os.name").toLowerCase().contains("mac");
|
||||
if (isMac) {
|
||||
glslVersion = "#version 150";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
|
||||
} else {
|
||||
glslVersion = "#version 130";
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize Dear ImGui.
|
||||
private void setupImGui() {
|
||||
// IMPORTANT!!
|
||||
// This line is critical for Dear ImGui to work.
|
||||
ImGui.createContext();
|
||||
ImNodes.initialize();
|
||||
// ------------------------------------------------------------
|
||||
// Initialize ImGuiIO config
|
||||
final ImGuiIO io = ImGui.getIO();
|
||||
|
||||
io.setIniFilename(null); // We don't want to save .ini file
|
||||
io.addConfigFlags(ImGuiConfigFlags.NavEnableKeyboard); // Enable Keyboard Controls
|
||||
io.addConfigFlags(ImGuiConfigFlags.DockingEnable); // Enable Docking
|
||||
io.addConfigFlags(ImGuiConfigFlags.ViewportsEnable); // Enable Multi-Viewport / Platform Windows
|
||||
io.setConfigViewportsNoTaskBarIcon(true);
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Fonts configuration
|
||||
// Read: https://raw.githubusercontent.com/ocornut/imgui/master/docs/FONTS.txt
|
||||
|
||||
final ImFontAtlas fontAtlas = io.getFonts();
|
||||
final ImFontConfig fontConfig = new ImFontConfig(); // Natively allocated object, should be explicitly destroyed
|
||||
|
||||
// Glyphs could be added per-font as well as per config used globally like here
|
||||
fontConfig.setGlyphRanges(fontAtlas.getGlyphRangesCyrillic());
|
||||
|
||||
// Add a default font, which is 'ProggyClean.ttf, 13px'
|
||||
fontAtlas.addFontDefault();
|
||||
|
||||
// Fonts merge example
|
||||
fontConfig.setMergeMode(true); // When enabled, all fonts added with this config would be merged with the previously added font
|
||||
fontConfig.setPixelSnapH(true);
|
||||
|
||||
fontAtlas.addFontFromMemoryTTF(loadFromResources("basis33.ttf"), 16, fontConfig);
|
||||
|
||||
fontConfig.setMergeMode(false);
|
||||
fontConfig.setPixelSnapH(false);
|
||||
|
||||
// Fonts from file/memory example
|
||||
// We can add new fonts from the file system
|
||||
fontAtlas.addFontFromFileTTF("src/test/resources/Righteous-Regular.ttf", 14, fontConfig);
|
||||
fontAtlas.addFontFromFileTTF("src/test/resources/Righteous-Regular.ttf", 16, fontConfig);
|
||||
|
||||
// Or directly from the memory
|
||||
fontConfig.setName("Roboto-Regular.ttf, 14px"); // This name will be displayed in Style Editor
|
||||
fontAtlas.addFontFromMemoryTTF(loadFromResources("Roboto-Regular.ttf"), 14, fontConfig);
|
||||
fontConfig.setName("Roboto-Regular.ttf, 16px"); // We can apply a new config value every time we add a new font
|
||||
fontAtlas.addFontFromMemoryTTF(loadFromResources("Roboto-Regular.ttf"), 16, fontConfig);
|
||||
|
||||
fontConfig.destroy(); // After all fonts were added we don't need this config more
|
||||
|
||||
// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
|
||||
if (io.hasConfigFlags(ImGuiConfigFlags.ViewportsEnable)) {
|
||||
final ImGuiStyle style = ImGui.getStyle();
|
||||
style.setWindowRounding(0.0f);
|
||||
style.setColor(ImGuiCol.WindowBg, ImGui.getColorU32(ImGuiCol.WindowBg, 1));
|
||||
}
|
||||
}
|
||||
|
||||
// Main application loop
|
||||
private void loop() throws Exception {
|
||||
// Run the rendering loop until the user has attempted to close the window
|
||||
while (!glfwWindowShouldClose(windowPtr)) {
|
||||
startFrame();
|
||||
|
||||
// Any Dear ImGui code SHOULD go between ImGui.newFrame()/ImGui.render() methods
|
||||
imGuiGlfw.newFrame();
|
||||
ImGui.newFrame();
|
||||
|
||||
exampleUi.render();
|
||||
ImGui.render();
|
||||
|
||||
endFrame();
|
||||
}
|
||||
}
|
||||
|
||||
private void startFrame() {
|
||||
// Set the clear color and clear the window
|
||||
glClearColor(exampleUi.backgroundColor[0], exampleUi.backgroundColor[1], exampleUi.backgroundColor[2], 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
private void endFrame() {
|
||||
// After Dear ImGui prepared a draw data, we use it in the LWJGL3 renderer.
|
||||
// At that moment ImGui will be rendered to the current OpenGL context.
|
||||
imGuiGl3.renderDrawData(ImGui.getDrawData());
|
||||
|
||||
if (ImGui.getIO().hasConfigFlags(ImGuiConfigFlags.ViewportsEnable)) {
|
||||
final long backupWindowPtr = glfwGetCurrentContext();
|
||||
ImGui.updatePlatformWindows();
|
||||
ImGui.renderPlatformWindowsDefault();
|
||||
glfwMakeContextCurrent(backupWindowPtr);
|
||||
}
|
||||
|
||||
glfwSwapBuffers(windowPtr);
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
private void disposeWindow() {
|
||||
glfwFreeCallbacks(windowPtr);
|
||||
glfwDestroyWindow(windowPtr);
|
||||
glfwTerminate();
|
||||
Objects.requireNonNull(glfwSetErrorCallback(null)).free();
|
||||
}
|
||||
|
||||
private byte[] loadFromResources(final String fileName) {
|
||||
try (InputStream is = Objects.requireNonNull(getClass().getClassLoader().getResourceAsStream(fileName));
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
|
||||
|
||||
final byte[] data = new byte[16384];
|
||||
|
||||
int nRead;
|
||||
while ((nRead = is.read(data, 0, data.length)) != -1) {
|
||||
buffer.write(data, 0, nRead);
|
||||
}
|
||||
|
||||
return buffer.toByteArray();
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(final String[] args) throws Exception {
|
||||
new ImGuiGlfwExample().run();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user