{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreihpkkk3lvpuz5xj62w2mulpeh25mfjks4n5s7w3enh6vksmwjnxxq",
    "uri": "at://did:plc:dxjzgxe7cvirxkwfjr2tjspt/app.bsky.feed.post/3mn4ntjwtdpt2"
  },
  "path": "/t/use-imgui-in-version-3-10-0/49605#post_1",
  "publishedAt": "2026-05-31T01:05:33.000Z",
  "site": "https://hub.jmonkeyengine.org",
  "tags": [
    "@author"
  ],
  "textContent": "\n    package jmeimgui;\n\n    import com.jme3.system.JmeContext;\n    import com.jme3.system.JmeSystem;\n    import com.jme3.system.Platform;\n    import com.jme3.system.lwjgl.LwjglWindow;\n    import imgui.ImDrawData;\n    import imgui.ImGui;\n    import imgui.ImGuiIO;\n    import imgui.flag.ImGuiConfigFlags;\n    import imgui.gl3.ImGuiImplGl3;\n    import imgui.type.ImInt;\n    import java.nio.ByteBuffer;\n    import static org.lwjgl.opengl.GL11.GL_LINEAR;\n    import static org.lwjgl.opengl.GL11.GL_RENDERER;\n    import static org.lwjgl.opengl.GL11.GL_RGBA;\n    import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;\n    import static org.lwjgl.opengl.GL11.GL_TEXTURE_BINDING_2D;\n    import static org.lwjgl.opengl.GL11.GL_TEXTURE_MAG_FILTER;\n    import static org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE;\n    import static org.lwjgl.opengl.GL11.GL_VENDOR;\n    import static org.lwjgl.opengl.GL11.GL_VERSION;\n    import static org.lwjgl.opengl.GL11.glBindTexture;\n    import static org.lwjgl.opengl.GL11.glGenTextures;\n    import static org.lwjgl.opengl.GL11.glGetInteger;\n    import static org.lwjgl.opengl.GL11.glGetString;\n    import static org.lwjgl.opengl.GL11.glTexImage2D;\n    import static org.lwjgl.opengl.GL11.glTexParameteri;\n    import static org.lwjgl.opengl.GL11C.GL_TEXTURE_MIN_FILTER;\n    import static org.lwjgl.opengl.GL20.GL_SHADING_LANGUAGE_VERSION;\n\n    /**\n     * JME3与ImGui集成类 - 已适配 JME 3.10.0-alpha5 及 LWJGL 3.4.1 SDL3 后端\n     *\n     * @author Icyboxs\n     */\n    public class JmeImGui {\n\n        private final ImGuiImplGl3 imGuiGl3 = new ImGuiImplGl3();\n        private long windowHandle;\n        private boolean initialized = false;\n\n        // 【关键修改点 1】:新增成员变量,用来就地缓存初始化时喂进来的上下文句柄\n        private JmeContext cachedContext;\n\n        /**\n         * 初始化ImGui - 适配1.92.0+ 与 SDL 窗口上下文\n         */\n        /**\n         * 初始化ImGui - 适配 1.92.0+ 与新版 JME3.10 运行期上下文抢道冲突\n         */\n        public void init(JmeContext context, Runnable beforeInit) {\n            this.cachedContext = context;\n            this.windowHandle = ((LwjglWindow) context).getWindowHandle(); //\n\n            // 创建ImGui上下文\n            ImGui.createContext(); //\n\n            // 获取IO配置并启用新特性\n            ImGuiIO io = ImGui.getIO(); //\n            io.setIniFilename(null); // 禁用ini文件保存\n\n            // 启用常规特性\n            io.addConfigFlags(ImGuiConfigFlags.DockingEnable); //\n\n            // 执行自定义初始化回调\n            if (beforeInit != null) {\n                beforeInit.run(); //\n            }\n\n            // 告诉 ImGui 现在的底层平台环境,阻止其走入 GLFW 的事件劫持逻辑\n            try {\n                io.setBackendPlatformName(\"imgui_impl_sdl3\");\n                io.setBackendRendererName(\"imgui_impl_opengl3\");\n            } catch (NoSuchMethodError e) {\n                // 兼容防抱死\n            }\n\n            try {\n                if (org.lwjgl.opengl.GL.getCapabilities() == null) {\n                    // 如果发现真的没初始化(虽然基本不可能),再让它安全创建\n                    org.lwjgl.opengl.GL.createCapabilities();\n                }\n            } catch (IllegalStateException e) {\n                // 万一底层还在报错,直接强行注入 JME 当前已经绑定好的原生指针环境\n                // 彻底杜绝 LWJGL 抛出 \"setFunctionMissingAddresses has been called already\"\n                try {\n                    java.lang.reflect.Method makeCurrent = org.lwjgl.opengl.GL.class.getDeclaredMethod(\"makeCurrent\", org.lwjgl.opengl.GLCapabilities.class);\n                    makeCurrent.setAccessible(true);\n                    // 动态获取当前活动的句柄实例,让其复用\n                    org.lwjgl.opengl.GLCapabilities caps = org.lwjgl.opengl.GL.createCapabilities(false);\n                    makeCurrent.invoke(null, caps);\n                } catch (Exception ex) {\n                    // 忽略反射异常,确保静默向下通行\n                }\n            }\n            // ============================================================\n\n            // 此时初始化已经绝对安全,直接通行!\n            imGuiGl3.init(decideGlslVersion()); //\n            imGuiGl3.newFrame(); //\n\n            initialized = true;\n        }\n\n        public void init(JmeContext context) {\n            init(context, null); //\n        }\n\n        /**\n         * 开始新帧 - 彻底修复 JmeSystem 静态调用符号缺失问题\n         */\n        public void startFrame() {\n            if (!initialized) {\n                return;\n            }\n\n            imgui.ImGuiIO io = ImGui.getIO();\n\n            // 【关键修改点 3】:直接读取 cachedContext 成员,完美榨取宽度、高度和计时器信息\n            try {\n                if (cachedContext != null && cachedContext.getSettings() != null) {\n                    com.jme3.system.AppSettings settings = cachedContext.getSettings();\n                    io.setDisplaySize((float) settings.getWidth(), (float) settings.getHeight());\n\n                    if (cachedContext.getTimer() != null) {\n                        float tpf = cachedContext.getTimer().getTimePerFrame();\n                        io.setDeltaTime(tpf > 0 ? tpf : 1.0f / 60.0f);\n                    } else {\n                        io.setDeltaTime(1.0f / 60.0f);\n                    }\n                } else {\n                    io.setDisplaySize(1920f, 1080f);\n                    io.setDeltaTime(1.0f / 60.0f);\n                }\n            } catch (Exception e) {\n                io.setDisplaySize(1920f, 1080f);\n                io.setDeltaTime(1.0f / 60.0f);\n            }\n\n            // 正式步入 Dear ImGui 的核心渲染生命周期\n            ImGui.newFrame();\n        }\n\n        /**\n         * 结束帧并渲染\n         */\n        public void endFrame() {\n            if (!initialized || ImGui.getCurrentContext() == null) {\n                return;\n            }\n\n            try {\n                ImGui.render(); //\n                final ImDrawData drawData = ImGui.getDrawData(); //\n                if (!isValidDrawData(drawData)) { //\n                    return;\n                }\n\n                // 渲染主视口\n                imGuiGl3.renderDrawData(drawData); //\n\n            } catch (Exception e) {\n                System.err.println(\"ImGui渲染错误: \" + e.getClass().getSimpleName());\n                e.printStackTrace();\n            }\n        }\n\n        /**\n         * 验证绘制数据有效性\n         */\n        private boolean isValidDrawData(ImDrawData drawData) {\n            if (drawData == null) { //\n                return false;\n            }\n\n            if (!drawData.getValid()) { //\n                return false;\n            }\n\n            try {\n                float width = drawData.getDisplaySizeX(); //\n                float height = drawData.getDisplaySizeY(); //\n                return width > 0 && height > 0; //\n            } catch (Exception e) {\n                return false;\n            }\n        }\n\n        /**\n         * 刷新字体纹理\n         */\n        public void refreshFontTexture() {\n            if (!initialized) {\n                return;\n            }\n\n            ImInt fontW = new ImInt(); //\n            ImInt fontH = new ImInt(); //\n            ImGuiIO imGuiIO = ImGui.getIO(); //\n            ByteBuffer fontData = imGuiIO.getFonts().getTexDataAsRGBA32(fontW, fontH); //\n\n            int originalTexture = glGetInteger(GL_TEXTURE_BINDING_2D); //\n            int fontTexture = glGenTextures(); //\n\n            glBindTexture(GL_TEXTURE_2D, fontTexture); //\n            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //\n            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //\n            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fontW.get(), fontH.get(), 0, GL_RGBA, GL_UNSIGNED_BYTE, fontData); //\n\n            imGuiIO.getFonts().setTexID(fontTexture); //\n            glBindTexture(GL_TEXTURE_2D, originalTexture); //\n        }\n\n        /**\n         * 释放资源\n         */\n        public void dispose() {\n            if (!initialized) {\n                return;\n            }\n\n            try {\n                ImGui.destroyContext(); //\n                initialized = false;\n                System.out.println(\"ImGui资源已释放\");\n            } catch (Exception e) {\n                System.err.println(\"ImGui资源释放错误: \" + e.getMessage());\n            }\n        }\n\n        /**\n         * 判断GLSL版本\n         */\n        protected String decideGlslVersion() {\n            Platform.Os os = JmeSystem.getPlatform().getOs(); //\n            if (os == Platform.Os.Windows) { //\n                return \"#version 150\"; //\n            } else if (isOpenGLVersionSupported(3, 3)) { //\n                return \"#version 330\"; //\n            } else {\n                return \"#version 130\"; //\n            }\n        }\n\n        /**\n         * 检查是否支持特定的OpenGL版本\n         */\n        public boolean isOpenGLVersionSupported(int major, int minor) {\n            try {\n                String version = getOpenGLVersion();\n                if (version == null || version.equals(\"Unknown\")) {\n                    return false;\n                }\n\n                String[] parts = version.split(\"\\\\s+\")[0].split(\"\\\\.\");\n                if (parts.length >= 2) {\n                    int actualMajor = Integer.parseInt(parts[0]);\n                    int actualMinor = Integer.parseInt(parts[1]);\n\n                    if (actualMajor > major) {\n                        return true;\n                    }\n                    if (actualMajor == major && actualMinor >= minor) {\n                        return true;\n                    }\n                }\n                return false;\n            } catch (Exception e) {\n                return false;\n            }\n        }\n\n        public String getOpenGLVersion() {\n            try {\n                String version = glGetString(GL_VERSION);\n                return version != null ? version : \"Unknown\";\n            } catch (Exception e) {\n                return \"Error: \" + e.getMessage();\n            }\n        }\n\n        public String getOpenGLVendor() {\n            try {\n                String vendor = glGetString(GL_VENDOR);\n                return vendor != null ? vendor : \"Unknown\";\n            } catch (Exception e) {\n                return \"Error: \" + e.getMessage();\n            }\n        }\n\n        public String getOpenGLRenderer() {\n            try {\n                String renderer = glGetString(GL_RENDERER);\n                return renderer != null ? renderer : \"Unknown\";\n            } catch (Exception e) {\n                return \"Error: \" + e.getMessage();\n            }\n        }\n\n        public String getGLSLVersion() {\n            try {\n                String glslVersion = glGetString(GL_SHADING_LANGUAGE_VERSION);\n                return glslVersion != null ? glslVersion : \"Unknown\";\n            } catch (Exception e) {\n                return \"Error: \" + e.getMessage();\n            }\n        }\n\n        public void printOpenGLInfo() {\n            System.out.println(\"=== OpenGL 系统信息 ===\");\n            System.out.println(\"版本: \" + getOpenGLVersion());\n            System.out.println(\"供应商: \" + getOpenGLVendor());\n            System.out.println(\"渲染器: \" + getOpenGLRenderer());\n            System.out.println(\"GLSL版本: \" + getGLSLVersion());\n            System.out.println(\"ImGui版本: \" + ImGui.getVersion());\n            System.out.println(\"=====================\");\n        }\n\n        public void showOpenGLInfoWindow() {\n            if (!initialized) {\n                return;\n            }\n\n            ImGui.begin(\"OpenGL 信息\");\n\n            ImGui.text(\"OpenGL 版本: \" + getOpenGLVersion());\n            ImGui.text(\"供应商: \" + getOpenGLVendor());\n            ImGui.text(\"渲染器: \" + getOpenGLRenderer());\n            ImGui.text(\"GLSL 版本: \" + getGLSLVersion());\n            ImGui.text(\"ImGui 版本: \" + ImGui.getVersion());\n            ImGui.text(\"多视口支持: \" + (ImGui.getIO().hasConfigFlags(ImGuiConfigFlags.ViewportsEnable) ? \"是\" : \"否\"));\n            ImGui.text(\"停靠支持: \" + (ImGui.getIO().hasConfigFlags(ImGuiConfigFlags.DockingEnable) ? \"是\" : \"否\"));\n\n            if (ImGui.button(\"复制到剪贴板\")) {\n                String info = String.format(\n                        \"OpenGL版本: %s\\n供应商: %s\\n渲染器: %s\\nGLSL版本: %s\\nImGui版本: %s\",\n                        getOpenGLVersion(), getOpenGLVendor(), getOpenGLRenderer(), getGLSLVersion(), ImGui.getVersion()\n                );\n                ImGui.setClipboardText(info);\n            }\n\n            ImGui.end();\n        }\n\n        public boolean isNvidiaGPU() {\n            String vendor = getOpenGLVendor().toLowerCase();\n            return vendor.contains(\"nvidia\");\n        }\n\n        public boolean isAMDGPU() {\n            String vendor = getOpenGLVendor().toLowerCase();\n            return vendor.contains(\"amd\") || vendor.contains(\"ati\");\n        }\n\n        public boolean isIntelGPU() {\n            String vendor = getOpenGLVendor().toLowerCase();\n            return vendor.contains(\"intel\");\n        }\n\n        public String getOpenGLInfoReport() {\n            return String.format(\n                    \"OpenGL 系统信息报告:\\n\"\n                    + \"=====================\\n\"\n                    + \"版本: %s\\n供应商: %s\\n渲染器: %s\\n\"\n                    + \"GLSL版本: %s\\n显卡类型: %s\\nImGui版本: %s\\n\"\n                    + \"支持OpenGL 3.3+: %s\\n支持OpenGL 4.0+: %s\\n\"\n                    + \"多视口: %s\\n停靠: %s\\n\"\n                    + \"=====================\",\n                    getOpenGLVersion(), getOpenGLVendor(), getOpenGLRenderer(),\n                    getGLSLVersion(), getGPUType(), ImGui.getVersion(),\n                    isOpenGLVersionSupported(3, 3), isOpenGLVersionSupported(4, 0),\n                    ImGui.getIO().hasConfigFlags(ImGuiConfigFlags.ViewportsEnable),\n                    ImGui.getIO().hasConfigFlags(ImGuiConfigFlags.DockingEnable)\n            );\n        }\n\n        public String getGPUType() {\n            if (isNvidiaGPU()) {\n                return \"NVIDIA\";\n            }\n            if (isAMDGPU()) {\n                return \"AMD\";\n            }\n            if (isIntelGPU()) {\n                return \"Intel\";\n            }\n            return \"其他/未知\";\n        }\n\n        public boolean isInitialized() {\n            return initialized;\n        }\n\n        public String getImGuiVersion() {\n            return ImGui.getVersion();\n        }\n    }\n\n\n\nJME3 and ImGui Integration Class - Compatible with JME 3.10.0-alpha5 and LWJGL 3.4.1 SDL3 backend\n\nUnfortunately, the Angle renderer cannot use ImGui. Some small toys I made using the jme3.6.0 version run well in the 3.10.0-alpha5 version of the OpenGL renderer. However, in the Angle renderer, there seem to be many problems.\n\nI haven’t yet understood how the Angle renderer works. The issues encountered with the Angle renderer cannot be fixed at present.",
  "title": "Use ImGui in version 3.10.0"
}