Skip to content

Commit 05d8395

Browse files
authored
Merge branch 'master' into fix-chat-delay-high-player-count
2 parents c74126a + 7f23f61 commit 05d8395

18 files changed

Lines changed: 232 additions & 41 deletions

Client/core/Graphics/CGraphics.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,40 @@ extern std::atomic<bool> g_bInMTAScene;
2727

2828
using namespace std;
2929

30+
namespace
31+
{
32+
// Convert a straight-alpha ARGB tint into its premultiplied form:
33+
//
34+
// (R, G, B, A) -> (R*A/255, G*A/255, B*A/255, A)
35+
//
36+
// Needed when routing a translucent draw onto the (ONE, INVSRCALPHA)
37+
// pipeline. That pipeline computes
38+
//
39+
// out.rgb = src.rgb + dst.rgb * (1 - src.a)
40+
//
41+
// with src.rgb = tex.rgb * diff.rgb and src.a = tex.a * diff.a, so
42+
// diff.a never enters the source rgb term. For a PM texel tex.rgb =
43+
// R*A_tex, that leaves
44+
//
45+
// out.rgb = R * A_tex * diff.rgb + dst.rgb * (1 - A_tex * diff.a)
46+
//
47+
// which is brighter than the correct PM composite by a factor of
48+
// 1/diff.a during a fade. Premultiplying the diffuse here
49+
// (diff.rgb *= diff.a) restores the missing factor and the equation
50+
// reduces exactly to the PM "over" operator.
51+
inline SColor PremultiplyAlpha(SColor c) noexcept
52+
{
53+
if (c.A == 0xFF)
54+
return c;
55+
56+
const uint a = c.A;
57+
c.R = static_cast<unsigned char>((c.R * a + 127) / 255);
58+
c.G = static_cast<unsigned char>((c.G * a + 127) / 255);
59+
c.B = static_cast<unsigned char>((c.B * a + 127) / 255);
60+
return c;
61+
}
62+
} // namespace
63+
3064
template <>
3165
CGraphics* CSingleton<CGraphics>::m_pSingleton = NULL;
3266

@@ -1080,6 +1114,15 @@ void CGraphics::DrawMaterialPrimitiveQueued(std::vector<PrimitiveMaterialVertice
10801114
sDrawQueueItem Item;
10811115
Item.eType = QUEUE_PRIMITIVEMATERIAL;
10821116
Item.blendMode = m_ActiveBlendMode;
1117+
// Same PM auto-route as DrawTextureQueued, so dxDrawMaterialPrimitive
1118+
// composites SVG (premultiplied) textures correctly under default blend.
1119+
if (Item.blendMode == EBlendMode::BLEND && pMaterial && pMaterial->m_bPremultipliedAlpha)
1120+
{
1121+
Item.blendMode = EBlendMode::ADD;
1122+
// See PremultiplyAlpha for the diffuse premultiply rationale (#3828).
1123+
for (auto& vert : *pVecVertices)
1124+
vert.Color = PremultiplyAlpha(vert.Color);
1125+
}
10831126
Item.PrimitiveMaterial.eType = eType;
10841127
Item.PrimitiveMaterial.pMaterial = pMaterial;
10851128
Item.PrimitiveMaterial.pVecVertices = pVecVertices;
@@ -1134,6 +1177,16 @@ void CGraphics::DrawTextureQueued(float fX, float fY, float fWidth, float fHeigh
11341177
// Set up a queue item
11351178
sDrawQueueItem Item;
11361179
Item.blendMode = m_ActiveBlendMode;
1180+
// Premultiplied-alpha textures (SVG) need the (ONE, INVSRCALPHA) pipeline,
1181+
// not the straight-alpha BLEND mode. Route the default blend to ADD when
1182+
// the script hasn't asked for a specific mode (issue #4891), and convert
1183+
// the diffuse tint to PM so a translucent dxDrawImage call still fades
1184+
// correctly through that pipeline (issue #3828).
1185+
if (Item.blendMode == EBlendMode::BLEND && pMaterial && pMaterial->m_bPremultipliedAlpha)
1186+
{
1187+
Item.blendMode = EBlendMode::ADD;
1188+
ulColor = PremultiplyAlpha(ulColor);
1189+
}
11371190
Item.Texture.fX = fX;
11381191
Item.Texture.fY = fY;
11391192
Item.Texture.fWidth = fWidth;

Client/core/Graphics/CRenderItem.VectorGraphic.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ void CVectorGraphicItem::PostConstruct(CRenderItemManager* pManager, uint width,
2424
m_uiSizeY = height;
2525
m_uiSurfaceSizeX = width;
2626
m_uiSurfaceSizeY = height;
27+
// lunasvg writes premultiplied ARGB into the surface; let the draw
28+
// path use a PM-aware blend instead of unpremultiplying per pixel.
29+
m_bPremultipliedAlpha = true;
2730

2831
CreateUnderlyingData();
2932
}

Client/game_sa/CVehicleSA.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "StdInc.h"
1313
#include <core/CCoreInterface.h>
14+
#include <game/CAESoundManager.h>
1415
#include <multiplayer/CMultiplayer.h>
1516
#include "CAutomobileSA.h"
1617
#include "CBikeSA.h"
@@ -145,8 +146,29 @@ static void __declspec(naked) HOOK_CPlane_ProcessFlyingCarStuff()
145146
// clang-format on
146147
}
147148

149+
#define NUM_FirstStreamEngineSlot 7
150+
#define NUM_LastStreamEngineSlot 16
151+
#define NUM_AllSoundIndices 0xFFFFFFFF
152+
#define NUM_ResidentEngineSlot 40
153+
#define NUM_LocalVehicleAudioContext 0x0
154+
#define VAR_VehicleAudioContext 0x50230C
155+
148156
namespace
149157
{
158+
void CancelVehicleAudioSlots(CAEVehicleAudioEntitySAInterface* pAudioInterface)
159+
{
160+
auto* pSoundManager = pGame ? pGame->GetAESoundManager() : nullptr;
161+
if (!pAudioInterface || !pSoundManager)
162+
return;
163+
164+
if (pAudioInterface->m_wEngineBankSlotId >= NUM_FirstStreamEngineSlot && pAudioInterface->m_wEngineBankSlotId <= NUM_LastStreamEngineSlot)
165+
pSoundManager->CancelSoundsInBankSlot(pAudioInterface->m_wEngineBankSlotId, NUM_AllSoundIndices);
166+
167+
if (pAudioInterface->m_bPlayerDriver || pAudioInterface->m_bPlayerPassenger ||
168+
*reinterpret_cast<const BYTE*>(VAR_VehicleAudioContext) == NUM_LocalVehicleAudioContext)
169+
pSoundManager->CancelSoundsInBankSlot(NUM_ResidentEngineSlot, NUM_AllSoundIndices);
170+
}
171+
150172
bool ClumpDumpCB(RpAtomic* pAtomic, void* data)
151173
{
152174
CVehicleSA* pVehicleSA = (CVehicleSA*)data;
@@ -2582,7 +2604,14 @@ bool CVehicleSA::SetWindowOpenFlagState(unsigned char ucWindow, bool bState)
25822604

25832605
void CVehicleSA::ReinitAudio()
25842606
{
2607+
if (!m_pVehicleAudioEntity)
2608+
return;
2609+
25852610
auto* audioInterface = m_pVehicleAudioEntity->GetInterface();
2611+
if (!audioInterface)
2612+
return;
2613+
2614+
CancelVehicleAudioSlots(audioInterface);
25862615

25872616
audioInterface->TerminateAudio();
25882617
audioInterface->InitAudio(GetVehicleInterface());

Client/game_sa/CWeatherSA.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,22 @@ void CWeatherSA::Set(unsigned char primary, unsigned char secondary)
2525
MemPutFast<unsigned char>(0xC8131C, secondary); // CWeather::NewWeatherType
2626
}
2727

28+
void CWeatherSA::ResyncInterpolationWithGameClock(unsigned char primary, unsigned char secondary)
29+
{
30+
// CWeather::InterpolationValue — see plugin_sa CWeather.cpp (0xC8130C)
31+
constexpr DWORD VAR_InterpolationValue = 0xC8130C;
32+
constexpr DWORD VAR_TimeMinutes = 0xB70152;
33+
34+
if (primary == secondary)
35+
MemPutFast<float>(VAR_InterpolationValue, 0.0f);
36+
else
37+
{
38+
const auto ucMinute = *reinterpret_cast<unsigned char*>(VAR_TimeMinutes);
39+
const float fInterp = std::min(1.f, static_cast<float>(ucMinute) / 60.f);
40+
MemPutFast<float>(VAR_InterpolationValue, fInterp);
41+
}
42+
}
43+
2844
void CWeatherSA::Release()
2945
{
3046
MemPutFast<unsigned char>(0xC81318, 0xFF); // CWeather::ForcedWeatherType

Client/game_sa/CWeatherSA.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class CWeatherSA : public CWeather
2020
public:
2121
unsigned char Get();
2222
void Set(unsigned char primary, unsigned char secondary);
23+
void ResyncInterpolationWithGameClock(unsigned char primary, unsigned char secondary);
2324

2425
void Release();
2526

Client/mods/deathmatch/logic/CBlendedWeather.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,14 @@ void CBlendedWeather::DoPulse()
5353
}
5454
}
5555

56-
// Force the weather
57-
m_pWeather->Set(static_cast<unsigned char>(m_ucPrimaryWeather), static_cast<unsigned char>(m_ucSecondaryWeather));
56+
const auto ucPrimary = static_cast<unsigned char>(m_ucPrimaryWeather);
57+
const auto ucSecondary = static_cast<unsigned char>(m_ucSecondaryWeather);
58+
59+
m_pWeather->Set(ucPrimary, ucSecondary);
60+
// CWeather::Update (before this pulse) advances InterpolationValue for its own Old/New pair.
61+
// After we overwrite Old/New with MTA's state, keep the blend weight aligned with the game
62+
// clock so reflective/translucent materials (e.g. glass windows) match the sky (#4803).
63+
m_pWeather->ResyncInterpolationWithGameClock(ucPrimary, ucSecondary);
5864
}
5965

6066
void CBlendedWeather::SetWeather(unsigned char ucWeather)

Client/mods/deathmatch/logic/CClientGame.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo())
285285
g_pMultiplayer->SetRenderHeliLightHandler(CClientGame::StaticRenderHeliLightHandler);
286286
g_pMultiplayer->SetRenderEverythingBarRoadsHandler(CClientGame::StaticRenderEverythingBarRoadsHandler);
287287
g_pMultiplayer->SetChokingHandler(CClientGame::StaticChokingHandler);
288+
g_pMultiplayer->SetPreWeatherUpdateHandler(CClientGame::StaticPreWeatherUpdateHandler);
288289
g_pMultiplayer->SetPreWorldProcessHandler(CClientGame::StaticPreWorldProcessHandler);
289290
g_pMultiplayer->SetPostWorldProcessHandler(CClientGame::StaticPostWorldProcessHandler);
290291
g_pMultiplayer->SetPostWorldProcessPedsAfterPreRenderHandler(CClientGame::StaticPostWorldProcessPedsAfterPreRenderHandler);
@@ -494,6 +495,7 @@ CClientGame::~CClientGame()
494495
g_pMultiplayer->SetRenderHeliLightHandler(nullptr);
495496
g_pMultiplayer->SetRenderEverythingBarRoadsHandler(nullptr);
496497
g_pMultiplayer->SetChokingHandler(NULL);
498+
g_pMultiplayer->SetPreWeatherUpdateHandler(NULL);
497499
g_pMultiplayer->SetPreWorldProcessHandler(NULL);
498500
g_pMultiplayer->SetPostWorldProcessHandler(NULL);
499501
g_pMultiplayer->SetPostWorldProcessPedsAfterPreRenderHandler(nullptr);
@@ -3629,6 +3631,11 @@ bool CClientGame::StaticBlendAnimationHierarchyHandler(CAnimBlendAssociationSAIn
36293631
return g_pClientGame->BlendAnimationHierarchyHandler(pAnimAssoc, pOutAnimHierarchy, pFlags, pClump);
36303632
}
36313633

3634+
void CClientGame::StaticPreWeatherUpdateHandler()
3635+
{
3636+
g_pClientGame->PreWeatherUpdateHandler();
3637+
}
3638+
36323639
void CClientGame::StaticPreWorldProcessHandler()
36333640
{
36343641
g_pClientGame->PreWorldProcessHandler();
@@ -3900,6 +3907,20 @@ void CClientGame::PreRenderSkyHandler()
39003907
g_pCore->GetGraphics()->GetRenderItemManager()->PreDrawWorld();
39013908
}
39023909

3910+
void CClientGame::PreWeatherUpdateHandler()
3911+
{
3912+
// Fix #4803 (building light flicker): Re-apply MTA's weather state BEFORE
3913+
// CWeather::Update runs. CWeather::Update detects a clock wrap when setTime()
3914+
// jumps the game clock past InterpolationValue and re-derives Rain, Foggyness,
3915+
// CloudCoverage, ExtraSunnyness, SunGlare, HeatHaze, etc. from a freshly-picked
3916+
// weather pair. Those globals drive cloud, fog and night-time building light
3917+
// rendering for the rest of the frame, and PreWorldProcessHandler runs too late
3918+
// to undo the damage. Pre-syncing Old/New/InterpolationValue here keeps the
3919+
// wrap branch from firing.
3920+
if (m_pManager->IsGameLoaded() && m_pBlendedWeather)
3921+
m_pBlendedWeather->DoPulse();
3922+
}
3923+
39033924
void CClientGame::PreWorldProcessHandler()
39043925
{
39053926
// Fix #4803: Re-apply MTA's weather state before CTimeCycle::CalcColoursForPoint()

Client/mods/deathmatch/logic/CClientGame.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,7 @@ class CClientGame
577577
static void StaticRenderHeliLightHandler();
578578
static void StaticRenderEverythingBarRoadsHandler();
579579
static bool StaticChokingHandler(unsigned char ucWeaponType);
580+
static void StaticPreWeatherUpdateHandler();
580581
static void StaticPreWorldProcessHandler();
581582
static void StaticPostWorldProcessHandler();
582583
static void StaticPostWorldProcessPedsAfterPreRenderHandler();
@@ -626,6 +627,7 @@ class CClientGame
626627
void Render3DStuffHandler();
627628
void PreRenderSkyHandler();
628629
bool ChokingHandler(unsigned char ucWeaponType);
630+
void PreWeatherUpdateHandler();
629631
void PreWorldProcessHandler();
630632
void PostWorldProcessHandler();
631633
void PostWorldProcessPedsAfterPreRenderHandler();

Client/mods/deathmatch/logic/CClientVectorGraphicDisplay.cpp

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -56,37 +56,6 @@ void CClientVectorGraphicDisplay::Render()
5656
}
5757
}
5858

59-
void CClientVectorGraphicDisplay::UnpremultiplyBitmap(Bitmap& bitmap)
60-
{
61-
auto width = bitmap.width();
62-
auto height = bitmap.height();
63-
auto stride = bitmap.stride();
64-
auto rowData = bitmap.data();
65-
66-
for (decltype(height) y = 0; y < height; y++)
67-
{
68-
auto data = rowData;
69-
for (decltype(width) x = 0; x < width; x++)
70-
{
71-
auto& b = data[0];
72-
auto& g = data[1];
73-
auto& r = data[2];
74-
auto& a = data[3];
75-
76-
if (a != 0)
77-
{
78-
r = (r * 255) / a;
79-
g = (g * 255) / a;
80-
b = (b * 255) / a;
81-
}
82-
83-
data += 4;
84-
}
85-
86-
rowData += stride;
87-
}
88-
}
89-
9059
void CClientVectorGraphicDisplay::UpdateTexture()
9160
{
9261
if (!m_pVectorGraphic || m_pVectorGraphic->IsDestroyed())
@@ -144,7 +113,10 @@ void CClientVectorGraphicDisplay::UpdateTexture()
144113
return;
145114
}
146115

147-
UnpremultiplyBitmap(bitmap);
116+
// The surface stays in lunasvg's native premultiplied ARGB layout.
117+
// CGraphics::DrawTextureQueued routes the draw to a PM blend so we
118+
// avoid the precision loss caused by an integer unpremultiply
119+
// (see issue #4891 banding on translucent SVG gradients).
148120

149121
// Unlock surface
150122
surface->UnlockRect();

Client/mods/deathmatch/logic/CClientVectorGraphicDisplay.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ class CClientVectorGraphicDisplay final : public CClientDisplay
3636
void Update();
3737

3838
private:
39-
void UnpremultiplyBitmap(lunasvg::Bitmap& bitmap);
40-
4139
CClientVectorGraphic* m_pVectorGraphic;
4240

4341
bool m_bIsCleared;

0 commit comments

Comments
 (0)