quakeforge/tools/Forge/Bundles/MapEdit/render.m
Bill Currie b9d21cbe9a Catch resizes in the camera view.
Avoids a segfault when resizing the window.
2011-04-16 18:58:05 +09:00

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);
}