cube assembler replaced with a raycasting one - buggy, but faster!

This commit is contained in:
Ben Russell (300178622) 2012-11-12 16:47:11 +13:00
parent 7c25459609
commit cbbdfa1f0a
3 changed files with 1774 additions and 394 deletions

View File

@ -136,11 +136,15 @@ function new_player(settings)
function this.spawn()
local xlen,ylen,zlen
xlen,ylen,zlen = common.map_get_dims()
while true do
this.x = math.floor(math.random()*xlen/4.0)+0.5
this.z = math.floor(math.random()*zlen)+0.5
this.y = (common.map_pillar_get(this.x, this.z))[1+1]-3.0
if this.team == 1 then this.x = xlen - this.x end
this.y = (common.map_pillar_get(this.x, this.z))[1+1]
if this.y < 63 then break end
end
this.y = this.y - 3.0
this.alive = true
this.spawned = true

696
render.c
View File

@ -17,10 +17,12 @@
#include "common.h"
// TODO: bump up to 127.5f
#define FOG_DISTANCE 40.0f
//define DEBUG_INVERT_DRAW_DIR
#define FTB_MAX_PERSPAN 50
// TODO: bump up to 127.5f
#define FOG_DISTANCE 60.0f
#define RAYC_MAX ((int)((FOG_DISTANCE+1)*(FOG_DISTANCE+1)*8+10))
#define DF_NX 0x01
#define DF_NY 0x02
@ -51,15 +53,57 @@ int rtmp_width, rtmp_height, rtmp_pitch;
camera_t *rtmp_camera;
map_t *rtmp_map;
int *ftb_first;
typedef struct raydata {
int16_t x,y,z;
int8_t gx,gz;
float y1,y2;
float sx,sy,sz;
} raydata_t;
typedef struct rayblock {
uint32_t color;
float x,y,z;
} rayblock_t;
int rayc_block_len, rayc_block_head;
int rayc_data_len, rayc_data_head;
raydata_t rayc_data[RAYC_MAX];
rayblock_t *rayc_block = NULL;
int *rayc_mark = NULL;
int rayc_block_size = 0;
int rayc_mark_size = 0;
float *dbuf;
/*
* REFERENCE IMPLEMENTATION
*
*/
uint32_t render_fog_apply_new(uint32_t color, float depth)
{
int b = color&255;
int g = (color>>8)&255;
int r = (color>>16)&255;
int t = (color>>24)&255;
//float fog = (FOG_DISTANCE*FOG_DISTANCE/depth)/256.0f;
float fog = (FOG_DISTANCE*FOG_DISTANCE-(depth < 0.001f ? 0.001f : depth))
/(FOG_DISTANCE*FOG_DISTANCE);
if(fog > 1.0f)
fog = 1.0f;
if(fog < 0.0f)
fog = 0.0f;
r = (r*fog+0.5f);
g = (g*fog+0.5f);
b = (b*fog+0.5f);
return b|(g<<8)|(r<<16)|(t<<24);
}
uint32_t render_fog_apply(uint32_t color, float depth)
{
int b = color&255;
@ -198,7 +242,8 @@ void render_vxl_rect_ftb_fast(uint32_t *ccolor, float *cdepth, int x1, int y1, i
// NOTE: this approach seems to be faster than render_vxl_rect_btf.
// clip
render_rect_clip(&color, &x1, &y1, &x2, &y2, depth);
uint32_t dummy;
render_rect_clip(&dummy, &x1, &y1, &x2, &y2, depth);
if(x2 <= 0)
return;
@ -271,13 +316,11 @@ void render_vxl_cube(uint32_t *ccolor, float *cdepth, int x1, int y1, int x2, in
render_vxl_cube_sides(ccolor, cdepth, x1, y1, x2, y2, color, depth);
}
void render_vxl_face_vert(int blkx, int blky, int blkz,
void render_vxl_face_raycast(int blkx, int blky, int blkz,
float subx, float suby, float subz,
int face,
int gx, int gy, int gz)
{
// TODO: this function sucks, speed it up a bit
int sx,sy;
int i;
float tracemul = cubemap_size/2;
@ -294,13 +337,6 @@ void render_vxl_face_vert(int blkx, int blky, int blkz,
cdepth[i] = FOG_DISTANCE;
}
// clear FTB buffers
for(i = 0; i < cubemap_size; i++)
{
ftb_first[i] = 0;
//ccolor[i<<cubemap_shift] = cubemap_size|(cubemap_size<<16);
}
// get X cube direction
int xgx = gz+gy;
int xgy = 0;
@ -308,402 +344,301 @@ void render_vxl_face_vert(int blkx, int blky, int blkz,
// get Y cube direction
int ygx = 0;
int ygy = gx+gz;
int ygy = fabsf(gx+gz);
int ygz = gy;
// get cubemap offset
float cmoffsx = -(xgx*subx+xgy*suby+xgz*subz);
float cmoffsy = -(ygx*subx+ygy*suby+ygz*subz);
// get base pos
float bx = blkx+subx;
float by = blky+suby;
float bz = blkz+subz;
// get distance to wall
float dist = -(subx*gx+suby*gy+subz*gz);
if(dist < 0.0f)
dist = 1.0f+dist;
else {
//blky--;
blkx--;
blkz--;
}
dist -= 1.0f;
//int coz = blky;
// now build pillars
static uint32_t cdata[256]; // hypothetical maximum
// render cubes from centre out
float odist = dist;
int lbx1 = 2;
int lby1 = 2;
int lbx2 = -2;
int lby2 = -2;
// prep boundaries
int bx1 = 0;
int by1 = 0;
int bx2 = 0;
int by2 = 0;
if(gy < 0)
if(xgx+xgy+xgz < 0)
{
bx1++;
by1++;
bx2++;
by2++;
bx += xgx;
by += xgy;
bz += xgz;
}
int xlen = rtmp_map->xlen;
int ylen = rtmp_map->ylen;
int zlen = rtmp_map->zlen;
int xlenm1 = xlen-1;
int ylenm1 = ylen-1;
int zlenm1 = zlen-1;
while(dist < FOG_DISTANCE)
// now crawl through the block list
#ifdef DEBUG_INVERT_DRAW_DIR
rayblock_t *b = &rayc_block[rayc_block_len-1];
rayblock_t *b_end = &rayc_block[0];
for(; b >= b_end; b--)
#else
rayblock_t *b = &rayc_block[0];
rayblock_t *b_end = &rayc_block[rayc_block_len];
for(; b < b_end; b++)
#endif
{
// go through each
int cox,coy;
cox = bx1; coy = by1;
int pdx = 1, pdy = 0;
// get block delta
float dx = b->x - bx;
float dy = b->y - by;
float dz = b->z - bz;
for(;;)
{
// skip already-rendered stuff
//if(cox >= lbx1 && coy >= lby1 && cox <= lbx2 && coy <= lby2)
// continue;
// get correct screen positions
float sx = dx*xgx+dy*xgy+dz*xgz;
float sy = dx*ygx+dy*ygy+dz*ygz;
float sz = dx* gx+dy* gy+dz* gz;
// get pillar
uint8_t *pillar = rtmp_map->pillars[
((cox+blkx)&(xlenm1))
+(((coy+blkz)&(zlenm1))*xlen)]+4;
// check distance
if(sz < 0.001f || sz >= FOG_DISTANCE)
continue;
// load data
// frustum cull
if(fabsf(sx) > fabsf(sz+2.0f) || fabsf(sy) > fabsf(sz+2.0f))
continue;
i = 0;
for(;;)
{
uint8_t *csrc = &pillar[4];
int nrem = pillar[0]-1;
for(; i < pillar[1]; i++)
cdata[i] = 0;
for(; i <= pillar[2]; i++, nrem--, csrc += 4)
cdata[i] = *(uint32_t *)csrc;
if(pillar[0] == 0)
break;
pillar += 4*(int)pillar[0];
for(; i < pillar[3]-nrem; i++)
cdata[i] = 1;
for(; i < pillar[3]; i++, csrc += 4)
cdata[i] = *(uint32_t *)csrc;
}
for(; i < ylen; i++)
cdata[i] = 1;
// render data
if(gy >= 0)
{
// bottom cubemap
float fdist = 0.0f-blky-suby;
// TODO: work out min required distance by frustum
i = 0;
fdist += i;
for(; i < ylen; i++)
{
if(fdist >= dist && fdist >= 0.001f && cdata[i] > 1)
{
float boxsize = tracemul/fdist;
float px1 = (cox+cmoffsx)*boxsize+traceadd;
float py1 = (coy+cmoffsy)*boxsize+traceadd;
// draw
float boxsize = tracemul/fabsf(sz);
float px1 = sx*boxsize+traceadd;
float py1 = sy*boxsize+traceadd;
float px2 = px1+boxsize;
float py2 = py1+boxsize;
if(1 || i == 0 || cdata[i-1] == 0)
{
render_vxl_cube(ccolor, cdepth,
(int)px1, (int)py1, (int)px2, (int)py2,
cdata[i], fdist);
} else {
render_vxl_cube_sides(ccolor, cdepth,
(int)px1, (int)py1, (int)px2, (int)py2,
cdata[i], fdist);
}
}
fdist += 1.0f;
if(fdist >= FOG_DISTANCE)
break;
}
} else {
// top cubemap
float fdist = 0.0f-blky-suby;
fdist = -ylen-fdist;
// TODO: work out min required distance by frustum
i = ylenm1;
for(; i >= 0; i--)
{
if(fdist >= dist && fdist >= 0.001f && cdata[i] > 1)
{
float boxsize = tracemul/fdist;
float px1 = (-cox+cmoffsx)*boxsize+traceadd;
float py1 = (-coy+cmoffsy)*boxsize+traceadd;
float px2 = px1+boxsize;
float py2 = py1+boxsize;
if(1 || i == ylenm1 || cdata[i+1] == 0)
{
render_vxl_cube(ccolor, cdepth,
(int)px1, (int)py1, (int)px2, (int)py2,
cdata[i], fdist);
} else {
render_vxl_cube_sides(ccolor, cdepth,
(int)px1, (int)py1, (int)px2, (int)py2,
cdata[i], fdist);
}
}
fdist += 1.0f;
if(fdist >= FOG_DISTANCE)
break;
}
}
if(cox == bx2 && coy == by1)
pdx = 0, pdy = 1;
if(cox == bx2 && coy == by2)
pdx = -1, pdy = 0;
if(cox == bx1 && coy == by2)
pdx = 0, pdy = -1;
if(cox == bx1 && coy == by1 && pdy == -1)
break;
cox += pdx;
coy += pdy;
}
// store "last" bounding box
lbx1 = bx1;
lby1 = by1;
lbx2 = bx2;
lby2 = by2;
// expand box
bx1--;by1--;
bx2++;by2++;
// advance
dist += 1.0f;
}
}
void render_vxl_face_horiz(int blkx, int blky, int blkz,
float subx, float suby, float subz,
int face,
int gx, int gy, int gz)
{
int sx,sy;
int i;
float tracemul = cubemap_size/2;
float traceadd = tracemul;
// get cubemaps
uint32_t *ccolor = cubemap_color[face];
float *cdepth = cubemap_depth[face];
// clear cubemap
for(i = 0; i < cubemap_size*cubemap_size; i++)
{
ccolor[i] = 0x00000000;
cdepth[i] = FOG_DISTANCE;
}
// clear FTB buffers
for(i = 0; i < cubemap_size; i++)
{
ftb_first[i] = 0;
//ccolor[i<<cubemap_shift] = cubemap_size|(cubemap_size<<16);
}
// get X cube direction
int xgx = gz+gy;
int xgy = 0;
int xgz = -gx;
// get Y cube direction
int ygx = 0;
int ygy = gx+gz;
int ygz = gy;
// get cubemap offset
float cmoffsx = -(xgx*subx+xgy*suby+xgz*subz);
float cmoffsy = -(ygx*subx+ygy*suby+ygz*subz);
if(cmoffsy >= 0.0f)
cmoffsy = -cmoffsy;
if(cmoffsx >= 0.0f)
cmoffsx -= 1.0f;
//else
// blky--;
// get distance to wall
float dist = -(subx*gx+suby*gy+subz*gz);
if(dist < 0.0f)
dist = 1.0f+dist;
dist -= 1.0f;
int coz = blky;
// now loop and follow through
while(dist < FOG_DISTANCE)
{
// calculate frustum
int frustum = (int)(dist*cubemap_size);
// prep boundaries
int bx1 = 0;
int by1 = 0;
int bx2 = frustum*2;
int by2 = frustum*2;
// clamp wrt pixel counts
// TODO!
// relocate
bx1 -= frustum;
by1 -= frustum;
bx2 -= frustum;
by2 -= frustum;
// need to go towards 0, not -inf!
// (can be done as shifts, just looks nicer this way)
bx1 /= cubemap_size;
by1 /= cubemap_size;
bx2 /= cubemap_size;
by2 /= cubemap_size;
bx1-=2;by1--;
bx2+=2;by2++;
// go through loop
int cox,coy;
cox = 0;
coy = 0;
if(dist >= 0.001f)
{
float boxsize = tracemul/dist;
float nboxsize = tracemul/(dist+0.5f);
for(cox = bx1; cox <= bx2; cox++)
{
coz = 0;
uint8_t *pillar = rtmp_map->pillars[
((cox*gz+blkx)&(rtmp_map->xlen-1))
+(((-cox*gx+blkz)&(rtmp_map->zlen-1))*rtmp_map->xlen)]+4;
//printf("%4i %4i %4i - %i %i %i %i\n",cox,coy,coz,
// pillar[0],pillar[1],pillar[2],pillar[3]);
for(;;)
{
uint8_t *pcol = pillar+4;
// render top
if(pillar[2]-blky >= by1 && pillar[1]-blky <= by2)
for(coz = pillar[1]; coz <= pillar[2]; coz++)
{
if(coz-blky >= by1 && coz-blky <= by2)
{
float px1 = (cox+cmoffsx)*boxsize+traceadd;
float py1 = (coz+cmoffsy-blky)*boxsize+traceadd;
float px2 = px1+boxsize;
float py2 = py1+boxsize;
uint32_t xcolor = render_fog_apply_new(b->color, sx*sx+sy*sy+sz*sz);
render_vxl_cube(ccolor, cdepth,
(int)px1, (int)py1, (int)px2, (int)py2,
*((uint32_t *)pcol), dist);
}
pcol+=4;
}
// advance where sensible
if(pillar[2]-blky > by2)
break;
if(pillar[0] == 0)
break;
pillar += pillar[0]*4;
// render bottom
int diff = (pillar-pcol)>>2;
for(coz = pillar[3]-diff; coz < pillar[3]; coz++)
{
if(coz-blky >= by1 && coz-blky <= by2)
{
float px1 = (cox+cmoffsx)*boxsize+traceadd;
float py1 = (coz+cmoffsy-blky)*boxsize+traceadd;
float px2 = px1+boxsize;
float py2 = py1+boxsize;
render_vxl_cube(ccolor, cdepth,
(int)px1, (int)py1, (int)px2, (int)py2,
*((uint32_t *)pcol), dist);
}
pcol+=4;
}
}
}
}
dist += 1.0f;
blkx += gx;
blkz += gz;
xcolor, sz);
}
}
void render_vxl_redraw(camera_t *camera, map_t *map)
{
int x,y,z;
int i,x,y,z;
// stash stuff in globals to prevent spamming the stack too much
// (and in turn thrashing the cache)
rtmp_camera = camera;
rtmp_map = map;
// stash x/y/zlen
int xlen = map->xlen;
int ylen = map->ylen;
int zlen = map->zlen;
// get block pos
int blkx = ((int)floor(camera->mpx)) & (map->xlen-1);
int blky = ((int)floor(camera->mpy));// & (map->ylen-1);
int blkz = ((int)floor(camera->mpz)) & (map->zlen-1);
int blkx = ((int)floor(camera->mpx)) & (xlen-1);
int blky = ((int)floor(camera->mpy));// & (ylen-1);
int blkz = ((int)floor(camera->mpz)) & (zlen-1);
// get block subpos
float subx = (camera->mpx - floor(camera->mpx));
float suby = (camera->mpy - floor(camera->mpy));
float subz = (camera->mpz - floor(camera->mpz));
// get centre (base) pos
float bx = blkx + subx;
float by = blky + suby;
float bz = blkz + subz;
// check if we need to reallocate the mark table and block list
{
int markbase = xlen * zlen;
int blockbase = markbase * ylen;
if(rayc_mark_size != markbase)
{
rayc_mark_size = markbase;
rayc_mark = realloc(rayc_mark, rayc_mark_size*sizeof(int));
}
if(rayc_block_size != blockbase)
{
rayc_block_size = markbase;
rayc_block = realloc(rayc_block, rayc_block_size*sizeof(rayblock_t));
}
}
// clear the mark table
memset(rayc_mark, 0, rayc_mark_size*sizeof(int));
// prep the starting block
rayc_block_len = 0;
rayc_block_head = 0;
rayc_data_len = 1;
rayc_data_head = 0;
rayc_data[0].x = blkx;
rayc_data[0].y = blky;
rayc_data[0].z = blkz;
rayc_data[0].gx = 0;
rayc_data[0].gz = 0;
rayc_data[0].y1 = blky+suby;
rayc_data[0].y2 = blky+suby;
rayc_data[0].sx = subx;
rayc_data[0].sy = suby;
rayc_data[0].sz = subz;
rayc_mark[blkx + blkz*xlen] = 1;
// build your way up
while(rayc_data_head < rayc_data_len)
{
raydata_t *rd = &(rayc_data[rayc_data_head++]);
// get delta
float dx = rd->x - bx;
float dz = rd->z - bz;
// skip this if it's in the fog
if(dx*dx+dz*dz >= FOG_DISTANCE*FOG_DISTANCE)
continue;
// find where we are
int idx = (((int)(rd->z)) & (zlen-1))*xlen + (((int)rd->x) & (xlen-1));
uint8_t *p = map->pillars[idx]+4;
rayc_mark[idx] = -1;
int lastn = 0;
int topcount = 0;
int lasttop = 0;
while(p[0] != 0)
{
if(rd->y1 < p[1] && (lastn == 0 || rd->y1 >= lasttop))
break;
lastn = p[0];
lasttop = p[1];
topcount = p[0] - (p[2]-p[1]+1);
p += p[0]*4;
}
int spreadflag = !(
rd->y1 >= p[1] && (p[0] == 0 || rd->y1 <= p[p[0]*4+3])
&& rd->y2 >= p[1] && (p[0] == 0 || rd->y2 <= p[p[0]*4+3]));
// advance y1/y2
float y1 = rd->y1;
float y2 = rd->y2;
if(rayc_data_head == 1)
{
y1 = (lastn == 0 ? 0.0f : p[3]);
y2 = p[1];
} else {
float dist1 = sqrtf(dx*dx+dz*dz);
float dist2 = dist1 + 1.42f; // max dist this can travel
float travel = dist2/dist1;
y1 = by + (y1-by)*travel;
y2 = by + (y2-by)*travel;
}
int iy1 = floor(y1);
int iy2 = floor(y2);
float by1 = y1;
float by2 = y2;
// TODO: get the order right!
// add the top blocks (if they exist and we can see them)
if(p[3] >= iy1 && lastn != 0)
{
y2 = y1 = p[3]-1;
uint32_t *c = (uint32_t *)(&p[-4]);
for(i = p[3]-1; i >= p[3]-topcount && i >= iy1; i--)
{
rayblock_t *b = &rayc_block[rayc_block_len++];
b->x = rd->x;
b->z = rd->z;
b->y = i;
b->color = *(c--);
}
}
// sneak your way down
while(p[1] <= iy2)
{
y2 = p[1]+1;
//printf("%i %i %i %i [%i, %i]\n", p[0],p[1],p[2],p[3],iy1,iy2);
uint32_t *c = (uint32_t *)(&p[4]);
for(i = p[1]; i <= p[2] && i <= iy2; i++)
{
rayblock_t *b = &rayc_block[rayc_block_len++];
b->x = rd->x;
b->z = rd->z;
b->y = i;
b->color = *(c++);
}
if(p[0] == 0)
break;
lastn = p[0];
lasttop = p[1];
topcount = p[0] - (p[2]-p[1]+1);
p += 4*p[0];
c = (uint32_t *)(&p[-4]);
for(i = p[3]-1; i >= p[3]-topcount; i--)
{
if(i > iy2)
{
c--;
continue;
}
rayblock_t *b = &rayc_block[rayc_block_len++];
b->x = rd->x;
b->z = rd->z;
b->y = i;
b->color = *(c--);
}
}
// correct the y spread
if(y1 < by1)
y1 = by1;
if(y2 > by2)
y2 = by2;
// spread out
int ofx = 1;
int ofz = 0;
if(spreadflag) do
{
int idx2 = ((ofx + (int)rd->x) & (xlen-1))
+ xlen * ((ofz + (int)rd->z) & (zlen-1));
if((ofx != 0 && ofx == -rd->gx) || (ofz != 0 && ofz == -rd->gz))
{
// do nothing
} else if(rayc_mark[idx2] == 0) {
rayc_mark[idx2] = rayc_data_len+1;
raydata_t *rd2 = &(rayc_data[rayc_data_len++]);
rd2->x = ofx + (int)rd->x;
rd2->z = ofz + (int)rd->z;
rd2->y1 = y1;
rd2->y2 = y2;
rd2->sx = subx;
rd2->sy = suby;
rd2->sz = subz;
rd2->gx += ofx;
rd2->gz += ofz;
} else if(rayc_mark[idx2] != -1) {
raydata_t *rd2 = &(rayc_data[rayc_mark[idx2]-1]);
if(y1 < rd2->y1)
rd2->y1 = y1;
if(y2 > rd2->y2)
rd2->y2 = y2;
}
{
int t = ofx;
ofx = -ofz;
ofz = t;
}
} while(ofx != 1);
}
// render each face
render_vxl_face_horiz(blkx, blky, blkz, subx, suby, subz, CM_NX, -1, 0, 0);
render_vxl_face_vert(blkx, blky, blkz, subx, suby, subz, CM_NY, 0, -1, 0);
render_vxl_face_horiz(blkx, blky, blkz, subx, suby, subz, CM_NZ, 0, 0, -1);
render_vxl_face_horiz(blkx, blky, blkz, subx, suby, subz, CM_PX, 1, 0, 0);
render_vxl_face_vert(blkx, blky, blkz, subx, suby, subz, CM_PY, 0, 1, 0);
render_vxl_face_horiz(blkx, blky, blkz, subx, suby, subz, CM_PZ, 0, 0, 1);
render_vxl_face_raycast(blkx, blky, blkz, subx, suby, subz, CM_NX, -1, 0, 0);
render_vxl_face_raycast(blkx, blky, blkz, subx, suby, subz, CM_NY, 0, -1, 0);
render_vxl_face_raycast(blkx, blky, blkz, subx, suby, subz, CM_NZ, 0, 0, -1);
render_vxl_face_raycast(blkx, blky, blkz, subx, suby, subz, CM_PX, 1, 0, 0);
render_vxl_face_raycast(blkx, blky, blkz, subx, suby, subz, CM_PY, 0, 1, 0);
render_vxl_face_raycast(blkx, blky, blkz, subx, suby, subz, CM_PZ, 0, 0, 1);
}
void render_cubemap(uint32_t *pixels, int width, int height, int pitch, camera_t *camera, map_t *map)
@ -1062,10 +997,6 @@ int render_init(int width, int height)
size >>= 1;
}
// allocate space for FTB buffers
ftb_first = malloc(cubemap_size*sizeof(int));
// TODO: check if NULL
// allocate space for depth buffer
dbuf = malloc(width*height*sizeof(float));
// TODO: check if NULL
@ -1092,13 +1023,6 @@ void render_deinit(void)
}
}
// deallocate FTB buffers
if(ftb_first != NULL)
{
free(ftb_first);
ftb_first = NULL;
}
// deallocate depth buffer
if(dbuf != NULL)
{

1452
render.old1 Normal file

File diff suppressed because it is too large Load Diff