Skip to content
Snippets Groups Projects
Commit 15b2bcde authored by dg's avatar dg
Browse files

no more libfov

git-svn-id: http://svn.net-core.org/repos/t-engine4@29 51575b47-30f0-44d4-a5cc-537603b46e54
parent d5dde24e
No related branches found
No related tags found
No related merge requests found
......@@ -53,8 +53,8 @@ function _M:init(w, h)
setmetatable(self.remembers, {__call = mapbool})
self.surface = core.display.newSurface(self.viewport.width, self.viewport.height)
self.fov = core.fov.new(_M.opaque, _M.apply, self)
self.fov_lite = core.fov.new(_M.opaque, _M.applyLite, self)
self.fov = function(x, y, d) core.fov.calc(x, y, d,_M.opaque, _M.apply, self) end
self.fov_lite = function(x, y, d) core.fov.calc(x, y, d,_M.opaque, _M.applyLite, self) end
self.changed = true
end
......@@ -96,7 +96,7 @@ end
function _M:display()
-- If nothing changed, return the same surface as before
if not self.changed then return self.surface end
self.changed = false
self.changed = false print("redraw")
-- Erase and the display the map
self.surface:erase()
......@@ -152,10 +152,10 @@ end
-- No need to call it manually usualy
function _M:close()
self.tiles:close()
self.fovLite:close()
self.fovLite = nil
self.fov:close()
self.fov = nil
-- self.fovLite:close()
-- self.fovLite = nil
-- self.fov:close()
-- self.fov = nil
return true
end
......
......@@ -54,11 +54,13 @@ function _M:display()
self.log:display():toScreen(0, self.h * 0.80)
if self.level and self.level.map then
self.level.map.seens(self.player.x, self.player.y, true)
self.level.map.lites(self.player.x, self.player.y, true)
self.level.map.remembers(self.player.x, self.player.y, true)
self.level.map.fov(self.player.x, self.player.y, 20)
self.level.map.fov_lite(self.player.x, self.player.y, 4)
if self.level.map.changed then
self.level.map.seens(self.player.x, self.player.y, true)
self.level.map.lites(self.player.x, self.player.y, true)
self.level.map.remembers(self.player.x, self.player.y, true)
self.level.map.fov(self.player.x, self.player.y, 20)
self.level.map.fov_lite(self.player.x, self.player.y, 4)
end
local s = self.level.map:display()
if s then
s:toScreen(0, 0)
......
......@@ -7,7 +7,7 @@ return {
level_npcs = {5, 10},
generator = {
map = {
class= "engine.generator.map.Empty",
class= "engine.generator.map.Rooms",
floor = "FLOOR",
wall = "WALL",
up = "UP",
......
......@@ -6,7 +6,6 @@ solution "TEngine"
"src",
"src/lua",
"src/luasocket",
"src/fov",
"src/physfs",
"src/physfs/zlib123",
"/usr/include/SDL",
......@@ -32,7 +31,7 @@ project "TEngine"
language "C"
targetname "t-engine"
files { "src/*.c", }
links { "physfs", "lua", "fov", "luasocket" }
links { "physfs", "lua", "luasocket" }
defines { "_DEFAULT_VIDEOMODE_FLAGS_='SDL_HWSURFACE|SDL_DOUBLEBUF'" }
configuration "macosx"
......@@ -102,10 +101,3 @@ project "luasocket"
"src/luasocket/wsocket.c",
"src/luasocket/mime.c",
}
project "fov"
kind "StaticLib"
language "C"
targetname "fov"
files { "src/fov/*.c", }
#include "fov/fov.h"
#include "fov.h"
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
......@@ -15,97 +15,25 @@
* FOV *
******************************************************************
******************************************************************/
struct lua_fov
{
fov_settings_type fov_settings;
int apply_ref;
int opaque_ref;
int map_ref;
};
static void map_seen(void *m, int x, int y, int dx, int dy, void *src)
{
struct lua_fov *fov = (struct lua_fov *)m;
lua_rawgeti(L, LUA_REGISTRYINDEX, fov->apply_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, fov->map_ref);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_call(L, 3, 0);
}
static bool map_opaque(void *m, int x, int y)
{
struct lua_fov *fov = (struct lua_fov *)m;
lua_rawgeti(L, LUA_REGISTRYINDEX, fov->opaque_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, fov->map_ref);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_call(L, 3, 1);
bool res = lua_toboolean(L, -1);
lua_pop(L, 1);
return res;
}
static int lua_new_fov(lua_State *L)
static int lua_fov(lua_State *L)
{
/* printf("1\n");
luaL_checktype(L, LUA_TFUNCTION, 1);
printf("2\n");
luaL_checktype(L, LUA_TFUNCTION, 2);
printf("3\n");
luaL_checktype(L, LUA_TTABLE, 3);
printf("4\n");*/
int x = luaL_checknumber(L, 1);
int y = luaL_checknumber(L, 2);
int radius = luaL_checknumber(L, 3);
int map_ref = luaL_ref(L, LUA_REGISTRYINDEX);
int apply_ref = luaL_ref(L, LUA_REGISTRYINDEX);
int opaque_ref = luaL_ref(L, LUA_REGISTRYINDEX);
int check_ref = luaL_ref(L, LUA_REGISTRYINDEX);
struct lua_fov *fov = (struct lua_fov*)lua_newuserdata(L, sizeof(struct lua_fov));
auxiliar_setclass(L, "fov{core}", -1);
fov->apply_ref = apply_ref;
fov->opaque_ref = opaque_ref;
fov->map_ref = map_ref;
fov_settings_init(&(fov->fov_settings));
fov_settings_set_opacity_test_function(&(fov->fov_settings), map_opaque);
fov_settings_set_apply_lighting_function(&(fov->fov_settings), map_seen);
do_fov(x, y, radius, map_ref, check_ref, apply_ref);
return 1;
}
static int lua_free_fov(lua_State *L)
{
struct lua_fov *fov = (struct lua_fov*)auxiliar_checkclass(L, "fov{core}", 1);
fov_settings_free(&(fov->fov_settings));
luaL_unref(L, LUA_REGISTRYINDEX, fov->apply_ref);
luaL_unref(L, LUA_REGISTRYINDEX, fov->opaque_ref);
luaL_unref(L, LUA_REGISTRYINDEX, fov->map_ref);
lua_pushnumber(L, 1);
return 1;
}
static int lua_fov(lua_State *L)
{
struct lua_fov *fov = (struct lua_fov*)auxiliar_checkclass(L, "fov{core}", 1);
int x = luaL_checknumber(L, 2);
int y = luaL_checknumber(L, 3);
int radius = luaL_checknumber(L, 4);
fov_circle(&(fov->fov_settings), fov, NULL, x, y, radius);
return 0;
luaL_unref(L, LUA_REGISTRYINDEX, apply_ref);
luaL_unref(L, LUA_REGISTRYINDEX, check_ref);
luaL_unref(L, LUA_REGISTRYINDEX, map_ref);
}
static const struct luaL_reg fovlib[] =
{
{"new", lua_new_fov},
{NULL, NULL},
};
static const struct luaL_reg fov_reg[] =
{
{"__gc", lua_free_fov},
{"close", lua_free_fov},
{"__call", lua_fov},
{"calc", lua_fov},
{NULL, NULL},
};
......@@ -485,7 +413,7 @@ static const struct luaL_reg rnglib[] =
int luaopen_core(lua_State *L)
{
auxiliar_newclass(L, "fov{core}", fov_reg);
// auxiliar_newclass(L, "fov{core}", fov_reg);
auxiliar_newclass(L, "sdl{surface}", sdl_surface_reg);
auxiliar_newclass(L, "sdl{font}", sdl_font_reg);
luaL_openlib(L, "core.fov", fovlib, 0);
......
src/fov.c 0 → 100644
#include "fov.h"
#include "types.h"
#include "script.h"
#define f(x,y) for (x = 0; x < y; ++x)
int ccw(int x1, int y1, int x2, int y2, int x3, int y3) { // positive iff they are counterclockwise
return (x1*y2 + x2*y3 + x3*y1 - x1*y3 - x2*y1 - x3*y2);
}
static bool check(int map_ref, int check_ref, int x, int y)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, check_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, map_ref);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_call(L, 3, 1);
bool res = lua_toboolean(L, -1);
lua_pop(L, 1);
return res;
}
static void apply(int map_ref, int apply_ref, int px, int py, int cx, int cy, int dis)
{
if ((cx-px)*(cx-px) + (cy-py)*(cy-py) <= dis*dis + 1) { // circular view - can be changed if you like
lua_rawgeti(L, LUA_REGISTRYINDEX, apply_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, map_ref);
lua_pushnumber(L, cx);
lua_pushnumber(L, cy);
lua_pushnumber(L, dis);
lua_call(L, 4, 0);
}
}
// runs in O(N), "point" (read: unit length segment) to "point" line of sight that also checks intermediate "point"s.
// Gives identical results to the other algorithm, amazingly. Both are equivalent to checking for digital lines.
// you see those inner loops? Amortized time. Each while loop is entered at most N times, total.
static void trace(int src_x, int src_y, int dir, int n, int h, int map_ref, int check_ref, int apply_ref)
{
int topx[n+2], topy[n+2], botx[n+2], boty[n+2]; // convex hull of obstructions
int curt = 0, curb = 0; // size of top and bottom convex hulls
int s[2][2] = {{0, 0}, {0, 0}}; // too lazy to think of real variable names, four critical points on the convex hulls - these four points determine what is visible
topx[0] = botx[0] = boty[0] = 0, topy[0] = 1;
int ad1 = 1, ad2[2] = {0, 0}, eps[2] = {0, n-1};
for (; ad1 <= n; ++ad1) {
int i;
f(i,2) {
eps[i] += h; // good old Bresenham
if (eps[i] >= n) {
eps[i] -= n;
++ad2[i];
}
}
f(i,2) if (ccw(topx[s[!i][1]], topy[s[!i][1]], botx[s[i][0]], boty[s[i][0]], ad1, ad2[i]+i) <= 0) return; // the relevant region is no longer visible. If we don't exit the loop now, strange things happen.
int cx[2] = {ad1, ad1}, cy[2] = {ad2[0], ad2[1]};
f(i,2) {
if (dir&1) cx[i] = -cx[i];
if (dir&2) cy[i] = -cy[i];
if (dir&4) cx[i] ^= cy[i], cy[i] ^= cx[i], cx[i] ^= cy[i];
cx[i] += src_x, cy[i] += src_y;
if (ccw(topx[s[i][1]], topy[s[i][1]], botx[s[!i][0]], boty[s[!i][0]], ad1, ad2[i]+1-i) > 0) {
apply(map_ref, apply_ref, src_x, src_y, cx[i], cy[i], n);
}
}
if (check(map_ref, check_ref, cx[0], cy[0])) { // new obstacle, update convex hull
++curb;
botx[curb] = ad1, boty[curb] = ad2[0]+1;
if (ccw(botx[s[0][0]], boty[s[0][0]], topx[s[1][1]], topy[s[1][1]], ad1, ad2[0]+1) >= 0) return; // the obstacle obscures everything
if (ccw(topx[s[0][1]], topy[s[0][1]], botx[s[1][0]], boty[s[1][0]], ad1, ad2[0]+1) >= 0) {
s[1][0] = curb; // updating visible region
while (s[0][1] < curt && ccw(topx[s[0][1]], topy[s[0][1]], topx[s[0][1]+1], topy[s[0][1]+1], ad1, ad2[0]+1) >= 0) ++s[0][1];
}
while (curb > 1 && ccw(botx[curb-2], boty[curb-2], botx[curb-1], boty[curb-1], ad1, ad2[0]+1) >= 0) { // not convex anymore, delete a point
if (s[1][0] == curb) --s[1][0]; // s[0][0] won't be a problem
--curb;
botx[curb] = botx[curb+1], boty[curb] = boty[curb+1];
}
}
if (check(map_ref, check_ref, cx[1], cy[1])) { // same as above
++curt;
topx[curt] = ad1, topy[curt] = ad2[1];
if (ccw(botx[s[1][0]], boty[s[1][0]], topx[s[0][1]], topy[s[0][1]], ad1, ad2[1]) >= 0) return;
if (ccw(topx[s[1][1]], topy[s[1][1]], botx[s[0][0]], boty[s[0][0]], ad1, ad2[1]) >= 0) {
s[1][1] = curt;
while (s[0][0] < curb && ccw(botx[s[0][0]], boty[s[0][0]], botx[s[0][0]+1], boty[s[0][0]+1], ad1, ad2[1]) <= 0) ++s[0][0];
}
while (curt > 1 && ccw(topx[curt-2], topy[curt-2], topx[curt-1], topy[curt-1], ad1, ad2[1]) <= 0) {
if (s[1][1] == curt) --s[1][1];
--curt;
topx[curt] = topx[curt+1], topy[curt] = topy[curt+1];
}
}
}
}
void do_fov(int x, int y, int distance, int map_ref, int check_ref, int apply_ref)
{
int dir;
f(dir, 8) {
int i;
f(i,distance+1) trace(x, y, dir, distance, i, map_ref, check_ref, apply_ref);
}
}
#ifndef _FOV_H_
#define _FOV_H_
void do_fov(int x, int y, int distance, int map_ref, int check_ref, int apply_ref);
#endif
fov.lo fov.o: fov.c /usr/include/stdlib.h /usr/include/features.h \
/usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \
/usr/include/gnu/stubs.h /usr/include/gnu/stubs-32.h \
/usr/lib/gcc/i686-pc-linux-gnu/4.1.2/include/stddef.h \
/usr/include/string.h /usr/include/bits/string.h \
/usr/include/bits/string2.h /usr/include/endian.h \
/usr/include/bits/endian.h /usr/include/bits/types.h \
/usr/include/bits/typesizes.h /usr/include/stdio.h /usr/include/libio.h \
/usr/include/_G_config.h /usr/include/wchar.h /usr/include/bits/wchar.h \
/usr/include/gconv.h \
/usr/lib/gcc/i686-pc-linux-gnu/4.1.2/include/stdarg.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
/usr/include/bits/stdio.h /usr/include/math.h \
/usr/include/bits/huge_val.h /usr/include/bits/huge_valf.h \
/usr/include/bits/huge_vall.h /usr/include/bits/inf.h \
/usr/include/bits/nan.h /usr/include/bits/mathdef.h \
/usr/include/bits/mathcalls.h /usr/include/bits/mathinline.h \
/usr/lib/gcc/i686-pc-linux-gnu/4.1.2/include/float.h \
/usr/include/assert.h fov.h \
/usr/lib/gcc/i686-pc-linux-gnu/4.1.2/include/stdbool.h
/usr/include/stdlib.h:
/usr/include/features.h:
/usr/include/sys/cdefs.h:
/usr/include/bits/wordsize.h:
/usr/include/gnu/stubs.h:
/usr/include/gnu/stubs-32.h:
/usr/lib/gcc/i686-pc-linux-gnu/4.1.2/include/stddef.h:
/usr/include/string.h:
/usr/include/bits/string.h:
/usr/include/bits/string2.h:
/usr/include/endian.h:
/usr/include/bits/endian.h:
/usr/include/bits/types.h:
/usr/include/bits/typesizes.h:
/usr/include/stdio.h:
/usr/include/libio.h:
/usr/include/_G_config.h:
/usr/include/wchar.h:
/usr/include/bits/wchar.h:
/usr/include/gconv.h:
/usr/lib/gcc/i686-pc-linux-gnu/4.1.2/include/stdarg.h:
/usr/include/bits/stdio_lim.h:
/usr/include/bits/sys_errlist.h:
/usr/include/bits/stdio.h:
/usr/include/math.h:
/usr/include/bits/huge_val.h:
/usr/include/bits/huge_valf.h:
/usr/include/bits/huge_vall.h:
/usr/include/bits/inf.h:
/usr/include/bits/nan.h:
/usr/include/bits/mathdef.h:
/usr/include/bits/mathcalls.h:
/usr/include/bits/mathinline.h:
/usr/lib/gcc/i686-pc-linux-gnu/4.1.2/include/float.h:
/usr/include/assert.h:
fov.h:
/usr/lib/gcc/i686-pc-linux-gnu/4.1.2/include/stdbool.h:
/*
* Copyright (C) 2006, Greg McIntyre
* All rights reserved. See the file named COPYING in the distribution
* for more details.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define __USE_ISOC99 1
#include <math.h>
#include <float.h>
#include <assert.h>
#include "fov.h"
/*
+---++---++---++---+
| || || || |
| || || || |
| || || || |
+---++---++---++---+ 2
+---++---++---+#####
| || || |#####
| || || |#####
| || || |#####
+---++---++---+#####X 1 <-- y
+---++---++---++---+
| || || || |
| @ || || || | <-- srcy centre -> dy = 0.5 = y - 0.5
| || || || |
+---++---++---++---+ 0
0 1 2 3 4
^ ^
| |
srcx x -> dx = 3.5 = x + 0.5
centre
Slope from @ to X.
+---++---++---++---+
| || || || |
| || || || |
| || || || |
+---++---++---++---+ 2
+---++---++---++---+
| || || || |
| || || || |
| || || || |
+---++---++---+X---+ 1 <-- y
+---++---++---+#####
| || || |#####
| @ || || |##### <-- srcy centre -> dy = 0.5 = y - 0.5
| || || |#####
+---++---++---+##### 0
0 1 2 3
^ ^
| |
srcx x -> dx = 2.5 = x - 0.5
centre
Slope from @ to X
*/
/* Types ---------------------------------------------------------- */
/** \cond INTERNAL */
typedef struct {
/*@observer@*/ fov_settings_type *settings;
/*@observer@*/ void *map;
/*@observer@*/ void *source;
int source_x;
int source_y;
unsigned radius;
} fov_private_data_type;
/** \endcond */
/* Options -------------------------------------------------------- */
void fov_settings_init(fov_settings_type *settings) {
settings->shape = FOV_SHAPE_CIRCLE_PRECALCULATE;
settings->corner_peek = FOV_CORNER_NOPEEK;
settings->opaque_apply = FOV_OPAQUE_APPLY;
settings->opaque = NULL;
settings->apply = NULL;
settings->heights = NULL;
settings->numheights = 0;
}
void fov_settings_set_shape(fov_settings_type *settings,
fov_shape_type value) {
settings->shape = value;
}
void fov_settings_set_corner_peek(fov_settings_type *settings,
fov_corner_peek_type value) {
settings->corner_peek = value;
}
void fov_settings_set_opaque_apply(fov_settings_type *settings,
fov_opaque_apply_type value) {
settings->opaque_apply = value;
}
void fov_settings_set_opacity_test_function(fov_settings_type *settings,
bool (*f)(void *map,
int x, int y)) {
settings->opaque = f;
}
void fov_settings_set_apply_lighting_function(fov_settings_type *settings,
void (*f)(void *map,
int x, int y,
int dx, int dy,
void *src)) {
settings->apply = f;
}
/* Circular FOV --------------------------------------------------- */
/*@null@*/ static unsigned *precalculate_heights(unsigned maxdist) {
unsigned i;
unsigned *result = (unsigned *)malloc((maxdist+2)*sizeof(unsigned));
if (result) {
for (i = 0; i <= maxdist; ++i) {
result[i] = (unsigned)sqrtf((float)(maxdist*maxdist - i*i));
}
result[maxdist+1] = 0;
}
return result;
}
static unsigned height(fov_settings_type *settings, int x,
unsigned maxdist) {
unsigned **newheights;
if (maxdist > settings->numheights) {
newheights = (unsigned **)calloc((size_t)maxdist, sizeof(unsigned*));
if (newheights != NULL) {
if (settings->heights != NULL && settings->numheights > 0) {
/* Copy the pointers to the heights arrays we've already
* calculated. Once copied out, we can free the old
* array of pointers. */
memcpy(newheights, settings->heights,
settings->numheights*sizeof(unsigned*));
free(settings->heights);
}
settings->heights = newheights;
settings->numheights = maxdist;
}
}
if (settings->heights) {
if (settings->heights[maxdist-1] == NULL) {
settings->heights[maxdist-1] = precalculate_heights(maxdist);
}
if (settings->heights[maxdist-1] != NULL) {
return settings->heights[maxdist-1][abs(x)];
}
}
return 0;
}
void fov_settings_free(fov_settings_type *settings) {
unsigned i;
if (settings != NULL) {
if (settings->heights != NULL && settings->numheights > 0) {
/*@+forloopexec@*/
for (i = 0; i < settings->numheights; ++i) {
unsigned *h = settings->heights[i];
if (h != NULL) {
free(h);
}
settings->heights[i] = NULL;
}
/*@=forloopexec@*/
free(settings->heights);
settings->heights = NULL;
settings->numheights = 0;
}
}
}
/* Slope ---------------------------------------------------------- */
static float fov_slope(float dx, float dy) {
if (dx <= -FLT_EPSILON || dx >= FLT_EPSILON) {
return dy/dx;
} else {
return 0.0;
}
}
/* Octants -------------------------------------------------------- */
#define FOV_DEFINE_OCTANT(signx, signy, rx, ry, nx, ny, nf, apply_edge, apply_diag) \
static void fov_octant_##nx##ny##nf( \
fov_private_data_type *data, \
int dx, \
float start_slope, \
float end_slope) { \
int x, y, dy, dy0, dy1; \
unsigned h; \
int prev_blocked = -1; \
float end_slope_next; \
fov_settings_type *settings = data->settings; \
\
if (dx == 0) { \
fov_octant_##nx##ny##nf(data, dx+1, start_slope, end_slope); \
return; \
} else if ((unsigned)dx > data->radius) { \
return; \
} \
\
dy0 = (int)(0.5f + ((float)dx)*start_slope); \
dy1 = (int)(0.5f + ((float)dx)*end_slope); \
\
rx = data->source_##rx signx dx; \
ry = data->source_##ry signy dy0; \
\
if (!apply_diag && dy1 == dx) { \
/* We do diagonal lines on every second octant, so they don't get done twice. */ \
--dy1; \
} \
\
switch (settings->shape) { \
case FOV_SHAPE_CIRCLE_PRECALCULATE: \
h = height(settings, dx, data->radius); \
break; \
case FOV_SHAPE_CIRCLE: \
h = (unsigned)sqrtf((float)(data->radius*data->radius - dx*dx)); \
break; \
case FOV_SHAPE_OCTAGON: \
h = (data->radius - dx)<<1; \
break; \
default: \
h = data->radius; \
break; \
}; \
if ((unsigned)dy1 > h) { \
if (h == 0) { \
return; \
} \
dy1 = (int)h; \
} \
\
/*fprintf(stderr, "(%2d) = [%2d .. %2d] (%f .. %f), h=%d,edge=%d\n", \
dx, dy0, dy1, ((float)dx)*start_slope, \
0.5f + ((float)dx)*end_slope, h, apply_edge);*/ \
\
for (dy = dy0; dy <= dy1; ++dy) { \
ry = data->source_##ry signy dy; \
\
if (settings->opaque(data->map, x, y)) { \
if (settings->opaque_apply == FOV_OPAQUE_APPLY && (apply_edge || dy > 0)) { \
settings->apply(data->map, x, y, x - data->source_x, y - data->source_y, data->source); \
} \
if (prev_blocked == 0) { \
end_slope_next = fov_slope((float)dx + 0.5f, (float)dy - 0.5f); \
fov_octant_##nx##ny##nf(data, dx+1, start_slope, end_slope_next); \
} \
prev_blocked = 1; \
} else { \
if (apply_edge || dy > 0) { \
settings->apply(data->map, x, y, x - data->source_x, y - data->source_y, data->source); \
} \
if (prev_blocked == 1) { \
start_slope = fov_slope((float)dx - 0.5f, (float)dy - 0.5f); \
} \
prev_blocked = 0; \
} \
} \
\
if (prev_blocked == 0) { \
fov_octant_##nx##ny##nf(data, dx+1, start_slope, end_slope); \
} \
}
FOV_DEFINE_OCTANT(+,+,x,y,p,p,n,true,true)
FOV_DEFINE_OCTANT(+,+,y,x,p,p,y,true,false)
FOV_DEFINE_OCTANT(+,-,x,y,p,m,n,false,true)
FOV_DEFINE_OCTANT(+,-,y,x,p,m,y,false,false)
FOV_DEFINE_OCTANT(-,+,x,y,m,p,n,true,true)
FOV_DEFINE_OCTANT(-,+,y,x,m,p,y,true,false)
FOV_DEFINE_OCTANT(-,-,x,y,m,m,n,false,true)
FOV_DEFINE_OCTANT(-,-,y,x,m,m,y,false,false)
/* Circle --------------------------------------------------------- */
static void _fov_circle(fov_private_data_type *data) {
/*
* Octants are defined by (x,y,r) where:
* x = [p]ositive or [n]egative x increment
* y = [p]ositive or [n]egative y increment
* r = [y]es or [n]o for reflecting on axis x = y
*
* \pmy|ppy/
* \ | /
* \ | /
* mpn\|/ppn
* ----@----
* mmn/|\pmn
* / | \
* / | \
* /mmy|mpy\
*/
fov_octant_ppn(data, 1, (float)0.0f, (float)1.0f);
fov_octant_ppy(data, 1, (float)0.0f, (float)1.0f);
fov_octant_pmn(data, 1, (float)0.0f, (float)1.0f);
fov_octant_pmy(data, 1, (float)0.0f, (float)1.0f);
fov_octant_mpn(data, 1, (float)0.0f, (float)1.0f);
fov_octant_mpy(data, 1, (float)0.0f, (float)1.0f);
fov_octant_mmn(data, 1, (float)0.0f, (float)1.0f);
fov_octant_mmy(data, 1, (float)0.0f, (float)1.0f);
}
void fov_circle(fov_settings_type *settings,
void *map,
void *source,
int source_x,
int source_y,
unsigned radius) {
fov_private_data_type data;
data.settings = settings;
data.map = map;
data.source = source;
data.source_x = source_x;
data.source_y = source_y;
data.radius = radius;
_fov_circle(&data);
}
/**
* Limit x to the range [a, b].
*/
static float betweenf(float x, float a, float b) {
if (x - a < FLT_EPSILON) { /* x < a */
return a;
} else if (x - b > FLT_EPSILON) { /* x > b */
return b;
} else {
return x;
}
}
#define BEAM_DIRECTION(d, p1, p2, p3, p4, p5, p6, p7, p8) \
if (direction == d) { \
end_slope = betweenf(a, 0.0f, 1.0f); \
fov_octant_##p1(&data, 1, 0.0f, end_slope); \
fov_octant_##p2(&data, 1, 0.0f, end_slope); \
if (a - 1.0f > FLT_EPSILON) { /* a > 1.0f */ \
start_slope = betweenf(2.0f - a, 0.0f, 1.0f); \
fov_octant_##p3(&data, 1, start_slope, 1.0f); \
fov_octant_##p4(&data, 1, start_slope, 1.0f); \
} \
if (a - 2.0f > FLT_EPSILON) { /* a > 2.0f */ \
end_slope = betweenf(a - 2.0f, 0.0f, 1.0f); \
fov_octant_##p5(&data, 1, 0.0f, end_slope); \
fov_octant_##p6(&data, 1, 0.0f, end_slope); \
} \
if (a - 3.0f > FLT_EPSILON) { /* a > 3.0f */ \
start_slope = betweenf(4.0f - a, 0.0f, 1.0f); \
fov_octant_##p7(&data, 1, start_slope, 1.0f); \
fov_octant_##p8(&data, 1, start_slope, 1.0f); \
} \
}
#define BEAM_DIRECTION_DIAG(d, p1, p2, p3, p4, p5, p6, p7, p8) \
if (direction == d) { \
start_slope = betweenf(1.0f - a, 0.0f, 1.0f); \
fov_octant_##p1(&data, 1, start_slope, 1.0f); \
fov_octant_##p2(&data, 1, start_slope, 1.0f); \
if (a - 1.0f > FLT_EPSILON) { /* a > 1.0f */ \
end_slope = betweenf(a - 1.0f, 0.0f, 1.0f); \
fov_octant_##p3(&data, 1, 0.0f, end_slope); \
fov_octant_##p4(&data, 1, 0.0f, end_slope); \
} \
if (a - 2.0f > FLT_EPSILON) { /* a > 2.0f */ \
start_slope = betweenf(3.0f - a, 0.0f, 1.0f); \
fov_octant_##p5(&data, 1, start_slope, 1.0f); \
fov_octant_##p6(&data, 1, start_slope, 1.0f); \
} \
if (a - 3.0f > FLT_EPSILON) { /* a > 3.0f */ \
end_slope = betweenf(a - 3.0f, 0.0f, 1.0f); \
fov_octant_##p7(&data, 1, 0.0f, end_slope); \
fov_octant_##p8(&data, 1, 0.0f, end_slope); \
} \
}
void fov_beam(fov_settings_type *settings, void *map, void *source,
int source_x, int source_y, unsigned radius,
fov_direction_type direction, float angle) {
fov_private_data_type data;
float start_slope, end_slope, a;
data.settings = settings;
data.map = map;
data.source = source;
data.source_x = source_x;
data.source_y = source_y;
data.radius = radius;
if (angle <= 0.0f) {
return;
} else if (angle >= 360.0f) {
_fov_circle(&data);
return;
}
/* Calculate the angle as a percentage of 45 degrees, halved (for
* each side of the centre of the beam). e.g. angle = 180.0f means
* half the beam is 90.0 which is 2x45, so the result is 2.0.
*/
a = angle/90.0f;
BEAM_DIRECTION(FOV_EAST, ppn, pmn, ppy, mpy, pmy, mmy, mpn, mmn);
BEAM_DIRECTION(FOV_WEST, mpn, mmn, pmy, mmy, ppy, mpy, ppn, pmn);
BEAM_DIRECTION(FOV_NORTH, mpy, mmy, mmn, pmn, mpn, ppn, pmy, ppy);
BEAM_DIRECTION(FOV_SOUTH, pmy, ppy, mpn, ppn, mmn, pmn, mmy, mpy);
BEAM_DIRECTION_DIAG(FOV_NORTHEAST, pmn, mpy, mmy, ppn, mmn, ppy, mpn, pmy);
BEAM_DIRECTION_DIAG(FOV_NORTHWEST, mmn, mmy, mpn, mpy, pmy, pmn, ppy, ppn);
BEAM_DIRECTION_DIAG(FOV_SOUTHEAST, ppn, ppy, pmy, pmn, mpn, mpy, mmn, mmy);
BEAM_DIRECTION_DIAG(FOV_SOUTHWEST, pmy, mpn, ppy, mmn, ppn, mmy, pmn, mpy);
}
/*
* Copyright (C) 2006-2007, Greg McIntyre. All rights reserved. See the file
* named COPYING in the distribution for more details.
*/
/**
* \mainpage Field of View Library
*
* \section about About
*
* This is a C library which implements a course-grained lighting
* algorithm suitable for tile-based games such as roguelikes.
*
* \section copyright Copyright
*
* \verbinclude COPYING
*
* \section thanks Thanks
*
* Thanks to Bj&ouml;rn Bergstr&ouml;m
* <bjorn.bergstrom@hyperisland.se> for the algorithm.
*
*/
/**
* \file fov.h
* Field-of-view algorithm for dynamically casting light/shadow on a
* low resolution 2D raster.
*/
#ifndef LIBFOV_HEADER
#define LIBFOV_HEADER
#include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/** Eight-way directions. */
typedef enum {
FOV_EAST = 0,
FOV_NORTHEAST,
FOV_NORTH,
FOV_NORTHWEST,
FOV_WEST,
FOV_SOUTHWEST,
FOV_SOUTH,
FOV_SOUTHEAST
} fov_direction_type;
/** Values for the shape setting. */
typedef enum {
FOV_SHAPE_CIRCLE_PRECALCULATE,
FOV_SHAPE_SQUARE,
FOV_SHAPE_CIRCLE,
FOV_SHAPE_OCTAGON
} fov_shape_type;
/** Values for the corner peek setting. */
typedef enum {
FOV_CORNER_NOPEEK,
FOV_CORNER_PEEK
} fov_corner_peek_type;
/** Values for the opaque apply setting. */
typedef enum {
FOV_OPAQUE_APPLY,
FOV_OPAQUE_NOAPPLY
} fov_opaque_apply_type;
/** @cond INTERNAL */
typedef /*@null@*/ unsigned *height_array_t;
/** @endcond */
typedef struct {
/** Opacity test callback. */
/*@null@*/ bool (*opaque)(void *map, int x, int y);
/** Lighting callback to set lighting on a map tile. */
/*@null@*/ void (*apply)(void *map, int x, int y, int dx, int dy, void *src);
/** Shape setting. */
fov_shape_type shape;
/** Whether to peek around corners. */
fov_corner_peek_type corner_peek;
/** Whether to call apply on opaque tiles. */
fov_opaque_apply_type opaque_apply;
/** \cond INTERNAL */
/** Pre-calculated data. \internal */
/*@null@*/ height_array_t *heights;
/** Size of pre-calculated data. \internal */
unsigned numheights;
/** \endcond */
} fov_settings_type;
/** The opposite direction to that given. */
#define fov_direction_opposite(direction) ((fov_direction_type)(((direction)+4)&0x7))
/**
* Set all the default options. You must call this option when you
* create a new settings data structure.
*
* These settings are the defaults used:
*
* - shape: FOV_SHAPE_CIRCLE_PRECALCULATE
* - corner_peek: FOV_CORNER_NOPEEK
* - opaque_apply: FOV_OPAQUE_APPLY
*
* Callbacks still need to be set up after calling this function.
*
* \param settings Pointer to data structure containing settings.
*/
void fov_settings_init(fov_settings_type *settings);
/**
* Set the shape of the field of view.
*
* \param settings Pointer to data structure containing settings.
* \param value One of the following values, where R is the radius:
*
* - FOV_SHAPE_CIRCLE_PRECALCULATE \b (default): Limit the FOV to a
* circle with radius R by precalculating, which consumes more memory
* at the rate of 4*(R+2) bytes per R used in calls to fov_circle.
* Each radius is only calculated once so that it can be used again.
* Use fov_free() to free this precalculated data's memory.
*
* - FOV_SHAPE_CIRCLE: Limit the FOV to a circle with radius R by
* calculating on-the-fly.
*
* - FOV_SHAPE_OCTOGON: Limit the FOV to an octogon with maximum radius R.
*
* - FOV_SHAPE_SQUARE: Limit the FOV to an R*R square.
*/
void fov_settings_set_shape(fov_settings_type *settings, fov_shape_type value);
/**
* <em>NOT YET IMPLEMENTED</em>.
*
* Set whether sources will peek around corners.
*
* \param settings Pointer to data structure containing settings.
* \param value One of the following values:
*
* - FOV_CORNER_PEEK \b (default): Renders:
\verbatim
........
........
........
..@#
...#
\endverbatim
* - FOV_CORNER_NOPEEK: Renders:
\verbatim
......
.....
....
..@#
...#
\endverbatim
*/
void fov_settings_set_corner_peek(fov_settings_type *settings, fov_corner_peek_type value);
/**
* Whether to call the apply callback on opaque tiles.
*
* \param settings Pointer to data structure containing settings.
* \param value One of the following values:
*
* - FOV_OPAQUE_APPLY \b (default): Call apply callback on opaque tiles.
* - FOV_OPAQUE_NOAPPLY: Do not call the apply callback on opaque tiles.
*/
void fov_settings_set_opaque_apply(fov_settings_type *settings, fov_opaque_apply_type value);
/**
* Set the function used to test whether a map tile is opaque.
*
* \param settings Pointer to data structure containing settings.
* \param f The function called to test whether a map tile is opaque.
*/
void fov_settings_set_opacity_test_function(fov_settings_type *settings, bool (*f)(void *map, int x, int y));
/**
* Set the function used to apply lighting to a map tile.
*
* \param settings Pointer to data structure containing settings.
* \param f The function called to apply lighting to a map tile.
*/
void fov_settings_set_apply_lighting_function(fov_settings_type *settings, void (*f)(void *map, int x, int y, int dx, int dy, void *src));
/**
* Free any memory that may have been cached in the settings
* structure.
*
* \param settings Pointer to data structure containing settings.
*/
void fov_settings_free(fov_settings_type *settings);
/**
* Calculate a full circle field of view from a source at (x,y).
*
* \param settings Pointer to data structure containing settings.
* \param map Pointer to map data structure to be passed to callbacks.
* \param source Pointer to data structure holding source of light.
* \param source_x x-axis coordinate from which to start.
* \param source_y y-axis coordinate from which to start.
* \param radius Euclidean distance from (x,y) after which to stop.
*/
void fov_circle(fov_settings_type *settings, void *map, void *source,
int source_x, int source_y, unsigned radius
);
/**
* Calculate a field of view from source at (x,y), pointing
* in the given direction and with the given angle. The larger
* the angle, the wider, "less focused" the beam. Each side of the
* line pointing in the direction from the source will be half the
* angle given such that the angle specified will be represented on
* the raster.
*
* \param settings Pointer to data structure containing settings.
* \param map Pointer to map data structure to be passed to callbacks.
* \param source Pointer to data structure holding source of light.
* \param source_x x-axis coordinate from which to start.
* \param source_y y-axis coordinate from which to start.
* \param radius Euclidean distance from (x,y) after which to stop.
* \param direction One of eight directions the beam of light can point.
* \param angle The angle at the base of the beam of light, in degrees.
*/
void fov_beam(fov_settings_type *settings, void *map, void *source,
int source_x, int source_y, unsigned radius,
fov_direction_type direction, float angle
);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment