Enable ImGui for Android (#909)

* Add support for ImGui usage on Android
This commit is contained in:
RH 2022-10-10 06:53:24 +11:00 committed by GitHub
parent 9c7a876970
commit 34094038d5
12 changed files with 800 additions and 17 deletions

View File

@ -18,7 +18,7 @@ option(AX_ENABLE_EXT_FAIRYGUI "Build extension FairyGUI" ON)
option(AX_ENABLE_EXT_LIVE2D "Build extension Live2D" OFF) option(AX_ENABLE_EXT_LIVE2D "Build extension Live2D" OFF)
if(WINDOWS OR MACOSX OR LINUX) if(WINDOWS OR MACOSX OR LINUX OR ANDROID)
option(AX_ENABLE_EXT_IMGUI "Build extension ImGui" ON) option(AX_ENABLE_EXT_IMGUI "Build extension ImGui" ON)
else() else()
set(AX_ENABLE_EXT_IMGUI OFF) set(AX_ENABLE_EXT_IMGUI OFF)

View File

@ -5,7 +5,6 @@ include_directories(imgui)
set(HEADER set(HEADER
ImGuiPresenter.h ImGuiPresenter.h
# CCImGuiColorTextEdit.h # CCImGuiColorTextEdit.h
imgui_impl_ax.h
imgui/imconfig.h imgui/imconfig.h
imgui/imgui.h imgui/imgui.h
imgui/imgui_internal.h imgui/imgui_internal.h
@ -21,7 +20,6 @@ set(HEADER
set(SOURCE set(SOURCE
ImGuiPresenter.cpp ImGuiPresenter.cpp
# CCImGuiColorTextEdit.cpp # CCImGuiColorTextEdit.cpp
imgui_impl_ax.cpp
imgui/imgui.cpp imgui/imgui.cpp
imgui/imgui_demo.cpp imgui/imgui_demo.cpp
imgui/imgui_draw.cpp imgui/imgui_draw.cpp
@ -33,6 +31,14 @@ set(SOURCE
#~ implot/implot_demo.cpp #~ implot/implot_demo.cpp
) )
if(ANDROID)
list(APPEND HEADER imgui_impl_ax_android.cpp)
list(APPEND SOURCE imgui_impl_ax_android.h)
else()
list(APPEND HEADER imgui_impl_ax.cpp)
list(APPEND SOURCE imgui_impl_ax.h)
endif()
#~ if(AX_ENABLE_EXT_LUA) #~ if(AX_ENABLE_EXT_LUA)
#~ include_directories( #~ include_directories(
#~ lua-bindings #~ lua-bindings
@ -64,8 +70,8 @@ set(SOURCE
#~ ) #~ )
#~ endif() #~ endif()
add_library(${target_name} STATIC add_library(${target_name} STATIC
${HEADER} ${HEADER}
${SOURCE}) ${SOURCE})
setup_ax_extension_config(${target_name}) setup_ax_extension_config(${target_name})

View File

@ -1,6 +1,10 @@
#include "ImGuiPresenter.h" #include "ImGuiPresenter.h"
#include <assert.h> #include <assert.h>
#include "imgui_impl_ax.h" #if (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID)
#include "imgui_impl_ax_android.h"
#else
#include "imgui_impl_ax.h"
#endif
#include "imgui_internal.h" #include "imgui_internal.h"
// TODO: mac metal // TODO: mac metal
@ -197,8 +201,12 @@ void ImGuiPresenter::init()
style.Colors[ImGuiCol_WindowBg].w = 1.0f; style.Colors[ImGuiCol_WindowBg].w = 1.0f;
} }
#if (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID)
ImGui_ImplAndroid_InitForAx(Director::getInstance()->getOpenGLView(), true);
#else
auto window = static_cast<GLViewImpl*>(Director::getInstance()->getOpenGLView())->getWindow(); auto window = static_cast<GLViewImpl*>(Director::getInstance()->getOpenGLView())->getWindow();
ImGui_ImplGlfw_InitForAx(window, true); ImGui_ImplGlfw_InitForAx(window, true);
#endif
ImGui_ImplAx_Init(); ImGui_ImplAx_Init();
ImGui_ImplAx_SetCustomFontLoader(&ImGuiPresenter::loadCustomFonts, this); ImGui_ImplAx_SetCustomFontLoader(&ImGuiPresenter::loadCustomFonts, this);
@ -218,7 +226,11 @@ void ImGuiPresenter::cleanup()
ImGui_ImplAx_SetCustomFontLoader(nullptr, nullptr); ImGui_ImplAx_SetCustomFontLoader(nullptr, nullptr);
ImGui_ImplAx_Shutdown(); ImGui_ImplAx_Shutdown();
#if (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID)
ImGui_ImplAndroid_Shutdown();
#else
ImGui_ImplGlfw_Shutdown(); ImGui_ImplGlfw_Shutdown();
#endif
AX_SAFE_RELEASE_NULL(_fontsTexture); AX_SAFE_RELEASE_NULL(_fontsTexture);
@ -265,9 +277,12 @@ void ImGuiPresenter::loadCustomFonts(void* ud)
float ImGuiPresenter::scaleAllByDPI(float userScale) float ImGuiPresenter::scaleAllByDPI(float userScale)
{ {
// Gets scale
float xscale = 1.0f; float xscale = 1.0f;
#if (AX_TARGET_PLATFORM != AX_PLATFORM_ANDROID)
// Gets scale
glfwGetMonitorContentScale(glfwGetPrimaryMonitor(), &xscale, nullptr); glfwGetMonitorContentScale(glfwGetPrimaryMonitor(), &xscale, nullptr);
#endif
auto zoomFactor = userScale * xscale; auto zoomFactor = userScale * xscale;
auto imFonts = ImGui::GetIO().Fonts; auto imFonts = ImGui::GetIO().Fonts;
@ -293,6 +308,11 @@ float ImGuiPresenter::scaleAllByDPI(float userScale)
return zoomFactor; return zoomFactor;
} }
void ImGuiPresenter::setViewResolution(float width, float height)
{
ImGui_ImplAx_SetViewResolution(width, height);
}
void ImGuiPresenter::addFont(std::string_view fontFile, float fontSize, CHS_GLYPH_RANGE glyphRange) void ImGuiPresenter::addFont(std::string_view fontFile, float fontSize, CHS_GLYPH_RANGE glyphRange)
{ {
if (FileUtils::getInstance()->isFileExistInternal(fontFile)) if (FileUtils::getInstance()->isFileExistInternal(fontFile))
@ -340,7 +360,11 @@ void ImGuiPresenter::beginFrame()
{ {
// create frame // create frame
ImGui_ImplAx_NewFrame(); ImGui_ImplAx_NewFrame();
#if (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID)
ImGui_ImplAndroid_NewFrame();
#else
ImGui_ImplGlfw_NewFrame(); ImGui_ImplGlfw_NewFrame();
#endif
ImGui::NewFrame(); ImGui::NewFrame();
// move to endFrame? // move to endFrame?

View File

@ -45,6 +45,8 @@ public:
float scaleAllByDPI(float userScale); float scaleAllByDPI(float userScale);
float getContentZoomFactor() const { return _contentZoomFactor; } float getContentZoomFactor() const { return _contentZoomFactor; }
void setViewResolution(float width, float height);
/// <summary> /// <summary>
/// Add ImGui font with contentZoomFactor /// Add ImGui font with contentZoomFactor
/// </summary> /// </summary>

View File

@ -1296,7 +1296,7 @@ void ImGui_ImplAx_Shutdown()
ImGui_ImplAx_DestroyDeviceObjects(); ImGui_ImplAx_DestroyDeviceObjects();
} }
IMGUI_IMPL_API void ImGui_ImplAx_NewFrame() { IMGUI_IMPL_API void ImGui_ImplAx_NewFrame() {
auto bd = ImGui_ImplGlfw_GetBackendData(); auto bd = ImGui_ImplGlfw_GetBackendData();
//bd->CallbackCommands.clear(); //bd->CallbackCommands.clear();
bd->CustomCommands.clear(); bd->CustomCommands.clear();
@ -1330,6 +1330,13 @@ IMGUI_IMPL_API void ImGui_ImplAx_SetDeviceObjectsDirty()
bd->FontDeviceObjectsDirty = true; bd->FontDeviceObjectsDirty = true;
} }
IMGUI_IMPL_API void ImGui_ImplAx_SetViewResolution(float width, float height)
{
// Resize (expand) window
auto* view = (GLViewImpl*)Director::getInstance()->getOpenGLView();
view->setWindowed(width, height);
}
static void ImGui_ImplAx_CreateWindow(ImGuiViewport* viewport) static void ImGui_ImplAx_CreateWindow(ImGuiViewport* viewport)
{ {
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();

View File

@ -23,3 +23,6 @@ IMGUI_IMPL_API void* ImGui_ImplAx_GetFontsTexture();
// Sets Device objects dirty // Sets Device objects dirty
IMGUI_IMPL_API void ImGui_ImplAx_SetDeviceObjectsDirty(); IMGUI_IMPL_API void ImGui_ImplAx_SetDeviceObjectsDirty();
// Set the required view resolution for the UI
IMGUI_IMPL_API void ImGui_ImplAx_SetViewResolution(float width, float height);

View File

@ -0,0 +1,715 @@
#include "imgui_impl_ax_android.h"
#include "cocos2d.h"
#include "renderer/backend/Backend.h"
#include <functional>
#include <android/native_window.h>
#include <android/input.h>
#include <android/keycodes.h>
#include <android/log.h>
USING_NS_AX;
using namespace backend;
// Clang warnings with -Weverything
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
#if __has_warning("-Wzero-as-null-pointer-constant")
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
#endif
#endif
// GLFW
// Android data
static char g_LogTag[] = "ImGuiAndroid";
// axmol spec data
constexpr IndexFormat IMGUI_INDEX_FORMAT = sizeof(ImDrawIdx) == 2 ? IndexFormat::U_SHORT : IndexFormat::U_INT;
struct ProgramInfoData
{
Program* program = nullptr;
// Uniforms location
UniformLocation texture{};
UniformLocation projection{};
// Vertex attributes location
int position = 0;
int uv = 0;
int color = 0;
VertexLayout layout{};
};
struct SavedRenderStateData
{
backend::CullMode cull{};
Viewport vp{};
ScissorRect scissorRect{};
bool scissorTest{};
bool depthTest{};
};
// end of axmol spec
struct ImGui_ImplAndroid_Data
{
GLView* Window;
double Time;
bool InstalledCallbacks;
// ImGui_ImplAndroid_Data() { memset(this, 0, sizeof(*this)); }
// axmol spec data
std::chrono::steady_clock::time_point LastFrameTime{};
ImGuiImplCocos2dxLoadFontFun LoadCustomFont = nullptr;
void* LoadCustomFontUserData = nullptr;
ProgramInfoData ProgramInfo{};
ProgramInfoData ProgramFontInfo{};
bool FontDeviceObjectsDirty = false;
Texture2D* FontTexture = nullptr;
Mat4 Projection;
ImVec2 LastValidMousePos;
EventListener* TouchListener = nullptr;
// std::vector<std::shared_ptr<CallbackCommand>> CallbackCommands{};
std::vector<std::shared_ptr<CustomCommand>> CustomCommands{};
Vector<ProgramState*> ProgramStates{};
SavedRenderStateData SavedRenderState{};
Vec2 ViewResolution = Vec2(1920, 1080);
};
// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
static ImGui_ImplAndroid_Data* ImGui_ImplAndroid_GetBackendData()
{
return ImGui::GetCurrentContext() ? (ImGui_ImplAndroid_Data*)ImGui::GetIO().BackendPlatformUserData : NULL;
}
// Forward Declarations
static void ImGui_ImplAndroid_ShutdownPlatformInterface();
// Functions
static bool ImGui_ImplAndroid_Init(GLView* window, bool install_callbacks)
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!");
// Setup backend capabilities flags
ImGui_ImplAndroid_Data* bd = IM_NEW(ImGui_ImplAndroid_Data)();
io.BackendPlatformUserData = (void*)bd;
io.BackendPlatformName = "imgui_impl_android";
//io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
//io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional)
bd->Window = window;
bd->Time = 0.0;
io.ClipboardUserData = bd->Window;
// Our mouse update function expect PlatformHandle to be filled for the main viewport
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
main_viewport->PlatformHandle = (void*)bd->Window;
io.AddFocusEvent(true);
auto* touchListener = EventListenerTouchOneByOne::create();
touchListener->setSwallowTouches(true);
touchListener->retain();
bd->TouchListener = touchListener;
touchListener->onTouchBegan = [](Touch* touch, Event* /*event*/) -> bool {
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplAndroid_Data* bd = ImGui_ImplAndroid_GetBackendData();
auto location = touch->getLocationInView();
auto origin = bd->Window->getVisibleOrigin();
auto realX = location.x * bd->Window->getScaleX();
auto realY = (location.y - origin.y) * bd->Window->getScaleY();
realX /= io.DisplayFramebufferScale.x;
realY /= io.DisplayFramebufferScale.y;
io.AddMousePosEvent(realX, realY);
bd->LastValidMousePos = ImVec2(realX, realY);
io.AddMouseButtonEvent(0, true);
// We can't check if we're actually hovering over a ImGui element, since the
// AddMousePosEvent is not instant, it's queued. So, just return true here
// to indicate that we're handling this event.
return true;
};
touchListener->onTouchMoved = [](Touch* touch, Event* /*event*/) {
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplAndroid_Data* bd = ImGui_ImplAndroid_GetBackendData();
auto location = touch->getLocationInView();
auto origin = bd->Window->getVisibleOrigin();
auto realX = location.x * bd->Window->getScaleX();
auto realY = (location.y - origin.y) * bd->Window->getScaleY();
realX /= io.DisplayFramebufferScale.x;
realY /= io.DisplayFramebufferScale.y;
io.AddMousePosEvent(realX, realY);
bd->LastValidMousePos = ImVec2(realX, realY);;
};
touchListener->onTouchEnded = [](Touch* touch, Event* /*event*/) {
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplAndroid_Data* bd = ImGui_ImplAndroid_GetBackendData();
auto location = touch->getLocationInView();
auto origin = bd->Window->getVisibleOrigin();
auto realX = location.x * bd->Window->getScaleX();
auto realY = (location.y - origin.y) * bd->Window->getScaleY();
realX /= io.DisplayFramebufferScale.x;
realY /= io.DisplayFramebufferScale.y;
io.AddMousePosEvent(realX, realY);
bd->LastValidMousePos = ImVec2(realX, realY);;
io.AddMouseButtonEvent(0, false);
};
touchListener->onTouchCancelled = [](Touch* touch, Event* /*event*/) {
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplAndroid_Data* bd = ImGui_ImplAndroid_GetBackendData();
auto location = touch->getLocationInView();
auto origin = bd->Window->getVisibleOrigin();
auto realX = location.x * bd->Window->getScaleX();
auto realY = (location.y - origin.y) * bd->Window->getScaleY();
realX /= io.DisplayFramebufferScale.x;
realY /= io.DisplayFramebufferScale.y;
io.AddMousePosEvent(realX, realY);
bd->LastValidMousePos = ImVec2(realX, realY);
io.AddMouseButtonEvent(0, false);
};
Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(touchListener, 1);
return true;
}
IMGUI_IMPL_API void ImGui_ImplAx_RenderPlatform()
{
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
}
}
void ImGui_ImplAndroid_Shutdown()
{
ImGui_ImplAndroid_Data* bd = ImGui_ImplAndroid_GetBackendData();
IM_ASSERT(bd != NULL && "No platform backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplAndroid_ShutdownPlatformInterface();
io.BackendPlatformName = NULL;
io.BackendPlatformUserData = NULL;
IM_DELETE(bd);
}
void ImGui_ImplAndroid_NewFrame()
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplAndroid_Data* bd = ImGui_ImplAndroid_GetBackendData();
IM_ASSERT(bd != NULL && "Did you call ImGui_ImplAndroid_InitForXXX()?");
// Setup display size (every frame to accommodate for window resizing)
int32_t window_width = bd->ViewResolution.width;
int32_t window_height = bd->ViewResolution.height;
int display_width = bd->Window->getFrameSize().width;
int display_height = bd->Window->getFrameSize().height;
io.DisplaySize = ImVec2((float)window_width, (float)window_height);
if (window_width > 0 && window_height > 0)
io.DisplayFramebufferScale = ImVec2((float)display_width / window_width, (float)display_height / window_height);
// Setup time step
struct timespec current_timespec;
clock_gettime(CLOCK_MONOTONIC, &current_timespec);
double current_time = (double)(current_timespec.tv_sec) + (current_timespec.tv_nsec / 1000000000.0);
io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f);
bd->Time = current_time;
}
//--------------------------------------------------------------------------------------------------------
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
//--------------------------------------------------------------------------------------------------------
// Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data.
struct ImGui_ImplAndroid_ViewportData
{
GLView* Window;
bool WindowOwned;
int IgnoreWindowPosEventFrame;
int IgnoreWindowSizeEventFrame;
ImGui_ImplAndroid_ViewportData() { Window = NULL; WindowOwned = false; IgnoreWindowSizeEventFrame = IgnoreWindowPosEventFrame = -1; }
~ImGui_ImplAndroid_ViewportData() { IM_ASSERT(Window == NULL); }
};
static void ImGui_ImplAndroid_WindowCloseCallback(GLView* window)
{
if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window))
viewport->PlatformRequestClose = true;
}
static void ImGui_ImplAndroid_ShutdownPlatformInterface()
{
ImGui::DestroyPlatformWindows();
}
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
////////////////////////// axmol spec /////////////////////////
#define AX_PTR_CAST(v, pointer_type) reinterpret_cast<pointer_type>(v)
// fps macro
#define AX_IMGUI_DEFAULT_DELTA (1 / 60.f)
#define AX_IMGUI_MIN_DELTA (1 / 1000.f)
#define AX_IMGUI_MAX_DELTA (1 / 30.f)
enum
{
GlfwClientApi_Axmol = 0xadee,
};
// axmol spec
bool ImGui_ImplAndroid_InitForAx(GLView* window, bool install_callbacks)
{
return ImGui_ImplAndroid_Init(window, install_callbacks);
}
struct ImGui_ImplAx_Data
{
// axmol spec data
ImGuiImplCocos2dxLoadFontFun LoadCustomFont = nullptr;
void* LoadCustomFontUserData = nullptr;
ProgramInfoData ProgramInfo{};
ProgramInfoData ProgramFontInfo{};
bool FontDeviceObjectsDirty = false;
Texture2D* FontTexture = nullptr;
Mat4 Projection;
std::vector<std::shared_ptr<CallbackCommand>> CallbackCommands{};
std::vector<std::shared_ptr<CustomCommand>> CustomCommands{};
Vector<ProgramState*> ProgramStates{};
SavedRenderStateData SavedRenderState{};
};
static bool ImGui_ImplAx_CreateFontsTexture();
static void ImGui_ImplAx_DestroyFontsTexture();
static void ImGui_ImplAx_DestroyDeviceObjects();
static bool ImGui_ImplAx_CreateDeviceObjects();
static void ImGui_ImplAx_RenderWindow(ImGuiViewport* viewport, void*);
static void AddRendererCommand(const std::function<void()>& f);
static bool ImGui_ImplAx_createShaderPrograms();
static void ImGui_ImplAx_Renderer_RenderWindow(ImGuiViewport* viewport, void*);
static void ImGui_ImplAx_InitPlatformInterface()
{
// Register platform interface (will be coupled with a renderer interface)
ImGui_ImplAndroid_Data* bd = ImGui_ImplAndroid_GetBackendData();
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
platform_io.Renderer_RenderWindow = ImGui_ImplAx_Renderer_RenderWindow;
}
void ImGui_ImplAx_Init()
{
auto bd = ImGui_ImplAndroid_GetBackendData();
auto& io = ImGui::GetIO();
io.BackendRendererUserData = (void*)bd;
io.BackendRendererName = "imgui_impl_axmol";
io.BackendFlags |=
ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)
// axmol spec: disable auto load and save
io.IniFilename = nullptr;
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
ImGui_ImplAx_InitPlatformInterface();
}
void ImGui_ImplAx_Shutdown()
{
ImGui_ImplAx_DestroyDeviceObjects();
}
IMGUI_IMPL_API void ImGui_ImplAx_NewFrame() {
auto bd = ImGui_ImplAndroid_GetBackendData();
//bd->CallbackCommands.clear();
bd->CustomCommands.clear();
bd->ProgramStates.clear();
if (!bd->FontTexture)
ImGui_ImplAx_CreateDeviceObjects();
else if (bd->FontDeviceObjectsDirty)
{ // recreate device objects, fonts also should be device objects
ImGui_ImplAx_DestroyDeviceObjects();
ImGui_ImplAx_CreateDeviceObjects();
}
}
IMGUI_IMPL_API void ImGui_ImplAx_SetCustomFontLoader(ImGuiImplCocos2dxLoadFontFun fun, void* userdata)
{
auto bd = ImGui_ImplAndroid_GetBackendData();
bd->LoadCustomFont = fun;
bd->LoadCustomFontUserData = userdata;
}
IMGUI_IMPL_API void* ImGui_ImplAx_GetFontsTexture()
{
auto bd = ImGui_ImplAndroid_GetBackendData();
return bd->FontTexture;
}
IMGUI_IMPL_API void ImGui_ImplAx_SetDeviceObjectsDirty()
{
auto bd = ImGui_ImplAndroid_GetBackendData();
bd->FontDeviceObjectsDirty = true;
}
IMGUI_IMPL_API void ImGui_ImplAx_SetViewResolution(float width, float height)
{
auto bd = ImGui_ImplAndroid_GetBackendData();
bd->ViewResolution = Vec2(width, height);
}
static bool ImGui_ImplAx_CreateDeviceObjects()
{
auto bd = ImGui_ImplAndroid_GetBackendData();
if (bd->LoadCustomFont)
bd->LoadCustomFont(bd->LoadCustomFontUserData);
ImGui_ImplAx_createShaderPrograms();
ImGui_ImplAx_CreateFontsTexture();
bd->FontDeviceObjectsDirty = false;
return true;
}
static void ImGui_ImplAx_DestroyDeviceObjects()
{
auto bd = ImGui_ImplAndroid_GetBackendData();
AX_SAFE_RELEASE_NULL(bd->ProgramInfo.program);
AX_SAFE_RELEASE_NULL(bd->ProgramFontInfo.program);
AX_SAFE_RELEASE_NULL(bd->TouchListener);
ImGui_ImplAx_DestroyFontsTexture();
}
static bool ImGui_ImplAx_createShaderPrograms()
{
auto vertex_shader =
"uniform mat4 u_MVPMatrix;\n"
"attribute vec2 a_position;\n"
"attribute vec2 a_texCoord;\n"
"attribute vec4 a_color;\n"
"varying vec2 v_texCoord;\n"
"varying vec4 v_fragmentColor;\n"
"void main()\n"
"{\n"
" v_texCoord = a_texCoord;\n"
" v_fragmentColor = a_color;\n"
" gl_Position = u_MVPMatrix * vec4(a_position.xy, 0.0, 1.0);\n"
"}\n";
auto fragment_shader =
"#ifdef GL_ES\n"
" precision mediump float;\n"
"#endif\n"
"uniform sampler2D u_tex0;\n"
"varying vec2 v_texCoord;\n"
"varying vec4 v_fragmentColor;\n"
"void main()\n"
"{\n"
" gl_FragColor = v_fragmentColor * texture2D(u_tex0, v_texCoord);\n"
"}\n";
auto fragment_shader_font =
"#ifdef GL_ES\n"
" precision mediump float;\n"
"#endif\n"
"uniform sampler2D u_tex0;\n"
"varying vec2 v_texCoord;\n"
"varying vec4 v_fragmentColor;\n"
"void main()\n"
"{\n"
" float a = texture2D(u_tex0, v_texCoord).a;\n"
" gl_FragColor = vec4(v_fragmentColor.rgb, v_fragmentColor.a * a);\n"
"}\n";
auto bd = ImGui_ImplAndroid_GetBackendData();
AX_SAFE_RELEASE(bd->ProgramInfo.program);
AX_SAFE_RELEASE(bd->ProgramFontInfo.program);
bd->ProgramInfo.program = backend::Device::getInstance()->newProgram(vertex_shader, fragment_shader);
bd->ProgramFontInfo.program = backend::Device::getInstance()->newProgram(vertex_shader, fragment_shader_font);
IM_ASSERT(bd->ProgramInfo.program);
IM_ASSERT(bd->ProgramFontInfo.program);
if (!bd->ProgramInfo.program || !bd->ProgramFontInfo.program)
return false;
for (auto& p : {&bd->ProgramInfo, &bd->ProgramFontInfo})
{
p->texture = p->program->getUniformLocation(TEXTURE);
p->projection = p->program->getUniformLocation(MVP_MATRIX);
p->position = p->program->getAttributeLocation(POSITION);
p->uv = p->program->getAttributeLocation(TEXCOORD);
p->color = p->program->getAttributeLocation(COLOR);
IM_ASSERT(bool(p->texture));
IM_ASSERT(bool(p->projection));
IM_ASSERT(p->position >= 0);
IM_ASSERT(p->uv >= 0);
IM_ASSERT(p->color >= 0);
auto& layout = p->layout;
layout.setAttribute("a_position", p->position, VertexFormat::FLOAT2, 0, false);
layout.setAttribute("a_texCoord", p->uv, VertexFormat::FLOAT2, offsetof(ImDrawVert, uv), false);
layout.setAttribute("a_color", p->color, VertexFormat::UBYTE4, offsetof(ImDrawVert, col), true);
layout.setStride(sizeof(ImDrawVert));
}
return true;
}
bool ImGui_ImplAx_CreateFontsTexture()
{
auto bd = ImGui_ImplAndroid_GetBackendData();
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
// Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small)
// because it is more likely to be compatible with user's existing shaders.
// If your ImTextureId represent a higher-level concept than just a GL texture id,
// consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
io.Fonts->GetTexDataAsAlpha8(&pixels, &width, &height);
AX_SAFE_RELEASE(bd->FontTexture);
bd->FontTexture = new Texture2D();
bd->FontTexture->setAntiAliasTexParameters();
bd->FontTexture->initWithData(pixels, width * height, backend::PixelFormat::A8, width, height);
io.Fonts->TexID = (ImTextureID)bd->FontTexture;
return true;
}
IMGUI_IMPL_API void ImGui_ImplAx_DestroyFontsTexture()
{
auto bd = ImGui_ImplAndroid_GetBackendData();
if (bd->FontTexture)
{
ImGui::GetIO().Fonts->TexID = nullptr;
AX_SAFE_RELEASE_NULL(bd->FontTexture);
}
}
static void AddRendererCommand(const std::function<void()>& f)
{
auto bd = ImGui_ImplAndroid_GetBackendData();
const auto renderer = Director::getInstance()->getRenderer();
auto cmd = renderer->nextCallbackCommand();
cmd->init(0.f);
cmd->func = f;
renderer->addCommand(cmd);
//bd->CallbackCommands.push_back(cmd);
}
static void ImGui_ImplAx_SaveRenderState(ax::Renderer* renderer)
{
AddRendererCommand([renderer]() {
auto bd = ImGui_ImplAndroid_GetBackendData();
bd->SavedRenderState.cull = renderer->getCullMode();
bd->SavedRenderState.vp = renderer->getViewport();
bd->SavedRenderState.scissorTest = renderer->getScissorTest();
bd->SavedRenderState.scissorRect = renderer->getScissorRect();
bd->SavedRenderState.depthTest = renderer->getDepthTest();
});
}
static void ImGui_ImplAx_SetupRenderState(ax::Renderer* renderer,
ImDrawData* draw_data,
int fb_width,
int fb_height)
{
AddRendererCommand([=]() {
renderer->setCullMode(backend::CullMode::NONE);
renderer->setDepthTest(false);
renderer->setScissorTest(true);
renderer->setViewPort(0, 0, fb_width, fb_height);
});
const auto L = draw_data->DisplayPos.x;
const auto R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
const auto T = draw_data->DisplayPos.y;
const auto B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
auto bd = ImGui_ImplAndroid_GetBackendData();
Mat4::createOrthographicOffCenter(L, R, B, T, -1.f, 1.f, &bd->Projection);
}
static void ImGui_ImplAx_RestoreRenderState(ax::Renderer* renderer)
{
AddRendererCommand([renderer]() {
auto bd = ImGui_ImplAndroid_GetBackendData();
renderer->setCullMode(bd->SavedRenderState.cull);
auto& vp = bd->SavedRenderState.vp;
renderer->setViewPort(vp.x, vp.y, vp.w, vp.h);
renderer->setScissorTest(bd->SavedRenderState.scissorTest);
auto& sc = bd->SavedRenderState.scissorRect;
renderer->setScissorRect(sc.x, sc.y, sc.width, sc.height);
renderer->setDepthTest(bd->SavedRenderState.depthTest);
// apply raster state
renderer->beginRenderPass();
renderer->endRenderPass();
});
}
IMGUI_IMPL_API void ImGui_ImplAx_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized, scale coordinates for retina displays
// (screen coordinates != framebuffer coordinates)
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
if (fb_width <= 0 || fb_height <= 0)
return;
const auto renderer = Director::getInstance()->getRenderer();
ImGui_ImplAx_SaveRenderState(renderer);
ImGui_ImplAx_SetupRenderState(renderer, draw_data, fb_width, fb_height);
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
// Render command lists
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
size_t ibuffer_offset = 0;
// Upload vertex/index buffers
const auto vsize = cmd_list->VtxBuffer.Size * sizeof(ImDrawVert);
IM_ASSERT(vsize > 0);
auto vbuffer = backend::Device::getInstance()->newBuffer(vsize, BufferType::VERTEX, BufferUsage::STATIC);
vbuffer->autorelease();
vbuffer->updateData(cmd_list->VtxBuffer.Data, vsize);
const auto isize = cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx);
IM_ASSERT(isize > 0);
auto ibuffer = backend::Device::getInstance()->newBuffer(isize, BufferType::INDEX, BufferUsage::STATIC);
ibuffer->autorelease();
ibuffer->updateData(cmd_list->IdxBuffer.Data, isize);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != nullptr)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user
// to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplAx_SetupRenderState(renderer, draw_data, fb_width, fb_height);
else
{
AddRendererCommand([=]() { pcmd->UserCallback(cmd_list, pcmd); });
}
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec4 clip_rect;
clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x;
clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y;
clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x;
clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y;
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
{
// Apply scissor/clipping rectangle
AddRendererCommand([=]() {
renderer->setScissorRect(clip_rect.x, fb_height - clip_rect.w, clip_rect.z - clip_rect.x,
clip_rect.w - clip_rect.y);
});
auto bd = ImGui_ImplAndroid_GetBackendData();
if (typeid(*((Ref*)pcmd->TextureId)) == typeid(Texture2D))
{
auto tex = AX_PTR_CAST(pcmd->TextureId, Texture2D*);
auto cmd = std::make_shared<CustomCommand>();
bd->CustomCommands.push_back(cmd);
cmd->init(0.f, BlendFunc::ALPHA_NON_PREMULTIPLIED);
const auto pinfo = tex == bd->FontTexture ? &bd->ProgramFontInfo : &bd->ProgramInfo;
// create new ProgramState
auto state = new ProgramState(pinfo->program);
state->autorelease();
bd->ProgramStates.pushBack(state);
auto& desc = cmd->getPipelineDescriptor();
desc.programState = state;
// setup attributes for ImDrawVert
desc.programState->setVertexLayout(pinfo->layout);
desc.programState->setUniform(pinfo->projection, &bd->Projection, sizeof(Mat4));
desc.programState->setTexture(pinfo->texture, 0, tex->getBackendTexture());
// In order to composite our output buffer we need to preserve alpha
desc.blendDescriptor.sourceAlphaBlendFactor = BlendFactor::ONE;
// set vertex/index buffer
cmd->setIndexBuffer(ibuffer, IMGUI_INDEX_FORMAT);
cmd->setVertexBuffer(vbuffer);
cmd->setDrawType(CustomCommand::DrawType::ELEMENT);
cmd->setPrimitiveType(PrimitiveType::TRIANGLE);
cmd->setIndexDrawInfo(ibuffer_offset, pcmd->ElemCount);
renderer->addCommand(cmd.get());
}
else
{
auto node = AX_PTR_CAST(pcmd->TextureId, Node*);
const auto tr = node->getNodeToParentTransform();
node->setVisible(true);
node->setNodeToParentTransform(tr);
const auto& proj =
Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
node->visit(Director::getInstance()->getRenderer(), proj.getInversed() * bd->Projection, 0);
node->setVisible(false);
}
}
}
ibuffer_offset += pcmd->ElemCount;
}
}
ImGui_ImplAx_RestoreRenderState(renderer);
}
//--------------------------------------------------------------------------------------------------------
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports
// simultaneously. If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you
// completely ignore this section first..
//--------------------------------------------------------------------------------------------------------
static void ImGui_ImplAx_Renderer_RenderWindow(ImGuiViewport* viewport, void*)
{
if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear))
{
const auto renderer = Director::getInstance()->getRenderer();
renderer->clear(ClearFlag::COLOR, {0, 0, 0, 1}, 1, 0, 0);
}
ImGui_ImplAx_RenderDrawData(viewport->DrawData);
}

View File

@ -0,0 +1,30 @@
#pragma once
#include "imgui.h"
#include "platform/CCGLView.h"
struct ANativeWindow;
struct AInputEvent;
typedef void (*ImGuiImplCocos2dxLoadFontFun)(void* userdata);
/// ImGui glfw APIs
IMGUI_IMPL_API bool ImGui_ImplAndroid_InitForAx(ax::GLView* window, bool install_callbacks);
IMGUI_IMPL_API void ImGui_ImplAndroid_Shutdown();
IMGUI_IMPL_API void ImGui_ImplAndroid_NewFrame();
/// ImGui axmol render APIs
IMGUI_IMPL_API void ImGui_ImplAx_Init();
IMGUI_IMPL_API void ImGui_ImplAx_Shutdown();
IMGUI_IMPL_API void ImGui_ImplAx_NewFrame();
IMGUI_IMPL_API void ImGui_ImplAx_RenderDrawData(ImDrawData* draw_data);
IMGUI_IMPL_API void ImGui_ImplAx_RenderPlatform();
// Get FontTexture object ax::Texture2D*
IMGUI_IMPL_API void ImGui_ImplAx_SetCustomFontLoader(ImGuiImplCocos2dxLoadFontFun fun, void* userdata);
IMGUI_IMPL_API void* ImGui_ImplAx_GetFontsTexture();
// Sets Device objects dirty
IMGUI_IMPL_API void ImGui_ImplAx_SetDeviceObjectsDirty();
// Set the required view resolution for the UI
IMGUI_IMPL_API void ImGui_ImplAx_SetViewResolution(float width, float height);

View File

@ -6,7 +6,7 @@
USING_NS_AX; USING_NS_AX;
USING_NS_AX_EXT; USING_NS_AX_EXT;
#if defined(AX_PLATFORM_PC) #if defined(AX_PLATFORM_PC) || (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID)
static bool show_test_window = true; static bool show_test_window = true;
static bool show_another_window = true; static bool show_another_window = true;
@ -14,11 +14,7 @@ static ImVec4 clear_color = ImColor(114, 144, 154);
ImGuiTests::ImGuiTests() ImGuiTests::ImGuiTests()
{ {
// Resize (expand) window ImGuiPresenter::getInstance()->setViewResolution(1280, 720);
static Size resourceSize(1280, 720);
auto director = Director::getInstance();
GLViewImpl* view = (GLViewImpl*)Director::getInstance()->getOpenGLView();
view->setWindowed(resourceSize.width, resourceSize.height);
ADD_TEST_CASE(ImGuiTest); ADD_TEST_CASE(ImGuiTest);
} }

View File

@ -30,7 +30,7 @@
#include "cocos2d.h" #include "cocos2d.h"
#include "../BaseTest.h" #include "../BaseTest.h"
#if defined(AX_PLATFORM_PC) #if defined(AX_PLATFORM_PC) || (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID)
DEFINE_TEST_SUITE(ImGuiTests); DEFINE_TEST_SUITE(ImGuiTests);

View File

@ -44,7 +44,7 @@ public:
RootTests() RootTests()
{ {
// addTest("Node: Scene3D", [](){return new Scene3DTests(); }); // addTest("Node: Scene3D", [](){return new Scene3DTests(); });
#if defined(AX_PLATFORM_PC) #if defined(AX_PLATFORM_PC) || (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID)
addTest("ImGui", []() { return new ImGuiTests(); }); addTest("ImGui", []() { return new ImGuiTests(); });
#endif #endif
addTest("Texture2D", []() { return new Texture2DTests(); }); addTest("Texture2D", []() { return new Texture2DTests(); });

View File

@ -119,7 +119,7 @@
#include "ZwoptexTest/ZwoptexTest.h" #include "ZwoptexTest/ZwoptexTest.h"
#include "SpriteFrameCacheTest/SpriteFrameCacheTest.h" #include "SpriteFrameCacheTest/SpriteFrameCacheTest.h"
#include "ZipTest/ZipTests.h" #include "ZipTest/ZipTests.h"
#if defined(AX_PLATFORM_PC) #if defined(AX_PLATFORM_PC) || (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID)
# include "ImGuiTest/ImGuiTest.h" # include "ImGuiTest/ImGuiTest.h"
#endif #endif
#endif #endif