0ad/source/tools/atlas/GameInterface/View.cpp
Ralph Sennhauser fa9584fdc0
Fix some Atlas header includes
iwyu 0.26 now want's full declaration of TYPE in case of
std::vector<TYPE>, which is good as it allows the header to be
self standing.

There are some other changes suggested in part of the headers not being
updated when they should have been and some due to changes in iwyu
itself.

Ref: #8086
Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
2026-06-12 21:22:36 +02:00

481 lines
12 KiB
C++

/* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "View.h"
#include "graphics/Canvas2D.h"
#include "graphics/CinemaManager.h"
#include "graphics/Color.h"
#include "graphics/GameView.h"
#include "lib/debug.h"
#include "lib/utf8.h"
#include "maths/MathUtil.h"
#include "maths/Matrix3D.h"
#include "maths/Vector2D.h"
#include "ps/CStr.h"
#include "ps/CLogger.h"
#include "ps/ConfigDB.h"
#include "ps/Game.h"
#include "ps/VideoMode.h"
#include "renderer/DebugRenderer.h"
#include "renderer/Renderer.h"
#include "renderer/SceneRenderer.h"
#include "renderer/backend/ISwapChain.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpParticleManager.h"
#include "simulation2/components/ICmpPathfinder.h"
#include "simulation2/system/Component.h"
#include "soundmanager/ISoundManager.h"
#include "tools/atlas/GameInterface/ActorViewer.h"
#include "tools/atlas/GameInterface/GameLoop.h"
#include "tools/atlas/GameInterface/Messages.h"
#include "tools/atlas/GameInterface/SimState.h"
#include <sstream>
#include <utility>
#include <vector>
extern void (*Atlas_GLSwapBuffers)(void* context);
extern int g_xres, g_yres;
//////////////////////////////////////////////////////////////////////////
void AtlasView::SetParam(const std::wstring& /*name*/, bool /*value*/)
{
}
void AtlasView::SetParam(const std::wstring& /*name*/, const AtlasMessage::Color& /*value*/)
{
}
void AtlasView::SetParam(const std::wstring& /*name*/, const std::wstring& /*value*/)
{
}
void AtlasView::SetParam(const std::wstring& /*name*/, int /*value*/)
{
}
//////////////////////////////////////////////////////////////////////////
AtlasViewActor::AtlasViewActor()
: m_SpeedMultiplier(1.f), m_ActorViewer(new ActorViewer())
{
}
AtlasViewActor::~AtlasViewActor()
{
delete m_ActorViewer;
}
void AtlasViewActor::Update(float realFrameLength)
{
m_ActorViewer->Update(realFrameLength * m_SpeedMultiplier, realFrameLength);
}
void AtlasViewActor::Render()
{
SViewPort vp = { 0, 0, g_xres, g_yres };
CCamera& camera = GetCamera();
camera.SetViewPort(vp);
camera.SetPerspectiveProjection(2.f, 512.f, DEGTORAD(20.f));
camera.UpdateFrustum();
m_ActorViewer->Render();
Atlas_GLSwapBuffers((void*)g_AtlasGameLoop->glCanvas);
}
CCamera& AtlasViewActor::GetCamera()
{
return m_Camera;
}
CSimulation2* AtlasViewActor::GetSimulation2()
{
return m_ActorViewer->GetSimulation2();
}
entity_id_t AtlasViewActor::GetEntityId(AtlasMessage::ObjectID)
{
return m_ActorViewer->GetEntity();
}
bool AtlasViewActor::WantsHighFramerate()
{
if (m_SpeedMultiplier != 0.f)
return true;
return false;
}
void AtlasViewActor::SetEnabled(bool enabled)
{
m_ActorViewer->SetEnabled(enabled);
}
void AtlasViewActor::SetSpeedMultiplier(float speedMultiplier)
{
m_SpeedMultiplier = speedMultiplier;
}
ActorViewer& AtlasViewActor::GetActorViewer()
{
return *m_ActorViewer;
}
void AtlasViewActor::SetParam(const std::wstring& name, bool value)
{
if (name == L"wireframe")
g_Renderer.GetSceneRenderer().SetModelRenderMode(value ? WIREFRAME : SOLID);
else if (name == L"walk")
m_ActorViewer->SetWalkEnabled(value);
else if (name == L"ground")
m_ActorViewer->SetGroundEnabled(value);
// TODO: this causes corruption of WaterManager's global state
// which should be asociated with terrain or simulation instead
// see https://gitea.wildfiregames.com/0ad/0ad/issues/2692
//else if (name == L"water")
//m_ActorViewer->SetWaterEnabled(value);
else if (name == L"shadows")
m_ActorViewer->ToggleShadows();
else if (name == L"stats")
m_ActorViewer->SetStatsEnabled(value);
else if (name == L"bounding_box")
m_ActorViewer->SetBoundingBoxesEnabled(value);
else if (name == L"axes_marker")
m_ActorViewer->SetAxesMarkerEnabled(value);
}
void AtlasViewActor::SetParam(const std::wstring& name, int value)
{
if (name == L"prop_points")
m_ActorViewer->SetPropPointsMode(value);
}
void AtlasViewActor::SetParam(const std::wstring& /*name*/, const AtlasMessage::Color& /*value*/)
{
}
//////////////////////////////////////////////////////////////////////////
AtlasViewGame::AtlasViewGame()
: m_SpeedMultiplier(0.f), m_IsTesting(false), m_DrawMoveTool(false)
{
ENSURE(g_Game);
}
AtlasViewGame::~AtlasViewGame()
{
for (const std::pair<const std::wstring, SimState*>& p : m_SavedStates)
delete p.second;
}
CSimulation2* AtlasViewGame::GetSimulation2()
{
return g_Game->GetSimulation2();
}
void AtlasViewGame::Update(float realFrameLength)
{
const float actualFrameLength = realFrameLength * m_SpeedMultiplier;
// Clean up any entities destroyed during UI message processing
g_Game->GetSimulation2()->FlushDestroyedEntities();
if (m_SpeedMultiplier == 0.f)
{
// Update unit interpolation
g_Game->Interpolate(0.0, realFrameLength);
}
else
{
// Update the whole world
// (Tell the game update not to interpolate graphics - we'll do that
// ourselves)
g_Game->Update(actualFrameLength, false);
// Interpolate the graphics - we only want to do this once per visual frame,
// not in every call to g_Game->Update
g_Game->Interpolate(actualFrameLength, realFrameLength);
}
// Run sound idle tasks every frame.
if (g_SoundManager)
g_SoundManager->IdleTask();
// Cinematic motion should be independent of simulation update, so we can
// preview the cinematics by themselves
g_Game->GetView()->GetCinema()->Update(realFrameLength);
}
void AtlasViewGame::Render()
{
Renderer::Backend::ISwapChain* swapChain{g_VideoMode.GetOrCreateSwapChain()};
if (!swapChain || !swapChain->IsValid() || !swapChain->AcquireNextBackbuffer())
return;
SViewPort vp = { 0, 0, g_xres, g_yres };
CCamera& camera = GetCamera();
camera.SetViewPort(vp);
camera.SetProjectionFromCamera(*g_Game->GetView()->GetCamera());
camera.UpdateFrustum();
g_Renderer.RenderFrame(false);
Atlas_GLSwapBuffers((void*)g_AtlasGameLoop->glCanvas);
// In case of atlas the swapchain's present will do only internal stuff
// without calling a real backbuffer swap.
swapChain->Present();
}
void AtlasViewGame::DrawCinemaPathTool(Renderer::Backend::IDeviceCommandContext& deviceCommandContext)
{
if (!m_DrawMoveTool)
return;
const CVector3D focus = m_MoveTool;
const CVector3D camera = GetCamera().GetOrientation().GetTranslation();
const float scale = (focus - camera).Length();
const float axisLength = scale / 10.0f;
const float lineWidth = scale / 1e3f;
g_Renderer.GetDebugRenderer().DrawLine(
deviceCommandContext,
focus, focus + CVector3D(axisLength, 0, 0),
CColor(1.0f, 0.0f, 0.0f, 1.0f), lineWidth, false);
g_Renderer.GetDebugRenderer().DrawLine(
deviceCommandContext,
focus, focus + CVector3D(0, axisLength, 0),
CColor(0.0f, 1.0f, 0.0f, 1.0f), lineWidth, false);
g_Renderer.GetDebugRenderer().DrawLine(
deviceCommandContext,
focus, focus + CVector3D(0, 0, axisLength),
CColor(0.0f, 0.0f, 1.0f, 1.0f), lineWidth, false);
}
void AtlasViewGame::DrawOverlays(CCanvas2D& canvas)
{
if (m_Bandbox.left >= m_Bandbox.right || m_Bandbox.top >= m_Bandbox.bottom)
return;
const std::vector<CVector2D> outerPoints = {
m_Bandbox.TopLeft() + CVector2D(-1.0f, -1.0f),
m_Bandbox.TopRight() + CVector2D(1.0f, -1.0f),
m_Bandbox.BottomRight() + CVector2D(1.0f, 1.0f),
m_Bandbox.BottomLeft() + CVector2D(-1.0f, 1.0f),
m_Bandbox.TopLeft() + CVector2D(-1.0f, -1.0f)
};
canvas.DrawLine(outerPoints, 1.5f, CColor(0.0f, 0.0f, 0.0f, 1.0f));
const std::vector<CVector2D> innerPoints = {
m_Bandbox.TopLeft(),
m_Bandbox.TopRight(),
m_Bandbox.BottomRight(),
m_Bandbox.BottomLeft(),
m_Bandbox.TopLeft()
};
canvas.DrawLine(innerPoints, 1.5f, CColor(1.0f, 1.0f, 1.0f, 1.0f));
}
void AtlasViewGame::SetParam(const std::wstring& name, bool value)
{
if (name == L"priorities")
g_Renderer.GetSceneRenderer().SetDisplayTerrainPriorities(value);
else if (name == L"movetool")
m_DrawMoveTool = value;
}
void AtlasViewGame::SetParam(const std::wstring& name, float value)
{
if (name == L"movetool_x")
m_MoveTool.X = value;
else if (name == L"movetool_y")
m_MoveTool.Y = value;
else if (name == L"movetool_z")
m_MoveTool.Z = value;
}
void AtlasViewGame::SetParam(const std::wstring& name, const std::wstring& value)
{
if (name == L"passability")
{
m_DisplayPassability = CStrW(value).ToUTF8();
CmpPtr<ICmpPathfinder> cmpPathfinder(*GetSimulation2(), SYSTEM_ENTITY);
if (cmpPathfinder)
{
if (!value.empty())
cmpPathfinder->SetAtlasOverlay(true, cmpPathfinder->GetPassabilityClass(m_DisplayPassability));
else
cmpPathfinder->SetAtlasOverlay(false);
}
}
}
CCamera& AtlasViewGame::GetCamera()
{
return *g_Game->GetView()->GetCamera();
}
bool AtlasViewGame::WantsHighFramerate()
{
if (g_Game->GetView()->GetCinema()->IsPlaying())
return true;
if (m_SpeedMultiplier != 0.f)
return true;
return false;
}
void AtlasViewGame::SetSpeedMultiplier(float speed)
{
m_SpeedMultiplier = speed;
}
void AtlasViewGame::SetTesting(bool testing)
{
m_IsTesting = testing;
// If we're testing, particles should freeze on pause (like in-game), otherwise they keep going
CmpPtr<ICmpParticleManager> cmpParticleManager(*GetSimulation2(), SYSTEM_ENTITY);
if (cmpParticleManager)
cmpParticleManager->SetUseSimTime(m_IsTesting);
}
void AtlasViewGame::SaveState(const std::wstring& label)
{
delete m_SavedStates[label]; // in case it already exists
m_SavedStates[label] = SimState::Freeze();
}
void AtlasViewGame::RestoreState(const std::wstring& label)
{
SimState* simState = m_SavedStates[label];
if (! simState)
return;
simState->Thaw();
}
std::wstring AtlasViewGame::DumpState(bool binary)
{
std::stringstream stream;
if (binary)
{
if (! g_Game->GetSimulation2()->SerializeState(stream))
return L"(internal error)";
// We can't return raw binary data, because we want to handle it with wxJS which
// doesn't like \0 bytes in strings, so return it as hex
static const char digits[] = "0123456789abcdef";
std::string str = stream.str();
std::wstring ret;
ret.reserve(str.length()*3);
for (size_t i = 0; i < str.length(); ++i)
{
ret += digits[(unsigned char)str[i] >> 4];
ret += digits[(unsigned char)str[i] & 0x0f];
ret += ' ';
}
return ret;
}
else
{
if (! g_Game->GetSimulation2()->DumpDebugState(stream))
return L"(internal error)";
return wstring_from_utf8(stream.str());
}
}
void AtlasViewGame::SetBandbox(bool visible, float x0, float y0, float x1, float y1)
{
if (visible)
{
// Make sure corners are arranged in correct order
if (x0 > x1)
std::swap(x0, x1);
if (y0 > y1)
std::swap(y0, y1);
const float scale{g_ConfigDB.Get("gui.scale", 0.0f)};
m_Bandbox = CRect(x0 / scale, y0 / scale, x1 / scale, y1 / scale);
}
else
{
m_Bandbox = CRect{};
}
}
//////////////////////////////////////////////////////////////////////////
AtlasViewNone* view_None = NULL;
AtlasViewGame* view_Game = NULL;
AtlasViewActor* view_Actor = NULL;
AtlasView::~AtlasView()
{
}
AtlasView* AtlasView::GetView(int /*eRenderView*/ view)
{
switch (view)
{
case AtlasMessage::eRenderView::NONE: return AtlasView::GetView_None();
case AtlasMessage::eRenderView::GAME: return AtlasView::GetView_Game();
case AtlasMessage::eRenderView::ACTOR: return AtlasView::GetView_Actor();
default:
debug_warn(L"Invalid view type");
return AtlasView::GetView_None();
}
}
AtlasView* AtlasView::GetView_None()
{
if (! view_None)
view_None = new AtlasViewNone();
return view_None;
}
AtlasViewGame* AtlasView::GetView_Game()
{
if (! view_Game)
view_Game = new AtlasViewGame();
return view_Game;
}
AtlasViewActor* AtlasView::GetView_Actor()
{
try
{
if (!view_Actor)
view_Actor = new AtlasViewActor();
}
catch (const CSimulation2::LoadScriptError& e)
{
LOGERROR("%s", e.what());
}
return view_Actor;
}
void AtlasView::DestroyViews()
{
delete view_None; view_None = NULL;
delete view_Game; view_Game = NULL;
delete view_Actor; view_Actor = NULL;
}