ocornut / imgui

Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
MIT License
61.37k stars 10.33k forks source link

How can I render whole DX11 D3D scene inside ImGui window? #8148

Closed Duy-Thanh closed 1 week ago

Duy-Thanh commented 1 week ago

Version/Branch of Dear ImGui:

Version 1.91.5, Branch: docking

Back-ends:

imgui_impl_Win32.cpp + imgui_impl_dx11.cpp

Compiler, OS:

Windows 11 + MSVC 2022 17.11.5 + CMake

Full config/build information:

image

Dear ImGui 1.91.5 (19150)
--------------------------------
sizeof(size_t): 8, sizeof(ImDrawIdx): 2, sizeof(ImDrawVert): 20
define: __cplusplus=199711
define: _WIN32
define: _WIN64
define: _MSC_VER=1941
define: _MSVC_LANG=201703
define: IMGUI_HAS_VIEWPORT
define: IMGUI_HAS_DOCK
--------------------------------
io.BackendPlatformName: imgui_impl_win32
io.BackendRendererName: imgui_impl_dx11
io.ConfigFlags: 0x00000081
 NavEnableKeyboard
 DockingEnable
io.ConfigViewportsNoDecoration
io.ConfigNavCaptureKeyboard
io.ConfigInputTextCursorBlink
io.ConfigWindowsResizeFromEdges
io.ConfigMemoryCompactTimer = 60.0
io.BackendFlags: 0x00001C0E
 HasMouseCursors
 HasSetMousePos
 PlatformHasViewports
 HasMouseHoveredViewport
 RendererHasVtxOffset
 RendererHasViewports
--------------------------------
io.Fonts: 1 fonts, Flags: 0x00000000, TexSize: 512,64
io.DisplaySize: 1920.00,1032.00
io.DisplayFramebufferScale: 1.00,1.00
--------------------------------
style.WindowPadding: 8.00,8.00
style.WindowBorderSize: 1.00
style.FramePadding: 4.00,3.00
style.FrameRounding: 12.00
style.FrameBorderSize: 0.00
style.ItemSpacing: 8.00,4.00
style.ItemInnerSpacing: 4.00,4.00

Details:

Question

I want to make my application can render the 3D object inside Viewport window, renderer using here is DirectX 11 D3D

But in some weird case, I can't make it work.....

I uploaded the entire project to GitHub, but I don't know if it's possible to link it here

If you need it, let me know!

Screenshots/Video:

image

Minimal, Complete and Verifiable Example code:

Here is my Initialize code:

void EditorUI::Initialize(Scene* scene, D3DRenderer* renderer) {
    m_scene = scene;
    m_renderer = renderer;

    Debug::Log("EditorUI::Initialize called");

    if (!renderer || !scene) {
        Debug::LogError("EditorUI constructor: renderer or scene is null!");
        return;
    }

    // Initialize gizmos
    m_gizmos = std::make_unique<Gizmos>();
    m_gizmos->Initialize(renderer->GetDevice());

    // Setup ImGui context
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO(); (void)io;
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
    io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;

    // Setup ImGui style
    ImGui::StyleColorsDark();
    ImGuiStyle& style = ImGui::GetStyle();
    style.WindowRounding = 8.0f;
    style.FrameRounding = 12.0f;
    style.Colors[ImGuiCol_WindowBg] = ImVec4(0.15f, 0.15f, 0.15f, 1.0f);
    style.Colors[ImGuiCol_Header] = ImVec4(0.2f, 0.2f, 0.2f, 1.0f);
    style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.3f, 0.3f, 0.3f, 1.0f);
    style.Colors[ImGuiCol_HeaderActive] = ImVec4(0.25f, 0.25f, 0.25f, 1.0f);

    // Setup Platform/Renderer backends
    if (ImGui_ImplWin32_Init(GetActiveWindow())) {
        Debug::Log("ImGui Win32 implementation initialized");

        if (ImGui_ImplDX11_Init(m_renderer->GetDevice(), m_renderer->GetContext())) {
            Debug::Log("ImGui DX11 implementation initialized");
        } else {
            Debug::LogError("Failed to initialize ImGui DX11 implementation");
        }
    } else {
        Debug::LogError("Failed to initialize ImGui Win32 implementation");
    }

    Debug::Log("EditorUI initialized successfully");
}

void EditorUI::Render() {
    // Debug::Log("EditorUI::Update called");

    // Start the Dear ImGui frame
    ImGui_ImplDX11_NewFrame();
    ImGui_ImplWin32_NewFrame();
    ImGui::NewFrame();

    // Setup dockspace
    SetupDockspace();

    // Render UI components
    RenderMainMenuBar();
    RenderSceneHierarchy();
    RenderPropertiesPanel();
    RenderViewport();

    // Show ImGui demo window (Keep that)
    if (m_showDemoWindow)
        ImGui::ShowDemoWindow(&m_showDemoWindow);

    // Render ImGui
    ImGui::Render();
    ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
}

void EditorUI::SetupDockspace() {
    static bool opt_fullscreen = true;
    static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None;

    ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
    if (opt_fullscreen) {
        const ImGuiViewport* viewport = ImGui::GetMainViewport();
        ImGui::SetNextWindowPos(viewport->WorkPos);
        ImGui::SetNextWindowSize(viewport->WorkSize);
        ImGui::SetNextWindowViewport(viewport->ID);
        ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
        ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
        window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
        window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
    }

    ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
    ImGui::Begin("DockSpace", nullptr, window_flags);
    ImGui::PopStyleVar();

    if (opt_fullscreen)
        ImGui::PopStyleVar(2);

    ImGuiIO& io = ImGui::GetIO();
    if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) {
        ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
        ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
    }

    ImGui::End();
}

And the Viewport window:

void EditorUI::RenderViewport() {
    ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
    ImGui::Begin("Viewport");

    // Get the available region for the viewport
    ImVec2 viewportPanelSize = ImGui::GetContentRegionAvail();

    // Update viewport size if changed
    if (m_viewportWidth != static_cast<uint32_t>(viewportPanelSize.x) || 
        m_viewportHeight != static_cast<uint32_t>(viewportPanelSize.y)) 
    {
        m_viewportWidth = static_cast<uint32_t>(viewportPanelSize.x);
        m_viewportHeight = static_cast<uint32_t>(viewportPanelSize.y);

        if (m_renderer && m_viewportWidth > 0 && m_viewportHeight > 0) {
            m_renderer->ResizeViewport(m_viewportWidth, m_viewportHeight);
        }
    }

    // Display the scene texture
    if (auto sceneView = m_renderer->GetSceneTexture()) {
        ImGui::Image((ImTextureID)sceneView, viewportPanelSize);
        Debug::Log("Rendering viewport with scene texture");
    }

    ImGui::End();
    ImGui::PopStyleVar();
}
GamingMinds-DanielC commented 1 week ago

Your basic idea to render into a texture and display that is valid, but there is not nearly enough information to say what went wrong. You can use tools like RenderDoc to analyze your rendering and check if your texture has the contents you expect it to have. Maybe your texture has an alpha channel that is zeroed out, that would make it effectively invisible.

If your problem boils down to how to render into a texture in the first place, that has nothing to do with ImGui itself, but there are lots of tutorials about that online.

ocornut commented 1 week ago

As mentioned, none of this is really an dear imgui question. Your question is how to get your scene into a texture which is a DX11 question and we are not equipped to answer that here. Assuming GetSceneTexture() returns a valid ID3D11ShaderResourceView* your call to ImGui::Image() is enough.