mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-25 14:00:18 +01:00
Replaces our lighting system with CM's. (#21465)
Depends on #21458. Ports https://github.com/cmss13-devs/cmss13/pull/4229, with the original authors as: - https://github.com/tgstation/TerraGov-Marine-Corps/pull/1964 for the lighting controller (A-lexa) - https://github.com/tgstation/TerraGov-Marine-Corps/pull/4747 and https://github.com/tgstation/TerraGov-Marine-Corps/pull/7263 for the lighting (TiviPlus) - https://github.com/tgstation/tgstation/pull/54520 for the dir lighting component - https://github.com/tgstation/tgstation/pull/75018 for the out of bounds fix in lighting - https://github.com/tgstation/TerraGov-Marine-Corps/pull/6678 for the emissives (TiviPlus) The main driving reason behind this is that current lighting consumes way too much processing power, especially for things like odysseys/away sites where a billion light sources are processing/moving at once and the game slows down to a crawl. Hopefully this improves the situation by a good margin, but we will need some testmerging to confirm that. <img width="1349" height="1349" alt="image" src="https://github.com/user-attachments/assets/1059ba2b-c0c5-495a-9c76-2d75d0c42bf2" /> <img width="1349" height="1349" alt="image" src="https://github.com/user-attachments/assets/9704b0f6-4cf6-4dfd-a6cb-5702ad07d677" /> - [x] Resolve todos - [x] Look into open space fuckery (border objects) --------- Co-authored-by: Matt Atlas <liermattia@gmail.com> Co-authored-by: JohnWildkins <john.wildkins@gmail.com>
This commit is contained in:
co-authored by
Matt Atlas
JohnWildkins
parent
8ebd7c9ad5
commit
94d92803b4
@@ -0,0 +1,12 @@
|
||||
/area
|
||||
///Whether this area allows static lighting and thus loads the lighting objects
|
||||
var/static_lighting = TRUE
|
||||
|
||||
//Non static lighting areas.
|
||||
//Any lighting area that wont support static lights.
|
||||
//These areas will NOT have corners generated.
|
||||
|
||||
/area/space
|
||||
static_lighting = FALSE
|
||||
base_lighting_alpha = 255
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
/atom
|
||||
///The light source, datum. Dont fuck with this directly
|
||||
var/tmp/datum/static_light_source/static_light
|
||||
///Static light sources currently attached to this atom, this includes ones owned by atoms inside this atom
|
||||
var/tmp/list/static_light_sources
|
||||
|
||||
///Pretty simple, just updates static lights on this atom
|
||||
/atom/proc/static_update_light()
|
||||
set waitfor = FALSE
|
||||
if (QDELETED(src))
|
||||
return
|
||||
|
||||
if (!light_power || !light_range) // We won't emit light anyways, destroy the light source.
|
||||
QDEL_NULL(static_light)
|
||||
else
|
||||
if(!ismovableatom(loc)) // We choose what atom should be the top atom of the light here.
|
||||
. = src
|
||||
else
|
||||
. = loc
|
||||
|
||||
if(static_light) // Update the light or create it if it does not exist.
|
||||
static_light.update(.)
|
||||
else
|
||||
static_light = new/datum/static_light_source(src, .)
|
||||
@@ -0,0 +1,176 @@
|
||||
// Because we can control each corner of every lighting object.
|
||||
// And corners get shared between multiple turfs (unless you're on the corners of the map, then 1 corner doesn't).
|
||||
// For the record: these should never ever ever be deleted, even if the turf doesn't have dynamic lighting.
|
||||
|
||||
/datum/static_lighting_corner
|
||||
var/list/datum/static_light_source/affecting // Light sources affecting us.
|
||||
|
||||
var/x = 0
|
||||
var/y = 0
|
||||
|
||||
var/turf/master_NE
|
||||
var/turf/master_SE
|
||||
var/turf/master_SW
|
||||
var/turf/master_NW
|
||||
|
||||
var/lum_r = 0
|
||||
var/lum_g = 0
|
||||
var/lum_b = 0
|
||||
|
||||
//true color values, guaranteed to be between 0 and 1
|
||||
var/cache_r = LIGHTING_SOFT_THRESHOLD
|
||||
var/cache_g = LIGHTING_SOFT_THRESHOLD
|
||||
var/cache_b = LIGHTING_SOFT_THRESHOLD
|
||||
|
||||
///the maximum of lum_r, lum_g, and lum_b. if this is > 1 then the three cached color values are divided by this
|
||||
var/largest_color_luminosity = 0
|
||||
|
||||
///whether we are to be added to SSlighting's corners_queue list for an update
|
||||
var/needs_update = FALSE
|
||||
|
||||
/datum/static_lighting_corner/New(turf/new_turf, diagonal)
|
||||
. = ..()
|
||||
save_master(new_turf, turn(diagonal, 180))
|
||||
|
||||
var/vertical = diagonal & ~(diagonal - 1) // The horizontal directions (4 and 8) are bigger than the vertical ones (1 and 2), so we can reliably say the lsb is the horizontal direction.
|
||||
var/horizontal = diagonal & ~vertical // Now that we know the horizontal one we can get the vertical one.
|
||||
|
||||
x = new_turf.x + (horizontal == EAST ? 0.5 : -0.5)
|
||||
y = new_turf.y + (vertical == NORTH ? 0.5 : -0.5)
|
||||
|
||||
// My initial plan was to make this loop through a list of all the dirs (horizontal, vertical, diagonal).
|
||||
// Issue being that the only way I could think of doing it was very messy, slow and honestly overengineered.
|
||||
// So we'll have this hardcode instead.
|
||||
var/turf/new_master_turf
|
||||
|
||||
// Diagonal one is easy.
|
||||
new_master_turf = get_step(new_turf, diagonal)
|
||||
if(new_master_turf) // In case we're on the map's border.
|
||||
save_master(new_master_turf, diagonal)
|
||||
|
||||
// Now the horizontal one.
|
||||
new_master_turf = get_step(new_turf, horizontal)
|
||||
if(new_master_turf) // Ditto.
|
||||
save_master(new_master_turf, ((new_master_turf.x > x) ? EAST : WEST) | ((new_master_turf.y > y) ? NORTH : SOUTH)) // Get the dir based on coordinates.
|
||||
|
||||
// And finally the vertical one.
|
||||
new_master_turf = get_step(new_turf, vertical)
|
||||
if (new_master_turf)
|
||||
save_master(new_master_turf, ((new_master_turf.x > x) ? EAST : WEST) | ((new_master_turf.y > y) ? NORTH : SOUTH)) // Get the dir based on coordinates.
|
||||
|
||||
/datum/static_lighting_corner/proc/save_master(turf/master, dir)
|
||||
switch (dir)
|
||||
if (NORTHEAST)
|
||||
master_NE = master
|
||||
master.lighting_corner_SW = src
|
||||
if (SOUTHEAST)
|
||||
master_SE = master
|
||||
master.lighting_corner_NW = src
|
||||
if (SOUTHWEST)
|
||||
master_SW = master
|
||||
master.lighting_corner_NE = src
|
||||
if (NORTHWEST)
|
||||
master_NW = master
|
||||
master.lighting_corner_SE = src
|
||||
|
||||
/datum/static_lighting_corner/proc/self_destruct_if_idle()
|
||||
if (!LAZYLEN(affecting))
|
||||
qdel(src, force = TRUE)
|
||||
|
||||
/datum/static_lighting_corner/proc/vis_update()
|
||||
for (var/datum/static_light_source/light_source as anything in affecting)
|
||||
light_source.vis_update()
|
||||
|
||||
/datum/static_lighting_corner/proc/full_update()
|
||||
for (var/datum/static_light_source/light_source as anything in affecting)
|
||||
light_source.recalc_corner(src)
|
||||
|
||||
// God that was a mess, now to do the rest of the corner code! Hooray!
|
||||
/datum/static_lighting_corner/proc/update_lumcount(delta_r, delta_g, delta_b)
|
||||
|
||||
if(!(delta_r || delta_g || delta_b)) // 0 is falsey ok
|
||||
return
|
||||
|
||||
lum_r += delta_r
|
||||
lum_g += delta_g
|
||||
lum_b += delta_b
|
||||
|
||||
if(!needs_update)
|
||||
needs_update = TRUE
|
||||
SSlighting.corners_queue += src
|
||||
|
||||
/datum/static_lighting_corner/proc/update_objects()
|
||||
// Cache these values a head of time so 4 individual lighting objects don't all calculate them individually.
|
||||
var/lum_r = src.lum_r
|
||||
var/lum_g = src.lum_g
|
||||
var/lum_b = src.lum_b
|
||||
var/largest_color_luminosity = max(lum_r, lum_g, lum_b) // Scale it so one of them is the strongest lum, if it is above 1.
|
||||
. = 1 // factor
|
||||
if (largest_color_luminosity > 1)
|
||||
. = 1 / largest_color_luminosity
|
||||
|
||||
#if LIGHTING_SOFT_THRESHOLD != 0
|
||||
else if (largest_color_luminosity < LIGHTING_SOFT_THRESHOLD)
|
||||
. = 0 // 0 means soft lighting.
|
||||
|
||||
cache_r = round(lum_r * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD
|
||||
cache_g = round(lum_g * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD
|
||||
cache_b = round(lum_b * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD
|
||||
#else
|
||||
cache_r = round(lum_r * ., LIGHTING_ROUND_VALUE)
|
||||
cache_g = round(lum_g * ., LIGHTING_ROUND_VALUE)
|
||||
cache_b = round(lum_b * ., LIGHTING_ROUND_VALUE)
|
||||
#endif
|
||||
|
||||
src.largest_color_luminosity = round(largest_color_luminosity, LIGHTING_ROUND_VALUE)
|
||||
|
||||
var/datum/static_lighting_object/lighting_object = master_NE?.static_lighting_object
|
||||
if (lighting_object && !lighting_object.needs_update)
|
||||
lighting_object.needs_update = TRUE
|
||||
SSlighting.objects_queue += lighting_object
|
||||
|
||||
lighting_object = master_SE?.static_lighting_object
|
||||
if (lighting_object && !lighting_object.needs_update)
|
||||
lighting_object.needs_update = TRUE
|
||||
SSlighting.objects_queue += lighting_object
|
||||
|
||||
lighting_object = master_SW?.static_lighting_object
|
||||
if (lighting_object && !lighting_object.needs_update)
|
||||
lighting_object.needs_update = TRUE
|
||||
SSlighting.objects_queue += lighting_object
|
||||
|
||||
lighting_object = master_NW?.static_lighting_object
|
||||
if (lighting_object && !lighting_object.needs_update)
|
||||
lighting_object.needs_update = TRUE
|
||||
SSlighting.objects_queue += lighting_object
|
||||
|
||||
self_destruct_if_idle()
|
||||
|
||||
|
||||
/datum/static_lighting_corner/dummy/New()
|
||||
return
|
||||
|
||||
|
||||
/datum/static_lighting_corner/Destroy(force)
|
||||
if (!force)
|
||||
return QDEL_HINT_LETMELIVE
|
||||
|
||||
for(var/datum/static_light_source/light_source as anything in affecting)
|
||||
LAZYREMOVE(light_source.effect_str, src)
|
||||
affecting = null
|
||||
|
||||
if(master_NE)
|
||||
master_NE.lighting_corner_SW = null
|
||||
master_NE.lighting_corners_initialised = FALSE
|
||||
if(master_SE)
|
||||
master_SE.lighting_corner_NW = null
|
||||
master_SE.lighting_corners_initialised = FALSE
|
||||
if(master_SW)
|
||||
master_SW.lighting_corner_NE = null
|
||||
master_SW.lighting_corners_initialised = FALSE
|
||||
if(master_NW)
|
||||
master_NW.lighting_corner_SE = null
|
||||
master_NW.lighting_corners_initialised = FALSE
|
||||
if(needs_update)
|
||||
SSlighting.corners_queue -= src
|
||||
return ..()
|
||||
@@ -0,0 +1,115 @@
|
||||
/datum/static_lighting_object
|
||||
///the underlay we are currently applying to our turf to apply light
|
||||
var/mutable_appearance/current_underlay
|
||||
|
||||
///whether we are already in the SSlighting.objects_queue list
|
||||
var/needs_update = FALSE
|
||||
|
||||
///the turf that our light is applied to
|
||||
var/turf/affected_turf
|
||||
|
||||
/datum/static_lighting_object/New(turf/source)
|
||||
if(!isturf(source))
|
||||
qdel(src, force=TRUE)
|
||||
stack_trace("a lighting object was assigned to [source], a non turf! ")
|
||||
return
|
||||
..()
|
||||
|
||||
current_underlay = mutable_appearance(LIGHTING_ICON, "transparent", FLOAT_LAYER, LIGHTING_PLANE, 255, RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM)
|
||||
|
||||
affected_turf = source
|
||||
if (affected_turf.static_lighting_object)
|
||||
qdel(affected_turf.static_lighting_object, force = TRUE)
|
||||
stack_trace("a lighting object was assigned to a turf that already had a lighting object!")
|
||||
|
||||
affected_turf.static_lighting_object = src
|
||||
affected_turf.luminosity = 0
|
||||
|
||||
needs_update = TRUE
|
||||
SSlighting.objects_queue += src
|
||||
|
||||
/datum/static_lighting_object/Destroy(force)
|
||||
if (!force)
|
||||
return QDEL_HINT_LETMELIVE
|
||||
SSlighting.objects_queue -= src
|
||||
if (isturf(affected_turf))
|
||||
affected_turf.static_lighting_object = null
|
||||
affected_turf.luminosity = 1
|
||||
affected_turf.underlays -= current_underlay
|
||||
affected_turf = null
|
||||
return ..()
|
||||
|
||||
/datum/static_lighting_object/proc/update()
|
||||
|
||||
// To the future coder who sees this and thinks
|
||||
// "Why didn't he just use a loop?"
|
||||
// Well my man, it's because the loop performed like shit.
|
||||
// And there's no way to improve it because
|
||||
// without a loop you can make the list all at once which is the fastest you're gonna get.
|
||||
// Oh it's also shorter line wise.
|
||||
// Including with these comments.
|
||||
|
||||
var/static/datum/static_lighting_corner/dummy/dummy_lighting_corner = new
|
||||
|
||||
var/datum/static_lighting_corner/red_corner = affected_turf.lighting_corner_SW || dummy_lighting_corner
|
||||
var/datum/static_lighting_corner/green_corner = affected_turf.lighting_corner_SE || dummy_lighting_corner
|
||||
var/datum/static_lighting_corner/blue_corner = affected_turf.lighting_corner_NW || dummy_lighting_corner
|
||||
var/datum/static_lighting_corner/alpha_corner = affected_turf.lighting_corner_NE || dummy_lighting_corner
|
||||
|
||||
var/max = max(red_corner.largest_color_luminosity, green_corner.largest_color_luminosity, blue_corner.largest_color_luminosity, alpha_corner.largest_color_luminosity)
|
||||
|
||||
var/rr = red_corner.cache_r
|
||||
var/rg = red_corner.cache_g
|
||||
var/rb = red_corner.cache_b
|
||||
|
||||
var/gr = green_corner.cache_r
|
||||
var/gg = green_corner.cache_g
|
||||
var/gb = green_corner.cache_b
|
||||
|
||||
var/br = blue_corner.cache_r
|
||||
var/bg = blue_corner.cache_g
|
||||
var/bb = blue_corner.cache_b
|
||||
|
||||
var/ar = alpha_corner.cache_r
|
||||
var/ag = alpha_corner.cache_g
|
||||
var/ab = alpha_corner.cache_b
|
||||
|
||||
#if LIGHTING_SOFT_THRESHOLD != 0
|
||||
var/set_luminosity = max > LIGHTING_SOFT_THRESHOLD
|
||||
#else
|
||||
// Because of floating points�?, it won't even be a flat 0.
|
||||
// This number is mostly arbitrary.
|
||||
var/set_luminosity = max > 1e-6
|
||||
#endif
|
||||
|
||||
if((rr & gr & br & ar) && (rg + gg + bg + ag + rb + gb + bb + ab == 8))
|
||||
//anything that passes the first case is very likely to pass the second, and addition is a little faster in this case
|
||||
affected_turf.underlays -= current_underlay
|
||||
current_underlay.icon_state = "lighting_transparent"
|
||||
current_underlay.color = null
|
||||
affected_turf.underlays += current_underlay
|
||||
else if(!set_luminosity)
|
||||
affected_turf.underlays -= current_underlay
|
||||
current_underlay.icon_state = "lighting_dark"
|
||||
current_underlay.color = null
|
||||
affected_turf.underlays += current_underlay
|
||||
else
|
||||
affected_turf.underlays -= current_underlay
|
||||
current_underlay.icon_state = null
|
||||
current_underlay.color = list(
|
||||
rr, rg, rb, 00,
|
||||
gr, gg, gb, 00,
|
||||
br, bg, bb, 00,
|
||||
ar, ag, ab, 00,
|
||||
00, 00, 00, 01
|
||||
)
|
||||
|
||||
affected_turf.underlays += current_underlay
|
||||
|
||||
var/area/A = affected_turf.loc
|
||||
//We are luminous
|
||||
if(set_luminosity)
|
||||
affected_turf.luminosity = set_luminosity
|
||||
//We are not lit by static light OR dynamic light.
|
||||
else if(!LAZYLEN(affected_turf.hybrid_lights_affecting) && !A.base_lighting_alpha)
|
||||
affected_turf.luminosity = 0
|
||||
@@ -0,0 +1,10 @@
|
||||
/proc/create_all_lighting_objects()
|
||||
for(var/area/A in world)
|
||||
|
||||
if(!A.static_lighting)
|
||||
continue
|
||||
|
||||
for(var/turf/T in A)
|
||||
new/datum/static_lighting_object(T)
|
||||
CHECK_TICK
|
||||
CHECK_TICK
|
||||
@@ -0,0 +1,306 @@
|
||||
// This is where the fun begins.
|
||||
// These are the main datums that emit light.
|
||||
|
||||
/datum/static_light_source
|
||||
///The atom we're emitting light from (for example a mob if we're from a flashlight that's being held).
|
||||
var/atom/top_atom
|
||||
///The atom that we belong to.
|
||||
var/atom/source_atom
|
||||
|
||||
///The turf under the source atom.
|
||||
var/turf/source_turf
|
||||
///The turf the top_atom appears to over.
|
||||
var/turf/pixel_turf
|
||||
///Intensity of the emitter light.
|
||||
var/light_power
|
||||
/// The range of the emitted light.
|
||||
var/light_range
|
||||
/// The colour of the light, string, decomposed by parse_light_color()
|
||||
var/light_color
|
||||
// Variables for keeping track of the colour.
|
||||
var/lum_r
|
||||
var/lum_g
|
||||
var/lum_b
|
||||
|
||||
// The lumcount values used to apply the light.
|
||||
var/tmp/applied_lum_r
|
||||
var/tmp/applied_lum_g
|
||||
var/tmp/applied_lum_b
|
||||
|
||||
/// List used to store how much we're affecting corners.
|
||||
var/list/datum/static_lighting_corner/effect_str
|
||||
|
||||
/// Whether we have applied our light yet or not.
|
||||
var/applied = FALSE
|
||||
|
||||
/// whether we are to be added to SSlighting's static_sources_queue list for an update
|
||||
var/needs_update = LIGHTING_NO_UPDATE
|
||||
|
||||
// Thanks to Lohikar for flinging this tiny bit of code at me, increasing my brain cell count from 1 to 2 in the process.
|
||||
// This macro will only offset up to 1 tile, but anything with a greater offset is an outlier and probably should handle its own lighting offsets.
|
||||
// Anything pixelshifted 16px or more will be considered on the next tile.
|
||||
#define GET_APPROXIMATE_PIXEL_DIR(PX, PY) ((!(PX) ? 0 : ((PX >= 16 ? EAST : (PX <= -16 ? WEST : 0)))) | (!PY ? 0 : (PY >= 16 ? NORTH : (PY <= -16 ? SOUTH : 0))))
|
||||
#define UPDATE_APPROXIMATE_PIXEL_TURF var/_mask = GET_APPROXIMATE_PIXEL_DIR(top_atom.pixel_x, top_atom.pixel_y); pixel_turf = _mask ? (get_step(source_turf, _mask) || source_turf) : source_turf
|
||||
|
||||
/datum/static_light_source/New(atom/owner, atom/top)
|
||||
source_atom = owner // Set our new owner.
|
||||
LAZYADD(source_atom.static_light_sources, src)
|
||||
top_atom = top
|
||||
if (top_atom != source_atom)
|
||||
LAZYADD(top_atom.static_light_sources, src)
|
||||
|
||||
source_turf = top_atom
|
||||
UPDATE_APPROXIMATE_PIXEL_TURF
|
||||
|
||||
light_power = source_atom.light_power
|
||||
light_range = source_atom.light_range
|
||||
light_color = source_atom.light_color
|
||||
|
||||
PARSE_LIGHT_COLOR(src)
|
||||
|
||||
update()
|
||||
|
||||
/datum/static_light_source/Destroy(force)
|
||||
remove_lum()
|
||||
if (source_atom)
|
||||
LAZYREMOVE(source_atom.static_light_sources, src)
|
||||
|
||||
if (top_atom)
|
||||
LAZYREMOVE(top_atom.static_light_sources, src)
|
||||
|
||||
if (needs_update)
|
||||
SSlighting.static_sources_queue -= src
|
||||
return ..()
|
||||
|
||||
#define EFFECT_UPDATE(level) \
|
||||
if (needs_update == LIGHTING_NO_UPDATE) \
|
||||
SSlighting.static_sources_queue += src; \
|
||||
if (needs_update < level) \
|
||||
needs_update = level; \
|
||||
|
||||
|
||||
/// This proc will cause the light source to update the top atom, and add itself to the update queue.
|
||||
/datum/static_light_source/proc/update(atom/new_top_atom)
|
||||
// This top atom is different.
|
||||
if (new_top_atom && new_top_atom != top_atom)
|
||||
if(top_atom != source_atom && top_atom.static_light_sources) // Remove ourselves from the light sources of that top atom.
|
||||
LAZYREMOVE(top_atom.static_light_sources, src)
|
||||
|
||||
top_atom = new_top_atom
|
||||
|
||||
if (top_atom != source_atom)
|
||||
LAZYADD(top_atom.static_light_sources, src) // Add ourselves to the light sources of our new top atom.
|
||||
|
||||
EFFECT_UPDATE(LIGHTING_CHECK_UPDATE)
|
||||
|
||||
/// Will force an update without checking if it's actually needed.
|
||||
/datum/static_light_source/proc/force_update()
|
||||
EFFECT_UPDATE(LIGHTING_FORCE_UPDATE)
|
||||
|
||||
/// Will cause the light source to recalculate turfs that were removed or added to visibility only.
|
||||
/datum/static_light_source/proc/vis_update()
|
||||
EFFECT_UPDATE(LIGHTING_VIS_UPDATE)
|
||||
|
||||
// Macro that applies light to a new corner.
|
||||
// It is a macro in the interest of speed, yet not having to copy paste it.
|
||||
// If you're wondering what's with the backslashes, the backslashes cause BYOND to not automatically end the line.
|
||||
// As such this all gets counted as a single line.
|
||||
// The braces and semicolons are there to be able to do this on a single line.
|
||||
|
||||
//Original lighting falloff calculation. This looks the best out of the three. However, this is also the most expensive.
|
||||
//#define LUM_FALLOFF(C, T) (1 - CLAMP01(sqrt((C.x - T.x) ** 2 + (C.y - T.y) ** 2 + LIGHTING_HEIGHT) / max(1, light_range)))
|
||||
|
||||
//Cubic lighting falloff. This has the *exact* same range as the original lighting falloff calculation, down to the exact decimal, but it looks a little unnatural due to the harsher falloff and how it's generally brighter across the board.
|
||||
//#define LUM_FALLOFF(C, T) (1 - CLAMP01((((C.x - T.x) * (C.x - T.x)) + ((C.y - T.y) * (C.y - T.y)) + LIGHTING_HEIGHT) / max(1, light_range*light_range)))
|
||||
|
||||
//Linear lighting falloff. This resembles the original lighting falloff calculation the best, but results in lights having a slightly larger range, which is most noticeable with large light sources. This also results in lights being diamond-shaped, fuck. This looks the darkest out of the three due to how lights are brighter closer to the source compared to the original falloff algorithm. This falloff method also does not at all take into account lighting height, as it acts as a flat reduction to light range with this method.
|
||||
//#define LUM_FALLOFF(C, T) (1 - CLAMP01(((abs(C.x - T.x) + abs(C.y - T.y))) / max(1, light_range+1)))
|
||||
|
||||
//Linear lighting falloff but with an octagonal shape in place of a diamond shape. Lummox JR please add pointer support.
|
||||
#define GET_LUM_DIST(DISTX, DISTY) (DISTX + DISTY + abs(DISTX - DISTY)*0.4)
|
||||
#define LUM_FALLOFF(C, T) (1 - CLAMP01(max(GET_LUM_DIST(abs(C.x - T.x), abs(C.y - T.y)),LIGHTING_HEIGHT) / max(1, light_range+1)))
|
||||
|
||||
#define APPLY_CORNER(C) \
|
||||
. = LUM_FALLOFF(C, pixel_turf); \
|
||||
. *= light_power; \
|
||||
var/OLD = effect_str[C]; \
|
||||
C.update_lumcount \
|
||||
( \
|
||||
(. * lum_r) - (OLD * applied_lum_r), \
|
||||
(. * lum_g) - (OLD * applied_lum_g), \
|
||||
(. * lum_b) - (OLD * applied_lum_b) \
|
||||
);
|
||||
|
||||
#define REMOVE_CORNER(C) \
|
||||
. = -effect_str[C]; \
|
||||
C.update_lumcount \
|
||||
( \
|
||||
. * applied_lum_r, \
|
||||
. * applied_lum_g, \
|
||||
. * applied_lum_b \
|
||||
);
|
||||
|
||||
/// This is the define used to calculate falloff.
|
||||
/datum/static_light_source/proc/remove_lum()
|
||||
applied = FALSE
|
||||
for(var/datum/static_lighting_corner/corner as anything in effect_str)
|
||||
REMOVE_CORNER(corner)
|
||||
LAZYREMOVE(corner.affecting, src)
|
||||
|
||||
effect_str = null
|
||||
|
||||
/datum/static_light_source/proc/recalc_corner(datum/static_lighting_corner/corner)
|
||||
LAZYINITLIST(effect_str)
|
||||
if (effect_str[corner]) // Already have one.
|
||||
REMOVE_CORNER(corner)
|
||||
effect_str[corner] = 0
|
||||
|
||||
APPLY_CORNER(corner)
|
||||
effect_str[corner] = .
|
||||
|
||||
/datum/static_light_source/proc/update_corners()
|
||||
var/update = FALSE
|
||||
var/atom/source_atom = src.source_atom
|
||||
|
||||
if (QDELETED(source_atom))
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
if (source_atom.light_power != light_power)
|
||||
light_power = source_atom.light_power
|
||||
update = TRUE
|
||||
|
||||
if (source_atom.light_range != light_range)
|
||||
light_range = source_atom.light_range
|
||||
update = TRUE
|
||||
|
||||
if (!top_atom)
|
||||
top_atom = source_atom
|
||||
update = TRUE
|
||||
|
||||
if (!light_range || !light_power)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
if (isturf(top_atom))
|
||||
if (source_turf != top_atom)
|
||||
source_turf = top_atom
|
||||
UPDATE_APPROXIMATE_PIXEL_TURF
|
||||
update = TRUE
|
||||
else if (top_atom.loc != source_turf)
|
||||
source_turf = top_atom.loc
|
||||
UPDATE_APPROXIMATE_PIXEL_TURF
|
||||
update = TRUE
|
||||
else
|
||||
var/pixel_loc = get_turf_pixel(top_atom)
|
||||
if (pixel_loc != pixel_turf)
|
||||
pixel_turf = pixel_loc
|
||||
update = TRUE
|
||||
|
||||
if (!isturf(source_turf))
|
||||
if (applied)
|
||||
remove_lum()
|
||||
return
|
||||
|
||||
if (light_range && light_power && !applied)
|
||||
update = TRUE
|
||||
|
||||
if (source_atom.light_color != light_color)
|
||||
light_color = source_atom.light_color
|
||||
PARSE_LIGHT_COLOR(src)
|
||||
update = TRUE
|
||||
|
||||
else if (applied_lum_r != lum_r || applied_lum_g != lum_g || applied_lum_b != lum_b)
|
||||
update = TRUE
|
||||
|
||||
if (update)
|
||||
needs_update = LIGHTING_CHECK_UPDATE
|
||||
applied = TRUE
|
||||
else if (needs_update == LIGHTING_CHECK_UPDATE)
|
||||
return //nothing's changed
|
||||
|
||||
var/list/datum/static_lighting_corner/corners = list()
|
||||
var/list/turf/turfs = list()
|
||||
if (source_turf)
|
||||
var/oldlum = source_turf.luminosity
|
||||
source_turf.luminosity = ceil(light_range)
|
||||
for(var/turf/T in view(ceil(light_range), source_turf))
|
||||
if(!IS_OPAQUE_TURF(T))
|
||||
if (!T.lighting_corners_initialised)
|
||||
T.static_generate_missing_corners()
|
||||
corners[T.lighting_corner_NE] = 0
|
||||
corners[T.lighting_corner_SE] = 0
|
||||
corners[T.lighting_corner_SW] = 0
|
||||
corners[T.lighting_corner_NW] = 0
|
||||
turfs += T
|
||||
|
||||
var/turf/above = GET_TURF_ABOVE(T)
|
||||
|
||||
while(above && istransparentturf(above))
|
||||
if (!above.lighting_corners_initialised)
|
||||
above.static_generate_missing_corners()
|
||||
corners[above.lighting_corner_NE] = 0
|
||||
corners[above.lighting_corner_SE] = 0
|
||||
corners[above.lighting_corner_SW] = 0
|
||||
corners[above.lighting_corner_NW] = 0
|
||||
|
||||
above = GET_TURF_ABOVE(above)
|
||||
|
||||
turfs += above
|
||||
|
||||
var/turf/below = GET_TURF_BELOW(T)
|
||||
var/turf/previous = T
|
||||
|
||||
while(below && istransparentturf(previous))
|
||||
if (!below.lighting_corners_initialised)
|
||||
below.static_generate_missing_corners()
|
||||
corners[below.lighting_corner_NE] = 0
|
||||
corners[below.lighting_corner_SE] = 0
|
||||
corners[below.lighting_corner_SW] = 0
|
||||
corners[below.lighting_corner_NW] = 0
|
||||
|
||||
previous = below
|
||||
below = GET_TURF_BELOW(below)
|
||||
|
||||
source_turf.luminosity = oldlum
|
||||
|
||||
var/list/datum/static_lighting_corner/new_corners = (corners - effect_str)
|
||||
LAZYINITLIST(effect_str)
|
||||
if (needs_update == LIGHTING_VIS_UPDATE)
|
||||
for (var/datum/static_lighting_corner/corner as anything in new_corners)
|
||||
APPLY_CORNER(corner)
|
||||
if (. != 0)
|
||||
LAZYADD(corner.affecting, src)
|
||||
effect_str[corner] = .
|
||||
else
|
||||
for (var/datum/static_lighting_corner/corner as anything in new_corners)
|
||||
APPLY_CORNER(corner)
|
||||
if (. != 0)
|
||||
LAZYADD(corner.affecting, src)
|
||||
effect_str[corner] = .
|
||||
|
||||
for (var/datum/static_lighting_corner/corner as anything in corners - new_corners) // Existing corners
|
||||
APPLY_CORNER(corner)
|
||||
if (. != 0)
|
||||
effect_str[corner] = .
|
||||
else
|
||||
LAZYREMOVE(corner.affecting, src)
|
||||
effect_str -= corner
|
||||
|
||||
var/list/datum/static_lighting_corner/gone_corners = effect_str - corners
|
||||
for (var/datum/static_lighting_corner/corner as anything in gone_corners)
|
||||
REMOVE_CORNER(corner)
|
||||
LAZYREMOVE(corner.affecting, src)
|
||||
effect_str -= gone_corners
|
||||
|
||||
applied_lum_r = lum_r
|
||||
applied_lum_g = lum_g
|
||||
applied_lum_b = lum_b
|
||||
|
||||
UNSETEMPTY(effect_str)
|
||||
|
||||
#undef EFFECT_UPDATE
|
||||
#undef LUM_FALLOFF
|
||||
#undef GET_LUM_DIST
|
||||
#undef REMOVE_CORNER
|
||||
#undef APPLY_CORNER
|
||||
@@ -0,0 +1,64 @@
|
||||
/turf
|
||||
var/tmp/lighting_corners_initialised = FALSE
|
||||
var/tmp/datum/static_lighting_object/static_lighting_object
|
||||
|
||||
///Lighting Corner datums.
|
||||
var/tmp/datum/static_lighting_corner/lighting_corner_NE
|
||||
var/tmp/datum/static_lighting_corner/lighting_corner_SE
|
||||
var/tmp/datum/static_lighting_corner/lighting_corner_SW
|
||||
var/tmp/datum/static_lighting_corner/lighting_corner_NW
|
||||
|
||||
/turf/proc/static_lighting_clear_overlay()
|
||||
if (static_lighting_object)
|
||||
qdel(static_lighting_object, TRUE)
|
||||
|
||||
/// Builds a lighting object for us, but only if our area is dynamic.
|
||||
/turf/proc/static_lighting_build_overlay(area/our_area = loc)
|
||||
if(static_lighting_object)
|
||||
qdel(static_lighting_object, force=TRUE) //Shitty fix for lighting objects persisting after death
|
||||
|
||||
new/datum/static_lighting_object(src)
|
||||
|
||||
|
||||
|
||||
// Returns a boolean whether the turf is on soft lighting.
|
||||
// Soft lighting being the threshold at which point the overlay considers
|
||||
// itself as too dark to allow sight and see_in_dark becomes useful.
|
||||
// So basically if this returns true the tile is unlit black.
|
||||
/turf/proc/static_is_softly_lit()
|
||||
if (!static_lighting_object)
|
||||
return FALSE
|
||||
|
||||
return !(luminosity || dynamic_lumcount)
|
||||
|
||||
///Transfer the lighting of one area to another
|
||||
/turf/proc/transfer_area_lighting(area/old_area, area/new_area)
|
||||
if(SSlighting.initialized)
|
||||
if (new_area.static_lighting != old_area.static_lighting)
|
||||
if (new_area.static_lighting)
|
||||
static_lighting_build_overlay(new_area)
|
||||
else
|
||||
static_lighting_clear_overlay()
|
||||
//Inherit overlay of new area
|
||||
if(old_area.lighting_effect)
|
||||
overlays -= old_area.lighting_effect
|
||||
if(new_area.lighting_effect)
|
||||
overlays += new_area.lighting_effect
|
||||
|
||||
|
||||
|
||||
/turf/proc/static_generate_missing_corners()
|
||||
if (!lighting_corner_NE)
|
||||
lighting_corner_NE = new/datum/static_lighting_corner(src, NORTH|EAST)
|
||||
|
||||
if (!lighting_corner_SE)
|
||||
lighting_corner_SE = new/datum/static_lighting_corner(src, SOUTH|EAST)
|
||||
|
||||
if (!lighting_corner_SW)
|
||||
lighting_corner_SW = new/datum/static_lighting_corner(src, SOUTH|WEST)
|
||||
|
||||
if (!lighting_corner_NW)
|
||||
lighting_corner_NW = new/datum/static_lighting_corner(src, NORTH|WEST)
|
||||
|
||||
lighting_corners_initialised = TRUE
|
||||
|
||||
Reference in New Issue
Block a user