/*
This file is part of Iceball.
Iceball is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Iceball is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Iceball. If not, see .
*/
#include "common.h"
//define DEBUG_INVERT_DRAW_DIR
// 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
#define DF_NZ 0x04
#define DF_PX 0x08
#define DF_PY 0x10
#define DF_PZ 0x20
#define DF_SPREAD 0x3F
enum
{
CM_NX = 0,
CM_NY,
CM_NZ,
CM_PX,
CM_PY,
CM_PZ,
CM_MAX
};
uint32_t *cubemap_color[CM_MAX];
float *cubemap_depth[CM_MAX];
int cubemap_size;
int cubemap_shift;
uint32_t *rtmp_pixels;
int rtmp_width, rtmp_height, rtmp_pitch;
camera_t *rtmp_camera;
map_t *rtmp_map;
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;
int g = (color>>8)&255;
int r = (color>>16)&255;
int t = (color>>24)&255;
float fog = (FOG_DISTANCE-(depth < 0.001f ? 0.001f : depth))/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);
}
void render_rect_clip(uint32_t *color, int *x1, int *y1, int *x2, int *y2, float depth)
{
*color = render_fog_apply(*color, depth);
// arrange *1 <= *2
if(*x1 > *x2)
{
int t = *x1;
*x1 = *x2;
*x2 = t;
}
if(*y1 > *y2)
{
int t = *y1;
*y1 = *y2;
*y2 = t;
}
// clip
if(*x1 < 0)
*x1 = 0;
if(*y1 < 0)
*y1 = 0;
if(*x2 > cubemap_size)
*x2 = cubemap_size;
if(*y2 > cubemap_size)
*y2 = cubemap_size;
}
void render_rect_clip_screen(uint32_t *color, int *x1, int *y1, int *x2, int *y2, float depth)
{
*color = render_fog_apply(*color, depth);
// arrange *1 <= *2
if(*x1 > *x2)
{
int t = *x1;
*x1 = *x2;
*x2 = t;
}
if(*y1 > *y2)
{
int t = *y1;
*y1 = *y2;
*y2 = t;
}
// clip
if(*x1 < 0)
*x1 = 0;
if(*y1 < 0)
*y1 = 0;
if(*x2 > rtmp_width)
*x2 = rtmp_width;
if(*y2 > rtmp_height)
*y2 = rtmp_height;
}
void render_rect_zbuf(uint32_t *ccolor, float *cdepth, int x1, int y1, int x2, int y2, uint32_t color, float depth)
{
int x,y;
// clip
render_rect_clip_screen(&color, &x1, &y1, &x2, &y2, depth);
//uint32_t dummy;
//render_rect_clip_screen(&dummy, &x1, &y1, &x2, &y2, depth);
if(x2 <= 0)
return;
if(x1 >= rtmp_width)
return;
if(y2 <= 0)
return;
if(y1 >= rtmp_height)
return;
if(x1 == x2)
return;
if(y1 == y2)
return;
// render
uint32_t *cptr = &ccolor[y1*rtmp_pitch+x1];
float *dptr = &cdepth[y1*rtmp_width+x1];
int stride = x2-x1;
int pitch = rtmp_pitch - stride;
int dpitch = rtmp_width - stride;
for(y = y1; y < y2; y++)
{
for(x = x1; x < x2; x++)
{
if(*dptr > depth)
{
*dptr = depth;
*cptr = color;
}
cptr++; dptr++;
}
dptr += dpitch;
cptr += pitch;
}
}
// TODO: fast ver?
void render_vxl_rect_ftb_fast(uint32_t *ccolor, float *cdepth, int x1, int y1, int x2, int y2, uint32_t color, float depth)
//void render_vxl_rect_ftb_slow(uint32_t *ccolor, float *cdepth, int x1, int y1, int x2, int y2, uint32_t color, float depth)
{
int x,y;
// TODO: stop using this bloody function
// (alternatively, switch to the fast FTB as used in Doom and Quake)
//
// NOTE: this approach seems to be faster than render_vxl_rect_btf.
// clip
uint32_t dummy;
render_rect_clip(&dummy, &x1, &y1, &x2, &y2, depth);
if(x2 <= 0)
return;
if(x1 >= cubemap_size)
return;
if(y2 <= 0)
return;
if(y1 >= cubemap_size)
return;
if(x1 == x2)
return;
if(y1 == y2)
return;
// render
uint32_t *cptr = &ccolor[(y1<>1);
int x3 = ((x1-hsize)*depth)/(depth+0.5f)+hsize;
int y3 = ((y1-hsize)*depth)/(depth+0.5f)+hsize;
int x4 = ((x2-hsize)*depth)/(depth+0.5f)+hsize;
int y4 = ((y2-hsize)*depth)/(depth+0.5f)+hsize;
// TODO: replace these with trapezium drawing routines
if(x3 < x1)
render_vxl_rect_ftb_fast(ccolor, cdepth,
(int)x3, (int)y3, (int)x1, (int)y4,
color, depth);
else if(x2 < x4)
render_vxl_rect_ftb_fast(ccolor, cdepth,
(int)x2, (int)y3, (int)x4, (int)y4,
color, depth);
if(y3 < y1)
render_vxl_rect_ftb_fast(ccolor, cdepth,
(int)x3, (int)y3, (int)x4, (int)y1,
color, depth);
else if(y2 < y4)
render_vxl_rect_ftb_fast(ccolor, cdepth,
(int)x3, (int)y2, (int)x4, (int)y4,
color, depth);
}
void render_vxl_cube(uint32_t *ccolor, float *cdepth, int x1, int y1, int x2, int y2, uint32_t color, float depth)
{
render_vxl_rect_ftb_fast(ccolor, cdepth, x1, y1, x2, y2, color, depth);
render_vxl_cube_sides(ccolor, cdepth, x1, y1, x2, y2, color, depth);
}
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)
{
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;
}
// get X cube direction
int xgx = gz+gy;
int xgy = 0;
int xgz = -gx;
// get Y cube direction
int ygx = 0;
int ygy = fabsf(gx+gz);
int ygz = gy;
// get base pos
float bx = blkx+subx;
float by = blky+suby;
float bz = blkz+subz;
if(xgx+xgy+xgz < 0)
{
bx += xgx;
by += xgy;
bz += xgz;
}
// 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
{
// get block delta
float dx = b->x - bx;
float dy = b->y - by;
float dz = b->z - bz;
// 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;
// check distance
if(sz < 0.001f || sz >= FOG_DISTANCE)
continue;
// frustum cull
if(fabsf(sx) > fabsf(sz+2.0f) || fabsf(sy) > fabsf(sz+2.0f))
continue;
// draw
float boxsize = tracemul/fabsf(sz);
float px1 = sx*boxsize+traceadd;
float py1 = sy*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,
xcolor, sz);
}
}
void render_vxl_redraw(camera_t *camera, map_t *map)
{
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)) & (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_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)
{
int x,y,z;
// stash stuff in globals to prevent spamming the stack too much
// (and in turn thrashing the cache)
rtmp_pixels = pixels;
rtmp_width = width;
rtmp_height = height;
rtmp_pitch = pitch;
rtmp_camera = camera;
rtmp_map = map;
// get corner traces
float tracemul = cubemap_size/2;
float traceadd = tracemul;
float ctrx1 = (camera->mzx+camera->mxx-camera->myx);
float ctry1 = (camera->mzy+camera->mxy-camera->myy);
float ctrz1 = (camera->mzz+camera->mxz-camera->myz);
float ctrx2 = (camera->mzx-camera->mxx-camera->myx);
float ctry2 = (camera->mzy-camera->mxy-camera->myy);
float ctrz2 = (camera->mzz-camera->mxz-camera->myz);
float ctrx3 = (camera->mzx+camera->mxx+camera->myx);
float ctry3 = (camera->mzy+camera->mxy+camera->myy);
float ctrz3 = (camera->mzz+camera->mxz+camera->myz);
float ctrx4 = (camera->mzx-camera->mxx+camera->myx);
float ctry4 = (camera->mzy-camera->mxy+camera->myy);
float ctrz4 = (camera->mzz-camera->mxz+camera->myz);
// calculate deltas
float fbx = ctrx1, fby = ctry1, fbz = ctrz1; // base
float fex = ctrx2, fey = ctry2, fez = ctrz2; // end
float flx = ctrx3-fbx, fly = ctry3-fby, flz = ctrz3-fbz; // left side
float frx = ctrx4-fex, fry = ctry4-fey, frz = ctrz4-fez; // right side
flx /= (float)width; fly /= (float)width; flz /= (float)width;
frx /= (float)width; fry /= (float)width; frz /= (float)width;
// scale cubemap correctly
fbx += flx*((float)(width-height))/2.0f;
fby += fly*((float)(width-height))/2.0f;
fbz += flz*((float)(width-height))/2.0f;
fex += frx*((float)(width-height))/2.0f;
fey += fry*((float)(width-height))/2.0f;
fez += frz*((float)(width-height))/2.0f;
// raytrace it
// TODO: find some faster method
uint32_t *p = pixels;
float *d = dbuf;
int hwidth = width/2;
int hheight = height/2;
for(y = -hheight; y < hheight; y++)
{
float fx = fbx;
float fy = fby;
float fz = fbz;
float fdx = (fex-fbx)/(float)width;
float fdy = (fey-fby)/(float)width;
float fdz = (fez-fbz)/(float)width;
for(x = -hwidth; x < hwidth; x++)
{
int pidx, pmap;
// get correct cube map + pos
if(fabsf(fx) > fabsf(fy) && fabsf(fx) > fabsf(fz))
{
pidx = ((cubemap_size-1)&(int)(-fz*tracemul/fx+traceadd))
|(((cubemap_size-1)&(int)(fy*tracemul/fabsf(fx)+traceadd))<= 0.0f ? CM_PX : CM_NX;
} else if(fabsf(fz) > fabsf(fy) && fabsf(fz) > fabsf(fx)) {
pidx = ((cubemap_size-1)&(int)(fx*tracemul/fz+traceadd))
|(((cubemap_size-1)&(int)(fy*tracemul/fabsf(fz)+traceadd))<= 0.0f ? CM_PZ : CM_NZ;
} else {
pidx = ((cubemap_size-1)&(int)(fx*tracemul/fy+traceadd))
|(((cubemap_size-1)&(int)(fz*tracemul/fy+traceadd))<= 0.0f ? CM_PY : CM_NY;
}
*(p++) = cubemap_color[pmap][pidx];
*(d++) = cubemap_depth[pmap][pidx];//*sqrtf(fx*fx+fy*fy+fz*fz);
fx += fdx;
fy += fdy;
fz += fdz;
}
p += pitch-width;
fbx += flx;
fby += fly;
fbz += flz;
fex += frx;
fey += fry;
fez += frz;
}
/*
// TEST: draw something
for(x = 0; x < 512; x++)
for(y = 0; y < 512; y++)
{
pixels[y*pitch+x] = *(uint32_t *)&(map->pillars[y*map->xlen+x][8]);
//pixels[y*pitch+x] = cubemap_color[CM_PZ][y*cubemap_size+x];
}*/
}
void render_pmf_box(float x, float y, float z, float depth, float r, uint32_t color)
{
// check Z straight away
if(z < 0.001f)
return;
// get box
int x1 = (( x-r)/z)*rtmp_width/2+rtmp_width/2;
int y1 = (( y-r)/z)*rtmp_width/2+rtmp_height/2;
int x2 = (( x+r)/z)*rtmp_width/2+rtmp_width/2;
int y2 = (( y+r)/z)*rtmp_width/2+rtmp_height/2;
// render
render_rect_zbuf(rtmp_pixels, dbuf, x1, y1, x2, y2, color, depth);
}
void render_pmf_bone(uint32_t *pixels, int width, int height, int pitch, camera_t *cam_base,
model_bone_t *bone, int islocal,
float px, float py, float pz, float ry, float rx, float scale)
{
// stash stuff in globals to prevent spamming the stack too much
// (and in turn thrashing the cache)
rtmp_pixels = pixels;
rtmp_width = width;
rtmp_height = height;
rtmp_pitch = pitch;
rtmp_camera = cam_base;
scale /= 256.0f;
int i;
for(i = 0; i < bone->ptlen; i++)
{
model_point_t *pt = &(bone->pts[i]);
// get color
uint32_t color = (pt->b)|(pt->g<<8)|(pt->r<<16)|(1<<24);
// get position
float x = pt->x;
float y = pt->y;
float z = pt->z;
// rotate
float sry = sin(ry);
float cry = cos(ry);
float srx = sin(rx);
float crx = cos(rx);
float tx = (x*cry+z*sry);
float ty = y;
float tz = (z*cry-x*sry);
x = tx;
y = (ty*crx-tz*srx);
z = (tz*crx+ty*srx);
// scalinate
x *= scale;
y *= scale;
z *= scale;
// offsettate
x += px;
y += py;
z += pz;
if(!islocal)
{
x -= cam_base->mpx;
y -= cam_base->mpy;
z -= cam_base->mpz;
}
// get correct centre depth
float max_axis = fabsf(x);
if(max_axis < fabsf(y))
max_axis = fabsf(y);
if(max_axis < fabsf(z))
max_axis = fabsf(z);
float dlen = sqrtf(x*x+y*y+z*z);
float depth = max_axis/dlen;
// cameranananinate
if(!islocal)
{
float nx = x*cam_base->mxx+y*cam_base->mxy+z*cam_base->mxz;
float ny = x*cam_base->myx+y*cam_base->myy+z*cam_base->myz;
float nz = x*cam_base->mzx+y*cam_base->mzy+z*cam_base->mzz;
x = nx;
y = ny;
z = nz;
}
depth *= z;
// plotinate
render_pmf_box(-x, y, z, depth, pt->radius*scale, color);
}
}
void render_blit_img(uint32_t *pixels, int width, int height, int pitch,
img_t *src, int dx, int dy, int bw, int bh, int sx, int sy, uint32_t color)
{
int x,y;
// clip blit width/height
if(bw > src->head.width)
bw = src->head.width;
if(bh > src->head.height)
bh = src->head.height;
// drop if completely out of range
if(dx >= width || dy >= height)
return;
if(dx+bw <= 0 || dy+bh <= 0)
return;
// top-left clip
if(dx < 0)
{
sx += -dx;
bw += -dx;
dx = 0;
}
if(dy < 0)
{
sy += -dy;
bh += -dy;
dy = 0;
}
// bottom-right clip
if(dx+bw > width)
bw = width-dx;
if(dy+bh > height)
bh = height-dy;
// drop if width/height sucks
if(bw <= 0 || bh <= 0)
return;
// get pointers
uint32_t *ps = src->pixels;
ps = &ps[sx+sy*src->head.width];
uint32_t *pd = &(pixels[dx+dy*pitch]);
int spitch = src->head.width - bw;
int dpitch = pitch - bw;
// now blit!
for(y = 0; y < bh; y++)
{
for(x = 0; x < bw; x++)
{
// TODO: MMX/SSE2 version
uint32_t s = *(ps++);
uint32_t d = *pd;
// apply base color
// DANGER! BRACKETITIS!
s = (((s&0xFF)*((color&0xFF))>>8)
| ((((s>>8)&0xFF)*(((color>>8)&0xFF)+1))&0xFF00)
| ((((s>>8)&0xFF00)*(((color>>16)&0xFF)+1))&0xFF0000)
| ((((s>>8)&0xFF0000)*(((color>>24)&0xFF)+1))&0xFF000000)
);
uint32_t alpha = (s >> 24);
if(alpha >= 0x80) alpha++;
uint32_t ialpha = 0x100 - alpha;
uint32_t sa = s & 0x00FF00FF;
uint32_t sb = s & 0x0000FF00;
uint32_t da = d & 0x00FF00FF;
uint32_t db = d & 0x0000FF00;
sa *= alpha;
sb *= alpha;
da *= ialpha;
db *= ialpha;
//printf("%i %i\n", alpha, ialpha);
uint32_t va = ((sa + da)>>8) & 0x00FF00FF;
uint32_t vb = ((sb + db)>>8) & 0x0000FF00;
*(pd++) = va + vb;
}
ps += spitch;
pd += dpitch;
}
}
int render_init(int width, int height)
{
int i;
int size = (width > height ? width : height);
// get nearest power of 2
size = (size-1);
size |= size>>1;
size |= size>>2;
size |= size>>4;
size |= size>>8;
size++;
int msize = size;
// reduce quality a little bit
// 800x600 -> 1024^2 -> 512^2 ends up as 1MB x 6 textures = 6MB
size >>= 1;
// allocate cubemaps
for(i = 0; i < CM_MAX; i++)
{
cubemap_color[i] = malloc(size*size*4);
cubemap_depth[i] = malloc(size*size*4);
if(cubemap_color[i] == NULL || cubemap_depth[i] == NULL)
{
// Can't allocate :. Can't continue
// Clean up like a boss
fprintf(stderr, "render_init: could not allocate cubemap %i\n", i);
for(; i >= 0; i--)
{
if(cubemap_color[i] != NULL)
free(cubemap_color[i]);
if(cubemap_depth[i] != NULL)
free(cubemap_depth[i]);
cubemap_color[i] = NULL;
cubemap_depth[i] = NULL;
}
return 1;
}
}
// we might as well set this, too!
cubemap_size = size;
// calculate shift factor
cubemap_shift = -1;
while(size != 0)
{
cubemap_shift++;
size >>= 1;
}
// allocate space for depth buffer
dbuf = malloc(width*height*sizeof(float));
// TODO: check if NULL
return 0;
}
void render_deinit(void)
{
int i;
// deallocate cubemaps
for(i = 0; i < CM_MAX; i++)
{
if(cubemap_color[i] != NULL)
{
free(cubemap_color[i]);
cubemap_color[i] = NULL;
}
if(cubemap_depth[i] != NULL)
{
free(cubemap_depth[i]);
cubemap_depth[i] = NULL;
}
}
// deallocate depth buffer
if(dbuf != NULL)
{
free(dbuf);
dbuf = NULL;
}
}