mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-08-15 14:43:32 -07:00
Compare commits
19 commits
e97eb59c70
...
d6bdf51d83
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6bdf51d83 | ||
|
|
ae936e8177 | ||
|
|
06e199b12a | ||
|
|
84567e66c2 | ||
|
|
c4e7364802 | ||
|
|
867bdf318b | ||
|
|
e64a138f92 | ||
|
|
4a7b7e7df1 | ||
|
|
fa90ddc8b6 | ||
|
|
764a420797 | ||
|
|
8a2bb10827 | ||
|
|
af9d102126 | ||
|
|
822092fcb4 | ||
|
|
6e79dc2f70 | ||
|
|
6fdcd68172 | ||
|
|
e4f4030a5e | ||
|
|
2fb6347125 | ||
|
|
7aa756f6a4 | ||
|
|
e712601602 |
40 changed files with 364 additions and 569 deletions
|
|
@ -11,7 +11,7 @@ jobs:
|
|||
lfscheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Fetch the base branch
|
||||
run: git fetch origin ${{ env.BASE_SHA }}
|
||||
|
|
@ -24,27 +24,14 @@ jobs:
|
|||
env:
|
||||
GIT_LFS_SKIP_SMUDGE: "1"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: "3.11"
|
||||
- name: Add remote fork origin for LFS
|
||||
run: |
|
||||
PR_REPO="${{ gitea.event.pull_request.head.repo.full_name || gitea.repository }}"
|
||||
git remote add ${{ gitea.actor }} https://gitea.wildfiregames.com/${PR_REPO}.git
|
||||
- name: Workaround for authentication problem with LFS
|
||||
# https://gitea.com/gitea/act_runner/issues/164
|
||||
run: |
|
||||
git config --local \
|
||||
http.${{ gitea.server_url }}/${{ gitea.repository }}.git/info/lfs/objects/.extraheader ''
|
||||
|
||||
PR_REPO="${{ gitea.event.pull_request.head.repo.full_name || gitea.repository }}"
|
||||
EXTRAHEADER="$(git config --get --local http.${{ gitea.server_url }}/.extraheader)"
|
||||
git config --local \
|
||||
http.${{ gitea.server_url }}/${PR_REPO}.git/info/lfs/objects/batch.extraheader \
|
||||
'${EXTRAHEADER}'
|
||||
git config --local \
|
||||
http.${{ gitea.server_url }}/${PR_REPO}.git/info/lfs/objects/.extraheader ''
|
||||
- name: Download necessary LFS assets
|
||||
shell: sh {0}
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ jobs:
|
|||
cppcheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
# cache only works for items in workspace, so configure apt to allow for caching.
|
||||
- name: Setup apt cache locations
|
||||
|
|
@ -26,7 +26,7 @@ jobs:
|
|||
|
||||
- name: Cache apt pkg db and and deb files
|
||||
id: apt-cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: |
|
||||
apt-cache
|
||||
|
|
@ -49,11 +49,11 @@ jobs:
|
|||
copyright:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 100
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
|
|
@ -66,10 +66,10 @@ jobs:
|
|||
jenkinsfiles:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
|
|
|
|||
|
|
@ -8,5 +8,5 @@
|
|||
{ "value": "Researcher/TechCostMultiplier/metal", "multiply": 0.9 }
|
||||
],
|
||||
"auraName": "Economic Fortune",
|
||||
"auraDescription": "Solon brought in a new system of weights and measures, fathers were encouraged to find trades for their sons.\nEconomic technologies −10% resource costs."
|
||||
"auraDescription": "Solon brought in a new system of weights and measures, and fathers were encouraged to find trades for their sons.\nEconomic technologies −10% resource costs."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,5 +5,5 @@
|
|||
{ "value": "TerritoryInfluence/Radius", "multiply": 1.2 }
|
||||
],
|
||||
"auraName": "Territorial Expansion",
|
||||
"auraDescription": "At its height, the Empire's borders spanned from the Fergana Valley in the west, to Korea in the east, and to northern Vietnam in the south.\nTerritory influence bonus +20%."
|
||||
"auraDescription": "At its height, the Empire's borders spanned from the Fergana Valley in the west to Korea in the east and to northern Vietnam in the south.\nTerritory influence bonus +20%."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,5 +7,5 @@
|
|||
{ "value": "Resistance/Entity/Damage/Crush", "add": 1 }
|
||||
],
|
||||
"auraName": "Founder and Defender of the Republic",
|
||||
"auraDescription": "Brutus was one of the key figures in the overthrow of the monarchy and the founding of the Roman Republic. Later, as consul he led a Roman army to victory against the Etruscan King Tarquinius who sought to retake the throne.\nHumans and Siege Engines +1 crush, hack, pierce resistance."
|
||||
"auraDescription": "Brutus was one of the key figures in the overthrow of the monarchy and the founding of the Roman Republic. Later, as consul, he led a Roman army to victory against the Etruscan King Tarquinius who sought to retake the throne.\nHumans and Siege Engines +1 crush, hack, and pierce resistance."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ echo "Building cpp-httplib..."
|
|||
if [ -e .already-built ] && [ "$(cat .already-built || true)" = "${LIB_VERSION}" ]; then
|
||||
echo "Skipping - already built (use --force-rebuild to override)"
|
||||
exit
|
||||
else
|
||||
rm -f .already-built
|
||||
fi
|
||||
|
||||
# fetch
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ done
|
|||
if [ -e .already-built ] && [ "$(cat .already-built || true)" = "${LIB_VERSION}" ]; then
|
||||
echo "Skipping - already built (use --force-rebuild to override)"
|
||||
exit
|
||||
else
|
||||
rm -f .already-built
|
||||
fi
|
||||
|
||||
# fetch
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ done
|
|||
if [ -e .already-built ] && [ "$(cat .already-built || true)" = "${LIB_VERSION}" ]; then
|
||||
echo "Skipping - already built (use --force-rebuild to override)"
|
||||
exit
|
||||
else
|
||||
rm -f .already-built
|
||||
fi
|
||||
|
||||
# fetch
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ done
|
|||
if [ -e .already-built ] && [ "$(cat .already-built || true)" = "${LIB_VERSION}" ]; then
|
||||
echo "Skipping - already built (use --force-rebuild to override)"
|
||||
exit
|
||||
else
|
||||
rm -f .already-built
|
||||
fi
|
||||
|
||||
# fetch
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ done
|
|||
if [ -e .already-built ] && [ "$(cat .already-built || true)" = "${LIB_VERSION}" ]; then
|
||||
echo "Skipping - already built (use --force-rebuild to override)"
|
||||
exit
|
||||
else
|
||||
rm -f .already-built
|
||||
fi
|
||||
|
||||
# fetch
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ echo "Building SpiderMonkey..."
|
|||
if [ -e .already-built ] && [ "$(cat .already-built || true)" = "${LIB_VERSION}" ]; then
|
||||
echo "Skipping - already built (use --force-rebuild to override)"
|
||||
exit
|
||||
else
|
||||
rm -f .already-built
|
||||
fi
|
||||
|
||||
OS="${OS:=$(uname -s)}"
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ done
|
|||
if [ -e .already-built ] && [ "$(cat .already-built || true)" = "${LIB_VERSION}" ]; then
|
||||
echo "Skipping - already built (use --force-rebuild to override)"
|
||||
exit
|
||||
else
|
||||
rm -f .already-built
|
||||
fi
|
||||
|
||||
# fetch
|
||||
|
|
|
|||
|
|
@ -53,25 +53,6 @@ public:
|
|||
void SetElevation(float f);
|
||||
void SetRotation(float f);
|
||||
|
||||
/**
|
||||
* Calculate brightness of a point of a unit with the given normal vector,
|
||||
* for rendering with CPU lighting.
|
||||
* The resulting color contains both ambient and diffuse light.
|
||||
* To cope with sun overbrightness, the color is scaled by 0.5.
|
||||
*
|
||||
* @param normal normal vector (must have length 1)
|
||||
*/
|
||||
RGBColor EvaluateUnitScaled(const CVector3D& normal) const
|
||||
{
|
||||
float dot = -normal.Dot(m_SunDir);
|
||||
|
||||
RGBColor color = m_AmbientColor;
|
||||
if (dot > 0)
|
||||
color += m_SunColor * dot;
|
||||
|
||||
return color * 0.5f;
|
||||
}
|
||||
|
||||
// Comparison operators
|
||||
bool operator==(const CLightEnv& o) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* 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
|
||||
|
|
@ -69,7 +69,7 @@ CObjectBase::CObjectBase(CObjectManager& objectManager, CActorDef& actorDef, u8
|
|||
m_Properties.m_FloatOnWater = false;
|
||||
|
||||
// Remove leading art/actors/ & include quality level.
|
||||
m_Identifier = m_ActorDef.m_Pathname.string8().substr(11) + CStr::FromInt(m_QualityLevel);
|
||||
m_Identifier = fmt::format("{}{}", m_ActorDef.m_Pathname.string8().substr(11), m_QualityLevel);
|
||||
}
|
||||
|
||||
std::unique_ptr<CObjectBase> CObjectBase::CopyWithQuality(u8 newQualityLevel) const
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* 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
|
||||
|
|
@ -156,7 +156,8 @@ bool ResolveIncludesImpl(
|
|||
it = includeCache.emplace(path, std::move(includeContent)).first;
|
||||
}
|
||||
// We need to insert #line directives to have correct line numbers in errors.
|
||||
chunks.emplace_back(lineDirective + "1\n" + it->second + "\n" + lineDirective + CStr::FromUInt(line + 1) + "\n");
|
||||
chunks.emplace_back(fmt::format("{}1\n{}\n{}{}\n", lineDirective, it->second, lineDirective,
|
||||
line + 1));
|
||||
processedParts.emplace_back(currentPart.substr(0, lineStart));
|
||||
if (!ResolveIncludesImpl(chunks.back(), includeCache, includeCallback, chunks, processedParts))
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -755,7 +755,7 @@ void CGUI::Xeromyces_ReadObject(const XMBData& xmb, XMBElement element, IGUIObje
|
|||
// Check if name isn't set, generate an internal name in that case.
|
||||
if (!NameSet)
|
||||
{
|
||||
object->SetName("__internal(" + CStr::FromInt(m_InternalNameNumber) + ")");
|
||||
object->SetName(fmt::format("__internal({})", m_InternalNameNumber));
|
||||
++m_InternalNameNumber;
|
||||
}
|
||||
|
||||
|
|
@ -977,7 +977,7 @@ void CGUI::Xeromyces_ReadRepeat(const XMBData& xmb, XMBElement element, IGUIObje
|
|||
|
||||
for (int n = 0; n < count; ++n)
|
||||
{
|
||||
NameSubst.emplace_back(var, "[" + CStr::FromInt(n) + "]");
|
||||
NameSubst.emplace_back(var, fmt::format("[{}]", n));
|
||||
|
||||
XERO_ITER_EL(element, child)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ CStr CNetMessage::ToString() const
|
|||
if (GetType() == NMT_INVALID)
|
||||
return "MESSAGE_TYPE_NONE { Undefined Message }";
|
||||
else
|
||||
return "Unknown Message " + CStr::FromInt(GetType());
|
||||
return fmt::format("Unknown Message {}", static_cast<int>(GetType()));
|
||||
}
|
||||
|
||||
CNetMessage* CNetMessageFactory::CreateMessage(const void* pData,
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <fmt/format.h>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
|
|
@ -70,6 +71,10 @@
|
|||
#include <miniupnpc/upnperrors.h>
|
||||
#endif
|
||||
|
||||
#if FMT_VERSION >= 80000
|
||||
#include <fmt/xchar.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Number of peers to allocate for the enet host.
|
||||
* Limited by ENET_PROTOCOL_MAXIMUM_PEER_ID (4096).
|
||||
|
|
@ -1641,7 +1646,7 @@ CStrW CNetServerWorker::DeduplicatePlayerName(const CStrW& original)
|
|||
if (unique)
|
||||
return name;
|
||||
|
||||
name = original + L" (" + CStrW::FromUInt(id++) + L")";
|
||||
name = fmt::format(L"{}({})", original, id++);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "NetStats.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <string>
|
||||
|
||||
enum
|
||||
|
|
@ -77,7 +78,7 @@ const std::vector<ProfileColumn>& CNetStatsTable::GetColumns()
|
|||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
|
||||
for (size_t i = 0; i < m_LatchedData.size(); ++i)
|
||||
m_ColumnDescriptions.push_back(ProfileColumn("Peer "+CStr::FromUInt(i), 80));
|
||||
m_ColumnDescriptions.push_back(ProfileColumn(fmt::format("Peer {}", i), 80));
|
||||
}
|
||||
|
||||
return m_ColumnDescriptions;
|
||||
|
|
@ -95,7 +96,7 @@ CStr CNetStatsTable::GetCellText(size_t row, size_t col)
|
|||
#define ROW(id, title, member) \
|
||||
case id: \
|
||||
if (col == 0) return title; \
|
||||
if (m_Peer) return CStr::FromUInt(m_Peer->member); \
|
||||
if (m_Peer) return std::to_string(m_Peer->member); \
|
||||
return "???"
|
||||
|
||||
switch(row)
|
||||
|
|
@ -129,7 +130,7 @@ void CNetStatsTable::LatchHostState(const ENetHost& host)
|
|||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
|
||||
#define ROW(id, title, member) \
|
||||
m_LatchedData[i].push_back(CStr::FromUInt(host.peers[i].member));
|
||||
m_LatchedData[i].push_back(std::to_string(host.peers[i].member));
|
||||
|
||||
m_LatchedData.clear();
|
||||
m_LatchedData.resize(host.peerCount);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* 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
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
static inline CStr NetMessageStringConvert(u32 arg)
|
||||
{
|
||||
return CStr::FromUInt(arg);
|
||||
return std::to_string(arg);
|
||||
}
|
||||
|
||||
static inline CStr NetMessageStringConvert(const CStr8& arg)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2021 Wildfire Games.
|
||||
/* 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
|
||||
|
|
@ -202,27 +202,6 @@ CStr CStr::Repeat(const CStr& str, size_t reps)
|
|||
|
||||
// Construction from numbers:
|
||||
|
||||
CStr CStr::FromInt(int n)
|
||||
{
|
||||
tstringstream<StrBase> ss;
|
||||
ss << n;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
CStr CStr::FromUInt(unsigned int n)
|
||||
{
|
||||
tstringstream<StrBase> ss;
|
||||
ss << n;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
CStr CStr::FromInt64(i64 n)
|
||||
{
|
||||
tstringstream<StrBase> ss;
|
||||
ss << n;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
CStr CStr::FromDouble(double n)
|
||||
{
|
||||
tstringstream<StrBase> ss;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* 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
|
||||
|
|
@ -95,9 +95,6 @@ public:
|
|||
|
||||
// Conversions:
|
||||
|
||||
static CStr FromInt(int n);
|
||||
static CStr FromUInt(unsigned int n);
|
||||
static CStr FromInt64(i64 n);
|
||||
static CStr FromDouble(double n);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -90,7 +90,6 @@ CGame::CGame(bool replayLog, const SimulationDebugOptions debugOptions):
|
|||
m_SimRate(1.0f),
|
||||
m_PlayerID(-1),
|
||||
m_ViewedPlayerID(-1),
|
||||
m_IsSavedGame(false),
|
||||
m_IsVisualReplay(false),
|
||||
m_ReplayStream(NULL)
|
||||
{
|
||||
|
|
@ -224,7 +223,7 @@ void CGame::RegisterInit(const JS::HandleValue attribs, const std::string& saved
|
|||
const Script::Interface& scriptInterface = m_Simulation2->GetScriptInterface();
|
||||
Script::Request rq(scriptInterface);
|
||||
|
||||
m_IsSavedGame = !savedState.empty();
|
||||
const bool isSavedGame{!savedState.empty()};
|
||||
|
||||
m_Simulation2->SetInitAttributes(attribs);
|
||||
|
||||
|
|
@ -285,7 +284,7 @@ void CGame::RegisterInit(const JS::HandleValue attribs, const std::string& saved
|
|||
co_return g_Renderer.GetSceneRenderer().GetWaterManager().LoadWaterTextures();
|
||||
}, L"LoadWaterTextures", 80);
|
||||
|
||||
if (m_IsSavedGame)
|
||||
if (isSavedGame)
|
||||
PS::Loader::Register(std::bind_front(
|
||||
[](CGame* game, const std::string& state) -> PS::Loader::Task
|
||||
{
|
||||
|
|
@ -298,13 +297,32 @@ void CGame::RegisterInit(const JS::HandleValue attribs, const std::string& saved
|
|||
co_return game->LoadVisualReplayData();
|
||||
}, this), L"Loading visual replay data", 1000);
|
||||
|
||||
// Call the script function InitGame only for new games, not saved games
|
||||
if (!isSavedGame)
|
||||
{
|
||||
// Perform some simulation initializations (replace skirmish entities, explore territories, etc.)
|
||||
// that needs to be done before setting up the AI and shouldn't be done in Atlas
|
||||
if (!g_AtlasGameLoop->running)
|
||||
{
|
||||
PS::Loader::Register(std::bind_front([](CGame* game) -> PS::Loader::Task
|
||||
{
|
||||
game->m_Simulation2->PreInitGame();
|
||||
co_return 0;
|
||||
}, this), L"PreInitGame", 5000);
|
||||
}
|
||||
|
||||
PS::Loader::Register(std::bind_front([](CGame* game) -> PS::Loader::Task
|
||||
{
|
||||
game->m_Simulation2->InitGame();
|
||||
co_return 0;
|
||||
}, this), L"InitGame", 4000);
|
||||
}
|
||||
|
||||
PS::Loader::EndRegistering();
|
||||
}
|
||||
|
||||
int CGame::LoadInitialState(const std::string& savedState)
|
||||
{
|
||||
ENSURE(m_IsSavedGame);
|
||||
|
||||
std::stringstream stream(savedState);
|
||||
|
||||
bool ok = m_Simulation2->DeserializeState(stream);
|
||||
|
|
@ -324,17 +342,6 @@ int CGame::LoadInitialState(const std::string& savedState)
|
|||
**/
|
||||
PSRETURN CGame::ReallyStartGame()
|
||||
{
|
||||
// Call the script function InitGame only for new games, not saved games
|
||||
if (!m_IsSavedGame)
|
||||
{
|
||||
// Perform some simulation initializations (replace skirmish entities, explore territories, etc.)
|
||||
// that needs to be done before setting up the AI and shouldn't be done in Atlas
|
||||
if (!g_AtlasGameLoop->running)
|
||||
m_Simulation2->PreInitGame();
|
||||
|
||||
m_Simulation2->InitGame();
|
||||
}
|
||||
|
||||
// We need to do an initial Interpolate call to set up all the models etc,
|
||||
// because Update might never interpolate (e.g. if the game starts paused)
|
||||
// and we could end up rendering before having set up any models (so they'd
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* 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
|
||||
|
|
@ -226,7 +226,6 @@ private:
|
|||
std::vector<CColor> m_PlayerColors;
|
||||
|
||||
int LoadInitialState(const std::string& savedState);
|
||||
bool m_IsSavedGame; // true if loading a saved game; false for a new game
|
||||
|
||||
bool m_CheatsEnabled;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* 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
|
||||
|
|
@ -27,6 +27,7 @@
|
|||
#include <SDL_keycode.h>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <fmt/format.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
|
@ -122,7 +123,7 @@ CStr FindScancodeName(SDL_Scancode scancode)
|
|||
const char* name = SDL_GetScancodeName(scancode);
|
||||
// Some scancodes have no name, but we must have something to save/load/recognize it, so parse it as SYM_XX
|
||||
if (strlen(name) == 0)
|
||||
return CStr("SYM_") + CStr::FromInt(scancode);
|
||||
return fmt::format("SYM_{}", static_cast<int>(scancode));
|
||||
return name;
|
||||
}
|
||||
|
||||
|
|
@ -221,7 +222,7 @@ CStr FindKeyName(SDL_Scancode scancode)
|
|||
return name;
|
||||
|
||||
// Else, show something regardless, so the player knows it's at least recognized.
|
||||
return CStr("SYM_") + CStr::FromInt(scancode);
|
||||
return fmt::format("SYM_{}", static_cast<int>(scancode));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -517,7 +517,7 @@ void rewriteBuffer(u8* buffer, u32& bufferSize)
|
|||
std::string basic = attrib;
|
||||
std::map<std::string, double>::iterator time_attrib = time_per_attribute.find(attrib);
|
||||
if (time_attrib != time_per_attribute.end())
|
||||
basic += " " + CStr::FromInt(1000000*time_attrib->second) + "us";
|
||||
basic = fmt::format("{} {}us", basic, 1000000 * time_attrib->second);
|
||||
|
||||
u32 length = static_cast<u32>(basic.size());
|
||||
memcpy(buffer + writePos, &length, sizeof(length));
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* 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
|
||||
|
|
@ -44,6 +44,7 @@
|
|||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <deque>
|
||||
#include <fmt/format.h>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
|
@ -337,7 +338,7 @@ private:
|
|||
{
|
||||
long code = -1;
|
||||
curl_easy_getinfo(m_Curl, CURLINFO_RESPONSE_CODE, &code);
|
||||
SetStatus("completed:" + CStr::FromInt(code));
|
||||
SetStatus(fmt::format("completed:{}", code));
|
||||
|
||||
// Check for success code
|
||||
if (code == 200)
|
||||
|
|
@ -360,7 +361,7 @@ private:
|
|||
if (errorString.empty())
|
||||
errorString = curl_easy_strerror(err);
|
||||
|
||||
SetStatus("failed:" + CStr::FromInt(err) + ":" + errorString);
|
||||
SetStatus(fmt::format("failed:{}:{}", static_cast<int>(err), errorString));
|
||||
}
|
||||
|
||||
// We got an unhandled return code or a connection failure;
|
||||
|
|
@ -385,12 +386,12 @@ private:
|
|||
r += "user_id=";
|
||||
AppendEscaped(r, m_UserID);
|
||||
|
||||
r += "&time=" + CStr::FromInt64(report.m_Time);
|
||||
r = fmt::format("{}&time={}", std::move(r), report.m_Time);
|
||||
|
||||
r += "&type=";
|
||||
AppendEscaped(r, report.m_Type);
|
||||
|
||||
r += "&version=" + CStr::FromInt(report.m_Version);
|
||||
r = fmt::format("{}&version={}", std::move(r), report.m_Version);
|
||||
|
||||
r += "&data=";
|
||||
AppendEscaped(r, report.m_Data);
|
||||
|
|
@ -529,7 +530,7 @@ bool CUserReporter::IsReportingEnabled()
|
|||
|
||||
void CUserReporter::SetReportingEnabled(bool enabled)
|
||||
{
|
||||
CStr val = CStr::FromInt(enabled ? REPORTER_VERSION : 0);
|
||||
const std::string val{std::to_string(enabled ? REPORTER_VERSION : 0)};
|
||||
g_ConfigDB.SetValueString(CFG_USER, "userreport.enabledversion", val);
|
||||
g_ConfigDB.WriteValueToFile(CFG_USER, "userreport.enabledversion", val);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* 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
|
||||
|
|
@ -33,7 +33,7 @@ namespace Renderer::Backend { class IDeviceCommandContext; }
|
|||
* This computes and binds per-vertex data; the modifier is responsible
|
||||
* for setting any shader uniforms etc.
|
||||
*/
|
||||
class CPUSkinnedModelVertexRenderer : public ModelVertexRenderer
|
||||
class CPUSkinnedModelVertexRenderer final : public ModelVertexRenderer
|
||||
{
|
||||
public:
|
||||
CPUSkinnedModelVertexRenderer();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* 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
|
||||
|
|
@ -34,7 +34,7 @@ namespace Renderer::Backend { class IShaderProgram; }
|
|||
* This computes and binds per-vertex data; the modifier is responsible
|
||||
* for setting any shader uniforms etc.
|
||||
*/
|
||||
class GPUSkinnedModelModelRenderer : public ModelVertexRenderer
|
||||
class GPUSkinnedModelModelRenderer final : public ModelVertexRenderer
|
||||
{
|
||||
public:
|
||||
GPUSkinnedModelModelRenderer();
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ struct InstancingModelRendererInternals;
|
|||
* This computes and binds per-vertex data; the modifier is responsible
|
||||
* for setting any shader uniforms etc (including the instancing transform).
|
||||
*/
|
||||
class InstancingModelRenderer : public ModelVertexRenderer
|
||||
class InstancingModelRenderer final : public ModelVertexRenderer
|
||||
{
|
||||
public:
|
||||
InstancingModelRenderer();
|
||||
|
|
|
|||
|
|
@ -102,15 +102,11 @@ CMaterial::Pass GetMaterialPassFromCullGroup(const int cullGroup, const ERenderM
|
|||
|
||||
}
|
||||
|
||||
void ModelRenderer::Init()
|
||||
{
|
||||
}
|
||||
|
||||
// Helper function to copy object-space position and normal vectors into arrays.
|
||||
// static
|
||||
void ModelRenderer::CopyPositionAndNormals(
|
||||
const CModelDefPtr& mdef,
|
||||
const VertexArrayIterator<CVector3D>& Position,
|
||||
const VertexArrayIterator<CVector3D>& Normal)
|
||||
const CModelDefPtr& mdef,
|
||||
const VertexArrayIterator<CVector3D>& Position,
|
||||
const VertexArrayIterator<CVector3D>& Normal)
|
||||
{
|
||||
size_t numVertices = mdef->GetNumVertices();
|
||||
SModelVertex* vertices = mdef->GetVertices();
|
||||
|
|
@ -122,11 +118,11 @@ void ModelRenderer::CopyPositionAndNormals(
|
|||
}
|
||||
}
|
||||
|
||||
// Helper function to transform position and normal vectors into world-space.
|
||||
// static
|
||||
void ModelRenderer::BuildPositionAndNormals(
|
||||
CModel* model,
|
||||
const VertexArrayIterator<CVector3D>& Position,
|
||||
const VertexArrayIterator<CVector3D>& Normal)
|
||||
CModel* model,
|
||||
const VertexArrayIterator<CVector3D>& Position,
|
||||
const VertexArrayIterator<CVector3D>& Normal)
|
||||
{
|
||||
CModelDefPtr mdef = model->GetModelDef();
|
||||
size_t numVertices = mdef->GetNumVertices();
|
||||
|
|
@ -160,43 +156,16 @@ void ModelRenderer::BuildPositionAndNormals(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// Helper function for lighting
|
||||
void ModelRenderer::BuildColor4ub(
|
||||
CModel* model,
|
||||
const VertexArrayIterator<CVector3D>& Normal,
|
||||
const VertexArrayIterator<SColor4ub>& Color)
|
||||
{
|
||||
PROFILE("lighting vertices");
|
||||
|
||||
CModelDefPtr mdef = model->GetModelDef();
|
||||
size_t numVertices = mdef->GetNumVertices();
|
||||
const CLightEnv& lightEnv = g_Renderer.GetSceneRenderer().GetLightEnv();
|
||||
CColor shadingColor = model->GetShadingColor();
|
||||
|
||||
for (size_t j = 0; j < numVertices; ++j)
|
||||
{
|
||||
RGBColor tempcolor = lightEnv.EvaluateUnitScaled(Normal[j]);
|
||||
tempcolor.X *= shadingColor.r;
|
||||
tempcolor.Y *= shadingColor.g;
|
||||
tempcolor.Z *= shadingColor.b;
|
||||
Color[j] = ConvertRGBColorTo4ub(tempcolor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
void ModelRenderer::GenTangents(const CModelDefPtr& mdef, std::vector<float>& newVertices, bool gpuSkinning)
|
||||
{
|
||||
MikkTSpace ms(mdef, newVertices, gpuSkinning);
|
||||
ms.Generate();
|
||||
}
|
||||
|
||||
|
||||
// Copy UV coordinates
|
||||
// static
|
||||
void ModelRenderer::BuildUV(
|
||||
const CModelDefPtr& mdef,
|
||||
const VertexArrayIterator<float[2]>& UV,
|
||||
int UVset)
|
||||
const CModelDefPtr& mdef, const VertexArrayIterator<float[2]>& UV, int UVset)
|
||||
{
|
||||
const size_t numVertices = mdef->GetNumVertices();
|
||||
const size_t numberOfUVPerVertex = mdef->GetNumUVsPerVertex();
|
||||
|
|
@ -209,11 +178,9 @@ void ModelRenderer::BuildUV(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// Build default indices array.
|
||||
// static
|
||||
void ModelRenderer::BuildIndices(
|
||||
const CModelDefPtr& mdef,
|
||||
const VertexArrayIterator<u16>& Indices)
|
||||
const CModelDefPtr& mdef, const VertexArrayIterator<u16>& Indices)
|
||||
{
|
||||
size_t idxidx = 0;
|
||||
SModelFace* faces = mdef->GetFaces();
|
||||
|
|
@ -227,105 +194,6 @@ void ModelRenderer::BuildIndices(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ShaderModelRenderer implementation
|
||||
|
||||
|
||||
/**
|
||||
* Internal data of the ShaderModelRenderer.
|
||||
*
|
||||
* Separated into the source file to increase implementation hiding (and to
|
||||
* avoid some causes of recompiles).
|
||||
*/
|
||||
struct ShaderModelRenderer::ShaderModelRendererInternals
|
||||
{
|
||||
ShaderModelRendererInternals(ShaderModelRenderer* r) : m_Renderer(r) { }
|
||||
|
||||
/// Back-link to "our" renderer
|
||||
ShaderModelRenderer* m_Renderer;
|
||||
|
||||
/// ModelVertexRenderer used for vertex transformations
|
||||
ModelVertexRendererPtr vertexRenderer;
|
||||
|
||||
/// List of submitted models for rendering in this frame
|
||||
std::vector<CModel*> submissions[CSceneRenderer::CULL_MAX];
|
||||
};
|
||||
|
||||
|
||||
// Construction/Destruction
|
||||
ShaderModelRenderer::ShaderModelRenderer(ModelVertexRendererPtr vertexrenderer)
|
||||
{
|
||||
m = new ShaderModelRendererInternals(this);
|
||||
m->vertexRenderer = vertexrenderer;
|
||||
}
|
||||
|
||||
ShaderModelRenderer::~ShaderModelRenderer()
|
||||
{
|
||||
delete m;
|
||||
}
|
||||
|
||||
// Submit one model.
|
||||
void ShaderModelRenderer::Submit(int cullGroup, CModel* model)
|
||||
{
|
||||
CModelRData* rdata = (CModelRData*)model->GetRenderData();
|
||||
|
||||
// Ensure model data is valid
|
||||
const void* key = m->vertexRenderer.get();
|
||||
if (!rdata || rdata->GetKey() != key)
|
||||
{
|
||||
model->InvalidatePosition();
|
||||
rdata = m->vertexRenderer->CreateModelData(key, model);
|
||||
model->SetRenderData(rdata);
|
||||
model->SetDirty(~0u);
|
||||
}
|
||||
|
||||
m->submissions[cullGroup].push_back(model);
|
||||
}
|
||||
|
||||
|
||||
// Call update for all submitted models and enter the rendering phase
|
||||
void ShaderModelRenderer::PrepareModels(
|
||||
Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
|
||||
{
|
||||
for (int cullGroup = 0; cullGroup < CSceneRenderer::CULL_MAX; ++cullGroup)
|
||||
{
|
||||
for (CModel* model : m->submissions[cullGroup])
|
||||
{
|
||||
model->ValidatePosition();
|
||||
|
||||
CModelRData* rdata = static_cast<CModelRData*>(model->GetRenderData());
|
||||
ENSURE(rdata->GetKey() == m->vertexRenderer.get());
|
||||
}
|
||||
|
||||
m->vertexRenderer->UpdateModelsData(deviceCommandContext, m->submissions[cullGroup]);
|
||||
|
||||
for (CModel* model : m->submissions[cullGroup])
|
||||
{
|
||||
CModelRData* rdata = static_cast<CModelRData*>(model->GetRenderData());
|
||||
rdata->m_UpdateFlags = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderModelRenderer::UploadModels(
|
||||
Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
|
||||
{
|
||||
for (int cullGroup = 0; cullGroup < CSceneRenderer::CULL_MAX; ++cullGroup)
|
||||
{
|
||||
m->vertexRenderer->UploadModelsData(deviceCommandContext, m->submissions[cullGroup]);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the submissions list
|
||||
void ShaderModelRenderer::EndFrame()
|
||||
{
|
||||
for (int cullGroup = 0; cullGroup < CSceneRenderer::CULL_MAX; ++cullGroup)
|
||||
m->submissions[cullGroup].clear();
|
||||
}
|
||||
|
||||
|
||||
// Helper structs for ShaderModelRenderer::Render():
|
||||
|
||||
struct SMRSortByDistItem
|
||||
|
|
@ -413,12 +281,12 @@ struct SMRCompareTechBucket
|
|||
}
|
||||
};
|
||||
|
||||
void ShaderModelRenderer::Render(
|
||||
void ModelRenderer::Render(
|
||||
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
|
||||
const RenderModifierPtr& modifier, const CShaderDefines& context,
|
||||
int cullGroup, int flags, const ERenderMode renderMode)
|
||||
ModelVertexRenderer& modelVertexRenderer, RenderModifier& modifier, const CShaderDefines& context,
|
||||
int cullGroup, int flags, const ERenderMode renderMode, std::span<CModel*> submissions)
|
||||
{
|
||||
if (m->submissions[cullGroup].empty())
|
||||
if (submissions.empty())
|
||||
return;
|
||||
|
||||
CMatrix3D worldToCam;
|
||||
|
|
@ -429,7 +297,7 @@ void ShaderModelRenderer::Render(
|
|||
/*
|
||||
* Rendering approach:
|
||||
*
|
||||
* m->submissions contains the list of CModels to render.
|
||||
* submissions contains the list of CModels to render.
|
||||
*
|
||||
* The data we need to render a model is:
|
||||
* - CShaderTechnique
|
||||
|
|
@ -497,10 +365,9 @@ void ShaderModelRenderer::Render(
|
|||
{
|
||||
PROFILE3("bucketing by material");
|
||||
|
||||
for (size_t i = 0; i < m->submissions[cullGroup].size(); ++i)
|
||||
for (CModel* model : submissions)
|
||||
{
|
||||
CModel* model = m->submissions[cullGroup][i];
|
||||
const CMaterial material{model->GetMaterial()};
|
||||
const CMaterial& material{model->GetMaterial()};
|
||||
const CShaderDefines& defines{material.GetShaderDefines()};
|
||||
const CStrIntern shaderEffect{material.GetShaderEffect(materialPass)};
|
||||
SMRMaterialBucketKey key(shaderEffect, defines);
|
||||
|
|
@ -683,7 +550,7 @@ void ShaderModelRenderer::Render(
|
|||
|
||||
Renderer::Backend::IShaderProgram* shader = currentTech->GetShader(pass);
|
||||
|
||||
modifier->BeginPass(deviceCommandContext, shader);
|
||||
modifier.BeginPass(deviceCommandContext, shader);
|
||||
|
||||
// TODO: Use a more generic approach to handle bound queries.
|
||||
bool boundTime = false;
|
||||
|
|
@ -753,7 +620,7 @@ void ShaderModelRenderer::Render(
|
|||
if (newModeldef != currentModeldef)
|
||||
{
|
||||
currentModeldef = newModeldef;
|
||||
m->vertexRenderer->PrepareModelDef(deviceCommandContext, *currentModeldef);
|
||||
modelVertexRenderer.PrepareModelDef(deviceCommandContext, *currentModeldef);
|
||||
}
|
||||
|
||||
// Bind all uniforms when any change
|
||||
|
|
@ -813,12 +680,12 @@ void ShaderModelRenderer::Render(
|
|||
}
|
||||
}
|
||||
|
||||
modifier->PrepareModel(deviceCommandContext, model);
|
||||
modifier.PrepareModel(deviceCommandContext, model);
|
||||
|
||||
CModelRData* rdata = static_cast<CModelRData*>(model->GetRenderData());
|
||||
ENSURE(rdata->GetKey() == m->vertexRenderer.get());
|
||||
ENSURE(rdata->GetKey() == &modelVertexRenderer);
|
||||
|
||||
m->vertexRenderer->RenderModel(deviceCommandContext, shader, model, rdata);
|
||||
modelVertexRenderer.RenderModel(deviceCommandContext, shader, model, rdata);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include "lib/types.h"
|
||||
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
class CModel;
|
||||
|
|
@ -39,17 +40,8 @@ namespace Renderer::Backend { class IDeviceCommandContext; }
|
|||
struct SColor4ub;
|
||||
template <typename T> class VertexArrayIterator;
|
||||
|
||||
class RenderModifier;
|
||||
typedef std::shared_ptr<RenderModifier> RenderModifierPtr;
|
||||
|
||||
class LitRenderModifier;
|
||||
typedef std::shared_ptr<LitRenderModifier> LitRenderModifierPtr;
|
||||
|
||||
class ModelVertexRenderer;
|
||||
typedef std::shared_ptr<ModelVertexRenderer> ModelVertexRendererPtr;
|
||||
|
||||
class ModelRenderer;
|
||||
typedef std::shared_ptr<ModelRenderer> ModelRendererPtr;
|
||||
class RenderModifier;
|
||||
|
||||
/**
|
||||
* Class CModelRData: Render data that is maintained per CModel.
|
||||
|
|
@ -83,25 +75,12 @@ private:
|
|||
|
||||
|
||||
/**
|
||||
* Class ModelRenderer: Abstract base class for all model renders.
|
||||
* ModelRenderer renders a per-frame list of models. It loads the appropriate
|
||||
* shaders for rendering each model, and that batches by shader technique (and
|
||||
* by mesh and texture).
|
||||
*
|
||||
* A ModelRenderer manages a per-frame list of models.
|
||||
*
|
||||
* It is supposed to be derived in order to create new ways in which
|
||||
* the per-frame list of models can be managed (for batching, for
|
||||
* transparent rendering, etc.) or potentially for rarely used special
|
||||
* effects.
|
||||
*
|
||||
* A typical ModelRenderer will delegate vertex transformation/setup
|
||||
* to a ModelVertexRenderer.
|
||||
* It will delegate fragment stage setup to a RenderModifier.
|
||||
*
|
||||
* For most purposes, you should use a BatchModelRenderer with
|
||||
* specialized ModelVertexRenderer and RenderModifier implementations.
|
||||
*
|
||||
* It is suggested that a derived class implement the provided generic
|
||||
* Render function, however in some cases it may be necessary to supply
|
||||
* a Render function with a different prototype.
|
||||
* ModelRenderer delegates vertex transformation/setup to a
|
||||
* ModelVertexRenderer. It delegates fragment stage setup to a RenderModifier.
|
||||
*
|
||||
* ModelRenderer also contains a number of static helper functions
|
||||
* for building vertex arrays.
|
||||
|
|
@ -109,61 +88,10 @@ private:
|
|||
class ModelRenderer
|
||||
{
|
||||
public:
|
||||
ModelRenderer() { }
|
||||
virtual ~ModelRenderer() { }
|
||||
|
||||
/**
|
||||
* Initialise global settings.
|
||||
* Should be called before using the class.
|
||||
*/
|
||||
static void Init();
|
||||
|
||||
/**
|
||||
* Submit: Submit a model for rendering this frame.
|
||||
*
|
||||
* preconditions : The model must not have been submitted to any
|
||||
* ModelRenderer in this frame. Submit may only be called
|
||||
* after EndFrame and before PrepareModels.
|
||||
*
|
||||
* @param model The model that will be added to the list of models
|
||||
* submitted this frame.
|
||||
*/
|
||||
virtual void Submit(int cullGroup, CModel* model) = 0;
|
||||
|
||||
/**
|
||||
* PrepareModels: Calculate renderer data for all previously
|
||||
* submitted models.
|
||||
*
|
||||
* Must be called before any rendering calls and after all models
|
||||
* for this frame have been submitted.
|
||||
*/
|
||||
virtual void PrepareModels(
|
||||
Renderer::Backend::IDeviceCommandContext* deviceCommandContext) = 0;
|
||||
|
||||
/**
|
||||
* Upload renderer data for all previously submitted models to backend.
|
||||
*
|
||||
* Must be called before any rendering calls and after all models
|
||||
* for this frame have been prepared.
|
||||
*/
|
||||
virtual void UploadModels(
|
||||
Renderer::Backend::IDeviceCommandContext* deviceCommandContext) = 0;
|
||||
|
||||
/**
|
||||
* EndFrame: Remove all models from the list of submitted
|
||||
* models.
|
||||
*/
|
||||
virtual void EndFrame() = 0;
|
||||
|
||||
/**
|
||||
* Render: Render submitted models, using the given RenderModifier to setup
|
||||
* the fragment stage.
|
||||
*
|
||||
* @note It is suggested that derived model renderers implement and use
|
||||
* this Render functions. However, a highly specialized model renderer
|
||||
* may need to "disable" this function and provide its own Render function
|
||||
* with a different prototype.
|
||||
*
|
||||
* preconditions : PrepareModels must be called after all models have been
|
||||
* submitted and before calling Render.
|
||||
*
|
||||
|
|
@ -172,10 +100,10 @@ public:
|
|||
* If flags is non-zero, only models that contain flags in their
|
||||
* CModel::GetFlags() are rendered.
|
||||
*/
|
||||
virtual void Render(
|
||||
void Render(
|
||||
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
|
||||
const RenderModifierPtr& modifier, const CShaderDefines& context,
|
||||
int cullGroup, int flags, const ERenderMode renderMode) = 0;
|
||||
ModelVertexRenderer& modelVertexRenderer, RenderModifier& modifier, const CShaderDefines& context,
|
||||
int cullGroup, int flags, const ERenderMode renderMode, std::span<CModel*> submissions);
|
||||
|
||||
/**
|
||||
* CopyPositionAndNormals: Copy unanimated object-space vertices and
|
||||
|
|
@ -190,9 +118,9 @@ public:
|
|||
* The array behind the iterator must be as large as the Position array.
|
||||
*/
|
||||
static void CopyPositionAndNormals(
|
||||
const CModelDefPtr& mdef,
|
||||
const VertexArrayIterator<CVector3D>& Position,
|
||||
const VertexArrayIterator<CVector3D>& Normal);
|
||||
const CModelDefPtr& mdef,
|
||||
const VertexArrayIterator<CVector3D>& Position,
|
||||
const VertexArrayIterator<CVector3D>& Normal);
|
||||
|
||||
/**
|
||||
* BuildPositionAndNormals: Build animated vertices and normals,
|
||||
|
|
@ -209,25 +137,9 @@ public:
|
|||
* the Position array.
|
||||
*/
|
||||
static void BuildPositionAndNormals(
|
||||
CModel* model,
|
||||
const VertexArrayIterator<CVector3D>& Position,
|
||||
const VertexArrayIterator<CVector3D>& Normal);
|
||||
|
||||
/**
|
||||
* BuildColor4ub: Build lighting colors for the given model,
|
||||
* based on previously calculated world space normals.
|
||||
*
|
||||
* @param model The model that is to be lit.
|
||||
* @param Normal Array of the model's normal vectors, animated and
|
||||
* transformed into world space.
|
||||
* @param Color Points to the array that will receive the lit vertex color.
|
||||
* The array behind the iterator must large enough to hold
|
||||
* model->GetModelDef()->GetNumVertices() vertices.
|
||||
*/
|
||||
static void BuildColor4ub(
|
||||
CModel* model,
|
||||
const VertexArrayIterator<CVector3D>& Normal,
|
||||
const VertexArrayIterator<SColor4ub>& Color);
|
||||
CModel* model,
|
||||
const VertexArrayIterator<CVector3D>& Position,
|
||||
const VertexArrayIterator<CVector3D>& Normal);
|
||||
|
||||
/**
|
||||
* BuildUV: Copy UV coordinates into the given vertex array.
|
||||
|
|
@ -238,9 +150,9 @@ public:
|
|||
* mdef->GetNumVertices() vertices.
|
||||
*/
|
||||
static void BuildUV(
|
||||
const CModelDefPtr& mdef,
|
||||
const VertexArrayIterator<float[2]>& UV,
|
||||
int UVset);
|
||||
const CModelDefPtr& mdef,
|
||||
const VertexArrayIterator<float[2]>& UV,
|
||||
int UVset);
|
||||
|
||||
/**
|
||||
* BuildIndices: Create the indices array for the given CModelDef.
|
||||
|
|
@ -250,8 +162,7 @@ public:
|
|||
* mdef->GetNumFaces()*3 elements.
|
||||
*/
|
||||
static void BuildIndices(
|
||||
const CModelDefPtr& mdef,
|
||||
const VertexArrayIterator<u16>& Indices);
|
||||
const CModelDefPtr& mdef, const VertexArrayIterator<u16>& Indices);
|
||||
|
||||
/**
|
||||
* GenTangents: Generate tangents for the given CModelDef.
|
||||
|
|
@ -263,33 +174,4 @@ public:
|
|||
static void GenTangents(const CModelDefPtr& mdef, std::vector<float>& newVertices, bool gpuSkinning);
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of ModelRenderer that loads the appropriate shaders for
|
||||
* rendering each model, and that batches by shader technique (and by mesh and texture).
|
||||
*/
|
||||
class ShaderModelRenderer : public ModelRenderer
|
||||
{
|
||||
friend struct ShaderModelRendererInternals;
|
||||
|
||||
public:
|
||||
ShaderModelRenderer(ModelVertexRendererPtr vertexrender);
|
||||
~ShaderModelRenderer() override;
|
||||
|
||||
// Batching implementations
|
||||
void Submit(int cullGroup, CModel* model) override;
|
||||
void PrepareModels(
|
||||
Renderer::Backend::IDeviceCommandContext* deviceCommandContext) override;
|
||||
void UploadModels(
|
||||
Renderer::Backend::IDeviceCommandContext* deviceCommandContext) override;
|
||||
void EndFrame() override;
|
||||
void Render(
|
||||
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
|
||||
const RenderModifierPtr& modifier, const CShaderDefines& context,
|
||||
int cullGroup, int flags, const ERenderMode renderMode) override;
|
||||
|
||||
private:
|
||||
struct ShaderModelRendererInternals;
|
||||
ShaderModelRendererInternals* m;
|
||||
};
|
||||
|
||||
#endif // INCLUDED_MODELRENDERER
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@
|
|||
#include "ps/VideoMode.h"
|
||||
#include "ps/World.h"
|
||||
#include "renderer/DebugRenderer.h"
|
||||
#include "renderer/ModelRenderer.h"
|
||||
#include "renderer/PostprocManager.h"
|
||||
#include "renderer/RenderingOptions.h"
|
||||
#include "renderer/SceneRenderer.h"
|
||||
|
|
@ -456,7 +455,6 @@ CRenderer::CRenderer(Renderer::Backend::IDevice* device)
|
|||
|
||||
ModelDefActivateFastImpl();
|
||||
ColorActivateFastImpl();
|
||||
ModelRenderer::Init();
|
||||
}
|
||||
|
||||
CRenderer::~CRenderer()
|
||||
|
|
|
|||
|
|
@ -212,17 +212,17 @@ void CRenderingOptions::ReadConfigAndSetupHooks()
|
|||
m_ConfigHooks->Setup("silhouettes", m_Silhouettes);
|
||||
|
||||
m_ConfigHooks->Setup("gpuskinning", [this]() {
|
||||
const Renderer::Backend::IDevice::Capabilities& capabilities{
|
||||
g_VideoMode.GetBackendDevice()->GetCapabilities()};
|
||||
if (!g_ConfigDB.Get("gpuskinning", false))
|
||||
return;
|
||||
|
||||
if (capabilities.computeShaders && capabilities.storage)
|
||||
m_GPUSkinning = true;
|
||||
else
|
||||
if (g_ConfigDB.Get("gpuskinning", false))
|
||||
{
|
||||
m_GPUSkinning = false;
|
||||
LOGMESSAGE("GPU skinning isn't supported on the current hardware.");
|
||||
const Renderer::Backend::IDevice::Capabilities& capabilities{
|
||||
g_VideoMode.GetBackendDevice()->GetCapabilities()};
|
||||
if (capabilities.computeShaders && capabilities.storage)
|
||||
m_GPUSkinning = true;
|
||||
else
|
||||
{
|
||||
m_GPUSkinning = false;
|
||||
LOGMESSAGE("GPU skinning isn't supported on the current hardware.");
|
||||
}
|
||||
}
|
||||
|
||||
if (CRenderer::IsInitialised())
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <optional>
|
||||
|
||||
struct SScreenRect
|
||||
{
|
||||
|
|
@ -115,78 +116,192 @@ public:
|
|||
|
||||
SilhouetteRenderer silhouetteRenderer;
|
||||
|
||||
/// Various model renderers
|
||||
// Various model renderers
|
||||
struct Models
|
||||
{
|
||||
// NOTE: The current renderer design (with ModelRenderer, ModelVertexRenderer,
|
||||
// RenderModifier, etc) is mostly a relic of an older design that implemented
|
||||
// the different materials and rendering modes through extensive subclassing
|
||||
// and hooking objects together in various combinations.
|
||||
// The new design uses the CShaderManager API to abstract away the details
|
||||
// of rendering, and uses a data-driven approach to materials, so there are
|
||||
// now a small number of generic subclasses instead of many specialised subclasses,
|
||||
// but most of the old infrastructure hasn't been refactored out yet and leads to
|
||||
// some unwanted complexity.
|
||||
|
||||
// Submitted models are split on two axes:
|
||||
// - Normal vs Transp[arent] - alpha-blended models are stored in a separate
|
||||
// - Opaque vs Transparent - alpha-blended models are stored in a separate
|
||||
// list so we can draw them above/below the alpha-blended water plane correctly
|
||||
// - Skinned vs Unskinned - with hardware lighting we don't need to
|
||||
// duplicate mesh data per model instance (except for skinned models),
|
||||
// so non-skinned models get different ModelVertexRenderers
|
||||
// - Skinned vs Unskinned - we don't need to duplicate mesh data per
|
||||
// model instance (except for skinned models), so non-skinned models
|
||||
// get different ModelVertexRenderers
|
||||
|
||||
ModelRendererPtr NormalSkinned;
|
||||
ModelRendererPtr NormalUnskinned; // == NormalSkinned if unskinned shader instancing not supported
|
||||
ModelRendererPtr TranspSkinned;
|
||||
ModelRendererPtr TranspUnskinned; // == TranspSkinned if unskinned shader instancing not supported
|
||||
struct Submissions
|
||||
{
|
||||
std::vector<CModel*> submissions[CSceneRenderer::CULL_MAX];
|
||||
};
|
||||
|
||||
ModelVertexRendererPtr VertexRendererShader;
|
||||
ModelVertexRendererPtr VertexInstancingShader;
|
||||
ModelVertexRendererPtr VertexGPUSkinningShader;
|
||||
// Unskinned submissions should be prepared and rendered with
|
||||
// VertexInstancingShader. Skinned - with Vertex*SkinningShader
|
||||
// depending on whether GPU skinning is enabled.
|
||||
Submissions OpaqueSkinned;
|
||||
Submissions OpaqueUnskinned;
|
||||
Submissions TransparentSkinned;
|
||||
Submissions TransparentUnskinned;
|
||||
|
||||
LitRenderModifierPtr ModShader;
|
||||
ModelRenderer modelRenderer;
|
||||
|
||||
InstancingModelRenderer VertexInstancingShader;
|
||||
CPUSkinnedModelVertexRenderer VertexCPUSkinningShader;
|
||||
// We can't create GPU skinning renderer for renderer devices without
|
||||
// its support.
|
||||
std::optional<GPUSkinnedModelModelRenderer> VertexGPUSkinningShader;
|
||||
|
||||
ShaderRenderModifier ModShader;
|
||||
|
||||
bool GPUSkinningEnabled{false};
|
||||
} Model;
|
||||
|
||||
CShaderDefines globalContext;
|
||||
|
||||
/**
|
||||
* Upload renderer data for all previously submitted models to backend.
|
||||
*
|
||||
* Must be called before any rendering calls and after all models
|
||||
* for this frame have been prepared.
|
||||
*/
|
||||
void UploadModels(Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
|
||||
{
|
||||
PROFILE3("upload models");
|
||||
|
||||
ModelVertexRenderer& modelVertexSkinningRenderer{
|
||||
Model.GPUSkinningEnabled
|
||||
? static_cast<ModelVertexRenderer&>(*Model.VertexGPUSkinningShader)
|
||||
: static_cast<ModelVertexRenderer&>(Model.VertexCPUSkinningShader)};
|
||||
|
||||
for (int cullGroup{0}; cullGroup < CSceneRenderer::CULL_MAX; ++cullGroup)
|
||||
{
|
||||
modelVertexSkinningRenderer.UploadModelsData(deviceCommandContext, Model.OpaqueSkinned.submissions[cullGroup]);
|
||||
modelVertexSkinningRenderer.UploadModelsData(deviceCommandContext, Model.TransparentSkinned.submissions[cullGroup]);
|
||||
}
|
||||
|
||||
for (int cullGroup{0}; cullGroup < CSceneRenderer::CULL_MAX; ++cullGroup)
|
||||
{
|
||||
Model.VertexInstancingShader.UploadModelsData(deviceCommandContext, Model.OpaqueUnskinned.submissions[cullGroup]);
|
||||
Model.VertexInstancingShader.UploadModelsData(deviceCommandContext, Model.TransparentUnskinned.submissions[cullGroup]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PrepareModels: Calculate renderer data for all previously
|
||||
* submitted models.
|
||||
*
|
||||
* Must be called before any rendering calls and after all models
|
||||
* for this frame have been submitted.
|
||||
*/
|
||||
void PrepareModels(
|
||||
Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
|
||||
{
|
||||
PROFILE3("prepare models");
|
||||
|
||||
ModelVertexRenderer& modelVertexSkinningRenderer{
|
||||
Model.GPUSkinningEnabled
|
||||
? static_cast<ModelVertexRenderer&>(*Model.VertexGPUSkinningShader)
|
||||
: static_cast<ModelVertexRenderer&>(Model.VertexCPUSkinningShader)};
|
||||
|
||||
for (int cullGroup{0}; cullGroup < CSceneRenderer::CULL_MAX; ++cullGroup)
|
||||
{
|
||||
PrepareModels(deviceCommandContext, modelVertexSkinningRenderer, Model.OpaqueSkinned.submissions[cullGroup]);
|
||||
PrepareModels(deviceCommandContext, modelVertexSkinningRenderer, Model.TransparentSkinned.submissions[cullGroup]);
|
||||
}
|
||||
|
||||
for (int cullGroup{0}; cullGroup < CSceneRenderer::CULL_MAX; ++cullGroup)
|
||||
{
|
||||
PrepareModels(deviceCommandContext, Model.VertexInstancingShader, Model.OpaqueUnskinned.submissions[cullGroup]);
|
||||
PrepareModels(deviceCommandContext, Model.VertexInstancingShader, Model.TransparentUnskinned.submissions[cullGroup]);
|
||||
}
|
||||
}
|
||||
|
||||
void PrepareModels(
|
||||
Renderer::Backend::IDeviceCommandContext* deviceCommandContext, ModelVertexRenderer& modelVertexRenderer, std::span<CModel*> submissions)
|
||||
{
|
||||
for (CModel* model : submissions)
|
||||
{
|
||||
model->ValidatePosition();
|
||||
|
||||
CModelRData* rdata = static_cast<CModelRData*>(model->GetRenderData());
|
||||
ENSURE(rdata->GetKey() == &modelVertexRenderer);
|
||||
}
|
||||
|
||||
modelVertexRenderer.UpdateModelsData(deviceCommandContext, submissions);
|
||||
|
||||
for (CModel* model : submissions)
|
||||
{
|
||||
CModelRData* rdata = static_cast<CModelRData*>(model->GetRenderData());
|
||||
rdata->m_UpdateFlags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit: Submit a model for rendering this frame.
|
||||
*
|
||||
* preconditions : The model must not have been submitted to any
|
||||
* ModelRenderer in this frame. Submit may only be called
|
||||
* after EndFrame and before PrepareModels.
|
||||
*
|
||||
* @param model The model that will be added to the list of models
|
||||
* submitted this frame.
|
||||
*/
|
||||
void Submit(const int cullGroup, ModelVertexRenderer& modelVertexRenderer, Models::Submissions& submissions, CModel* model)
|
||||
{
|
||||
CModelRData* rdata{static_cast<CModelRData*>(model->GetRenderData())};
|
||||
|
||||
// Ensure model data is valid.
|
||||
// TODO: using a pointer as a key is unsafe.
|
||||
const void* key{&modelVertexRenderer};
|
||||
if (!rdata || rdata->GetKey() != key)
|
||||
{
|
||||
model->InvalidatePosition();
|
||||
rdata = modelVertexRenderer.CreateModelData(key, model);
|
||||
model->SetRenderData(rdata);
|
||||
model->SetDirty(~0u);
|
||||
}
|
||||
|
||||
submissions.submissions[cullGroup].push_back(model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders all non-alpha-blended models with the given context.
|
||||
*/
|
||||
void CallModelRenderers(
|
||||
void CallOpaqueModelRenderers(
|
||||
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
|
||||
const CShaderDefines& context, int cullGroup, int flags, const ERenderMode renderMode)
|
||||
{
|
||||
CShaderDefines contextSkinned = context;
|
||||
if (g_RenderingOptions.GetGPUSkinning())
|
||||
contextSkinned.Add(str_USE_INSTANCING, str_1);
|
||||
Model.NormalSkinned->Render(deviceCommandContext, Model.ModShader, contextSkinned, cullGroup, flags, renderMode);
|
||||
ModelVertexRenderer& modelVertexSkinningRenderer{
|
||||
Model.GPUSkinningEnabled
|
||||
? static_cast<ModelVertexRenderer&>(*Model.VertexGPUSkinningShader)
|
||||
: static_cast<ModelVertexRenderer&>(Model.VertexCPUSkinningShader)};
|
||||
|
||||
if (Model.NormalUnskinned != Model.NormalSkinned)
|
||||
{
|
||||
CShaderDefines contextUnskinned = context;
|
||||
contextUnskinned.Add(str_USE_INSTANCING, str_1);
|
||||
Model.NormalUnskinned->Render(deviceCommandContext, Model.ModShader, contextUnskinned, cullGroup, flags, renderMode);
|
||||
}
|
||||
CShaderDefines contextSkinned = context;
|
||||
if (Model.GPUSkinningEnabled)
|
||||
contextSkinned.Add(str_USE_INSTANCING, str_1);
|
||||
Model.modelRenderer.Render(deviceCommandContext, modelVertexSkinningRenderer, Model.ModShader, contextSkinned, cullGroup, flags, renderMode, Model.OpaqueSkinned.submissions[cullGroup]);
|
||||
|
||||
CShaderDefines contextUnskinned = context;
|
||||
contextUnskinned.Add(str_USE_INSTANCING, str_1);
|
||||
Model.modelRenderer.Render(deviceCommandContext, Model.VertexInstancingShader, Model.ModShader, contextUnskinned, cullGroup, flags, renderMode, Model.OpaqueUnskinned.submissions[cullGroup]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders all alpha-blended models with the given context.
|
||||
*/
|
||||
void CallTranspModelRenderers(
|
||||
void CallTransparentModelRenderers(
|
||||
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
|
||||
const CShaderDefines& context, int cullGroup, int flags, const ERenderMode renderMode)
|
||||
{
|
||||
CShaderDefines contextSkinned = context;
|
||||
if (g_RenderingOptions.GetGPUSkinning())
|
||||
contextSkinned.Add(str_USE_INSTANCING, str_1);
|
||||
Model.TranspSkinned->Render(deviceCommandContext, Model.ModShader, contextSkinned, cullGroup, flags, renderMode);
|
||||
ModelVertexRenderer& modelVertexSkinningRenderer{
|
||||
Model.GPUSkinningEnabled
|
||||
? static_cast<ModelVertexRenderer&>(*Model.VertexGPUSkinningShader)
|
||||
: static_cast<ModelVertexRenderer&>(Model.VertexCPUSkinningShader)};
|
||||
|
||||
if (Model.TranspUnskinned != Model.TranspSkinned)
|
||||
{
|
||||
CShaderDefines contextUnskinned = context;
|
||||
contextUnskinned.Add(str_USE_INSTANCING, str_1);
|
||||
Model.TranspUnskinned->Render(deviceCommandContext, Model.ModShader, contextUnskinned, cullGroup, flags, renderMode);
|
||||
}
|
||||
CShaderDefines contextSkinned = context;
|
||||
if (Model.GPUSkinningEnabled)
|
||||
contextSkinned.Add(str_USE_INSTANCING, str_1);
|
||||
Model.modelRenderer.Render(deviceCommandContext, modelVertexSkinningRenderer, Model.ModShader, contextSkinned, cullGroup, flags, renderMode, Model.TransparentSkinned.submissions[cullGroup]);
|
||||
|
||||
CShaderDefines contextUnskinned = context;
|
||||
contextUnskinned.Add(str_USE_INSTANCING, str_1);
|
||||
Model.modelRenderer.Render(deviceCommandContext, Model.VertexInstancingShader, Model.ModShader, contextUnskinned, cullGroup, flags, renderMode, Model.TransparentUnskinned.submissions[cullGroup]);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -239,26 +354,12 @@ void CSceneRenderer::ReloadShaders([[maybe_unused]] Renderer::Backend::IDevice*
|
|||
m->globalContext.Add(str_RENDER_DEBUG_MODE,
|
||||
RenderDebugModeEnum::ToString(g_RenderingOptions.GetRenderDebugMode()));
|
||||
|
||||
m->Model.ModShader = LitRenderModifierPtr(new ShaderRenderModifier());
|
||||
|
||||
m->Model.VertexRendererShader = ModelVertexRendererPtr(new CPUSkinnedModelVertexRenderer());
|
||||
m->Model.VertexInstancingShader = ModelVertexRendererPtr(new InstancingModelRenderer());
|
||||
|
||||
if (g_RenderingOptions.GetGPUSkinning())
|
||||
m->Model.GPUSkinningEnabled = g_RenderingOptions.GetGPUSkinning();
|
||||
if (m->Model.GPUSkinningEnabled)
|
||||
{
|
||||
m->Model.VertexGPUSkinningShader = ModelVertexRendererPtr(new GPUSkinnedModelModelRenderer());
|
||||
m->Model.NormalSkinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexGPUSkinningShader));
|
||||
m->Model.TranspSkinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexGPUSkinningShader));
|
||||
if (!m->Model.VertexGPUSkinningShader.has_value())
|
||||
m->Model.VertexGPUSkinningShader.emplace();
|
||||
}
|
||||
else
|
||||
{
|
||||
m->Model.VertexGPUSkinningShader.reset();
|
||||
m->Model.NormalSkinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexRendererShader));
|
||||
m->Model.TranspSkinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexRendererShader));
|
||||
}
|
||||
|
||||
m->Model.NormalUnskinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexInstancingShader));
|
||||
m->Model.TranspUnskinned = ModelRendererPtr(new ShaderModelRenderer(m->Model.VertexInstancingShader));
|
||||
}
|
||||
|
||||
void CSceneRenderer::Initialize()
|
||||
|
|
@ -282,8 +383,8 @@ void CSceneRenderer::Resize(int /*width*/, int /*height*/)
|
|||
void CSceneRenderer::BeginFrame()
|
||||
{
|
||||
// choose model renderers for this frame
|
||||
m->Model.ModShader->SetShadowMap(&m->shadow);
|
||||
m->Model.ModShader->SetLightEnv(m_LightEnv);
|
||||
m->Model.ModShader.SetShadowMap(&m->shadow);
|
||||
m->Model.ModShader.SetLightEnv(m_LightEnv);
|
||||
}
|
||||
|
||||
void CSceneRenderer::SetSimulation(CSimulation2* simulation)
|
||||
|
|
@ -315,12 +416,12 @@ void CSceneRenderer::RenderShadowMap(
|
|||
|
||||
{
|
||||
PROFILE("render models");
|
||||
m->CallModelRenderers(deviceCommandContext, {}, cullGroup, ModelFlag::CAST_SHADOWS, ERenderMode::SOLID);
|
||||
m->CallOpaqueModelRenderers(deviceCommandContext, {}, cullGroup, ModelFlag::CAST_SHADOWS, ERenderMode::SOLID);
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE("render transparent models");
|
||||
m->CallTranspModelRenderers(deviceCommandContext, {}, cullGroup, ModelFlag::CAST_SHADOWS, ERenderMode::SOLID);
|
||||
m->CallTransparentModelRenderers(deviceCommandContext, {}, cullGroup, ModelFlag::CAST_SHADOWS, ERenderMode::SOLID);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -363,10 +464,10 @@ void CSceneRenderer::RenderModels(
|
|||
const ERenderMode modelRenderMode{
|
||||
m_ModelRenderMode == WIREFRAME ? WIREFRAME : SOLID};
|
||||
|
||||
m->CallModelRenderers(deviceCommandContext, context, cullGroup, flags, modelRenderMode);
|
||||
m->CallOpaqueModelRenderers(deviceCommandContext, context, cullGroup, flags, modelRenderMode);
|
||||
|
||||
if (m_ModelRenderMode == EDGED_FACES)
|
||||
m->CallModelRenderers(deviceCommandContext, {}, cullGroup, flags, EDGED_FACES);
|
||||
m->CallOpaqueModelRenderers(deviceCommandContext, {}, cullGroup, flags, EDGED_FACES);
|
||||
}
|
||||
|
||||
void CSceneRenderer::RenderTransparentModels(
|
||||
|
|
@ -388,13 +489,13 @@ void CSceneRenderer::RenderTransparentModels(
|
|||
m_ModelRenderMode == WIREFRAME ? WIREFRAME : SOLID};
|
||||
|
||||
if (transparentMode == TRANSPARENT || transparentMode == TRANSPARENT_OPAQUE)
|
||||
m->CallTranspModelRenderers(deviceCommandContext, contextOpaque, cullGroup, flags, modelRenderMode);
|
||||
m->CallTransparentModelRenderers(deviceCommandContext, contextOpaque, cullGroup, flags, modelRenderMode);
|
||||
|
||||
if (transparentMode == TRANSPARENT || transparentMode == TRANSPARENT_BLEND)
|
||||
m->CallTranspModelRenderers(deviceCommandContext, contextBlend, cullGroup, flags, modelRenderMode);
|
||||
m->CallTransparentModelRenderers(deviceCommandContext, contextBlend, cullGroup, flags, modelRenderMode);
|
||||
|
||||
if (m_ModelRenderMode == EDGED_FACES)
|
||||
m->CallTranspModelRenderers(deviceCommandContext, {}, cullGroup, flags, EDGED_FACES);
|
||||
m->CallTransparentModelRenderers(deviceCommandContext, {}, cullGroup, flags, EDGED_FACES);
|
||||
}
|
||||
|
||||
// SetObliqueFrustumClipping: change the near plane to the given clip plane (in world space)
|
||||
|
|
@ -709,24 +810,24 @@ void CSceneRenderer::RenderSilhouettes(
|
|||
|
||||
{
|
||||
PROFILE("render model occluders");
|
||||
m->CallModelRenderers(deviceCommandContext, {}, CULL_SILHOUETTE_OCCLUDER, 0, ERenderMode::SOLID);
|
||||
m->CallOpaqueModelRenderers(deviceCommandContext, {}, CULL_SILHOUETTE_OCCLUDER, 0, ERenderMode::SOLID);
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE("render transparent occluders");
|
||||
m->CallTranspModelRenderers(deviceCommandContext, {}, CULL_SILHOUETTE_OCCLUDER, 0, ERenderMode::SOLID);
|
||||
m->CallTransparentModelRenderers(deviceCommandContext, {}, CULL_SILHOUETTE_OCCLUDER, 0, ERenderMode::SOLID);
|
||||
}
|
||||
|
||||
// Since we can't sort, we'll use the stencil buffer to ensure we only draw
|
||||
// a pixel once (using the color of whatever model happens to be drawn first).
|
||||
{
|
||||
PROFILE("render model casters");
|
||||
m->CallModelRenderers(deviceCommandContext, {}, CULL_SILHOUETTE_CASTER, 0, ERenderMode::SOLID);
|
||||
m->CallOpaqueModelRenderers(deviceCommandContext, {}, CULL_SILHOUETTE_CASTER, 0, ERenderMode::SOLID);
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE("render transparent casters");
|
||||
m->CallTranspModelRenderers(deviceCommandContext, {}, CULL_SILHOUETTE_CASTER, 0, ERenderMode::SOLID);
|
||||
m->CallTransparentModelRenderers(deviceCommandContext, {}, CULL_SILHOUETTE_CASTER, 0, ERenderMode::SOLID);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -770,15 +871,7 @@ void CSceneRenderer::PrepareSubmissions(
|
|||
CShaderDefines context = m->globalContext;
|
||||
|
||||
// Prepare model renderers
|
||||
{
|
||||
PROFILE3("prepare models");
|
||||
m->Model.NormalSkinned->PrepareModels(deviceCommandContext);
|
||||
m->Model.TranspSkinned->PrepareModels(deviceCommandContext);
|
||||
if (m->Model.NormalUnskinned != m->Model.NormalSkinned)
|
||||
m->Model.NormalUnskinned->PrepareModels(deviceCommandContext);
|
||||
if (m->Model.TranspUnskinned != m->Model.TranspSkinned)
|
||||
m->Model.TranspUnskinned->PrepareModels(deviceCommandContext);
|
||||
}
|
||||
m->PrepareModels(deviceCommandContext);
|
||||
|
||||
m->terrainRenderer.PrepareForRendering();
|
||||
|
||||
|
|
@ -786,15 +879,7 @@ void CSceneRenderer::PrepareSubmissions(
|
|||
|
||||
m->particleRenderer.PrepareForRendering(context);
|
||||
|
||||
{
|
||||
PROFILE3("upload models");
|
||||
m->Model.NormalSkinned->UploadModels(deviceCommandContext);
|
||||
m->Model.TranspSkinned->UploadModels(deviceCommandContext);
|
||||
if (m->Model.NormalUnskinned != m->Model.NormalSkinned)
|
||||
m->Model.NormalUnskinned->UploadModels(deviceCommandContext);
|
||||
if (m->Model.TranspUnskinned != m->Model.TranspSkinned)
|
||||
m->Model.TranspUnskinned->UploadModels(deviceCommandContext);
|
||||
}
|
||||
m->UploadModels(deviceCommandContext);
|
||||
|
||||
m->overlayRenderer.Upload(deviceCommandContext);
|
||||
|
||||
|
|
@ -908,13 +993,13 @@ void CSceneRenderer::EndFrame()
|
|||
m->particleRenderer.EndFrame();
|
||||
m->silhouetteRenderer.EndFrame();
|
||||
|
||||
// Finish model renderers
|
||||
m->Model.NormalSkinned->EndFrame();
|
||||
m->Model.TranspSkinned->EndFrame();
|
||||
if (m->Model.NormalUnskinned != m->Model.NormalSkinned)
|
||||
m->Model.NormalUnskinned->EndFrame();
|
||||
if (m->Model.TranspUnskinned != m->Model.TranspSkinned)
|
||||
m->Model.TranspUnskinned->EndFrame();
|
||||
for (int cullGroup{0}; cullGroup < CSceneRenderer::CULL_MAX; ++cullGroup)
|
||||
{
|
||||
m->Model.OpaqueSkinned.submissions[cullGroup].clear();
|
||||
m->Model.TransparentSkinned.submissions[cullGroup].clear();
|
||||
m->Model.OpaqueUnskinned.submissions[cullGroup].clear();
|
||||
m->Model.TransparentUnskinned.submissions[cullGroup].clear();
|
||||
}
|
||||
}
|
||||
|
||||
void CSceneRenderer::DisplayFrustum(Renderer::Backend::IDeviceCommandContext& deviceCommandContext)
|
||||
|
|
@ -1029,21 +1114,25 @@ void CSceneRenderer::SubmitNonRecursive(CModel* model)
|
|||
m->shadow.AddShadowCasterBound(cascade, model->GetWorldBounds());
|
||||
}
|
||||
|
||||
bool requiresSkinning = (model->GetModelDef()->GetNumBones() != 0);
|
||||
const bool requiresSkinning{model->GetModelDef()->GetNumBones() != 0};
|
||||
ModelVertexRenderer& modelVertexSkinningRenderer{
|
||||
m->Model.GPUSkinningEnabled
|
||||
? static_cast<ModelVertexRenderer&>(*m->Model.VertexGPUSkinningShader)
|
||||
: static_cast<ModelVertexRenderer&>(m->Model.VertexCPUSkinningShader)};
|
||||
|
||||
if (model->GetMaterial().UsesAlphaBlending())
|
||||
{
|
||||
if (requiresSkinning)
|
||||
m->Model.TranspSkinned->Submit(m_CurrentCullGroup, model);
|
||||
m->Submit(m_CurrentCullGroup, modelVertexSkinningRenderer, m->Model.TransparentSkinned, model);
|
||||
else
|
||||
m->Model.TranspUnskinned->Submit(m_CurrentCullGroup, model);
|
||||
m->Submit(m_CurrentCullGroup, m->Model.VertexInstancingShader, m->Model.TransparentUnskinned, model);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (requiresSkinning)
|
||||
m->Model.NormalSkinned->Submit(m_CurrentCullGroup, model);
|
||||
m->Submit(m_CurrentCullGroup, modelVertexSkinningRenderer, m->Model.OpaqueSkinned, model);
|
||||
else
|
||||
m->Model.NormalUnskinned->Submit(m_CurrentCullGroup, model);
|
||||
m->Submit(m_CurrentCullGroup, m->Model.VertexInstancingShader, m->Model.OpaqueUnskinned, model);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -90,21 +90,21 @@ CStr CScriptStatsTable::GetCellText(size_t row, size_t col)
|
|||
if (col == 0)
|
||||
return "max nominal heap bytes";
|
||||
uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetGeneralJSContext(), JSGC_MAX_BYTES);
|
||||
return CStr::FromUInt(n);
|
||||
return std::to_string(n);
|
||||
}
|
||||
case Row_Bytes:
|
||||
{
|
||||
if (col == 0)
|
||||
return "allocated bytes";
|
||||
uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetGeneralJSContext(), JSGC_BYTES);
|
||||
return CStr::FromUInt(n);
|
||||
return std::to_string(n);
|
||||
}
|
||||
case Row_NumberGC:
|
||||
{
|
||||
if (col == 0)
|
||||
return "number of GCs";
|
||||
uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetGeneralJSContext(), JSGC_NUMBER);
|
||||
return CStr::FromUInt(n);
|
||||
return std::to_string(n);
|
||||
}
|
||||
default:
|
||||
return "???";
|
||||
|
|
|
|||
|
|
@ -45,21 +45,15 @@
|
|||
|
||||
using AtlasMessage::Shareable;
|
||||
|
||||
enum {
|
||||
ID_PathsDrawing,
|
||||
ID_PathsList,
|
||||
ID_AddPath,
|
||||
ID_DeletePath
|
||||
};
|
||||
|
||||
CinemaSidebar::CinemaSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer)
|
||||
: Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer)
|
||||
{
|
||||
{
|
||||
auto* sizer = new wxStaticBoxSizer(wxVERTICAL, this, _T("Common settings"));
|
||||
|
||||
m_DrawPath = new wxCheckBox(sizer->GetStaticBox(), ID_PathsDrawing, _("Draw all paths"));
|
||||
m_DrawPath = new wxCheckBox(sizer->GetStaticBox(), wxID_ANY, _("Draw all paths"));
|
||||
m_DrawPath->SetToolTip(_("Display every cinematic path added to the map"));
|
||||
m_DrawPath->Bind(wxEVT_CHECKBOX, [this](auto&){ SetPathsDrawing(m_DrawPath->IsChecked()); });
|
||||
|
||||
sizer->Add(m_DrawPath, wxSizerFlags().Expand().Border(wxALL, Atlas::Style::STATICBOX_PADDING));
|
||||
|
||||
|
|
@ -70,20 +64,22 @@ CinemaSidebar::CinemaSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarCo
|
|||
auto* boxSizer = new wxStaticBoxSizer(wxVERTICAL, this, _T("Paths"));
|
||||
auto* box = boxSizer->GetStaticBox();
|
||||
|
||||
m_PathList = new wxListBox(box, ID_PathsList, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_SINGLE | wxLB_SORT);
|
||||
m_PathList = new wxListBox(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_SINGLE | wxLB_SORT);
|
||||
|
||||
auto* deleteButton = new wxButton(box, ID_DeletePath, _("Delete"));
|
||||
auto* deleteButton = new wxButton(box, wxID_ANY, _("Delete"));
|
||||
deleteButton->SetToolTip(_T("Delete selected path"));
|
||||
deleteButton->Bind(wxEVT_BUTTON, [this](auto&){ DeleteSelectedPath(); });
|
||||
|
||||
m_NewPathName = new wxTextCtrl(box, wxID_ANY);
|
||||
auto* newPathName = new wxTextCtrl(box, wxID_ANY);
|
||||
|
||||
auto* addButton = new wxButton(box, ID_AddPath, _("Add"));
|
||||
auto* addButton = new wxButton(box, wxID_ANY, _("Add"));
|
||||
addButton->Bind(wxEVT_BUTTON, [this, newPathName](auto&){ AddPath(newPathName->GetValue()); newPathName->Clear(); });
|
||||
|
||||
wxFlexGridSizer* pathsSizer = new wxFlexGridSizer(1, 5, 5);
|
||||
pathsSizer->AddGrowableCol(0);
|
||||
pathsSizer->Add(m_PathList, wxSizerFlags().Proportion(1).Expand());
|
||||
pathsSizer->Add(deleteButton, wxSizerFlags().Expand());
|
||||
pathsSizer->Add(m_NewPathName, wxSizerFlags().Expand());
|
||||
pathsSizer->Add(newPathName, wxSizerFlags().Expand());
|
||||
pathsSizer->Add(addButton, wxSizerFlags().Expand());
|
||||
|
||||
boxSizer->Add(pathsSizer, wxSizerFlags().Expand().Border(wxALL, Atlas::Style::STATICBOX_PADDING));
|
||||
|
|
@ -106,22 +102,21 @@ void CinemaSidebar::OnMapReload()
|
|||
ReloadPathList();
|
||||
}
|
||||
|
||||
void CinemaSidebar::OnTogglePathsDrawing(wxCommandEvent& evt)
|
||||
void CinemaSidebar::SetPathsDrawing(const bool enable)
|
||||
{
|
||||
POST_COMMAND(SetCinemaPathsDrawing, (evt.IsChecked()));
|
||||
POST_COMMAND(SetCinemaPathsDrawing, (enable));
|
||||
}
|
||||
|
||||
void CinemaSidebar::OnAddPath(wxCommandEvent&)
|
||||
void CinemaSidebar::AddPath(wxString name)
|
||||
{
|
||||
if (m_NewPathName->GetValue().empty())
|
||||
if (name.empty())
|
||||
return;
|
||||
|
||||
POST_COMMAND(AddCinemaPath, (m_NewPathName->GetValue().ToStdWstring()));
|
||||
m_NewPathName->Clear();
|
||||
POST_COMMAND(AddCinemaPath, (name.ToStdWstring()));
|
||||
ReloadPathList();
|
||||
}
|
||||
|
||||
void CinemaSidebar::OnDeletePath(wxCommandEvent&)
|
||||
void CinemaSidebar::DeleteSelectedPath()
|
||||
{
|
||||
int index = m_PathList->GetSelection();
|
||||
if (index < 0)
|
||||
|
|
@ -148,9 +143,3 @@ void CinemaSidebar::ReloadPathList()
|
|||
|
||||
m_PathList->SetStringSelection(selection);
|
||||
}
|
||||
|
||||
wxBEGIN_EVENT_TABLE(CinemaSidebar, Sidebar)
|
||||
EVT_CHECKBOX(ID_PathsDrawing, CinemaSidebar::OnTogglePathsDrawing)
|
||||
EVT_BUTTON(ID_AddPath, CinemaSidebar::OnAddPath)
|
||||
EVT_BUTTON(ID_DeletePath, CinemaSidebar::OnDeletePath)
|
||||
wxEND_EVENT_TABLE();
|
||||
|
|
|
|||
|
|
@ -37,15 +37,12 @@ protected:
|
|||
void OnFirstDisplay() override;
|
||||
|
||||
private:
|
||||
void OnTogglePathsDrawing(wxCommandEvent& evt);
|
||||
void OnAddPath(wxCommandEvent& evt);
|
||||
void OnDeletePath(wxCommandEvent& evt);
|
||||
void SetPathsDrawing(const bool enable);
|
||||
void AddPath(wxString name);
|
||||
void DeleteSelectedPath();
|
||||
|
||||
void ReloadPathList();
|
||||
|
||||
wxCheckBox* m_DrawPath;
|
||||
wxListBox* m_PathList;
|
||||
wxTextCtrl* m_NewPathName;
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@ public:
|
|||
wxGridSizer* gridSizer = new wxGridSizer(3, 5, 5);
|
||||
wxButton* cameraSet = new wxButton(cameraSizer->GetStaticBox(), ID_CameraSet, _("Set"), wxDefaultPosition, wxSize(48, -1));
|
||||
gridSizer->Add(Tooltipped(cameraSet,
|
||||
_("Set player camera to cameraSizer->GetStaticBox() view")), wxSizerFlags().Expand());
|
||||
_("Set player camera to this view")), wxSizerFlags().Expand());
|
||||
wxButton* cameraView = new wxButton(cameraSizer->GetStaticBox(), ID_CameraView, _("View"), wxDefaultPosition, wxSize(48, -1));
|
||||
cameraView->Enable(false);
|
||||
gridSizer->Add(Tooltipped(cameraView,
|
||||
|
|
|
|||
|
|
@ -65,8 +65,8 @@ unknownMacro:source/lib/sysdep/os/win/wfirmware.cpp
|
|||
unknownMacro:source/lib/sysdep/os/win/wposix/wutsname.cpp
|
||||
unknownMacro:source/ps/CStr.cpp
|
||||
|
||||
uninitvar:source/ps/Game.cpp:246
|
||||
uninitvar:source/ps/scripting/JSInterface_SavedGame.cpp:149
|
||||
uninitvar:source/ps/Game.cpp
|
||||
uninitvar:source/ps/scripting/JSInterface_SavedGame.cpp
|
||||
|
||||
danglingLifetime:source/renderer/backend/gl/Device.cpp
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue