2016-09-14 18:01:13 +00:00
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2004-2016 Christoph Oelckers
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
2013-06-23 07:49:34 +00:00
/*
* * a_dynlight . cpp
* * Implements actors representing dynamic lights ( hardware independent )
* *
2016-09-14 18:01:13 +00:00
* *
* * all functions marked with [ TS ] are licensed under
2013-06-23 07:49:34 +00:00
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* * Copyright 2003 Timothy Stump
* * All rights reserved .
* *
* * Redistribution and use in source and binary forms , with or without
* * modification , are permitted provided that the following conditions
* * are met :
* *
* * 1. Redistributions of source code must retain the above copyright
* * notice , this list of conditions and the following disclaimer .
* * 2. Redistributions in binary form must reproduce the above copyright
* * notice , this list of conditions and the following disclaimer in the
* * documentation and / or other materials provided with the distribution .
* * 3. The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission .
* *
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* * IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* * INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* * NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* * DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* * THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* * THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
*/
# include "c_dispatch.h"
2017-04-12 23:12:04 +00:00
# include "thingdef.h"
2016-02-16 21:01:04 +00:00
# include "r_utility.h"
2016-05-08 07:34:22 +00:00
# include "doomstat.h"
2016-09-23 23:47:44 +00:00
# include "serializer.h"
2017-01-08 17:45:30 +00:00
# include "g_levellocals.h"
2017-03-16 12:49:34 +00:00
# include "a_dynlight.h"
2017-03-10 01:22:42 +00:00
# include "actorinlines.h"
2019-01-01 18:35:55 +00:00
# include "memarena.h"
2013-06-23 07:49:34 +00:00
2019-01-01 18:35:55 +00:00
static FMemArena DynLightArena ( sizeof ( FDynamicLight ) * 200 ) ;
static TArray < FDynamicLight * > FreeList ;
static FRandom randLight ;
2013-06-23 07:49:34 +00:00
2017-03-12 15:56:00 +00:00
CUSTOM_CVAR ( Bool , gl_lights , true , CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL )
{
if ( self ) AActor : : RecreateAllAttachedLights ( ) ;
else AActor : : DeleteAllAttachedLights ( ) ;
}
CVAR ( Bool , gl_attachedlights , true , CVAR_ARCHIVE | CVAR_GLOBALCONFIG ) ;
2013-06-23 07:49:34 +00:00
//==========================================================================
//
//==========================================================================
2019-01-01 18:35:55 +00:00
DEFINE_SCRIPTED_PROPERTY ( type , S , DynamicLight )
2013-06-23 07:49:34 +00:00
{
PROP_STRING_PARM ( str , 0 ) ;
static const char * ltype_names [ ] = {
2018-11-05 23:13:23 +00:00
" Point " , " Pulse " , " Flicker " , " Sector " , " RandomFlicker " , " ColorPulse " , " ColorFlicker " , " RandomColorFlicker " , nullptr } ;
2013-06-23 07:49:34 +00:00
static const int ltype_values [ ] = {
PointLight , PulseLight , FlickerLight , SectorLight , RandomFlickerLight , ColorPulseLight , ColorFlickerLight , RandomColorFlickerLight } ;
int style = MatchString ( str , ltype_names ) ;
if ( style < 0 ) I_Error ( " Unknown light type '%s' " , str ) ;
2019-01-01 18:35:55 +00:00
defaults - > IntVar ( NAME_lighttype ) = ltype_values [ style ] ;
2013-06-23 07:49:34 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
2019-01-01 18:35:55 +00:00
static FDynamicLight * GetLight ( )
{
FDynamicLight * ret ;
if ( FreeList . Size ( ) )
{
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 ;
}
2018-01-04 16:58:11 +00:00
2013-06-23 07:49:34 +00:00
//==========================================================================
//
2019-01-01 18:35:55 +00:00
// Attaches a dynamic light descriptor to a dynamic light actor.
// Owned lights do not use this function.
2013-06-23 07:49:34 +00:00
//
//==========================================================================
2019-01-01 18:35:55 +00:00
void AttachLight ( AActor * self )
{
auto light = GetLight ( ) ;
light - > pSpotInnerAngle = & self - > AngleVar ( NAME_SpotInnerAngle ) ;
light - > pSpotOuterAngle = & self - > AngleVar ( NAME_SpotOuterAngle ) ;
light - > pPitch = & self - > Angles . Pitch ;
2019-01-03 08:24:22 +00:00
light - > pLightFlags = ( LightFlags * ) & self - > IntVar ( NAME_lightflags ) ;
2019-01-01 18:35:55 +00:00
light - > pArgs = self - > args ;
light - > specialf1 = DAngle ( double ( self - > SpawnAngle ) ) . Normalized360 ( ) . Degrees ;
light - > Sector = self - > Sector ;
light - > target = self ;
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 ;
}
2013-06-23 07:49:34 +00:00
//==========================================================================
//
//
//
//==========================================================================
2017-06-18 08:15:31 +00:00
2019-01-01 18:35:55 +00:00
void ActivateLight ( AActor * self )
{
for ( auto l : self - > AttachedLights ) l - > Activate ( ) ;
2013-06-23 07:49:34 +00:00
}
2019-01-01 18:35:55 +00:00
DEFINE_ACTION_FUNCTION_NATIVE ( ADynamicLight , ActivateLight , ActivateLight )
2016-09-23 23:47:44 +00:00
{
2019-01-01 18:35:55 +00:00
PARAM_SELF_PROLOGUE ( AActor ) ;
ActivateLight ( self ) ;
return 0 ;
2016-09-23 23:47:44 +00:00
}
2019-01-01 18:35:55 +00:00
2013-06-23 07:49:34 +00:00
//==========================================================================
//
2019-01-01 18:35:55 +00:00
//
2013-06-23 07:49:34 +00:00
//
//==========================================================================
2019-01-01 18:35:55 +00:00
void DeactivateLight ( AActor * self )
2013-06-23 07:49:34 +00:00
{
2019-01-02 23:39:42 +00:00
for ( auto l : self - > AttachedLights ) l - > Deactivate ( ) ;
2019-01-01 18:35:55 +00:00
}
2013-06-23 07:49:34 +00:00
2019-01-01 18:35:55 +00:00
DEFINE_ACTION_FUNCTION_NATIVE ( ADynamicLight , DeactivateLight , DeactivateLight )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
DeactivateLight ( self ) ;
return 0 ;
2013-06-23 07:49:34 +00:00
}
//==========================================================================
//
2019-01-01 18:35:55 +00:00
//
2013-06-23 07:49:34 +00:00
//
//==========================================================================
2019-01-01 18:35:55 +00:00
static void SetOffset ( AActor * self , double x , double y , double z )
2013-06-23 07:49:34 +00:00
{
2019-01-01 18:35:55 +00:00
for ( auto l : self - > AttachedLights )
2013-06-23 07:49:34 +00:00
{
2019-01-01 18:35:55 +00:00
l - > SetOffset ( DVector3 ( x , y , z ) ) ;
2013-06-23 07:49:34 +00:00
}
2019-01-01 18:35:55 +00:00
}
2013-06-23 07:49:34 +00:00
2019-01-01 18:35:55 +00:00
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 ) ;
2013-06-23 07:49:34 +00:00
}
//==========================================================================
//
2016-09-14 18:01:13 +00:00
// [TS]
2013-06-23 07:49:34 +00:00
//
//==========================================================================
2019-01-01 18:35:55 +00:00
void FDynamicLight : : Activate ( )
2013-06-23 07:49:34 +00:00
{
2019-01-01 18:35:55 +00:00
m_active = true ;
m_currentRadius = float ( GetIntensity ( ) ) ;
2013-06-23 07:49:34 +00:00
m_tickCount = 0 ;
if ( lighttype = = PulseLight )
{
2017-03-16 17:51:54 +00:00
float pulseTime = float ( specialf1 / TICRATE ) ;
2019-01-01 18:35:55 +00:00
2013-06-23 07:49:34 +00:00
m_lastUpdate = level . maptime ;
2019-01-01 18:35:55 +00:00
if ( ! swapped ) m_cycler . SetParams ( float ( GetSecondaryIntensity ( ) ) , float ( GetIntensity ( ) ) , pulseTime ) ;
else m_cycler . SetParams ( float ( GetIntensity ( ) ) , float ( GetSecondaryIntensity ( ) ) , pulseTime ) ;
2013-06-23 07:49:34 +00:00
m_cycler . ShouldCycle ( true ) ;
m_cycler . SetCycleType ( CYCLE_Sin ) ;
2017-03-16 17:51:54 +00:00
m_currentRadius = float ( m_cycler . GetVal ( ) ) ;
2013-06-23 07:49:34 +00:00
}
2017-01-29 11:00:05 +00:00
if ( m_currentRadius < = 0 ) m_currentRadius = 1 ;
2013-06-23 07:49:34 +00:00
}
//==========================================================================
//
2016-09-14 18:01:13 +00:00
// [TS]
2013-06-23 07:49:34 +00:00
//
//==========================================================================
2019-01-01 18:35:55 +00:00
void FDynamicLight : : Tick ( )
2013-06-23 07:49:34 +00:00
{
2019-01-01 18:35:55 +00:00
if ( ! target )
{
delete this ;
return ;
}
2013-06-23 07:49:34 +00:00
2019-01-01 18:35:55 +00:00
if ( owned )
2013-06-23 07:49:34 +00:00
{
2019-01-01 18:35:55 +00:00
if ( ! target - > state )
{
delete this ;
return ;
}
if ( target - > flags & MF_UNMORPHED )
2013-06-23 07:49:34 +00:00
{
2019-01-01 18:35:55 +00:00
m_active = false ;
2013-06-23 07:49:34 +00:00
return ;
}
2016-05-04 09:33:18 +00:00
visibletoplayer = target - > IsVisibleToPlayer ( ) ; // cache this value for the renderer to speed up calculations.
2013-06-23 07:49:34 +00:00
}
// Don't bother if the light won't be shown
if ( ! IsActive ( ) ) return ;
// I am doing this with a type field so that I can dynamically alter the type of light
// without having to create or maintain multiple objects.
switch ( lighttype )
{
case PulseLight :
{
float diff = ( level . maptime - m_lastUpdate ) / ( float ) TICRATE ;
m_lastUpdate = level . maptime ;
m_cycler . Update ( diff ) ;
2017-03-16 17:51:54 +00:00
m_currentRadius = float ( m_cycler . GetVal ( ) ) ;
2013-06-23 07:49:34 +00:00
break ;
}
case FlickerLight :
{
2019-01-01 18:35:55 +00:00
int rnd = randLight ( 360 ) ;
m_currentRadius = float ( ( rnd > = int ( specialf1 ) ) ? GetIntensity ( ) : GetSecondaryIntensity ( ) ) ;
2013-06-23 07:49:34 +00:00
break ;
}
case RandomFlickerLight :
{
2019-01-01 18:35:55 +00:00
int flickerRange = GetSecondaryIntensity ( ) - GetIntensity ( ) ;
2013-06-23 07:49:34 +00:00
float amt = randLight ( ) / 255.f ;
2016-12-06 17:35:34 +00:00
if ( m_tickCount > specialf1 )
2013-06-23 07:49:34 +00:00
{
m_tickCount = 0 ;
}
2019-01-01 18:35:55 +00:00
if ( m_tickCount + + = = 0 | | m_currentRadius > GetSecondaryIntensity ( ) )
2016-12-06 17:35:34 +00:00
{
2019-01-01 18:35:55 +00:00
m_currentRadius = float ( GetIntensity ( ) + ( amt * flickerRange ) ) ;
2016-12-06 17:35:34 +00:00
}
2013-06-23 07:49:34 +00:00
break ;
}
#if 0
// These need some more work elsewhere
case ColorFlickerLight :
{
2017-02-03 09:13:16 +00:00
int rnd = randLight ( ) ;
2016-12-06 17:35:34 +00:00
float pct = specialf1 / 360.f ;
2013-06-23 07:49:34 +00:00
2016-04-17 11:53:29 +00:00
m_currentRadius = m_Radius [ rnd > = pct * 255 ] ;
2013-06-23 07:49:34 +00:00
break ;
}
case RandomColorFlickerLight :
{
2019-01-01 18:35:55 +00:00
int flickerRange = GetSecondaryIntensity ( ) - GetIntensity ( ) ;
2013-06-23 07:49:34 +00:00
float amt = randLight ( ) / 255.f ;
m_tickCount + + ;
2016-12-06 17:35:34 +00:00
if ( m_tickCount > specialf1 )
2013-06-23 07:49:34 +00:00
{
2019-01-01 18:35:55 +00:00
m_currentRadius = GetIntensity ( ) + ( amt * flickerRange ) ;
2013-06-23 07:49:34 +00:00
m_tickCount = 0 ;
}
break ;
}
# endif
case SectorLight :
{
float intensity ;
2019-01-01 18:35:55 +00:00
float scale = GetIntensity ( ) / 8.f ;
2013-06-23 07:49:34 +00:00
if ( scale = = 0.f ) scale = 1.f ;
intensity = Sector - > lightlevel * scale ;
2018-11-04 19:56:44 +00:00
intensity = clamp < float > ( intensity , 0.f , 255.f ) ;
2013-06-23 07:49:34 +00:00
2016-04-17 11:53:29 +00:00
m_currentRadius = intensity ;
2013-06-23 07:49:34 +00:00
break ;
}
case PointLight :
2019-01-01 18:35:55 +00:00
m_currentRadius = float ( GetIntensity ( ) ) ;
2013-06-23 07:49:34 +00:00
break ;
}
2017-01-29 11:00:05 +00:00
if ( m_currentRadius < = 0 ) m_currentRadius = 1 ;
2013-06-23 07:49:34 +00:00
UpdateLocation ( ) ;
}
//==========================================================================
//
//
//
//==========================================================================
2019-01-01 18:35:55 +00:00
void FDynamicLight : : UpdateLocation ( )
2013-06-23 07:49:34 +00:00
{
2016-03-21 01:57:02 +00:00
double oldx = X ( ) ;
double oldy = Y ( ) ;
2019-01-01 18:35:55 +00:00
float oldradius = radius ;
2013-06-23 07:49:34 +00:00
if ( IsActive ( ) )
{
2019-01-01 18:35:55 +00:00
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 ( ) ;
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.
2013-06-23 07:49:34 +00:00
2019-01-01 18:35:55 +00:00
// 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. ;
2013-06-23 07:49:34 +00:00
// The radius being used here is always the maximum possible with the
// current settings. This avoids constant relinking of flickering lights
2019-01-01 18:35:55 +00:00
float intensity ;
2016-04-12 14:01:08 +00:00
if ( lighttype = = FlickerLight | | lighttype = = RandomFlickerLight | | lighttype = = PulseLight )
2013-06-23 07:49:34 +00:00
{
2019-01-01 18:35:55 +00:00
intensity = float ( MAX ( GetIntensity ( ) , GetSecondaryIntensity ( ) ) ) ;
2013-06-23 07:49:34 +00:00
}
else
{
2016-04-17 11:53:29 +00:00
intensity = m_currentRadius ;
2013-06-23 07:49:34 +00:00
}
2016-09-04 10:45:09 +00:00
radius = intensity * 2.0f ;
2017-02-01 18:24:05 +00:00
if ( radius < m_currentRadius * 2 ) radius = m_currentRadius * 2 ;
2013-06-23 07:49:34 +00:00
2016-03-21 01:57:02 +00:00
if ( X ( ) ! = oldx | | Y ( ) ! = oldy | | radius ! = oldradius )
2013-06-23 07:49:34 +00:00
{
//Update the light lists
LinkLight ( ) ;
}
}
}
//=============================================================================
//
// These have been copied from the secnode code and modified for the light links
//
// P_AddSecnode() searches the current list to see if this sector is
// already there. If not, it adds a sector node at the head of the list of
// sectors this object appears in. This is called when creating a list of
// nodes that will get linked in later. Returns a pointer to the new node.
//
//=============================================================================
2019-01-01 18:35:55 +00:00
FLightNode * AddLightNode ( FLightNode * * thread , void * linkto , FDynamicLight * light , FLightNode * & nextnode )
2013-06-23 07:49:34 +00:00
{
FLightNode * node ;
node = nextnode ;
while ( node )
{
if ( node - > targ = = linkto ) // Already have a node for this sector?
{
node - > lightsource = light ; // Yes. Setting m_thing says 'keep it'.
return ( nextnode ) ;
}
node = node - > nextTarget ;
}
// Couldn't find an existing node for this sector. Add one at the head
// of the list.
2014-09-21 10:40:08 +00:00
node = new FLightNode ;
2013-06-23 07:49:34 +00:00
node - > targ = linkto ;
node - > lightsource = light ;
node - > prevTarget = & nextnode ;
node - > nextTarget = nextnode ;
if ( nextnode ) nextnode - > prevTarget = & node - > nextTarget ;
// Add new node at head of sector thread starting at s->touching_thinglist
node - > prevLight = thread ;
node - > nextLight = * thread ;
if ( node - > nextLight ) node - > nextLight - > prevLight = & node - > nextLight ;
* thread = node ;
return ( node ) ;
}
//=============================================================================
//
// P_DelSecnode() deletes a sector node from the list of
// sectors this object appears in. Returns a pointer to the next node
2018-11-05 23:13:23 +00:00
// on the linked list, or nullptr.
2013-06-23 07:49:34 +00:00
//
//=============================================================================
static FLightNode * DeleteLightNode ( FLightNode * node )
{
FLightNode * tn ; // next node on thing thread
if ( node )
{
* node - > prevTarget = node - > nextTarget ;
if ( node - > nextTarget ) node - > nextTarget - > prevTarget = node - > prevTarget ;
* node - > prevLight = node - > nextLight ;
if ( node - > nextLight ) node - > nextLight - > prevLight = node - > prevLight ;
// Return this node to the freelist
tn = node - > nextTarget ;
2014-09-21 10:40:08 +00:00
delete node ;
2013-06-23 07:49:34 +00:00
return ( tn ) ;
}
2018-11-05 23:13:23 +00:00
return ( nullptr ) ;
2013-06-23 07:49:34 +00:00
} // phares 3/13/98
//==========================================================================
//
// Gets the light's distance to a line
//
//==========================================================================
2019-01-01 18:35:55 +00:00
double FDynamicLight : : DistToSeg ( const DVector3 & pos , vertex_t * start , vertex_t * end )
2013-06-23 07:49:34 +00:00
{
2016-03-29 09:26:33 +00:00
double u , px , py ;
2013-06-23 07:49:34 +00:00
2018-11-05 23:47:43 +00:00
double seg_dx = end - > fX ( ) - start - > fX ( ) ;
double seg_dy = end - > fY ( ) - start - > fY ( ) ;
2016-03-29 09:26:33 +00:00
double seg_length_sq = seg_dx * seg_dx + seg_dy * seg_dy ;
2013-06-23 07:49:34 +00:00
2018-11-05 23:47:43 +00:00
u = ( ( ( pos . X - start - > fX ( ) ) * seg_dx ) + ( pos . Y - start - > fY ( ) ) * seg_dy ) / seg_length_sq ;
2016-03-29 09:26:33 +00:00
if ( u < 0. ) u = 0. ; // clamp the test point to the line segment
2016-03-30 18:01:44 +00:00
else if ( u > 1. ) u = 1. ;
2013-06-23 07:49:34 +00:00
2018-11-05 23:47:43 +00:00
px = start - > fX ( ) + ( u * seg_dx ) ;
py = start - > fY ( ) + ( u * seg_dy ) ;
2013-06-23 07:49:34 +00:00
2016-03-30 18:01:44 +00:00
px - = pos . X ;
py - = pos . Y ;
2013-06-23 07:49:34 +00:00
2016-03-08 20:22:12 +00:00
return ( px * px ) + ( py * py ) ;
2013-06-23 07:49:34 +00:00
}
//==========================================================================
//
// Collect all touched sidedefs and subsectors
// to sidedefs and sector parts.
//
//==========================================================================
2016-12-08 11:49:40 +00:00
struct LightLinkEntry
{
2018-11-05 23:13:23 +00:00
FSection * sect ;
2016-12-08 11:49:40 +00:00
DVector3 pos ;
} ;
static TArray < LightLinkEntry > collected_ss ;
2013-06-23 07:49:34 +00:00
2019-01-01 18:35:55 +00:00
void FDynamicLight : : CollectWithinRadius ( const DVector3 & opos , FSection * section , float radius )
2013-06-23 07:49:34 +00:00
{
2018-11-05 23:13:23 +00:00
if ( ! section ) return ;
2016-12-08 11:49:40 +00:00
collected_ss . Clear ( ) ;
2018-11-05 23:13:23 +00:00
collected_ss . Push ( { section , opos } ) ;
section - > validcount = dl_validcount ;
2013-06-23 07:49:34 +00:00
2017-03-19 23:34:19 +00:00
bool hitonesidedback = false ;
2016-12-08 11:49:40 +00:00
for ( unsigned i = 0 ; i < collected_ss . Size ( ) ; i + + )
2014-05-11 20:57:42 +00:00
{
2018-11-05 23:47:43 +00:00
auto & pos = collected_ss [ i ] . pos ;
2018-11-05 23:13:23 +00:00
section = collected_ss [ i ] . sect ;
2013-06-23 07:49:34 +00:00
2018-11-05 23:13:23 +00:00
touching_sector = AddLightNode ( & section - > lighthead , section , this , touching_sector ) ;
2013-06-23 07:49:34 +00:00
2016-12-08 11:49:40 +00:00
2018-11-06 10:53:03 +00:00
auto processSide = [ & ] ( side_t * sidedef , const vertex_t * v1 , const vertex_t * v2 )
2018-11-05 23:47:43 +00:00
{
auto linedef = sidedef - > linedef ;
if ( linedef & & linedef - > validcount ! = : : validcount )
2013-06-23 07:49:34 +00:00
{
2018-11-05 23:47:43 +00:00
// light is in front of the seg
if ( ( pos . Y - v1 - > fY ( ) ) * ( v2 - > fX ( ) - v1 - > fX ( ) ) + ( v1 - > fX ( ) - pos . X ) * ( v2 - > fY ( ) - v1 - > fY ( ) ) < = 0 )
2016-04-08 10:38:09 +00:00
{
2018-11-05 23:47:43 +00:00
linedef - > validcount = : : validcount ;
touching_sides = AddLightNode ( & sidedef - > lighthead , sidedef , this , touching_sides ) ;
2016-04-08 10:38:09 +00:00
}
2018-11-05 23:47:43 +00:00
else if ( linedef - > sidedef [ 0 ] = = sidedef & & linedef - > sidedef [ 1 ] = = nullptr )
{
hitonesidedback = true ;
}
}
if ( linedef )
{
FLinePortal * port = linedef - > getPortal ( ) ;
if ( port & & port - > mType = = PORTT_LINKED )
2016-03-08 20:22:12 +00:00
{
2018-11-05 23:47:43 +00:00
line_t * other = port - > mDestination ;
if ( other - > validcount ! = : : validcount )
2016-03-08 20:22:12 +00:00
{
2018-11-05 23:47:43 +00:00
subsector_t * othersub = R_PointInSubsector ( other - > v1 - > fPos ( ) + other - > Delta ( ) / 2 ) ;
FSection * othersect = othersub - > section ;
if ( othersect - > validcount ! = : : validcount )
2016-12-08 11:49:40 +00:00
{
2018-11-05 23:47:43 +00:00
othersect - > validcount = : : validcount ;
2019-01-01 18:35:55 +00:00
collected_ss . Push ( { othersect , PosRelative ( other - > frontsector - > PortalGroup ) } ) ;
2016-12-08 11:49:40 +00:00
}
2016-03-08 20:22:12 +00:00
}
}
2018-11-05 23:47:43 +00:00
}
} ;
2013-06-23 07:49:34 +00:00
2018-11-05 23:47:43 +00:00
for ( auto & segment : section - > segments )
{
// check distance from x/y to seg and if within radius add this seg and, if present the opposing subsector (lather/rinse/repeat)
// If out of range we do not need to bother with this seg.
if ( DistToSeg ( pos , segment . start , segment . end ) < = radius )
{
auto sidedef = segment . sidedef ;
if ( sidedef )
{
processSide ( sidedef , segment . start , segment . end ) ;
}
2013-06-23 07:49:34 +00:00
2018-11-05 23:13:23 +00:00
auto partner = segment . partner ;
2016-12-08 11:49:40 +00:00
if ( partner )
2013-06-23 07:49:34 +00:00
{
2018-11-05 23:13:23 +00:00
FSection * sect = partner - > section ;
if ( sect ! = nullptr & & sect - > validcount ! = dl_validcount )
2016-12-08 11:49:40 +00:00
{
2018-11-05 23:13:23 +00:00
sect - > validcount = dl_validcount ;
collected_ss . Push ( { sect , pos } ) ;
2016-12-08 11:49:40 +00:00
}
2013-06-23 07:49:34 +00:00
}
}
}
2018-11-05 23:47:43 +00:00
for ( auto side : section - > sides )
{
2018-11-06 10:53:03 +00:00
auto v1 = side - > V1 ( ) , v2 = side - > V2 ( ) ;
if ( DistToSeg ( pos , v1 , v2 ) < = radius )
{
processSide ( side , v1 , v2 ) ;
}
2018-11-05 23:47:43 +00:00
}
2018-11-05 23:13:23 +00:00
sector_t * sec = section - > sector ;
2016-12-08 11:49:40 +00:00
if ( ! sec - > PortalBlocksSight ( sector_t : : ceiling ) )
2016-03-08 20:22:12 +00:00
{
2018-11-05 23:13:23 +00:00
line_t * other = section - > segments [ 0 ] . sidedef - > linedef ;
2016-12-08 11:49:40 +00:00
if ( sec - > GetPortalPlaneZ ( sector_t : : ceiling ) < Z ( ) + radius )
{
DVector2 refpos = other - > v1 - > fPos ( ) + other - > Delta ( ) / 2 + sec - > GetPortalDisplacement ( sector_t : : ceiling ) ;
subsector_t * othersub = R_PointInSubsector ( refpos ) ;
2018-11-05 23:13:23 +00:00
FSection * othersect = othersub - > section ;
if ( othersect - > validcount ! = dl_validcount )
2016-12-08 11:49:40 +00:00
{
2018-11-05 23:13:23 +00:00
othersect - > validcount = dl_validcount ;
2019-01-01 18:35:55 +00:00
collected_ss . Push ( { othersect , PosRelative ( othersub - > sector - > PortalGroup ) } ) ;
2016-12-08 11:49:40 +00:00
}
}
2016-03-08 20:22:12 +00:00
}
2016-12-08 11:49:40 +00:00
if ( ! sec - > PortalBlocksSight ( sector_t : : floor ) )
2016-03-08 20:22:12 +00:00
{
2018-11-05 23:13:23 +00:00
line_t * other = section - > segments [ 0 ] . sidedef - > linedef ;
2016-12-08 11:49:40 +00:00
if ( sec - > GetPortalPlaneZ ( sector_t : : floor ) > Z ( ) - radius )
{
DVector2 refpos = other - > v1 - > fPos ( ) + other - > Delta ( ) / 2 + sec - > GetPortalDisplacement ( sector_t : : floor ) ;
subsector_t * othersub = R_PointInSubsector ( refpos ) ;
2018-11-05 23:13:23 +00:00
FSection * othersect = othersub - > section ;
if ( othersect - > validcount ! = dl_validcount )
2016-12-08 11:49:40 +00:00
{
2018-11-05 23:13:23 +00:00
othersect - > validcount = dl_validcount ;
2019-01-01 18:35:55 +00:00
collected_ss . Push ( { othersect , PosRelative ( othersub - > sector - > PortalGroup ) } ) ;
2016-12-08 11:49:40 +00:00
}
}
2016-03-08 20:22:12 +00:00
}
}
2019-01-03 08:24:22 +00:00
shadowmapped = hitonesidedback & & ! DontShadowmap ( ) ;
2013-06-23 07:49:34 +00:00
}
//==========================================================================
//
// Link the light into the world
//
//==========================================================================
2019-01-01 18:35:55 +00:00
void FDynamicLight : : LinkLight ( )
2013-06-23 07:49:34 +00:00
{
// mark the old light nodes
FLightNode * node ;
node = touching_sides ;
while ( node )
{
2018-11-05 23:13:23 +00:00
node - > lightsource = nullptr ;
2013-06-23 07:49:34 +00:00
node = node - > nextTarget ;
}
2014-05-11 20:57:42 +00:00
node = touching_sector ;
while ( node )
{
2018-11-05 23:13:23 +00:00
node - > lightsource = nullptr ;
2014-05-11 20:57:42 +00:00
node = node - > nextTarget ;
}
2013-06-23 07:49:34 +00:00
if ( radius > 0 )
{
2016-03-30 18:01:44 +00:00
// passing in radius*radius allows us to do a distance check without any calls to sqrt
2019-01-01 18:35:55 +00:00
FSection * sect = R_PointInSubsector ( Pos ) - > section ;
2018-11-05 23:13:23 +00:00
dl_validcount + + ;
2016-03-08 20:22:12 +00:00
: : validcount + + ;
2019-01-01 18:35:55 +00:00
CollectWithinRadius ( Pos , sect , float ( radius * radius ) ) ;
2016-03-08 20:22:12 +00:00
2013-06-23 07:49:34 +00:00
}
// Now delete any nodes that won't be used. These are the ones where
2018-11-05 23:13:23 +00:00
// m_thing is still nullptr.
2013-06-23 07:49:34 +00:00
node = touching_sides ;
while ( node )
{
2018-11-05 23:13:23 +00:00
if ( node - > lightsource = = nullptr )
2013-06-23 07:49:34 +00:00
{
node = DeleteLightNode ( node ) ;
}
else
node = node - > nextTarget ;
}
2014-05-11 20:57:42 +00:00
node = touching_sector ;
while ( node )
{
2018-11-05 23:13:23 +00:00
if ( node - > lightsource = = nullptr )
2014-05-11 20:57:42 +00:00
{
node = DeleteLightNode ( node ) ;
}
else
node = node - > nextTarget ;
}
2013-06-23 07:49:34 +00:00
}
//==========================================================================
//
// Deletes the link lists
//
//==========================================================================
2019-01-01 18:35:55 +00:00
void FDynamicLight : : UnlinkLight ( )
2013-06-23 07:49:34 +00:00
{
while ( touching_sides ) touching_sides = DeleteLightNode ( touching_sides ) ;
2014-09-21 10:40:08 +00:00
while ( touching_sector ) touching_sector = DeleteLightNode ( touching_sector ) ;
2017-03-19 23:34:19 +00:00
shadowmapped = false ;
2013-06-23 07:49:34 +00:00
}
2018-04-02 10:28:20 +00:00
//==========================================================================
//
//
//
//==========================================================================
void AActor : : AttachLight ( unsigned int count , const FLightDefaults * lightdef )
{
2019-01-01 18:35:55 +00:00
FDynamicLight * light ;
2018-04-02 10:28:20 +00:00
if ( count < AttachedLights . Size ( ) )
{
2019-01-01 18:35:55 +00:00
light = AttachedLights [ count ] ;
2018-11-05 23:13:23 +00:00
assert ( light ! = nullptr ) ;
2018-04-02 10:28:20 +00:00
}
else
{
2019-01-01 18:35:55 +00:00
light = GetLight ( ) ;
light - > SetActor ( this , true ) ;
2018-04-02 10:28:20 +00:00
AttachedLights . Push ( light ) ;
}
lightdef - > ApplyProperties ( light ) ;
}
//==========================================================================
//
// per-state light adjustment
//
//==========================================================================
extern TArray < FLightDefaults * > StateLights ;
void AActor : : SetDynamicLights ( )
{
TArray < FInternalLightAssociation * > & LightAssociations = GetInfo ( ) - > LightAssociations ;
unsigned int count = 0 ;
2018-11-05 23:13:23 +00:00
if ( state = = nullptr ) return ;
2018-04-02 10:28:20 +00:00
if ( LightAssociations . Size ( ) > 0 )
{
unsigned int i ;
for ( i = 0 ; i < LightAssociations . Size ( ) ; i + + )
{
if ( LightAssociations [ i ] - > Sprite ( ) = = sprite & &
( LightAssociations [ i ] - > Frame ( ) = = frame | | LightAssociations [ i ] - > Frame ( ) = = - 1 ) )
{
AttachLight ( count + + , LightAssociations [ i ] - > Light ( ) ) ;
}
}
}
if ( count = = 0 & & state - > Light > 0 )
{
2018-11-05 23:13:23 +00:00
for ( int i = state - > Light ; StateLights [ i ] ! = nullptr ; i + + )
2018-04-02 10:28:20 +00:00
{
if ( StateLights [ i ] ! = ( FLightDefaults * ) - 1 )
{
AttachLight ( count + + , StateLights [ i ] ) ;
}
}
}
for ( ; count < AttachedLights . Size ( ) ; count + + )
{
2019-01-01 18:35:55 +00:00
AttachedLights [ count ] - > Deactivate ( ) ;
2018-04-02 10:28:20 +00:00
}
}
//==========================================================================
//
2019-01-01 18:35:55 +00:00
//
2018-04-02 10:28:20 +00:00
//
//==========================================================================
2019-01-01 18:35:55 +00:00
void AActor : : DeleteAttachedLights ( )
2018-04-02 10:28:20 +00:00
{
2019-01-01 18:35:55 +00:00
for ( auto l : AttachedLights )
2018-04-02 10:28:20 +00:00
{
2019-01-01 18:35:55 +00:00
l - > UnlinkLight ( ) ;
l - > ReleaseLight ( ) ;
2018-04-02 10:28:20 +00:00
}
2019-01-01 18:35:55 +00:00
AttachedLights . Clear ( ) ;
2018-04-02 10:28:20 +00:00
}
//==========================================================================
//
// This is called before saving the game
//
//==========================================================================
void AActor : : DeleteAllAttachedLights ( )
{
TThinkerIterator < AActor > it ;
AActor * a ;
while ( ( a = it . Next ( ) ) )
{
2019-01-01 18:35:55 +00:00
a - > DeleteAttachedLights ( ) ;
2018-04-02 10:28:20 +00:00
}
}
//==========================================================================
//
//
//
//==========================================================================
void AActor : : RecreateAllAttachedLights ( )
{
TThinkerIterator < AActor > it ;
AActor * a ;
while ( ( a = it . Next ( ) ) )
{
2019-01-01 18:35:55 +00:00
if ( a - > IsKindOf ( NAME_DynamicLight ) )
{
: : AttachLight ( a ) ;
}
else
{
a - > SetDynamicLights ( ) ;
}
2018-04-02 10:28:20 +00:00
}
}
//==========================================================================
//
// CCMDs
//
//==========================================================================
2013-06-23 07:49:34 +00:00
CCMD ( listlights )
{
2018-11-05 21:35:24 +00:00
int walls , sectors ;
2014-05-11 20:57:42 +00:00
int allwalls = 0 , allsectors = 0 , allsubsecs = 0 ;
2017-03-19 23:34:19 +00:00
int i = 0 , shadowcount = 0 ;
2019-01-01 18:35:55 +00:00
FDynamicLight * dl ;
2013-06-23 07:49:34 +00:00
2019-01-01 18:35:55 +00:00
for ( dl = level . lights ; dl ; dl = dl - > next )
2013-06-23 07:49:34 +00:00
{
walls = 0 ;
sectors = 0 ;
2017-03-19 23:34:19 +00:00
Printf ( " %s at (%f, %f, %f), color = 0x%02x%02x%02x, radius = %f %s %s " ,
2019-01-01 18:35:55 +00:00
dl - > target - > GetClass ( ) - > TypeName . GetChars ( ) ,
dl - > X ( ) , dl - > Y ( ) , dl - > Z ( ) , dl - > GetRed ( ) , dl - > GetGreen ( ) , dl - > GetBlue ( ) ,
2019-01-03 08:24:22 +00:00
dl - > radius , dl - > IsAttenuated ( ) ? " attenuated " : " " , dl - > shadowmapped ? " shadowmapped " : " " ) ;
2013-06-23 07:49:34 +00:00
i + + ;
2017-03-19 23:34:19 +00:00
shadowcount + = dl - > shadowmapped ;
2013-06-23 07:49:34 +00:00
if ( dl - > target )
{
2017-03-16 12:49:34 +00:00
FTextureID spr = sprites [ dl - > target - > sprite ] . GetSpriteFrame ( dl - > target - > frame , 0 , 0. , nullptr ) ;
2018-12-07 02:01:40 +00:00
Printf ( " , frame = %s " , TexMan . GetTexture ( spr ) - > GetName ( ) . GetChars ( ) ) ;
2013-06-23 07:49:34 +00:00
}
FLightNode * node ;
node = dl - > touching_sides ;
while ( node )
{
walls + + ;
allwalls + + ;
node = node - > nextTarget ;
}
2014-05-11 20:57:42 +00:00
node = dl - > touching_sector ;
2013-06-23 07:49:34 +00:00
while ( node )
{
allsectors + + ;
sectors + + ;
node = node - > nextTarget ;
}
2018-11-05 21:35:24 +00:00
Printf ( " - %d walls, %d sectors \n " , walls , sectors ) ;
2013-06-23 07:49:34 +00:00
}
2018-11-05 21:35:24 +00:00
Printf ( " %i dynamic lights, %d shadowmapped, %d walls, %d sectors \n \n \n " , i , shadowcount , allwalls , allsectors ) ;
2013-06-23 07:49:34 +00:00
}