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"
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
//==========================================================================
//
//==========================================================================
DEFINE_CLASS_PROPERTY ( type , S , DynamicLight )
{
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 ) ;
defaults - > lighttype = ltype_values [ style ] ;
}
//==========================================================================
//
// Actor classes
//
// For flexibility all functionality has been packed into a single class
// which is controlled by flags
//
//==========================================================================
2017-01-13 22:17:04 +00:00
IMPLEMENT_CLASS ( ADynamicLight , false , false )
2013-06-23 07:49:34 +00:00
2018-01-04 16:58:11 +00:00
DEFINE_FIELD ( ADynamicLight , SpotInnerAngle )
DEFINE_FIELD ( ADynamicLight , SpotOuterAngle )
2013-06-23 07:49:34 +00:00
static FRandom randLight ;
//==========================================================================
//
// Base class
//
//==========================================================================
//==========================================================================
//
//
//
//==========================================================================
2016-09-23 23:47:44 +00:00
void ADynamicLight : : Serialize ( FSerializer & arc )
2013-06-23 07:49:34 +00:00
{
Super : : Serialize ( arc ) ;
2016-09-23 23:47:44 +00:00
auto def = static_cast < ADynamicLight * > ( GetDefault ( ) ) ;
arc ( " lightflags " , lightflags , def - > lightflags )
( " lighttype " , lighttype , def - > lighttype )
( " tickcount " , m_tickCount , def - > m_tickCount )
2018-01-04 16:58:11 +00:00
( " currentradius " , m_currentRadius , def - > m_currentRadius )
( " spotinnerangle " , SpotInnerAngle , def - > SpotInnerAngle )
( " spotouterangle " , SpotOuterAngle , def - > SpotOuterAngle ) ;
2016-09-23 23:47:44 +00:00
if ( lighttype = = PulseLight )
arc ( " lastupdate " , m_lastUpdate , def - > m_lastUpdate )
( " cycler " , m_cycler , def - > m_cycler ) ;
2017-06-18 08:15:31 +00:00
// Remap the old flags.
if ( SaveVersion < 4552 )
{
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.
}
2013-06-23 07:49:34 +00:00
}
2016-09-23 23:47:44 +00:00
void ADynamicLight : : PostSerialize ( )
{
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 ;
2018-06-10 20:57:34 +00:00
mShadowmapIndex = 1024 ;
2016-09-23 23:47:44 +00:00
LinkLight ( ) ;
}
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
//
//==========================================================================
void ADynamicLight : : BeginPlay ( )
{
//Super::BeginPlay();
ChangeStatNum ( STAT_DLIGHT ) ;
2016-12-06 17:35:34 +00:00
specialf1 = DAngle ( double ( SpawnAngle ) ) . Normalized360 ( ) . Degrees ;
2016-05-04 09:33:18 +00:00
visibletoplayer = true ;
2018-04-25 07:14:01 +00:00
mShadowmapIndex = 1024 ;
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
//
//==========================================================================
void ADynamicLight : : PostBeginPlay ( )
{
Super : : PostBeginPlay ( ) ;
if ( ! ( SpawnFlags & MTF_DORMANT ) )
{
2018-11-05 23:13:23 +00:00
Activate ( nullptr ) ;
2013-06-23 07:49:34 +00:00
}
2016-03-30 18:01:44 +00:00
subsector = R_PointInSubsector ( Pos ( ) ) ;
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
//
//==========================================================================
void ADynamicLight : : Activate ( AActor * activator )
{
//Super::Activate(activator);
flags2 & = ~ MF2_DORMANT ;
2017-01-18 18:10:25 +00:00
m_currentRadius = float ( args [ LIGHT_INTENSITY ] ) ;
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 ) ;
2013-06-23 07:49:34 +00:00
m_lastUpdate = level . maptime ;
2017-03-01 19:54:37 +00:00
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 ) ;
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
//
//==========================================================================
void ADynamicLight : : Deactivate ( AActor * activator )
{
//Super::Deactivate(activator);
flags2 | = MF2_DORMANT ;
}
//==========================================================================
//
2016-09-14 18:01:13 +00:00
// [TS]
2013-06-23 07:49:34 +00:00
//
//==========================================================================
void ADynamicLight : : Tick ( )
{
if ( IsOwned ( ) )
{
if ( ! target | | ! target - > state )
{
this - > Destroy ( ) ;
return ;
}
if ( target - > flags & MF_UNMORPHED ) 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 :
{
2017-02-03 09:13:16 +00:00
int rnd = randLight ( ) ;
2017-03-16 17:51:54 +00:00
float pct = float ( specialf1 / 360.f ) ;
2013-06-23 07:49:34 +00:00
2017-01-18 18:10:25 +00:00
m_currentRadius = float ( args [ LIGHT_INTENSITY + ( rnd > = pct * 255 ) ] ) ;
2013-06-23 07:49:34 +00:00
break ;
}
case RandomFlickerLight :
{
2017-01-18 18:10:25 +00:00
int flickerRange = args [ LIGHT_SECONDARY_INTENSITY ] - args [ LIGHT_INTENSITY ] ;
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 ;
}
2017-01-18 18:10:25 +00:00
if ( m_tickCount + + = = 0 | | m_currentRadius > args [ LIGHT_SECONDARY_INTENSITY ] )
2016-12-06 17:35:34 +00:00
{
2017-01-18 18:10:25 +00:00
m_currentRadius = float ( args [ LIGHT_INTENSITY ] + ( 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 :
{
2017-01-18 18:10:25 +00:00
int flickerRange = args [ LIGHT_SECONDARY_INTENSITY ] - args [ LIGHT_INTENSITY ] ;
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
{
2017-01-18 18:10:25 +00:00
m_currentRadius = args [ LIGHT_INTENSITY ] + ( amt * flickerRange ) ;
2013-06-23 07:49:34 +00:00
m_tickCount = 0 ;
}
break ;
}
# endif
case SectorLight :
{
float intensity ;
float scale = args [ LIGHT_SCALE ] / 8.f ;
if ( scale = = 0.f ) scale = 1.f ;
intensity = Sector - > lightlevel * scale ;
2017-05-31 08:41:43 +00:00
intensity = clamp < float > ( intensity , 0.f , 1024.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 :
2017-01-18 18:10:25 +00:00
m_currentRadius = float ( args [ LIGHT_INTENSITY ] ) ;
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 ( ) ;
}
//==========================================================================
//
//
//
//==========================================================================
void ADynamicLight : : UpdateLocation ( )
{
2016-03-21 01:57:02 +00:00
double oldx = X ( ) ;
double oldy = Y ( ) ;
double oldradius = radius ;
2013-06-23 07:49:34 +00:00
float intensity ;
if ( IsActive ( ) )
{
if ( target )
{
2016-03-24 12:38:37 +00:00
DAngle angle = target - > Angles . Yaw ;
double s = angle . Sin ( ) ;
double c = angle . Cos ( ) ;
DVector3 pos = target - > Vec3Offset ( m_off . X * c + m_off . Y * s , m_off . X * s - m_off . Y * c , m_off . Z + target - > GetBobOffset ( ) ) ;
2016-01-21 11:36:37 +00:00
SetXYZ ( pos ) ; // attached lights do not need to go into the regular blockmap
2016-03-29 09:26:33 +00:00
Prev = target - > Pos ( ) ;
2016-03-30 18:01:44 +00:00
subsector = R_PointInSubsector ( Prev ) ;
2013-06-23 07:49:34 +00:00
Sector = subsector - > sector ;
2016-10-03 14:21:50 +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. ) 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. ) ;
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
2016-04-12 14:01:08 +00:00
if ( lighttype = = FlickerLight | | lighttype = = RandomFlickerLight | | lighttype = = PulseLight )
2013-06-23 07:49:34 +00:00
{
2017-01-18 18:10:25 +00:00
intensity = float ( MAX ( args [ LIGHT_INTENSITY ] , args [ LIGHT_SECONDARY_INTENSITY ] ) ) ;
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 ( ) ;
}
}
}
//==========================================================================
//
//
//
//==========================================================================
2016-02-02 10:58:00 +00:00
2016-04-03 10:54:47 +00:00
void ADynamicLight : : SetOrigin ( double x , double y , double z , bool moving )
2016-02-02 10:58:00 +00:00
{
Super : : SetOrigin ( x , y , z , moving ) ;
LinkLight ( ) ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-03-24 12:38:37 +00:00
void ADynamicLight : : SetOffset ( const DVector3 & pos )
2013-06-23 07:49:34 +00:00
{
2016-03-24 12:38:37 +00:00
m_off = pos ;
2013-06-23 07:49:34 +00:00
UpdateLocation ( ) ;
}
//==========================================================================
//
// The target pointer in dynamic lights should never be substituted unless
2018-11-05 23:13:23 +00:00
// notOld is nullptr (which indicates that the object was destroyed by force.)
2013-06-23 07:49:34 +00:00
//
//==========================================================================
size_t ADynamicLight : : PointerSubstitution ( DObject * old , DObject * notOld )
{
AActor * saved_target = target ;
size_t ret = Super : : PointerSubstitution ( old , notOld ) ;
2018-11-05 23:13:23 +00:00
if ( notOld ! = nullptr ) target = saved_target ;
2013-06-23 07:49:34 +00:00
return ret ;
}
//=============================================================================
//
// 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.
//
//=============================================================================
FLightNode * AddLightNode ( FLightNode * * thread , void * linkto , ADynamicLight * light , FLightNode * & nextnode )
{
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
//
//==========================================================================
2018-11-05 23:13:23 +00:00
double ADynamicLight : : DistToSeg ( const DVector3 & pos , FSectionLine * segment )
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:13:23 +00:00
double seg_dx = segment - > end - > fX ( ) - segment - > start - > fX ( ) ;
double seg_dy = segment - > end - > fY ( ) - segment - > 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:13:23 +00:00
u = ( ( ( pos . X - segment - > start - > fX ( ) ) * seg_dx ) + ( pos . Y - segment - > 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:13:23 +00:00
px = segment - > start - > fX ( ) + ( u * seg_dx ) ;
py = segment - > 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
2018-11-05 23:13:23 +00:00
void ADynamicLight : : 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: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
2018-11-05 23:13:23 +00:00
for ( auto & segment : section - > segments )
2013-06-23 07:49:34 +00:00
{
2016-12-26 09:39:03 +00:00
auto & pos = collected_ss [ i ] . pos ;
2016-12-08 11:49:40 +00:00
// 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.
2018-11-05 23:13:23 +00:00
if ( DistToSeg ( pos , & segment ) < = radius )
2013-06-23 07:49:34 +00:00
{
2018-11-05 23:13:23 +00:00
if ( segment . sidedef )
2016-04-08 10:38:09 +00:00
{
2018-11-05 23:13:23 +00:00
auto sidedef = segment . sidedef ;
auto linedef = sidedef - > linedef ;
if ( linedef & & linedef - > validcount ! = : : validcount )
2017-03-19 23:34:19 +00:00
{
2018-11-05 23:13:23 +00:00
// light is in front of the seg
if ( ( pos . Y - segment . start - > fY ( ) ) * ( segment . end - > fX ( ) - segment . start - > fX ( ) ) + ( segment . start - > fX ( ) - pos . X ) * ( segment . end - > fY ( ) - segment . start - > fY ( ) ) < = 0 )
{
linedef - > validcount = : : validcount ;
touching_sides = AddLightNode ( & sidedef - > lighthead , sidedef , this , touching_sides ) ;
}
else if ( linedef - > sidedef [ 0 ] = = sidedef & & linedef - > sidedef [ 1 ] = = nullptr )
{
hitonesidedback = true ;
}
2017-03-19 23:34:19 +00:00
}
2018-11-05 23:13:23 +00:00
if ( linedef )
2016-03-08 20:22:12 +00:00
{
2018-11-05 23:13:23 +00:00
FLinePortal * port = linedef - > getPortal ( ) ;
if ( port & & port - > mType = = PORTT_LINKED )
2016-12-08 11:49:40 +00:00
{
2018-11-05 23:13:23 +00:00
line_t * other = port - > mDestination ;
if ( other - > validcount ! = : : validcount )
2016-12-08 11:49:40 +00:00
{
2018-11-05 23:13:23 +00:00
subsector_t * othersub = R_PointInSubsector ( other - > v1 - > fPos ( ) + other - > Delta ( ) / 2 ) ;
FSection * othersect = othersub - > section ;
if ( othersect - > validcount ! = : : validcount )
{
othersect - > validcount = : : validcount ;
collected_ss . Push ( { othersect , PosRelative ( other ) } ) ;
}
2016-12-08 11:49:40 +00:00
}
}
2016-03-08 20:22:12 +00:00
}
}
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: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 ;
collected_ss . Push ( { othersect , PosRelative ( othersub - > sector ) } ) ;
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 ;
collected_ss . Push ( { othersect , PosRelative ( othersub - > sector ) } ) ;
2016-12-08 11:49:40 +00:00
}
}
2016-03-08 20:22:12 +00:00
}
}
2017-06-18 08:15:31 +00:00
shadowmapped = hitonesidedback & & ! ( lightflags & LF_NOSHADOWMAP ) ;
2013-06-23 07:49:34 +00:00
}
//==========================================================================
//
// Link the light into the world
//
//==========================================================================
void ADynamicLight : : LinkLight ( )
{
// 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
2018-11-05 23:13:23 +00:00
FSection * sect = R_PointInSubsector ( Pos ( ) ) - > section ;
dl_validcount + + ;
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
//
//==========================================================================
void ADynamicLight : : UnlinkLight ( )
{
2018-11-05 23:13:23 +00:00
if ( owned & & target ! = nullptr )
2013-06-23 07:49:34 +00:00
{
// Delete reference in owning actor
2017-03-12 15:56:00 +00:00
for ( int c = target - > AttachedLights . Size ( ) - 1 ; c > = 0 ; c - - )
2013-06-23 07:49:34 +00:00
{
2017-03-12 15:56:00 +00:00
if ( target - > AttachedLights [ c ] = = this )
2013-06-23 07:49:34 +00:00
{
2017-03-12 15:56:00 +00:00
target - > AttachedLights . Delete ( c ) ;
2013-06-23 07:49:34 +00:00
break ;
}
}
}
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
}
2017-01-12 21:49:18 +00:00
void ADynamicLight : : OnDestroy ( )
2013-06-23 07:49:34 +00:00
{
UnlinkLight ( ) ;
2017-01-12 21:49:18 +00:00
Super : : OnDestroy ( ) ;
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 )
{
ADynamicLight * light ;
if ( count < AttachedLights . Size ( ) )
{
light = barrier_cast < ADynamicLight * > ( AttachedLights [ count ] ) ;
2018-11-05 23:13:23 +00:00
assert ( light ! = nullptr ) ;
2018-04-02 10:28:20 +00:00
}
else
{
light = Spawn < ADynamicLight > ( Pos ( ) , NO_REPLACE ) ;
light - > target = this ;
light - > owned = true ;
light - > ObjectFlags | = OF_Transient ;
//light->lightflags |= LF_ATTENUATE;
AttachedLights . Push ( light ) ;
}
light - > flags2 & = ~ MF2_DORMANT ;
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 )
{
ADynamicLight * lights , * tmpLight ;
unsigned int i ;
2018-11-05 23:13:23 +00:00
lights = tmpLight = nullptr ;
2018-04-02 10:28:20 +00:00
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 + + )
{
AttachedLights [ count ] - > flags2 | = MF2_DORMANT ;
memset ( AttachedLights [ count ] - > args , 0 , 3 * sizeof ( args [ 0 ] ) ) ;
}
}
//==========================================================================
//
// Needed for garbage collection
//
//==========================================================================
size_t AActor : : PropagateMark ( )
{
for ( unsigned i = 0 ; i < AttachedLights . Size ( ) ; i + + )
{
GC : : Mark ( AttachedLights [ i ] ) ;
}
return Super : : PropagateMark ( ) ;
}
//==========================================================================
//
// This is called before saving the game
//
//==========================================================================
void AActor : : DeleteAllAttachedLights ( )
{
TThinkerIterator < AActor > it ;
AActor * a ;
ADynamicLight * l ;
while ( ( a = it . Next ( ) ) )
{
a - > AttachedLights . Clear ( ) ;
}
TThinkerIterator < ADynamicLight > it2 ;
l = it2 . Next ( ) ;
while ( l )
{
ADynamicLight * ll = it2 . Next ( ) ;
if ( l - > owned ) l - > Destroy ( ) ;
l = ll ;
}
}
//==========================================================================
//
//
//
//==========================================================================
void AActor : : RecreateAllAttachedLights ( )
{
TThinkerIterator < AActor > it ;
AActor * a ;
while ( ( a = it . Next ( ) ) )
{
a - > SetDynamicLights ( ) ;
}
}
//==========================================================================
//
// 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 ;
2013-06-23 07:49:34 +00:00
ADynamicLight * dl ;
TThinkerIterator < ADynamicLight > it ;
while ( ( dl = it . Next ( ) ) )
{
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 " ,
2013-06-23 07:49:34 +00:00
dl - > target ? dl - > target - > GetClass ( ) - > TypeName . GetChars ( ) : dl - > GetClass ( ) - > TypeName . GetChars ( ) ,
2016-03-21 01:57:02 +00:00
dl - > X ( ) , dl - > Y ( ) , dl - > Z ( ) , dl - > args [ LIGHT_RED ] ,
2017-06-18 08:15:31 +00:00
dl - > args [ LIGHT_GREEN ] , dl - > args [ LIGHT_BLUE ] , dl - > radius , ( dl - > lightflags & LF_ATTENUATE ) ? " 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 ) ;
2014-06-01 07:27:16 +00:00
Printf ( " , frame = %s " , TexMan [ spr ] - > Name . 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
}
2014-09-21 10:40:08 +00:00