/* | |
Copyright (C) 1996-1997 Id Software, Inc. | |
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 the Free Software | |
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
*/ | |
// r_light.c | |
#include "quakedef.h" | |
int r_dlightframecount; | |
/* | |
================== | |
R_AnimateLight | |
================== | |
*/ | |
void R_AnimateLight (void) | |
{ | |
int i,j,k; | |
// | |
// light animations | |
// 'm' is normal light, 'a' is no light, 'z' is double bright | |
i = (int)(cl.time*10); | |
for (j=0 ; j<MAX_LIGHTSTYLES ; j++) | |
{ | |
if (!cl_lightstyle[j].length) | |
{ | |
d_lightstylevalue[j] = 256; | |
continue; | |
} | |
k = i % cl_lightstyle[j].length; | |
k = cl_lightstyle[j].map[k] - 'a'; | |
k = k*22; | |
d_lightstylevalue[j] = k; | |
} | |
} | |
/* | |
============================================================================= | |
DYNAMIC LIGHTS BLEND RENDERING | |
============================================================================= | |
*/ | |
void AddLightBlend (float r, float g, float b, float a2) | |
{ | |
float a; | |
v_blend[3] = a = v_blend[3] + a2*(1-v_blend[3]); | |
a2 = a2/a; | |
v_blend[0] = v_blend[1]*(1-a2) + r*a2; | |
v_blend[1] = v_blend[1]*(1-a2) + g*a2; | |
v_blend[2] = v_blend[2]*(1-a2) + b*a2; | |
} | |
void R_RenderDlight (dlight_t *light) | |
{ | |
int i, j; | |
float a; | |
vec3_t v; | |
float rad; | |
rad = light->radius * 0.35; | |
VectorSubtract (light->origin, r_origin, v); | |
if (Length (v) < rad) | |
{ // view is inside the dlight | |
AddLightBlend (1, 0.5, 0, light->radius * 0.0003); | |
return; | |
} | |
#ifdef USE_OPENGLES | |
glEnableClientState(GL_COLOR_ARRAY); | |
glDisableClientState(GL_TEXTURE_COORD_ARRAY); | |
glVertexPointer(3, GL_FLOAT, 0, gVertexBuffer); | |
glColorPointer(4, GL_FLOAT, 0, gColorBuffer); | |
{ | |
float* pPos = gVertexBuffer; | |
float* pColor = gColorBuffer; | |
*pColor++ = 0.2f; | |
*pColor++ = 0.1f; | |
*pColor++ = 0.0f; | |
*pColor++ = 1.0f; | |
for (i=0 ; i<3 ; i++) | |
*pPos++ = light->origin[i] - vpn[i]*rad; | |
for (i=16 ; i>=0 ; i--) | |
{ | |
*pColor++ = 0.0f; | |
*pColor++ = 0.0f; | |
*pColor++ = 0.0f; | |
*pColor++ = 0.0f; | |
a = i/16.0 * M_PI*2; | |
for (j=0 ; j<3 ; j++) | |
*pPos++ = light->origin[j] + vright[j]*cos(a)*rad | |
+ vup[j]*sin(a)*rad; | |
} | |
} | |
glDrawArrays(GL_TRIANGLE_FAN, 0, 18); | |
glDisableClientState(GL_COLOR_ARRAY); | |
glEnableClientState(GL_TEXTURE_COORD_ARRAY); | |
glColor3f(0,0,0); // Ensure the color ends up being zero just like the non-OpenGLES code | |
#else | |
glBegin (GL_TRIANGLE_FAN); | |
glColor3f (0.2,0.1,0.0); | |
for (i=0 ; i<3 ; i++) | |
v[i] = light->origin[i] - vpn[i]*rad; | |
glVertex3fv (v); | |
glColor3f (0,0,0); | |
for (i=16 ; i>=0 ; i--) | |
{ | |
a = i/16.0 * M_PI*2; | |
for (j=0 ; j<3 ; j++) | |
v[j] = light->origin[j] + vright[j]*cos(a)*rad | |
+ vup[j]*sin(a)*rad; | |
glVertex3fv (v); | |
} | |
glEnd (); | |
#endif | |
} | |
/* | |
============= | |
R_RenderDlights | |
============= | |
*/ | |
void R_RenderDlights (void) | |
{ | |
int i; | |
dlight_t *l; | |
if (!gl_flashblend.value) | |
return; | |
r_dlightframecount = r_framecount + 1; // because the count hasn't | |
// advanced yet for this frame | |
glDepthMask (0); | |
glDisable (GL_TEXTURE_2D); | |
glShadeModel (GL_SMOOTH); | |
glEnable (GL_BLEND); | |
glBlendFunc (GL_ONE, GL_ONE); | |
l = cl_dlights; | |
for (i=0 ; i<MAX_DLIGHTS ; i++, l++) | |
{ | |
if (l->die < cl.time || !l->radius) | |
continue; | |
R_RenderDlight (l); | |
} | |
glColor3f (1,1,1); | |
glDisable (GL_BLEND); | |
glEnable (GL_TEXTURE_2D); | |
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
glDepthMask (1); | |
} | |
/* | |
============================================================================= | |
DYNAMIC LIGHTS | |
============================================================================= | |
*/ | |
/* | |
============= | |
R_MarkLights | |
============= | |
*/ | |
void R_MarkLights (dlight_t *light, int bit, mnode_t *node) | |
{ | |
mplane_t *splitplane; | |
float dist; | |
msurface_t *surf; | |
int i; | |
if (node->contents < 0) | |
return; | |
splitplane = node->plane; | |
dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist; | |
if (dist > light->radius) | |
{ | |
R_MarkLights (light, bit, node->children[0]); | |
return; | |
} | |
if (dist < -light->radius) | |
{ | |
R_MarkLights (light, bit, node->children[1]); | |
return; | |
} | |
// mark the polygons | |
surf = cl.worldmodel->surfaces + node->firstsurface; | |
for (i=0 ; i<node->numsurfaces ; i++, surf++) | |
{ | |
if (surf->dlightframe != r_dlightframecount) | |
{ | |
surf->dlightbits = 0; | |
surf->dlightframe = r_dlightframecount; | |
} | |
surf->dlightbits |= bit; | |
} | |
R_MarkLights (light, bit, node->children[0]); | |
R_MarkLights (light, bit, node->children[1]); | |
} | |
/* | |
============= | |
R_PushDlights | |
============= | |
*/ | |
void R_PushDlights (void) | |
{ | |
int i; | |
dlight_t *l; | |
if (gl_flashblend.value) | |
return; | |
r_dlightframecount = r_framecount + 1; // because the count hasn't | |
// advanced yet for this frame | |
l = cl_dlights; | |
for (i=0 ; i<MAX_DLIGHTS ; i++, l++) | |
{ | |
if (l->die < cl.time || !l->radius) | |
continue; | |
R_MarkLights ( l, 1<<i, cl.worldmodel->nodes ); | |
} | |
} | |
/* | |
============================================================================= | |
LIGHT SAMPLING | |
============================================================================= | |
*/ | |
mplane_t *lightplane; | |
vec3_t lightspot; | |
int RecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end) | |
{ | |
int r; | |
float front, back, frac; | |
int side; | |
mplane_t *plane; | |
vec3_t mid; | |
msurface_t *surf; | |
int s, t, ds, dt; | |
int i; | |
mtexinfo_t *tex; | |
byte *lightmap; | |
unsigned scale; | |
int maps; | |
if (node->contents < 0) | |
return -1; // didn't hit anything | |
// calculate mid point | |
// FIXME: optimize for axial | |
plane = node->plane; | |
front = DotProduct (start, plane->normal) - plane->dist; | |
back = DotProduct (end, plane->normal) - plane->dist; | |
side = front < 0; | |
if ( (back < 0) == side) | |
return RecursiveLightPoint (node->children[side], start, end); | |
frac = front / (front-back); | |
mid[0] = start[0] + (end[0] - start[0])*frac; | |
mid[1] = start[1] + (end[1] - start[1])*frac; | |
mid[2] = start[2] + (end[2] - start[2])*frac; | |
// go down front side | |
r = RecursiveLightPoint (node->children[side], start, mid); | |
if (r >= 0) | |
return r; // hit something | |
if ( (back < 0) == side ) | |
return -1; // didn't hit anuthing | |
// check for impact on this node | |
VectorCopy (mid, lightspot); | |
lightplane = plane; | |
surf = cl.worldmodel->surfaces + node->firstsurface; | |
for (i=0 ; i<node->numsurfaces ; i++, surf++) | |
{ | |
if (surf->flags & SURF_DRAWTILED) | |
continue; // no lightmaps | |
tex = surf->texinfo; | |
s = (int) (DotProduct (mid, tex->vecs[0]) + tex->vecs[0][3]); | |
t = (int) (DotProduct (mid, tex->vecs[1]) + tex->vecs[1][3]); | |
if (s < surf->texturemins[0] || | |
t < surf->texturemins[1]) | |
continue; | |
ds = s - surf->texturemins[0]; | |
dt = t - surf->texturemins[1]; | |
if ( ds > surf->extents[0] || dt > surf->extents[1] ) | |
continue; | |
if (!surf->samples) | |
return 0; | |
ds >>= 4; | |
dt >>= 4; | |
lightmap = surf->samples; | |
r = 0; | |
if (lightmap) | |
{ | |
lightmap += dt * ((surf->extents[0]>>4)+1) + ds; | |
for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; | |
maps++) | |
{ | |
scale = d_lightstylevalue[surf->styles[maps]]; | |
r += *lightmap * scale; | |
lightmap += ((surf->extents[0]>>4)+1) * | |
((surf->extents[1]>>4)+1); | |
} | |
r >>= 8; | |
} | |
return r; | |
} | |
// go down back side | |
return RecursiveLightPoint (node->children[!side], mid, end); | |
} | |
int R_LightPoint (vec3_t p) | |
{ | |
vec3_t end; | |
int r; | |
if (!cl.worldmodel->lightdata) | |
return 255; | |
end[0] = p[0]; | |
end[1] = p[1]; | |
end[2] = p[2] - 2048; | |
r = RecursiveLightPoint (cl.worldmodel->nodes, p, end); | |
if (r == -1) | |
r = 0; | |
return r; | |
} |