#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);
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)
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;
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();
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
--- /dev/null
+#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;
+ }
+ }
+}
--- /dev/null
+#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_ */