34985b7177
* Add imgui sdl2 adapter * Added keyboard input to IMGUI example Co-authored-by: Amer Koleci <amerkoleci@gmail.com>
437 lines
13 KiB
C++
437 lines
13 KiB
C++
#include "stdafx.h"
|
|
#include "Example_ImGui.h"
|
|
|
|
#include "ImGui/imgui.h"
|
|
#include "ImGui/imgui_internal.h"
|
|
|
|
#ifdef _WIN32
|
|
#include "ImGui/imgui_impl_win32.h"
|
|
#elif defined(SDL2)
|
|
#include "ImGui/imgui_impl_sdl.h"
|
|
#endif
|
|
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <fstream>
|
|
#include <thread>
|
|
|
|
using namespace wiECS;
|
|
using namespace wiGraphics;
|
|
using namespace wiScene;
|
|
|
|
Shader imguiVS;
|
|
Shader imguiPS;
|
|
Texture fontTexture;
|
|
InputLayout imguiInputLayout;
|
|
PipelineState imguiPSO;
|
|
|
|
struct ImGui_Impl_Data
|
|
{
|
|
};
|
|
|
|
static ImGui_Impl_Data* ImGui_Impl_GetBackendData()
|
|
{
|
|
return ImGui::GetCurrentContext() ? (ImGui_Impl_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
|
|
}
|
|
|
|
bool ImGui_Impl_CreateDeviceObjects()
|
|
{
|
|
auto* backendData = ImGui_Impl_GetBackendData();
|
|
|
|
// Build texture atlas
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
unsigned char* pixels;
|
|
int width, height;
|
|
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
|
|
|
// Upload texture to graphics system
|
|
TextureDesc textureDesc;
|
|
textureDesc.Width = width;
|
|
textureDesc.Height = height;
|
|
textureDesc.MipLevels = 1;
|
|
textureDesc.ArraySize = 1;
|
|
textureDesc.Format = FORMAT_R8G8B8A8_UNORM;
|
|
textureDesc.BindFlags = BIND_SHADER_RESOURCE;
|
|
|
|
SubresourceData textureData;
|
|
textureData.pData = pixels;
|
|
textureData.rowPitch = width * GetFormatStride(textureDesc.Format);
|
|
textureData.slicePitch = textureData.rowPitch * height;
|
|
|
|
wiRenderer::GetDevice()->CreateTexture(&textureDesc, &textureData, &fontTexture);
|
|
|
|
// Store our identifier
|
|
io.Fonts->SetTexID((ImTextureID)&fontTexture);
|
|
|
|
imguiInputLayout.elements =
|
|
{
|
|
{ "POSITION", 0, FORMAT_R32G32_FLOAT, 0, (uint32_t)IM_OFFSETOF(ImDrawVert, pos), INPUT_PER_VERTEX_DATA },
|
|
{ "TEXCOORD", 0, FORMAT_R32G32_FLOAT, 0, (uint32_t)IM_OFFSETOF(ImDrawVert, uv), INPUT_PER_VERTEX_DATA },
|
|
{ "COLOR", 0, FORMAT_R8G8B8A8_UNORM, 0, (uint32_t)IM_OFFSETOF(ImDrawVert, col), INPUT_PER_VERTEX_DATA },
|
|
};
|
|
|
|
// Create pipeline
|
|
PipelineStateDesc desc;
|
|
desc.vs = &imguiVS;
|
|
desc.ps = &imguiPS;
|
|
desc.il = &imguiInputLayout;
|
|
desc.dss = wiRenderer::GetDepthStencilState(DSSTYPE_DEPTHREAD);
|
|
desc.rs = wiRenderer::GetRasterizerState(RSTYPE_DOUBLESIDED);
|
|
desc.bs = wiRenderer::GetBlendState(BSTYPE_TRANSPARENT);
|
|
desc.pt = TRIANGLELIST;
|
|
wiRenderer::GetDevice()->CreatePipelineState(&desc, &imguiPSO);
|
|
|
|
return true;
|
|
}
|
|
|
|
Example_ImGui::~Example_ImGui()
|
|
{
|
|
// Cleanup
|
|
//ImGui_ImplDX11_Shutdown();
|
|
#ifdef _WIN32
|
|
ImGui_ImplWin32_Shutdown();
|
|
#elif defined(SDL2)
|
|
ImGui_ImplSDL2_Shutdown();
|
|
#endif
|
|
ImGui::DestroyContext();
|
|
}
|
|
|
|
void Example_ImGui::Initialize()
|
|
{
|
|
// Compile shaders
|
|
{
|
|
wiShaderCompiler::Initialize();
|
|
|
|
auto shaderPath = wiRenderer::GetShaderSourcePath();
|
|
wiRenderer::SetShaderSourcePath(wiHelper::GetCurrentPath() + "/");
|
|
|
|
wiRenderer::LoadShader(VS, imguiVS, "ImGuiVS.cso");
|
|
wiRenderer::LoadShader(PS, imguiPS, "ImGuiPS.cso");
|
|
|
|
wiRenderer::SetShaderSourcePath(shaderPath);
|
|
}
|
|
|
|
// Setup Dear ImGui context
|
|
IMGUI_CHECKVERSION();
|
|
ImGui::CreateContext();
|
|
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
|
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
|
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
|
|
|
// Setup Dear ImGui style
|
|
ImGui::StyleColorsDark();
|
|
//ImGui::StyleColorsClassic();
|
|
|
|
#ifdef _WIN32
|
|
ImGui_ImplWin32_Init(window);
|
|
#elif defined(SDL2)
|
|
ImGui_ImplSDL2_InitForVulkan(window);
|
|
#endif
|
|
|
|
IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!");
|
|
|
|
// Setup backend capabilities flags
|
|
ImGui_Impl_Data* bd = IM_NEW(ImGui_Impl_Data)();
|
|
io.BackendRendererUserData = (void*)bd;
|
|
io.BackendRendererName = "Wicked";
|
|
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
|
|
|
MainComponent::Initialize();
|
|
|
|
infoDisplay.active = true;
|
|
infoDisplay.watermark = true;
|
|
infoDisplay.fpsinfo = true;
|
|
infoDisplay.resolution = true;
|
|
infoDisplay.heap_allocation_counter = true;
|
|
|
|
renderer.init(canvas);
|
|
renderer.Load();
|
|
|
|
ActivatePath(&renderer);
|
|
}
|
|
|
|
void Example_ImGui::Compose(wiGraphics::CommandList cmd)
|
|
{
|
|
MainComponent::Compose(cmd);
|
|
|
|
// Rendering
|
|
ImGui::Render();
|
|
|
|
auto drawData = ImGui::GetDrawData();
|
|
|
|
if (!drawData || drawData->TotalVtxCount == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
|
|
int fb_width = (int)(drawData->DisplaySize.x * drawData->FramebufferScale.x);
|
|
int fb_height = (int)(drawData->DisplaySize.y * drawData->FramebufferScale.y);
|
|
if (fb_width <= 0 || fb_height <= 0)
|
|
return;
|
|
|
|
auto* bd = ImGui_Impl_GetBackendData();
|
|
|
|
GraphicsDevice* device = wiRenderer::GetDevice();
|
|
|
|
// Get memory for vertex and index buffers
|
|
const uint64_t vbSize = sizeof(ImDrawVert) * drawData->TotalVtxCount;
|
|
const uint64_t ibSize = sizeof(ImDrawIdx) * drawData->TotalIdxCount;
|
|
auto vertexBufferAllocation = device->AllocateGPU(vbSize, cmd);
|
|
auto indexBufferAllocation = device->AllocateGPU(ibSize, cmd);
|
|
|
|
// Copy and convert all vertices into a single contiguous buffer
|
|
ImDrawVert* vertexCPUMem = reinterpret_cast<ImDrawVert*>(vertexBufferAllocation.data);
|
|
ImDrawIdx* indexCPUMem = reinterpret_cast<ImDrawIdx*>(indexBufferAllocation.data);
|
|
for (int cmdListIdx = 0; cmdListIdx < drawData->CmdListsCount; cmdListIdx++)
|
|
{
|
|
const ImDrawList* drawList = drawData->CmdLists[cmdListIdx];
|
|
memcpy(vertexCPUMem, &drawList->VtxBuffer[0], drawList->VtxBuffer.Size * sizeof(ImDrawVert));
|
|
memcpy(indexCPUMem, &drawList->IdxBuffer[0], drawList->IdxBuffer.Size * sizeof(ImDrawIdx));
|
|
vertexCPUMem += drawList->VtxBuffer.Size;
|
|
indexCPUMem += drawList->IdxBuffer.Size;
|
|
}
|
|
|
|
// Setup orthographic projection matrix into our constant buffer
|
|
struct ImGuiConstants
|
|
{
|
|
float mvp[4][4];
|
|
};
|
|
|
|
{
|
|
const float L = drawData->DisplayPos.x;
|
|
const float R = drawData->DisplayPos.x + drawData->DisplaySize.x;
|
|
const float T = drawData->DisplayPos.y;
|
|
const float B = drawData->DisplayPos.y + drawData->DisplaySize.y;
|
|
|
|
//Matrix4x4::CreateOrthographicOffCenter(0.0f, drawData->DisplaySize.x, drawData->DisplaySize.y, 0.0f, 0.0f, 1.0f, &constants.projectionMatrix);
|
|
|
|
ImGuiConstants constants;
|
|
|
|
float mvp[4][4] =
|
|
{
|
|
{ 2.0f / (R - L), 0.0f, 0.0f, 0.0f },
|
|
{ 0.0f, 2.0f / (T - B), 0.0f, 0.0f },
|
|
{ 0.0f, 0.0f, 0.5f, 0.0f },
|
|
{ (R + L) / (L - R), (T + B) / (B - T), 0.5f, 1.0f },
|
|
};
|
|
memcpy(&constants.mvp, mvp, sizeof(mvp));
|
|
|
|
device->BindDynamicConstantBuffer(constants, 0, cmd);
|
|
}
|
|
|
|
const GPUBuffer* vbs[] = {
|
|
&vertexBufferAllocation.buffer,
|
|
};
|
|
const uint32_t strides[] = {
|
|
sizeof(ImDrawVert),
|
|
};
|
|
const uint64_t offsets[] = {
|
|
vertexBufferAllocation.offset,
|
|
};
|
|
|
|
device->BindVertexBuffers(vbs, 0, 1, strides, offsets, cmd);
|
|
device->BindIndexBuffer(&indexBufferAllocation.buffer, INDEXFORMAT_16BIT, indexBufferAllocation.offset, cmd);
|
|
|
|
Viewport viewport;
|
|
viewport.Width = (float)fb_width;
|
|
viewport.Height = (float)fb_height;
|
|
device->BindViewports(1, &viewport, cmd);
|
|
|
|
device->BindPipelineState(&imguiPSO, cmd);
|
|
|
|
// Will project scissor/clipping rectangles into framebuffer space
|
|
ImVec2 clip_off = drawData->DisplayPos; // (0,0) unless using multi-viewports
|
|
ImVec2 clip_scale = drawData->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
|
|
|
|
//passEncoder->SetSampler(0, Sampler::LinearWrap());
|
|
|
|
// Render command lists
|
|
int32_t vertexOffset = 0;
|
|
uint32_t indexOffset = 0;
|
|
for (uint32_t cmdListIdx = 0; cmdListIdx < (uint32_t)drawData->CmdListsCount; ++cmdListIdx)
|
|
{
|
|
const ImDrawList* drawList = drawData->CmdLists[cmdListIdx];
|
|
for (uint32_t cmdIndex = 0; cmdIndex < (uint32_t)drawList->CmdBuffer.size(); ++cmdIndex)
|
|
{
|
|
const ImDrawCmd* drawCmd = &drawList->CmdBuffer[cmdIndex];
|
|
if (drawCmd->UserCallback)
|
|
{
|
|
// 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 (drawCmd->UserCallback == ImDrawCallback_ResetRenderState)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
drawCmd->UserCallback(drawList, drawCmd);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Project scissor/clipping rectangles into framebuffer space
|
|
ImVec2 clip_min(drawCmd->ClipRect.x - clip_off.x, drawCmd->ClipRect.y - clip_off.y);
|
|
ImVec2 clip_max(drawCmd->ClipRect.z - clip_off.x, drawCmd->ClipRect.w - clip_off.y);
|
|
if (clip_max.x < clip_min.x || clip_max.y < clip_min.y)
|
|
continue;
|
|
|
|
// Apply scissor/clipping rectangle
|
|
Rect scissor;
|
|
scissor.left = (int32_t)(clip_min.x);
|
|
scissor.top = (int32_t)(clip_min.y);
|
|
scissor.right = (int32_t)(clip_max.x);
|
|
scissor.bottom = (int32_t)(clip_max.y);
|
|
device->BindScissorRects(1, &scissor, cmd);
|
|
|
|
const Texture* texture = (const Texture*)drawCmd->TextureId;
|
|
device->BindResource(texture, 0, cmd);
|
|
device->DrawIndexed(drawCmd->ElemCount, indexOffset, vertexOffset, cmd);
|
|
}
|
|
indexOffset += drawCmd->ElemCount;
|
|
}
|
|
vertexOffset += drawList->VtxBuffer.size();
|
|
}
|
|
|
|
//// Update and Render additional Platform Windows
|
|
//if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
|
//{
|
|
// ImGui::UpdatePlatformWindows();
|
|
// //ImGui::RenderPlatformWindowsDefault(NULL, (void*)g_pd3dCommandList);
|
|
//}
|
|
}
|
|
|
|
void Example_ImGuiRenderer::ResizeLayout()
|
|
{
|
|
RenderPath3D::ResizeLayout();
|
|
|
|
float screenW = GetLogicalWidth();
|
|
float screenH = GetLogicalHeight();
|
|
label.SetPos(XMFLOAT2(screenW / 2.f - label.scale.x / 2.f, screenH * 0.95f));
|
|
}
|
|
|
|
void Example_ImGuiRenderer::Render() const
|
|
{
|
|
RenderPath3D::Render();
|
|
}
|
|
|
|
void Example_ImGuiRenderer::Load()
|
|
{
|
|
setSSREnabled(false);
|
|
setReflectionsEnabled(true);
|
|
setFXAAEnabled(false);
|
|
|
|
label.Create("Label1");
|
|
label.SetText("Wicked Engine ImGui integration");
|
|
label.font.params.h_align = WIFALIGN_CENTER;
|
|
label.SetSize(XMFLOAT2(240, 20));
|
|
GetGUI().AddWidget(&label);
|
|
|
|
// Reset all state that tests might have modified:
|
|
wiEvent::SetVSync(true);
|
|
wiRenderer::SetToDrawGridHelper(false);
|
|
wiRenderer::SetTemporalAAEnabled(true);
|
|
wiRenderer::ClearWorld(wiScene::GetScene());
|
|
wiScene::GetScene().weather = WeatherComponent();
|
|
this->ClearSprites();
|
|
this->ClearFonts();
|
|
if (wiLua::GetLuaState() != nullptr) {
|
|
wiLua::KillProcesses();
|
|
}
|
|
|
|
// Reset camera position:
|
|
TransformComponent transform;
|
|
transform.Translate(XMFLOAT3(0, 2.f, -4.5f));
|
|
transform.UpdateTransform();
|
|
wiScene::GetCamera().TransformCamera(transform);
|
|
|
|
// Load model.
|
|
wiScene::LoadModel("../Content/models/teapot.wiscene");
|
|
|
|
RenderPath3D::Load();
|
|
}
|
|
|
|
bool show_demo_window = true;
|
|
bool show_another_window = false;
|
|
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
|
|
|
void Example_ImGuiRenderer::Update(float dt)
|
|
{
|
|
// Start the Dear ImGui frame
|
|
auto* backendData = ImGui_Impl_GetBackendData();
|
|
IM_ASSERT(backendData != NULL);
|
|
|
|
if (!fontTexture.IsValid())
|
|
{
|
|
ImGui_Impl_CreateDeviceObjects();
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
ImGui_ImplWin32_NewFrame();
|
|
#elif defined(SDL2)
|
|
ImGui_ImplSDL2_NewFrame();
|
|
#endif
|
|
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!).
|
|
if (show_demo_window)
|
|
ImGui::ShowDemoWindow(&show_demo_window);
|
|
|
|
// 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window.
|
|
{
|
|
static float f = 0.0f;
|
|
static int counter = 0;
|
|
|
|
ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
|
|
|
|
ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
|
|
ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state
|
|
ImGui::Checkbox("Another Window", &show_another_window);
|
|
|
|
ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
|
|
ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
|
|
|
|
if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
|
|
counter++;
|
|
ImGui::SameLine();
|
|
ImGui::Text("counter = %d", counter);
|
|
|
|
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
|
|
ImGui::End();
|
|
}
|
|
|
|
// 3. Show another simple window.
|
|
if (show_another_window)
|
|
{
|
|
ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
|
|
ImGui::Text("Hello from another window!");
|
|
if (ImGui::Button("Close Me"))
|
|
show_another_window = false;
|
|
ImGui::End();
|
|
}
|
|
|
|
Scene& scene = wiScene::GetScene();
|
|
// teapot_material Base Base_mesh Top Top_mesh editorLight
|
|
wiECS::Entity e_teapot_base = scene.Entity_FindByName("Base");
|
|
wiECS::Entity e_teapot_top = scene.Entity_FindByName("Top");
|
|
assert(e_teapot_base != wiECS::INVALID_ENTITY);
|
|
assert(e_teapot_top != wiECS::INVALID_ENTITY);
|
|
TransformComponent* transform_base = scene.transforms.GetComponent(e_teapot_base);
|
|
TransformComponent* transform_top = scene.transforms.GetComponent(e_teapot_top);
|
|
assert(transform_base != nullptr);
|
|
assert(transform_top != nullptr);
|
|
float rotation = dt;
|
|
if (wiInput::Down(wiInput::KEYBOARD_BUTTON_LEFT))
|
|
{
|
|
transform_base->Rotate(XMVectorSet(0, rotation, 0, 1));
|
|
transform_top->Rotate(XMVectorSet(0, rotation, 0, 1));
|
|
}
|
|
else if (wiInput::Down(wiInput::KEYBOARD_BUTTON_RIGHT))
|
|
{
|
|
transform_base->Rotate(XMVectorSet(0, -rotation, 0, 1));
|
|
transform_top->Rotate(XMVectorSet(0, -rotation, 0, 1));
|
|
}
|
|
|
|
RenderPath3D::Update(dt);
|
|
}
|