mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-17 10:20:55 +00:00
b9d21cbe9a
Avoids a segfault when resizing the window.
681 lines
13 KiB
Objective-C
681 lines
13 KiB
Objective-C
#include "render.h"
|
|
|
|
extern vec3_t xy_viewnormal;
|
|
|
|
// 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;
|
|
|
|
memset (r_zbuffer, 0, size * sizeof (float));
|
|
memset (r_picbuffer, 0, size * sizeof (unsigned));
|
|
}
|
|
|
|
/*
|
|
====================
|
|
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;
|
|
int *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 = (int *) &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 rfrustum[5];
|
|
|
|
void
|
|
REN_BeginCamera (void)
|
|
{
|
|
r_width_2 = (float) r_width / 2;
|
|
r_height_3 = (float) r_height / 3;
|
|
|
|
// clip to right side
|
|
rfrustum[0].normal[0] = -1;
|
|
rfrustum[0].normal[1] = 0;
|
|
rfrustum[0].normal[2] = 1;
|
|
rfrustum[0].dist = 0;
|
|
|
|
// clip to left side
|
|
rfrustum[1].normal[0] = 1;
|
|
rfrustum[1].normal[1] = 0;
|
|
rfrustum[1].normal[2] = 1;
|
|
rfrustum[1].dist = 0;
|
|
|
|
// clip to top side
|
|
rfrustum[2].normal[0] = 0;
|
|
rfrustum[2].normal[1] = -1;
|
|
rfrustum[2].normal[2] = r_height_3 / r_width_2;
|
|
rfrustum[2].dist = 0;
|
|
|
|
// clip to bottom side
|
|
rfrustum[3].normal[0] = 0;
|
|
rfrustum[3].normal[1] = 1;
|
|
rfrustum[3].normal[2] = 2 * r_height_3 / r_width_2;
|
|
rfrustum[3].dist = 0;
|
|
|
|
// near Z
|
|
rfrustum[4].normal[0] = 0;
|
|
rfrustum[4].normal[1] = 0;
|
|
rfrustum[4].normal[2] = 1;
|
|
rfrustum[4].dist = 1;
|
|
}
|
|
|
|
void
|
|
REN_BeginXY (void)
|
|
{
|
|
rfrustum[0].normal[0] = 1;
|
|
rfrustum[0].normal[1] = 0;
|
|
rfrustum[0].normal[2] = 0;
|
|
rfrustum[0].dist = 0;
|
|
|
|
rfrustum[1].normal[0] = -1;
|
|
rfrustum[1].normal[1] = 0;
|
|
rfrustum[1].normal[2] = 0;
|
|
rfrustum[1].dist = -r_width;
|
|
|
|
rfrustum[2].normal[0] = 0;
|
|
rfrustum[2].normal[1] = 1;
|
|
rfrustum[2].normal[2] = 0;
|
|
rfrustum[2].dist = 0;
|
|
|
|
rfrustum[3].normal[0] = 0;
|
|
rfrustum[3].normal[1] = -1;
|
|
rfrustum[3].normal[2] = 0;
|
|
rfrustum[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, &rfrustum[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, &rfrustum[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);
|
|
}
|