quakeforge/tools/Forge/render.m

749 lines
12 KiB
Objective-C

#import "qedefs.h"
//define NOLIGHT
vec3_t r_origin, r_matrix[3];
int t_width, t_height;
unsigned *t_data;
int t_widthmask, t_heightmask, t_widthshift;
float t_widthadd, t_heightadd;
int r_width, r_height;
float *r_zbuffer;
unsigned *r_picbuffer;
vec5_t rightside, leftside, rightstep,leftstep;
face_t *r_face;
BOOL r_drawflat;
pixel32_t r_flatcolor;
int sy[20];
/*
====================
REN_ClearBuffers
====================
*/
void REN_ClearBuffers (void)
{
int size;
size = r_width * r_height*4;
memset (r_zbuffer, 0, size);
memset (r_picbuffer, 0, size);
}
/*
====================
REN_SetTexture
====================
*/
void REN_SetTexture (face_t *face)
{
int i;
int t_heightshift;
qtexture_t *q;
if (!face->qtexture)
face->qtexture = TEX_ForName (face->texture.texture); // try to load
q = face->qtexture;
t_width = q->width;
t_height = q->height;
t_data = q->data;
r_flatcolor = q->flatcolor;
r_flatcolor.chan[0] *= r_face->light;
r_flatcolor.chan[1] *= r_face->light;
r_flatcolor.chan[2] *= r_face->light;
t_widthadd = t_width*1024;
t_heightadd = t_height*1024;
t_widthmask = t_width-1;
t_heightmask = t_height-1;
t_widthshift = 0;
i = t_width;
while (i >= 2)
{
t_widthshift++;
i>>=1;
}
t_heightshift = 0;
i = t_width;
while (i >= 2)
{
t_heightshift++;
i>>=1;
}
if ( (1<<t_widthshift) != t_width || (1<<t_heightshift) != t_height)
t_widthshift = t_heightshift = 0; // non power of two
}
/*
==================
REN_DrawSpan
==================
*/
void REN_DrawSpan (int y)
{
int x, count;
int ofs;
int tx, ty;
int x1, x2;
float ufrac, vfrac, zfrac, lightfrac, ustep, vstep, zstep;
pixel32_t *in, *out;
float scale;
if (y<0 || y >= r_height)
return;
x1 = (leftside[0]);
x2 = (rightside[0]);
count = x2 - x1;
if (count < 0)
return;
zfrac = leftside[2];
ufrac = leftside[3];
vfrac = leftside[4];
lightfrac = r_face->light;
if (!count)
scale = 1;
else
scale = 1.0/count;
zstep = (rightside[2] - zfrac)*scale;
ustep = (rightside[3] - ufrac)*scale;
vstep = (rightside[4] - vfrac)*scale;
if (x1 < 0)
{
ufrac -= x1*ustep;
vfrac -= x1*vstep;
zfrac -= x1*zstep;
x1 = 0;
}
if (x2 > r_width)
x2 = r_width;
ofs = y*r_width+x1;
// this should be specialized for 1.0 / 0.5 / 0.75 light levels
for (x=x1 ; x < x2 ; x++)
{
if (r_zbuffer[ofs] <= zfrac)
{
scale = 1/zfrac;
r_zbuffer[ofs] = zfrac;
if (t_widthshift)
{
tx = (int)((ufrac*scale)) & t_widthmask;
ty = (int)((vfrac*scale)) & t_heightmask;
in = (pixel32_t *)&t_data [(ty<<t_widthshift)+tx];
}
else
{
tx = (int)((ufrac*scale)+t_widthadd) % t_width;
ty = (int)((vfrac*scale)+t_heightadd) % t_height;
in = (pixel32_t *)&t_data [ty*t_width+tx];
}
out = (pixel32_t *)&r_picbuffer[ofs];
#ifdef NOLIGHT
*out = *in;
#else
out->chan[0] = in->chan[0]*lightfrac;
out->chan[1] = in->chan[1]*lightfrac;
out->chan[2] = in->chan[2]*lightfrac;
out->chan[3] = 0xff;
#endif
}
ufrac += ustep;
vfrac += vstep;
zfrac += zstep;
ofs++;
}
}
/*
==================
REN_DrawFlatSpan
==================
*/
void REN_DrawFlatSpan (int y)
{
int x, count;
int ofs;
int x1, x2;
float zfrac, zstep;
pixel32_t *out;
if (y<0 || y >= r_height)
return;
x1 = (leftside[0]);
x2 = (rightside[0]);
count = x2 - x1;
if (count < 0)
return;
zfrac = leftside[2];
zstep = (rightside[2] - zfrac)/count;
if (x1 < 0)
{
zfrac -= x1*zstep;
x1 = 0;
}
if (x2 > r_width)
x2 = r_width;
ofs = y*r_width+x1;
// this should be specialized for 1.0 / 0.5 / 0.75 light levels
for (x=x1 ; x < x2 ; x++)
{
if (r_zbuffer[ofs] <= zfrac)
{
r_zbuffer[ofs] = zfrac;
out = (pixel32_t *)&r_picbuffer[ofs];
*out = r_flatcolor.p;
}
zfrac += zstep;
ofs++;
}
}
/*
=====================
REN_RasterizeFace
=====================
*/
void REN_RasterizeFace (winding_t *w)
{
int y;
int i;
int top, bot;
int leftv, rightv;
int count;
int numvertex;
//
// find top vertex
//
numvertex = w->numpoints;
top = 0x7fffffff;
bot = 0x80000000;
leftv = 0;
for (i=0 ; i<numvertex ; i++)
{
w->points[i][3] *= w->points[i][2];
w->points[i][4] *= w->points[i][2];
sy[i] = (int)w->points[i][1];
if (sy[i] < top)
{
top = sy[i];
leftv = i;
}
if (sy[i] > bot)
bot = sy[i];
}
rightv = leftv;
if (top < 0 || bot > r_height || top > bot)
return; // shouldn't have to have this...
//
// render a trapezoid
//
y = top;
while (y < bot)
{
if (y >= sy[leftv])
{
do
{
for (i=0 ; i<5 ; i++)
leftside[i] = w->points[leftv][i];
leftv--;
if (leftv == -1)
leftv = numvertex-1;
} while (sy[leftv] <= y);
count = sy[leftv]-y;
for (i=0 ; i<5 ; i++)
leftstep[i] = (w->points[leftv][i] - leftside[i])/count;
}
if (y >= sy[rightv])
{
do
{
for (i=0 ; i<5 ; i++)
rightside[i] = w->points[rightv][i];
rightv++;
if (rightv == numvertex)
rightv = 0;
} while (sy[rightv] <= y);
count = sy[rightv]-y;
for (i=0 ; i<5 ; i++)
rightstep[i] = (w->points[rightv][i] - rightside[i])/count;
}
if (r_drawflat)
REN_DrawFlatSpan (y);
else
REN_DrawSpan (y);
for (i=0 ; i<5 ; i++)
{
leftside[i] += leftstep[i];
rightside[i] += rightstep[i];
}
y++;
}
}
//=============================================================================
/*
==================
REN_DrawSpanLinear
==================
*/
void REN_DrawSpanLinear (int y)
{
int x, count;
int ofs;
int tx, ty;
int x1, x2;
float ufrac, vfrac, zfrac, ustep, vstep, zstep;
pixel32_t *in, *out;
float scale;
if (y<0 || y >= r_height)
return;
x1 = (leftside[0]);
x2 = (rightside[0]);
count = x2 - x1;
if (count < 0)
return;
zfrac = leftside[2];
ufrac = leftside[3];
vfrac = leftside[4];
if (!count)
scale = 1;
else
scale = 1.0/count;
zstep = (rightside[2] - zfrac)*scale;
ustep = (rightside[3] - ufrac)*scale;
vstep = (rightside[4] - vfrac)*scale;
if (x1 < 0)
{
ufrac -= x1*ustep;
vfrac -= x1*vstep;
zfrac -= x1*zstep;
x1 = 0;
}
if (x2 > r_width)
x2 = r_width;
ofs = y*r_width+x1;
for (x=x1 ; x < x2 ; x++)
{
if (r_zbuffer[ofs] <= zfrac)
{
r_zbuffer[ofs] = zfrac;
if (t_widthshift)
{
tx = (int)ufrac & t_widthmask;
ty = (int)vfrac & t_heightmask;
in = (pixel32_t *)&t_data [(ty<<t_widthshift)+tx];
}
else
{
tx = (int)(ufrac+t_widthadd) % t_width;
ty = (int)(vfrac+t_heightadd) % t_height;
in = (pixel32_t *)&t_data [ty*t_width+tx];
}
out = (pixel32_t *)&r_picbuffer[ofs];
*out = *in;
}
ufrac += ustep;
vfrac += vstep;
zfrac += zstep;
ofs++;
}
}
/*
=====================
REN_RasterizeFaceLinear
=====================
*/
void REN_RasterizeFaceLinear (winding_t *w)
{
int y;
int i;
int top, bot;
int leftv, rightv;
int count;
int numvertex;
//
// find top vertex
//
numvertex = w->numpoints;
top = 0x7fffffff;
bot = 0x80000000;
leftv = 0;
for (i=0 ; i<numvertex ; i++)
{
sy[i] = (int)w->points[i][1];
if (sy[i] < top)
{
top = sy[i];
leftv = i;
}
if (sy[i] > bot)
bot = sy[i];
}
rightv = leftv;
if (top < 0 || bot > r_height || top > bot)
return; // shouldn't have to have this...
//
// render a trapezoid
//
y = top;
while (y < bot)
{
if (y >= sy[leftv])
{
do
{
for (i=0 ; i<5 ; i++)
leftside[i] = w->points[leftv][i];
leftv--;
if (leftv == -1)
leftv = numvertex-1;
} while (sy[leftv] <= y);
count = sy[leftv]-y;
for (i=0 ; i<5 ; i++)
leftstep[i] = (w->points[leftv][i] - leftside[i])/count;
}
if (y >= sy[rightv])
{
do
{
for (i=0 ; i<5 ; i++)
rightside[i] = w->points[rightv][i];
rightv++;
if (rightv == numvertex)
rightv = 0;
} while (sy[rightv] <= y);
count = sy[rightv]-y;
for (i=0 ; i<5 ; i++)
rightstep[i] = (w->points[rightv][i] - rightside[i])/count;
}
REN_DrawSpanLinear (y);
for (i=0 ; i<5 ; i++)
{
leftside[i] += leftstep[i];
rightside[i] += rightstep[i];
}
y++;
}
}
//============================================================================
/*
==================
REN_BeginCamera
===================
*/
float r_width_2, r_height_3;
plane_t frustum[5];
void REN_BeginCamera (void)
{
r_width_2 = (float)r_width / 2;
r_height_3 = (float)r_height / 3;
// clip to right side
frustum[0].normal[0] = -1;
frustum[0].normal[1] = 0;
frustum[0].normal[2] = 1;
frustum[0].dist = 0;
// clip to left side
frustum[1].normal[0] = 1;
frustum[1].normal[1] = 0;
frustum[1].normal[2] = 1;
frustum[1].dist = 0;
// clip to top side
frustum[2].normal[0] = 0;
frustum[2].normal[1] = -1;
frustum[2].normal[2] = r_height_3 / r_width_2;
frustum[2].dist = 0;
// clip to bottom side
frustum[3].normal[0] = 0;
frustum[3].normal[1] = 1;
frustum[3].normal[2] = 2*r_height_3 / r_width_2;
frustum[3].dist = 0;
// near Z
frustum[4].normal[0] = 0;
frustum[4].normal[1] = 0;
frustum[4].normal[2] = 1;
frustum[4].dist = 1;
}
void REN_BeginXY (void)
{
frustum[0].normal[0] = 1;
frustum[0].normal[1] = 0;
frustum[0].normal[2] = 0;
frustum[0].dist = 0;
frustum[1].normal[0] = -1;
frustum[1].normal[1] = 0;
frustum[1].normal[2] = 0;
frustum[1].dist = -r_width;
frustum[2].normal[0] = 0;
frustum[2].normal[1] = 1;
frustum[2].normal[2] = 0;
frustum[2].dist = 0;
frustum[3].normal[0] = 0;
frustum[3].normal[1] = -1;
frustum[3].normal[2] = 0;
frustum[3].dist = -r_height;
}
/*
=====================
REN_DrawCameraFace
=====================
*/
void REN_DrawCameraFace (face_t *idpol)
{
int i;
float scale;
int numvertex;
winding_t *w, *in;
vec3_t temp;
if (!idpol->w)
return; // overconstrained plane
r_face = idpol;
//
// back face cull
//
if (DotProduct (r_origin, idpol->plane.normal) <= idpol->plane.dist)
return;
//
// transform in 3D (FIXME: clip first, then transform)
//
in = idpol->w;
numvertex = in->numpoints;
w = NewWinding (numvertex);
w->numpoints = numvertex;
for (i=0 ; i<numvertex ; i++)
{
VectorSubtract (in->points[i], r_origin, temp);
w->points[i][0] = DotProduct(temp,r_matrix[0]);
w->points[i][1] = DotProduct(temp,r_matrix[1]);
w->points[i][2] = DotProduct(temp,r_matrix[2]);
w->points[i][3] = in->points[i][3];
w->points[i][4] = in->points[i][4];
}
//
// 3D clip
//
for (i=0 ; i<4 ; i++)
{
w = ClipWinding (w, &frustum[i]);
if (!w)
return;
}
//
// project to 2D
//
for (i=0 ; i<w->numpoints ; i++)
{
scale = r_width_2 / w->points[i][2];
w->points[i][0] = r_width_2 + scale*w->points[i][0];
w->points[i][1] = r_height_3 - scale*w->points[i][1];
w->points[i][2] = scale;
}
//
// draw it
//
REN_SetTexture (idpol);
REN_RasterizeFace (w);
free (w);
}
/*
=====================
REN_DrawXYFace
=====================
*/
void REN_DrawXYFace (face_t *idpol)
{
int i, j, numvertex;
winding_t *w, *in;
float *dest, *source;
float temp;
if (!idpol->w)
return; // overconstrained plane
w = idpol->w;
r_face = idpol;
//
// back (and side) face cull
//
if (DotProduct (idpol->plane.normal, xy_viewnormal) > -VECTOR_EPSILON)
return;
//
// transform
//
in = idpol->w;
numvertex = in->numpoints;
w = NewWinding (numvertex);
w->numpoints = numvertex;
for (i=0 ; i<numvertex ; i++)
{
// using Z as a scale for the 2D projection
w->points[i][0] = (in->points[i][0] - r_origin[0])*r_origin[2];
w->points[i][1] = r_height - (in->points[i][1] - r_origin[1])*r_origin[2];
w->points[i][2] = in->points[i][2] + 3000;
w->points[i][3] = in->points[i][3];
w->points[i][4] = in->points[i][4];
}
//
// clip
//
for (i=0 ; i<4 ; i++)
{
w = ClipWinding (w, &frustum[i]);
if (!w)
return;
}
//
// project to 2D
//
for (i=0 ; i<w->numpoints ; i++)
{
dest = w->points[i];
if (dest[0] < 0)
dest[0] = 0;
if (dest[0] > r_width)
dest[0] = r_width;
if (dest[1] < 0)
dest[1] = 0;
if (dest[1] > r_height)
dest[1] = r_height;
if (xy_viewnormal[2] > 0)
dest[2] = 4096-dest[2];
}
if (xy_viewnormal[2] > 0)
{ // flip order when upside down
for (i=0 ; i<w->numpoints/2 ; i++)
{
dest = w->points[i];
source = w->points[w->numpoints-1-i];
for (j=0 ; j<5 ; j++)
{
temp = dest[j];
dest[j] = source[j];
source[j] = temp;
}
}
}
REN_SetTexture (idpol);
//
// draw it
//
REN_RasterizeFaceLinear (w);
free (w);
}