Files
o3de/Gems/ImGui/Code/Editor/ImGuiViewport.cpp
T

344 lines
12 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "ImGui_precompiled.h"
#include "ImGuiViewport.h"
#include <IRenderer.h>
#include <IEditor.h>
#include <QResizeEvent>
#include <imgui/imgui.h>
#include "ImGuiBus.h"
#include "ImGuiManager.h"
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
using namespace ImGui;
ImGuiViewportWidget::ImGuiViewportWidget(QWidget* parent)
: QWidget(parent)
, m_width(0)
, m_height(0)
, m_renderContextCreated(false)
, m_creatingRenderContext(false)
, m_lastTime(0)
, m_lastFrameTime(0.f)
, m_averageFrameTime(0.f)
{
// Setup a timer for the maximum refresh rate we want.
// Refresh is actually triggered by interaction events and by the IdleUpdate. This avoids the UI
// Editor slowing down the main editor when no UI interaction is occurring.
QObject::connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(RefreshTick()));
const int kUpdateIntervalInMillseconds = 1000 / 60; // 60 Hz
m_updateTimer.start(kUpdateIntervalInMillseconds);
CreateRenderContext();
}
ImGuiViewportWidget::~ImGuiViewportWidget()
{
DestroyRenderContext();
ImGuiManagerBus::Broadcast(&IImGuiManager::SetEditorWindowState,
DisplayState::Hidden);
}
bool ImGuiViewportWidget::CreateRenderContext()
{
if (m_creatingRenderContext)
{
return false;
}
m_creatingRenderContext = true;
DestroyRenderContext();
HWND window = (HWND)QWidget::winId();
IEditor* editor = nullptr;
EBUS_EVENT_RESULT(editor, AzToolsFramework::EditorRequests::Bus, GetEditor);
if (window && editor->GetEnv()->pRenderer && !m_renderContextCreated)
{
m_renderContextCreated = true;
StorePreviousContext();
editor->GetEnv()->pRenderer->CreateContext(window);
RestorePreviousContext();
ImGuiManagerBus::Broadcast(&IImGuiManager::SetEditorWindowState,
DisplayState::Visible);
m_creatingRenderContext = false;
return true;
}
m_creatingRenderContext = false;
return false;
}
void ImGuiViewportWidget::DestroyRenderContext()
{
IEditor* editor = nullptr;
EBUS_EVENT_RESULT(editor, AzToolsFramework::EditorRequests::Bus, GetEditor);
if (editor->GetEnv()->pRenderer && m_renderContextCreated)
{
HWND window = (HWND)QWidget::winId();
if (window != editor->GetEnv()->pRenderer->GetHWND())
{
editor = nullptr;
EBUS_EVENT_RESULT(editor, AzToolsFramework::EditorRequests::Bus, GetEditor);
editor->GetEnv()->pRenderer->DeleteContext(window);
}
m_renderContextCreated = false;
}
}
void ImGuiViewportWidget::StorePreviousContext()
{
SPreviousContext previous;
IEditor* editor = nullptr;
EBUS_EVENT_RESULT(editor, AzToolsFramework::EditorRequests::Bus, GetEditor);
previous.width = editor->GetEnv()->pRenderer->GetWidth();
previous.height = editor->GetEnv()->pRenderer->GetHeight();
previous.window = (HWND)editor->GetEnv()->pRenderer->GetCurrentContextHWND();
previous.renderCamera = editor->GetEnv()->pRenderer->GetCamera();
previous.systemCamera = gEnv->pSystem->GetViewCamera();
previous.isMainViewport = editor->GetEnv()->pRenderer->IsCurrentContextMainVP();
m_previousContexts.push(previous);
}
void ImGuiViewportWidget::SetCurrentContext()
{
StorePreviousContext();
IEditor* editor = nullptr;
EBUS_EVENT_RESULT(editor, AzToolsFramework::EditorRequests::Bus, GetEditor);
HWND window = (HWND)QWidget::winId();
editor->GetEnv()->pRenderer->SetCurrentContext(window);
editor->GetEnv()->pRenderer->ChangeViewport(0, 0, m_width, m_height);
}
void ImGuiViewportWidget::RestorePreviousContext()
{
if (m_previousContexts.empty())
{
return;
}
SPreviousContext x = m_previousContexts.top();
m_previousContexts.pop();
IEditor* editor = nullptr;
EBUS_EVENT_RESULT(editor, AzToolsFramework::EditorRequests::Bus, GetEditor);
editor->GetEnv()->pRenderer->SetCurrentContext(x.window);
editor->GetEnv()->pRenderer->ChangeViewport(0, 0, x.width, x.height, x.isMainViewport);
editor->GetEnv()->pRenderer->SetCamera(x.renderCamera);
gEnv->pSystem->SetViewCamera(x.systemCamera);
}
void ImGuiViewportWidget::Render()
{
SetCurrentContext();
IEditor* editor = nullptr;
EBUS_EVENT_RESULT(editor, AzToolsFramework::EditorRequests::Bus, GetEditor);
editor->GetEnv()->pSystem->RenderBegin();
IRenderer* renderer = editor->GetEnv()->pRenderer;
ColorF viewportBackgroundColor(Col_Gray);
ColorF stateMessageColor(Col_Gray);
AZStd::string stateMessage = "No State";
DisplayState visibilityState = DisplayState::Hidden;
ImGuiManagerBus::BroadcastResult(visibilityState,
&IImGuiManager::GetEditorWindowState);
switch (visibilityState)
{
case ImGui::DisplayState::Hidden:
stateMessage = "Invisible";
break;
case ImGui::DisplayState::Visible:
stateMessage = "ImGui Window";
stateMessageColor = Col_SteelBlue;
viewportBackgroundColor = Col_SkyBlue;
break;
case ImGui::DisplayState::VisibleNoMouse:
stateMessage = "Game Focus";
stateMessageColor = Col_Gray;
viewportBackgroundColor = Col_LightGray;
break;
}
renderer->ClearTargetsImmediately(FRT_CLEAR | FRT_CLEAR_IMMEDIATE, viewportBackgroundColor);
renderer->ResetToDefault();
renderer->Draw2dLabel(
12.0f, m_height - 50.f, 1.25f, Col_White, false, "FPS: %.2f", 1.0f / m_averageFrameTime);
renderer->Draw2dLabel(
12.0f, m_height - 30.f, 2.f, stateMessageColor, false, "State: %s", stateMessage.c_str());
if (ImDrawData* drawData = ImGui::GetDrawData())
{
// Configure renderer for 2d imgui rendering
renderer->SetCullMode(R_CULL_DISABLE);
TransformationMatrices backupSceneMatrices;
renderer->Set2DMode(renderer->GetWidth(), renderer->GetHeight(), backupSceneMatrices);
renderer->SetColorOp(eCO_REPLACE, eCO_MODULATE, eCA_Diffuse, DEF_TEXARG0);
renderer->SetSrgbWrite(false);
renderer->SetState(GS_BLSRC_SRCALPHA | GS_BLDST_ONEMINUSSRCALPHA | GS_NODEPTHTEST);
// Expand vertex buffer if necessary
m_vertBuffer.reserve(drawData->TotalVtxCount);
if (m_vertBuffer.size() < drawData->TotalVtxCount)
{
m_vertBuffer.insert(m_vertBuffer.end(),
drawData->TotalVtxCount - m_vertBuffer.size(),
SVF_P3F_C4B_T2F());
}
// Expand index buffer if necessary
m_idxBuffer.reserve(drawData->TotalIdxCount);
if (m_idxBuffer.size() < drawData->TotalIdxCount)
{
m_idxBuffer.insert(m_idxBuffer.end(), drawData->TotalIdxCount - m_idxBuffer.size(), 0);
}
// Process each draw command list individually
for (int n = 0; n < drawData->CmdListsCount; n++)
{
const ImDrawList* cmd_list = drawData->CmdLists[n];
// Cache max vert count for easy access
int numVerts = cmd_list->VtxBuffer.Size;
// Copy command list verts into buffer
for (int i = 0; i < numVerts; ++i)
{
const ImDrawVert& imguiVert = cmd_list->VtxBuffer[i];
SVF_P3F_C4B_T2F& vert = m_vertBuffer[i];
vert.xyz = Vec3(imguiVert.pos.x, imguiVert.pos.y, 0.0f);
// Convert color from RGBA to ARGB
vert.color.dcolor = (imguiVert.col & 0xFF00FF00)
| ((imguiVert.col & 0xFF0000) >> 16)
| ((imguiVert.col & 0xFF) << 16);
vert.st = Vec2(imguiVert.uv.x, imguiVert.uv.y);
}
// Copy command list indices into buffer
for (int i = 0; i < cmd_list->IdxBuffer.Size; ++i)
{
m_idxBuffer[i] = uint16(cmd_list->IdxBuffer[i]);
}
// Use offset pointer to step along rendering operation
uint16* idxBufferDataOffset = m_idxBuffer.data();
// Process each draw command individually
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.size(); cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
// Defer to user rendering callback, if appropriate
if (pcmd->UserCallback)
{
pcmd->UserCallback(cmd_list, pcmd);
}
// Otherwise render our buffers
else
{
int textureId = ((ITexture*)pcmd->TextureId)->GetTextureID();
renderer->SetTexture(textureId);
renderer->SetScissor((int)pcmd->ClipRect.x,
(int)(pcmd->ClipRect.y),
(int)(pcmd->ClipRect.z - pcmd->ClipRect.x),
(int)(pcmd->ClipRect.w - pcmd->ClipRect.y));
renderer->DrawDynVB(m_vertBuffer.data(),
idxBufferDataOffset,
numVerts,
pcmd->ElemCount,
prtTriangleList);
}
// Update offset pointer into command list's index buffer
idxBufferDataOffset += pcmd->ElemCount;
}
}
// Reset scissor usage on renderer
renderer->SetScissor();
// Clean up renderer settings
renderer->Unset2DMode(backupSceneMatrices);
}
bool renderStats = false;
editor->GetEnv()->pSystem->RenderEnd(renderStats, false);
RestorePreviousContext();
}
void ImGuiViewportWidget::resizeEvent(QResizeEvent* ev)
{
QWidget::resizeEvent(ev);
int cx = ev->size().width();
int cy = ev->size().height();
if (cx == 0 || cy == 0)
{
return;
}
m_width = cx;
m_height = cy;
IEditor* editor = nullptr;
EBUS_EVENT_RESULT(editor, AzToolsFramework::EditorRequests::Bus, GetEditor);
editor->GetEnv()->pSystem->GetISystemEventDispatcher()->OnSystemEvent(
ESYSTEM_EVENT_RESIZE, cx, cy);
RefreshTick();
}
void ImGuiViewportWidget::RefreshTick()
{
IEditor* editor = nullptr;
EBUS_EVENT_RESULT(editor, AzToolsFramework::EditorRequests::Bus, GetEditor);
int64 time = editor->GetEnv()->pSystem->GetITimer()->GetAsyncTime().GetMilliSecondsAsInt64();
if (m_lastTime == 0)
{
m_lastTime = time;
}
m_lastFrameTime = (time - m_lastTime) * 0.001f;
m_lastTime = time;
if (m_averageFrameTime == 0.0f)
{
m_averageFrameTime = m_lastFrameTime;
}
else
{
m_averageFrameTime = 0.01f * m_lastFrameTime + 0.99f * m_averageFrameTime;
}
Render();
m_updateTimer.start();
}
bool ImGuiViewportWidget::event(QEvent* ev)
{
bool result = QWidget::event(ev);
if (ev->type() == QEvent::WinIdChange)
{
CreateRenderContext();
}
return result;
}
#include <Editor/moc_ImGuiViewport.cpp>