voxelscape port
authorJohn Tsiombikas <nuclear@member.fsf.org>
Mon, 17 Oct 2022 09:42:14 +0000 (12:42 +0300)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Mon, 17 Oct 2022 09:42:14 +0000 (12:42 +0300)
Makefile
src/data.h [new file with mode: 0644]
src/data.s [new file with mode: 0644]
src/gamescr.c
src/voxscape.c [new file with mode: 0644]
src/voxscape.h [new file with mode: 0644]

index 8b1d20f..268d1ee 100644 (file)
--- 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 (file)
index 0000000..4cb08e2
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef DATA_H_
+#define DATA_H_
+
+#include <stdint.h>
+/*#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 (file)
index 0000000..7a6431d
--- /dev/null
@@ -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"
index bad3bec..7981cb4 100644 (file)
@@ -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; i<lvl->numvis; 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 (file)
index 0000000..2f2296e
--- /dev/null
@@ -0,0 +1,325 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+#include <assert.h>
+#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; i<vox->nslices; 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; i<vox->nslices; 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; i<vox->fbwidth/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; j<colheight; j++) {
+                               *fbptr = color | ((uint16_t)color << 8);
+                               fbptr += vox->fbwidth >> 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; i<vox->fbwidth/2; i++) {
+               fbptr = vox->fb + i;
+               colheight = vox->fbheight - vox->coltop[i];
+               for(j=0; j<colheight; j++) {
+                       *fbptr = color | ((uint16_t)color << 8);
+                       fbptr += vox->fbwidth >> 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; i<d; i++) {
+               t = (i << 8) / d;
+               grad[i] = XLERP(ctop, chor, t, 8);
+       }
+       for(i=d; i<vox->fbheight; i++) {
+               grad[i] = chor;
+       }
+
+       /* TODO double columns */
+       for(i=0; i<vox->fbwidth/2; i++) {
+               fbptr = vox->fb + i;
+               colheight = vox->fbheight - vox->coltop[i];
+               for(j=0; j<colheight; j++) {
+                       *fbptr = grad[j] | ((uint16_t)grad[j] << 8);
+                       fbptr += vox->fbwidth >> 1;
+               }
+       }
+}
diff --git a/src/voxscape.h b/src/voxscape.h
new file mode 100644 (file)
index 0000000..56295ed
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef VOXSCAPE_H_
+#define VOXSCAPE_H_
+
+#include <stdint.h>
+
+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_ */