Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 32 additions & 10 deletions Core/GameEngine/Include/GameClient/Smudge.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,39 +28,59 @@

struct Smudge : public DLNodeClass<Smudge>
{
typedef void *Identifier;

W3DMPO_GLUE(Smudge)

Identifier m_identifier; //a number or pointer to identify this smudge
Vector3 m_pos; //position of smudge center
Vector2 m_offset; // difference in position between "texture" extraction and re-insertion for center vertex
Real m_size; //size of smudge in world space.
Real m_opacity; //alpha of center vertex, corners are assumed at 0
Bool m_draw; //whether this smudge needs to be drawn

struct smudgeVertex
{ Vector3 pos; //world-space position of vertex
{
Vector3 pos; //world-space position of vertex
Vector2 uv; //uv coordinates of vertex
};
smudgeVertex m_verts[5]; //5 vertices of this smudge (in counter-clockwise order, starting at top-left, ending in center.)
};

struct SmudgeSet : public DLNodeClass<SmudgeSet>
#ifdef USING_STLPORT
namespace std
{
W3DMPO_GLUE(SmudgeSet)

public:
template<> struct hash<Smudge::Identifier>
{
size_t operator()(Smudge::Identifier id) const { return reinterpret_cast<size_t>(id); }
};
}
#endif // USING_STLPORT

struct SmudgeSet : public DLNodeClass<SmudgeSet>
{
friend class SmudgeManager;

W3DMPO_GLUE(SmudgeSet)

SmudgeSet();
virtual ~SmudgeSet() override;

void reset();
void resetDraw();

Smudge *addSmudgeToSet(Smudge::Identifier identifier); ///< add and return a smudge to the set with the given identifier
void removeSmudgeFromSet(Smudge *&smudge); ///< remove and invalidate the given smudge
Smudge *findSmudge(Smudge::Identifier identifier); ///< find the smudge that belongs to this identifier

Smudge *addSmudgeToSet();
void removeSmudgeFromSet ( Smudge &mySmudge);
DLListClass<Smudge> &getUsedSmudgeList() { return m_usedSmudgeList;}
Int getUsedSmudgeCount() { return m_usedSmudgeCount; } ///<active smudges that need rendering.

private:
typedef std::hash_map<Smudge::Identifier, Smudge *> SmudgeIdToPtrMap;

DLListClass<Smudge> m_usedSmudgeList; ///<list of smudges in this set.
SmudgeIdToPtrMap m_usedSmudgeMap;
static DLListClass<Smudge> m_freeSmudgeList; ///<list of unused smudges for use by SmudgeSets.
Int m_usedSmudgeCount;
};
Expand All @@ -76,10 +96,12 @@ class SmudgeManager
virtual void ReleaseResources() {}
virtual void ReAcquireResources() {}

SmudgeSet *addSmudgeSet();
void removeSmudgeSet(SmudgeSet &mySmudge);
void resetDraw(); ///< reset whether all smudges need to be drawn

SmudgeSet *addSmudgeSet(); ///< add and return a new smudge set
void removeSmudgeSet(SmudgeSet *&smudgeSet); ///< remove and invalidate the given smudge set
Smudge *findSmudge(Smudge::Identifier identifier); ///< find the smudge from any smudge set
Int getSmudgeCountLastFrame() {return m_smudgeCountLastFrame;} ///<return number of smudges submitted last frame.
void setSmudgeCountLastFrame(Int count) { m_smudgeCountLastFrame = count;}
Bool getHardwareSupport() { return m_hardwareSupportStatus != SMUDGE_SUPPORT_NO;}

protected:
Expand Down
38 changes: 38 additions & 0 deletions Core/GameEngine/Source/GameClient/System/ParticleSys.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include "GameClient/GameClient.h"
#include "GameClient/InGameUI.h"
#include "GameClient/ParticleSys.h"
#include "GameClient/Smudge.h"

#include "GameLogic/GameLogic.h"
#include "GameLogic/Object.h"
Expand Down Expand Up @@ -3003,6 +3004,43 @@ void ParticleSystemManager::update()
deleteInstance(sys);
}
}

const Bool drawSmudge = TheSmudgeManager && TheSmudgeManager->getHardwareSupport() && TheGlobalData->m_useHeatEffects;

if (drawSmudge)
{
// TheSuperHackers @bugfix The smudge time step is now decoupled from the render update.
// This clears all prior smudges and recreates them for all current smudge particles.

TheSmudgeManager->reset();
SmudgeSet *set = TheSmudgeManager->addSmudgeSet(); //global smudge set through which all smudges are rendered.

for (ParticleSystemManager::ParticleSystemListIt it = m_allParticleSystemList.begin(); it != m_allParticleSystemList.end(); ++it)
{
ParticleSystem *sys = (*it);
if (!sys)
continue;

// only look at particle/point style systems
if (sys->isUsingDrawables())
continue;

// temporary hack that checks if texture name starts with "SMUD" - if so, we can assume it's a smudge type
if (/*sys->isUsingSmudge()*/ *((DWORD *)sys->getParticleTypeName().str()) == 0x44554D53)
{
for (Particle *p = sys->getFirstParticle(); p; p = p->m_systemNext)
{
const Coord3D *pos = p->getPosition();
Smudge *smudge = set->addSmudgeToSet(p);
smudge->m_pos.Set(pos->x, pos->y, pos->z);
smudge->m_offset.Set(GameClientRandomValueReal(-0.06f,0.06f), GameClientRandomValueReal(-0.03f,0.03f));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is strange that the Y component has a different value range. This needs some investigation.

Copy link
Author

@xezon xezon Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this value and it is responsible for the texture offset. What we need to do is set GameClientRandomValueReal(-0.06f,0.06f) for both X and Y, which will resemble the look of the original.

For follow up change.

Test with offset GameClientRandomValueReal(-2,2)

shot_20260325_190057_2

smudge->m_size = p->getSize();
smudge->m_opacity = p->getAlpha();
smudge->m_draw = false;
}
}
}
}
}

// ------------------------------------------------------------------------------------------------
Expand Down
69 changes: 57 additions & 12 deletions Core/GameEngine/Source/GameClient/System/Smudge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ void SmudgeManager::reset()
}
}

void SmudgeManager::resetDraw()
{
SmudgeSet* smudgeSet = m_usedSmudgeSetList.Head();
for (; smudgeSet; smudgeSet = smudgeSet->Succ()) {
smudgeSet->resetDraw();
}
}

SmudgeSet *SmudgeManager::addSmudgeSet()
{
SmudgeSet* set=m_freeSmudgeSetList.Head();
Expand All @@ -89,12 +97,25 @@ SmudgeSet *SmudgeManager::addSmudgeSet()
return set;
}

void SmudgeManager::removeSmudgeSet(SmudgeSet &mySmudge)
void SmudgeManager::removeSmudgeSet(SmudgeSet *&smudgeSet)
{
mySmudge.Remove(); //remove from used list
m_freeSmudgeSetList.Add_Head(&mySmudge); //add to free list.
smudgeSet->Remove(); //remove from used list
m_freeSmudgeSetList.Add_Head(smudgeSet); //add to free list.
smudgeSet = nullptr;
}

Smudge *SmudgeManager::findSmudge(Smudge::Identifier identifier)
{
SmudgeSet *smudgeSet = m_usedSmudgeSetList.Head();
for (; smudgeSet; smudgeSet = smudgeSet->Succ()) {
if (Smudge *smudge = smudgeSet->findSmudge(identifier)) {
return smudge;
}
}
return nullptr;
}


SmudgeSet::SmudgeSet()
{
m_usedSmudgeCount=0;
Expand All @@ -113,26 +134,50 @@ void SmudgeSet::reset()
m_usedSmudgeList.Remove_Head ();
m_freeSmudgeList.Add_Head(head); //add to free list
}

m_usedSmudgeMap.clear();
m_usedSmudgeCount = 0;
}

void SmudgeSet::resetDraw()
{
Smudge* smudge = m_usedSmudgeList.Head();
for (; smudge; smudge = smudge->Succ()) {
smudge->m_draw = false;
}
}

Smudge *SmudgeSet::addSmudgeToSet()
Smudge *SmudgeSet::addSmudgeToSet(Smudge::Identifier identifier)
{
Smudge* smudge=m_freeSmudgeList.Head();
DEBUG_ASSERTCRASH(m_usedSmudgeMap.find(identifier) == m_usedSmudgeMap.end(),
("SmudgeSet::addSmudgeToSet: identifier already present"));

Smudge* smudge = m_freeSmudgeList.Head();
if (smudge) {
smudge->Remove(); //remove from free list
m_usedSmudgeList.Add_Tail(smudge); //add to used list.
m_usedSmudgeCount++;
return smudge;
} else {
smudge = W3DNEW Smudge();
}
smudge=W3DNEW Smudge();
smudge->m_identifier = identifier;
m_usedSmudgeList.Add_Tail(smudge); //add to used list.
m_usedSmudgeMap[identifier] = smudge;
m_usedSmudgeCount++;
return smudge;
}

void SmudgeSet::removeSmudgeFromSet(Smudge &mySmudge)
void SmudgeSet::removeSmudgeFromSet(Smudge *&smudge)
{
mySmudge.Remove(); //remove from used list.
m_freeSmudgeList.Add_Head(&mySmudge); //add to free list
m_usedSmudgeMap.erase(smudge->m_identifier);
smudge->Remove(); //remove from used list.
m_freeSmudgeList.Add_Head(smudge); //add to free list
smudge = nullptr;
m_usedSmudgeCount--;
}

Smudge *SmudgeSet::findSmudge(Smudge::Identifier identifier)
{
SmudgeIdToPtrMap::const_iterator it = m_usedSmudgeMap.find(identifier);
if (it != m_usedSmudgeMap.end())
return it->second;
return nullptr;
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class DX8IndexBufferClass;

//#define USE_COPY_RECTS 1 //this was the old method that didn't render to texture. Just copied backbuffer into texture. Slow on Nvidia.

class W3DSmudgeManager : public SmudgeManager
class W3DSmudgeManager final : public SmudgeManager
{
public:
W3DSmudgeManager();
Expand Down
27 changes: 18 additions & 9 deletions Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DSmudge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,16 +366,20 @@ void W3DSmudgeManager::render(RenderInfoClass &rinfo)
Int count = 0;

if (set)
{ //there are possibly some smudges to render, so make sure background particles have finished drawing.
{
//there are possibly some smudges to render, so make sure background particles have finished drawing.
SortingRendererClass::Flush(); //draw sorted translucent polys like particles.
}

while (set)
{
Smudge *smudge=set->getUsedSmudgeList().Head();

while (smudge)
for (; smudge; smudge = smudge->Succ())
{
if (!smudge->m_draw)
continue;

//Get view-space center
Matrix3D::Transform_Vector(view,smudge->m_pos,&vsVert);

Expand All @@ -385,6 +389,8 @@ void W3DSmudgeManager::render(RenderInfoClass &rinfo)
//Do center vertex outside 'for' loop since it's different.
verts[4].pos = vsVert;

Vector2 offset = smudge->m_offset;

for (Int i=0; i<4; i++)
{
verts[i].pos = vsVert + vertex_offsets[i] * smudge->m_size;
Expand All @@ -399,26 +405,27 @@ void W3DSmudgeManager::render(RenderInfoClass &rinfo)

// Zero coordinates that fall outside valid texel bounds
if (thisUV.X < 0 || thisUV.X > texClampX)
smudge->m_offset.X = 0;
offset.X = 0;

if (thisUV.Y < 0 || thisUV.Y > texClampY)
smudge->m_offset.Y = 0;
offset.Y = 0;
}

//Finish center vertex
//Ge uv coordinates by interpolating corner uv coordinates and applying desired offset.
uvSpanX=verts[3].uv.X - verts[0].uv.X;
uvSpanY=verts[1].uv.Y - verts[0].uv.Y;
verts[4].uv.X=verts[0].uv.X+uvSpanX*(0.5f+smudge->m_offset.X);
verts[4].uv.Y=verts[0].uv.Y+uvSpanY*(0.5f+smudge->m_offset.Y);
verts[4].uv.X=verts[0].uv.X+uvSpanX*(0.5f+offset.X);
verts[4].uv.Y=verts[0].uv.Y+uvSpanY*(0.5f+offset.Y);

count++; //increment visible smudge count.
smudge=smudge->Succ();
}

set=set->Succ(); //advance to next node.
}

m_smudgeCountLastFrame = count;

if (!count)
{
REF_PTR_RELEASE(background);
Expand Down Expand Up @@ -481,8 +488,11 @@ void W3DSmudgeManager::render(RenderInfoClass &rinfo)
{
Smudge *smudge=remainingSmudgeStart;

while (smudge)
for (; smudge; smudge=smudge->Succ())
{
if (!smudge->m_draw)
continue;

Smudge::smudgeVertex *smVerts = smudge->m_verts;

//Check if we exceeded maximum number of smudges allowed per draw call.
Expand Down Expand Up @@ -513,7 +523,6 @@ void W3DSmudgeManager::render(RenderInfoClass &rinfo)
}

smudgesInRenderBatch++;
smudge=smudge->Succ();
}

set=set->Succ(); //advance to next node.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,6 @@ void W3DParticleSystemManager::doParticles(RenderInfoClass &rinfo)
/// @todo lorenzen sez: this should be debug only:
m_onScreenParticleCount = 0;

Int visibleSmudgeCount = 0;
if (TheSmudgeManager)
TheSmudgeManager->setSmudgeCountLastFrame(0); //keep track of visible smudges

const FrustumClass & frustum = rinfo.Camera.Get_Frustum();
AABoxClass bbox;

Expand All @@ -141,9 +137,12 @@ void W3DParticleSystemManager::doParticles(RenderInfoClass &rinfo)

m_fieldParticleCount = 0;

SmudgeSet *set=nullptr;
if (TheSmudgeManager)
set=TheSmudgeManager->addSmudgeSet(); //global smudge set through which all smudges are rendered.
const Bool drawSmudge = TheSmudgeManager && TheSmudgeManager->getHardwareSupport() && TheGlobalData->m_useHeatEffects;

if (drawSmudge)
{
TheSmudgeManager->resetDraw();
}

ParticleSystemManager::ParticleSystemList &particleSysList = TheParticleSystemManager->getAllParticleSystems();
for( ParticleSystemManager::ParticleSystemListIt it = particleSysList.begin(); it != particleSysList.end(); ++it)
Expand All @@ -160,9 +159,8 @@ void W3DParticleSystemManager::doParticles(RenderInfoClass &rinfo)
//temporary hack that checks if texture name starts with "SMUD" - if so, we can assume it's a smudge type
if (/*sys->isUsingSmudge()*/ *((DWORD *)sys->getParticleTypeName().str()) == 0x44554D53)
{
if (TheSmudgeManager && ((W3DSmudgeManager*)TheSmudgeManager)->getHardwareSupport() && TheGlobalData->m_useHeatEffects)
if (drawSmudge)
{
//set-up all the per-particle
for (Particle *p = sys->getFirstParticle(); p; p = p->m_systemNext)
{
const Coord3D *pos = p->getPosition();
Expand All @@ -178,13 +176,11 @@ void W3DParticleSystemManager::doParticles(RenderInfoClass &rinfo)
if (WWMath::Fabs( pos->z - bcZ ) > ( beZ + psize ) )
continue;

Smudge *smudge = set->addSmudgeToSet();

smudge->m_pos.Set( pos->x, pos->y, pos->z );
smudge->m_offset.Set( GameClientRandomValueReal(-0.06f,0.06f), GameClientRandomValueReal(-0.03f,0.03f) );
smudge->m_size = psize;
smudge->m_opacity = p->getAlpha();
visibleSmudgeCount++;
if (Smudge *smudge = TheSmudgeManager->findSmudge(p))
{
// The particle is in view. Draw the smudge!
smudge->m_draw = true;
}
}
}
continue;
Expand Down Expand Up @@ -376,7 +372,5 @@ void W3DParticleSystemManager::doParticles(RenderInfoClass &rinfo)
if(TheSmudgeManager)
{
((W3DSmudgeManager *)TheSmudgeManager)->render(rinfo);
TheSmudgeManager->reset(); //clear all the smudges after rendering since we fill again each frame.
TheSmudgeManager->setSmudgeCountLastFrame(visibleSmudgeCount);
}
}
Loading