quakeforge/libs/video/renderer/gl/gl_rsurf.c
Bill Currie 9514ad1ae4 [gl] Draw sky chains before brush entities
Drawing sky chains first ensures that sky surfaces correctly block parts
of the map that should not be visible (by writing the correct depth to
the depth buffer when doing box or dome skies). Writing brush models
first means that the models (ammo boxes etc) could be visible when they
should not be.
2022-03-07 08:40:39 +09:00

917 lines
22 KiB
C

/*
gl_rsurf.c
surface-related refresh code
Copyright (C) 1996-1997 Id Software, Inc.
Copyright (C) 2000 Joseph Carter <knghtbrd@debian.org>
This program 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 2
of the License, or (at your option) any later version.
This program 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 this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#define NH_DEFINE
#include "namehack.h"
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include "qfalloca.h"
#include <math.h>
#include <stdio.h>
#include "QF/cvar.h"
#include "QF/render.h"
#include "QF/sys.h"
#include "QF/scene/entity.h"
#include "QF/GL/defines.h"
#include "QF/GL/funcs.h"
#include "QF/GL/qf_lightmap.h"
#include "QF/GL/qf_rmain.h"
#include "QF/GL/qf_rsurf.h"
#include "QF/GL/qf_sky.h"
#include "QF/GL/qf_textures.h"
#include "QF/GL/qf_vid.h"
#include "compat.h"
#include "r_internal.h"
static instsurf_t *waterchain = NULL;
static instsurf_t **waterchain_tail = &waterchain;
static instsurf_t *sky_chain;
static instsurf_t **sky_chain_tail;
typedef struct glbspctx_s {
mod_brush_t *brush;
entity_t *entity;
vec_t *transform;
float *color;
} glbspctx_t;
#define CHAIN_SURF_F2B(surf,chain) \
({ \
instsurf_t *inst = (surf)->instsurf; \
if (__builtin_expect(!inst, 1)) \
inst = get_instsurf (); \
inst->surface = (surf); \
*(chain##_tail) = inst; \
(chain##_tail) = &inst->tex_chain; \
*(chain##_tail) = 0; \
inst; \
})
#define CHAIN_SURF_B2F(surf,chain) \
({ \
instsurf_t *inst = (surf)->instsurf; \
if (__builtin_expect(!inst, 1)) \
inst = get_instsurf (); \
inst->surface = (surf); \
inst->tex_chain = (chain); \
(chain) = inst; \
inst; \
})
static gltex_t **r_texture_chains;
static int r_num_texture_chains;
static int max_texture_chains;
static instsurf_t *static_chains;
static instsurf_t *free_instsurfs;
static instsurf_t *alloced_instsurfs;
static instsurf_t **alloced_instsurfs_tail = &alloced_instsurfs;
#define NUM_INSTSURFS (64 * 6) // most brush models are simple boxes.
static inline instsurf_t *
get_instsurf (void)
{
instsurf_t *instsurf;
if (!free_instsurfs) {
int i;
free_instsurfs = calloc (NUM_INSTSURFS, sizeof (instsurf_t));
for (i = 0; i < NUM_INSTSURFS - 1; i++)
free_instsurfs[i]._next = &free_instsurfs[i + 1];
}
instsurf = free_instsurfs;
free_instsurfs = instsurf->_next;
instsurf->_next = 0;
//build the chain of allocated instance surfaces so they can all be freed
//in one go
*alloced_instsurfs_tail = instsurf;
alloced_instsurfs_tail = &instsurf->_next;
return instsurf;
}
static inline void
release_instsurfs (void)
{
if (alloced_instsurfs) {
*alloced_instsurfs_tail = free_instsurfs;
free_instsurfs = alloced_instsurfs;
alloced_instsurfs = 0;
alloced_instsurfs_tail = &alloced_instsurfs;
}
}
void
gl_R_AddTexture (texture_t *tx)
{
int i;
if (r_num_texture_chains == max_texture_chains) {
max_texture_chains += 64;
r_texture_chains = realloc (r_texture_chains,
max_texture_chains * sizeof (gltex_t *));
for (i = r_num_texture_chains; i < max_texture_chains; i++)
r_texture_chains[i] = 0;
}
gltex_t *tex = tx->render;
r_texture_chains[r_num_texture_chains++] = tex;
tex->tex_chain = NULL;
tex->tex_chain_tail = &tex->tex_chain;
}
void
gl_R_InitSurfaceChains (mod_brush_t *brush)
{
if (static_chains)
free (static_chains);
static_chains = calloc (brush->nummodelsurfaces, sizeof (instsurf_t));
for (unsigned i = 0; i < brush->nummodelsurfaces; i++)
brush->surfaces[i].instsurf = static_chains + i;
release_instsurfs ();
}
void
gl_R_ClearTextures (void)
{
r_num_texture_chains = 0;
}
// BRUSH MODELS ===============================================================
static void
R_RenderFullbrights (void)
{
float *v;
int i, j;
glpoly_t *p;
instsurf_t *sc;
gltex_t *tex;
for (i = 0; i < r_num_texture_chains; i++) {
if (!(tex = r_texture_chains[i]) || !tex->gl_fb_texturenum)
continue;
qfglBindTexture (GL_TEXTURE_2D, tex->gl_fb_texturenum);
for (sc = tex->tex_chain; sc; sc = sc->tex_chain) {
if (sc->transform) {
qfglPushMatrix ();
qfglLoadMatrixf (sc->transform);
}
if (sc->color)
qfglColor4fv (sc->color);
for (p = sc->surface->polys; p; p = p->next) {
qfglBegin (GL_POLYGON);
for (j = 0, v = p->verts[0]; j < p->numverts;
j++, v += VERTEXSIZE)
{
qfglTexCoord2fv (&v[3]);
qfglVertex3fv (v);
}
qfglEnd ();
}
if (sc->transform)
qfglPopMatrix ();
if (sc->color)
qfglColor3ubv (color_white);
}
}
}
static inline void
R_RenderBrushPoly_3 (msurface_t *surf)
{
float *v;
int i;
gl_c_brush_polys++;
qfglBegin (GL_POLYGON);
v = surf->polys->verts[0];
for (i = 0; i < surf->polys->numverts; i++, v += VERTEXSIZE) {
qglMultiTexCoord2fv (gl_mtex_enum + 0, &v[3]);
qglMultiTexCoord2fv (gl_mtex_enum + 1, &v[5]);
qglMultiTexCoord2fv (gl_mtex_enum + 2, &v[3]);
qfglVertex3fv (v);
}
qfglEnd ();
}
static inline void
R_RenderBrushPoly_2 (msurface_t *surf)
{
float *v;
int i;
gl_c_brush_polys++;
qfglBegin (GL_POLYGON);
v = surf->polys->verts[0];
for (i = 0; i < surf->polys->numverts; i++, v += VERTEXSIZE) {
qglMultiTexCoord2fv (gl_mtex_enum + 0, &v[3]);
qglMultiTexCoord2fv (gl_mtex_enum + 1, &v[5]);
qfglVertex3fv (v);
}
qfglEnd ();
}
static inline void
R_RenderBrushPoly_1 (msurface_t *surf)
{
float *v;
int i;
gl_c_brush_polys++;
qfglBegin (GL_POLYGON);
v = surf->polys->verts[0];
for (i = 0; i < surf->polys->numverts; i++, v += VERTEXSIZE) {
qfglTexCoord2fv (&v[3]);
qfglVertex3fv (v);
}
qfglEnd ();
}
static inline void
R_AddToLightmapChain (glbspctx_t *bctx, msurface_t *surf, instsurf_t *sc)
{
int maps, smax, tmax;
glRect_t *theRect;
// add the poly to the proper lightmap chain
sc->lm_chain = gl_lightmap_polys[surf->lightmaptexturenum];
gl_lightmap_polys[surf->lightmaptexturenum] = sc;
// check for lightmap modification
for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++)
if (d_lightstylevalue[surf->styles[maps]] != surf->cached_light[maps])
goto dynamic;
if ((surf->dlightframe == r_framecount) || surf->cached_dlight) {
dynamic:
if (r_dynamic->int_val) {
gl_lightmap_modified[surf->lightmaptexturenum] = true;
theRect = &gl_lightmap_rectchange[surf->lightmaptexturenum];
if (surf->light_t < theRect->t) {
if (theRect->h)
theRect->h += theRect->t - surf->light_t;
theRect->t = surf->light_t;
}
if (surf->light_s < theRect->l) {
if (theRect->w)
theRect->w += theRect->l - surf->light_s;
theRect->l = surf->light_s;
}
smax = (surf->extents[0] >> 4) + 1;
tmax = (surf->extents[1] >> 4) + 1;
if ((theRect->w + theRect->l) < (surf->light_s + smax))
theRect->w = (surf->light_s - theRect->l) + smax;
if ((theRect->h + theRect->t) < (surf->light_t + tmax))
theRect->h = (surf->light_t - theRect->t) + tmax;
gl_R_BuildLightMap (bctx->entity->transform, bctx->brush, surf);
}
}
}
void
gl_R_DrawWaterSurfaces (void)
{
int i;
instsurf_t *s;
msurface_t *surf;
float wateralpha = max (vr_data.min_wateralpha, r_wateralpha->value);
if (!waterchain)
return;
// go back to the world matrix
qfglLoadMatrixf (gl_r_world_matrix);
if (wateralpha < 1.0) {
qfglDepthMask (GL_FALSE);
color_white[3] = wateralpha * 255;
qfglColor4ubv (color_white);
}
i = -1;
for (s = waterchain; s; s = s->tex_chain) {
gltex_t *tex;
surf = s->surface;
if (s->transform)
qfglLoadMatrixf (s->transform);
else
qfglLoadMatrixf (gl_r_world_matrix);
tex = surf->texinfo->texture->render;
if (i != tex->gl_texturenum) {
i = tex->gl_texturenum;
qfglBindTexture (GL_TEXTURE_2D, i);
}
GL_EmitWaterPolys (surf);
}
qfglLoadMatrixf (gl_r_world_matrix);
waterchain = NULL;
waterchain_tail = &waterchain;
if (wateralpha < 1.0) {
qfglDepthMask (GL_TRUE);
qfglColor3ubv (color_white);
}
}
static void
DrawTextureChains (int disable_blend, int do_bind)
{
int i;
instsurf_t *s;
msurface_t *surf;
gltex_t *tex;
if (gl_mtex_active_tmus >= 2) {
// Lightmaps
qglActiveTexture (gl_mtex_enum + 1);
qfglEnable (GL_TEXTURE_2D);
// Base Texture
qglActiveTexture (gl_mtex_enum + 0);
qfglTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
for (i = 0; i < r_num_texture_chains; i++) {
tex = r_texture_chains[i];
if (!tex)
continue;
qfglBindTexture (GL_TEXTURE_2D, tex->gl_texturenum);
if (tex->gl_fb_texturenum && gl_mtex_fullbright) {
qglActiveTexture (gl_mtex_enum + 2);
qfglEnable (GL_TEXTURE_2D);
qfglBindTexture (GL_TEXTURE_2D, tex->gl_fb_texturenum);
qglActiveTexture (gl_mtex_enum + 1);
for (s = tex->tex_chain; s; s = s->tex_chain) {
surf = s->surface;
if (s->transform) {
qfglPushMatrix ();
qfglLoadMatrixf (s->transform);
}
if (s->color && do_bind)
qfglColor4fv (s->color);
qfglBindTexture (GL_TEXTURE_2D, gl_lightmap_textures +
surf->lightmaptexturenum);
R_RenderBrushPoly_3 (surf);
if (s->transform)
qfglPopMatrix ();
if (s->color && do_bind)
qfglColor3ubv (color_white);
}
qglActiveTexture (gl_mtex_enum + 2);
qfglDisable (GL_TEXTURE_2D);
qglActiveTexture (gl_mtex_enum + 0);
} else {
qglActiveTexture (gl_mtex_enum + 1);
for (s = tex->tex_chain; s; s = s->tex_chain) {
surf = s->surface;
qfglBindTexture (GL_TEXTURE_2D, gl_lightmap_textures +
surf->lightmaptexturenum);
if (s->transform) {
qfglPushMatrix ();
qfglLoadMatrixf (s->transform);
}
if (s->color && do_bind)
qfglColor4fv (s->color);
R_RenderBrushPoly_2 (surf);
if (s->transform)
qfglPopMatrix ();
if (s->color && do_bind)
qfglColor3ubv (color_white);
}
qglActiveTexture (gl_mtex_enum + 0);
}
}
// Turn off lightmaps for other entities
qglActiveTexture (gl_mtex_enum + 1);
qfglDisable (GL_TEXTURE_2D);
// Reset mode for default TMU
qglActiveTexture (gl_mtex_enum + 0);
qfglTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
} else {
if (disable_blend)
qfglDisable (GL_BLEND);
for (i = 0; i < r_num_texture_chains; i++) {
tex = r_texture_chains[i];
if (!tex)
continue;
if (do_bind)
qfglBindTexture (GL_TEXTURE_2D, tex->gl_texturenum);
for (s = tex->tex_chain; s; s = s->tex_chain) {
if (s->transform) {
qfglPushMatrix ();
qfglLoadMatrixf (s->transform);
}
R_RenderBrushPoly_1 (s->surface);
if (s->transform)
qfglPopMatrix ();
if (s->color && do_bind)
qfglColor3ubv (color_white);
}
}
if (disable_blend)
qfglEnable (GL_BLEND);
}
}
static void
clear_texture_chains (void)
{
int i;
gltex_t *tex;
for (i = 0; i < r_num_texture_chains; i++) {
tex = r_texture_chains[i];
if (!tex)
continue;
tex->tex_chain = NULL;
tex->tex_chain_tail = &tex->tex_chain;
}
tex = r_notexture_mip->render;
tex->tex_chain = NULL;
tex->tex_chain_tail = &tex->tex_chain;
release_instsurfs ();
memset (gl_lightmap_polys, 0, sizeof (gl_lightmap_polys));
}
static inline void
chain_surface (glbspctx_t *bctx, msurface_t *surf)
{
instsurf_t *sc;
if (surf->flags & SURF_DRAWTURB) {
sc = CHAIN_SURF_B2F (surf, waterchain);
} else if (surf->flags & SURF_DRAWSKY) {
sc = CHAIN_SURF_F2B (surf, sky_chain);
} else {
texture_t *tx;
gltex_t *tex;
if (!surf->texinfo->texture->anim_total)
tx = surf->texinfo->texture;
else
tx = R_TextureAnimation (bctx->entity, surf);
tex = tx->render;
sc = CHAIN_SURF_F2B (surf, tex->tex_chain);
R_AddToLightmapChain (bctx, surf, sc);
}
sc->transform = bctx->transform;
sc->color = bctx->color;
}
void
gl_R_DrawBrushModel (entity_t *e)
{
float dot, radius;
msurface_t *surf;
qboolean rotated;
vec3_t mins, maxs;
mat4f_t worldMatrix;
model_t *model = e->renderer.model;
mod_brush_t *brush = &model->brush;
glbspctx_t bspctx = {
brush,
e,
e->renderer.full_transform,
e->renderer.colormod,
};
Transform_GetWorldMatrix (e->transform, worldMatrix);
if (worldMatrix[0][0] != 1 || worldMatrix[1][1] != 1
|| worldMatrix[2][2] != 1) {
rotated = true;
radius = model->radius;
#if 0 //QSG FIXME
if (e->scale != 1.0)
radius *= e->scale;
#endif
if (R_CullSphere (&worldMatrix[3][0], radius)) {//FIXME
return;
}
} else {
rotated = false;
VectorAdd (worldMatrix[3], model->mins, mins);
VectorAdd (worldMatrix[3], model->maxs, maxs);
#if 0 // QSG FIXME
if (e->scale != 1.0) {
VectorScale (mins, e->scale, mins);
VectorScale (maxs, e->scale, maxs);
}
#endif
if (R_CullBox (mins, maxs))
return;
}
VectorSubtract (r_refdef.viewposition, worldMatrix[3], modelorg);
if (rotated) {
vec4f_t temp = { modelorg[0], modelorg[1], modelorg[2], 0 };
modelorg[0] = dotf (temp, worldMatrix[0])[0];
modelorg[1] = dotf (temp, worldMatrix[1])[0];
modelorg[2] = dotf (temp, worldMatrix[2])[0];
}
// calculate dynamic lighting for bmodel if it's not an instanced model
if (brush->firstmodelsurface != 0 && r_dlight_lightmap->int_val) {
vec3_t lightorigin;
for (unsigned k = 0; k < r_maxdlights; k++) {
if ((r_dlights[k].die < vr_data.realtime)
|| (!r_dlights[k].radius))
continue;
VectorSubtract (r_dlights[k].origin, worldMatrix[3], lightorigin);
R_RecursiveMarkLights (brush, lightorigin, &r_dlights[k], k,
brush->nodes + brush->hulls[0].firstclipnode);
}
}
qfglPushMatrix ();
gl_R_RotateForEntity (e);
qfglGetFloatv (GL_MODELVIEW_MATRIX, e->renderer.full_transform);
qfglPopMatrix ();
surf = &brush->surfaces[brush->firstmodelsurface];
// draw texture
for (unsigned i = 0; i < brush->nummodelsurfaces; i++, surf++) {
// find which side of the node we are on
plane_t *plane = surf->plane;
dot = DotProduct (modelorg, plane->normal) - plane->dist;
// draw the polygon
if (((surf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) ||
(!(surf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) {
chain_surface (&bspctx, surf);
}
}
}
// WORLD MODEL ================================================================
static inline void
visit_leaf (mleaf_t *leaf)
{
// deal with model fragments in this leaf
if (leaf->efrags)
R_StoreEfrags (leaf->efrags);
}
static inline int
get_side (mnode_t *node)
{
// find which side of the node we are on
plane_t *plane = node->plane;
if (plane->type < 3)
return (modelorg[plane->type] - plane->dist) < 0;
return (DotProduct (modelorg, plane->normal) - plane->dist) < 0;
}
static inline void
visit_node (glbspctx_t *bctx, mnode_t *node, int side)
{
int c;
msurface_t *surf;
// sneaky hack for side = side ? SURF_PLANEBACK : 0;
side = (~side + 1) & SURF_PLANEBACK;
// draw stuff
if ((c = node->numsurfaces)) {
surf = bctx->brush->surfaces + node->firstsurface;
for (; c; c--, surf++) {
if (surf->visframe != r_visframecount)
continue;
// side is either 0 or SURF_PLANEBACK
if (side ^ (surf->flags & SURF_PLANEBACK))
continue; // wrong side
chain_surface (bctx, surf);
}
}
}
static inline int
test_node (mnode_t *node)
{
if (node->contents < 0)
return 0;
if (node->visframe != r_visframecount)
return 0;
if (R_CullBox (node->minmaxs, node->minmaxs + 3))
return 0;
return 1;
}
static void
R_VisitWorldNodes (glbspctx_t *bctx)
{
typedef struct {
mnode_t *node;
int side;
} rstack_t;
mod_brush_t *brush = bctx->brush;
rstack_t *node_ptr;
rstack_t *node_stack;
mnode_t *node;
mnode_t *front;
int side;
node = brush->nodes;
// +2 for paranoia
node_stack = alloca ((brush->depth + 2) * sizeof (rstack_t));
node_ptr = node_stack;
while (1) {
while (test_node (node)) {
side = get_side (node);
front = node->children[side];
if (test_node (front)) {
node_ptr->node = node;
node_ptr->side = side;
node_ptr++;
node = front;
continue;
}
if (front->contents < 0 && front->contents != CONTENTS_SOLID)
visit_leaf ((mleaf_t *) front);
visit_node (bctx, node, side);
node = node->children[!side];
}
if (node->contents < 0 && node->contents != CONTENTS_SOLID)
visit_leaf ((mleaf_t *) node);
if (node_ptr != node_stack) {
node_ptr--;
node = node_ptr->node;
side = node_ptr->side;
visit_node (bctx, node, side);
node = node->children[!side];
continue;
}
break;
}
if (node->contents < 0 && node->contents != CONTENTS_SOLID)
visit_leaf ((mleaf_t *) node);
}
void
gl_R_DrawWorld (void)
{
entity_t worldent;
glbspctx_t bctx = { };
memset (&worldent, 0, sizeof (worldent));
worldent.renderer.model = r_worldentity.renderer.model;
VectorCopy (r_refdef.viewposition, modelorg);
sky_chain = 0;
sky_chain_tail = &sky_chain;
if (!gl_sky_clip->int_val) {
gl_R_DrawSky ();
}
bctx.brush = &worldent.renderer.model->brush;
bctx.entity = &r_worldentity;
R_VisitWorldNodes (&bctx);
gl_R_CalcLightmaps ();
gl_R_DrawSkyChain (sky_chain);
if (r_drawentities->int_val) {
for (size_t i = 0; i < r_ent_queue->ent_queues[mod_brush].size; i++) { \
entity_t *ent = r_ent_queue->ent_queues[mod_brush].a[i]; \
gl_R_DrawBrushModel (ent);
}
}
if (!gl_Fog_GetDensity ()
|| (gl_fb_bmodels->int_val && gl_mtex_fullbright)
|| gl_mtex_active_tmus > 1) {
// we have enough active TMUs to render everything in one go
// or we're not doing fog
DrawTextureChains (1, 1);
if (gl_mtex_active_tmus <= 1)
gl_R_BlendLightmaps ();
if (gl_fb_bmodels->int_val && !gl_mtex_fullbright)
R_RenderFullbrights ();
} else {
if (gl_mtex_active_tmus > 1) {
// textures and lightmaps in one pass
// black fog
// no blending
gl_Fog_StartAdditive ();
DrawTextureChains (1, 1);
// buf = fTL + (1-f)0
// = fTL
} else {
// texture pass + lightmap pass
// no fog
// no blending
gl_Fog_DisableGFog ();
DrawTextureChains (1, 1);
// buf = T
// black fog
// blend: buf = zero, src (non-overbright)
// FIXME overbright broken?
gl_Fog_EnableGFog ();
gl_Fog_StartAdditive ();
gl_R_BlendLightmaps (); //leaves blending as As, 1-As
// buf = I*0 + buf*I
// = T*C
// = T(fL + (1-f)0)
// = fTL
}
// fullbright pass
// fog is still black
R_RenderFullbrights ();
// buf = aI + (1-a)buf
// = aC + (1-a)fTL
// = a(fG + (1-f)0) + (1-a)fTL
// = afG + (1-a)fTL
// = f((1-a)TL + aG)
gl_Fog_StopAdditive (); // use fog color
qfglDepthMask (GL_FALSE); // don't write Z
qfglBlendFunc (GL_ONE, GL_ONE);
// draw black polys
qfglColor4f (0, 0, 0, 1);
DrawTextureChains (0, 0);
// buf = I + buf
// = C + f((1-a)TL + aG)
// = (f0 + (1-f)F) + f((1-a)TL + aG)
// = (1-f)F + f((1-a)TL + aG)
// = f((1-a)TL + aG) + (1-f)F
// restore state
qfglColor4ubv (color_white);
qfglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
qfglDepthMask (GL_TRUE);
}
clear_texture_chains ();
}
model_t *gl_currentmodel;
void
GL_BuildSurfaceDisplayList (msurface_t *surf)
{
float s, t;
float *vec;
int lindex, lnumverts, i;
glpoly_t *poly;
medge_t *pedges, *r_pedge;
// reconstruct the polygon
pedges = gl_currentmodel->brush.edges;
lnumverts = surf->numedges;
// draw texture
poly = Hunk_Alloc (0, sizeof (glpoly_t) + (lnumverts - 4) *
VERTEXSIZE * sizeof (float));
poly->next = surf->polys;
poly->flags = surf->flags;
surf->polys = poly;
poly->numverts = lnumverts;
mtexinfo_t *texinfo = surf->texinfo;
for (i = 0; i < lnumverts; i++) {
lindex = gl_currentmodel->brush.surfedges[surf->firstedge + i];
if (lindex > 0) {
r_pedge = &pedges[lindex];
vec = r_pcurrentvertbase[r_pedge->v[0]].position;
} else {
r_pedge = &pedges[-lindex];
vec = r_pcurrentvertbase[r_pedge->v[1]].position;
}
s = DotProduct (vec, texinfo->vecs[0]) + texinfo->vecs[0][3];
s /= texinfo->texture->width;
t = DotProduct (vec, texinfo->vecs[1]) + texinfo->vecs[1][3];
t /= texinfo->texture->height;
VectorCopy (vec, poly->verts[i]);
poly->verts[i][3] = s;
poly->verts[i][4] = t;
// lightmap texture coordinates
s = DotProduct (vec, texinfo->vecs[0]) + texinfo->vecs[0][3];
s -= surf->texturemins[0];
s += surf->light_s * 16;
s += 8;
s /= BLOCK_WIDTH * 16;
t = DotProduct (vec, texinfo->vecs[1]) + texinfo->vecs[1][3];
t -= surf->texturemins[1];
t += surf->light_t * 16;
t += 8;
t /= BLOCK_HEIGHT * 16;
poly->verts[i][5] = s;
poly->verts[i][6] = t;
}
// remove co-linear points - Ed
if (!gl_keeptjunctions->int_val && !(surf->flags & SURF_UNDERWATER)) {
for (i = 0; i < lnumverts; ++i) {
vec3_t v1, v2;
float *prev, *this, *next;
prev = poly->verts[(i + lnumverts - 1) % lnumverts];
this = poly->verts[i];
next = poly->verts[(i + 1) % lnumverts];
VectorSubtract (this, prev, v1);
VectorNormalize (v1);
VectorSubtract (next, prev, v2);
VectorNormalize (v2);
// skip co-linear points
# define COLINEAR_EPSILON 0.001
if ((fabs (v1[0] - v2[0]) <= COLINEAR_EPSILON) &&
(fabs (v1[1] - v2[1]) <= COLINEAR_EPSILON) &&
(fabs (v1[2] - v2[2]) <= COLINEAR_EPSILON)) {
int j;
for (j = i + 1; j < lnumverts; ++j) {
int k;
for (k = 0; k < VERTEXSIZE; ++k)
poly->verts[j - 1][k] = poly->verts[j][k];
}
--lnumverts;
// retry next vertex next time, which is now current vertex
--i;
}
}
}
poly->numverts = lnumverts;
}