Ambient Occlusion (#3419)

Adds an implementation of Europa's wall ambient occlusion, extended to operate on openturfs as well.
This commit is contained in:
Lohikar
2017-09-15 09:49:53 -05:00
committed by GitHub
parent 86220edeb3
commit fe8d586b31
24 changed files with 351 additions and 43 deletions

View File

@@ -0,0 +1,62 @@
// Openturfs need special snowflake CAO and UA behavior - they're shadowing based on turf type instead of ao opacity,
// as well as applying the overlays to the openspace shadower object instead of the turf itself.
/turf/simulated/open/calculate_ao_neighbors()
ao_neighbors = 0
var/turf/T
for (var/tdir in cardinal)
T = get_step(src, tdir)
if (isopenturf(T))
ao_neighbors |= 1 << tdir
if (ao_neighbors & N_NORTH)
if (ao_neighbors & N_WEST)
T = get_step(src, NORTHWEST)
if (isopenturf(T))
ao_neighbors |= N_NORTHWEST
if (ao_neighbors & N_EAST)
T = get_step(src, NORTHEAST)
if (isopenturf(T))
ao_neighbors |= N_NORTHEAST
if (ao_neighbors & N_SOUTH)
if (ao_neighbors & N_WEST)
T = get_step(src, SOUTHWEST)
if (isopenturf(T))
ao_neighbors |= N_SOUTHWEST
if (ao_neighbors & N_EAST)
T = get_step(src, SOUTHEAST)
if (isopenturf(T))
ao_neighbors |= N_SOUTHEAST
/turf/simulated/open/update_ao()
if (ao_overlays)
shadower.cut_overlay(ao_overlays)
ao_overlays.Cut()
var/list/cache = SSicon_cache.ao_cache
for(var/i = 1 to 4)
var/cdir = cornerdirs[i]
var/corner = 0
if (ao_neighbors & (1 << cdir))
corner |= 2
if (ao_neighbors & (1 << turn(cdir, 45)))
corner |= 1
if (ao_neighbors & (1 << turn(cdir, -45)))
corner |= 4
if (corner != 7) // 7 is the 'no shadows' state, no reason to add overlays for it.
var/image/I = cache["[corner]-[i]"]
if (!I)
I = make_ao_image(corner, i)
LAZYADD(ao_overlays, I)
UNSETEMPTY(ao_overlays)
if (ao_overlays)
shadower.add_overlay(ao_overlays)

View File

@@ -0,0 +1,115 @@
#ifdef AO_USE_LIGHTING_OPACITY
#define AO_TURF_CHECK(T) (!T.has_opaque_atom)
#else
#define AO_TURF_CHECK(T) (!T.density || !T.opacity)
#endif
/turf
var/permit_ao = TRUE
var/tmp/list/ao_overlays // Current ambient occlusion overlays. Tracked so we can reverse them without dropping all priority overlays.
var/tmp/ao_neighbors = 0
var/ao_queued = AO_UPDATE_NONE
/turf/proc/regenerate_ao()
for (var/thing in RANGE_TURFS(1, src))
var/turf/T = thing
if (T.permit_ao)
T.queue_ao(TRUE)
/turf/proc/calculate_ao_neighbors()
ao_neighbors = 0
if (!permit_ao)
return
var/turf/T
for (var/tdir in cardinal)
T = get_step(src, tdir)
if (T && AO_TURF_CHECK(T))
ao_neighbors |= 1 << tdir
if (ao_neighbors & N_NORTH)
if (ao_neighbors & N_WEST)
T = get_step(src, NORTHWEST)
if (AO_TURF_CHECK(T))
ao_neighbors |= N_NORTHWEST
if (ao_neighbors & N_EAST)
T = get_step(src, NORTHEAST)
if (AO_TURF_CHECK(T))
ao_neighbors |= N_NORTHEAST
if (ao_neighbors & N_SOUTH)
if (ao_neighbors & N_WEST)
T = get_step(src, SOUTHWEST)
if (AO_TURF_CHECK(T))
ao_neighbors |= N_SOUTHWEST
if (ao_neighbors & N_EAST)
T = get_step(src, SOUTHEAST)
if (AO_TURF_CHECK(T))
ao_neighbors |= N_SOUTHEAST
/proc/make_ao_image(corner, i)
var/list/cache = SSicon_cache.ao_cache
var/cstr = "[corner]"
var/key = "[cstr]-[i]"
var/image/I = image('icons/turf/flooring/shadows.dmi', cstr, dir = 1 << (i-1))
I.alpha = WALL_AO_ALPHA
I.blend_mode = BLEND_OVERLAY
I.appearance_flags = RESET_ALPHA|RESET_COLOR|TILE_BOUND
. = cache[key] = I
/turf/proc/queue_ao(rebuild = TRUE)
if (!ao_queued)
SSocclusion.queue += src
var/new_level = rebuild ? AO_UPDATE_REBUILD : AO_UPDATE_OVERLAY
if (ao_queued < new_level)
ao_queued = new_level
/turf/proc/update_ao()
if (ao_overlays)
cut_overlay(ao_overlays, TRUE)
ao_overlays.Cut()
if (!permit_ao)
return
var/list/cache = SSicon_cache.ao_cache
if (!has_opaque_atom)
for(var/i = 1 to 4)
var/cdir = cornerdirs[i]
var/corner = 0
if (ao_neighbors & (1 << cdir))
corner |= 2
if (ao_neighbors & (1 << turn(cdir, 45)))
corner |= 1
if (ao_neighbors & (1 << turn(cdir, -45)))
corner |= 4
if (corner != 7) // 7 is the 'no shadows' state, no reason to add overlays for it.
var/image/I = cache["[corner]-[i]"]
if (!I)
I = make_ao_image(corner, i) // this will also add the image to the cache.
if (!pixel_x && !pixel_y && !pixel_w && !pixel_z) // We can only use the cache if we're not shifting.
LAZYADD(ao_overlays, I)
else
// There's a pixel var set, so we're going to need to make a new instance just for this type.
var/mutable_appearance/MA = new(I)
// We invert the offsets here to counteract the pixel shifting of the parent turf.
MA.pixel_x = -(pixel_x)
MA.pixel_y = -(pixel_y)
MA.pixel_w = -(pixel_w)
MA.pixel_z = -(pixel_z)
LAZYADD(ao_overlays, MA)
UNSETEMPTY(ao_overlays)
if (ao_overlays)
add_overlay(ao_overlays, TRUE)

View File

@@ -104,6 +104,9 @@
if (new_opacity == TRUE)
T.has_opaque_atom = TRUE
T.reconsider_lights()
#ifdef AO_USE_LIGHTING_OPACITY
T.regenerate_ao()
#endif
else
var/old_has_opaque_atom = T.has_opaque_atom
T.recalc_atom_opacity()

View File

@@ -119,20 +119,38 @@
// Can't think of a good name, this proc will recalculate the has_opaque_atom variable.
/turf/proc/recalc_atom_opacity()
#ifdef AO_USE_LIGHTING_OPACITY
var/old = has_opaque_atom
#endif
has_opaque_atom = FALSE
for (var/atom/A in src.contents + src) // Loop through every movable atom on our tile PLUS ourselves (we matter too...)
if (A.opacity)
has_opaque_atom = TRUE
return // No need to continue if we find something opaque.
if (opacity)
has_opaque_atom = TRUE
else
for (var/thing in src) // Loop through every movable atom on our tile
var/atom/movable/A = thing
if (A.opacity)
has_opaque_atom = TRUE
break // No need to continue if we find something opaque.
#ifdef AO_USE_LIGHTING_OPACITY
if (old != has_opaque_atom)
queue_ao()
#endif
// If an opaque movable atom moves around we need to potentially update visibility.
/turf/Entered(atom/movable/Obj, atom/OldLoc)
. = ..()
if (Obj && Obj.opacity)
if (Obj && Obj.opacity && !has_opaque_atom)
has_opaque_atom = TRUE // Make sure to do this before reconsider_lights(), incase we're on instant updates. Guaranteed to be on in this case.
reconsider_lights()
#ifdef AO_USE_LIGHTING_OPACITY
// Hook for AO.
regenerate_ao()
#endif
/turf/Exited(atom/movable/Obj, atom/newloc)
. = ..()
@@ -187,9 +205,12 @@
var/list/old_affecting_lights = affecting_lights
var/old_lighting_overlay = lighting_overlay
var/list/old_corners = corners
var/old_ao_neighbors = ao_neighbors
. = ..()
ao_neighbors = old_ao_neighbors
recalc_atom_opacity()
lighting_overlay = old_lighting_overlay
if (lighting_overlay && lighting_overlay.loc != src)

View File

@@ -168,6 +168,7 @@
. = ..()
icon_state = "" // Clear out the debug icon.
SSopenturf.openspace_turfs += src
shadower = new(src)
update()
/**