345 lines
9.4 KiB
C++
345 lines
9.4 KiB
C++
#include <imgui/imgui.h>
|
|
|
|
#include "game_view.h"
|
|
#include "editor/asset_browser.h"
|
|
#include "editor/asset_compiler.h"
|
|
#include "editor/settings.h"
|
|
#include "editor/studio_app.h"
|
|
#include "editor/utils.h"
|
|
#include "editor/world_editor.h"
|
|
#include "engine/engine.h"
|
|
#include "engine/geometry.h"
|
|
#include "engine/input_system.h"
|
|
#include "engine/lua_wrapper.h"
|
|
#include "engine/profiler.h"
|
|
#include "engine/resource_manager.h"
|
|
#include "engine/world.h"
|
|
#include "gui/gui_system.h"
|
|
#include "lua_script/lua_script.h"
|
|
#include "renderer/gpu/gpu.h"
|
|
#include "renderer/pipeline.h"
|
|
#include "renderer/render_module.h"
|
|
#include "renderer/renderer.h"
|
|
#include "renderer/texture.h"
|
|
|
|
|
|
namespace Lumix
|
|
{
|
|
|
|
|
|
struct GUIInterface : GUISystem::Interface
|
|
{
|
|
explicit GUIInterface(GameView& game_view)
|
|
: m_game_view(game_view)
|
|
{
|
|
}
|
|
|
|
Pipeline* getPipeline() override { return m_game_view.m_pipeline.get(); }
|
|
Vec2 getPos() const override { return m_game_view.m_pos; }
|
|
Vec2 getSize() const override { return m_game_view.m_size; }
|
|
void setCursor(os::CursorType type) override { m_game_view.setCursor(type); }
|
|
void enableCursor(bool enable) override { m_game_view.enableIngameCursor(enable); }
|
|
|
|
GameView& m_game_view;
|
|
};
|
|
|
|
|
|
GameView::GameView(StudioApp& app)
|
|
: m_app(app)
|
|
, m_is_open(false)
|
|
, m_is_fullscreen(false)
|
|
, m_is_mouse_captured(false)
|
|
, m_is_ingame_cursor(false)
|
|
, m_time_multiplier(1.0f)
|
|
{
|
|
Engine& engine = app.getEngine();
|
|
auto f = &LuaWrapper::wrapMethodClosure<&GameView::forceViewport>;
|
|
LuaWrapper::createSystemClosure(engine.getState(), "GameView", this, "forceViewport", f);
|
|
}
|
|
|
|
|
|
void GameView::init() {
|
|
m_toggle_ui.init("Game View", "Toggle game view", "game_view", "", Action::IMGUI_PRIORITY);
|
|
m_toggle_ui.func.bind<&GameView::onToggleOpen>(this);
|
|
m_toggle_ui.is_selected.bind<&GameView::isOpen>(this);
|
|
m_app.addWindowAction(&m_toggle_ui);
|
|
|
|
m_fullscreen_action.init("Game View fullscreen", "Game View fullscreen", "game_view_fullscreen", "", Action::IMGUI_PRIORITY);
|
|
m_app.addAction(&m_fullscreen_action);
|
|
|
|
Engine& engine = m_app.getEngine();
|
|
auto* renderer = (Renderer*)engine.getSystemManager().getSystem("renderer");
|
|
LuaScript* pres = engine.getResourceManager().load<LuaScript>(Path("pipelines/main.lua"));
|
|
m_pipeline = Pipeline::create(*renderer, pres, "GAME_VIEW");
|
|
|
|
auto* gui = static_cast<GUISystem*>(engine.getSystemManager().getSystem("gui"));
|
|
if (gui)
|
|
{
|
|
m_gui_interface = UniquePtr<GUIInterface>::create(engine.getAllocator(), *this);
|
|
gui->setInterface(m_gui_interface.get());
|
|
}
|
|
}
|
|
|
|
|
|
GameView::~GameView()
|
|
{
|
|
m_app.removeAction(&m_toggle_ui);
|
|
m_app.removeAction(&m_fullscreen_action);
|
|
Engine& engine = m_app.getEngine();
|
|
auto* gui = static_cast<GUISystem*>(engine.getSystemManager().getSystem("gui"));
|
|
if (gui) {
|
|
gui->setInterface(nullptr);
|
|
}
|
|
}
|
|
|
|
bool GameView::onAction(const Action& action) {
|
|
if (&action == &m_fullscreen_action) toggleFullscreen();
|
|
else return false;
|
|
return true;
|
|
}
|
|
|
|
void GameView::setCursor(os::CursorType type)
|
|
{
|
|
m_cursor_type = type;
|
|
}
|
|
|
|
void GameView::enableIngameCursor(bool enable)
|
|
{
|
|
m_is_ingame_cursor = enable;
|
|
if (!m_is_mouse_captured) return;
|
|
|
|
os::showCursor(m_is_ingame_cursor);
|
|
}
|
|
|
|
|
|
void GameView::captureMouse(bool capture) {
|
|
if (m_is_mouse_captured == capture) return;
|
|
|
|
m_is_mouse_captured = capture;
|
|
os::showCursor(!capture || m_is_ingame_cursor);
|
|
if (capture) m_app.clipMouseCursor();
|
|
else m_app.unclipMouseCursor();
|
|
}
|
|
|
|
void GameView::onSettingsLoaded() {
|
|
m_is_open = m_app.getSettings().getValue(Settings::GLOBAL, "is_game_view_open", false);
|
|
}
|
|
|
|
void GameView::onBeforeSettingsSaved() {
|
|
m_app.getSettings().setValue(Settings::GLOBAL, "is_game_view_open", m_is_open);
|
|
}
|
|
|
|
void GameView::onFullscreenGUI(WorldEditor& editor)
|
|
{
|
|
processInputEvents();
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
bool open = true;
|
|
ImVec2 size = io.DisplaySize;
|
|
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->Pos);
|
|
ImGui::SetNextWindowSize(size);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
|
if (!ImGui::Begin("game view fullscreen",
|
|
&open,
|
|
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse |
|
|
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings))
|
|
{
|
|
ImGui::End();
|
|
ImGui::PopStyleVar(2);
|
|
return;
|
|
}
|
|
|
|
RenderModule* render_module = m_pipeline->getModule();
|
|
EntityPtr camera = render_module->getActiveCamera();
|
|
if (camera.isValid()) {
|
|
Viewport vp = render_module->getCameraViewport((EntityRef)camera);
|
|
vp.w = (int)size.x;
|
|
vp.h = (int)size.y;
|
|
render_module->setCameraScreenSize((EntityRef)camera, vp.w, vp.h);
|
|
m_pipeline->setViewport(vp);
|
|
m_pipeline->render(false);
|
|
const gpu::TextureHandle texture_handle = m_pipeline->getOutput();
|
|
if (gpu::isOriginBottomLeft())
|
|
{
|
|
ImGui::Image(texture_handle, size, ImVec2(0, 1), ImVec2(1, 0));
|
|
}
|
|
else
|
|
{
|
|
ImGui::Image(texture_handle, size);
|
|
}
|
|
}
|
|
else {
|
|
ImGuiEx::Rect(size.x, size.y, 0xff0000FF);
|
|
}
|
|
m_pos = ImGui::GetItemRectMin();
|
|
m_size = ImGui::GetItemRectSize();
|
|
|
|
ImGui::End();
|
|
ImGui::PopStyleVar(2);
|
|
|
|
if (m_is_fullscreen && (ImGui::IsKeyPressed(ImGuiKey_Escape) || !editor.isGameMode()))
|
|
{
|
|
setFullscreen(false);
|
|
}
|
|
}
|
|
|
|
|
|
void GameView::toggleFullscreen() {
|
|
if (!m_app.getWorldEditor().isGameMode()) return;
|
|
setFullscreen(!m_is_fullscreen);
|
|
}
|
|
|
|
|
|
void GameView::setFullscreen(bool fullscreen)
|
|
{
|
|
captureMouse(fullscreen);
|
|
m_app.setFullscreen(fullscreen);
|
|
m_is_fullscreen = fullscreen;
|
|
}
|
|
|
|
void GameView::forceViewport(bool enable, int w, int h)
|
|
{
|
|
m_forced_viewport.enabled = enable;
|
|
m_forced_viewport.width = w;
|
|
m_forced_viewport.height = h;
|
|
}
|
|
|
|
void GameView::processInputEvents()
|
|
{
|
|
if (!m_is_mouse_captured) return;
|
|
|
|
Engine& engine = m_app.getEngine();
|
|
InputSystem& input = engine.getInputSystem();
|
|
for (const os::Event e : m_app.getEvents()) {
|
|
input.injectEvent(e, int(m_pos.x), int(m_pos.y));
|
|
}
|
|
}
|
|
|
|
void GameView::controlsGUI(WorldEditor& editor) {
|
|
Engine& engine = m_app.getEngine();
|
|
ImGui::SetNextItemWidth(50);
|
|
if (ImGui::DragFloat("Time multiplier", &m_time_multiplier, 0.01f, 0.01f, 30.0f)) {
|
|
engine.setTimeMultiplier(m_time_multiplier);
|
|
}
|
|
if(editor.isGameMode()) {
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Fullscreen")) setFullscreen(true);
|
|
}
|
|
ImGui::SameLine();
|
|
m_pipeline->callLuaFunction("onGUI");
|
|
}
|
|
|
|
|
|
void GameView::onGUI()
|
|
{
|
|
PROFILE_FUNCTION();
|
|
WorldEditor& editor = m_app.getWorldEditor();
|
|
m_pipeline->setWorld(editor.getWorld());
|
|
|
|
const bool is_game_mode = m_app.getWorldEditor().isGameMode();
|
|
if (is_game_mode && !m_was_game_mode && m_app.getSettings().m_focus_game_view_on_game_mode_start) {
|
|
ImGui::SetNextWindowFocus();
|
|
m_is_open = true;
|
|
}
|
|
m_was_game_mode = is_game_mode;
|
|
|
|
if (m_is_mouse_captured && (ImGui::IsKeyDown(ImGuiKey_Escape) || !editor.isGameMode() || !m_app.isMouseCursorClipped())) {
|
|
captureMouse(false);
|
|
}
|
|
|
|
const char* window_name = ICON_FA_CAMERA "Game View###game_view";
|
|
if (m_is_mouse_captured) {
|
|
window_name = ICON_FA_CAMERA "Game View (mouse captured)###game_view";
|
|
os::setCursor(m_cursor_type);
|
|
}
|
|
|
|
if (m_is_fullscreen && m_pipeline->isReady()) {
|
|
onFullscreenGUI(editor);
|
|
return;
|
|
}
|
|
|
|
if (!m_is_open) {
|
|
captureMouse(false);
|
|
return;
|
|
}
|
|
|
|
if (!m_pipeline->isReady()) captureMouse(false);
|
|
|
|
ImVec2 view_size;
|
|
bool is_game_view_visible = false;
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
|
if (ImGui::Begin(window_name, &m_is_open, ImGuiWindowFlags_NoNavInputs)) {
|
|
is_game_view_visible = true;
|
|
|
|
const ImVec2 content_min = ImGui::GetCursorScreenPos();
|
|
view_size = ImGui::GetContentRegionAvail();
|
|
view_size.y -= ImGui::GetTextLineHeightWithSpacing() + ImGui::GetStyle().ItemSpacing.y * 3;
|
|
ImVec2 content_max(content_min.x + view_size.x, content_min.y + view_size.y);
|
|
if (m_forced_viewport.enabled) view_size = { (float)m_forced_viewport.width, (float)m_forced_viewport.height };
|
|
if (view_size.x > 0 && view_size.y > 0) {
|
|
RenderModule* module = m_pipeline->getModule();
|
|
const EntityPtr camera = module->getActiveCamera();
|
|
Viewport vp;
|
|
if (camera.isValid()) {
|
|
vp = module->getCameraViewport((EntityRef)camera);
|
|
vp.w = (int)view_size.x;
|
|
vp.h = (int)view_size.y;
|
|
module->setCameraScreenSize((EntityRef)camera, vp.w, vp.h);
|
|
}
|
|
else {
|
|
vp.w = (int)view_size.x;
|
|
vp.h = (int)view_size.y;
|
|
vp.fov = degreesToRadians(90.f);
|
|
vp.is_ortho = false;
|
|
vp.far = 10'000.f;
|
|
vp.near = 1.f;
|
|
vp.pos = DVec3(0);
|
|
vp.rot = Quat(0, 0, 0, 1);
|
|
}
|
|
m_pipeline->setViewport(vp);
|
|
m_pipeline->render(false);
|
|
const gpu::TextureHandle texture_handle = m_pipeline->getOutput();
|
|
|
|
controlsGUI(editor);
|
|
|
|
const ImVec2 view_pos = ImGui::GetCursorScreenPos();
|
|
if (texture_handle) {
|
|
if (gpu::isOriginBottomLeft()) {
|
|
ImGui::Image(texture_handle, view_size, ImVec2(0, 1), ImVec2(1, 0));
|
|
}
|
|
else {
|
|
ImGui::Image(texture_handle, view_size);
|
|
}
|
|
}
|
|
else {
|
|
ImGuiEx::Rect(view_size.x, view_size.y, 0xffFF00FF);
|
|
}
|
|
if (m_is_mouse_captured) {
|
|
os::Rect rect;
|
|
rect.left = (i32)view_pos.x;
|
|
rect.top = (i32)view_pos.y;
|
|
rect.width = (i32)view_size.x;
|
|
rect.height = (i32)view_size.y;
|
|
m_app.setMouseClipRect(ImGui::GetWindowViewport()->PlatformHandle, rect);
|
|
}
|
|
|
|
const bool is_hovered = ImGui::IsItemHovered();
|
|
if (is_hovered && ImGui::IsMouseReleased(0) && editor.isGameMode()) captureMouse(true);
|
|
m_pos = ImGui::GetItemRectMin();
|
|
m_size = ImGui::GetItemRectSize();
|
|
|
|
processInputEvents();
|
|
}
|
|
|
|
}
|
|
|
|
if (m_is_mouse_captured && !is_game_view_visible) captureMouse(false);
|
|
|
|
ImGui::End();
|
|
ImGui::PopStyleVar();
|
|
}
|
|
|
|
|
|
} // namespace Lumix
|