| /* |
| 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; |
| } |