From f35c65d9ef3693fb4bec0e0e99883d3a60736d03 Mon Sep 17 00:00:00 2001 From: Kyle Benesch <4b796c65+github@gmail.com> Date: Sun, 10 Nov 2024 13:54:08 -0800 Subject: [PATCH] Add triage FOV --- buildsys/autotools/sources.am | 2 + include/libtcod-fov.h | 1 + include/libtcod-fov/fov_triage.h | 15 ++++ include/libtcod-fov/map_inline.h | 9 +++ src/libtcod-fov/fov_triage.c | 122 +++++++++++++++++++++++++++++++ src/sources.cmake | 2 + 6 files changed, 151 insertions(+) create mode 100644 include/libtcod-fov/fov_triage.h create mode 100644 src/libtcod-fov/fov_triage.c diff --git a/buildsys/autotools/sources.am b/buildsys/autotools/sources.am index 8a786c18..af974767 100644 --- a/buildsys/autotools/sources.am +++ b/buildsys/autotools/sources.am @@ -9,6 +9,7 @@ libtcod_fov_include_HEADERS = \ ../../include/libtcod-fov/error.hpp \ ../../include/libtcod-fov/fov.h \ ../../include/libtcod-fov/fov.hpp \ + ../../include/libtcod-fov/fov_triage.h \ ../../include/libtcod-fov/fov_types.h \ ../../include/libtcod-fov/libtcod_int.h \ ../../include/libtcod-fov/logging.h \ @@ -27,4 +28,5 @@ libtcod_fov_la_SOURCES = \ ../../src/libtcod-fov/fov_recursive_shadowcasting.c \ ../../src/libtcod-fov/fov_restrictive.c \ ../../src/libtcod-fov/fov_symmetric_shadowcast.c \ + ../../src/libtcod-fov/fov_triage.c \ ../../src/libtcod-fov/logging.c diff --git a/include/libtcod-fov.h b/include/libtcod-fov.h index 343f2d4c..fed18eb4 100644 --- a/include/libtcod-fov.h +++ b/include/libtcod-fov.h @@ -36,6 +36,7 @@ #include "libtcod-fov/bresenham.h" #include "libtcod-fov/error.h" #include "libtcod-fov/fov.h" +#include "libtcod-fov/fov_triage.h" #include "libtcod-fov/logging.h" #include "libtcod-fov/map_inline.h" #include "libtcod-fov/map_types.h" diff --git a/include/libtcod-fov/fov_triage.h b/include/libtcod-fov/fov_triage.h new file mode 100644 index 00000000..38a8342b --- /dev/null +++ b/include/libtcod-fov/fov_triage.h @@ -0,0 +1,15 @@ + +#pragma once +#ifndef TCODFOV_FOV_TRIAGE_H_ +#define TCODFOV_FOV_TRIAGE_H_ + +#include + +#include "config.h" +#include "error.h" +#include "map_types.h" + +TCODFOV_PUBLIC TCODFOV_Error +TCODFOV_triage_2d(const TCODFOV_Map2D* __restrict transparent, TCODFOV_Map2D* __restrict out, int pov_x, int pov_y); + +#endif // TCODFOV_FOV_TRIAGE_H_ diff --git a/include/libtcod-fov/map_inline.h b/include/libtcod-fov/map_inline.h index ef7bf63d..52a9efba 100644 --- a/include/libtcod-fov/map_inline.h +++ b/include/libtcod-fov/map_inline.h @@ -141,4 +141,13 @@ static inline void TCODFOV_map2d_set_bool(TCODFOV_Map2D* __restrict map, int x, return; } } + +/// @brief Assign `value` to `{x, y}` on `map`. Out-of-bounds writes are ignored. +/// @param map Map union pointer, can be NULL. +/// @param x X coordinate. +/// @param y Y coordinate. +/// @param value Assigned value. +static inline void TCODFOV_map2d_set_int(TCODFOV_Map2D* __restrict map, int x, int y, int value) { + TCODFOV_map2d_set_bool(map, x, y, value != 0); // Fallback until maps can hold integers +} #endif // TCODFOV_MAP_INLINE_H_ diff --git a/src/libtcod-fov/fov_triage.c b/src/libtcod-fov/fov_triage.c new file mode 100644 index 00000000..da9608ed --- /dev/null +++ b/src/libtcod-fov/fov_triage.c @@ -0,0 +1,122 @@ +#include "fov_triage.h" + +#include +#include +#include + +#include "map_inline.h" + +/// @brief Compute the reachability of tiles on this side of the row. +static void triage_scan_line( + const TCODFOV_Map2D* __restrict transparent, // Input transparency + int pov_x, // X position + int scan_y, // Y position + int iteration, // Distance from pov_y, always `abs(scan_y - pov_y)` + const int8_t* __restrict prev_row, // Previous row data + int8_t* __restrict next_row, // Active row to be written + int x_begin, + int x_end, + int_fast8_t x_step // Step direction: -1 or 1 +) { + for (int x = x_begin; x != x_end; x += x_step) { + int_fast8_t tests = 0; // Number of tiles checked + int_fast8_t always_hit = 0; // Number of checked tiles which are guaranteed visible + int_fast8_t maybe_hit = 0; // Number of checked tiles which are maybe visible + + // Check diagonal + ++tests; + if (prev_row[x - x_step] & 0b101) ++maybe_hit; + if (prev_row[x - x_step] & 0b110) ++always_hit; + + if (pov_x - iteration <= x && x <= pov_x + iteration) { // Check previous row + ++tests; + if (prev_row[x] & 0b101) ++maybe_hit; + if (prev_row[x] & 0b110) ++always_hit; + } + if (x <= pov_x - iteration || pov_x + iteration <= x) { // Check adjacent tile + ++tests; + if (next_row[x - x_step] & 0b101) ++maybe_hit; + if (next_row[x - x_step] & 0b110) ++always_hit; + } + next_row[x] = 0; + // If any checked tile is maybe visible, then this tile is maybe visible + next_row[x] |= maybe_hit ? 0b1 : 0; + // If all checked tiles are always visible then this tile is always visible + next_row[x] |= (always_hit == tests) ? 0b10 : 0; + next_row[x] |= (next_row[x] && TCODFOV_map2d_get_bool(transparent, x, scan_y) ? 0b100 : 0); + } +} + +/// @brief Compute the next row of visiblity, continue until out-of-bounds +static void triage_scan_next_row( + const TCODFOV_Map2D* __restrict transparent, // Input transparency + TCODFOV_Map2D* __restrict out, // Triage output + int pov_x, // X position + int scan_y, // Y position + int_fast8_t scan_dir, // Y step direction, -1 or 1 + int iteration, // Distance from pov_y, always `abs(scan_y - pov_y)` + int8_t* __restrict prev_row, // Previous row buffer + int8_t* __restrict next_row // Active row buffer +) { + if (scan_y < 0 || scan_y >= TCODFOV_map2d_get_height(out)) return; // Finished, out-of-bounds + + // Compute tile at pov_x, scan_y + next_row[pov_x] = prev_row[pov_x] & 0b100 ? prev_row[pov_x] : 0; + if (next_row[pov_x] && TCODFOV_map2d_get_bool(transparent, pov_x, scan_y)) next_row[pov_x] &= 0b11; + + // Compute tiles on sides of active row + triage_scan_line(transparent, pov_x, scan_y, iteration, prev_row, next_row, pov_x - 1, -1, -1); + triage_scan_line( + transparent, pov_x, scan_y, iteration, prev_row, next_row, pov_x + 1, TCODFOV_map2d_get_width(out), 1); + + // Output triage data + for (int x = 0; x < TCODFOV_map2d_get_width(out); ++x) { + TCODFOV_map2d_set_int(out, x, scan_y, next_row[x] & 0b11); + } + + // Swap next_row and prev_row and continue + triage_scan_next_row(transparent, out, pov_x, scan_y + scan_dir, scan_dir, iteration + 1, next_row, prev_row); +} + +/// @brief Compute initial triage data for the middle row. +static void triage_scan_init( + const TCODFOV_Map2D* __restrict transparent, // Input transparency + TCODFOV_Map2D* __restrict out, // Triage output + int pov_x, + int pov_y, + int8_t* __restrict row) { + row[pov_x] = TCODFOV_map2d_get_bool(transparent, pov_x, pov_y) ? 0b111 : 0b011; + for (int x = pov_x - 1; x >= 0; --x) { + row[x] = 0; + if (row[x + 1] & 0b100) row[x] = TCODFOV_map2d_get_bool(transparent, x, pov_y) ? 0b111 : 0b011; + } + for (int x = pov_x + 1; x < TCODFOV_map2d_get_width(out); ++x) { + row[x] = 0; + if (row[x - 1] & 0b100) row[x] = TCODFOV_map2d_get_bool(transparent, x, pov_y) ? 0b111 : 0b011; + } + for (int x = 0; x < TCODFOV_map2d_get_width(out); ++x) { + TCODFOV_map2d_set_int(out, x, pov_y, row[x] & 0b11); + } +} + +TCODFOV_Error TCODFOV_triage_2d( + const TCODFOV_Map2D* __restrict transparent, TCODFOV_Map2D* __restrict out, int pov_x, int pov_y) { + // Triage data on row format: 0bXYZ + // X = transparent: caches input transparency if applicable + // Y = always visible: all tiles to this tile are always visible + // Z = maybe visible: at least one tile to this tile is maybe visible + int8_t* __restrict row = malloc(TCODFOV_map2d_get_width(out) * 3); + if (!row) { + TCODFOV_set_errorv("Out of memory."); + return TCODFOV_E_OUT_OF_MEMORY; + } + int8_t* __restrict row2 = row + TCODFOV_map2d_get_width(out); + int8_t* __restrict row3 = row + TCODFOV_map2d_get_width(out) * 2; + + triage_scan_init(transparent, out, pov_x, pov_y, row); + + memcpy(row2, row, TCODFOV_map2d_get_width(out)); + triage_scan_next_row(transparent, out, pov_x, pov_y - 1, -1, 1, row2, row3); + triage_scan_next_row(transparent, out, pov_x, pov_y + 1, 1, 1, row, row2); + return TCODFOV_E_OK; +} diff --git a/src/sources.cmake b/src/sources.cmake index 3fda8b98..dd3c6577 100644 --- a/src/sources.cmake +++ b/src/sources.cmake @@ -9,6 +9,7 @@ target_sources(${PROJECT_NAME} PRIVATE libtcod-fov/fov_recursive_shadowcasting.c libtcod-fov/fov_restrictive.c libtcod-fov/fov_symmetric_shadowcast.c + libtcod-fov/fov_triage.c libtcod-fov/logging.c libtcod-fov/utility.h ) @@ -25,6 +26,7 @@ install(FILES ../include/libtcod-fov/error.hpp ../include/libtcod-fov/fov.h ../include/libtcod-fov/fov.hpp + ../include/libtcod-fov/fov_triage.h ../include/libtcod-fov/fov_types.h ../include/libtcod-fov/libtcod_int.h ../include/libtcod-fov/logging.h