From ddf193afa41762a7b363610ecd3ddd82be9fc4db Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Mon, 17 Oct 2022 12:42:14 +0300 Subject: [PATCH] voxelscape port --- Makefile | 5 +- src/data.h | 14 +++ src/data.s | 15 +++ src/gamescr.c | 121 ++++++++------------- src/voxscape.c | 325 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/voxscape.h | 38 +++++++ 6 files changed, 438 insertions(+), 80 deletions(-) create mode 100644 src/data.h create mode 100644 src/data.s create mode 100644 src/voxscape.c create mode 100644 src/voxscape.h diff --git a/Makefile b/Makefile index 8b1d20f..268d1ee 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,13 @@ src = $(wildcard src/*.c) $(wildcard src/gba/*.c) -ssrc = $(wildcard src/gba/*.s) data/lut.s +ssrc = $(wildcard src/*.s) $(wildcard src/gba/*.s) data/lut.s obj = $(src:.c=.arm.o) $(ssrc:.s=.arm.o) dep = $(src:.c=.arm.d) name = gbajam22 elf = $(name).elf bin = $(name).gba +data = data/color.raw data/color.pal data/height.raw + libs = libs/maxmod/libmm.a TCPREFIX = arm-none-eabi- @@ -47,6 +49,7 @@ $(elf): $(obj) $(libs) $(AS) -o $@ $(ASFLAGS) $< src/data.o: src/data.s $(data) +src/data.arm.o: src/data.s $(data) tools/pngdump/pngdump: $(MAKE) -C tools/pngdump diff --git a/src/data.h b/src/data.h new file mode 100644 index 0000000..4cb08e2 --- /dev/null +++ b/src/data.h @@ -0,0 +1,14 @@ +#ifndef DATA_H_ +#define DATA_H_ + +#include +/*#include "data/snd.h"*/ + +#define CONV_RGB24_RGB15(r, g, b) \ + (((r) >> 3) | (((uint16_t)(g) & 0xf8) << 2) | (((uint16_t)(b) & 0xf8) << 7)) + +extern unsigned char color_pixels[]; +extern unsigned char color_cmap[]; +extern unsigned char height_pixels[]; + +#endif /* DATA_H_ */ diff --git a/src/data.s b/src/data.s new file mode 100644 index 0000000..7a6431d --- /dev/null +++ b/src/data.s @@ -0,0 +1,15 @@ + .section .rodata + + .globl color_pixels + .globl color_cmap + .globl height_pixels + + .align 1 +color_pixels: + .incbin "data/color.raw" + .align 1 +color_cmap: + .incbin "data/color.pal" + .align 1 +height_pixels: + .incbin "data/height.raw" diff --git a/src/gamescr.c b/src/gamescr.c index bad3bec..7981cb4 100644 --- a/src/gamescr.c +++ b/src/gamescr.c @@ -11,8 +11,6 @@ #include "sprite.h" #include "debug.h" #include "level.h" -#include "xgl.h" -#include "polyfill.h" static int gamescr_start(void); static void gamescr_stop(void); @@ -33,39 +31,12 @@ static struct screen gamescr = { static int nframes, num_vbl, backbuf; static uint16_t *vram[] = { gba_vram_lfb0, gba_vram_lfb1 }; -static const char *testlvl = - "################\n" - "################\n" - "################\n" - "################\n" - "################\n" - "####### s#####\n" - "####### ########\n" - "####### #####\n" - "###### #####\n" - "###### #####\n" - "###### #####\n" - "###### ### #####\n" - "###### ### #####\n" - "###### #####\n" - "######## #######\n" - "################\n" - "################\n" - "################\n" - "################\n" - "################\n"; - -static struct xvertex tm_floor[] __attribute__((section(".rodata"))) = { - {0x10000, -0x10000, 0x10000, 0, 0x10000, 0, 210}, - {-0x10000, -0x10000, 0x10000, 0, 0x10000, 0, 210}, - {-0x10000, -0x10000, -0x10000, 0, 0x10000, 0, 210}, - {0x10000, -0x10000, -0x10000, 0, 0x10000, 0, 210} -}; - +static int32_t pos[2], angle; +static struct voxscape *vox; -static struct level *lvl; +#define COLOR_HORIZON 0xcc77ff +#define COLOR_ZENITH 0x5588cc -static struct player player; struct screen *init_game_screen(void) @@ -75,29 +46,16 @@ struct screen *init_game_screen(void) static int gamescr_start(void) { - int i; - uint16_t *cmap; - gba_setmode(4, DISPCNT_BG2 | DISPCNT_OBJ | DISPCNT_FB1); vblperf_setcolor(1); - lvl = init_level(testlvl); - - xgl_init(); - - init_player(&player, lvl); - player.phi = 0x100; - - cmap = gba_bgpal; - *cmap++ = 0; - for(i=1; i<255; i++) { - *cmap++ = rand(); + if(!(vox = vox_create(512, 512))) { + panic(get_pc(), "vox_create"); } - *cmap = 0xffff; - + vox_proj(vox, 45, 1, 250); - select_input(BN_DPAD | BN_A | BN_B); + /*select_input(BN_DPAD | BN_LT | BN_RT);*/ nframes = 0; return 0; @@ -114,8 +72,7 @@ static void gamescr_frame(void) backbuf = ++nframes & 1; fb = (unsigned char*)vram[backbuf]; - polyfill_framebuffer(fb, 240, 160); - fillblock_16byte(fb, 0, 240 * 160 / 16); + vox_framebuf(vox, 240, 160, fb, -1); update(); draw(); @@ -126,42 +83,48 @@ static void gamescr_frame(void) vblperf_begin(); } +#define WALK_SPEED 0x40000 +#define TURN_SPEED 0x100 + static void update(void) { - uint16_t bnstate; + int32_t fwd[2], right[2]; + uint16_t input; + + input = ~REG_KEYINPUT; + + if(input & BN_LT) angle += TURN_SPEED; + if(input & BN_RT) angle -= TURN_SPEED; - bnstate = get_input(); + fwd[0] = -SIN(angle); + fwd[1] = COS(angle); + right[0] = fwd[1]; + right[1] = -fwd[0]; - player_input(&player, bnstate); + if(input & BN_UP) { + pos[0] += fwd[0]; + pos[1] += fwd[1]; + } + if(input & BN_DOWN) { + pos[0] -= fwd[0]; + pos[1] -= fwd[1]; + } + if(input & BN_RIGHT) { + pos[0] += right[0]; + pos[1] += right[1]; + } + if(input & BN_LEFT) { + pos[0] -= right[0]; + pos[1] -= right[1]; + } - upd_vis(lvl, &player); + vox_view(vox, pos[0], pos[1], -30, angle); } static void draw(void) { - int i, x, y; - struct cell *cell; - - xgl_load_identity(); -#ifndef BUILD_GBA - xgl_translate(0, 0, view_zoom); -#endif - xgl_rotate_x(player.phi); - xgl_rotate_y(player.theta); - xgl_translate(player.x, 0, player.y); - - for(i=0; inumvis; i++) { - cell = lvl->vis[i]; - - x = (int32_t)(cell->x - player.cx) << 17; - y = -(int32_t)(cell->y - player.cy) << 17; - - xgl_push_matrix(); - xgl_translate(x, 0, y); - xgl_index(i + 1); - xgl_draw(XGL_QUADS, tm_floor, sizeof tm_floor / sizeof *tm_floor); - xgl_pop_matrix(); - } + vox_render(vox); + vox_sky_grad(vox, COLOR_HORIZON, COLOR_ZENITH); } #ifdef BUILD_GBA diff --git a/src/voxscape.c b/src/voxscape.c new file mode 100644 index 0000000..2f2296e --- /dev/null +++ b/src/voxscape.c @@ -0,0 +1,325 @@ +#include +#include +#include +#include +#include +#include +#include "voxscape.h" +#include "util.h" +#include "debug.h" + +#define XLERP(a, b, t, fp) \ + ((((a) << (fp)) + ((b) - (a)) * (t)) >> fp) + +enum { + SLICELEN = 1 +}; + +struct voxscape { + int xsz, ysz; + unsigned char *height; + unsigned char *color; + int xshift, xmask, ymask; + + int hfilt, cfilt; + + /* framebuffer */ + uint16_t *fb; + int fbwidth, fbheight; + int *coltop; + int horizon; + + /* view */ + int32_t x, y, angle; + int vheight; + + /* projection */ + int fov, znear, zfar; + int nslices; + int32_t *slicelen; + int proj_dist; + + int zfog; /* fog start Z (0: no fog) */ + uint8_t fogcolor; + + unsigned int valid; +}; + +struct voxscape *vox_create(int xsz, int ysz) +{ + struct voxscape *vox; + + if(!(vox = calloc(1, sizeof *vox))) { + return 0; + } + if(!(vox->height = calloc(xsz * ysz, 1))) { + panic(get_pc(), "vox_create: failed to allocate %dx%d heightmap\n", xsz, ysz); + } + if(!(vox->color = calloc(xsz * ysz, 1))) { + panic(get_pc(), "vox_create: failed to allocate %dx%d color map\n", xsz, ysz); + } + vox->xsz = xsz; + vox->ysz = ysz; + + vox->xmask = vox->xsz - 1; + vox->ymask = vox->ysz - 1; + + vox->xshift = -1; + while(xsz) { + xsz >>= 1; + vox->xshift++; + } + + vox->vheight = 80; + vox->proj_dist = 4; /* TODO */ + + return vox; +} + +void vox_free(struct voxscape *vox) +{ + if(!vox) return; + + free(vox->color); + free(vox->height); + free(vox->coltop); + free(vox->slicelen); + free(vox); +} + +uint8_t *vox_texture(struct voxscape *vox, uint8_t *data) +{ + if(data) { + memcpy(vox->color, data, vox->xsz * vox->ysz); + } + return vox->color; +} + +uint8_t *vox_heightmap(struct voxscape *vox, uint8_t *data) +{ + if(data) { + memcpy(vox->height, data, vox->xsz * vox->ysz); + } + return vox->height; +} + +void vox_fog(struct voxscape *vox, int zstart, uint8_t color) +{ + vox->zfog = zstart; + vox->fogcolor = color; +} + +#define H(x, y) \ + vox->height[((((y) >> 16) & vox->ymask) << vox->xshift) + (((x) >> 16) & vox->xmask)] +#define C(x, y) \ + vox->color[((((y) >> 16) & vox->ymask) << vox->xshift) + (((x) >> 16) & vox->xmask)] + + +int vox_height(struct voxscape *vox, int32_t x, int32_t y) +{ + int32_t u, v; + int h00, h01, h10, h11, h0, h1; + + if(!vox->hfilt) { + return H(x, y); + } + + h00 = H(x, y); + h01 = H(x, y + 0x10000); + h10 = H(x + 0x10000, y); + h11 = H(x + 0x10000, y + 0x10000); + + u = x & 0xffff; + v = y & 0xffff; + + h0 = XLERP(h00, h01, v, 16); + h1 = XLERP(h10, h11, v, 16); + return XLERP(h0, h1, u, 16); +} + +int vox_color(struct voxscape *vox, int32_t x, int32_t y) +{ + int32_t u, v; + int c00, c01, c10, c11, c0, c1; + + if(!vox->cfilt) { + return C(x, y); + } + + c00 = C(x, y); + c01 = C(x, y + 0x10000); + c10 = C(x + 0x10000, y); + c11 = C(x + 0x10000, y + 0x10000); + + u = x & 0xffff; + v = y & 0xffff; + + c0 = XLERP(c00, c01, v, 16); + c1 = XLERP(c10, c11, v, 16); + return XLERP(c0, c1, u, 16); +} + + +void vox_filter(struct voxscape *vox, int hfilt, int cfilt) +{ + vox->hfilt = hfilt; + vox->cfilt = cfilt; +} + +void vox_framebuf(struct voxscape *vox, int xres, int yres, void *fb, int horizon) +{ + if(xres != vox->fbwidth) { + free(vox->coltop); + if(!(vox->coltop = malloc(xres * sizeof *vox->coltop))) { + fprintf(stderr, "vox_framebuf: failed to allocate column table (%d)\n", xres); + return; + } + } + vox->fb = fb; + vox->fbwidth = xres; + vox->fbheight = yres; + vox->horizon = horizon >= 0 ? horizon : (vox->fbheight >> 1); +} + +void vox_view(struct voxscape *vox, int32_t x, int32_t y, int h, int32_t angle) +{ + if(h < 0) { + h = vox_height(vox, x, y) - h; + } + + vox->x = x; + vox->y = y; + vox->vheight = h; + vox->angle = angle; + + vox->valid &= ~SLICELEN; +} + +void vox_proj(struct voxscape *vox, int fov, int znear, int zfar) +{ + vox->fov = fov; + vox->znear = znear; + vox->zfar = zfar; + + vox->nslices = vox->zfar - vox->znear; + free(vox->slicelen); + if(!(vox->slicelen = malloc(vox->nslices * sizeof *vox->slicelen))) { + fprintf(stderr, "vox_proj: failed to allocate slice length table (%d)\n", vox->nslices); + return; + } + + vox->valid &= ~SLICELEN; +} + +/* algorithm: + * calculate extents of horizontal equidistant line from the viewer based on fov + * for each column step along this line and compute height for each pixel + * fill the visible (top) part of each column + */ + +void vox_render(struct voxscape *vox) +{ + int i; + + vox_begin(vox); + for(i=0; inslices; i++) { + vox_render_slice(vox, i); + } +} + +void vox_begin(struct voxscape *vox) +{ + int i; + + memset(vox->coltop, 0, vox->fbwidth * sizeof *vox->coltop); + + if(!(vox->valid & SLICELEN)) { + float theta = (float)vox->fov * M_PI / 360.0f; /* half angle */ + for(i=0; inslices; i++) { + vox->slicelen[i] = (int32_t)((vox->znear + i) * tan(theta) * 4.0f * 65536.0f); + } + vox->valid |= SLICELEN; + } +} + +void vox_render_slice(struct voxscape *vox, int n) +{ + int i, j, hval, colstart, colheight, z; + int32_t x, y, len, xstep, ystep; + uint8_t color; + uint16_t *fbptr; + + z = vox->znear + n; + + len = vox->slicelen[n] >> 8; + xstep = ((COS(vox->angle) >> 8) * len) / vox->fbwidth; + ystep = ((SIN(vox->angle) >> 8) * len) / vox->fbwidth; + + x = vox->x - SIN(vox->angle) * z - xstep * (vox->fbwidth >> 1); + y = vox->y + COS(vox->angle) * z - ystep * (vox->fbwidth >> 1); + /* TODO double column */ + for(i=0; ifbwidth/2; i++) { + hval = vox_height(vox, x, y) - vox->vheight; + hval = hval * 160 / (vox->znear + n) + vox->horizon; + if(hval > vox->fbheight) hval = vox->fbheight; + if(hval > vox->coltop[i]) { + color = vox_color(vox, x, y); + colstart = vox->fbheight - hval; + colheight = hval - vox->coltop[i]; + fbptr = vox->fb + colstart * vox->fbwidth + i; + + for(j=0; jfbwidth >> 1; + } + vox->coltop[i] = hval; + } + + x += xstep; + y += ystep; + } +} + +void vox_sky_solid(struct voxscape *vox, uint8_t color) +{ + int i, j, colheight; + uint16_t *fbptr; + + /* TODO double columns */ + for(i=0; ifbwidth/2; i++) { + fbptr = vox->fb + i; + colheight = vox->fbheight - vox->coltop[i]; + for(j=0; jfbwidth >> 1; + } + } +} + +void vox_sky_grad(struct voxscape *vox, uint8_t chor, uint8_t ctop) +{ + int i, j, colheight, t; + int d = vox->fbheight - vox->horizon; + uint8_t *grad; + uint16_t *fbptr; + + grad = alloca(vox->fbheight * sizeof *grad); + + for(i=0; ifbheight; i++) { + grad[i] = chor; + } + + /* TODO double columns */ + for(i=0; ifbwidth/2; i++) { + fbptr = vox->fb + i; + colheight = vox->fbheight - vox->coltop[i]; + for(j=0; jfbwidth >> 1; + } + } +} diff --git a/src/voxscape.h b/src/voxscape.h new file mode 100644 index 0000000..56295ed --- /dev/null +++ b/src/voxscape.h @@ -0,0 +1,38 @@ +#ifndef VOXSCAPE_H_ +#define VOXSCAPE_H_ + +#include + +enum { + VOX_NEAREST, + VOX_LINEAR +}; + +struct voxscape; + +struct voxscape *vox_create(int xsz, int ysz); +void vox_free(struct voxscape *vox); + +/* data argument can be null */ +uint8_t *vox_texture(struct voxscape *vox, uint8_t *data); +uint8_t *vox_heightmap(struct voxscape *vox, uint8_t *data); + +void vox_fog(struct voxscape *vox, int zstart, uint8_t color); +int vox_height(struct voxscape *vox, int32_t x, int32_t y); + +void vox_filter(struct voxscape *vox, int hfilt, int cfilt); + +void vox_framebuf(struct voxscape *vox, int xres, int yres, void *fb, int horizon); +/* negative height for auto at -h above terrain */ +void vox_view(struct voxscape *vox, int32_t x, int32_t y, int h, int32_t angle); +void vox_proj(struct voxscape *vox, int fov, int znear, int zfar); + +void vox_render(struct voxscape *vox); + +void vox_begin(struct voxscape *vox); +void vox_render_slice(struct voxscape *vox, int n); + +void vox_sky_solid(struct voxscape *vox, uint8_t color); +void vox_sky_grad(struct voxscape *vox, uint8_t chor, uint8_t ctop); + +#endif /* VOXSCAPE_H_ */ -- 1.7.10.4