diff --git a/src/doomdata.h b/src/doomdata.h index ce9939d682..4e3acdce61 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -372,6 +372,8 @@ struct FMapThing uint32_t RenderStyle; int FloatbobPhase; int friendlyseeblocks; + double SpotInnerAngle = 10; + double SpotOuterAngle = 25; }; diff --git a/src/g_shared/a_dynlight.cpp b/src/g_shared/a_dynlight.cpp index 0cad5e9ba7..1858a9a929 100644 --- a/src/g_shared/a_dynlight.cpp +++ b/src/g_shared/a_dynlight.cpp @@ -114,6 +114,9 @@ DEFINE_CLASS_PROPERTY(type, S, DynamicLight) //========================================================================== IMPLEMENT_CLASS(ADynamicLight, false, false) +DEFINE_FIELD(ADynamicLight, SpotInnerAngle) +DEFINE_FIELD(ADynamicLight, SpotOuterAngle) + static FRandom randLight; //========================================================================== @@ -134,7 +137,9 @@ void ADynamicLight::Serialize(FSerializer &arc) arc("lightflags", lightflags, def->lightflags) ("lighttype", lighttype, def->lighttype) ("tickcount", m_tickCount, def->m_tickCount) - ("currentradius", m_currentRadius, def->m_currentRadius); + ("currentradius", m_currentRadius, def->m_currentRadius) + ("spotinnerangle", SpotInnerAngle, def->SpotInnerAngle) + ("spotouterangle", SpotOuterAngle, def->SpotOuterAngle); if (lighttype == PulseLight) arc("lastupdate", m_lastUpdate, def->m_lastUpdate) diff --git a/src/g_shared/a_dynlight.h b/src/g_shared/a_dynlight.h index 5446ba8cf9..b659507d12 100644 --- a/src/g_shared/a_dynlight.h +++ b/src/g_shared/a_dynlight.h @@ -28,7 +28,8 @@ enum LightFlag LF_DONTLIGHTSELF = 4, LF_ATTENUATE = 8, LF_NOSHADOWMAP = 16, - LF_DONTLIGHTACTORS = 32 + LF_DONTLIGHTACTORS = 32, + LF_SPOT = 64 }; typedef TFlags LightFlags; @@ -42,7 +43,7 @@ enum ELightType FlickerLight, RandomFlickerLight, SectorLight, - SpotLight, + DummyLight, ColorPulseLight, ColorFlickerLight, RandomColorFlickerLight @@ -100,6 +101,7 @@ public: 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_subsectors; @@ -127,6 +129,7 @@ public: bool shadowmapped; int bufferindex; LightFlags lightflags; - + DAngle SpotInnerAngle = 10.0; + DAngle SpotOuterAngle = 25.0; }; diff --git a/src/g_shared/a_dynlightdata.cpp b/src/g_shared/a_dynlightdata.cpp index fcdbcd3d10..817e7a80b2 100644 --- a/src/g_shared/a_dynlightdata.cpp +++ b/src/g_shared/a_dynlightdata.cpp @@ -128,6 +128,9 @@ public: void SetAttenuate(bool on) { m_attenuate = on; } void SetHalo(bool halo) { m_halo = halo; } void SetDontLightActors(bool on) { m_dontlightactors = on; } + void SetSpot(bool spot) { m_spot = spot; } + void SetSpotInnerAngle(double angle) { m_spotInnerAngle = angle; } + void SetSpotOuterAngle(double angle) { m_spotOuterAngle = angle; } void OrderIntensities() { @@ -151,6 +154,9 @@ protected: bool m_dontlightself = false; bool m_dontlightactors = false; bool m_swapped = false; + bool m_spot = false; + double m_spotInnerAngle = 10.0; + double m_spotOuterAngle = 25.0; }; TDeletingArray LightDefaults; @@ -183,6 +189,10 @@ void FLightDefaults::ApplyProperties(ADynamicLight * light) const 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->m_tickCount = 0; if (m_type == PulseLight) { @@ -233,7 +243,8 @@ static const char *LightTags[]= "dontlightself", "attenuate", "dontlightactors", - NULL + "spot", + nullptr }; @@ -255,6 +266,7 @@ enum { LIGHTTAG_DONTLIGHTSELF, LIGHTTAG_ATTENUATE, LIGHTTAG_DONTLIGHTACTORS, + LIGHTTAG_SPOT }; @@ -395,6 +407,15 @@ static void ParsePointLight(FScanner &sc) case LIGHTTAG_DONTLIGHTACTORS: defaults->SetDontLightActors(ParseInt(sc) != 0); break; + case LIGHTTAG_SPOT: + { + float innerAngle = ParseFloat(sc); + float outerAngle = ParseFloat(sc); + defaults->SetSpot(true); + defaults->SetSpotInnerAngle(innerAngle); + defaults->SetSpotOuterAngle(outerAngle); + } + break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } @@ -480,6 +501,15 @@ static void ParsePulseLight(FScanner &sc) case LIGHTTAG_DONTLIGHTACTORS: defaults->SetDontLightActors(ParseInt(sc) != 0); break; + case LIGHTTAG_SPOT: + { + float innerAngle = ParseFloat(sc); + float outerAngle = ParseFloat(sc); + defaults->SetSpot(true); + defaults->SetSpotInnerAngle(innerAngle); + defaults->SetSpotOuterAngle(outerAngle); + } + break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } @@ -567,6 +597,15 @@ void ParseFlickerLight(FScanner &sc) case LIGHTTAG_DONTLIGHTACTORS: defaults->SetDontLightActors(ParseInt(sc) != 0); break; + case LIGHTTAG_SPOT: + { + float innerAngle = ParseFloat(sc); + float outerAngle = ParseFloat(sc); + defaults->SetSpot(true); + defaults->SetSpotInnerAngle(innerAngle); + defaults->SetSpotOuterAngle(outerAngle); + } + break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } @@ -653,6 +692,15 @@ void ParseFlickerLight2(FScanner &sc) case LIGHTTAG_DONTLIGHTACTORS: defaults->SetDontLightActors(ParseInt(sc) != 0); break; + case LIGHTTAG_SPOT: + { + float innerAngle = ParseFloat(sc); + float outerAngle = ParseFloat(sc); + defaults->SetSpot(true); + defaults->SetSpotInnerAngle(innerAngle); + defaults->SetSpotOuterAngle(outerAngle); + } + break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } @@ -736,6 +784,15 @@ static void ParseSectorLight(FScanner &sc) case LIGHTTAG_DONTLIGHTACTORS: defaults->SetDontLightActors(ParseInt(sc) != 0); break; + case LIGHTTAG_SPOT: + { + float innerAngle = ParseFloat(sc); + float outerAngle = ParseFloat(sc); + defaults->SetSpot(true); + defaults->SetSpotInnerAngle(innerAngle); + defaults->SetSpotOuterAngle(outerAngle); + } + break; default: sc.ScriptError("Unknown tag: %s\n", sc.String); } diff --git a/src/gl/dynlights/gl_dynlight1.cpp b/src/gl/dynlights/gl_dynlight1.cpp index e42f89ac4b..42f1c27cfb 100644 --- a/src/gl/dynlights/gl_dynlight1.cpp +++ b/src/gl/dynlights/gl_dynlight1.cpp @@ -128,7 +128,26 @@ void gl_AddLightToList(int group, ADynamicLight * light, FDynLightData &ldata) if (attenuate) shadowIndex = -shadowIndex; - float *data = &ldata.arrays[i][ldata.arrays[i].Reserve(8)]; + float lightType = 0.0f; + float spotInnerAngle = 0.0f; + float spotOuterAngle = 0.0f; + float spotDirX = 0.0f; + float spotDirY = 0.0f; + float spotDirZ = 0.0f; + if (light->IsSpot()) + { + lightType = 1.0f; + spotInnerAngle = light->SpotInnerAngle.Cos(); + spotOuterAngle = light->SpotOuterAngle.Cos(); + + DAngle negPitch = -light->Angles.Pitch; + double xzLen = negPitch.Cos(); + spotDirX = -light->Angles.Yaw.Cos() * xzLen; + spotDirY = -negPitch.Sin(); + spotDirZ = -light->Angles.Yaw.Sin() * xzLen; + } + + float *data = &ldata.arrays[i][ldata.arrays[i].Reserve(16)]; data[0] = pos.X; data[1] = pos.Z; data[2] = pos.Y; @@ -137,5 +156,13 @@ void gl_AddLightToList(int group, ADynamicLight * light, FDynLightData &ldata) data[5] = g; data[6] = b; data[7] = shadowIndex; + data[8] = spotDirX; + data[9] = spotDirY; + data[10] = spotDirZ; + data[11] = lightType; + data[12] = spotInnerAngle; + data[13] = spotOuterAngle; + data[14] = 0.0f; // unused + data[15] = 0.0f; // unused } diff --git a/src/gl/scene/gl_spritelight.cpp b/src/gl/scene/gl_spritelight.cpp index 2d2e65b760..ac7e6971e5 100644 --- a/src/gl/scene/gl_spritelight.cpp +++ b/src/gl/scene/gl_spritelight.cpp @@ -49,6 +49,13 @@ FDynLightData modellightdata; int modellightindex = -1; +template +T smoothstep(const T edge0, const T edge1, const T x) +{ + auto t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); + return t * t * (3.0 - 2.0 * t); +} + //========================================================================== // // Sets a single light value from all dynamic lights affecting the specified location @@ -70,6 +77,7 @@ void gl_SetDynSpriteLight(AActor *self, float x, float y, float z, subsector_t * if (light->visibletoplayer && !(light->flags2&MF2_DORMANT) && (!(light->lightflags&LF_DONTLIGHTSELF) || light->target != self) && !(light->lightflags&LF_DONTLIGHTACTORS)) { float dist; + FVector3 L; // This is a performance critical section of code where we cannot afford to let the compiler decide whether to inline the function or not. // This will do the calculations explicitly rather than calling one of AActor's utility functions. @@ -80,14 +88,15 @@ void gl_SetDynSpriteLight(AActor *self, float x, float y, float z, subsector_t * if (fromgroup == togroup || fromgroup == 0 || togroup == 0) goto direct; DVector2 offset = Displacements.getOffset(fromgroup, togroup); - dist = FVector3(x - light->X() - offset.X, y - light->Y() - offset.Y, z - light->Z()).LengthSquared(); + L = FVector3(x - light->X() - offset.X, y - light->Y() - offset.Y, z - light->Z()); } else { direct: - dist = FVector3(x - light->X(), y - light->Y(), z - light->Z()).LengthSquared(); + L = FVector3(x - light->X(), y - light->Y(), z - light->Z()); } + dist = L.LengthSquared(); radius = light->GetRadius(); if (dist < radius * radius) @@ -96,6 +105,18 @@ void gl_SetDynSpriteLight(AActor *self, float x, float y, float z, subsector_t * frac = 1.0f - (dist / radius); + if (light->IsSpot()) + { + L *= -1.0f / dist; + DAngle negPitch = -light->Angles.Pitch; + double xyLen = negPitch.Cos(); + double spotDirX = -light->Angles.Yaw.Cos() * xyLen; + double spotDirY = -light->Angles.Yaw.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); + } + if (frac > 0 && GLRenderer->mShadowMap.ShadowTest(light, { x, y, z })) { lr = light->GetRed() / 255.0f; diff --git a/src/namedef.h b/src/namedef.h index ace456a691..fa345397f6 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -503,6 +503,8 @@ xx(Scale) xx(ScaleX) xx(ScaleY) xx(FriendlySeeBlocks) +xx(SpotInnerAngle) +xx(SpotOuterAngle) xx(Floatbobphase) xx(Floatbobstrength) xx(Target) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 2c05874ab9..09fdc3eac9 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -108,6 +108,7 @@ #include "a_morph.h" #include "events.h" #include "actorinlines.h" +#include "a_dynlight.h" // MACROS ------------------------------------------------------------------ @@ -6003,6 +6004,8 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) } } + + // spawn it double sz; @@ -6091,6 +6094,12 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) mobj->fillcolor = (mthing->fillcolor & 0xffffff) | (ColorMatcher.Pick((mthing->fillcolor & 0xff0000) >> 16, (mthing->fillcolor & 0xff00) >> 8, (mthing->fillcolor & 0xff)) << 24); + if (i->IsDescendantOf(RUNTIME_CLASS(ADynamicLight))) + { + ((ADynamicLight*)mobj)->SpotInnerAngle = mthing->SpotInnerAngle; + ((ADynamicLight*)mobj)->SpotOuterAngle = mthing->SpotOuterAngle; + } + mobj->CallBeginPlay (); if (!(mobj->ObjectFlags & OF_EuthanizeMe)) { diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 0f3eb58757..5b3d0e3542 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -799,6 +799,14 @@ public: th->Scale.X = th->Scale.Y = CheckFloat(key); break; + case NAME_SpotInnerAngle: + th->SpotInnerAngle = CheckFloat(key); + break; + + case NAME_SpotOuterAngle: + th->SpotOuterAngle = CheckFloat(key); + break; + case NAME_FriendlySeeBlocks: CHECK_N(Zd | Zdt) th->friendlyseeblocks = CheckInt(key); diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index c746c536d7..09e28ba556 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -494,6 +494,7 @@ static FFlagDef DynLightFlagDefs[] = 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 FFlagDef PowerSpeedFlagDefs[] = diff --git a/wadsrc/static/mapinfo/common.txt b/wadsrc/static/mapinfo/common.txt index 5782f8808a..1c1559b061 100644 --- a/wadsrc/static/mapinfo/common.txt +++ b/wadsrc/static/mapinfo/common.txt @@ -109,6 +109,26 @@ DoomEdNums 9832 = PointLightFlickerAttenuated 9833 = SectorPointLightAttenuated 9834 = PointLightFlickerRandomAttenuated + 9840 = SpotLight + 9841 = SpotLightPulse + 9842 = SpotLightFlicker + 9843 = SectorSpotLight + 9844 = SpotLightFlickerRandom + 9850 = SpotLightAdditive + 9851 = SpotLightPulseAdditive + 9852 = SpotLightFlickerAdditive + 9853 = SectorSpotLightAdditive + 9854 = SpotLightFlickerRandomSubtractive + 9860 = SpotLightSubtractive + 9861 = SpotLightPulseSubtractive + 9862 = SpotLightFlickerSubtractive + 9863 = SectorSpotLightSubtractive + 9864 = SpotLightFlickerRandomSubtractive + 9870 = SpotLightAttenuated + 9871 = SpotLightPulseAttenuated + 9872 = SpotLightFlickerAttenuated + 9873 = SectorSpotLightAttenuated + 9874 = SpotLightFlickerRandomAttenuated 9982 = SecActEyesAboveC 9983 = SecActEyesBelowC 9988 = CustomSprite diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp index 83cabbd0d3..25a2c14d2a 100644 --- a/wadsrc/static/shaders/glsl/main.fp +++ b/wadsrc/static/shaders/glsl/main.fp @@ -278,6 +278,13 @@ float pointLightAttenuation(vec4 lightpos, float lightcolorA) } } +float spotLightAttenuation(vec4 lightpos, vec3 spotdir, float lightCosInnerAngle, float lightCosOuterAngle) +{ + vec3 lightDirection = normalize(lightpos.xyz - pixelpos.xyz); + float cosDir = dot(lightDirection, spotdir); + return smoothstep(lightCosOuterAngle, lightCosInnerAngle, cosDir); +} + //=========================================================================== // // Calculate light @@ -348,23 +355,33 @@ vec4 getLightColor(float fogdist, float fogfactor) // // modulated lights // - for(int i=lightRange.x; i