Changeset View
Changeset View
Standalone View
Standalone View
source/simulation2/components/CCmpUnitRenderer.cpp
/* Copyright (C) 2020 Wildfire Games. | /* Copyright (C) 2021 Wildfire Games. | ||||
* This file is part of 0 A.D. | * This file is part of 0 A.D. | ||||
* | * | ||||
* 0 A.D. is free software: you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | ||||
* the Free Software Foundation, either version 2 of the License, or | * the Free Software Foundation, either version 2 of the License, or | ||||
* (at your option) any later version. | * (at your option) any later version. | ||||
* | * | ||||
* 0 A.D. is distributed in the hope that it will be useful, | * 0 A.D. is distributed in the hope that it will be useful, | ||||
Show All 20 Lines | |||||
#include "graphics/ModelAbstract.h" | #include "graphics/ModelAbstract.h" | ||||
#include "graphics/ObjectEntry.h" | #include "graphics/ObjectEntry.h" | ||||
#include "graphics/Overlay.h" | #include "graphics/Overlay.h" | ||||
#include "graphics/Unit.h" | #include "graphics/Unit.h" | ||||
#include "maths/BoundingSphere.h" | #include "maths/BoundingSphere.h" | ||||
#include "maths/Frustum.h" | #include "maths/Frustum.h" | ||||
#include "maths/Matrix3D.h" | #include "maths/Matrix3D.h" | ||||
#include "ps/ConfigDB.h" | |||||
#include "ps/GameSetup/Config.h" | #include "ps/GameSetup/Config.h" | ||||
#include "ps/Profile.h" | #include "ps/Profile.h" | ||||
#include "renderer/RenderingOptions.h" | #include "renderer/RenderingOptions.h" | ||||
#include "renderer/Renderer.h" | |||||
#include "renderer/Scene.h" | #include "renderer/Scene.h" | ||||
#include "tools/atlas/GameInterface/GameLoop.h" | #include "tools/atlas/GameInterface/GameLoop.h" | ||||
/** | /** | ||||
* Efficiently(ish) renders all the units in the world. | * Efficiently(ish) renders all the units in the world. | ||||
* | * | ||||
* The class maintains a list of all units that currently exist, and the data | * The class maintains a list of all units that currently exist, and the data | ||||
Show All 16 Lines | |||||
{ | { | ||||
public: | public: | ||||
struct SUnit | struct SUnit | ||||
{ | { | ||||
CEntityHandle entity; | CEntityHandle entity; | ||||
CUnit* actor; | CUnit* actor; | ||||
int flags; | int flags; | ||||
vladislavbelov: What does the `LOD` name mean? It's not obvious from the name. | |||||
/** | /** | ||||
* m_FrameNumber from when the model's transform was last updated. | * m_FrameNumber from when the model's transform was last updated. | ||||
* This is used to avoid recomputing it multiple times per frame | * This is used to avoid recomputing it multiple times per frame | ||||
* if a model is visible in multiple cull groups. | * if a model is visible in multiple cull groups. | ||||
*/ | */ | ||||
int lastTransformFrame; | int lastTransformFrame; | ||||
▲ Show 20 Lines • Show All 305 Lines • ▼ Show 20 Lines | void CCmpUnitRenderer::RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling) | ||||
// TODO: need a coarse culling pass based on some kind of spatial data | // TODO: need a coarse culling pass based on some kind of spatial data | ||||
// structure - that's the main point of this design. Once we've got a | // structure - that's the main point of this design. Once we've got a | ||||
// rough list of possibly-visible units, then we can do the more precise | // rough list of possibly-visible units, then we can do the more precise | ||||
// culling. (And once it's cheap enough, we can do multiple culling passes | // culling. (And once it's cheap enough, we can do multiple culling passes | ||||
// per frame - one for shadow generation, one for water reflections, etc.) | // per frame - one for shadow generation, one for water reflections, etc.) | ||||
PROFILE3("UnitRenderer::RenderSubmit"); | PROFILE3("UnitRenderer::RenderSubmit"); | ||||
float nearLOD, farLOD; | |||||
CFG_GET_VAL("lod.near", nearLOD); | |||||
CFG_GET_VAL("lod.far", farLOD); | |||||
CVector3D cameraOrigin = CRenderer::GetSingleton().GetViewCamera().m_Orientation.GetTranslation(); | |||||
// Multiply the perceived distance by FOV - for smaller FOV, distances must be longer for the minute-of-angle delta to be the same. | |||||
float fovRatio = std::max(0.01f, CRenderer::GetSingleton().GetViewCamera().GetFOV() / 0.785398f); | |||||
for (size_t i = 0; i < m_Units.size(); ++i) | for (size_t i = 0; i < m_Units.size(); ++i) | ||||
{ | { | ||||
SUnit& unit = m_Units[i]; | SUnit& unit = m_Units[i]; | ||||
unit.culled = true; | unit.culled = true; | ||||
if (!unit.actor) | if (!unit.actor) | ||||
continue; | continue; | ||||
Show All 10 Lines | for (size_t i = 0; i < m_Units.size(); ++i) | ||||
if (!g_AtlasGameLoop->running && (unit.flags & VISIBLE_IN_ATLAS_ONLY)) | if (!g_AtlasGameLoop->running && (unit.flags & VISIBLE_IN_ATLAS_ONLY)) | ||||
continue; | continue; | ||||
if (culling && !frustum.IsSphereVisible(unit.sweptBounds.GetCenter(), unit.sweptBounds.GetRadius())) | if (culling && !frustum.IsSphereVisible(unit.sweptBounds.GetCenter(), unit.sweptBounds.GetRadius())) | ||||
continue; | continue; | ||||
unit.culled = false; | unit.culled = false; | ||||
CmpPtr<ICmpPosition> cmpPosition(unit.entity); | |||||
if (!cmpPosition) | |||||
continue; | |||||
float ratio = (cameraOrigin - cmpPosition->GetPosition()).Length() * fovRatio; | |||||
ratio = 255 - 255 * (ratio - nearLOD < 0 ? 0 : ratio - nearLOD) / (farLOD-nearLOD); | |||||
unit.actor->UpdateQualityLevel(ratio <= 0.f ? 0 : (ratio >= 255.f ? 255 : (int)ratio)); | |||||
CModelAbstract& unitModel = unit.actor->GetModel(); | CModelAbstract& unitModel = unit.actor->GetModel(); | ||||
if (unit.lastTransformFrame != m_FrameNumber) | if (unit.lastTransformFrame != m_FrameNumber) | ||||
{ | { | ||||
CmpPtr<ICmpPosition> cmpPosition(unit.entity); | CmpPtr<ICmpPosition> cmpPosition(unit.entity); | ||||
if (!cmpPosition) | if (!cmpPosition) | ||||
continue; | continue; | ||||
CMatrix3D transform(cmpPosition->GetInterpolatedTransform(m_FrameOffset)); | CMatrix3D transform(cmpPosition->GetInterpolatedTransform(m_FrameOffset)); | ||||
unitModel.SetTransform(transform); | unitModel.SetTransform(transform); | ||||
unit.lastTransformFrame = m_FrameNumber; | unit.lastTransformFrame = m_FrameNumber; | ||||
} | } | ||||
if (culling && !frustum.IsBoxVisible(unitModel.GetWorldBoundsRec())) | if (culling && !frustum.IsBoxVisible(unitModel.GetWorldBoundsRec())) | ||||
continue; | continue; | ||||
collector.SubmitRecursive(&unitModel); | collector.SubmitRecursive(&unitModel); | ||||
} | } | ||||
for (size_t i = 0; i < m_DebugSpheres.size(); ++i) | for (size_t i = 0; i < m_DebugSpheres.size(); ++i) | ||||
collector.Submit(&m_DebugSpheres[i]); | collector.Submit(&m_DebugSpheres[i]); | ||||
} | } | ||||
Done Inline ActionsDon't implement "behind the camera" culling. Since you don't actually account for the entity BB, you'll fail occasionally and units will "pop-in". Further, culling is already handled, so this is just double-work. wraitii: Don't implement "behind the camera" culling.
Since you don't actually account for the entity… | |||||
void CCmpUnitRenderer::UpdateVisibility(SUnit& unit) const | void CCmpUnitRenderer::UpdateVisibility(SUnit& unit) const | ||||
{ | { | ||||
if (unit.inWorld) | if (unit.inWorld) | ||||
{ | { | ||||
// The 'always visible' flag means we should always render the unit | // The 'always visible' flag means we should always render the unit | ||||
// (regardless of whether the LOS system thinks it's visible) | // (regardless of whether the LOS system thinks it's visible) | ||||
CmpPtr<ICmpVisibility> cmpVisibility(unit.entity); | CmpPtr<ICmpVisibility> cmpVisibility(unit.entity); | ||||
if (cmpVisibility && cmpVisibility->GetAlwaysVisible()) | if (cmpVisibility && cmpVisibility->GetAlwaysVisible()) | ||||
Show All 20 Lines |
Wildfire Games · Phabricator
What does the LOD name mean? It's not obvious from the name.