From d654e02deab1611426160c4f053089ae7a8b4fce Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 1 Jan 2019 19:35:55 +0100 Subject: [PATCH] - 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. --- src/actor.h | 6 +- src/d_main.cpp | 3 +- src/dobject.h | 7 + src/dthinker.cpp | 22 + src/g_levellocals.h | 2 + src/g_shared/a_dynlight.cpp | 462 ++++++++----------- src/g_shared/a_dynlight.h | 130 +++--- src/hwrenderer/dynlights/hw_dynlightdata.cpp | 16 +- src/hwrenderer/dynlights/hw_dynlightdata.h | 4 +- src/hwrenderer/dynlights/hw_shadowmap.cpp | 17 +- src/hwrenderer/dynlights/hw_shadowmap.h | 4 +- src/hwrenderer/scene/hw_flats.cpp | 6 +- src/hwrenderer/scene/hw_renderhacks.cpp | 4 +- src/hwrenderer/scene/hw_spritelight.cpp | 19 +- src/hwrenderer/scene/hw_walls.cpp | 4 +- src/namedef.h | 6 + src/p_mobj.cpp | 24 +- src/p_setup.cpp | 8 - src/polyrenderer/poly_renderthread.h | 4 +- src/polyrenderer/scene/poly_model.cpp | 6 +- src/polyrenderer/scene/poly_plane.cpp | 4 +- src/polyrenderer/scene/poly_sprite.cpp | 4 +- src/polyrenderer/scene/poly_wall.cpp | 4 +- src/r_data/a_dynlightdata.cpp | 26 +- src/r_data/gldefs.cpp | 2 +- src/scripting/thingdef_data.cpp | 13 - src/swrenderer/line/r_walldraw.cpp | 4 +- src/swrenderer/plane/r_flatplane.cpp | 2 +- src/swrenderer/plane/r_visibleplane.cpp | 2 +- src/swrenderer/plane/r_visibleplane.h | 4 +- src/swrenderer/r_renderthread.h | 4 +- src/swrenderer/things/r_model.cpp | 6 +- src/swrenderer/things/r_sprite.cpp | 4 +- wadsrc/static/zscript/shared/dynlights.txt | 59 ++- 34 files changed, 459 insertions(+), 433 deletions(-) diff --git a/src/actor.h b/src/actor.h index 132c5204bd..824f6d436b 100644 --- a/src/actor.h +++ b/src/actor.h @@ -1235,7 +1235,7 @@ public: DVector3 Prev; DRotator PrevAngles; int PrevPortalGroup; - TArray > AttachedLights; + TArray 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 diff --git a/src/d_main.cpp b/src/d_main.cpp index 32e674fe2e..97f2005974 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -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 it(STAT_DLIGHT); - level.HasDynamicLights = !!it.Next(); + level.HasDynamicLights = !!level.lights; } else level.HasDynamicLights = false; // lights are off so effectively we have none. diff --git a/src/dobject.h b/src/dobject.h index 8632ec1ddf..b57f75bedf 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -38,6 +38,7 @@ #include #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 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 inline T *&DObject::PointerVar(FName field) { diff --git a/src/dthinker.cpp b/src/dthinker.cpp index 7ed3e80468..0c41054799 100644 --- a/src/dthinker.cpp +++ b/src/dthinker.cpp @@ -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; diff --git a/src/g_levellocals.h b/src/g_levellocals.h index e17472dcd5..583696fe3e 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -198,6 +198,8 @@ struct FLevelLocals : public FLevelData bool lightadditivesurfaces; bool notexturefill; + FDynamicLight *lights; + bool IsJumpingAllowed() const; bool IsCrouchingAllowed() const; bool IsFreelookAllowed() const; diff --git a/src/g_shared/a_dynlight.cpp b/src/g_shared/a_dynlight.cpp index 441dcd1009..840dff6803 100644 --- a/src/g_shared/a_dynlight.cpp +++ b/src/g_shared/a_dynlight.cpp @@ -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 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(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; +} + + +//========================================================================== +// +// Attaches a dynamic light descriptor to a dynamic light actor. +// Owned lights do not use this function. +// +//========================================================================== + +void AttachLight(AActor *self) +{ + 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; +} + +//========================================================================== +// +// +// +//========================================================================== + +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; +} + +//========================================================================== +// +// +// +//========================================================================== + +static void SetOffset(AActor *self, double x, double y, double z) +{ + for (auto l : self->AttachedLights) + { + l->SetOffset(DVector3(x, y, z)); } } - -void ADynamicLight::PostSerialize() +DEFINE_ACTION_FUNCTION_NATIVE(ADynamicLight, SetOffset, SetOffset) { - 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(); + PARAM_SELF_PROLOGUE(AActor); + PARAM_FLOAT(x); + PARAM_FLOAT(y); + PARAM_FLOAT(z); + SetOffset(self, x, y, z); + return 0; } //========================================================================== // -// [TS] +// // //========================================================================== -void ADynamicLight::BeginPlay() + +void FDynamicLight::ReleaseLight() { - //Super::BeginPlay(); - ChangeStatNum(STAT_DLIGHT); - - specialf1 = DAngle(double(SpawnAngle)).Normalized360().Degrees; - visibletoplayer = true; - mShadowmapIndex = 1024; -} - -//========================================================================== -// -// [TS] -// -//========================================================================== -void ADynamicLight::PostBeginPlay() -{ - Super::PostBeginPlay(); - - if (!(SpawnFlags & MTF_DORMANT)) - { - Activate (nullptr); - } - - subsector = R_PointInSubsector(Pos()); + 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,21 +236,19 @@ 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) { 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 (IsOwned()) + if (!target) { - if (!target || !target->state) + delete this; + return; + } + + if (owned) + { + if (!target->state) { - this->Destroy(); + 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) - { - DAngle angle = target->Angles.Yaw; - double s = angle.Sin(); - double c = angle.Cos(); + AActor *target = this->target; // perform the read barrier only once. - 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; + // Offset is calculated in relation to the owning actor. + DAngle angle = target->Angles.Yaw; + double s = angle.Sin(); + double c = angle.Cos(); - // 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.); - } + 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.) 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 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(AttachedLights[count]); + light = AttachedLights[count]; assert(light != nullptr); } else { - light = Spawn(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(;countflags2 |= 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; iUnlinkLight(); + l->ReleaseLight(); } - return Super::PropagateMark(); + AttachedLights.Clear(); } //========================================================================== @@ -888,21 +836,10 @@ void AActor::DeleteAllAttachedLights() { TThinkerIterator it; AActor * a; - ADynamicLight * l; while ((a=it.Next())) { - a->AttachedLights.Clear(); - } - - TThinkerIterator it2; - - l=it2.Next(); - while (l) - { - ADynamicLight * ll = it2.Next(); - if (l->owned) l->Destroy(); - l=ll; + a->DeleteAttachedLights(); } } @@ -919,7 +856,14 @@ void AActor::RecreateAllAttachedLights() while ((a=it.Next())) { - a->SetDynamicLights(); + 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 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); } - diff --git a/src/g_shared/a_dynlight.h b/src/g_shared/a_dynlight.h index 685c3a39c3..e22a510751 100644 --- a/src/g_shared/a_dynlight.h +++ b/src/g_shared/a_dynlight.h @@ -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 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; }; diff --git a/src/hwrenderer/dynlights/hw_dynlightdata.cpp b/src/hwrenderer/dynlights/hw_dynlightdata.cpp index c881f9b4ec..e30f60cdb0 100644 --- a/src/hwrenderer/dynlights/hw_dynlightdata.cpp +++ b/src/hwrenderer/dynlights/hw_dynlightdata.cpp @@ -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)]; diff --git a/src/hwrenderer/dynlights/hw_dynlightdata.h b/src/hwrenderer/dynlights/hw_dynlightdata.h index f1a1ffb7b0..ec5f6d89ea 100644 --- a/src/hwrenderer/dynlights/hw_dynlightdata.h +++ b/src/hwrenderer/dynlights/hw_dynlightdata.h @@ -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); }; diff --git a/src/hwrenderer/dynlights/hw_shadowmap.cpp b/src/hwrenderer/dynlights/hw_shadowmap.cpp index fda7f9561f..e43f73b244 100644 --- a/src/hwrenderer/dynlights/hw_shadowmap.cpp +++ b/src/hwrenderer/dynlights/hw_shadowmap.cpp @@ -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 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 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) diff --git a/src/hwrenderer/dynlights/hw_shadowmap.h b/src/hwrenderer/dynlights/hw_shadowmap.h index 8c004b559c..03c74f811f 100644 --- a/src/hwrenderer/dynlights/hw_shadowmap.h +++ b/src/hwrenderer/dynlights/hw_shadowmap.h @@ -5,7 +5,7 @@ #include "stats.h" #include -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; diff --git a/src/hwrenderer/scene/hw_flats.cpp b/src/hwrenderer/scene/hw_flats.cpp index 71e90a80fb..c60acfe13c 100644 --- a/src/hwrenderer/scene/hw_flats.cpp +++ b/src/hwrenderer/scene/hw_flats.cpp @@ -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 ((planehZ() && ceiling) || (planeh>light->Z() && !ceiling)) { node = node->nextLight; diff --git a/src/hwrenderer/scene/hw_renderhacks.cpp b/src/hwrenderer/scene/hw_renderhacks.cpp index 59d71231d1..b2467a5eb3 100644 --- a/src/hwrenderer/scene/hw_renderhacks.cpp +++ b/src/hwrenderer/scene/hw_renderhacks.cpp @@ -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; diff --git a/src/hwrenderer/scene/hw_spritelight.cpp b/src/hwrenderer/scene/hw_spritelight.cpp index a0d76343b2..f3058cf53f 100644 --- a/src/hwrenderer/scene/hw_spritelight.cpp +++ b/src/hwrenderer/scene/hw_spritelight.cpp @@ -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 addedLightsArray; +static thread_local TArray 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); diff --git a/src/hwrenderer/scene/hw_walls.cpp b/src/hwrenderer/scene/hw_walls.cpp index 66275f9636..48c9097cf6 100644 --- a/src/hwrenderer/scene/hw_walls.cpp +++ b/src/hwrenderer/scene/hw_walls.cpp @@ -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; diff --git a/src/namedef.h b/src/namedef.h index a0d395f888..c217261e7b 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -1034,3 +1034,9 @@ xx(RedCard) xx(BlueSkull) xx(YellowSkull) xx(RedSkull) +xx(DynamicLight) +xx(SpotInnerAngle) +xx(SpotOuterAngle) +xx(lightflags) +xx(lighttype) +xx(InternalDynamicLight) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index e43dcb99be..32460efb65 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -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(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]); } } diff --git a/src/p_setup.cpp b/src/p_setup.cpp index bba370ec2f..5fe4e60372 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -322,14 +322,6 @@ void FLevelLocals::ClearLevelData() void P_FreeLevelData () { - TThinkerIterator 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); diff --git a/src/polyrenderer/poly_renderthread.h b/src/polyrenderer/poly_renderthread.h index 69e2e67e80..d6be7db158 100644 --- a/src/polyrenderer/poly_renderthread.h +++ b/src/polyrenderer/poly_renderthread.h @@ -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> SectorPortals; std::vector> LinePortals; - TArray AddedLightsArray; + TArray AddedLightsArray; // Make sure texture can accessed safely void PrepareTexture(FSoftwareTexture *texture, FRenderStyle style); diff --git a/src/polyrenderer/scene/poly_model.cpp b/src/polyrenderer/scene/poly_model.cpp index a2f7875cb3..08f21deb28 100644 --- a/src/polyrenderer/scene/poly_model.cpp +++ b/src/polyrenderer/scene/poly_model.cpp @@ -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(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; diff --git a/src/polyrenderer/scene/poly_plane.cpp b/src/polyrenderer/scene/poly_plane.cpp index f267bde586..1736288063 100644 --- a/src/polyrenderer/scene/poly_plane.cpp +++ b/src/polyrenderer/scene/poly_plane.cpp @@ -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; diff --git a/src/polyrenderer/scene/poly_sprite.cpp b/src/polyrenderer/scene/poly_sprite.cpp index be5b85f00a..b6487c231b 100644 --- a/src/polyrenderer/scene/poly_sprite.cpp +++ b/src/polyrenderer/scene/poly_sprite.cpp @@ -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()); diff --git a/src/polyrenderer/scene/poly_wall.cpp b/src/polyrenderer/scene/poly_wall.cpp index 616de754ee..0d5d88ae47 100644 --- a/src/polyrenderer/scene/poly_wall.cpp +++ b/src/polyrenderer/scene/poly_wall.cpp @@ -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; diff --git a/src/r_data/a_dynlightdata.cpp b/src/r_data/a_dynlightdata.cpp index 2f69728d05..b838f174ef 100644 --- a/src/r_data/a_dynlightdata.cpp +++ b/src/r_data/a_dynlightdata.cpp @@ -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)(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,7 +110,8 @@ 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. +} //========================================================================== diff --git a/src/r_data/gldefs.cpp b/src/r_data/gldefs.cpp index 84ace27980..3b8a5cf906 100644 --- a/src/r_data/gldefs.cpp +++ b/src/r_data/gldefs.cpp @@ -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); diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 3426576074..2e500efb31 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -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)) diff --git a/src/swrenderer/line/r_walldraw.cpp b/src/swrenderer/line/r_walldraw.cpp index 0cd3fa7053..457129352d 100644 --- a/src/swrenderer/line/r_walldraw.cpp +++ b/src/swrenderer/line/r_walldraw.cpp @@ -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; diff --git a/src/swrenderer/plane/r_flatplane.cpp b/src/swrenderer/plane/r_flatplane.cpp index b0627c2e00..b7f82b5f43 100644 --- a/src/swrenderer/plane/r_flatplane.cpp +++ b/src/swrenderer/plane/r_flatplane.cpp @@ -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; } diff --git a/src/swrenderer/plane/r_visibleplane.cpp b/src/swrenderer/plane/r_visibleplane.cpp index 72e8388129..a61b6e84d0 100644 --- a/src/swrenderer/plane/r_visibleplane.cpp +++ b/src/swrenderer/plane/r_visibleplane.cpp @@ -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; diff --git a/src/swrenderer/plane/r_visibleplane.h b/src/swrenderer/plane/r_visibleplane.h index b77c37215e..afb493cfd4 100644 --- a/src/swrenderer/plane/r_visibleplane.h +++ b/src/swrenderer/plane/r_visibleplane.h @@ -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; }; diff --git a/src/swrenderer/r_renderthread.h b/src/swrenderer/r_renderthread.h index 8823b59cf8..5e7b4f818e 100644 --- a/src/swrenderer/r_renderthread.h +++ b/src/swrenderer/r_renderthread.h @@ -28,7 +28,7 @@ class DrawerCommandQueue; typedef std::shared_ptr 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 Light; DrawerCommandQueuePtr DrawQueue; - TArray AddedLightsArray; + TArray AddedLightsArray; std::thread thread; diff --git a/src/swrenderer/things/r_model.cpp b/src/swrenderer/things/r_model.cpp index 938b5f20cf..b3235057ed 100644 --- a/src/swrenderer/things/r_model.cpp +++ b/src/swrenderer/things/r_model.cpp @@ -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(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; diff --git a/src/swrenderer/things/r_sprite.cpp b/src/swrenderer/things/r_sprite.cpp index bc44bcebe9..fa8ce436d2 100644 --- a/src/swrenderer/things/r_sprite.cpp +++ b/src/swrenderer/things/r_sprite.cpp @@ -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()); diff --git a/wadsrc/static/zscript/shared/dynlights.txt b/wadsrc/static/zscript/shared/dynlights.txt index 488f8d3174..bc059255b3 100644 --- a/wadsrc/static/zscript/shared/dynlights.txt +++ b/wadsrc/static/zscript/shared/dynlights.txt @@ -1,10 +1,20 @@ -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 { @@ -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(); + } + }