quakeforge/libs/video/renderer/glsl/glsl_bsp.c
Bill Currie 706f09b247 Put the transparent surfaces on the water chain.
I'd actually done this the first time, but then got confused and forgot the
waterchain works with multiple textures. This is actually the right place
as all transparent surfaces need to be sorted irrespective to their
textures. Really, waterchain needs to be renamed.
2012-01-31 18:08:06 +09:00

1332 lines
34 KiB
C

/*
glsl_bsp.c
GLSL bsps
Copyright (C) 2012 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2012/1/7
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
static __attribute__ ((used)) const char rcsid[] = "$Id$";
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include "QF/cvar.h"
#include "QF/dstring.h"
#include "QF/image.h"
#include "QF/render.h"
#include "QF/sys.h"
#include "QF/va.h"
#include "QF/vrect.h"
#include "QF/GLSL/defines.h"
#include "QF/GLSL/funcs.h"
#include "QF/GLSL/qf_bsp.h"
#include "QF/GLSL/qf_lightmap.h"
#include "QF/GLSL/qf_textures.h"
#include "QF/GLSL/qf_vid.h"
#include "r_cvar.h"
#include "r_local.h"
typedef struct {
GLushort count;
GLushort indices[1];
} glslpoly_t;
#define ALLOC_CHUNK 64
instsurf_t *waterchain = NULL;
instsurf_t **waterchain_tail = &waterchain;
instsurf_t *sky_chain;
instsurf_t **sky_chain_tail = &sky_chain;
static texture_t **r_texture_chains;
static int r_num_texture_chains;
static int max_texture_chains;
// for world and non-instance models
static instsurf_t *static_instsurfs;
static instsurf_t **static_instsurfs_tail = &static_instsurfs;
static instsurf_t *free_static_instsurfs;
// for instance models
static elechain_t *elechains;
static elechain_t **elechains_tail = &elechains;
static elechain_t *free_elechains;
static elements_t *elementss;
static elements_t **elementss_tail = &elementss;
static elements_t *free_elementss;
static instsurf_t *instsurfs;
static instsurf_t **instsurfs_tail = &instsurfs;
static instsurf_t *free_instsurfs;
static GLuint bsp_vbo;
static mat4_t bsp_vp;
static GLuint skybox_tex;
static qboolean skybox_loaded;
static quat_t sky_rotation[2];
static quat_t sky_velocity;
static double sky_time;
static quat_t default_color = { 1, 1, 1, 1 };
static float *last_color = default_color;
static const char quakebsp_vert[] =
#include "quakebsp.vc"
;
static const char quakebsp_frag[] =
#include "quakebsp.fc"
;
static const char quaketurb_frag[] =
#include "quaketrb.fc"
;
static const char quakesky_vert[] =
#include "quakesky.vc"
;
static const char quakeskyid_frag[] =
#include "quakeski.fc"
;
static const char quakeskybox_frag[] =
#include "quakeskb.fc"
;
static struct {
int program;
shaderparam_t mvp_matrix;
shaderparam_t tlst;
shaderparam_t vertex;
shaderparam_t colormap;
shaderparam_t texture;
shaderparam_t lightmap;
shaderparam_t color;
shaderparam_t fog;
} quake_bsp = {
0,
{"mvp_mat", 1},
{"tlst", 0},
{"vertex", 0},
{"colormap", 1},
{"texture", 1},
{"lightmap", 1},
{"vcolor", 0},
{"fog", 1},
};
static struct {
int program;
shaderparam_t mvp_matrix;
shaderparam_t tlst;
shaderparam_t vertex;
shaderparam_t palette;
shaderparam_t texture;
shaderparam_t realtime;
shaderparam_t color;
shaderparam_t fog;
} quake_turb = {
0,
{"mvp_mat", 1},
{"tlst", 0},
{"vertex", 0},
{"palette", 1},
{"texture", 1},
{"realtime", 1},
{"vcolor", 0},
{"fog", 1},
};
static struct {
int program;
shaderparam_t mvp_matrix;
shaderparam_t sky_matrix;
shaderparam_t vertex;
shaderparam_t palette;
shaderparam_t solid;
shaderparam_t trans;
shaderparam_t realtime;
shaderparam_t fog;
} quake_skyid = {
0,
{"mvp_mat", 1},
{"sky_mat", 1},
{"vertex", 0},
{"palette", 1},
{"solid", 1},
{"trans", 1},
{"realtime", 1},
{"fog", 1},
};
static struct {
int program;
shaderparam_t mvp_matrix;
shaderparam_t sky_matrix;
shaderparam_t vertex;
shaderparam_t sky;
shaderparam_t fog;
} quake_skybox = {
0,
{"mvp_mat", 1},
{"sky_mat", 1},
{"vertex", 0},
{"sky", 1},
{"fog", 1},
};
static struct {
shaderparam_t *mvp_matrix;
shaderparam_t *sky_matrix;
shaderparam_t *vertex;
shaderparam_t *fog;
} sky_params;
#define CHAIN_SURF_F2B(surf,chain) \
do { \
instsurf_t *inst = (surf)->instsurf; \
if (!inst) (surf)->tinst = inst = get_instsurf (); \
inst->surface = (surf); \
*(chain##_tail) = inst; \
(chain##_tail) = &inst->tex_chain; \
*(chain##_tail) = 0; \
} while (0)
#define CHAIN_SURF_B2F(surf,chain) \
do { \
instsurf_t *inst = (surf)->instsurf; \
if (!inst) (surf)->tinst = inst = get_instsurf (); \
inst->surface = (surf); \
inst->tex_chain = (chain); \
(chain) = inst; \
} while (0)
#define GET_RELEASE(type,name) \
static inline type * \
get_##name (void) \
{ \
type *ele; \
if (!free_##name##s) { \
int i; \
free_##name##s = calloc (ALLOC_CHUNK, sizeof (type)); \
for (i = 0; i < ALLOC_CHUNK - 1; i++) \
free_##name##s[i]._next = &free_##name##s[i + 1]; \
} \
ele = free_##name##s; \
free_##name##s = ele->_next; \
ele->_next = 0; \
*name##s_tail = ele; \
name##s_tail = &ele->_next; \
return ele; \
} \
static inline void \
release_##name##s (void) \
{ \
if (name##s) { \
*name##s_tail = free_##name##s; \
free_##name##s = name##s; \
name##s = 0; \
name##s_tail = &name##s; \
} \
}
GET_RELEASE (elechain_t, elechain)
GET_RELEASE (elements_t, elements)
GET_RELEASE (instsurf_t, static_instsurf)
GET_RELEASE (instsurf_t, instsurf)
void
R_AddTexture (texture_t *tex)
{
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 (texture_t *));
for (i = r_num_texture_chains; i < max_texture_chains; i++)
r_texture_chains[i] = 0;
}
r_texture_chains[r_num_texture_chains++] = tex;
tex->tex_chain = 0;
tex->tex_chain_tail = &tex->tex_chain;
tex->elechain = 0;
tex->elechain_tail = &tex->elechain;
}
void
R_InitSurfaceChains (model_t *model)
{
int i;
release_static_instsurfs ();
release_instsurfs ();
for (i = 0; i < model->nummodelsurfaces; i++) {
model->surfaces[i].instsurf = get_static_instsurf ();
model->surfaces[i].instsurf->surface = &model->surfaces[i];
}
}
static inline void
clear_tex_chain (texture_t *tex)
{
tex->tex_chain = 0;
tex->tex_chain_tail = &tex->tex_chain;
tex->elechain = 0;
tex->elechain_tail = &tex->elechain;
}
static void
clear_texture_chains (void)
{
int i;
for (i = 0; i < r_num_texture_chains; i++) {
if (!r_texture_chains[i])
continue;
clear_tex_chain (r_texture_chains[i]);
}
clear_tex_chain (r_notexture_mip);
release_elechains ();
release_elementss ();
release_instsurfs ();
}
void
R_ClearElements (void)
{
release_elechains ();
release_elementss ();
}
static void
update_lightmap (msurface_t *surf)
{
int maps;
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)
R_BuildLightMap (surf);
}
}
static inline void
chain_surface (msurface_t *surf, vec_t *transform, float *color)
{
instsurf_t *is;
if (surf->flags & SURF_DRAWSKY) {
CHAIN_SURF_F2B (surf, sky_chain);
} else if ((surf->flags & SURF_DRAWTURB) || (color && color[3] < 1.0)) {
CHAIN_SURF_B2F (surf, waterchain);
} else {
texture_t *tex;
if (!surf->texinfo->texture->anim_total)
tex = surf->texinfo->texture;
else
tex = R_TextureAnimation (surf);
CHAIN_SURF_F2B (surf, tex->tex_chain);
update_lightmap (surf);
}
if (!(is = surf->instsurf))
is = surf->tinst;
is->transform = transform;
is->color = color;
}
static void
register_textures (model_t *model)
{
int i;
texture_t *tex;
for (i = 0; i < model->numtextures; i++) {
tex = model->textures[i];
if (!tex)
continue;
R_AddTexture (tex);
}
}
void
R_ClearTextures (void)
{
r_num_texture_chains = 0;
}
void
R_RegisterTextures (model_t **models, int num_models)
{
int i;
model_t *m;
R_ClearTextures ();
R_InitSurfaceChains (r_worldentity.model);
R_AddTexture (r_notexture_mip);
register_textures (r_worldentity.model);
for (i = 0; i < num_models; i++) {
m = models[i];
if (!m)
continue;
// sub-models are done as part of the main model
if (*m->name == '*')
continue;
// world has already been done, not interested in non-brush models
if (m == r_worldentity.model || m->type != mod_brush)
continue;
m->numsubmodels = 1; // no support for submodels in non-world model
register_textures (m);
}
}
static elechain_t *
add_elechain (texture_t *tex, int ec_index)
{
elechain_t *ec;
ec = get_elechain ();
ec->elements = get_elements ();
ec->index = ec_index;
ec->transform = 0;
ec->color = 0;
*tex->elechain_tail = ec;
tex->elechain_tail = &ec->next;
return ec;
}
static void
build_surf_displist (model_t **models, msurface_t *fa, int base,
dstring_t *vert_list)
{
int numverts;
int numtris;
int numindices;
int i;
vec_t *vec;
mvertex_t *vertices;
medge_t *edges;
int *surfedges;
int index;
bspvert_t *verts;
glslpoly_t *poly;
GLushort *ind;
float s, t;
if (fa->ec_index < 0) {
vertices = models[-fa->ec_index - 1]->vertexes;
edges = models[-fa->ec_index - 1]->edges;
surfedges = models[-fa->ec_index - 1]->surfedges;
} else {
vertices = r_worldentity.model->vertexes;
edges = r_worldentity.model->edges;
surfedges = r_worldentity.model->surfedges;
}
numverts = fa->numedges;
numtris = numverts - 2;
numindices = numtris * 3;
verts = alloca (numverts * sizeof (bspvert_t));
poly = malloc (field_offset (glslpoly_t, indices[numindices]));
poly->count = numindices;
for (i = 0, ind = poly->indices; i < numtris; i++) {
*ind++ = base;
*ind++ = base + i + 1;
*ind++ = base + i + 2;
}
fa->polys = (glpoly_t *) poly;
for (i = 0; i < numverts; i++) {
index = surfedges[fa->firstedge + i];
if (index > 0)
vec = vertices[edges[index].v[0]].position;
else
vec = vertices[edges[-index].v[1]].position;
s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3];
t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3];
VectorCopy (vec, verts[i].vertex);
verts[i].vertex[3] = 1;
verts[i].tlst[0] = s / fa->texinfo->texture->width;
verts[i].tlst[1] = t / fa->texinfo->texture->height;
//lightmap texture coordinates
if (!fa->lightpic) {
// sky and water textures don't have lightmaps
verts[i].tlst[2] = 0;
verts[i].tlst[3] = 0;
continue;
}
s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3];
t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3];
s -= fa->texturemins[0];
t -= fa->texturemins[1];
s += fa->lightpic->rect->x * 16 + 8;
t += fa->lightpic->rect->y * 16 + 8;
s /= 16;
t /= 16;
verts[i].tlst[2] = s * fa->lightpic->size;
verts[i].tlst[3] = t * fa->lightpic->size;
}
dstring_append (vert_list, (char *) verts, numverts * sizeof (bspvert_t));
}
void
R_BuildDisplayLists (model_t **models, int num_models)
{
int i, j;
int vertex_index_base;
model_t *m;
dmodel_t *dm;
msurface_t *surf;
dstring_t *vertices;
QuatSet (1, 0, 0, 0, sky_rotation[0]);
QuatSet (1, 0, 0, 0, sky_rotation[1]);
QuatSet (0, 0, 0, 0, sky_velocity);
QuatExp (sky_velocity, sky_velocity);
sky_time = r_realtime;
// now run through all surfaces, chaining them to their textures, thus
// effectively sorting the surfaces by texture (without worrying about
// surface order on the same texture chain).
for (i = 0; i < num_models; i++) {
m = models[i];
if (!m)
continue;
// sub-models are done as part of the main model
if (*m->name == '*')
continue;
// non-bsp models don't have surfaces.
dm = m->submodels;
for (j = 0; j < m->numsurfaces; j++) {
texture_t *tex;
if (j == dm->firstface + dm->numfaces) {
dm++;
if (dm - m->submodels == m->numsubmodels) {
// limit the surfaces
// probably never hit
Sys_Printf ("R_BuildDisplayLists: too many surfaces\n");
m->numsurfaces = j;
break;
}
}
surf = m->surfaces + j;
surf->ec_index = dm - m->submodels;
if (!surf->ec_index && m != r_worldentity.model)
surf->ec_index = -1 - i; // instanced model
tex = surf->texinfo->texture;
CHAIN_SURF_F2B (surf, tex->tex_chain);
}
}
// All vertices from all brush models go into one giant vbo.
vertices = dstring_new ();
vertex_index_base = 0;
// All usable surfaces have been chained to the (base) texture they use.
// Run through the textures, using their chains to build display maps.
// For animated textures, if a surface is on one texture of the group, it
// will be on all.
for (i = 0; i < r_num_texture_chains; i++) {
texture_t *tex;
instsurf_t *is;
elechain_t *ec = 0;
elements_t *el = 0;
tex = r_texture_chains[i];
for (is = tex->tex_chain; is; is = is->tex_chain) {
msurface_t *surf = is->surface;
if (!tex->elechain) {
ec = add_elechain (tex, surf->ec_index);
el = ec->elements;
el->base = (byte *) vertices->size;
vertex_index_base = 0;
}
if (surf->ec_index != ec->index) { // next sub-model
ec = add_elechain (tex, surf->ec_index);
el = ec->elements;
el->base = (byte *) vertices->size;
vertex_index_base = 0;
}
if (vertex_index_base + surf->numedges > 65535) {
// elements index overflow
el->next = get_elements ();
el = el->next;
el->base = (byte *) vertices->size;
vertex_index_base = 0;
}
// we don't use it now, but pre-initializing the list won't hurt
if (!el->list)
el->list = dstring_new ();
dstring_clear (el->list);
surf->base = el->base;
build_surf_displist (models, surf, vertex_index_base, vertices);
vertex_index_base += surf->numedges;
}
}
clear_texture_chains ();
Sys_MaskPrintf (SYS_GLSL, "R_BuildDisplayLists: %ld verts total\n",
vertices->size / sizeof (bspvert_t));
if (!bsp_vbo)
qfglGenBuffers (1, &bsp_vbo);
qfglBindBuffer (GL_ARRAY_BUFFER, bsp_vbo);
qfglBufferData (GL_ARRAY_BUFFER, vertices->size, vertices->str,
GL_STATIC_DRAW);
qfglBindBuffer (GL_ARRAY_BUFFER, 0);
dstring_delete (vertices);
}
static void
R_DrawBrushModel (entity_t *e)
{
float dot, radius;
int i;
unsigned k;
model_t *model;
plane_t *plane;
msurface_t *surf;
qboolean rotated;
vec3_t mins, maxs, org;
model = e->model;
if (e->transform[0] != 1 || e->transform[5] != 1 || e->transform[10] != 1) {
rotated = true;
radius = model->radius;
if (R_CullSphere (e->origin, radius))
return;
} else {
rotated = false;
VectorAdd (e->origin, model->mins, mins);
VectorAdd (e->origin, model->maxs, maxs);
if (R_CullBox (mins, maxs))
return;
}
VectorSubtract (r_refdef.vieworg, e->origin, org);
if (rotated) {
vec3_t temp;
VectorCopy (org, temp);
org[0] = DotProduct (temp, e->transform + 0);
org[1] = DotProduct (temp, e->transform + 4);
org[2] = DotProduct (temp, e->transform + 8);
}
// calculate dynamic lighting for bmodel if it's not an instanced model
if (model->firstmodelsurface != 0 && r_dlight_lightmap->int_val) {
vec3_t lightorigin;
for (k = 0; k < r_maxdlights; k++) {
if ((r_dlights[k].die < r_realtime) || (!r_dlights[k].radius))
continue;
VectorSubtract (r_dlights[k].origin, e->origin, lightorigin);
R_RecursiveMarkLights (lightorigin, &r_dlights[k], 1 << k,
model->nodes + model->hulls[0].firstclipnode);
}
}
surf = &model->surfaces[model->firstmodelsurface];
for (i = 0; i < model->nummodelsurfaces; i++, surf++) {
// find the node side on which we are
plane = surf->plane;
dot = PlaneDiff (org, plane);
// enqueue the polygon
if (((surf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON))
|| (!(surf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) {
chain_surface (surf, e->transform, e->colormod);
}
}
}
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 the node side on which we are
plane_t *plane = node->plane;
if (plane->type < 3)
return (r_origin[plane->type] - plane->dist) < 0;
return (DotProduct (r_origin, plane->normal) - plane->dist) < 0;
}
static inline void
visit_node (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 = r_worldentity.model->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 (surf, 0, 0);
}
}
}
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 (mnode_t *node)
{
#define NODE_STACK 1024
struct {
mnode_t *node;
int side;
} *node_ptr, node_stack[NODE_STACK];
mnode_t *front;
int side;
node_ptr = node_stack;
while (1) {
while (test_node (node)) {
side = get_side (node);
front = node->children[side];
if (test_node (front)) {
if (node_ptr - node_stack == NODE_STACK)
Sys_Error ("node_stack overflow");
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 (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 (node, side);
node = node->children[!side];
continue;
}
break;
}
if (node->contents < 0 && node->contents != CONTENTS_SOLID)
visit_leaf ((mleaf_t *) node);
}
static void
draw_elechain (elechain_t *ec, int matloc, int vertloc, int tlstloc,
int colloc)
{
mat4_t mat;
elements_t *el;
int count;
float *color;
color = ec->color;
if (color != last_color && colloc >= 0) {
last_color = color;
if (!color)
color = default_color;
qfglVertexAttrib4fv (quake_bsp.color.location, color);
}
if (ec->transform) {
Mat4Mult (bsp_vp, ec->transform, mat);
qfglUniformMatrix4fv (matloc, 1, false, mat);
} else {
qfglUniformMatrix4fv (matloc, 1, false, bsp_vp);
}
for (el = ec->elements; el; el = el->next) {
if (!el->list->size)
continue;
count = el->list->size / sizeof (GLushort);
qfglVertexAttribPointer (vertloc, 4, GL_FLOAT,
0, sizeof (bspvert_t),
el->base + field_offset (bspvert_t, vertex));
if (tlstloc >= 0)
qfglVertexAttribPointer (tlstloc, 4, GL_FLOAT,
0, sizeof (bspvert_t),
el->base + field_offset (bspvert_t,tlst));
qfglDrawElements (GL_TRIANGLES, count,
GL_UNSIGNED_SHORT, el->list->str);
dstring_clear (el->list);
}
}
static void
bsp_begin (void)
{
quat_t fog;
default_color[3] = 1;
last_color = default_color;
Mat4Mult (glsl_projection, glsl_view, bsp_vp);
qfglUseProgram (quake_bsp.program);
qfglEnableVertexAttribArray (quake_bsp.vertex.location);
qfglEnableVertexAttribArray (quake_bsp.tlst.location);
qfglDisableVertexAttribArray (quake_bsp.color.location);
qfglVertexAttrib4fv (quake_bsp.color.location, default_color);
VectorCopy (Fog_GetColor (), fog);
fog[3] = Fog_GetDensity () / 64.0;
qfglUniform4fv (quake_bsp.fog.location, 1, fog);
qfglUniform1i (quake_bsp.colormap.location, 2);
qfglActiveTexture (GL_TEXTURE0 + 2);
qfglEnable (GL_TEXTURE_2D);
qfglBindTexture (GL_TEXTURE_2D, glsl_colormap);
qfglUniform1i (quake_bsp.lightmap.location, 1);
qfglActiveTexture (GL_TEXTURE0 + 1);
qfglEnable (GL_TEXTURE_2D);
qfglBindTexture (GL_TEXTURE_2D, R_LightmapTexture ());
qfglUniform1i (quake_bsp.texture.location, 0);
qfglActiveTexture (GL_TEXTURE0 + 0);
qfglEnable (GL_TEXTURE_2D);
qfglBindBuffer (GL_ARRAY_BUFFER, bsp_vbo);
}
static void
bsp_end (void)
{
qfglDisableVertexAttribArray (quake_bsp.vertex.location);
qfglDisableVertexAttribArray (quake_bsp.tlst.location);
qfglActiveTexture (GL_TEXTURE0 + 0);
qfglDisable (GL_TEXTURE_2D);
qfglActiveTexture (GL_TEXTURE0 + 1);
qfglDisable (GL_TEXTURE_2D);
qfglActiveTexture (GL_TEXTURE0 + 2);
qfglDisable (GL_TEXTURE_2D);
qfglBindBuffer (GL_ARRAY_BUFFER, 0);
}
static void
turb_begin (void)
{
quat_t fog;
default_color[3] = bound (0, r_wateralpha->value, 1);
last_color = default_color;
Mat4Mult (glsl_projection, glsl_view, bsp_vp);
qfglUseProgram (quake_turb.program);
qfglEnableVertexAttribArray (quake_turb.vertex.location);
qfglEnableVertexAttribArray (quake_turb.tlst.location);
qfglDisableVertexAttribArray (quake_turb.color.location);
qfglVertexAttrib4fv (quake_turb.color.location, default_color);
VectorCopy (Fog_GetColor (), fog);
fog[3] = Fog_GetDensity () / 64.0;
qfglUniform4fv (quake_turb.fog.location, 1, fog);
qfglUniform1i (quake_turb.palette.location, 1);
qfglActiveTexture (GL_TEXTURE0 + 1);
qfglEnable (GL_TEXTURE_2D);
qfglBindTexture (GL_TEXTURE_2D, glsl_palette);
qfglUniform1f (quake_turb.realtime.location, r_realtime);
qfglUniform1i (quake_turb.texture.location, 0);
qfglActiveTexture (GL_TEXTURE0 + 0);
qfglEnable (GL_TEXTURE_2D);
qfglBindBuffer (GL_ARRAY_BUFFER, bsp_vbo);
}
static void
turb_end (void)
{
qfglDisableVertexAttribArray (quake_turb.vertex.location);
qfglDisableVertexAttribArray (quake_turb.tlst.location);
qfglActiveTexture (GL_TEXTURE0 + 0);
qfglDisable (GL_TEXTURE_2D);
qfglActiveTexture (GL_TEXTURE0 + 1);
qfglDisable (GL_TEXTURE_2D);
qfglBindBuffer (GL_ARRAY_BUFFER, 0);
}
static void
spin (mat4_t mat)
{
quat_t q;
mat4_t m;
float blend;
while (r_realtime - sky_time > 1) {
QuatCopy (sky_rotation[1], sky_rotation[0]);
QuatMult (sky_velocity, sky_rotation[0], sky_rotation[1]);
sky_time += 1;
}
blend = bound (0, (r_realtime - sky_time), 1);
QuatBlend (sky_rotation[0], sky_rotation[1], blend, q);
Mat4Identity (mat);
VectorNegate (r_origin, mat + 12);
QuatToMatrix (q, m, 1, 1);
Mat4Mult (m, mat, mat);
}
static void
sky_begin (void)
{
mat4_t mat;
quat_t fog;
default_color[3] = 1;
last_color = default_color;
Mat4Mult (glsl_projection, glsl_view, bsp_vp);
if (skybox_loaded) {
sky_params.mvp_matrix = &quake_skybox.mvp_matrix;
sky_params.vertex = &quake_skybox.vertex;
sky_params.sky_matrix = &quake_skybox.sky_matrix;
sky_params.fog = &quake_skybox.fog;
qfglUseProgram (quake_skybox.program);
qfglEnableVertexAttribArray (quake_skybox.vertex.location);
qfglUniform1i (quake_skybox.sky.location, 0);
qfglActiveTexture (GL_TEXTURE0 + 0);
qfglEnable (GL_TEXTURE_CUBE_MAP);
qfglBindTexture (GL_TEXTURE_CUBE_MAP, skybox_tex);
} else {
sky_params.mvp_matrix = &quake_skyid.mvp_matrix;
sky_params.sky_matrix = &quake_skyid.sky_matrix;
sky_params.vertex = &quake_skyid.vertex;
sky_params.fog = &quake_skyid.fog;
qfglUseProgram (quake_skyid.program);
qfglEnableVertexAttribArray (quake_skyid.vertex.location);
qfglUniform1i (quake_skyid.palette.location, 2);
qfglActiveTexture (GL_TEXTURE0 + 2);
qfglEnable (GL_TEXTURE_2D);
qfglBindTexture (GL_TEXTURE_2D, glsl_palette);
qfglUniform1f (quake_skyid.realtime.location, r_realtime);
qfglUniform1i (quake_skyid.trans.location, 0);
qfglActiveTexture (GL_TEXTURE0 + 0);
qfglEnable (GL_TEXTURE_2D);
qfglUniform1i (quake_skyid.solid.location, 1);
qfglActiveTexture (GL_TEXTURE0 + 1);
qfglEnable (GL_TEXTURE_2D);
}
VectorCopy (Fog_GetColor (), fog);
fog[3] = Fog_GetDensity () / 64.0;
qfglUniform4fv (sky_params.fog->location, 1, fog);
spin (mat);
qfglUniformMatrix4fv (sky_params.sky_matrix->location, 1, false, mat);
qfglBindBuffer (GL_ARRAY_BUFFER, bsp_vbo);
}
static void
sky_end (void)
{
qfglDisableVertexAttribArray (sky_params.vertex->location);
qfglActiveTexture (GL_TEXTURE0 + 0);
qfglDisable (GL_TEXTURE_2D);
qfglDisable (GL_TEXTURE_CUBE_MAP);
qfglActiveTexture (GL_TEXTURE0 + 1);
qfglDisable (GL_TEXTURE_2D);
qfglActiveTexture (GL_TEXTURE0 + 2);
qfglDisable (GL_TEXTURE_2D);
qfglBindBuffer (GL_ARRAY_BUFFER, 0);
}
static inline void
add_surf_elements (texture_t *tex, instsurf_t *is,
elechain_t **ec, elements_t **el)
{
msurface_t *surf = is->surface;
glslpoly_t *poly = (glslpoly_t *) surf->polys;
if (!tex->elechain) {
(*ec) = add_elechain (tex, surf->ec_index);
(*ec)->transform = is->transform;
(*ec)->color = is->color;
(*el) = (*ec)->elements;
(*el)->base = surf->base;
if (!(*el)->list)
(*el)->list = dstring_new ();
dstring_clear ((*el)->list);
}
if (is->transform != (*ec)->transform || is->color != (*ec)->color) {
(*ec) = add_elechain (tex, surf->ec_index);
(*ec)->transform = is->transform;
(*ec)->color = is->color;
(*el) = (*ec)->elements;
(*el)->base = surf->base;
if (!(*el)->list)
(*el)->list = dstring_new ();
dstring_clear ((*el)->list);
}
if (surf->base != (*el)->base) {
(*el)->next = get_elements ();
(*el) = (*el)->next;
(*el)->base = surf->base;
if (!(*el)->list)
(*el)->list = dstring_new ();
dstring_clear ((*el)->list);
}
dstring_append ((*el)->list, (char *) poly->indices,
poly->count * sizeof (poly->indices[0]));
}
static void
build_tex_elechain (texture_t *tex)
{
instsurf_t *is;
elechain_t *ec = 0;
elements_t *el = 0;
for (is = tex->tex_chain; is; is = is->tex_chain) {
add_surf_elements (tex, is, &ec, &el);
}
}
void
R_DrawWorld (void)
{
entity_t worldent;
int i;
clear_texture_chains (); // do this first for water and skys
memset (&worldent, 0, sizeof (worldent));
worldent.model = r_worldentity.model;
currententity = &worldent;
R_VisitWorldNodes (worldent.model->nodes);
if (r_drawentities->int_val) {
entity_t *ent;
for (ent = r_ent_queue; ent; ent = ent->next) {
if (ent->model->type != mod_brush)
continue;
currententity = ent;
R_DrawBrushModel (ent);
}
}
bsp_begin ();
for (i = 0; i < r_num_texture_chains; i++) {
texture_t *tex;
elechain_t *ec = 0;
tex = r_texture_chains[i];
qfglActiveTexture (GL_TEXTURE0 + 0);
qfglBindTexture (GL_TEXTURE_2D, tex->gl_texturenum);
build_tex_elechain (tex);
for (ec = tex->elechain; ec; ec = ec->next) {
draw_elechain (ec, quake_bsp.mvp_matrix.location,
quake_bsp.vertex.location,
quake_bsp.tlst.location,
quake_bsp.color.location);
}
}
bsp_end ();
}
void
R_DrawWaterSurfaces ()
{
instsurf_t *is;
msurface_t *surf;
texture_t *tex = 0;
elechain_t *ec = 0;
elements_t *el = 0;
if (!waterchain)
return;
turb_begin ();
for (is = waterchain; is; is = is->tex_chain) {
surf = is->surface;
if (tex != surf->texinfo->texture) {
if (tex) {
qfglBindTexture (GL_TEXTURE_2D, tex->gl_texturenum);
for (ec = tex->elechain; ec; ec = ec->next)
draw_elechain (ec, quake_turb.mvp_matrix.location,
quake_turb.vertex.location,
quake_turb.tlst.location,
quake_turb.color.location);
tex->elechain = 0;
tex->elechain_tail = &tex->elechain;
}
tex = surf->texinfo->texture;
}
add_surf_elements (tex, is, &ec, &el);
}
if (tex) {
qfglBindTexture (GL_TEXTURE_2D, tex->gl_texturenum);
for (ec = tex->elechain; ec; ec = ec->next)
draw_elechain (ec, quake_turb.mvp_matrix.location,
quake_turb.vertex.location,
quake_turb.tlst.location,
quake_turb.color.location);
tex->elechain = 0;
tex->elechain_tail = &tex->elechain;
}
turb_end ();
waterchain = 0;
waterchain_tail = &waterchain;
}
void
R_DrawSky (void)
{
instsurf_t *is;
msurface_t *surf;
texture_t *tex = 0;
elechain_t *ec = 0;
elements_t *el = 0;
if (!sky_chain)
return;
sky_begin ();
for (is = sky_chain; is; is = is->tex_chain) {
surf = is->surface;
if (tex != surf->texinfo->texture) {
if (tex) {
if (!skybox_loaded) {
qfglActiveTexture (GL_TEXTURE0 + 0);
qfglBindTexture (GL_TEXTURE_2D, tex->sky_tex[0]);
qfglActiveTexture (GL_TEXTURE0 + 1);
qfglBindTexture (GL_TEXTURE_2D, tex->sky_tex[1]);
}
for (ec = tex->elechain; ec; ec = ec->next)
draw_elechain (ec, sky_params.mvp_matrix->location,
sky_params.vertex->location, -1, -1);
tex->elechain = 0;
tex->elechain_tail = &tex->elechain;
}
tex = surf->texinfo->texture;
}
add_surf_elements (tex, is, &ec, &el);
}
if (tex) {
if (!skybox_loaded) {
qfglActiveTexture (GL_TEXTURE0 + 0);
qfglBindTexture (GL_TEXTURE_2D, tex->sky_tex[0]);
qfglActiveTexture (GL_TEXTURE0 + 1);
qfglBindTexture (GL_TEXTURE_2D, tex->sky_tex[1]);
}
for (ec = tex->elechain; ec; ec = ec->next)
draw_elechain (ec, sky_params.mvp_matrix->location,
sky_params.vertex->location, -1, -1);
tex->elechain = 0;
tex->elechain_tail = &tex->elechain;
}
sky_end ();
sky_chain = 0;
sky_chain_tail = &sky_chain;
}
void
R_InitBsp (void)
{
int vert;
int frag;
vert = GL_CompileShader ("quakebsp.vert", quakebsp_vert, GL_VERTEX_SHADER);
frag = GL_CompileShader ("quakebsp.frag", quakebsp_frag,
GL_FRAGMENT_SHADER);
quake_bsp.program = GL_LinkProgram ("quakebsp", vert, frag);
GL_ResolveShaderParam (quake_bsp.program, &quake_bsp.mvp_matrix);
GL_ResolveShaderParam (quake_bsp.program, &quake_bsp.tlst);
GL_ResolveShaderParam (quake_bsp.program, &quake_bsp.vertex);
GL_ResolveShaderParam (quake_bsp.program, &quake_bsp.colormap);
GL_ResolveShaderParam (quake_bsp.program, &quake_bsp.texture);
GL_ResolveShaderParam (quake_bsp.program, &quake_bsp.lightmap);
GL_ResolveShaderParam (quake_bsp.program, &quake_bsp.color);
GL_ResolveShaderParam (quake_bsp.program, &quake_bsp.fog);
frag = GL_CompileShader ("quaketrb.frag", quaketurb_frag,
GL_FRAGMENT_SHADER);
quake_turb.program = GL_LinkProgram ("quaketrb", vert, frag);
GL_ResolveShaderParam (quake_turb.program, &quake_turb.mvp_matrix);
GL_ResolveShaderParam (quake_turb.program, &quake_turb.tlst);
GL_ResolveShaderParam (quake_turb.program, &quake_turb.vertex);
GL_ResolveShaderParam (quake_turb.program, &quake_turb.palette);
GL_ResolveShaderParam (quake_turb.program, &quake_turb.texture);
GL_ResolveShaderParam (quake_turb.program, &quake_turb.realtime);
GL_ResolveShaderParam (quake_turb.program, &quake_turb.color);
GL_ResolveShaderParam (quake_turb.program, &quake_turb.fog);
vert = GL_CompileShader ("quakesky.vert", quakesky_vert, GL_VERTEX_SHADER);
frag = GL_CompileShader ("quakeski.frag", quakeskyid_frag,
GL_FRAGMENT_SHADER);
quake_skyid.program = GL_LinkProgram ("quakeskyid", vert, frag);
GL_ResolveShaderParam (quake_skyid.program, &quake_skyid.mvp_matrix);
GL_ResolveShaderParam (quake_skyid.program, &quake_skyid.sky_matrix);
GL_ResolveShaderParam (quake_skyid.program, &quake_skyid.vertex);
GL_ResolveShaderParam (quake_skyid.program, &quake_skyid.palette);
GL_ResolveShaderParam (quake_skyid.program, &quake_skyid.solid);
GL_ResolveShaderParam (quake_skyid.program, &quake_skyid.trans);
GL_ResolveShaderParam (quake_skyid.program, &quake_skyid.realtime);
GL_ResolveShaderParam (quake_skyid.program, &quake_skyid.fog);
frag = GL_CompileShader ("quakeskb.frag", quakeskybox_frag,
GL_FRAGMENT_SHADER);
quake_skybox.program = GL_LinkProgram ("quakeskybox", vert, frag);
GL_ResolveShaderParam (quake_skybox.program, &quake_skybox.mvp_matrix);
GL_ResolveShaderParam (quake_skybox.program, &quake_skybox.sky_matrix);
GL_ResolveShaderParam (quake_skybox.program, &quake_skybox.vertex);
GL_ResolveShaderParam (quake_skybox.program, &quake_skybox.sky);
GL_ResolveShaderParam (quake_skybox.program, &quake_skybox.fog);
}
VISIBLE void
R_LoadSkys (const char *sky)
{
const char *name;
int i;
// NOTE: quake's world and GL's world are rotated relative to each other
// quake has x right, y in, z up. gl has x right, y up, z out
static const char *sky_suffix[] = { "ft", "bk", "lf", "rt", "up", "dn"};
static int sky_target[] = {
GL_TEXTURE_CUBE_MAP_POSITIVE_X,
GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
};
if (!sky || !*sky)
sky = r_skyname->string;
if (!*sky || !strcasecmp (sky, "none")) {
skybox_loaded = false;
return;
}
if (!skybox_tex)
qfglGenTextures (1, &skybox_tex);
qfglBindTexture (GL_TEXTURE_CUBE_MAP, skybox_tex);
skybox_loaded = true;
for (i = 0; i < 6; i++) {
tex_t *tex;
tex = LoadImage (name = va ("env/%s%s", sky, sky_suffix[i]));
if (!tex || tex->format < 3) { // FIXME pcx support
Sys_MaskPrintf (SYS_GLSL, "Couldn't load %s\n", name);
// also look in gfx/env, where Darkplaces looks for skies
tex = LoadImage (name = va ("gfx/env/%s%s", sky, sky_suffix[i]));
if (!tex || tex->format < 3) { // FIXME pcx support
Sys_MaskPrintf (SYS_GLSL, "Couldn't load %s\n", name);
skybox_loaded = false;
continue;
}
}
Sys_MaskPrintf (SYS_GLSL, "Loaded %s\n", name);
qfglTexImage2D (sky_target[i], 0, tex->format == 3 ? GL_RGB : GL_RGBA,
tex->width, tex->height, 0,
tex->format == 3 ? GL_RGB : GL_RGBA,
GL_UNSIGNED_BYTE, tex->data);
}
qfglTexParameteri (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
qfglTexParameteri (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
qfglTexParameteri (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
qfglTexParameteri (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
qfglGenerateMipmap (GL_TEXTURE_CUBE_MAP);
}