[API] Add imgui-app and example modules

This commit is contained in:
SpaiR committed 2021-01-24 17:56:45 +02:00
1 parent 0ed291cb0f
commit 46f74381aa
8 files changed
+408

No files matched your search

+66
View File
@@ -0,0 +1,66 @@
plugins {
id 'java-library'
id 'io.freefair.lombok' version '5.3.0'
id 'checkstyle'
id 'com.jfrog.bintray' version '1.8.4'
id 'maven-publish'
}
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
api platform('org.lwjgl:lwjgl-bom:3.2.3')
api 'org.lwjgl:lwjgl'
api 'org.lwjgl:lwjgl-glfw'
api 'org.lwjgl:lwjgl-opengl'
['natives-linux', 'natives-windows', 'natives-macos'].each {
runtime "org.lwjgl:lwjgl::$it"
runtime "org.lwjgl:lwjgl-glfw::$it"
runtime "org.lwjgl:lwjgl-opengl::$it"
}
api project(':imgui-binding')
api project(':imgui-lwjgl3')
}
jar {
from('../bin') {
include 'imgui-java64.dll'
include 'libimgui-java64.so'
include 'libimgui-java64.dylib'
into 'io/imgui/java/native-bin/'
}
}
task sourcesJar(type: Jar, dependsOn: classes) {
archiveClassifier.set('sources')
from sourceSets.main.allSource
}
publishing {
publications {
MyPublication(MavenPublication) {
from components.java
groupId 'io.imgui.java'
artifactId 'imgui-java-app'
artifact sourcesJar
version property('version')
}
}
}
bintray {
user = System.getenv('BINTRAY_USER')
key = System.getenv('BINTRAY_API_KEY')
publications = ['MyPublication']
pkg {
repo = 'io.imgui.java'
name = 'imgui-java-app'
}
}
+2
View File
@@ -0,0 +1,2 @@
# This file is generated by the 'io.freefair.lombok' Gradle plugin
config.stopBubbling = true
@@ -0,0 +1,81 @@
package imgui.app;
/**
* Application class from which ImGui applications extend.
* Serves as an abstraction layer to hide all low-level details about window creation and rendering routine.
*
* <p><h3>Life-cycle</h3>
* The entry point for ImGui applications is the Application class and {@link #launch(Application)} method.
* It initializes application instance and starts the main application loop.
*
* <ol>
* <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 #process()} Method is meant to be overridden with user application logic.</li>
* <li>{@link #disposeImGui()} Destroys Dear ImGui context.</li>
* <li>{@link #disposeWindow()} Destroys application window.</li>
* </ol>
*
* <p>As it could be seen, ImGui application differs from the classic one in the way of its life-cycle flow.
* Instead of creating widgets and adding listeners to them we have an application loop where everything is handled right away.
* Read more about Immediate GUI mode to understand that paradigm better.
*
* <p><h3>Example</h3>
* The simplest application example could be done in the next way:
*
* <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");
* }
*
* public static void main(final String[] args) {
* launch(new Main());
* }
* }
* }</pre>
*
* <p>As its said, {@link #process()} method is meant to be overridden. All your application logic should go there.
*
* <p><h3>Threading</h3>
* Unlike other Java applications, ImGui is about "one thread for everything". You still can use multi-threading, but be careful.
* For example, large list of computations could be separated between application ticks. {@link #process()} method is called constantly.
* Use that wisely and remember that all GUI should be in the main thread.
*/
public abstract class Application extends Window {
/**
* Method is 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) {
}
/**
* Entry point of any ImGui application. Use it to start the application loop.
*
* @param app application instance to run
*/
public static void launch(final Application app) {
initialize(app);
app.run();
app.dispose();
}
private static void initialize(final Application app) {
final Configuration config = new Configuration();
app.configure(config);
app.init(config);
}
}
@@ -0,0 +1,26 @@
package imgui.app;
import lombok.Data;
/**
* Data class to provide basic information about the window. Like, the title name etc.
*/
@Data
public final class Configuration {
/**
* Application title.
*/
private String title = "ImGui Java Application";
/**
* Application window width.
*/
private int width = 1280;
/**
* Application window height.
*/
private int height = 768;
/**
* When true, application will be maximized by default.
*/
private boolean fullScreen = false;
}
@@ -0,0 +1,186 @@
package imgui.app;
import imgui.ImGui;
import imgui.flag.ImGuiConfigFlags;
import imgui.gl3.ImGuiImplGl3;
import imgui.glfw.ImGuiImplGlfw;
import org.lwjgl.glfw.Callbacks;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
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;
/**
* Low-level abstraction, which creates application window and starts the main loop.
* It's recommended to use {@link Application}, but this class could be extended directly as well.
* Take a note, that in this case you need to call all application loop methods ({@link #init(Configuration)}, {@link #run()}, {@link #dispose()}) manually.
*/
public abstract class Window {
private final ImGuiImplGlfw imGuiGlfw = new ImGuiImplGlfw();
private final ImGuiImplGl3 imGuiGl3 = new ImGuiImplGl3();
private String glslVersion = null;
/**
* Pointer to native GLFW window.
*/
protected long handle;
/**
* Background color of the window.
*/
protected Color colorBg = Color.GRAY;
/**
* Method to initialize application.
*
* @param config configuration object with basic window information
*/
protected final void init(final Configuration config) {
initWindow(config);
initImGui(config);
imGuiGlfw.init(handle, true);
imGuiGl3.init(glslVersion);
}
/**
* Method to dispose all used application resources and destroy its window.
*/
protected final void dispose() {
imGuiGl3.dispose();
imGuiGlfw.dispose();
disposeImGui();
disposeWindow();
}
/**
* Method to create and initialize GLFW window.
*
* @param config configuration object with basic window information
*/
protected void initWindow(final Configuration config) {
GLFWErrorCallback.createPrint(System.err).set();
if (!GLFW.glfwInit()) {
throw new IllegalStateException("Unable to initialize GLFW");
}
decideGlGlslVersions();
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
handle = GLFW.glfwCreateWindow(config.getWidth(), config.getHeight(), config.getTitle(), MemoryUtil.NULL, MemoryUtil.NULL);
if (handle == MemoryUtil.NULL) {
throw new RuntimeException("Failed to create the GLFW window");
}
try (MemoryStack stack = MemoryStack.stackPush()) {
final IntBuffer pWidth = stack.mallocInt(1); // int*
final IntBuffer pHeight = stack.mallocInt(1); // int*
GLFW.glfwGetWindowSize(handle, pWidth, pHeight);
final GLFWVidMode vidmode = Objects.requireNonNull(GLFW.glfwGetVideoMode(GLFW.glfwGetPrimaryMonitor()));
GLFW.glfwSetWindowPos(handle, (vidmode.width() - pWidth.get(0)) / 2, (vidmode.height() - pHeight.get(0)) / 2);
}
GLFW.glfwMakeContextCurrent(handle);
GLFW.glfwSwapInterval(GLFW.GLFW_TRUE);
GLFW.glfwShowWindow(handle);
if (config.isFullScreen()) {
GLFW.glfwMaximizeWindow(handle);
}
GL.createCapabilities();
}
private void decideGlGlslVersions() {
final boolean isMac = System.getProperty("os.name").toLowerCase().contains("mac");
if (isMac) {
glslVersion = "#version 150";
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 3);
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 2);
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT, GLFW.GLFW_TRUE); // Required on Mac
} else {
glslVersion = "#version 130";
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 3);
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 0);
}
}
/**
* Method to initialize Dear ImGui context. Could be overridden to do custom Dear ImGui setup before application start.
*
* @param config configuration object with basic window information
*/
protected void initImGui(final Configuration config) {
ImGui.createContext();
}
/**
* Main application loop.
*/
protected final void run() {
while (!GLFW.glfwWindowShouldClose(handle)) {
startFrame();
process();
endFrame();
}
}
/**
* Method to be overridden by user to provide main application logic.
*/
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.glClear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT);
imGuiGlfw.newFrame();
ImGui.newFrame();
}
private void endFrame() {
ImGui.render();
imGuiGl3.renderDrawData(ImGui.getDrawData());
if (ImGui.getIO().hasConfigFlags(ImGuiConfigFlags.ViewportsEnable)) {
final long backupWindowPtr = GLFW.glfwGetCurrentContext();
ImGui.updatePlatformWindows();
ImGui.renderPlatformWindowsDefault();
GLFW.glfwMakeContextCurrent(backupWindowPtr);
}
GLFW.glfwSwapBuffers(handle);
GLFW.glfwPollEvents();
}
/**
* Method to destroy Dear ImGui context.
*/
protected void disposeImGui() {
ImGui.destroyContext();
}
/**
* Method to destroy GLFW window.
*/
protected void disposeWindow() {
Callbacks.glfwFreeCallbacks(handle);
GLFW.glfwDestroyWindow(handle);
GLFW.glfwTerminate();
Objects.requireNonNull(GLFW.glfwSetErrorCallback(null)).free();
}
}