X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=blobdiff_plain;f=src%2Fvoxscape.c;h=54c0b10b3c15ebb4a736dfff0d185fa0ce0d7a08;hb=e10cd82c8f0b2a2140aa6a4792ed56329932271f;hp=b9a18c3eecdda8b36363a70fea637023baa60e3d;hpb=59682b78987c148f793a6d7d7c60280048ca986c;p=voxscape diff --git a/src/voxscape.c b/src/voxscape.c index b9a18c3..54c0b10 100644 --- a/src/voxscape.c +++ b/src/voxscape.c @@ -3,8 +3,17 @@ #include #include #include +#include #include #include "voxscape.h" +#include "lut.h" + +#define XLERP(a, b, t, fp) \ + ((((a) << (fp)) + ((b) - (a)) * (t)) >> fp) + +static uint32_t lerp_rgb(int r0, int g0, int b0, int r1, int g1, int b1, int32_t t); +static uint32_t lerp_color(uint32_t ca, uint32_t cb, int32_t t); +static uint32_t lerp_pcol_rgb(uint32_t pcol, int r, int g, int b, int32_t t); enum { SLICELEN = 1 @@ -14,19 +23,28 @@ struct voxscape { int xsz, ysz; unsigned char *height; uint32_t *color; + int xshift, xmask, ymask; + + int hfilt, cfilt; /* framebuffer */ uint32_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; /* 24.8 */ + int32_t *slicelen; + int proj_dist; + + int zfog; /* fog start Z (0: no fog) */ + int fogcolor[3]; unsigned int valid; }; @@ -52,6 +70,18 @@ struct voxscape *vox_create(int xsz, int 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; } @@ -59,7 +89,7 @@ struct voxscape *vox_open(const char *hfile, const char *cfile) { unsigned char *hpix; uint32_t *cpix; - int width, height, cwidth, cheight; + int i, width, height, cwidth, cheight; struct voxscape *vox; if(!(hpix = img_load_pixels(hfile, &width, &height, IMG_FMT_GREY8))) { @@ -83,8 +113,14 @@ struct voxscape *vox_open(const char *hfile, const char *cfile) img_free_pixels(cpix); return 0; } + memcpy(vox->height, hpix, width * height); - memcpy(vox->color, cpix, width * height * sizeof *vox->color); + + /* swap r/b and discard alpha while copying */ + for(i=0; icolor[i] = (cpix[i] & 0xff00) | ((cpix[i] & 0xff) << 16) | + ((cpix[i] & 0xff0000) >> 16); + } img_free_pixels(hpix); img_free_pixels(cpix); @@ -103,7 +139,73 @@ void vox_free(struct voxscape *vox) free(vox); } -void vox_framebuf(struct voxscape *vox, int xres, int yres, uint32_t *fb) +void vox_fog(struct voxscape *vox, int zstart, uint32_t color) +{ + vox->zfog = zstart; + + vox->fogcolor[2] = color >> 16; + vox->fogcolor[1] = (color >> 8) & 0xff; + vox->fogcolor[0] = color & 0xff; +} + +#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; + uint32_t 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 = lerp_color(c00, c01, v); + c1 = lerp_color(c10, c11, v); + return lerp_color(c0, c1, u); +} + + +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, uint32_t *fb, int horizon) { if(xres != vox->fbwidth) { free(vox->coltop); @@ -115,22 +217,25 @@ void vox_framebuf(struct voxscape *vox, int xres, int yres, uint32_t *fb) vox->fb = fb; vox->fbwidth = xres; vox->fbheight = yres; + vox->horizon = horizon >= 0 ? horizon : vox->fbheight / 2; } -void vox_view(struct voxscape *vox, int32_t x, int32_t y, int32_t angle) +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; - /* TODO precalc stuff */ - valid &= ~SLICELEN; + vox->valid &= ~SLICELEN; } void vox_proj(struct voxscape *vox, int fov, int znear, int zfar) { - int i; - vox->fov = fov; vox->znear = znear; vox->zfar = zfar; @@ -142,7 +247,7 @@ void vox_proj(struct voxscape *vox, int fov, int znear, int zfar) return; } - valid &= ~SLICELEN; + vox->valid &= ~SLICELEN; } /* algorithm: @@ -165,14 +270,12 @@ void vox_begin(struct voxscape *vox) { int i; - for(i=0; ifbwidth; i++) { - vox->coltop[i] = vox->fbheight; - } + memset(vox->coltop, 0, vox->fbwidth * sizeof *vox->coltop); if(!(vox->valid & SLICELEN)) { - float theta = (float)vox->angle * M_PI / 360.0f; /* half angle */ + 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) * 2.0f * 256.0f) + vox->slicelen[i] = (int32_t)((vox->znear + i) * tan(theta) * 4.0f * 65536.0f); } vox->valid |= SLICELEN; } @@ -180,9 +283,128 @@ void vox_begin(struct voxscape *vox) void vox_render_slice(struct voxscape *vox, int n) { - int32_t len; + int i, j, hval, colstart, colheight, z; + int32_t x, y, len, xstep, ystep, fog; + uint32_t color; + uint32_t *fbptr; + + if(vox->zfog > 0 && n > vox->zfog) { + fog = ((n - vox->zfog) << 16) / (vox->zfar - vox->zfog); + } else { + fog = 0; + } + + 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); + for(i=0; ifbwidth; 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; + + if(fog > 0) { + color = lerp_pcol_rgb(color, vox->fogcolor[0], vox->fogcolor[1], vox->fogcolor[2], fog); + } + + for(j=0; jfbwidth; + } + vox->coltop[i] = hval; + } - len = vox->slicelen[n]; + x += xstep; + y += ystep; + } +} - /* TODO cont. */ +void vox_sky_solid(struct voxscape *vox, uint32_t color) +{ + int i, j, colheight; + uint32_t *fbptr; + + for(i=0; ifbwidth; i++) { + fbptr = vox->fb + i; + colheight = vox->fbheight - vox->coltop[i]; + for(j=0; jfbwidth; + } + } } + +void vox_sky_grad(struct voxscape *vox, uint32_t chor, uint32_t ctop) +{ + int i, j, colheight, t; + int r0, g0, b0, r1, g1, b1; + int d = vox->fbheight - vox->horizon; + uint32_t *grad, *fbptr; + + grad = alloca(vox->fbheight * sizeof *grad); + + b0 = ctop >> 16; + g0 = (ctop >> 8) & 0xff; + r0 = ctop & 0xff; + b1 = chor >> 16; + g1 = (chor >> 8) & 0xff; + r1 = chor & 0xff; + + for(i=0; ifbheight; i++) { + grad[i] = chor; + } + + for(i=0; ifbwidth; i++) { + fbptr = vox->fb + i; + colheight = vox->fbheight - vox->coltop[i]; + for(j=0; jfbwidth; + } + } +} + +static uint32_t lerp_rgb(int r0, int g0, int b0, int r1, int g1, int b1, int32_t t) +{ + int r = XLERP(r0, r1, t, 16); + int g = XLERP(g0, g1, t, 16); + int b = XLERP(b0, b1, t, 16); + return (b << 16) | (g << 8) | r; +} + +static uint32_t lerp_color(uint32_t ca, uint32_t cb, int32_t t) +{ + int r0, g0, b0, r1, g1, b1; + + b0 = ca >> 16; + g0 = (ca >> 8) & 0xff; + r0 = ca & 0xff; + b1 = cb >> 16; + g1 = (cb >> 8) & 0xff; + r1 = cb & 0xff; + + return lerp_rgb(r0, g0, b0, r1, g1, b1, t); +} + +static uint32_t lerp_pcol_rgb(uint32_t pcol, int r, int g, int b, int32_t t) +{ + int b0 = pcol >> 16; + int g0 = (pcol >> 8) & 0xff; + int r0 = pcol & 0xff; + + return lerp_rgb(r0, g0, b0, r, g, b, t); +} +