- rewrote dynamic lights to not use actors for the internal representation and made DynamicLight a purely scripted class.

This should be less of a drag on the playsim than having each light a separate actor. A quick check with ZDCMP2 showed that the light processing time was reduced to 1/3rd from 0.5 ms to 0.17 ms per tic.
It's also one native actor class less.
This commit is contained in:
Christoph Oelckers 2019-01-01 19:35:55 +01:00
parent 99479d84b1
commit d654e02dea
34 changed files with 459 additions and 433 deletions

View file

@ -1235,7 +1235,7 @@ public:
DVector3 Prev;
DRotator PrevAngles;
int PrevPortalGroup;
TArray<TObjPtr<AActor*> > AttachedLights;
TArray<FDynamicLight *> AttachedLights;
// When was this actor spawned?
int SpawnTime;
@ -1489,13 +1489,11 @@ public:
int ApplyDamageFactor(FName damagetype, int damage) const;
int GetModifiedDamage(FName damagetype, int damage, bool passive);
void DeleteAttachedLights();
static void DeleteAllAttachedLights();
static void RecreateAllAttachedLights();
bool hasmodel;
size_t PropagateMark();
};
class FActorIterator

View file

@ -749,8 +749,7 @@ void D_Display ()
// Check for the presence of dynamic lights at the start of the frame once.
if ((gl_lights && vid_rendermode == 4) || (r_dynlights && vid_rendermode != 4))
{
TThinkerIterator<ADynamicLight> it(STAT_DLIGHT);
level.HasDynamicLights = !!it.Next();
level.HasDynamicLights = !!level.lights;
}
else level.HasDynamicLights = false; // lights are off so effectively we have none.

View file

@ -38,6 +38,7 @@
#include <type_traits>
#include "doomtype.h"
#include "i_system.h"
#include "vectors.h"
class PClass;
class PType;
@ -238,6 +239,7 @@ public:
inline PalEntry &ColorVar(FName field);
inline FName &NameVar(FName field);
inline double &FloatVar(FName field);
inline DAngle &AngleVar(FName field);
inline FString &StringVar(FName field);
template<class T> T*& PointerVar(FName field);
@ -456,6 +458,11 @@ inline double &DObject::FloatVar(FName field)
return *(double*)ScriptVar(field, nullptr);
}
inline DAngle &DObject::AngleVar(FName field)
{
return *(DAngle*)ScriptVar(field, nullptr);
}
template<class T>
inline T *&DObject::PointerVar(FName field)
{

View file

@ -40,6 +40,8 @@
#include "vm.h"
#include "c_dispatch.h"
#include "v_text.h"
#include "g_levellocals.h"
#include "a_dynlight.h"
static int ThinkCount;
@ -617,6 +619,13 @@ void DThinker::RunThinkers ()
count += TickThinkers(&FreshThinkers[i], &Thinkers[i]);
}
} while (count != 0);
for (auto light = level.lights; light;)
{
auto next = light->next;
light->Tick();
light = next;
}
}
else
{
@ -637,6 +646,19 @@ void DThinker::RunThinkers ()
}
} while (count != 0);
// Also profile the internal dynamic lights, even though they are not implemented as thinkers.
auto &prof = Profiles[NAME_InternalDynamicLight];
prof.timer.Clock();
for (auto light = level.lights; light;)
{
prof.numcalls++;
auto next = light->next;
light->Tick();
light = next;
}
prof.timer.Unclock();
struct SortedProfileInfo
{
const char* className;

View file

@ -198,6 +198,8 @@ struct FLevelLocals : public FLevelData
bool lightadditivesurfaces;
bool notexturefill;
FDynamicLight *lights;
bool IsJumpingAllowed() const;
bool IsCrouchingAllowed() const;
bool IsFreelookAllowed() const;

View file

@ -63,7 +63,11 @@
#include "g_levellocals.h"
#include "a_dynlight.h"
#include "actorinlines.h"
#include "memarena.h"
static FMemArena DynLightArena(sizeof(FDynamicLight) * 200);
static TArray<FDynamicLight*> FreeList;
static FRandom randLight;
CUSTOM_CVAR (Bool, gl_lights, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
{
@ -76,7 +80,7 @@ CVAR (Bool, gl_attachedlights, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
//==========================================================================
//
//==========================================================================
DEFINE_CLASS_PROPERTY(type, S, DynamicLight)
DEFINE_SCRIPTED_PROPERTY(type, S, DynamicLight)
{
PROP_STRING_PARM(str, 0);
static const char * ltype_names[]={
@ -87,105 +91,143 @@ DEFINE_CLASS_PROPERTY(type, S, DynamicLight)
int style = MatchString(str, ltype_names);
if (style < 0) I_Error("Unknown light type '%s'", str);
defaults->lighttype = ltype_values[style];
defaults->IntVar(NAME_lighttype) = ltype_values[style];
}
//==========================================================================
//
// Actor classes
//
// For flexibility all functionality has been packed into a single class
// which is controlled by flags
//
//==========================================================================
IMPLEMENT_CLASS(ADynamicLight, false, false)
DEFINE_FIELD(ADynamicLight, SpotInnerAngle)
DEFINE_FIELD(ADynamicLight, SpotOuterAngle)
static FRandom randLight;
//==========================================================================
//
// Base class
//
//==========================================================================
//==========================================================================
//
//
//
//==========================================================================
void ADynamicLight::Serialize(FSerializer &arc)
static FDynamicLight *GetLight()
{
Super::Serialize (arc);
auto def = static_cast<ADynamicLight*>(GetDefault());
arc("lightflags", lightflags, def->lightflags)
("lighttype", lighttype, def->lighttype)
("tickcount", m_tickCount, def->m_tickCount)
("currentradius", m_currentRadius, def->m_currentRadius)
("spotinnerangle", SpotInnerAngle, def->SpotInnerAngle)
("spotouterangle", SpotOuterAngle, def->SpotOuterAngle);
if (lighttype == PulseLight)
arc("lastupdate", m_lastUpdate, def->m_lastUpdate)
("cycler", m_cycler, def->m_cycler);
// Remap the old flags.
if (SaveVersion < 4552)
FDynamicLight *ret;
if (FreeList.Size())
{
lightflags = 0;
if (flags4 & MF4_MISSILEEVENMORE) lightflags |= LF_SUBTRACTIVE;
if (flags4 & MF4_MISSILEMORE) lightflags |= LF_ADDITIVE;
if (flags4 & MF4_SEESDAGGERS) lightflags |= LF_DONTLIGHTSELF;
if (flags4 & MF4_INCOMBAT) lightflags |= LF_ATTENUATE;
if (flags4 & MF4_STANDSTILL) lightflags |= LF_NOSHADOWMAP;
if (flags4 & MF4_EXTREMEDEATH) lightflags |= LF_DONTLIGHTACTORS;
flags4 &= ~(MF4_SEESDAGGERS); // this flag is dangerous and must be cleared. The others do not matter.
FreeList.Pop(ret);
}
else ret = (FDynamicLight*)DynLightArena.Alloc(sizeof(FDynamicLight));
memset(ret, 0, sizeof(*ret));
ret->next = level.lights;
level.lights = ret;
if (ret->next) ret->next->prev = ret;
ret->visibletoplayer = true;
ret->mShadowmapIndex = 1024;
ret->Pos.X = -10000000; // not a valid coordinate.
return ret;
}
void ADynamicLight::PostSerialize()
//==========================================================================
//
// Attaches a dynamic light descriptor to a dynamic light actor.
// Owned lights do not use this function.
//
//==========================================================================
void AttachLight(AActor *self)
{
Super::PostSerialize();
// The default constructor which is used for creating objects before deserialization will not set this variable.
// It needs to be true for all placed lights.
visibletoplayer = true;
mShadowmapIndex = 1024;
LinkLight();
auto light = GetLight();
light->pSpotInnerAngle = &self->AngleVar(NAME_SpotInnerAngle);
light->pSpotOuterAngle = &self->AngleVar(NAME_SpotOuterAngle);
light->pPitch = &self->Angles.Pitch;
light->pArgs = self->args;
light->specialf1 = DAngle(double(self->SpawnAngle)).Normalized360().Degrees;
light->Sector = self->Sector;
light->target = self;
light->lightflags.FromInt(self->IntVar(NAME_lightflags));
light->mShadowmapIndex = 1024;
light->m_active = false;
light->visibletoplayer = true;
light->lighttype = (uint8_t)self->IntVar(NAME_lighttype);
self->AttachedLights.Push(light);
}
DEFINE_ACTION_FUNCTION_NATIVE(ADynamicLight, AttachLight, AttachLight)
{
PARAM_SELF_PROLOGUE(AActor);
AttachLight(self);
return 0;
}
//==========================================================================
//
// [TS]
//
//
//==========================================================================
void ADynamicLight::BeginPlay()
{
//Super::BeginPlay();
ChangeStatNum(STAT_DLIGHT);
specialf1 = DAngle(double(SpawnAngle)).Normalized360().Degrees;
visibletoplayer = true;
mShadowmapIndex = 1024;
void ActivateLight(AActor *self)
{
for (auto l : self->AttachedLights) l->Activate();
}
DEFINE_ACTION_FUNCTION_NATIVE(ADynamicLight, ActivateLight, ActivateLight)
{
PARAM_SELF_PROLOGUE(AActor);
ActivateLight(self);
return 0;
}
//==========================================================================
//
//
//
//==========================================================================
void DeactivateLight(AActor *self)
{
for (auto l : self->AttachedLights) l->Activate();
}
DEFINE_ACTION_FUNCTION_NATIVE(ADynamicLight, DeactivateLight, DeactivateLight)
{
PARAM_SELF_PROLOGUE(AActor);
DeactivateLight(self);
return 0;
}
//==========================================================================
//
// [TS]
//
//
//==========================================================================
void ADynamicLight::PostBeginPlay()
{
Super::PostBeginPlay();
if (!(SpawnFlags & MTF_DORMANT))
static void SetOffset(AActor *self, double x, double y, double z)
{
Activate (nullptr);
for (auto l : self->AttachedLights)
{
l->SetOffset(DVector3(x, y, z));
}
}
subsector = R_PointInSubsector(Pos());
DEFINE_ACTION_FUNCTION_NATIVE(ADynamicLight, SetOffset, SetOffset)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_FLOAT(x);
PARAM_FLOAT(y);
PARAM_FLOAT(z);
SetOffset(self, x, y, z);
return 0;
}
//==========================================================================
//
//
//
//==========================================================================
void FDynamicLight::ReleaseLight()
{
assert(prev != nullptr || this == level.lights);
if (prev != nullptr) prev->next = next;
else level.lights = next;
if (next != nullptr) next->prev = prev;
prev = nullptr;
FreeList.Push(this);
Printf("Releasing %p\n", this);
}
@ -194,12 +236,10 @@ void ADynamicLight::PostBeginPlay()
// [TS]
//
//==========================================================================
void ADynamicLight::Activate(AActor *activator)
void FDynamicLight::Activate()
{
//Super::Activate(activator);
flags2&=~MF2_DORMANT;
m_currentRadius = float(args[LIGHT_INTENSITY]);
m_active = true;
m_currentRadius = float(GetIntensity());
m_tickCount = 0;
if (lighttype == PulseLight)
@ -207,8 +247,8 @@ void ADynamicLight::Activate(AActor *activator)
float pulseTime = float(specialf1 / TICRATE);
m_lastUpdate = level.maptime;
if (!swapped) m_cycler.SetParams(float(args[LIGHT_SECONDARY_INTENSITY]), float(args[LIGHT_INTENSITY]), pulseTime);
else m_cycler.SetParams(float(args[LIGHT_INTENSITY]), float(args[LIGHT_SECONDARY_INTENSITY]), pulseTime);
if (!swapped) m_cycler.SetParams(float(GetSecondaryIntensity()), float(GetIntensity()), pulseTime);
else m_cycler.SetParams(float(GetIntensity()), float(GetSecondaryIntensity()), pulseTime);
m_cycler.ShouldCycle(true);
m_cycler.SetCycleType(CYCLE_Sin);
m_currentRadius = float(m_cycler.GetVal());
@ -222,28 +262,26 @@ void ADynamicLight::Activate(AActor *activator)
// [TS]
//
//==========================================================================
void ADynamicLight::Deactivate(AActor *activator)
void FDynamicLight::Tick()
{
//Super::Deactivate(activator);
flags2|=MF2_DORMANT;
}
//==========================================================================
//
// [TS]
//
//==========================================================================
void ADynamicLight::Tick()
if (!target)
{
if (IsOwned())
{
if (!target || !target->state)
{
this->Destroy();
delete this;
return;
}
if (owned)
{
if (!target->state)
{
delete this;
return;
}
if (target->flags & MF_UNMORPHED)
{
m_active = false;
return;
}
if (target->flags & MF_UNMORPHED) return;
visibletoplayer = target->IsVisibleToPlayer(); // cache this value for the renderer to speed up calculations.
}
@ -266,25 +304,23 @@ void ADynamicLight::Tick()
case FlickerLight:
{
int rnd = randLight();
float pct = float(specialf1 / 360.f);
m_currentRadius = float(args[LIGHT_INTENSITY + (rnd >= pct * 255)]);
int rnd = randLight(360);
m_currentRadius = float((rnd >= int(specialf1))? GetIntensity() : GetSecondaryIntensity());
break;
}
case RandomFlickerLight:
{
int flickerRange = args[LIGHT_SECONDARY_INTENSITY] - args[LIGHT_INTENSITY];
int flickerRange = GetSecondaryIntensity() - GetIntensity();
float amt = randLight() / 255.f;
if (m_tickCount > specialf1)
{
m_tickCount = 0;
}
if (m_tickCount++ == 0 || m_currentRadius > args[LIGHT_SECONDARY_INTENSITY])
if (m_tickCount++ == 0 || m_currentRadius > GetSecondaryIntensity())
{
m_currentRadius = float(args[LIGHT_INTENSITY] + (amt * flickerRange));
m_currentRadius = float(GetIntensity() + (amt * flickerRange));
}
break;
}
@ -302,14 +338,14 @@ void ADynamicLight::Tick()
case RandomColorFlickerLight:
{
int flickerRange = args[LIGHT_SECONDARY_INTENSITY] - args[LIGHT_INTENSITY];
int flickerRange = GetSecondaryIntensity() - GetIntensity();
float amt = randLight() / 255.f;
m_tickCount++;
if (m_tickCount > specialf1)
{
m_currentRadius = args[LIGHT_INTENSITY] + (amt * flickerRange);
m_currentRadius = GetIntensity() + (amt * flickerRange);
m_tickCount = 0;
}
break;
@ -319,7 +355,7 @@ void ADynamicLight::Tick()
case SectorLight:
{
float intensity;
float scale = args[LIGHT_SCALE] / 8.f;
float scale = GetIntensity() / 8.f;
if (scale == 0.f) scale = 1.f;
@ -331,7 +367,7 @@ void ADynamicLight::Tick()
}
case PointLight:
m_currentRadius = float(args[LIGHT_INTENSITY]);
m_currentRadius = float(GetIntensity());
break;
}
if (m_currentRadius <= 0) m_currentRadius = 1;
@ -346,45 +382,37 @@ void ADynamicLight::Tick()
//
//
//==========================================================================
void ADynamicLight::UpdateLocation()
void FDynamicLight::UpdateLocation()
{
double oldx= X();
double oldy= Y();
double oldradius= radius;
float intensity;
float oldradius = radius;
if (IsActive())
{
if (target)
{
AActor *target = this->target; // perform the read barrier only once.
// Offset is calculated in relation to the owning actor.
DAngle angle = target->Angles.Yaw;
double s = angle.Sin();
double c = angle.Cos();
DVector3 pos = target->Vec3Offset(m_off.X * c + m_off.Y * s, m_off.X * s - m_off.Y * c, m_off.Z + target->GetBobOffset());
SetXYZ(pos); // attached lights do not need to go into the regular blockmap
Prev = target->Pos();
subsector = R_PointInSubsector(Prev);
Sector = subsector->sector;
Pos = target->Vec3Offset(m_off.X * c + m_off.Y * s, m_off.X * s - m_off.Y * c, m_off.Z + target->GetBobOffset());
Sector = target->subsector->sector; // Get the render sector. target->Sector is the sector according to play logic.
// Some z-coordinate fudging to prevent the light from getting too close to the floor or ceiling planes. With proper attenuation this would render them invisible.
// A distance of 5 is needed so that the light's effect doesn't become too small.
if (Z() < target->floorz + 5.) SetZ(target->floorz + 5.);
else if (Z() > target->ceilingz - 5.) SetZ(target->ceilingz - 5.);
}
else
{
if (Z() < floorz + 5.) SetZ(floorz + 5.);
else if (Z() > ceilingz - 5.) SetZ(ceilingz - 5.);
}
if (Z() < target->floorz + 5.) Pos.Z = target->floorz + 5.;
else if (Z() > target->ceilingz - 5.) Pos.Z = target->ceilingz - 5.;
// The radius being used here is always the maximum possible with the
// current settings. This avoids constant relinking of flickering lights
float intensity;
if (lighttype == FlickerLight || lighttype == RandomFlickerLight || lighttype == PulseLight)
{
intensity = float(MAX(args[LIGHT_INTENSITY], args[LIGHT_SECONDARY_INTENSITY]));
intensity = float(MAX(GetIntensity(), GetSecondaryIntensity()));
}
else
{
@ -401,60 +429,6 @@ void ADynamicLight::UpdateLocation()
}
}
//==========================================================================
//
//
//
//==========================================================================
void ADynamicLight::SetOrigin(double x, double y, double z, bool moving)
{
Super::SetOrigin(x, y, z, moving);
LinkLight();
}
//==========================================================================
//
//
//
//==========================================================================
void ADynamicLight::SetOffset(const DVector3 &pos)
{
m_off = pos;
UpdateLocation();
}
static void SetOffset(ADynamicLight *self, double x, double y, double z)
{
self->SetOffset(DVector3(x, y, z));
}
DEFINE_ACTION_FUNCTION_NATIVE(ADynamicLight, SetOffset, SetOffset)
{
PARAM_SELF_PROLOGUE(ADynamicLight)
PARAM_FLOAT(x)
PARAM_FLOAT(y)
PARAM_FLOAT(z)
self->SetOffset(DVector3(x, y, z));
return 0;
}
//==========================================================================
//
// The target pointer in dynamic lights should never be substituted unless
// notOld is nullptr (which indicates that the object was destroyed by force.)
//
//==========================================================================
size_t ADynamicLight::PointerSubstitution (DObject *old, DObject *notOld)
{
AActor *saved_target = target;
size_t ret = Super::PointerSubstitution(old, notOld);
if (notOld != nullptr) target = saved_target;
return ret;
}
//=============================================================================
//
// These have been copied from the secnode code and modified for the light links
@ -466,7 +440,7 @@ size_t ADynamicLight::PointerSubstitution (DObject *old, DObject *notOld)
//
//=============================================================================
FLightNode * AddLightNode(FLightNode ** thread, void * linkto, ADynamicLight * light, FLightNode *& nextnode)
FLightNode * AddLightNode(FLightNode ** thread, void * linkto, FDynamicLight * light, FLightNode *& nextnode)
{
FLightNode * node;
@ -541,7 +515,7 @@ static FLightNode * DeleteLightNode(FLightNode * node)
//
//==========================================================================
double ADynamicLight::DistToSeg(const DVector3 &pos, vertex_t *start, vertex_t *end)
double FDynamicLight::DistToSeg(const DVector3 &pos, vertex_t *start, vertex_t *end)
{
double u, px, py;
@ -576,7 +550,7 @@ struct LightLinkEntry
};
static TArray<LightLinkEntry> collected_ss;
void ADynamicLight::CollectWithinRadius(const DVector3 &opos, FSection *section, float radius)
void FDynamicLight::CollectWithinRadius(const DVector3 &opos, FSection *section, float radius)
{
if (!section) return;
collected_ss.Clear();
@ -621,7 +595,7 @@ void ADynamicLight::CollectWithinRadius(const DVector3 &opos, FSection *section,
if (othersect->validcount != ::validcount)
{
othersect->validcount = ::validcount;
collected_ss.Push({ othersect, PosRelative(other) });
collected_ss.Push({ othersect, PosRelative(other->frontsector->PortalGroup) });
}
}
}
@ -672,7 +646,7 @@ void ADynamicLight::CollectWithinRadius(const DVector3 &opos, FSection *section,
if (othersect->validcount != dl_validcount)
{
othersect->validcount = dl_validcount;
collected_ss.Push({ othersect, PosRelative(othersub->sector) });
collected_ss.Push({ othersect, PosRelative(othersub->sector->PortalGroup) });
}
}
}
@ -687,7 +661,7 @@ void ADynamicLight::CollectWithinRadius(const DVector3 &opos, FSection *section,
if (othersect->validcount != dl_validcount)
{
othersect->validcount = dl_validcount;
collected_ss.Push({ othersect, PosRelative(othersub->sector) });
collected_ss.Push({ othersect, PosRelative(othersub->sector->PortalGroup) });
}
}
}
@ -701,7 +675,7 @@ void ADynamicLight::CollectWithinRadius(const DVector3 &opos, FSection *section,
//
//==========================================================================
void ADynamicLight::LinkLight()
void FDynamicLight::LinkLight()
{
// mark the old light nodes
FLightNode * node;
@ -722,11 +696,11 @@ void ADynamicLight::LinkLight()
if (radius>0)
{
// passing in radius*radius allows us to do a distance check without any calls to sqrt
FSection *sect = R_PointInSubsector(Pos())->section;
FSection *sect = R_PointInSubsector(Pos)->section;
dl_validcount++;
::validcount++;
CollectWithinRadius(Pos(), sect, float(radius*radius));
CollectWithinRadius(Pos, sect, float(radius*radius));
}
@ -762,32 +736,13 @@ void ADynamicLight::LinkLight()
// Deletes the link lists
//
//==========================================================================
void ADynamicLight::UnlinkLight ()
void FDynamicLight::UnlinkLight ()
{
if (owned && target != nullptr)
{
// Delete reference in owning actor
for(int c=target->AttachedLights.Size()-1; c>=0; c--)
{
if (target->AttachedLights[c] == this)
{
target->AttachedLights.Delete(c);
break;
}
}
}
while (touching_sides) touching_sides = DeleteLightNode(touching_sides);
while (touching_sector) touching_sector = DeleteLightNode(touching_sector);
shadowmapped = false;
}
void ADynamicLight::OnDestroy()
{
UnlinkLight();
Super::OnDestroy();
}
//==========================================================================
//
//
@ -796,23 +751,19 @@ void ADynamicLight::OnDestroy()
void AActor::AttachLight(unsigned int count, const FLightDefaults *lightdef)
{
ADynamicLight *light;
FDynamicLight *light;
if (count < AttachedLights.Size())
{
light = barrier_cast<ADynamicLight*>(AttachedLights[count]);
light = AttachedLights[count];
assert(light != nullptr);
}
else
{
light = Spawn<ADynamicLight>(Pos(), NO_REPLACE);
light->target = this;
light->owned = true;
light->ObjectFlags |= OF_Transient;
//light->lightflags |= LF_ATTENUATE;
light = GetLight();
light->SetActor(this, true);
AttachedLights.Push(light);
}
light->flags2&=~MF2_DORMANT;
lightdef->ApplyProperties(light);
}
@ -831,11 +782,8 @@ void AActor::SetDynamicLights()
if (state == nullptr) return;
if (LightAssociations.Size() > 0)
{
ADynamicLight *lights, *tmpLight;
unsigned int i;
lights = tmpLight = nullptr;
for (i = 0; i < LightAssociations.Size(); i++)
{
if (LightAssociations[i]->Sprite() == sprite &&
@ -858,24 +806,24 @@ void AActor::SetDynamicLights()
for(;count<AttachedLights.Size();count++)
{
AttachedLights[count]->flags2 |= MF2_DORMANT;
memset(AttachedLights[count]->args, 0, 3*sizeof(args[0]));
AttachedLights[count]->Deactivate();
}
}
//==========================================================================
//
// Needed for garbage collection
//
//
//==========================================================================
size_t AActor::PropagateMark()
void AActor::DeleteAttachedLights()
{
for (unsigned i = 0; i<AttachedLights.Size(); i++)
for (auto l : AttachedLights)
{
GC::Mark(AttachedLights[i]);
l->UnlinkLight();
l->ReleaseLight();
}
return Super::PropagateMark();
AttachedLights.Clear();
}
//==========================================================================
@ -888,21 +836,10 @@ void AActor::DeleteAllAttachedLights()
{
TThinkerIterator<AActor> it;
AActor * a;
ADynamicLight * l;
while ((a=it.Next()))
{
a->AttachedLights.Clear();
}
TThinkerIterator<ADynamicLight> it2;
l=it2.Next();
while (l)
{
ADynamicLight * ll = it2.Next();
if (l->owned) l->Destroy();
l=ll;
a->DeleteAttachedLights();
}
}
@ -918,10 +855,17 @@ void AActor::RecreateAllAttachedLights()
AActor * a;
while ((a=it.Next()))
{
if (a->IsKindOf(NAME_DynamicLight))
{
::AttachLight(a);
}
else
{
a->SetDynamicLights();
}
}
}
//==========================================================================
//
@ -934,17 +878,16 @@ CCMD(listlights)
int walls, sectors;
int allwalls=0, allsectors=0, allsubsecs = 0;
int i=0, shadowcount = 0;
ADynamicLight * dl;
TThinkerIterator<ADynamicLight> it;
FDynamicLight * dl;
while ((dl=it.Next()))
for (dl = level.lights; dl; dl = dl->next)
{
walls=0;
sectors=0;
Printf("%s at (%f, %f, %f), color = 0x%02x%02x%02x, radius = %f %s %s",
dl->target? dl->target->GetClass()->TypeName.GetChars() : dl->GetClass()->TypeName.GetChars(),
dl->X(), dl->Y(), dl->Z(), dl->args[LIGHT_RED],
dl->args[LIGHT_GREEN], dl->args[LIGHT_BLUE], dl->radius, (dl->lightflags & LF_ATTENUATE)? "attenuated" : "", dl->shadowmapped? "shadowmapped" : "");
dl->target->GetClass()->TypeName.GetChars(),
dl->X(), dl->Y(), dl->Z(), dl->GetRed(), dl->GetGreen(), dl->GetBlue(),
dl->radius, (dl->lightflags & LF_ATTENUATE)? "attenuated" : "", dl->shadowmapped? "shadowmapped" : "");
i++;
shadowcount += dl->shadowmapped;
@ -981,4 +924,3 @@ CCMD(listlights)
Printf("%i dynamic lights, %d shadowmapped, %d walls, %d sectors\n\n\n", i, shadowcount, allwalls, allsectors);
}

View file

@ -2,6 +2,7 @@
#include "c_cvars.h"
#include "actor.h"
#include "cycler.h"
#include "g_levellocals.h"
EXTERN_CVAR(Bool, r_dynlights)
EXTERN_CVAR(Bool, gl_lights)
@ -10,7 +11,6 @@ EXTERN_CVAR(Bool, gl_attachedlights)
struct side_t;
struct seg_t;
class ADynamicLight;
class FSerializer;
struct FSectionLine;
@ -34,7 +34,6 @@ enum
LIGHT_BLUE = 2,
LIGHT_INTENSITY = 3,
LIGHT_SECONDARY_INTENSITY = 4,
LIGHT_SCALE = 3,
};
enum LightFlag
@ -58,7 +57,7 @@ class FLightDefaults
public:
FLightDefaults(FName name, ELightType type);
void ApplyProperties(ADynamicLight * light) const;
void ApplyProperties(FDynamicLight * light) const;
FName GetName() const { return m_Name; }
void SetParameter(double p) { m_Param = p; }
void SetArg(int arg, int val) { m_Args[arg] = val; }
@ -98,8 +97,10 @@ protected:
bool m_dontlightactors = false;
bool m_swapped = false;
bool m_spot = false;
double m_spotInnerAngle = 10.0;
double m_spotOuterAngle = 25.0;
bool m_explicitPitch = false;
DAngle m_spotInnerAngle = 10.0;
DAngle m_spotOuterAngle = 25.0;
DAngle m_pitch = 0.0;
};
//==========================================================================
@ -159,7 +160,7 @@ struct FLightNode
FLightNode * nextTarget;
FLightNode ** prevLight;
FLightNode * nextLight;
ADynamicLight * lightsource;
FDynamicLight * lightsource;
union
{
side_t * targLine;
@ -168,72 +169,85 @@ struct FLightNode
};
};
//
// Base class
//
// [CO] I merged everything together in this one class so that I don't have
// to create and re-create an excessive amount of objects
//
class ADynamicLight : public AActor
struct FDynamicLight
{
friend class FLightDefaults;
DECLARE_CLASS(ADynamicLight, AActor)
public:
virtual void Tick();
void Serialize(FSerializer &arc);
void PostSerialize();
uint8_t GetRed() const { return args[LIGHT_RED]; }
uint8_t GetGreen() const { return args[LIGHT_GREEN]; }
uint8_t GetBlue() const { return args[LIGHT_BLUE]; }
inline DVector3 PosRelative(int portalgroup) const
{
return Pos + level.Displacements.getOffset(Sector->PortalGroup, portalgroup);
}
bool ShouldLightActor(AActor *check)
{
return visibletoplayer && IsActive() && (!(lightflags & LF_DONTLIGHTSELF) || target != check) && !(lightflags&LF_DONTLIGHTACTORS);
}
void SetOffset(const DVector3 &pos)
{
m_off = pos;
}
bool IsActive() const { return m_active; }
float GetRadius() const { return (IsActive() ? m_currentRadius * 2.f : 0.f); }
int GetRed() const { return pArgs[LIGHT_RED]; }
int GetGreen() const { return pArgs[LIGHT_GREEN]; }
int GetBlue() const { return pArgs[LIGHT_BLUE]; }
int GetIntensity() const { return pArgs[LIGHT_INTENSITY]; }
int GetSecondaryIntensity() const { return pArgs[LIGHT_SECONDARY_INTENSITY]; }
bool IsSubtractive() const { return !!(lightflags & LF_SUBTRACTIVE); }
bool IsAdditive() const { return !!(lightflags & LF_ADDITIVE); }
bool IsSpot() const { return !!(lightflags & LF_SPOT); }
void Deactivate() { m_active = false; }
void Activate();
void SetActor(AActor *ac, bool isowned) { target = ac; owned = isowned; }
double X() const { return Pos.X; }
double Y() const { return Pos.Y; }
double Z() const { return Pos.Z; }
void Tick();
void UpdateLocation();
void LinkLight();
void UnlinkLight();
size_t PointerSubstitution(DObject *old, DObject *notOld);
void BeginPlay();
void SetOrigin(double x, double y, double z, bool moving = false);
void PostBeginPlay();
void OnDestroy() override;
void Activate(AActor *activator);
void Deactivate(AActor *activator);
void SetOffset(const DVector3 &pos);
void UpdateLocation();
bool IsOwned() const { return owned; }
bool IsActive() const { return !(flags2&MF2_DORMANT); }
bool IsSubtractive() { return !!(lightflags & LF_SUBTRACTIVE); }
bool IsAdditive() { return !!(lightflags & LF_ADDITIVE); }
bool IsSpot() { return !!(lightflags & LF_SPOT); }
FState *targetState;
FLightNode * touching_sides;
FLightNode * touching_sector;
void ReleaseLight();
private:
double DistToSeg(const DVector3 &pos, vertex_t *start, vertex_t *end);
void CollectWithinRadius(const DVector3 &pos, FSection *section, float radius);
protected:
DVector3 m_off;
float m_currentRadius;
unsigned int m_lastUpdate;
FCycler m_cycler;
subsector_t * subsector;
public:
FCycler m_cycler;
DVector3 Pos;
DVector3 m_off;
// This date can either come from the owning actor or from a light definition
// To avoid having to copy these around every tic, these are pointers to the source data.
const DAngle *pSpotInnerAngle;
const DAngle *pSpotOuterAngle;
const DAngle *pPitch; // This is to handle pitch overrides through GLDEFS, it can either point to the target's pitch or the light definition.
const int *pArgs;
double specialf1;
FDynamicLight *next, *prev;
sector_t *Sector;
TObjPtr<AActor *> target;
FLightNode * touching_sides;
FLightNode * touching_sector;
LightFlags lightflags;
float radius; // The maximum size the light can be with its current settings.
float m_currentRadius; // The current light size.
int m_tickCount;
int m_lastUpdate;
int mShadowmapIndex;
bool m_active;
bool visibletoplayer;
bool shadowmapped;
uint8_t lighttype;
bool owned;
bool halo;
uint8_t color2[3];
bool visibletoplayer;
bool swapped;
bool shadowmapped;
int bufferindex;
LightFlags lightflags;
DAngle SpotInnerAngle;
DAngle SpotOuterAngle;
int mShadowmapIndex;
bool explicitpitch;
};

View file

@ -44,12 +44,13 @@ CVAR (Bool, gl_light_particles, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
CVAR(Int, gl_attenuate, -1, 0); // This is mainly a debug option.
//==========================================================================
//
// Sets up the parameters to render one dynamic light onto one plane
//
//==========================================================================
bool FDynLightData::GetLight(int group, Plane & p, ADynamicLight * light, bool checkside)
bool FDynLightData::GetLight(int group, Plane & p, FDynamicLight * light, bool checkside)
{
DVector3 pos = light->PosRelative(group);
float radius = (light->GetRadius());
@ -72,7 +73,7 @@ bool FDynLightData::GetLight(int group, Plane & p, ADynamicLight * light, bool c
// Add one dynamic light to the light data list
//
//==========================================================================
void FDynLightData::AddLightToList(int group, ADynamicLight * light, bool forceAttenuate)
void FDynLightData::AddLightToList(int group, FDynamicLight * light, bool forceAttenuate)
{
int i = 0;
@ -123,14 +124,15 @@ void FDynLightData::AddLightToList(int group, ADynamicLight * light, bool forceA
if (light->IsSpot())
{
lightType = 1.0f;
spotInnerAngle = (float)light->SpotInnerAngle.Cos();
spotOuterAngle = (float)light->SpotOuterAngle.Cos();
spotInnerAngle = (float)light->pSpotInnerAngle->Cos();
spotOuterAngle = (float)light->pSpotOuterAngle->Cos();
DAngle negPitch = -light->Angles.Pitch;
DAngle negPitch = -*light->pPitch;
DAngle Angle = light->target->Angles.Yaw;
double xzLen = negPitch.Cos();
spotDirX = float(-light->Angles.Yaw.Cos() * xzLen);
spotDirX = float(-Angle.Cos() * xzLen);
spotDirY = float(-negPitch.Sin());
spotDirZ = float(-light->Angles.Yaw.Sin() * xzLen);
spotDirZ = float(-Angle.Sin() * xzLen);
}
float *data = &arrays[i][arrays[i].Reserve(16)];

View file

@ -53,8 +53,8 @@ struct FDynLightData
if (siz[2] > max) siz[2] = max;
}
bool GetLight(int group, Plane & p, ADynamicLight * light, bool checkside);
void AddLightToList(int group, ADynamicLight * light, bool forceAttenuate);
bool GetLight(int group, Plane & p, FDynamicLight * light, bool checkside);
void AddLightToList(int group, FDynamicLight * light, bool forceAttenuate);
};

View file

@ -85,19 +85,19 @@ CUSTOM_CVAR (Bool, gl_light_shadowmap, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (!self)
{
// Unset any residual shadow map indices in the light actors.
TThinkerIterator<ADynamicLight> it(STAT_DLIGHT);
while (auto light = it.Next())
auto light = level.lights;
while (light)
{
light->mShadowmapIndex = 1024;
light = light->next;
}
}
}
bool IShadowMap::ShadowTest(ADynamicLight *light, const DVector3 &pos)
bool IShadowMap::ShadowTest(FDynamicLight *light, const DVector3 &pos)
{
if (light->shadowmapped && light->radius > 0.0 && IsEnabled() && mAABBTree)
return mAABBTree->RayTest(light->Pos(), pos) >= 1.0f;
if (light->shadowmapped && light->GetRadius() > 0.0 && IsEnabled() && mAABBTree)
return mAABBTree->RayTest(light->Pos, pos) >= 1.0f;
else
return true;
}
@ -113,8 +113,7 @@ void IShadowMap::CollectLights()
int lightindex = 0;
// Todo: this should go through the blockmap in a spiral pattern around the player so that closer lights are preferred.
TThinkerIterator<ADynamicLight> it(STAT_DLIGHT);
while (auto light = it.Next())
for (auto light = level.lights; light; light = light->next)
{
LightsProcessed++;
if (light->shadowmapped && light->IsActive() && lightindex < 1024 * 4)

View file

@ -5,7 +5,7 @@
#include "stats.h"
#include <memory>
class ADynamicLight;
struct FDynamicLight;
struct level_info_t;
class IDataBuffer;
@ -16,7 +16,7 @@ public:
virtual ~IShadowMap();
// Test if a world position is in shadow relative to the specified light and returns false if it is
bool ShadowTest(ADynamicLight *light, const DVector3 &pos);
bool ShadowTest(FDynamicLight *light, const DVector3 &pos);
// Returns true if gl_light_shadowmap is enabled and supported by the hardware
bool IsEnabled() const;

View file

@ -148,9 +148,9 @@ void GLFlat::SetupLights(HWDrawInfo *di, FLightNode * node, FDynLightData &light
}
while (node)
{
ADynamicLight * light = node->lightsource;
FDynamicLight * light = node->lightsource;
if (light->flags2&MF2_DORMANT)
if (!light->IsActive())
{
node = node->nextLight;
continue;
@ -159,7 +159,7 @@ void GLFlat::SetupLights(HWDrawInfo *di, FLightNode * node, FDynLightData &light
// we must do the side check here because gl_GetLight needs the correct plane orientation
// which we don't have for Legacy-style 3D-floors
double planeh = plane.plane.ZatPoint(light);
double planeh = plane.plane.ZatPoint(light->Pos);
if ((planeh<light->Z() && ceiling) || (planeh>light->Z() && !ceiling))
{
node = node->nextLight;

View file

@ -117,9 +117,9 @@ int HWDrawInfo::SetupLightsForOtherPlane(subsector_t * sub, FDynLightData &light
lightdata.Clear();
while (node)
{
ADynamicLight * light = node->lightsource;
FDynamicLight * light = node->lightsource;
if (light->flags2&MF2_DORMANT)
if (!light->IsActive())
{
node = node->nextLight;
continue;

View file

@ -51,7 +51,7 @@ T smoothstep(const T edge0, const T edge1, const T x)
void HWDrawInfo::GetDynSpriteLight(AActor *self, float x, float y, float z, FLightNode *node, int portalgroup, float *out)
{
ADynamicLight *light;
FDynamicLight *light;
float frac, lr, lg, lb;
float radius;
@ -60,7 +60,7 @@ void HWDrawInfo::GetDynSpriteLight(AActor *self, float x, float y, float z, FLig
while (node)
{
light=node->lightsource;
if (light->visibletoplayer && !(light->flags2&MF2_DORMANT) && (!(light->lightflags&LF_DONTLIGHTSELF) || light->target != self || !self) && !(light->lightflags&LF_DONTLIGHTACTORS))
if (light->ShouldLightActor(self))
{
float dist;
FVector3 L;
@ -94,13 +94,14 @@ void HWDrawInfo::GetDynSpriteLight(AActor *self, float x, float y, float z, FLig
if (light->IsSpot())
{
L *= -1.0f / dist;
DAngle negPitch = -light->Angles.Pitch;
DAngle negPitch = -*light->pPitch;
DAngle Angle = light->target->Angles.Yaw;
double xyLen = negPitch.Cos();
double spotDirX = -light->Angles.Yaw.Cos() * xyLen;
double spotDirY = -light->Angles.Yaw.Sin() * xyLen;
double spotDirX = -Angle.Cos() * xyLen;
double spotDirY = -Angle.Sin() * xyLen;
double spotDirZ = -negPitch.Sin();
double cosDir = L.X * spotDirX + L.Y * spotDirY + L.Z * spotDirZ;
frac *= (float)smoothstep(light->SpotOuterAngle.Cos(), light->SpotInnerAngle.Cos(), cosDir);
frac *= (float)smoothstep(light->pSpotOuterAngle->Cos(), light->pSpotInnerAngle->Cos(), cosDir);
}
if (frac > 0 && (!light->shadowmapped || screen->mShadowMap.ShadowTest(light, { x, y, z })))
@ -141,7 +142,7 @@ void HWDrawInfo::GetDynSpriteLight(AActor *thing, particle_t *particle, float *o
// static so that we build up a reserve (memory allocations stop)
// For multithread processing each worker thread needs its own copy, though.
static thread_local TArray<ADynamicLight*> addedLightsArray;
static thread_local TArray<FDynamicLight*> addedLightsArray;
void hw_GetDynModelLight(AActor *self, FDynLightData &modellightdata)
{
@ -166,8 +167,8 @@ void hw_GetDynModelLight(AActor *self, FDynLightData &modellightdata)
FLightNode * node = section->lighthead;
while (node) // check all lights touching a subsector
{
ADynamicLight *light = node->lightsource;
if (light->visibletoplayer && !(light->flags2&MF2_DORMANT) && (!(light->lightflags&LF_DONTLIGHTSELF) || light->target != self) && !(light->lightflags&LF_DONTLIGHTACTORS))
FDynamicLight *light = node->lightsource;
if (light->ShouldLightActor(self))
{
int group = subsector->sector->PortalGroup;
DVector3 pos = light->PosRelative(group);

View file

@ -359,11 +359,11 @@ void GLWall::SetupLights(HWDrawInfo *di, FDynLightData &lightdata)
// Iterate through all dynamic lights which touch this wall and render them
while (node)
{
if (!(node->lightsource->flags2&MF2_DORMANT))
if (node->lightsource->IsActive())
{
iter_dlight++;
DVector3 posrel = node->lightsource->PosRelative(seg->frontsector);
DVector3 posrel = node->lightsource->PosRelative(seg->frontsector->PortalGroup);
float x = posrel.X;
float y = posrel.Y;
float z = posrel.Z;

View file

@ -1034,3 +1034,9 @@ xx(RedCard)
xx(BlueSkull)
xx(YellowSkull)
xx(RedSkull)
xx(DynamicLight)
xx(SpotInnerAngle)
xx(SpotOuterAngle)
xx(lightflags)
xx(lighttype)
xx(InternalDynamicLight)

View file

@ -4842,6 +4842,7 @@ void AActor::OnDestroy ()
// note: if OnDestroy is ever made optional, E_WorldThingDestroyed should still be called for ANY thing.
E_WorldThingDestroyed(this);
DeleteAttachedLights();
ClearRenderSectorList();
ClearRenderLineList();
@ -5526,27 +5527,26 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position)
(mthing->fillcolor & 0xff00) >> 8, (mthing->fillcolor & 0xff)) << 24);
// allow color strings for lights and reshuffle the args for spot lights
if (i->IsDescendantOf(RUNTIME_CLASS(ADynamicLight)))
if (i->IsDescendantOf(NAME_DynamicLight))
{
auto light = static_cast<ADynamicLight*>(mobj);
if (mthing->arg0str != NAME_None)
{
PalEntry color = V_GetColor(nullptr, mthing->arg0str);
light->args[0] = color.r;
light->args[1] = color.g;
light->args[2] = color.b;
mobj->args[0] = color.r;
mobj->args[1] = color.g;
mobj->args[2] = color.b;
}
else if (light->lightflags & LF_SPOT)
else if (mobj->IntVar(NAME_lightflags) & LF_SPOT)
{
light->args[0] = RPART(mthing->args[0]);
light->args[1] = GPART(mthing->args[0]);
light->args[2] = BPART(mthing->args[0]);
mobj->args[0] = RPART(mthing->args[0]);
mobj->args[1] = GPART(mthing->args[0]);
mobj->args[2] = BPART(mthing->args[0]);
}
if (light->lightflags & LF_SPOT)
if (mobj->IntVar(NAME_lightflags) & LF_SPOT)
{
light->SpotInnerAngle = double(mthing->args[1]);
light->SpotOuterAngle = double(mthing->args[2]);
mobj->AngleVar(NAME_SpotInnerAngle) = double(mthing->args[1]);
mobj->AngleVar(NAME_SpotOuterAngle) = double(mthing->args[2]);
}
}

View file

@ -322,14 +322,6 @@ void FLevelLocals::ClearLevelData()
void P_FreeLevelData ()
{
TThinkerIterator<ADynamicLight> it(STAT_DLIGHT);
auto mo = it.Next();
while (mo)
{
auto next = it.Next();
mo->Destroy();
mo = next;
}
// [ZZ] delete per-map event handlers
E_Shutdown(true);

View file

@ -32,7 +32,7 @@ class RenderMemory;
class PolyTranslucentObject;
class PolyDrawSectorPortal;
class PolyDrawLinePortal;
class ADynamicLight;
struct FDynamicLight;
class PolyRenderThread
{
@ -54,7 +54,7 @@ public:
std::vector<std::unique_ptr<PolyDrawSectorPortal>> SectorPortals;
std::vector<std::unique_ptr<PolyDrawLinePortal>> LinePortals;
TArray<ADynamicLight*> AddedLightsArray;
TArray<FDynamicLight*> AddedLightsArray;
// Make sure texture can accessed safely
void PrepareTexture(FSoftwareTexture *texture, FRenderStyle style);

View file

@ -127,8 +127,8 @@ void PolyModelRenderer::AddLights(AActor *actor)
FLightNode * node = subsector->section->lighthead;
while (node) // check all lights touching a subsector
{
ADynamicLight *light = node->lightsource;
if (light->visibletoplayer && !(light->flags2&MF2_DORMANT) && (!(light->lightflags&LF_DONTLIGHTSELF) || light->target != actor) && !(light->lightflags&LF_DONTLIGHTACTORS))
FDynamicLight *light = node->lightsource;
if (light->ShouldLightActor(actor))
{
int group = subsector->sector->PortalGroup;
DVector3 pos = light->PosRelative(group);
@ -153,7 +153,7 @@ void PolyModelRenderer::AddLights(AActor *actor)
Lights = Thread->FrameMemory->AllocMemory<PolyLight>(NumLights);
for (int i = 0; i < NumLights; i++)
{
ADynamicLight *lightsource = addedLights[i];
FDynamicLight *lightsource = addedLights[i];
bool is_point_light = (lightsource->lightflags & LF_ATTENUATE) != 0;

View file

@ -289,7 +289,7 @@ void RenderPolyPlane::SetDynLights(PolyRenderThread *thread, PolyDrawArgs &args,
FLightNode *cur_node = light_list;
while (cur_node)
{
if (!(cur_node->lightsource->flags2&MF2_DORMANT))
if (cur_node->lightsource->IsActive())
max_lights++;
cur_node = cur_node->nextLight;
}
@ -307,7 +307,7 @@ void RenderPolyPlane::SetDynLights(PolyRenderThread *thread, PolyDrawArgs &args,
cur_node = light_list;
while (cur_node)
{
if (!(cur_node->lightsource->flags2&MF2_DORMANT))
if (cur_node->lightsource->IsActive())
{
bool is_point_light = (cur_node->lightsource->lightflags & LF_ATTENUATE) != 0;

View file

@ -391,8 +391,8 @@ void RenderPolySprite::SetDynlight(AActor *thing, PolyDrawArgs &args)
auto node = thing->section->lighthead;
while (node != nullptr)
{
ADynamicLight *light = node->lightsource;
if (light->visibletoplayer && !(light->flags2&MF2_DORMANT) && (!(light->lightflags&LF_DONTLIGHTSELF) || light->target != thing) && !(light->lightflags&LF_DONTLIGHTACTORS))
FDynamicLight *light = node->lightsource;
if (light->ShouldLightActor(thing))
{
float lx = (float)(light->X() - thing->X());
float ly = (float)(light->Y() - thing->Y());

View file

@ -425,7 +425,7 @@ void RenderPolyWall::SetDynLights(PolyRenderThread *thread, PolyDrawArgs &args)
FLightNode *cur_node = light_list;
while (cur_node)
{
if (!(cur_node->lightsource->flags2&MF2_DORMANT))
if (cur_node->lightsource->IsActive())
max_lights++;
cur_node = cur_node->nextLight;
}
@ -443,7 +443,7 @@ void RenderPolyWall::SetDynLights(PolyRenderThread *thread, PolyDrawArgs &args)
cur_node = light_list;
while (cur_node)
{
if (!(cur_node->lightsource->flags2&MF2_DORMANT))
if (cur_node->lightsource->IsActive())
{
bool is_point_light = (cur_node->lightsource->lightflags & LF_ATTENUATE) != 0;

View file

@ -68,34 +68,35 @@ FLightDefaults::FLightDefaults(FName name, ELightType type)
m_type = type;
}
void FLightDefaults::ApplyProperties(ADynamicLight * light) const
void FLightDefaults::ApplyProperties(FDynamicLight * light) const
{
auto oldtype = light->lighttype;
light->m_active = true;
light->lighttype = m_type;
light->specialf1 = m_Param;
light->SetOffset(m_Pos);
light->halo = m_halo;
for (int a = 0; a < 3; a++) light->args[a] = clamp<int>((int)(m_Args[a]), 0, 255);
light->args[LIGHT_INTENSITY] = m_Args[LIGHT_INTENSITY];
light->args[LIGHT_SECONDARY_INTENSITY] = m_Args[LIGHT_SECONDARY_INTENSITY];
light->lightflags &= ~(LF_ADDITIVE | LF_SUBTRACTIVE | LF_DONTLIGHTSELF);
light->pArgs = m_Args;
light->lightflags &= ~(LF_ADDITIVE | LF_SUBTRACTIVE | LF_DONTLIGHTSELF | LF_SPOT);
if (m_subtractive) light->lightflags |= LF_SUBTRACTIVE;
if (m_additive) light->lightflags |= LF_ADDITIVE;
if (m_dontlightself) light->lightflags |= LF_DONTLIGHTSELF;
if (m_dontlightactors) light->lightflags |= LF_DONTLIGHTACTORS;
if (m_spot)
{
light->lightflags |= LF_SPOT;
light->SpotInnerAngle = m_spotInnerAngle;
light->SpotOuterAngle = m_spotOuterAngle;
light->pSpotInnerAngle = &m_spotInnerAngle;
light->pSpotOuterAngle = &m_spotOuterAngle;
if (m_explicitPitch) light->pPitch = &m_pitch;
else light->pPitch = &light->target->Angles.Pitch;
}
light->m_tickCount = 0;
if (m_type == PulseLight)
{
float pulseTime = float(m_Param / TICRATE);
light->m_lastUpdate = level.maptime;
if (m_swapped) light->m_cycler.SetParams(float(light->args[LIGHT_SECONDARY_INTENSITY]), float(light->args[LIGHT_INTENSITY]), pulseTime, oldtype == PulseLight);
else light->m_cycler.SetParams(float(light->args[LIGHT_INTENSITY]), float(light->args[LIGHT_SECONDARY_INTENSITY]), pulseTime, oldtype == PulseLight);
if (m_swapped) light->m_cycler.SetParams(float(m_Args[LIGHT_SECONDARY_INTENSITY]), float(m_Args[LIGHT_INTENSITY]), pulseTime, oldtype == PulseLight);
else light->m_cycler.SetParams(float(m_Args[LIGHT_INTENSITY]), float(m_Args[LIGHT_SECONDARY_INTENSITY]), pulseTime, oldtype == PulseLight);
light->m_cycler.ShouldCycle(true);
light->m_cycler.SetCycleType(CYCLE_Sin);
light->m_currentRadius = (float)light->m_cycler.GetVal();
@ -109,6 +110,7 @@ void FLightDefaults::ApplyProperties(ADynamicLight * light) const
case 1: light->lightflags |= LF_ATTENUATE; break;
default: if (level.flags3 & LEVEL3_ATTENUATE) light->lightflags |= LF_ATTENUATE; else light->lightflags &= ~LF_ATTENUATE; break;
}
light->SetOffset(m_Pos); // this must be the last thing to do.
}

View file

@ -815,7 +815,7 @@ class GLDefsParser
break;
case LIGHTTAG_SCALE:
floatVal = ParseFloat(sc);
defaults->SetArg(LIGHT_SCALE, clamp((int)(floatVal * 255), 1, 1024));
defaults->SetArg(LIGHT_INTENSITY, clamp((int)(floatVal * 255), 1, 1024));
break;
case LIGHTTAG_SUBTRACTIVE:
defaults->SetSubtractive(ParseInt(sc) != 0);

View file

@ -419,25 +419,12 @@ static FFlagDef PlayerPawnFlagDefs[] =
DEFINE_FLAG(PPF, CROUCHABLEMORPH, APlayerPawn, PlayerFlags),
};
static FFlagDef DynLightFlagDefs[] =
{
// PlayerPawn flags
DEFINE_FLAG(LF, SUBTRACTIVE, ADynamicLight, lightflags),
DEFINE_FLAG(LF, ADDITIVE, ADynamicLight, lightflags),
DEFINE_FLAG(LF, DONTLIGHTSELF, ADynamicLight, lightflags),
DEFINE_FLAG(LF, ATTENUATE, ADynamicLight, lightflags),
DEFINE_FLAG(LF, NOSHADOWMAP, ADynamicLight, lightflags),
DEFINE_FLAG(LF, DONTLIGHTACTORS, ADynamicLight, lightflags),
DEFINE_FLAG(LF, SPOT, ADynamicLight, lightflags),
};
static const struct FFlagList { const PClass * const *Type; FFlagDef *Defs; int NumDefs; int Use; } FlagLists[] =
{
{ &RUNTIME_CLASS_CASTLESS(AActor), ActorFlagDefs, countof(ActorFlagDefs), 3 }, // -1 to account for the terminator
{ &RUNTIME_CLASS_CASTLESS(AActor), MoreFlagDefs, countof(MoreFlagDefs), 1 },
{ &RUNTIME_CLASS_CASTLESS(AActor), InternalActorFlagDefs, countof(InternalActorFlagDefs), 2 },
{ &RUNTIME_CLASS_CASTLESS(APlayerPawn), PlayerPawnFlagDefs, countof(PlayerPawnFlagDefs), 3 },
{ &RUNTIME_CLASS_CASTLESS(ADynamicLight),DynLightFlagDefs, countof(DynLightFlagDefs), 3 },
};
#define NUM_FLAG_LISTS (countof(FlagLists))

View file

@ -362,7 +362,7 @@ namespace swrenderer
FLightNode *cur_node = light_list;
while (cur_node)
{
if (!(cur_node->lightsource->flags2&MF2_DORMANT))
if (cur_node->lightsource->IsActive())
max_lights++;
cur_node = cur_node->nextLight;
}
@ -374,7 +374,7 @@ namespace swrenderer
cur_node = light_list;
while (cur_node)
{
if (!(cur_node->lightsource->flags2&MF2_DORMANT))
if (cur_node->lightsource->IsActive())
{
double lightX = cur_node->lightsource->X() - viewport->viewpoint.Pos.X;
double lightY = cur_node->lightsource->Y() - viewport->viewpoint.Pos.Y;

View file

@ -215,7 +215,7 @@ namespace swrenderer
VisiblePlaneLight *cur_node = light_list;
while (cur_node)
{
if (!(cur_node->lightsource->flags2&MF2_DORMANT))
if (cur_node->lightsource->IsActive())
max_lights++;
cur_node = cur_node->next;
}

View file

@ -76,7 +76,7 @@ namespace swrenderer
while (node)
{
if (!(node->lightsource->flags2&MF2_DORMANT) && (height.PointOnSide(node->lightsource->Pos()) > 0))
if (node->lightsource->IsActive() && (height.PointOnSide(node->lightsource->Pos) > 0))
{
bool found = false;
VisiblePlaneLight *light_node = lights;

View file

@ -27,7 +27,7 @@
#include "r_state.h"
#include "swrenderer/r_memory.h"
class ADynamicLight;
struct FDynamicLight;
struct FLightNode;
struct FDynamicColormap;
struct FSectorPortal;
@ -38,7 +38,7 @@ namespace swrenderer
struct VisiblePlaneLight
{
ADynamicLight *lightsource;
FDynamicLight *lightsource;
VisiblePlaneLight *next;
};

View file

@ -28,7 +28,7 @@
class DrawerCommandQueue;
typedef std::shared_ptr<DrawerCommandQueue> DrawerCommandQueuePtr;
class RenderMemory;
class ADynamicLight;
struct FDynamicLight;
EXTERN_CVAR(Bool, r_models);
extern bool r_modelscene;
@ -77,7 +77,7 @@ namespace swrenderer
std::unique_ptr<LightVisibility> Light;
DrawerCommandQueuePtr DrawQueue;
TArray<ADynamicLight*> AddedLightsArray;
TArray<FDynamicLight*> AddedLightsArray;
std::thread thread;

View file

@ -168,8 +168,8 @@ namespace swrenderer
FLightNode * node = subsector->section->lighthead;
while (node) // check all lights touching a subsector
{
ADynamicLight *light = node->lightsource;
if (light->visibletoplayer && !(light->flags2&MF2_DORMANT) && (!(light->lightflags&LF_DONTLIGHTSELF) || light->target != actor) && !(light->lightflags&LF_DONTLIGHTACTORS))
FDynamicLight *light = node->lightsource;
if (light->ShouldLightActor(actor))
{
int group = subsector->sector->PortalGroup;
DVector3 pos = light->PosRelative(group);
@ -194,7 +194,7 @@ namespace swrenderer
Lights = Thread->FrameMemory->AllocMemory<PolyLight>(NumLights);
for (int i = 0; i < NumLights; i++)
{
ADynamicLight *lightsource = addedLights[i];
FDynamicLight *lightsource = addedLights[i];
bool is_point_light = (lightsource->lightflags & LF_ATTENUATE) != 0;

View file

@ -256,8 +256,8 @@ namespace swrenderer
auto node = vis->section->lighthead;
while (node != nullptr)
{
ADynamicLight *light = node->lightsource;
if (light->visibletoplayer && !(light->flags2&MF2_DORMANT) && (!(light->lightflags&LF_DONTLIGHTSELF) || light->target != thing) && !(light->lightflags&LF_DONTLIGHTACTORS))
FDynamicLight *light = node->lightsource;
if (light->ShouldLightActor(thing))
{
float lx = (float)(light->X() - thing->X());
float ly = (float)(light->Y() - thing->Y());

View file

@ -1,11 +1,21 @@
class DynamicLight : Actor native
class DynamicLight : Actor
{
native double SpotInnerAngle;
native double SpotOuterAngle;
double SpotInnerAngle;
double SpotOuterAngle;
private int lighttype;
private int lightflags;
property SpotInnerAngle: SpotInnerAngle;
property SpotOuterAngle: SpotOuterAngle;
flagdef subtractive: lightflags, 0;
flagdef additive: lightflags, 1;
flagdef dontlightself: lightflags, 2;
flagdef attenuate: lightflags, 3;
flagdef noshadowmap: lightflags, 4;
flagdef dontlightactors: lightflags, 5;
flagdef spot: lightflags, 6;
enum EArgs
{
LIGHT_RED = 0,
@ -30,6 +40,9 @@ class DynamicLight : Actor native
};
native void SetOffset(Vector3 offset);
private native void AttachLight();
private native void ActivateLight();
private native void DeactivateLight();
Default
{
@ -44,6 +57,46 @@ class DynamicLight : Actor native
+FIXMAPTHINGPOS
+INVISIBLE
}
//==========================================================================
//
//
//
//==========================================================================
override void Tick()
{
// Lights do not call the super method.
}
override void BeginPlay()
{
ChangeStatNum(STAT_DLIGHT);
AttachLight();
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
if (!(SpawnFlags & MTF_DORMANT))
{
Activate(self);
}
}
override void Activate(Actor activator)
{
bDormant = false;
ActivateLight();
}
override void Deactivate(Actor activator)
{
bDormant = true;
DeactivateLight();
}
}