Examples: Emscripten: tweak to allow building on desktop (#3699)
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index b67f333..63e46d2 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -130,6 +130,7 @@
- Examples: Apple+Metal: Forward events down so OS key combination like Cmd+Q can work. (#3554) [@rokups]
- Examples: Emscripten: Renamed example_emscripten/ to example_emscripten_opengl3/. (#3632)
- Examples: Emscripten: Added 'make serve' helper to spawn a web-server on localhost. (#3705) [@Horki]
+- Examples: Emscripten: Tweak to allow building on desktop. (#3699)
- Examples: DirectX12: Move ImGui::Render() call above the first barrier to clarify its lack of effect on the graphics pipe.
- CI: Fix testing for Windows DLL builds. (#3603, #3601) [@iboB]
- Docs: Split examples/README.txt into docs/BACKENDS.md and docs/EXAMPLES.md improved them.
diff --git a/examples/example_emscripten_opengl3/build_win32.bat b/examples/example_emscripten_opengl3/build_win32.bat
new file mode 100644
index 0000000..cc01271
--- /dev/null
+++ b/examples/example_emscripten_opengl3/build_win32.bat
@@ -0,0 +1,8 @@
+@REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler.
+@set OUT_DIR=Debug
+@set OUT_EXE=example_emscripten_opengl3
+@set INCLUDES=/I..\.. /I..\..\backends /I%SDL2_DIR%\include /I..\libs\gl3w
+@set SOURCES=main.cpp ..\..\backends\imgui_impl_sdl.cpp ..\..\backends\imgui_impl_opengl3.cpp ..\..\imgui*.cpp ..\libs\gl3w\GL\gl3w.c
+@set LIBS=/LIBPATH:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib opengl32.lib
+mkdir %OUT_DIR%
+cl /nologo /Zi /MD %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console
diff --git a/examples/example_emscripten_opengl3/main.cpp b/examples/example_emscripten_opengl3/main.cpp
index 13f90ed..36c59e1 100644
--- a/examples/example_emscripten_opengl3/main.cpp
+++ b/examples/example_emscripten_opengl3/main.cpp
@@ -3,61 +3,132 @@
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
-// This is mostly the same code as the SDL2 + OpenGL3 example, simply with the modifications needed to run on Emscripten.
-// It is possible to combine both code into a single source file that will compile properly on Desktop and using Emscripten.
-// See https://github.com/ocornut/imgui/pull/2492 as an example on how to do just that.
+// This is mostly the same code as the SDL2 + OpenGL3 example, with modifications needed to run on Emscripten.
+// We allow this to also compile on Desktop, hence the '#ifdef __EMSCRIPTEN__' blocks.
#include "imgui.h"
#include "imgui_impl_sdl.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
-#include <emscripten.h>
#include <SDL.h>
+
+#ifdef __EMSCRIPTEN__
+#include <emscripten.h>
#include <SDL_opengles2.h>
+#else
+// About Desktop OpenGL function loaders:
+// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
+// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
+// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
+#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
+#include <GL/gl3w.h> // Initialize with gl3wInit()
+#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
+#include <GL/glew.h> // Initialize with glewInit()
+#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
+#include <glad/glad.h> // Initialize with gladLoadGL()
+#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2)
+#include <glad/gl.h> // Initialize with gladLoadGL(...) or gladLoaderLoadGL()
+#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2)
+#define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors.
+#include <glbinding/Binding.h> // Initialize with glbinding::Binding::initialize()
+#include <glbinding/gl/gl.h>
+using namespace gl;
+#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3)
+#define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors.
+#include <glbinding/glbinding.h>// Initialize with glbinding::initialize()
+#include <glbinding/gl/gl.h>
+using namespace gl;
+#else
+#include IMGUI_IMPL_OPENGL_LOADER_CUSTOM
+#endif
+#endif
// Emscripten requires to have full control over the main loop. We're going to store our SDL book-keeping variables globally.
// Having a single function that acts as a loop prevents us to store state in the stack of said function. So we need some location for this.
-SDL_Window* g_Window = NULL;
-SDL_GLContext g_GLContext = NULL;
+static SDL_Window* window = NULL;
+static SDL_GLContext gl_context = NULL;
// For clarity, our main loop code is declared at the end.
void main_loop(void*);
+// Main code
int main(int, char**)
{
// Setup SDL
+ // (Some versions of SDL before <2.0.10 appears to have performance/stalling issues on a minority of Windows systems,
+ // depending on whether SDL_INIT_GAMECONTROLLER is enabled or disabled.. updating to latest version of SDL is recommended!)
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0)
{
printf("Error: %s\n", SDL_GetError());
return -1;
}
- // For the browser using Emscripten, we are going to use WebGL1 with GL ES2. See the Makefile. for requirement details.
- // It is very likely the generated file won't work in many browsers. Firefox is the only sure bet, but I have successfully
- // run this code on Chrome for Android for example.
+#if defined(__EMSCRIPTEN__)
+ // Browser/Emscripten: GL ES2 + GLSL 100
+ // See the README and Makefile for requirement details.
const char* glsl_version = "#version 100";
//const char* glsl_version = "#version 300 es";
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
+#elif defined(__APPLE__)
+ // Desktop: GL 3.2 Core + GLSL 150
+ const char* glsl_version = "#version 150";
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
+#else
+ // Desktop: GL 3.0 + GLSL 130
+ const char* glsl_version = "#version 130";
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
+#endif
// Create window with graphics context
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
- SDL_DisplayMode current;
- SDL_GetCurrentDisplayMode(0, ¤t);
SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
- g_Window = SDL_CreateWindow("Dear ImGui Emscripten example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags);
- g_GLContext = SDL_GL_CreateContext(g_Window);
- if (!g_GLContext)
+ window = SDL_CreateWindow("Dear ImGui Emscripten example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags);
+ gl_context = SDL_GL_CreateContext(window);
+ if (!gl_context)
{
- fprintf(stderr, "Failed to initialize WebGL context!\n");
+ fprintf(stderr, "Failed to initialize GL context!\n");
return 1;
}
+ SDL_GL_MakeCurrent(window, gl_context);
SDL_GL_SetSwapInterval(1); // Enable vsync
+ // Desktop: Initialize OpenGL loader
+#if !defined(__EMSCRIPTEN__)
+#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
+ bool err = gl3wInit() != 0;
+#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
+ bool err = glewInit() != GLEW_OK;
+#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
+ bool err = gladLoadGL() == 0;
+#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2)
+ bool err = gladLoadGL((GLADloadfunc)SDL_GL_GetProcAddress) == 0; // glad2 recommend using the windowing library loader instead of the (optionally) bundled one.
+#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2)
+ bool err = false;
+ glbinding::Binding::initialize();
+#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3)
+ bool err = false;
+ glbinding::initialize([](const char* name) { return (glbinding::ProcAddress)SDL_GL_GetProcAddress(name); });
+#else
+ bool err = false; // If you use IMGUI_IMPL_OPENGL_LOADER_CUSTOM, your loader is likely to requires some form of initialization.
+#endif
+ if (err)
+ {
+ fprintf(stderr, "Failed to initialize OpenGL loader!\n");
+ return 1;
+ }
+#endif
+
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
@@ -65,16 +136,18 @@
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
+#if defined(__EMSCRIPTEN__)
// For an Emscripten build we are disabling file-system access, so let's not attempt to do a fopen() of the imgui.ini file.
// You may manually call LoadIniSettingsFromMemory() to load settings from your own storage.
io.IniFilename = NULL;
+#endif
// Setup Dear ImGui style
ImGui::StyleColorsDark();
//ImGui::StyleColorsClassic();
// Setup Platform/Renderer backends
- ImGui_ImplSDL2_InitForOpenGL(g_Window, g_GLContext);
+ ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
ImGui_ImplOpenGL3_Init(glsl_version);
// Load Fonts
@@ -86,7 +159,7 @@
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
// - Emscripten allows preloading a file or folder to be accessible at runtime. See Makefile for details.
//io.Fonts->AddFontDefault();
-#ifndef IMGUI_DISABLE_FILE_FUNCTIONS
+#if defined(__EMSCRIPTEN__) && !defined(IMGUI_DISABLE_FILE_FUNCTIONS)
io.Fonts->AddFontFromFileTTF("fonts/Roboto-Medium.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("fonts/Cousine-Regular.ttf", 15.0f);
//io.Fonts->AddFontFromFileTTF("fonts/DroidSans.ttf", 16.0f);
@@ -95,14 +168,35 @@
//IM_ASSERT(font != NULL);
#endif
+#if defined(__EMSCRIPTEN__)
// This function call won't return, and will engage in an infinite loop, processing events from the browser, and dispatching them.
emscripten_set_main_loop_arg(main_loop, NULL, 0, true);
+#else
+ // Main loop
+ bool done = false;
+ while (!done)
+ {
+ main_loop((void*)&done);
+ }
+
+ // Cleanup
+ ImGui_ImplOpenGL3_Shutdown();
+ ImGui_ImplSDL2_Shutdown();
+ ImGui::DestroyContext();
+
+ SDL_GL_DeleteContext(gl_context);
+ SDL_DestroyWindow(window);
+ SDL_Quit();
+
+ return 0;
+#endif
}
void main_loop(void* arg)
{
ImGuiIO& io = ImGui::GetIO();
- IM_UNUSED(arg); // We can pass this argument as the second parameter of emscripten_set_main_loop_arg(), but we don't use that.
+ bool* p_done = (bool*)arg;
+ IM_UNUSED(p_done);
// Our state (make them static = more or less global) as a convenience to keep the example terse.
static bool show_demo_window = true;
@@ -118,12 +212,17 @@
while (SDL_PollEvent(&event))
{
ImGui_ImplSDL2_ProcessEvent(&event);
- // Capture events here, based on io.WantCaptureMouse and io.WantCaptureKeyboard
+#if !defined(__EMSCRIPTEN__)
+ if (event.type == SDL_QUIT)
+ *p_done = true;
+ if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))
+ *p_done = true;
+#endif
}
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
- ImGui_ImplSDL2_NewFrame(g_Window);
+ ImGui_ImplSDL2_NewFrame(window);
ImGui::NewFrame();
// 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
@@ -165,10 +264,9 @@
// Rendering
ImGui::Render();
- SDL_GL_MakeCurrent(g_Window, g_GLContext);
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
- SDL_GL_SwapWindow(g_Window);
+ SDL_GL_SwapWindow(window);
}