/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code 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. Quake III Arena source code 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 Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "cmdlib.h" #include "mathlib.h" #include "bspfile.h" #include "imagelib.h" #include "threads.h" #include "mutex.h" #include "scriplib.h" #include "shaders.h" #include "mesh.h" #ifdef _WIN32 //Improve floating-point consistency. #pragma optimize( "p", on ) #endif #ifdef _WIN32 #include "../libs/pakstuff.h" #endif #define MAX_CLUSTERS 16384 #define MAX_PORTALS 32768 #define MAX_FACETS 65536 #define MAX_LIGHTS 16384 #define LIGHTMAP_SIZE 128 #define LIGHTMAP_PIXELSHIFT 0.5 //#define LIGHTMAP_PATCHSHIFT #define PORTALFILE "PRT1" #define ON_EPSILON 0.1 #define VectorSet(v, x, y, z) v[0] = x;v[1] = y;v[2] = z; typedef struct { vec3_t normal; float dist; } plane_t; #define MAX_POINTS_ON_WINDING 64 //NOTE: whenever this is overflowed parts of lightmaps might end up not being lit #define MAX_POINTS_ON_FIXED_WINDING 48 typedef struct { int numpoints; vec3_t points[MAX_POINTS_ON_FIXED_WINDING]; // variable sized } winding_t; typedef struct { plane_t plane; // normal pointing into neighbor int leaf; // neighbor winding_t *winding; vec3_t origin; // for fast clip testing float radius; } lportal_t; #define MAX_PORTALS_ON_LEAF 128 typedef struct lleaf_s { int numportals; lportal_t *portals[MAX_PORTALS_ON_LEAF]; // int numSurfaces; int firstSurface; } lleaf_t; typedef struct lFacet_s { int num; plane_t plane; vec3_t points[4]; // int numpoints; float lightmapCoords[4][2]; plane_t boundaries[4]; // negative is outside the bounds float textureMatrix[2][4]; // texture coordinates for translucency float lightmapMatrix[2][4]; // lightmap texture coordinates vec3_t mins; int x, y, width, height; } lFacet_t; typedef struct lsurfaceTest_s { vec3_t mins, maxs; vec3_t origin; float radius; qboolean patch; // true if this is a patch qboolean trisoup; // true if this is a triangle soup int numFacets; lFacet_t *facets; mesh_t *detailMesh; // detailed mesh with points for each lmp shaderInfo_t *shader; // for translucency mutex_t *mutex; int numvolumes; // number of volumes casted at this surface // int always_tracelight; int always_vlight; } lsurfaceTest_t; //volume types #define VOLUME_NORMAL 0 #define VOLUME_DIRECTED 1 #define MAX_TRANSLUCENTFACETS 32 typedef struct lightvolume_s { int num; int cluster; //cluster this light volume started in plane_t endplane; //end plane plane_t farplane; //original end plane vec3_t points[MAX_POINTS_ON_WINDING]; //end winding points plane_t planes[MAX_POINTS_ON_WINDING]; //volume bounding planes int numplanes; //number of volume bounding planes int type; //light volume type //list with translucent surfaces the volume went through int transFacets[MAX_TRANSLUCENTFACETS]; int transSurfaces[MAX_TRANSLUCENTFACETS]; int numtransFacets; //clusters already tested byte clusterTested[MAX_CLUSTERS/8]; //facets already tested byte facetTested[MAX_FACETS/8]; int facetNum; //number of the facet blocking the light in this volume int surfaceNum; //number of the surface blocking the light in this volume } lightvolume_t; //light types #define LIGHT_POINTRADIAL 1 #define LIGHT_POINTSPOT 2 #define LIGHT_POINTFAKESURFACE 3 #define LIGHT_SURFACEDIRECTED 4 #define LIGHT_SURFACERADIAL 5 #define LIGHT_SURFACESPOT 6 //light distance attenuation types #define LDAT_QUADRATIC 0 #define LDAT_LINEAR 1 #define LDAT_NOSCALE 2 //light angle attenuation types #define LAAT_NORMAL 0 #define LAAT_QUADRATIC 1 #define LAAT_DOUBLEQUADRATIC 2 typedef struct vlight_s { vec3_t origin; //light origin, for point lights winding_t w; //light winding, for area lights vec4_t plane; //light winding plane vec3_t normal; //direction of the light int type; //light type vec3_t color; //light color qboolean twosided; //radiates light at both sides of the winding int style; //light style (not used) int atten_disttype; //light distance attenuation type int atten_angletype; //light angle attenuation type float atten_distscale; //distance attenuation scale float atten_anglescale; //angle attenuation scale float radiusByDist; //radius by distance for spot lights float photons; //emitted photons float intensity; //intensity vec3_t emitColor; //full out-of-gamut value (not used) struct shaderInfo_s *si; //shader info int insolid; //set when light is in solid } vlight_t; float lightLinearScale = 1.0 / 8000; float lightPointScale = 7500; float lightAreaScale = 0.25; float lightFormFactorValueScale = 3; int lightDefaultSubdivide = 999; // vary by surface size? vec3_t lightAmbientColor; int portalclusters, numportals, numfaces; lleaf_t *leafs; lportal_t *portals; int numvlights = 0; vlight_t *vlights[MAX_LIGHTS]; int nostitching = 0; int noalphashading = 0; int nocolorshading = 0; int nobackfaceculling = 0; int defaulttracelight = 0; int radiosity = 0; int radiosity_scale; int clustersurfaces[MAX_MAP_LEAFFACES]; int numclustersurfaces = 0; lsurfaceTest_t *lsurfaceTest[MAX_MAP_DRAW_SURFS]; int numfacets; float lightmappixelarea[MAX_MAP_LIGHTING/3]; float *lightFloats;//[MAX_MAP_LIGHTING]; // from polylib.c winding_t *AllocWinding (int points); void FreeWinding (winding_t *w); void WindingCenter (winding_t *w, vec3_t center); void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs); vec_t WindingArea (winding_t *w); winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist); void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist, vec_t epsilon, winding_t **front, winding_t **back); winding_t *ReverseWinding (winding_t *w); // from light.c extern char source[1024]; extern vec3_t surfaceOrigin[ MAX_MAP_DRAW_SURFS ]; extern int entitySurface[ MAX_MAP_DRAW_SURFS ]; extern int samplesize; extern int novertexlighting; extern int nogridlighting; extern qboolean patchshadows; extern vec3_t gridSize; float PointToPolygonFormFactor( const vec3_t point, const vec3_t normal, const winding_t *w ); void ColorToBytes( const float *color, byte *colorBytes ); void CountLightmaps( void ); void GridAndVertexLighting( void ); void SetEntityOrigins( void ); //#define DEBUGNET #ifdef DEBUGNET #include "l_net.h" socket_t *debug_socket; /* ===================== DebugNet_Setup ===================== */ void DebugNet_Setup(void) { address_t address; int i; Net_Setup(); Net_StringToAddress("127.0.0.1:28000", &address); for (i = 0; i < 10; i++) { debug_socket = Net_Connect(&address, 28005 + i); if (debug_socket) break; } } /* ===================== DebugNet_Shutdown ===================== */ void DebugNet_Shutdown(void) { netmessage_t msg; if (debug_socket) { NMSG_Clear(&msg); NMSG_WriteByte(&msg, 1); Net_Send(debug_socket, &msg); Net_Disconnect(debug_socket); } debug_socket = NULL; Net_Shutdown(); } /* ===================== DebugNet_RemoveAllPolys ===================== */ void DebugNet_RemoveAllPolys(void) { netmessage_t msg; if (!debug_socket) return; NMSG_Clear(&msg); NMSG_WriteByte(&msg, 2); //remove all debug polys Net_Send(debug_socket, &msg); } /* ==================== DebugNet_DrawWinding ===================== */ void DebugNet_DrawWinding(winding_t *w, int color) { netmessage_t msg; int i; if (!debug_socket) return; NMSG_Clear(&msg); NMSG_WriteByte(&msg, 0); //draw a winding NMSG_WriteByte(&msg, w->numpoints); //number of points NMSG_WriteLong(&msg, color); //color for (i = 0; i < w->numpoints; i++) { NMSG_WriteFloat(&msg, w->points[i][0]); NMSG_WriteFloat(&msg, w->points[i][1]); NMSG_WriteFloat(&msg, w->points[i][2]); } Net_Send(debug_socket, &msg); } /* ===================== DebugNet_DrawLine ===================== */ void DebugNet_DrawLine(vec3_t p1, vec3_t p2, int color) { netmessage_t msg; if (!debug_socket) return; NMSG_Clear(&msg); NMSG_WriteByte(&msg, 1); //draw a line NMSG_WriteLong(&msg, color); //color NMSG_WriteFloat(&msg, p1[0]); NMSG_WriteFloat(&msg, p1[1]); NMSG_WriteFloat(&msg, p1[2]); NMSG_WriteFloat(&msg, p2[0]); NMSG_WriteFloat(&msg, p2[1]); NMSG_WriteFloat(&msg, p2[2]); Net_Send(debug_socket, &msg); } /* ===================== DebugNet_DrawMesh ===================== */ void DebugNet_DrawMesh(mesh_t *mesh) { int i, j; float dot; drawVert_t *v1, *v2, *v3, *v4; winding_t winding; plane_t plane; vec3_t d1, d2; for ( i = 0 ; i < mesh->width - 1 ; i++ ) { for ( j = 0 ; j < mesh->height - 1 ; j++ ) { v1 = mesh->verts + j * mesh->width + i; v2 = v1 + 1; v3 = v1 + mesh->width + 1; v4 = v1 + mesh->width; VectorSubtract( v4->xyz, v1->xyz, d1 ); VectorSubtract( v3->xyz, v1->xyz, d2 ); CrossProduct( d2, d1, plane.normal ); if ( VectorNormalize( plane.normal, plane.normal ) != 0 ) { plane.dist = DotProduct( v1->xyz, plane.normal ); dot = DotProduct(plane.normal, v2->xyz) - plane.dist; if (fabs(dot) < 0.1) { VectorCopy(v1->xyz, winding.points[0]); VectorCopy(v4->xyz, winding.points[1]); VectorCopy(v3->xyz, winding.points[2]); VectorCopy(v2->xyz, winding.points[3]); winding.numpoints = 4; DebugNet_DrawWinding(&winding, 2); continue; } } winding.numpoints = 3; VectorCopy(v1->xyz, winding.points[0]); VectorCopy(v4->xyz, winding.points[1]); VectorCopy(v3->xyz, winding.points[2]); DebugNet_DrawWinding(&winding, 2); VectorCopy(v1->xyz, winding.points[0]); VectorCopy(v3->xyz, winding.points[1]); VectorCopy(v2->xyz, winding.points[2]); DebugNet_DrawWinding(&winding, 2); } } } /* ===================== VL_DrawLightVolume ===================== */ int VL_ChopWinding (winding_t *in, plane_t *split, float epsilon); void VL_DrawLightVolume(vlight_t *light, lightvolume_t *volume) { winding_t w; int i; vec3_t p2, invlight; memcpy(w.points, volume->points, volume->numplanes * sizeof(vec3_t)); w.numpoints = volume->numplanes; DebugNet_DrawWinding(&w, 2); if (volume->type == VOLUME_DIRECTED) { VectorCopy(light->normal, invlight); VectorInverse(invlight); for (i = 0; i < volume->numplanes; i++) { VectorCopy(volume->points[i], w.points[0]); VectorCopy(volume->points[(i+1) % volume->numplanes], w.points[1]); VectorMA(w.points[1], MAX_WORLD_COORD, invlight, w.points[2]); VectorMA(w.points[0], MAX_WORLD_COORD, invlight, w.points[3]); w.numpoints = 4; DebugNet_DrawWinding(&w, 2); VectorMA(volume->points[i], 8, volume->planes[i].normal, p2); DebugNet_DrawLine(volume->points[i], p2, 3); } } else { // VectorCopy(light->origin, w.points[0]); w.numpoints = 3; for (i = 0; i < volume->numplanes; i++) { VectorCopy(volume->points[i], w.points[1]); VectorCopy(volume->points[(i+1) % volume->numplanes], w.points[2]); VL_ChopWinding(&w, &volume->endplane, 0); DebugNet_DrawWinding(&w, 2); VectorMA(volume->points[i], 8, volume->planes[i].normal, p2); DebugNet_DrawLine(volume->points[i], p2, 3); } } } /* ============= VL_DrawLightmapPixel ============= */ void VL_DrawLightmapPixel(int surfaceNum, int x, int y, int color) { winding_t w; dsurface_t *ds; mesh_t *mesh; ds = &drawSurfaces[surfaceNum]; if (ds->surfaceType == MST_PATCH) { mesh = lsurfaceTest[surfaceNum]->detailMesh; VectorCopy( mesh->verts[(y-ds->lightmapY)*mesh->width+x-ds->lightmapX].xyz, w.points[0]); VectorCopy( mesh->verts[(y+1-ds->lightmapY)*mesh->width+x-ds->lightmapX].xyz, w.points[1]); VectorCopy( mesh->verts[(y+1-ds->lightmapY)*mesh->width+x+1-ds->lightmapX].xyz, w.points[2]); VectorCopy( mesh->verts[(y-ds->lightmapY)*mesh->width+x+1-ds->lightmapX].xyz, w.points[3]); w.numpoints = 4; } else { VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT - ds->lightmapX, ds->lightmapVecs[0], w.points[0]); VectorMA(w.points[0], (float) y - LIGHTMAP_PIXELSHIFT - ds->lightmapY, ds->lightmapVecs[1], w.points[0]); VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT - ds->lightmapX, ds->lightmapVecs[0], w.points[1]); VectorMA(w.points[1], (float) y - LIGHTMAP_PIXELSHIFT + 1 - ds->lightmapY, ds->lightmapVecs[1], w.points[1]); VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT + 1 - ds->lightmapX, ds->lightmapVecs[0], w.points[2]); VectorMA(w.points[2], (float) y - LIGHTMAP_PIXELSHIFT + 1 - ds->lightmapY, ds->lightmapVecs[1], w.points[2]); VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT + 1 - ds->lightmapX, ds->lightmapVecs[0], w.points[3]); VectorMA(w.points[3], (float) y - LIGHTMAP_PIXELSHIFT - ds->lightmapY, ds->lightmapVecs[1], w.points[3]); w.numpoints = 4; } DebugNet_DrawWinding(&w, color); } /* ============ VL_DrawPortals ============ */ void VL_DrawPortals(void) { int j; lportal_t *p; for (j = 0; j < numportals * 2; j++) { p = portals + j; DebugNet_DrawWinding(p->winding, 1); } } /* ============ VL_DrawLeaf ============ */ void VL_DrawLeaf(int cluster) { int i; lleaf_t *leaf; lportal_t *p; leaf = &leafs[cluster]; for (i = 0; i < leaf->numportals; i++) { p = leaf->portals[i]; DebugNet_DrawWinding(p->winding, 1); } } #endif //DEBUGNET /* ============= VL_SplitWinding ============= */ int VL_SplitWinding (winding_t *in, winding_t *back, plane_t *split, float epsilon) { vec_t dists[128]; int sides[128]; int counts[3]; vec_t dot; int i, j; vec_t *p1, *p2; vec3_t mid; winding_t out; winding_t *neww; counts[0] = counts[1] = counts[2] = 0; // determine sides for each point for (i=0 ; inumpoints ; i++) { dot = DotProduct (in->points[i], split->normal); dot -= split->dist; dists[i] = dot; if (dot > epsilon) sides[i] = SIDE_FRONT; else if (dot < -epsilon) sides[i] = SIDE_BACK; else { sides[i] = SIDE_ON; } counts[sides[i]]++; } if (!counts[SIDE_BACK]) { if (!counts[SIDE_FRONT]) return SIDE_ON; else return SIDE_FRONT; } if (!counts[SIDE_FRONT]) { return SIDE_BACK; } sides[i] = sides[0]; dists[i] = dists[0]; neww = &out; neww->numpoints = 0; back->numpoints = 0; for (i=0 ; inumpoints ; i++) { p1 = in->points[i]; if (neww->numpoints >= MAX_POINTS_ON_FIXED_WINDING) { _printf("WARNING: VL_SplitWinding -> MAX_POINTS_ON_FIXED_WINDING overflowed\n"); return SIDE_FRONT; // can't chop -- fall back to original } if (back->numpoints >= MAX_POINTS_ON_FIXED_WINDING) { _printf("WARNING: VL_SplitWinding -> MAX_POINTS_ON_FIXED_WINDING overflowed\n"); return SIDE_FRONT; } if (sides[i] == SIDE_ON) { VectorCopy (p1, neww->points[neww->numpoints]); neww->numpoints++; VectorCopy (p1, back->points[back->numpoints]); back->numpoints++; continue; } if (sides[i] == SIDE_FRONT) { VectorCopy (p1, neww->points[neww->numpoints]); neww->numpoints++; } if (sides[i] == SIDE_BACK) { VectorCopy (p1, back->points[back->numpoints]); back->numpoints++; } if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue; if (neww->numpoints >= MAX_POINTS_ON_FIXED_WINDING) { _printf("WARNING: VL_SplitWinding -> MAX_POINTS_ON_FIXED_WINDING overflowed\n"); return SIDE_FRONT; // can't chop -- fall back to original } if (back->numpoints >= MAX_POINTS_ON_FIXED_WINDING) { _printf("WARNING: VL_SplitWinding -> MAX_POINTS_ON_FIXED_WINDING overflowed\n"); return SIDE_FRONT; // can't chop -- fall back to original } // generate a split point p2 = in->points[(i+1)%in->numpoints]; dot = dists[i] / (dists[i]-dists[i+1]); for (j=0 ; j<3 ; j++) { // avoid round off error when possible if (split->normal[j] == 1) mid[j] = split->dist; else if (split->normal[j] == -1) mid[j] = -split->dist; else mid[j] = p1[j] + dot*(p2[j]-p1[j]); } VectorCopy (mid, neww->points[neww->numpoints]); neww->numpoints++; VectorCopy (mid, back->points[back->numpoints]); back->numpoints++; } memcpy(in, &out, sizeof(winding_t)); return SIDE_CROSS; } /* ===================== VL_LinkSurfaceIntoCluster ===================== */ void VL_LinkSurfaceIntoCluster(int cluster, int surfaceNum) { lleaf_t *leaf; int i; leaf = &leafs[cluster]; for (i = 0; i < leaf->numSurfaces; i++) { if (clustersurfaces[leaf->firstSurface + i] == surfaceNum) return; } for (i = numclustersurfaces; i > leaf->firstSurface + leaf->numSurfaces; i--) clustersurfaces[i] = clustersurfaces[i-1]; for (i = 0; i < portalclusters; i++) { if (i == cluster) continue; if (leafs[i].firstSurface >= leaf->firstSurface + leaf->numSurfaces) leafs[i].firstSurface++; } clustersurfaces[leaf->firstSurface + leaf->numSurfaces] = surfaceNum; leaf->numSurfaces++; numclustersurfaces++; if (numclustersurfaces >= MAX_MAP_LEAFFACES) Error("MAX_MAP_LEAFFACES"); } /* ===================== VL_R_LinkSurface ===================== */ void VL_R_LinkSurface(int nodenum, int surfaceNum, winding_t *w) { int leafnum, cluster, res; dnode_t *node; dplane_t *plane; winding_t back; plane_t split; while(nodenum >= 0) { node = &dnodes[nodenum]; plane = &dplanes[node->planeNum]; VectorCopy(plane->normal, split.normal); split.dist = plane->dist; res = VL_SplitWinding (w, &back, &split, 0.1); if (res == SIDE_FRONT) { nodenum = node->children[0]; } else if (res == SIDE_BACK) { nodenum = node->children[1]; } else if (res == SIDE_ON) { memcpy(&back, w, sizeof(winding_t)); VL_R_LinkSurface(node->children[1], surfaceNum, &back); nodenum = node->children[0]; } else { VL_R_LinkSurface(node->children[1], surfaceNum, &back); nodenum = node->children[0]; } } leafnum = -nodenum - 1; cluster = dleafs[leafnum].cluster; if (cluster != -1) { VL_LinkSurfaceIntoCluster(cluster, surfaceNum); } } /* ===================== VL_LinkSurfaces maybe link each facet seperately instead of the test surfaces? ===================== */ void VL_LinkSurfaces(void) { int i, j; lsurfaceTest_t *test; lFacet_t *facet; winding_t winding; for ( i = 0 ; i < numDrawSurfaces ; i++ ) { test = lsurfaceTest[ i ]; if (!test) continue; for (j = 0; j < test->numFacets; j++) { facet = &test->facets[j]; memcpy(winding.points, facet->points, facet->numpoints * sizeof(vec3_t)); winding.numpoints = facet->numpoints; VL_R_LinkSurface(0, i, &winding); } } } /* ===================== VL_TextureMatrixFromPoints ===================== */ void VL_TextureMatrixFromPoints( lFacet_t *f, drawVert_t *a, drawVert_t *b, drawVert_t *c ) { int i, j; float t; float m[3][4]; float s; // This is an incredibly stupid way of solving a three variable equation for ( i = 0 ; i < 2 ; i++ ) { m[0][0] = a->xyz[0]; m[0][1] = a->xyz[1]; m[0][2] = a->xyz[2]; m[0][3] = a->st[i]; m[1][0] = b->xyz[0]; m[1][1] = b->xyz[1]; m[1][2] = b->xyz[2]; m[1][3] = b->st[i]; m[2][0] = c->xyz[0]; m[2][1] = c->xyz[1]; m[2][2] = c->xyz[2]; m[2][3] = c->st[i]; if ( fabs(m[1][0]) > fabs(m[0][0]) && fabs(m[1][0]) > fabs(m[2][0]) ) { for ( j = 0 ; j < 4 ; j ++ ) { t = m[0][j]; m[0][j] = m[1][j]; m[1][j] = t; } } else if ( fabs(m[2][0]) > fabs(m[0][0]) && fabs(m[2][0]) > fabs(m[1][0]) ) { for ( j = 0 ; j < 4 ; j ++ ) { t = m[0][j]; m[0][j] = m[2][j]; m[2][j] = t; } } s = 1.0 / m[0][0]; m[0][0] *= s; m[0][1] *= s; m[0][2] *= s; m[0][3] *= s; s = m[1][0]; m[1][0] -= m[0][0] * s; m[1][1] -= m[0][1] * s; m[1][2] -= m[0][2] * s; m[1][3] -= m[0][3] * s; s = m[2][0]; m[2][0] -= m[0][0] * s; m[2][1] -= m[0][1] * s; m[2][2] -= m[0][2] * s; m[2][3] -= m[0][3] * s; if ( fabs(m[2][1]) > fabs(m[1][1]) ) { for ( j = 0 ; j < 4 ; j ++ ) { t = m[1][j]; m[1][j] = m[2][j]; m[2][j] = t; } } s = 1.0 / m[1][1]; m[1][0] *= s; m[1][1] *= s; m[1][2] *= s; m[1][3] *= s; s = m[2][1];// / m[1][1]; m[2][0] -= m[1][0] * s; m[2][1] -= m[1][1] * s; m[2][2] -= m[1][2] * s; m[2][3] -= m[1][3] * s; s = 1.0 / m[2][2]; m[2][0] *= s; m[2][1] *= s; m[2][2] *= s; m[2][3] *= s; f->textureMatrix[i][2] = m[2][3]; f->textureMatrix[i][1] = m[1][3] - f->textureMatrix[i][2] * m[1][2]; f->textureMatrix[i][0] = m[0][3] - f->textureMatrix[i][2] * m[0][2] - f->textureMatrix[i][1] * m[0][1]; f->textureMatrix[i][3] = 0; /* s = fabs( DotProduct( a->xyz, f->textureMatrix[i] ) - a->st[i] ); if ( s > 0.01 ) { Error( "Bad textureMatrix" ); } s = fabs( DotProduct( b->xyz, f->textureMatrix[i] ) - b->st[i] ); if ( s > 0.01 ) { Error( "Bad textureMatrix" ); } s = fabs( DotProduct( c->xyz, f->textureMatrix[i] ) - c->st[i] ); if ( s > 0.01 ) { Error( "Bad textureMatrix" ); } */ } } /* ===================== VL_LightmapMatrixFromPoints ===================== */ void VL_LightmapMatrixFromPoints( dsurface_t *dsurf, shaderInfo_t *si, lFacet_t *f, drawVert_t *a, drawVert_t *b, drawVert_t *c ) { int i, j; float t; float m[3][4], al, bl, cl; float s; int h, w, ssize; vec3_t mins, maxs, delta, size, planeNormal; drawVert_t *verts; static int message; // vertex-lit triangle model if ( dsurf->surfaceType == MST_TRIANGLE_SOUP ) { return; } if ( dsurf->lightmapNum < 0 ) { return; // doesn't need lighting } VectorClear(f->mins); if (dsurf->surfaceType != MST_PATCH) { ssize = samplesize; if (si->lightmapSampleSize) ssize = si->lightmapSampleSize; ClearBounds( mins, maxs ); verts = &drawVerts[dsurf->firstVert]; for ( i = 0 ; i < dsurf->numVerts ; i++ ) { AddPointToBounds( verts[i].xyz, mins, maxs ); } // round to the lightmap resolution for ( i = 0 ; i < 3 ; i++ ) { mins[i] = ssize * floor( mins[i] / ssize ); maxs[i] = ssize * ceil( maxs[i] / ssize ); f->mins[i] = mins[i]; size[i] = (maxs[i] - mins[i]) / ssize + 1; } // the two largest axis will be the lightmap size VectorClear(f->lightmapMatrix[0]); f->lightmapMatrix[0][3] = 0; VectorClear(f->lightmapMatrix[1]); f->lightmapMatrix[1][3] = 0; planeNormal[0] = fabs( dsurf->lightmapVecs[2][0] ); planeNormal[1] = fabs( dsurf->lightmapVecs[2][1] ); planeNormal[2] = fabs( dsurf->lightmapVecs[2][2] ); if ( planeNormal[0] >= planeNormal[1] && planeNormal[0] >= planeNormal[2] ) { w = size[1]; h = size[2]; f->lightmapMatrix[0][1] = 1.0 / ssize; f->lightmapMatrix[1][2] = 1.0 / ssize; } else if ( planeNormal[1] >= planeNormal[0] && planeNormal[1] >= planeNormal[2] ) { w = size[0]; h = size[2]; f->lightmapMatrix[0][0] = 1.0 / ssize; f->lightmapMatrix[1][2] = 1.0 / ssize; } else { w = size[0]; h = size[1]; f->lightmapMatrix[0][0] = 1.0 / ssize; f->lightmapMatrix[1][1] = 1.0 / ssize; } if ( w > LIGHTMAP_WIDTH ) { VectorScale ( f->lightmapMatrix[0], (float)LIGHTMAP_SIZE/w, f->lightmapMatrix[0] ); } if ( h > LIGHTMAP_HEIGHT ) { VectorScale ( f->lightmapMatrix[1], (float)LIGHTMAP_SIZE/h, f->lightmapMatrix[1] ); } VectorSubtract(a->xyz, f->mins, delta); s = (DotProduct( delta, f->lightmapMatrix[0] ) + dsurf->lightmapX + 0.5) / LIGHTMAP_SIZE; if ( fabs(s - a->lightmap[0]) > 0.01 ) { _printf( "Bad lightmapMatrix" ); } t = (DotProduct( delta, f->lightmapMatrix[1] ) + dsurf->lightmapY + 0.5) / LIGHTMAP_SIZE; if ( fabs(t - a->lightmap[1]) > 0.01 ) { _printf( "Bad lightmapMatrix" ); } VectorSubtract(b->xyz, f->mins, delta); s = (DotProduct( delta, f->lightmapMatrix[0] ) + dsurf->lightmapX + 0.5) / LIGHTMAP_SIZE; if ( fabs(s - b->lightmap[0]) > 0.01 ) { _printf( "Bad lightmapMatrix" ); } t = (DotProduct( delta, f->lightmapMatrix[1] ) + dsurf->lightmapY + 0.5) / LIGHTMAP_SIZE; if ( fabs(t - b->lightmap[1]) > 0.01 ) { _printf( "Bad lightmapMatrix" ); } VectorSubtract(c->xyz, f->mins, delta); s = (DotProduct( delta, f->lightmapMatrix[0] ) + dsurf->lightmapX + 0.5) / LIGHTMAP_SIZE; if ( fabs(s - c->lightmap[0]) > 0.01 ) { _printf( "Bad lightmapMatrix" ); } t = (DotProduct( delta, f->lightmapMatrix[1] ) + dsurf->lightmapY + 0.5) / LIGHTMAP_SIZE; if ( fabs(t - c->lightmap[1]) > 0.01 ) { _printf( "Bad lightmapMatrix" ); } VectorAdd(f->mins, surfaceOrigin[dsurf - drawSurfaces], f->mins); return; } // This is an incredibly stupid way of solving a three variable equation for ( i = 0 ; i < 2 ; i++ ) { if (i) al = a->lightmap[i] - ((float) dsurf->lightmapY + 0.5) / LIGHTMAP_SIZE; else al = a->lightmap[i] - ((float) dsurf->lightmapX + 0.5) / LIGHTMAP_SIZE; m[0][0] = a->xyz[0] - f->mins[0]; m[0][1] = a->xyz[1] - f->mins[1]; m[0][2] = a->xyz[2] - f->mins[2]; m[0][3] = al; if (i) bl = b->lightmap[i] - ((float) dsurf->lightmapY + 0.5) / LIGHTMAP_SIZE; else bl = b->lightmap[i] - ((float) dsurf->lightmapX + 0.5) / LIGHTMAP_SIZE; m[1][0] = b->xyz[0] - f->mins[0]; m[1][1] = b->xyz[1] - f->mins[1]; m[1][2] = b->xyz[2] - f->mins[2]; m[1][3] = bl; if (i) cl = c->lightmap[i] - ((float) dsurf->lightmapY + 0.5) / LIGHTMAP_SIZE; else cl = c->lightmap[i] - ((float) dsurf->lightmapX + 0.5) / LIGHTMAP_SIZE; m[2][0] = c->xyz[0] - f->mins[0]; m[2][1] = c->xyz[1] - f->mins[1]; m[2][2] = c->xyz[2] - f->mins[2]; m[2][3] = cl; if ( fabs(m[1][0]) > fabs(m[0][0]) && fabs(m[1][0]) >= fabs(m[2][0]) ) { for ( j = 0 ; j < 4 ; j ++ ) { t = m[0][j]; m[0][j] = m[1][j]; m[1][j] = t; } } else if ( fabs(m[2][0]) > fabs(m[0][0]) && fabs(m[2][0]) >= fabs(m[1][0]) ) { for ( j = 0 ; j < 4 ; j ++ ) { t = m[0][j]; m[0][j] = m[2][j]; m[2][j] = t; } } if (m[0][0]) { s = 1.0 / m[0][0]; m[0][0] *= s; m[0][1] *= s; m[0][2] *= s; m[0][3] *= s; s = m[1][0]; m[1][0] -= m[0][0] * s; m[1][1] -= m[0][1] * s; m[1][2] -= m[0][2] * s; m[1][3] -= m[0][3] * s; s = m[2][0]; m[2][0] -= m[0][0] * s; m[2][1] -= m[0][1] * s; m[2][2] -= m[0][2] * s; m[2][3] -= m[0][3] * s; } if ( fabs(m[2][1]) > fabs(m[1][1]) ) { for ( j = 0 ; j < 4 ; j ++ ) { t = m[1][j]; m[1][j] = m[2][j]; m[2][j] = t; } } if (m[1][1]) { s = 1.0 / m[1][1]; m[1][0] *= s; m[1][1] *= s; m[1][2] *= s; m[1][3] *= s; s = m[2][1]; m[2][0] -= m[1][0] * s; m[2][1] -= m[1][1] * s; m[2][2] -= m[1][2] * s; m[2][3] -= m[1][3] * s; } if (m[2][2]) { s = 1.0 / m[2][2]; m[2][0] *= s; m[2][1] *= s; m[2][2] *= s; m[2][3] *= s; } f->lightmapMatrix[i][2] = m[2][3]; f->lightmapMatrix[i][1] = m[1][3] - f->lightmapMatrix[i][2] * m[1][2]; f->lightmapMatrix[i][0] = m[0][3] - f->lightmapMatrix[i][2] * m[0][2] - f->lightmapMatrix[i][1] * m[0][1]; f->lightmapMatrix[i][3] = 0; VectorSubtract(a->xyz, f->mins, delta); s = fabs( DotProduct( delta, f->lightmapMatrix[i] ) - al ); if ( s > 0.01 ) { if (!message) _printf( "Bad lightmapMatrix\n" ); message = qtrue; } VectorSubtract(b->xyz, f->mins, delta); s = fabs( DotProduct( delta, f->lightmapMatrix[i] ) - bl ); if ( s > 0.01 ) { if (!message) _printf( "Bad lightmapMatrix\n" ); message = qtrue; } VectorSubtract(c->xyz, f->mins, delta); s = fabs( DotProduct( delta, f->lightmapMatrix[i] ) - cl ); if ( s > 0.01 ) { if (!message) _printf( "Bad lightmapMatrix\n" ); message = qtrue; } VectorAdd(f->mins, surfaceOrigin[dsurf - drawSurfaces], f->mins); } } /* ============= Plane_Equal ============= */ #define NORMAL_EPSILON 0.0001 #define DIST_EPSILON 0.02 int Plane_Equal(plane_t *a, plane_t *b, int flip) { vec3_t normal; float dist; if (flip) { normal[0] = - b->normal[0]; normal[1] = - b->normal[1]; normal[2] = - b->normal[2]; dist = - b->dist; } else { normal[0] = b->normal[0]; normal[1] = b->normal[1]; normal[2] = b->normal[2]; dist = b->dist; } if ( fabs(a->normal[0] - normal[0]) < NORMAL_EPSILON && fabs(a->normal[1] - normal[1]) < NORMAL_EPSILON && fabs(a->normal[2] - normal[2]) < NORMAL_EPSILON && fabs(a->dist - dist) < DIST_EPSILON ) return qtrue; return qfalse; } /* ============= VL_PlaneFromPoints ============= */ qboolean VL_PlaneFromPoints( plane_t *plane, const vec3_t a, const vec3_t b, const vec3_t c ) { vec3_t d1, d2; VectorSubtract( b, a, d1 ); VectorSubtract( c, a, d2 ); CrossProduct( d2, d1, plane->normal ); if ( VectorNormalize( plane->normal, plane->normal ) == 0 ) { return qfalse; } plane->dist = DotProduct( a, plane->normal ); return qtrue; } /* ===================== VL_GenerateBoundaryForPoints ===================== */ void VL_GenerateBoundaryForPoints( plane_t *boundary, plane_t *plane, vec3_t a, vec3_t b ) { vec3_t d1; // make a perpendicular vector to the edge and the surface VectorSubtract( a, b, d1 ); CrossProduct( plane->normal, d1, boundary->normal ); VectorNormalize( boundary->normal, boundary->normal ); boundary->dist = DotProduct( a, boundary->normal ); } /* ===================== VL_GenerateFacetFor3Points ===================== */ qboolean VL_GenerateFacetFor3Points( dsurface_t *dsurf, shaderInfo_t *si, lFacet_t *f, drawVert_t *a, drawVert_t *b, drawVert_t *c ) { // vec3_t dir; int i; // if we can't generate a valid plane for the points, ignore the facet if ( !VL_PlaneFromPoints( &f->plane, a->xyz, b->xyz, c->xyz ) ) { f->numpoints = 0; return qfalse; } f->num = numfacets++; VectorAdd( a->xyz, surfaceOrigin[dsurf - drawSurfaces], f->points[0] ); VectorAdd( b->xyz, surfaceOrigin[dsurf - drawSurfaces], f->points[1] ); VectorAdd( c->xyz, surfaceOrigin[dsurf - drawSurfaces], f->points[2] ); f->lightmapCoords[0][0] = a->lightmap[0]; f->lightmapCoords[0][1] = a->lightmap[1]; f->lightmapCoords[1][0] = b->lightmap[0]; f->lightmapCoords[1][1] = b->lightmap[1]; f->lightmapCoords[2][0] = c->lightmap[0]; f->lightmapCoords[2][1] = c->lightmap[1]; VL_GenerateBoundaryForPoints( &f->boundaries[0], &f->plane, f->points[0], f->points[1] ); VL_GenerateBoundaryForPoints( &f->boundaries[1], &f->plane, f->points[1], f->points[2] ); VL_GenerateBoundaryForPoints( &f->boundaries[2], &f->plane, f->points[2], f->points[0] ); for (i = 0; i < 3; i++) { VectorSubtract(f->points[(i+1)%3], f->points[i], dir); if (VectorLength(dir) < 0.1) return qfalse; } VL_TextureMatrixFromPoints( f, a, b, c ); VL_LightmapMatrixFromPoints( dsurf, si, f, a, b, c ); f->numpoints = 3; return qtrue; } /* ===================== VL_GenerateFacetFor4Points Attempts to use four points as a planar quad ===================== */ #define PLANAR_EPSILON 0.1 qboolean VL_GenerateFacetFor4Points( dsurface_t *dsurf, shaderInfo_t *si, lFacet_t *f, drawVert_t *a, drawVert_t *b, drawVert_t *c, drawVert_t *d ) { float dist; vec3_t dir; int i; plane_t plane; // if we can't generate a valid plane for the points, ignore the facet if ( !VL_PlaneFromPoints( &f->plane, a->xyz, b->xyz, c->xyz ) ) { f->numpoints = 0; return qfalse; } // if the fourth point is also on the plane, we can make a quad facet dist = DotProduct( d->xyz, f->plane.normal ) - f->plane.dist; if ( fabs( dist ) > PLANAR_EPSILON ) { f->numpoints = 0; return qfalse; } VectorAdd( a->xyz, surfaceOrigin[dsurf - drawSurfaces], f->points[0] ); VectorAdd( b->xyz, surfaceOrigin[dsurf - drawSurfaces], f->points[1] ); VectorAdd( c->xyz, surfaceOrigin[dsurf - drawSurfaces], f->points[2] ); VectorAdd( d->xyz, surfaceOrigin[dsurf - drawSurfaces], f->points[3] ); for (i = 1; i < 4; i++) { if ( !VL_PlaneFromPoints( &plane, f->points[i], f->points[(i+1) % 4], f->points[(i+2) % 4]) ) { f->numpoints = 0; return qfalse; } if (!Plane_Equal(&f->plane, &plane, qfalse)) { f->numpoints = 0; return qfalse; } } f->lightmapCoords[0][0] = a->lightmap[0]; f->lightmapCoords[0][1] = a->lightmap[1]; f->lightmapCoords[1][0] = b->lightmap[0]; f->lightmapCoords[1][1] = b->lightmap[1]; f->lightmapCoords[2][0] = c->lightmap[0]; f->lightmapCoords[2][1] = c->lightmap[1]; f->lightmapCoords[3][0] = d->lightmap[0]; f->lightmapCoords[3][1] = d->lightmap[1]; VL_GenerateBoundaryForPoints( &f->boundaries[0], &f->plane, f->points[0], f->points[1] ); VL_GenerateBoundaryForPoints( &f->boundaries[1], &f->plane, f->points[1], f->points[2] ); VL_GenerateBoundaryForPoints( &f->boundaries[2], &f->plane, f->points[2], f->points[3] ); VL_GenerateBoundaryForPoints( &f->boundaries[3], &f->plane, f->points[3], f->points[0] ); for (i = 0; i < 4; i++) { VectorSubtract(f->points[(i+1)%4], f->points[i], dir); if (VectorLength(dir) < 0.1) return qfalse; } VL_TextureMatrixFromPoints( f, a, b, c ); VL_LightmapMatrixFromPoints( dsurf, si, f, a, b, c ); f->num = numfacets++; f->numpoints = 4; return qtrue; } /* =============== VL_SphereFromBounds =============== */ void VL_SphereFromBounds( vec3_t mins, vec3_t maxs, vec3_t origin, float *radius ) { vec3_t temp; VectorAdd( mins, maxs, origin ); VectorScale( origin, 0.5, origin ); VectorSubtract( maxs, origin, temp ); *radius = VectorLength( temp ); } /* ==================== VL_FacetsForTriangleSurface ==================== */ void VL_FacetsForTriangleSurface( dsurface_t *dsurf, shaderInfo_t *si, lsurfaceTest_t *test ) { int i; drawVert_t *v1, *v2, *v3, *v4; int count; int i1, i2, i3, i4, i5, i6; test->patch = qfalse; if (dsurf->surfaceType == MST_TRIANGLE_SOUP) test->trisoup = qtrue; else test->trisoup = qfalse; test->numFacets = dsurf->numIndexes / 3; test->facets = malloc( sizeof( test->facets[0] ) * test->numFacets ); test->shader = si; count = 0; for ( i = 0 ; i < test->numFacets ; i++ ) { i1 = drawIndexes[ dsurf->firstIndex + i*3 ]; i2 = drawIndexes[ dsurf->firstIndex + i*3 + 1 ]; i3 = drawIndexes[ dsurf->firstIndex + i*3 + 2 ]; v1 = &drawVerts[ dsurf->firstVert + i1 ]; v2 = &drawVerts[ dsurf->firstVert + i2 ]; v3 = &drawVerts[ dsurf->firstVert + i3 ]; // try and make a quad out of two triangles if ( i != test->numFacets - 1 ) { i4 = drawIndexes[ dsurf->firstIndex + i*3 + 3 ]; i5 = drawIndexes[ dsurf->firstIndex + i*3 + 4 ]; i6 = drawIndexes[ dsurf->firstIndex + i*3 + 5 ]; if ( i4 == i3 && i5 == i2 ) { v4 = &drawVerts[ dsurf->firstVert + i6 ]; if ( VL_GenerateFacetFor4Points( dsurf, si, &test->facets[count], v1, v2, v4, v3 ) ) { count++; i++; // skip next tri continue; } } } if (VL_GenerateFacetFor3Points( dsurf, si, &test->facets[count], v1, v2, v3 )) { count++; } } // we may have turned some pairs into quads test->numFacets = count; } /* ==================== VL_FacetsForPatch ==================== */ void VL_FacetsForPatch( dsurface_t *dsurf, int surfaceNum, shaderInfo_t *si, lsurfaceTest_t *test ) { int i, j, x, y; drawVert_t *v1, *v2, *v3, *v4; int count, ssize; mesh_t mesh; mesh_t *subdivided, *detailmesh, *newmesh; int widthtable[LIGHTMAP_SIZE], heighttable[LIGHTMAP_SIZE]; mesh.width = dsurf->patchWidth; mesh.height = dsurf->patchHeight; mesh.verts = &drawVerts[ dsurf->firstVert ]; newmesh = SubdivideMesh( mesh, 8, 999 ); PutMeshOnCurve( *newmesh ); MakeMeshNormals( *newmesh ); subdivided = RemoveLinearMeshColumnsRows( newmesh ); FreeMesh(newmesh); // DebugNet_RemoveAllPolys(); // DebugNet_DrawMesh(subdivided); ssize = samplesize; if (si->lightmapSampleSize) ssize = si->lightmapSampleSize; if ( dsurf->lightmapNum >= 0 ) { detailmesh = SubdivideMeshQuads( subdivided, ssize, LIGHTMAP_SIZE, widthtable, heighttable); test->detailMesh = detailmesh; // DebugNet_RemoveAllPolys(); // DebugNet_DrawMesh(detailmesh); if ( detailmesh->width != dsurf->lightmapWidth || detailmesh->height != dsurf->lightmapHeight ) { Error( "Mesh lightmap miscount"); } } else { test->detailMesh = NULL; memset(widthtable, 0, sizeof(widthtable)); memset(heighttable, 0, sizeof(heighttable)); } test->patch = qtrue; test->trisoup = qfalse; test->numFacets = ( subdivided->width - 1 ) * ( subdivided->height - 1 ) * 2; test->facets = malloc( sizeof( test->facets[0] ) * test->numFacets ); test->shader = si; count = 0; x = 0; for ( i = 0 ; i < subdivided->width - 1 ; i++ ) { y = 0; for ( j = 0 ; j < subdivided->height - 1 ; j++ ) { v1 = subdivided->verts + j * subdivided->width + i; v2 = v1 + 1; v3 = v1 + subdivided->width + 1; v4 = v1 + subdivided->width; if ( VL_GenerateFacetFor4Points( dsurf, si, &test->facets[count], v1, v4, v3, v2 ) ) { test->facets[count].x = x; test->facets[count].y = y; test->facets[count].width = widthtable[i]; test->facets[count].height = heighttable[j]; count++; } else { if (VL_GenerateFacetFor3Points( dsurf, si, &test->facets[count], v1, v4, v3 )) { test->facets[count].x = x; test->facets[count].y = y; test->facets[count].width = widthtable[i]; test->facets[count].height = heighttable[j]; count++; } if (VL_GenerateFacetFor3Points( dsurf, si, &test->facets[count], v1, v3, v2 )) { test->facets[count].x = x; test->facets[count].y = y; test->facets[count].width = widthtable[i]; test->facets[count].height = heighttable[j]; count++; } } y += heighttable[j]; } x += widthtable[i]; } test->numFacets = count; FreeMesh(subdivided); } /* ===================== VL_InitSurfacesForTesting ===================== */ void VL_InitSurfacesForTesting( void ) { int i, j, k; dsurface_t *dsurf; lsurfaceTest_t *test; shaderInfo_t *si; lFacet_t *facet; for ( i = 0 ; i < numDrawSurfaces ; i++ ) { // don't light the entity surfaces with vlight if ( entitySurface[i] ) continue; // dsurf = &drawSurfaces[ i ]; if ( !dsurf->numIndexes && !dsurf->patchWidth ) { continue; } si = ShaderInfoForShader( dshaders[ dsurf->shaderNum].shader ); // if the surface is translucent and does not cast an alpha shadow if ( (si->contents & CONTENTS_TRANSLUCENT) && !(si->surfaceFlags & SURF_ALPHASHADOW) ) { // if the surface has no lightmap if ( dsurf->lightmapNum < 0 ) continue; } test = malloc( sizeof( *test ) ); memset(test, 0, sizeof( *test )); test->mutex = MutexAlloc(); test->numvolumes = 0; if (si->forceTraceLight) test->always_tracelight = qtrue; else if (si->forceVLight) test->always_vlight = qtrue; lsurfaceTest[i] = test; if ( dsurf->surfaceType == MST_TRIANGLE_SOUP || dsurf->surfaceType == MST_PLANAR ) { VL_FacetsForTriangleSurface( dsurf, si, test ); } else if ( dsurf->surfaceType == MST_PATCH ) { VL_FacetsForPatch( dsurf, i, si, test ); } if (numfacets >= MAX_FACETS) Error("numfacets >= MAX_FACETS (%d)", MAX_FACETS); ClearBounds( test->mins, test->maxs ); for (j = 0; j < test->numFacets; j++) { facet = &test->facets[j]; for ( k = 0 ; k < facet->numpoints; k++) { AddPointToBounds( facet->points[k], test->mins, test->maxs ); } } VL_SphereFromBounds( test->mins, test->maxs, test->origin, &test->radius ); } _printf("%6d facets\n", numfacets); _printf("linking surfaces...\n"); VL_LinkSurfaces(); } /* ============= VL_ChopWinding ============= */ int VL_ChopWinding (winding_t *in, plane_t *split, float epsilon) { vec_t dists[128]; int sides[128]; int counts[3]; vec_t dot; int i, j; vec_t *p1, *p2; vec3_t mid; winding_t out; winding_t *neww; counts[0] = counts[1] = counts[2] = 0; // determine sides for each point for (i=0 ; inumpoints ; i++) { dot = DotProduct (in->points[i], split->normal); dot -= split->dist; dists[i] = dot; if (dot > epsilon) sides[i] = SIDE_FRONT; else if (dot < -epsilon) sides[i] = SIDE_BACK; else { sides[i] = SIDE_ON; } counts[sides[i]]++; } if (!counts[SIDE_BACK]) { if (!counts[SIDE_FRONT]) return SIDE_ON; else return SIDE_FRONT; } if (!counts[SIDE_FRONT]) { return SIDE_BACK; } sides[i] = sides[0]; dists[i] = dists[0]; neww = &out; neww->numpoints = 0; for (i=0 ; inumpoints ; i++) { p1 = in->points[i]; if (neww->numpoints >= MAX_POINTS_ON_FIXED_WINDING) { _printf("WARNING: VL_ChopWinding -> MAX_POINTS_ON_FIXED_WINDING overflowed\n"); return SIDE_FRONT; // can't chop -- fall back to original } if (sides[i] == SIDE_ON) { VectorCopy (p1, neww->points[neww->numpoints]); neww->numpoints++; continue; } if (sides[i] == SIDE_FRONT) { VectorCopy (p1, neww->points[neww->numpoints]); neww->numpoints++; } if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue; if (neww->numpoints >= MAX_POINTS_ON_FIXED_WINDING) { _printf("WARNING: VL_ChopWinding -> MAX_POINTS_ON_FIXED_WINDING overflowed\n"); return SIDE_FRONT; // can't chop -- fall back to original } // generate a split point p2 = in->points[(i+1)%in->numpoints]; dot = dists[i] / (dists[i]-dists[i+1]); for (j=0 ; j<3 ; j++) { // avoid round off error when possible if (split->normal[j] == 1) mid[j] = split->dist; else if (split->normal[j] == -1) mid[j] = -split->dist; else mid[j] = p1[j] + dot*(p2[j]-p1[j]); } VectorCopy (mid, neww->points[neww->numpoints]); neww->numpoints++; } memcpy(in, &out, sizeof(winding_t)); return SIDE_CROSS; } /* ============= VL_ChopWindingWithBrush returns all winding fragments outside the brush ============= */ int VL_ChopWindingWithBrush(winding_t *w, dbrush_t *brush, winding_t *outwindings, int maxout) { int i, res, numout; winding_t front, back; plane_t plane; numout = 0; memcpy(front.points, w->points, w->numpoints * sizeof(vec3_t)); front.numpoints = w->numpoints; for (i = 0; i < brush->numSides; i++) { VectorCopy(dplanes[ dbrushsides[ brush->firstSide + i ].planeNum ].normal, plane.normal); VectorInverse(plane.normal); plane.dist = -dplanes[ dbrushsides[ brush->firstSide + i ].planeNum ].dist; res = VL_SplitWinding(&front, &back, &plane, 0.1); if (res == SIDE_BACK || res == SIDE_ON) { memcpy(outwindings[0].points, w->points, w->numpoints * sizeof(vec3_t)); outwindings[0].numpoints = w->numpoints; return 1; //did not intersect } if (res != SIDE_FRONT) { if (numout >= maxout) { _printf("WARNING: VL_ChopWindingWithBrush: more than %d windings\n", maxout); return 0; } memcpy(outwindings[numout].points, back.points, back.numpoints * sizeof(vec3_t)); outwindings[numout].numpoints = back.numpoints; numout++; } } return numout; } /* ============= VL_WindingAreaOutsideBrushes ============= */ float VL_WindingAreaOutsideBrushes(winding_t *w, int *brushnums, int numbrushes) { int i, j, numwindings[2], n; winding_t windingsbuf[2][64]; dbrush_t *brush; float area; memcpy(windingsbuf[0][0].points, w->points, w->numpoints * sizeof(vec3_t)); windingsbuf[0][0].numpoints = w->numpoints; numwindings[0] = 1; for (i = 0; i < numbrushes; i++) { brush = &dbrushes[brushnums[i]]; if (!(dshaders[brush->shaderNum].contentFlags & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER | CONTENTS_FOG | CONTENTS_AREAPORTAL | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP | CONTENTS_CLUSTERPORTAL | CONTENTS_DONOTENTER | CONTENTS_BODY | CONTENTS_CORPSE | CONTENTS_TRANSLUCENT | CONTENTS_TRIGGER | CONTENTS_NODROP) ) && (dshaders[brush->shaderNum].contentFlags & CONTENTS_SOLID) ) { numwindings[!(i & 1)] = 0; for (j = 0; j < numwindings[i&1]; j++) { n = VL_ChopWindingWithBrush(&windingsbuf[i&1][j], brush, &windingsbuf[!(i&1)][numwindings[!(i&1)]], 64 - numwindings[!(i&1)]); numwindings[!(i&1)] += n; } if (!numwindings[!(i&1)]) return 0; } else { for (j = 0; j < numwindings[i&1]; j++) { windingsbuf[!(i&1)][j] = windingsbuf[i&1][j]; } numwindings[!(i&1)] = numwindings[i&1]; } } area = 0; for (j = 0; j < numwindings[i&1]; j++) { area += WindingArea(&windingsbuf[i&1][j]); } return area; } /* ============= VL_R_WindingAreaOutsideSolid ============= */ float VL_R_WindingAreaOutsideSolid(winding_t *w, vec3_t normal, int nodenum) { int leafnum, res; float area; dnode_t *node; dleaf_t *leaf; dplane_t *plane; winding_t back; plane_t split; area = 0; while(nodenum >= 0) { node = &dnodes[nodenum]; plane = &dplanes[node->planeNum]; VectorCopy(plane->normal, split.normal); split.dist = plane->dist; res = VL_SplitWinding (w, &back, &split, 0.1); if (res == SIDE_FRONT) { nodenum = node->children[0]; } else if (res == SIDE_BACK) { nodenum = node->children[1]; } else if (res == SIDE_ON) { if (DotProduct(normal, plane->normal) > 0) nodenum = node->children[0]; else nodenum = node->children[1]; } else { area += VL_R_WindingAreaOutsideSolid(&back, normal, node->children[1]); nodenum = node->children[0]; } } leafnum = -nodenum - 1; leaf = &dleafs[leafnum]; if (leaf->cluster != -1) { area += VL_WindingAreaOutsideBrushes(w, &dleafbrushes[leaf->firstLeafBrush], leaf->numLeafBrushes); } return area; } /* ============= VL_WindingAreaOutsideSolid ============= */ float VL_WindingAreaOutsideSolid(winding_t *w, vec3_t normal) { return VL_R_WindingAreaOutsideSolid(w, normal, 0); } /* ============= VL_ChopWindingWithFacet ============= */ float VL_ChopWindingWithFacet(winding_t *w, lFacet_t *facet) { int i; for (i = 0; i < facet->numpoints; i++) { if (VL_ChopWinding(w, &facet->boundaries[i], 0) == SIDE_BACK) return 0; } if (nostitching) return WindingArea(w); else return VL_WindingAreaOutsideSolid(w, facet->plane.normal); } /* ============= VL_CalcVisibleLightmapPixelArea nice brute force ;) ============= */ void VL_CalcVisibleLightmapPixelArea(void) { int i, j, x, y, k; dsurface_t *ds; lsurfaceTest_t *test; mesh_t *mesh; winding_t w, tmpw; float area; _printf("calculating visible lightmap pixel area...\n"); for ( i = 0 ; i < numDrawSurfaces ; i++ ) { test = lsurfaceTest[ i ]; if (!test) continue; ds = &drawSurfaces[ i ]; if ( ds->lightmapNum < 0 ) continue; for (y = 0; y < ds->lightmapHeight; y++) { for (x = 0; x < ds->lightmapWidth; x++) { if (ds->surfaceType == MST_PATCH) { if (y == ds->lightmapHeight-1) continue; if (x == ds->lightmapWidth-1) continue; mesh = lsurfaceTest[i]->detailMesh; VectorCopy( mesh->verts[y*mesh->width+x].xyz, w.points[0]); VectorCopy( mesh->verts[(y+1)*mesh->width+x].xyz, w.points[1]); VectorCopy( mesh->verts[(y+1)*mesh->width+x+1].xyz, w.points[2]); VectorCopy( mesh->verts[y*mesh->width+x+1].xyz, w.points[3]); w.numpoints = 4; if (nostitching) area = WindingArea(&w); else area = VL_WindingAreaOutsideSolid(&w, mesh->verts[y*mesh->width+x].normal); } else { VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT, ds->lightmapVecs[0], w.points[0]); VectorMA(w.points[0], (float) y - LIGHTMAP_PIXELSHIFT, ds->lightmapVecs[1], w.points[0]); VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT, ds->lightmapVecs[0], w.points[3]); VectorMA(w.points[3], (float) y - LIGHTMAP_PIXELSHIFT + 1, ds->lightmapVecs[1], w.points[3]); VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT + 1, ds->lightmapVecs[0], w.points[2]); VectorMA(w.points[2], (float) y - LIGHTMAP_PIXELSHIFT + 1, ds->lightmapVecs[1], w.points[2]); VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT + 1, ds->lightmapVecs[0], w.points[1]); VectorMA(w.points[1], (float) y - LIGHTMAP_PIXELSHIFT, ds->lightmapVecs[1], w.points[1]); w.numpoints = 4; area = 0; for (j = 0; j < test->numFacets; j++) { memcpy(&tmpw, &w, sizeof(winding_t)); area += VL_ChopWindingWithFacet(&tmpw, &test->facets[j]); } } k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; lightmappixelarea[k] = area; } } } } /* ============= VL_FindAdjacentSurface ============= */ int VL_FindAdjacentSurface(int surfaceNum, int facetNum, vec3_t p1, vec3_t p2, int *sNum, int *fNum, int *point) { int i, j, k; lsurfaceTest_t *test; lFacet_t *facet; dsurface_t *ds; float *fp1, *fp2; vec3_t dir; plane_t *facetplane; // winding_t w; facetplane = &lsurfaceTest[surfaceNum]->facets[facetNum].plane; // DebugNet_RemoveAllPolys(); // memcpy(w.points, lsurfaceTest[surfaceNum]->facets[facetNum].points, // lsurfaceTest[surfaceNum]->facets[facetNum].numpoints * sizeof(vec3_t)); // w.numpoints = lsurfaceTest[surfaceNum]->facets[facetNum].numpoints; // DebugNet_DrawWinding(&w, 2); for ( i = 0 ; i < numDrawSurfaces ; i++ ) { if (i == surfaceNum) continue; test = lsurfaceTest[ i ]; if (!test) continue; if (test->trisoup)// || test->patch) continue; ds = &drawSurfaces[i]; if ( ds->lightmapNum < 0 ) continue; //if this surface is not even near the edge VectorSubtract(p1, test->origin, dir); if (fabs(dir[0]) > test->radius || fabs(dir[1]) > test->radius || fabs(dir[1]) > test->radius) { VectorSubtract(p2, test->origin, dir); if (fabs(dir[0]) > test->radius || fabs(dir[1]) > test->radius || fabs(dir[1]) > test->radius) { continue; } } // for (j = 0; j < test->numFacets; j++) { facet = &test->facets[j]; // //if (!Plane_Equal(&facet->plane, facetplane, qfalse)) if (DotProduct(facet->plane.normal, facetplane->normal) < 0.9) { if (!test->trisoup && !test->patch) break; continue; } // for (k = 0; k < facet->numpoints; k++) { fp1 = facet->points[k]; if (fabs(p2[0] - fp1[0]) < 0.1 && fabs(p2[1] - fp1[1]) < 0.1 && fabs(p2[2] - fp1[2]) < 0.1) { fp2 = facet->points[(k+1) % facet->numpoints]; if (fabs(p1[0] - fp2[0]) < 0.1 && fabs(p1[1] - fp2[1]) < 0.1 && fabs(p1[2] - fp2[2]) < 0.1) { // memcpy(w.points, facet->points, facet->numpoints * sizeof(vec3_t)); // w.numpoints = facet->numpoints; // DebugNet_DrawWinding(&w, 1); *sNum = i; *fNum = j; *point = k; return qtrue; } } /* else if (fabs(p1[0] - fp1[0]) < 0.1 && fabs(p1[1] - fp1[1]) < 0.1 && fabs(p1[2] - fp1[2]) < 0.1) { fp2 = facet->points[(k+1) % facet->numpoints]; if (fabs(p2[0] - fp2[0]) < 0.1 && fabs(p2[1] - fp2[1]) < 0.1 && fabs(p2[2] - fp2[2]) < 0.1) { // memcpy(w.points, facet->points, facet->numpoints * sizeof(vec3_t)); // w.numpoints = facet->numpoints; // DebugNet_DrawWinding(&w, 1); *sNum = i; *fNum = j; *point = k; return qtrue; } } //*/ } } } return qfalse; } /* ============= VL_SmoothenLightmapEdges this code is used to smoothen lightmaps across surface edges ============= */ void VL_SmoothenLightmapEdges(void) { int i, j, k, coords1[2][2]; float coords2[2][2]; int x1, y1, xinc1, yinc1, k1, k2; float x2, y2, xinc2, yinc2, length; int surfaceNum, facetNum, point; lsurfaceTest_t *test; lFacet_t *facet1, *facet2; dsurface_t *ds1, *ds2; float *p[2], s, t, *color1, *color2; vec3_t dir, cross; for ( i = 0 ; i < numDrawSurfaces ; i++ ) { test = lsurfaceTest[ i ]; if (!test) continue; if (test->trisoup)// || test->patch) continue; ds1 = &drawSurfaces[i]; if ( ds1->lightmapNum < 0 ) continue; for (j = 0; j < test->numFacets; j++) { facet1 = &test->facets[j]; // for (k = 0; k < facet1->numpoints; k++) { p[0] = facet1->points[k]; p[1] = facet1->points[(k+1)%facet1->numpoints]; // coords1[0][0] = facet1->lightmapCoords[k][0] * LIGHTMAP_SIZE; coords1[0][1] = facet1->lightmapCoords[k][1] * LIGHTMAP_SIZE; coords1[1][0] = facet1->lightmapCoords[(k+1)%facet1->numpoints][0] * LIGHTMAP_SIZE; coords1[1][1] = facet1->lightmapCoords[(k+1)%facet1->numpoints][1] * LIGHTMAP_SIZE; if (coords1[0][0] >= LIGHTMAP_SIZE) coords1[0][0] = LIGHTMAP_SIZE-1; if (coords1[0][1] >= LIGHTMAP_SIZE) coords1[0][1] = LIGHTMAP_SIZE-1; if (coords1[1][0] >= LIGHTMAP_SIZE) coords1[1][0] = LIGHTMAP_SIZE-1; if (coords1[1][1] >= LIGHTMAP_SIZE) coords1[1][1] = LIGHTMAP_SIZE-1; // try one row or column further because on flat faces the lightmap can // extend beyond the edge VectorSubtract(p[1], p[0], dir); VectorNormalize(dir, dir); CrossProduct(dir, facet1->plane.normal, cross); // if (coords1[0][0] - coords1[1][0] == 0) { s = DotProduct( cross, facet1->lightmapMatrix[0] ); coords1[0][0] += s < 0 ? 1 : -1; coords1[1][0] += s < 0 ? 1 : -1; if (coords1[0][0] < ds1->lightmapX || coords1[0][0] >= ds1->lightmapX + ds1->lightmapWidth) { coords1[0][0] += s < 0 ? -1 : 1; coords1[1][0] += s < 0 ? -1 : 1; } length = fabs(coords1[1][1] - coords1[0][1]); } else if (coords1[0][1] - coords1[1][1] == 0) { t = DotProduct( cross, facet1->lightmapMatrix[1] ); coords1[0][1] += t < 0 ? 1 : -1; coords1[1][1] += t < 0 ? 1 : -1; if (coords1[0][1] < ds1->lightmapY || coords1[0][1] >= ds1->lightmapY + ds1->lightmapHeight) { coords1[0][1] += t < 0 ? -1 : 1; coords1[1][1] += t < 0 ? -1 : 1; } length = fabs(coords1[1][0] - coords1[0][0]); } else { //the edge is not parallell to one of the lightmap axis continue; } // x1 = coords1[0][0]; y1 = coords1[0][1]; xinc1 = coords1[1][0] - coords1[0][0]; if (xinc1 < 0) xinc1 = -1; if (xinc1 > 0) xinc1 = 1; yinc1 = coords1[1][1] - coords1[0][1]; if (yinc1 < 0) yinc1 = -1; if (yinc1 > 0) yinc1 = 1; // the edge should be parallell to one of the lightmap axis if (xinc1 != 0 && yinc1 != 0) continue; // if (!VL_FindAdjacentSurface(i, j, p[0], p[1], &surfaceNum, &facetNum, &point)) continue; // ds2 = &drawSurfaces[surfaceNum]; facet2 = &lsurfaceTest[surfaceNum]->facets[facetNum]; coords2[0][0] = facet2->lightmapCoords[(point+1)%facet2->numpoints][0] * LIGHTMAP_SIZE; coords2[0][1] = facet2->lightmapCoords[(point+1)%facet2->numpoints][1] * LIGHTMAP_SIZE; coords2[1][0] = facet2->lightmapCoords[point][0] * LIGHTMAP_SIZE; coords2[1][1] = facet2->lightmapCoords[point][1] * LIGHTMAP_SIZE; if (coords2[0][0] >= LIGHTMAP_SIZE) coords2[0][0] = LIGHTMAP_SIZE-1; if (coords2[0][1] >= LIGHTMAP_SIZE) coords2[0][1] = LIGHTMAP_SIZE-1; if (coords2[1][0] >= LIGHTMAP_SIZE) coords2[1][0] = LIGHTMAP_SIZE-1; if (coords2[1][1] >= LIGHTMAP_SIZE) coords2[1][1] = LIGHTMAP_SIZE-1; // x2 = coords2[0][0]; y2 = coords2[0][1]; xinc2 = coords2[1][0] - coords2[0][0]; if (length) xinc2 = xinc2 / length; yinc2 = coords2[1][1] - coords2[0][1]; if (length) yinc2 = yinc2 / length; // the edge should be parallell to one of the lightmap axis if ((int) xinc2 != 0 && (int) yinc2 != 0) continue; // while(1) { k1 = ( ds1->lightmapNum * LIGHTMAP_HEIGHT + y1) * LIGHTMAP_WIDTH + x1; k2 = ( ds2->lightmapNum * LIGHTMAP_HEIGHT + ((int) y2)) * LIGHTMAP_WIDTH + ((int) x2); color1 = lightFloats + k1*3; color2 = lightFloats + k2*3; if (lightmappixelarea[k1] < 0.01) { color1[0] = color2[0]; color1[1] = color2[1]; color1[2] = color2[2]; } else { color1[0] = (float) color2[0] * 0.7 + (float) color1[0] * 0.3; color1[1] = (float) color2[1] * 0.7 + (float) color1[1] * 0.3; color1[2] = (float) color2[2] * 0.7 + (float) color1[2] * 0.3; } // if (x1 == coords1[1][0] && y1 == coords1[1][1]) break; x1 += xinc1; y1 += yinc1; x2 += xinc2; y2 += yinc2; if (x2 < ds2->lightmapX) x2 = ds2->lightmapX; if (x2 >= ds2->lightmapX + ds2->lightmapWidth) x2 = ds2->lightmapX + ds2->lightmapWidth-1; if (y2 < ds2->lightmapY) y2 = ds2->lightmapY; if (y2 >= ds2->lightmapY + ds2->lightmapHeight) y2 = ds2->lightmapY + ds2->lightmapHeight-1; } } } } } /* ============= VL_FixLightmapEdges ============= */ void VL_FixLightmapEdges(void) { int i, j, x, y, k, foundvalue, height, width, index; int pos, top, bottom; dsurface_t *ds; lsurfaceTest_t *test; float color[3]; float *ptr; byte filled[(LIGHTMAP_SIZE+1) * (LIGHTMAP_SIZE+1) / 8]; float lightmap_edge_epsilon; lightmap_edge_epsilon = 0.1 * samplesize; for ( i = 0 ; i < numDrawSurfaces ; i++ ) { test = lsurfaceTest[ i ]; if (!test) continue; ds = &drawSurfaces[ i ]; if ( ds->lightmapNum < 0 ) continue; if (ds->surfaceType == MST_PATCH) { height = ds->lightmapHeight - 1; width = ds->lightmapWidth - 1; } else { height = ds->lightmapHeight; width = ds->lightmapWidth; } memset(filled, 0, sizeof(filled)); // printf("\n"); for (x = 0; x < width; x++) { for (y = 0; y < height; y++) { k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; if (lightmappixelarea[k] > lightmap_edge_epsilon) { index = (ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; filled[index >> 3] |= 1 << (index & 7); // printf("*"); } // else // printf("_"); } // printf("\n"); } for (y = 0; y < height; y++) { pos = -2; for (x = 0; x < width; x++) { index = (ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; if (pos == -2) { if (filled[index >> 3] & (1 << (index & 7))) pos = -1; } else if (pos == -1) { if (!(filled[index >> 3] & (1 << (index & 7)))) pos = x - 1; } else { if (filled[index >> 3] & (1 << (index & 7))) { bottom = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + pos; top = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; for (j = 0; j < (x - pos + 1) / 2; j++) { k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + pos + j + 1; index = (ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + pos + j + 1; filled[index >> 3] |= 1 << (index & 7); (lightFloats + k*3)[0] = (lightFloats + top*3)[0]; (lightFloats + k*3)[1] = (lightFloats + top*3)[1]; (lightFloats + k*3)[2] = (lightFloats + top*3)[2]; k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x - j - 1; index = (ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x - j - 1; filled[index >> 3] |= 1 << (index & 7); (lightFloats + k*3)[0] = (lightFloats + bottom*3)[0]; (lightFloats + k*3)[1] = (lightFloats + bottom*3)[1]; (lightFloats + k*3)[2] = (lightFloats + bottom*3)[2]; } pos = -1; } } } } for (x = 0; x < width; x++) { pos = -2; for (y = 0; y < height; y++) { index = (ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; if (pos == -2) { if (filled[index >> 3] & (1 << (index & 7))) pos = -1; } else if (pos == -1) { if (!(filled[index >> 3] & (1 << (index & 7)))) pos = y - 1; } else { if (filled[index >> 3] & (1 << (index & 7))) { bottom = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + pos) * LIGHTMAP_WIDTH + ds->lightmapX + x; top = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; for (j = 0; j < (y - pos + 1) / 2; j++) { k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + pos + j + 1) * LIGHTMAP_WIDTH + ds->lightmapX + x; index = (ds->lightmapY + pos + j + 1) * LIGHTMAP_WIDTH + ds->lightmapX + x; filled[index >> 3] |= 1 << (index & 7); (lightFloats + k*3)[0] = (lightFloats + top*3)[0]; (lightFloats + k*3)[1] = (lightFloats + top*3)[1]; (lightFloats + k*3)[2] = (lightFloats + top*3)[2]; k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y - j - 1) * LIGHTMAP_WIDTH + ds->lightmapX + x; index = (ds->lightmapY + y - j - 1) * LIGHTMAP_WIDTH + ds->lightmapX + x; filled[index >> 3] |= 1 << (index & 7); (lightFloats + k*3)[0] = (lightFloats + bottom*3)[0]; (lightFloats + k*3)[1] = (lightFloats + bottom*3)[1]; (lightFloats + k*3)[2] = (lightFloats + bottom*3)[2]; } pos = -1; } } } } for (y = 0; y < height; y++) { foundvalue = qfalse; for (x = 0; x < width; x++) { k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; index = (ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; if (foundvalue) { if (filled[index >> 3] & (1 << (index & 7))) { ptr = lightFloats + k*3; color[0] = ptr[0]; color[1] = ptr[1]; color[2] = ptr[2]; } else { ptr = lightFloats + k*3; ptr[0] = color[0]; ptr[1] = color[1]; ptr[2] = color[2]; filled[index >> 3] |= 1 << (index & 7); } } else { if (filled[index >> 3] & (1 << (index & 7))) { ptr = lightFloats + k*3; color[0] = ptr[0]; color[1] = ptr[1]; color[2] = ptr[2]; foundvalue = qtrue; } } } foundvalue = qfalse; for (x = width-1; x >= 0; x--) { k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; index = (ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; if (foundvalue) { if (filled[index >> 3] & (1 << (index & 7))) { ptr = lightFloats + k*3; color[0] = ptr[0]; color[1] = ptr[1]; color[2] = ptr[2]; } else { ptr = lightFloats + k*3; ptr[0] = color[0]; ptr[1] = color[1]; ptr[2] = color[2]; filled[index >> 3] |= 1 << (index & 7); } } else { if (filled[index >> 3] & (1 << (index & 7))) { ptr = lightFloats + k*3; color[0] = ptr[0]; color[1] = ptr[1]; color[2] = ptr[2]; foundvalue = qtrue; } } } } for (x = 0; x < width; x++) { foundvalue = qfalse; for (y = 0; y < height; y++) { k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; index = (ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; if (foundvalue) { if (filled[index >> 3] & (1 << (index & 7))) { ptr = lightFloats + k*3; color[0] = ptr[0]; color[1] = ptr[1]; color[2] = ptr[2]; } else { ptr = lightFloats + k*3; ptr[0] = color[0]; ptr[1] = color[1]; ptr[2] = color[2]; filled[index >> 3] |= 1 << (index & 7); } } else { if (filled[index >> 3] & (1 << (index & 7))) { ptr = lightFloats + k*3; color[0] = ptr[0]; color[1] = ptr[1]; color[2] = ptr[2]; foundvalue = qtrue; } } } foundvalue = qfalse; for (y = height-1; y >= 0; y--) { k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; index = (ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; if (foundvalue) { if (filled[index >> 3] & (1 << (index & 7))) { ptr = lightFloats + k*3; color[0] = ptr[0]; color[1] = ptr[1]; color[2] = ptr[2]; } else { ptr = lightFloats + k*3; ptr[0] = color[0]; ptr[1] = color[1]; ptr[2] = color[2]; filled[index >> 3] |= 1 << (index & 7); } } else { if (filled[index >> 3] & (1 << (index & 7))) { ptr = lightFloats + k*3; color[0] = ptr[0]; color[1] = ptr[1]; color[2] = ptr[2]; foundvalue = qtrue; } } } } if (ds->surfaceType == MST_PATCH) { x = ds->lightmapWidth-1; for (y = 0; y < ds->lightmapHeight; y++) { k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; ptr = lightFloats + k*3; ptr[0] = (lightFloats + (k-1)*3)[0]; ptr[1] = (lightFloats + (k-1)*3)[1]; ptr[2] = (lightFloats + (k-1)*3)[2]; } y = ds->lightmapHeight-1; for (x = 0; x < ds->lightmapWidth; x++) { k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; ptr = lightFloats + k*3; ptr[0] = (lightFloats + (k-LIGHTMAP_WIDTH)*3)[0]; ptr[1] = (lightFloats + (k-LIGHTMAP_WIDTH)*3)[1]; ptr[2] = (lightFloats + (k-LIGHTMAP_WIDTH)*3)[2]; } } /* //colored debug edges if (ds->surfaceType == MST_PATCH) { x = ds->lightmapWidth-1; for (y = 0; y < ds->lightmapHeight; y++) { k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; ptr = lightFloats + k*3; ptr[0] = 255; ptr[1] = 0; ptr[2] = 0; } y = ds->lightmapHeight-1; for (x = 0; x < ds->lightmapWidth; x++) { k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; ptr = lightFloats + k*3; ptr[0] = 0; ptr[1] = 255; ptr[2] = 0; } } //*/ } // VL_SmoothenLightmapEdges(); } /* ============= VL_ShiftPatchLightmaps ============= */ void VL_ShiftPatchLightmaps(void) { int i, j, x, y, k; drawVert_t *verts; dsurface_t *ds; lsurfaceTest_t *test; float *ptr; for ( i = 0 ; i < numDrawSurfaces ; i++ ) { test = lsurfaceTest[ i ]; if (!test) continue; ds = &drawSurfaces[ i ]; if ( ds->lightmapNum < 0 ) continue; if (ds->surfaceType != MST_PATCH) continue; for (x = ds->lightmapWidth; x > 0; x--) { for (y = 0; y <= ds->lightmapHeight; y++) { k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; ptr = lightFloats + k*3; ptr[0] = (lightFloats + (k-1)*3)[0]; ptr[1] = (lightFloats + (k-1)*3)[1]; ptr[2] = (lightFloats + (k-1)*3)[2]; } } for (y = ds->lightmapHeight; y > 0; y--) { for (x = 0; x <= ds->lightmapWidth; x++) { k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; ptr = lightFloats + k*3; ptr[0] = (lightFloats + (k-LIGHTMAP_WIDTH)*3)[0]; ptr[1] = (lightFloats + (k-LIGHTMAP_WIDTH)*3)[1]; ptr[2] = (lightFloats + (k-LIGHTMAP_WIDTH)*3)[2]; } } verts = &drawVerts[ ds->firstVert ]; for ( j = 0 ; j < ds->patchHeight * ds->patchWidth; j++ ) { verts[j].lightmap[0] += 0.5 / LIGHTMAP_WIDTH; verts[j].lightmap[1] += 0.5 / LIGHTMAP_HEIGHT; } ds->lightmapHeight++; ds->lightmapWidth++; } } /* ============= VL_StoreLightmap ============= */ void VL_StoreLightmap(void) { int i, x, y, k; dsurface_t *ds; lsurfaceTest_t *test; float *src; byte *dst; _printf("storing lightmaps...\n"); //fix lightmap edges before storing them VL_FixLightmapEdges(); // #ifdef LIGHTMAP_PATCHSHIFT VL_ShiftPatchLightmaps(); #endif // for ( i = 0 ; i < numDrawSurfaces ; i++ ) { test = lsurfaceTest[ i ]; if (!test) continue; ds = &drawSurfaces[ i ]; if ( ds->lightmapNum < 0 ) continue; for (y = 0; y < ds->lightmapHeight; y++) { for (x = 0; x < ds->lightmapWidth; x++) { k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; VectorAdd((lightFloats + k*3), lightAmbientColor, (lightFloats + k*3)); src = &lightFloats[k*3]; dst = lightBytes + k*3; ColorToBytes(src, dst); } } } } /* ============= PointInLeafnum ============= */ int PointInLeafnum(vec3_t point) { int nodenum; vec_t dist; dnode_t *node; dplane_t *plane; nodenum = 0; while (nodenum >= 0) { node = &dnodes[nodenum]; plane = &dplanes[node->planeNum]; dist = DotProduct (point, plane->normal) - plane->dist; if (dist > 0) nodenum = node->children[0]; else nodenum = node->children[1]; } return -nodenum - 1; } /* ============= VL_PointInLeafnum_r ============= */ int VL_PointInLeafnum_r(vec3_t point, int nodenum) { int leafnum; vec_t dist; dnode_t *node; dplane_t *plane; while (nodenum >= 0) { node = &dnodes[nodenum]; plane = &dplanes[node->planeNum]; dist = DotProduct (point, plane->normal) - plane->dist; if (dist > 0.1) { nodenum = node->children[0]; } else if (dist < -0.1) { nodenum = node->children[1]; } else { leafnum = VL_PointInLeafnum_r(point, node->children[0]); if (dleafs[leafnum].cluster != -1) return leafnum; nodenum = node->children[1]; } } leafnum = -nodenum - 1; return leafnum; } /* ============= VL_PointInLeafnum ============= */ int VL_PointInLeafnum(vec3_t point) { return VL_PointInLeafnum_r(point, 0); } /* ============= VL_LightLeafnum ============= */ int VL_LightLeafnum(vec3_t point) { /* int leafnum; dleaf_t *leaf; float x, y, z; vec3_t test; leafnum = VL_PointInLeafnum(point); leaf = &dleafs[leafnum]; if (leaf->cluster != -1) return leafnum; for (z = 1; z >= -1; z -= 1) { for (x = 1; x >= -1; x -= 1) { for (y = 1; y >= -1; y -= 1) { VectorCopy(point, test); test[0] += x; test[1] += y; test[2] += z; leafnum = VL_PointInLeafnum(test); leaf = &dleafs[leafnum]; if (leaf->cluster != -1) { VectorCopy(test, point); return leafnum; } } } } return leafnum; */ return VL_PointInLeafnum(point); } //#define LIGHTPOLYS #ifdef LIGHTPOLYS winding_t *lightwindings[MAX_MAP_DRAW_SURFS]; int numlightwindings; /* ============= VL_DrawLightWindings ============= */ void VL_DrawLightWindings(void) { int i; for (i = 0; i < numlightwindings; i++) { #ifdef DEBUGNET DebugNet_DrawWinding(lightwindings[i], 1); #endif } } /* ============= VL_LightSurfaceWithVolume ============= */ void VL_LightSurfaceWithVolume(int surfaceNum, int facetNum, vlight_t *light, lightvolume_t *volume) { winding_t *w; lsurfaceTest_t *test; lFacet_t *facet; int i; test = lsurfaceTest[ surfaceNum ]; facet = &test->facets[ facetNum ]; // w = (winding_t *) malloc(sizeof(winding_t)); memcpy(w->points, facet->points, sizeof(vec3_t) * facet->numpoints); w->numpoints = facet->numpoints; for (i = 0; i < volume->numplanes; i++) { //if totally on the back if (VL_ChopWinding(w, &volume->planes[i], 0.01) == SIDE_BACK) return; } lightwindings[numlightwindings] = w; numlightwindings++; if (numlightwindings >= MAX_MAP_DRAW_SURFS) Error("MAX_LIGHTWINDINGS"); } #else /* ============= VL_LightSurfaceWithVolume ============= */ /* int VL_PointInsideLightVolume(vec3_t point, lightvolume_t *volume) { int i; float d; for (i = 0; i < volume->numplanes; i++) { d = DotProduct(volume->planes[i].normal, point) - volume->planes[i].dist; if (d < 0) return qfalse; } return qtrue; } void VL_LightSurfaceWithVolume( int surfaceNum, int facetNum, vlight_t *light, lightvolume_t *volume ) { dsurface_t *ds; int i, j, k; int numPositions; vec3_t base, normal, color; int sampleWidth, sampleHeight; vec3_t lightmapOrigin, lightmapVecs[2], dir; unsigned char *ptr; float add, dist, angle; mesh_t * mesh; ds = &drawSurfaces[surfaceNum]; // vertex-lit triangle model if ( ds->surfaceType == MST_TRIANGLE_SOUP ) { return; } if ( ds->lightmapNum < 0 ) { return; // doesn't need lighting } if ( ds->surfaceType == MST_PATCH ) { mesh = lsurfaceTest[surfaceNum]->detailMesh; } else { VectorCopy( ds->lightmapVecs[2], normal ); VectorCopy( ds->lightmapOrigin, lightmapOrigin ); VectorCopy( ds->lightmapVecs[0], lightmapVecs[0] ); VectorCopy( ds->lightmapVecs[1], lightmapVecs[1] ); } sampleWidth = ds->lightmapWidth; sampleHeight = ds->lightmapHeight; //calculate lightmap for ( i = 0 ; i < sampleWidth; i++ ) { for ( j = 0 ; j < sampleHeight; j++ ) { if ( ds->patchWidth ) { numPositions = 9; VectorCopy( mesh->verts[j*mesh->width+i].normal, normal ); // VectorNormalize( normal, normal ); // push off of the curve a bit VectorMA( mesh->verts[j*mesh->width+i].xyz, 1, normal, base ); // MakeNormalVectors( normal, lightmapVecs[0], lightmapVecs[1] ); } else { numPositions = 9; for ( k = 0 ; k < 3 ; k++ ) { base[k] = lightmapOrigin[k] + normal[k] + ((float) i) * lightmapVecs[0][k] + ((float) j) * lightmapVecs[1][k]; } } VectorAdd( base, surfaceOrigin[ surfaceNum ], base ); VectorSubtract(base, light->origin, dir); dist = VectorNormalize(dir, dir); if ( dist < 16 ) { dist = 16; } angle = 1;//DotProduct( normal, dir ); //1; if (angle > 1) angle = 1; if ( light->atten_disttype == LDAT_LINEAR ) { add = angle * light->photons * lightLinearScale - dist; if ( add < 0 ) { add = 0; } } else { add = light->photons / ( dist * dist ) * angle; } if (add <= 1.0) continue; if (VL_PointInsideLightVolume(base, volume)) { k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + j) * LIGHTMAP_WIDTH + ds->lightmapX + i; ptr = lightBytes + k*3; color[0] = (float) ptr[0] + add * light->color[0]; color[1] = (float) ptr[1] + add * light->color[1]; color[2] = (float) ptr[2] + add * light->color[2]; ColorToBytes(color, ptr); } } } } */ /* ============= VL_GetFilter FIXME: don't use a lightmap pixel origin but use the four corner points to map part of a translucent surface onto the lightmap pixel ============= */ void VL_GetFilter(vlight_t *light, lightvolume_t *volume, vec3_t lmp, vec3_t filter) { lFacet_t *facet; lsurfaceTest_t *test; float d, d1, d2, frac, s, t, ns; int i, j, is, it, b; int x, y, u, v, numsamples, radius, color[4], largest; byte *image; vec3_t point, origin, total; VectorSet(filter, 1, 1, 1); if (noalphashading) return; if (volume->numtransFacets <= 0) return; if (light->type == LIGHT_SURFACEDIRECTED) { // project the light map pixel origin onto the area light source plane d = DotProduct(lmp, light->normal) - DotProduct(light->normal, light->w.points[0]); VectorMA(lmp, -d, light->normal, origin); } else { VectorCopy(light->origin, origin); } for (i = 0; i < volume->numtransFacets; i++) { test = lsurfaceTest[ volume->transSurfaces[i] ]; facet = &test->facets[ volume->transFacets[i] ]; // if this surface does not cast an alpha shadow if ( !(test->shader->surfaceFlags & SURF_ALPHASHADOW) ) continue; // if there are no texture pixel available if ( !test->shader->pixels ) { continue; } // d1 = DotProduct( origin, facet->plane.normal) - facet->plane.dist; d2 = DotProduct( lmp, facet->plane.normal ) - facet->plane.dist; // this should never happen because the light volume went through the facet if ( ( d1 < 0 ) == ( d2 < 0 ) ) { continue; } // calculate the crossing point frac = d1 / ( d1 - d2 ); for ( j = 0 ; j < 3 ; j++ ) { point[j] = origin[j] + frac * ( lmp[j] - origin[j] ); } s = DotProduct( point, facet->textureMatrix[0] ) + facet->textureMatrix[0][3]; t = DotProduct( point, facet->textureMatrix[1] ) + facet->textureMatrix[1][3]; if (s < 0) s = 0; if (t < 0) t = 0; s = s - floor( s ); t = t - floor( t ); is = s * test->shader->width; it = t * test->shader->height; //if old style alpha shading if (nocolorshading) { image = test->shader->pixels + 4 * ( it * test->shader->width + is ); // alpha filter b = image[3]; // alpha test makes this a binary option b = b < 128 ? 0 : 255; filter[0] = filter[0] * (255-b) / 255; filter[1] = filter[1] * (255-b) / 255; filter[2] = filter[2] * (255-b) / 255; } else { VectorClear(total); numsamples = 0; radius = 2; for ( u = -radius; u <= radius; u++ ) { x = is + u; if ( x < 0 || x >= test->shader->width) continue; for ( v = -radius; v <= radius; v++ ) { y = it + v; if ( y < 0 || y >= test->shader->height) continue; image = test->shader->pixels + 4 * ( y * test->shader->width + x ); color[0] = image[0]; color[1] = image[1]; color[2] = image[2]; largest = 0; for (j = 0; j < 3; j++) if (image[j] > largest) largest = image[j]; if (largest <= 0 || image[3] == 0) { color[0] = 255; color[1] = 255; color[2] = 255; largest = 255; } total[0] += ((float) color[0]/largest) * (255-image[3]) / 255.0; total[1] += ((float) color[1]/largest) * (255-image[3]) / 255.0; total[2] += ((float) color[2]/largest) * (255-image[3]) / 255.0; numsamples++; } } ns = numsamples; // filter[0] *= total[0] / ns; filter[1] *= total[1] / ns; filter[2] *= total[2] / ns; } } } /* ============= VL_LightSurfaceWithVolume ============= */ void VL_LightSurfaceWithVolume( int surfaceNum, int facetNum, vlight_t *light, lightvolume_t *volume ) { int i; dsurface_t *ds; lFacet_t *facet; lsurfaceTest_t *test; winding_t w; vec3_t base, dir, delta, normal, filter, origin; int min_x[LIGHTMAP_SIZE+2], max_x[LIGHTMAP_SIZE+2]; int min_y, max_y, k, x, y, n; float *color, distscale; float d, add, angle, dist, area, insidearea, coords[MAX_POINTS_ON_WINDING+1][2]; mesh_t *mesh; byte polygonedges[(LIGHTMAP_SIZE+1) * (LIGHTMAP_SIZE+1) / 8]; ds = &drawSurfaces[surfaceNum]; // vertex-lit triangle model if ( ds->surfaceType == MST_TRIANGLE_SOUP ) { return; } if ( ds->lightmapNum < 0 ) { return; // doesn't need lighting } test = lsurfaceTest[ surfaceNum ]; facet = &test->facets[ facetNum ]; if (defaulttracelight && !test->always_vlight) return; if (test->always_tracelight) return; memcpy(w.points, facet->points, sizeof(vec3_t) * facet->numpoints); w.numpoints = facet->numpoints; for (i = 0; i < volume->numplanes; i++) { //if totally on the back if (VL_ChopWinding(&w, &volume->planes[i], 0.01) == SIDE_BACK) return; } // only one thread at a time may write to the lightmap of this surface MutexLock(test->mutex); test->numvolumes++; if (ds->surfaceType == MST_PATCH) { // FIXME: reduce size and don't mark all as edge min_y = ds->lightmapY + facet->y; max_y = ds->lightmapY + facet->y + facet->height - 1; for (y = min_y; y <= max_y; y++) { min_x[y] = ds->lightmapX + facet->x; max_x[y] = ds->lightmapX + facet->x + facet->width - 1; for (x = min_x[y]; x <= max_x[y]; x++) { n = y * LIGHTMAP_SIZE + x; polygonedges[n >> 3] |= 1 << (n & 7); } } } else { for (i = 0; i < w.numpoints; i++) { float s, t; if (i >= MAX_POINTS_ON_WINDING) _printf("coords overflow\n"); if (ds->surfaceType != MST_PATCH) { VectorSubtract(w.points[i], facet->mins, delta); s = DotProduct( delta, facet->lightmapMatrix[0] ) + ds->lightmapX + 0.5; t = DotProduct( delta, facet->lightmapMatrix[1] ) + ds->lightmapY + 0.5; if (s >= LIGHTMAP_SIZE) s = LIGHTMAP_SIZE - 0.5; if (s < 0) s = 0; if (t >= LIGHTMAP_SIZE) t = LIGHTMAP_SIZE - 0.5; if (t < 0) t = 0; coords[i][0] = s; coords[i][1] = t; } else { s = DotProduct( w.points[i], facet->lightmapMatrix[0] ) + facet->lightmapMatrix[0][3]; t = DotProduct( w.points[i], facet->lightmapMatrix[1] ) + facet->lightmapMatrix[1][3]; s = s - floor( s ); t = t - floor( t ); coords[i][0] = ds->lightmapX + s * LIGHTMAP_SIZE;// + 0.5; coords[i][1] = ds->lightmapY + t * LIGHTMAP_SIZE;// + 0.5; if (coords[i][0] >= LIGHTMAP_SIZE) coords[i][0] -= LIGHTMAP_SIZE; if (coords[i][1] >= LIGHTMAP_SIZE) coords[i][1] -= LIGHTMAP_SIZE; if (coords[i][0] < ds->lightmapX) coords[i][0] = ds->lightmapX; if (coords[i][1] < ds->lightmapY) coords[i][1] = ds->lightmapY; } x = coords[i][0]; y = coords[i][1]; if (x < ds->lightmapX || x >= LIGHTMAP_SIZE) _printf("VL_LightSurfaceWithVolume: x outside lightmap\n"); if (y < ds->lightmapY || y >= LIGHTMAP_SIZE) _printf("VL_LightSurfaceWithVolume: y outside lightmap\n"); } coords[i][0] = coords[0][0]; coords[i][1] = coords[0][1]; // min_y = LIGHTMAP_SIZE; max_y = 0; for (i = 0; i < LIGHTMAP_SIZE; i++) { min_x[i] = LIGHTMAP_SIZE; max_x[i] = 0; } memset(polygonedges, 0, sizeof(polygonedges)); // scan convert the polygon onto the lightmap // for each edge it marks *every* lightmap pixel the edge goes through // so no brasenham and no scan conversion used for texture mapping but // more something like ray casting // this is necesary because we need all lightmap pixels totally or partly // inside the light volume. these lightmap pixels are only lit for the part // that they are inside the light volume. for (i = 0; i < w.numpoints; i++) { float xf, yf, dx, dy, xstep, ystep, xfrac, yfrac; int xinc, yinc; xf = coords[i][0]; yf = coords[i][1]; dx = coords[i+1][0] - xf; dy = coords[i+1][1] - yf; // x = (int) xf; y = (int) yf; // if (y < min_y) min_y = y; if (y > max_y) max_y = y; // if (fabs(dx) > fabs(dy)) { if (dx > 0) { // y fraction at integer x below fractional x yfrac = yf + (floor(xf) - xf) * dy / dx; xinc = 1; } else if (dx < 0) { // y fraction at integer x above fractional x yfrac = yf + (floor(xf) + 1 - xf) * dy / dx; xinc = -1; } else { yfrac = yf; xinc = 0; } // step in y direction per 1 unit in x direction if (dx) ystep = dy / fabs(dx); else ystep = 0; while(1) { if (x < ds->lightmapX || x >= LIGHTMAP_SIZE) _printf("VL_LightSurfaceWithVolume: x outside lightmap\n"); if (y < ds->lightmapY || y >= LIGHTMAP_SIZE) _printf("VL_LightSurfaceWithVolume: y outside lightmap\n"); // n = y * LIGHTMAP_SIZE + x; polygonedges[n >> 3] |= 1 << (n & 7); if (x < min_x[y]) min_x[y] = x; if (x > max_x[y]) max_x[y] = x; if (x == (int) coords[i+1][0]) break; yfrac += ystep; if (dy > 0) { if (yfrac > (float) y + 1) { y += 1; // n = y * LIGHTMAP_SIZE + x; polygonedges[n >> 3] |= 1 << (n & 7); if (x < min_x[y]) min_x[y] = x; if (x > max_x[y]) max_x[y] = x; } } else { if (yfrac < (float) y) { y -= 1; // n = y * LIGHTMAP_SIZE + x; polygonedges[n >> 3] |= 1 << (n & 7); if (x < min_x[y]) min_x[y] = x; if (x > max_x[y]) max_x[y] = x; } } x += xinc; } } else { if (dy > 0) { //x fraction at integer y below fractional y xfrac = xf + (floor(yf) - yf) * dx / dy; yinc = 1; } else if (dy < 0) { //x fraction at integer y above fractional y xfrac = xf + (floor(yf) + 1 - yf) * dx / dy; yinc = -1; } else { xfrac = xf; yinc = 0; } // step in x direction per 1 unit in y direction if (dy) xstep = dx / fabs(dy); else xstep = 0; while(1) { if (x < ds->lightmapX || x >= LIGHTMAP_SIZE) _printf("VL_LightSurfaceWithVolume: x outside lightmap\n"); if (y < ds->lightmapY || y >= LIGHTMAP_SIZE) _printf("VL_LightSurfaceWithVolume: y outside lightmap\n"); // n = y * LIGHTMAP_SIZE + x; polygonedges[n >> 3] |= 1 << (n & 7); if (x < min_x[y]) min_x[y] = x; if (x > max_x[y]) max_x[y] = x; if (y == (int) coords[i+1][1]) break; xfrac += xstep; if (dx > 0) { if (xfrac > (float) x + 1) { x += 1; // n = y * LIGHTMAP_SIZE + x; polygonedges[n >> 3] |= 1 << (n & 7); if (x < min_x[y]) min_x[y] = x; if (x > max_x[y]) max_x[y] = x; } } else { if (xfrac < (float) x) { x -= 1; // n = y * LIGHTMAP_SIZE + x; polygonedges[n >> 3] |= 1 << (n & 7); if (x < min_x[y]) min_x[y] = x; if (x > max_x[y]) max_x[y] = x; } } y += yinc; } } } } // map light onto the lightmap for (y = min_y; y <= max_y; y++) { for (x = min_x[y]; x <= max_x[y]; x++) { if (ds->surfaceType == MST_PATCH) { mesh = test->detailMesh; VectorCopy( mesh->verts[(y-ds->lightmapY)*mesh->width+x-ds->lightmapX].xyz, base); VectorCopy( mesh->verts[(y-ds->lightmapY)*mesh->width+x-ds->lightmapX].normal, normal); //VectorCopy(facet->plane.normal, normal); } else { VectorMA(ds->lightmapOrigin, (float) x - ds->lightmapX, ds->lightmapVecs[0], base); VectorMA(base, (float) y - ds->lightmapY, ds->lightmapVecs[1], base); VectorCopy(facet->plane.normal, normal); } if (light->type == LIGHT_POINTSPOT) { float distByNormal; vec3_t pointAtDist; float radiusAtDist; float sampleRadius; vec3_t distToSample; float coneScale; VectorSubtract( light->origin, base, dir ); distByNormal = -DotProduct( dir, light->normal ); if ( distByNormal < 0 ) { continue; } VectorMA( light->origin, distByNormal, light->normal, pointAtDist ); radiusAtDist = light->radiusByDist * distByNormal; VectorSubtract( base, pointAtDist, distToSample ); sampleRadius = VectorLength( distToSample ); if ( sampleRadius >= radiusAtDist ) { continue; // outside the cone } if ( sampleRadius <= radiusAtDist - 32 ) { coneScale = 1.0; // fully inside } else { coneScale = ( radiusAtDist - sampleRadius ) / 32.0; } dist = VectorNormalize( dir, dir ); // clamp the distance to prevent super hot spots if ( dist < 16 ) { dist = 16; } angle = DotProduct( normal, dir ); if (angle > 1) angle = 1; if (angle > 0) { if ( light->atten_angletype == LAAT_QUADRATIC ) { angle = 1 - angle; angle *= angle; angle = 1 - angle; } else if ( light->atten_angletype == LAAT_DOUBLEQUADRATIC ) { angle = 1 - angle; angle *= angle * angle; angle = 1 - angle; } } if (light->atten_anglescale > 0) { angle /= light->atten_anglescale; if (angle > 1) angle = 1; } if (light->atten_distscale > 0) { distscale = light->atten_distscale; } else { distscale = 1; } // if ( light->atten_disttype == LDAT_NOSCALE ) { add = angle * coneScale; } else if ( light->atten_disttype == LDAT_LINEAR ) { add = angle * light->photons * lightLinearScale * coneScale - dist * distscale; if ( add < 0 ) { add = 0; } } else { add = light->photons / ( dist * dist * distscale) * angle * coneScale; } if (add <= 1.0) continue; } else if (light->type == LIGHT_POINTFAKESURFACE) { // calculate the contribution add = PointToPolygonFormFactor( base, normal, &light->w ); if ( add <= 0 ) { if ( light->twosided ) { add = -add; } else { continue; } } } else if (light->type == LIGHT_SURFACEDIRECTED) { //VectorCopy(light->normal, dir); //VectorInverse(dir); // project the light map pixel origin onto the area light source plane d = DotProduct(base, light->normal) - DotProduct(light->normal, light->w.points[0]); VectorMA(base, -d, light->normal, origin); VectorSubtract(origin, base, dir); dist = VectorNormalize(dir, dir); if ( dist < 16 ) { dist = 16; } // angle = DotProduct( normal, dir ); if (angle > 1) angle = 1; if (angle > 0) { if ( light->atten_angletype == LAAT_QUADRATIC ) { angle = 1 - angle; angle *= angle; angle = 1 - angle; } else if ( light->atten_angletype == LAAT_DOUBLEQUADRATIC ) { angle = 1 - angle; angle *= angle * angle; angle = 1 - angle; } } if (light->atten_anglescale > 0) { angle /= light->atten_anglescale; if (angle > 1) angle = 1; } if (light->atten_distscale > 0) { distscale = light->atten_distscale; } else { distscale = 1; } if ( light->atten_disttype == LDAT_NOSCALE ) { add = angle; } else if ( light->atten_disttype == LDAT_LINEAR ) { add = angle * light->photons * lightLinearScale - dist * distscale; if ( add < 0 ) { add = 0; } } else { //default quadratic add = light->photons / ( dist * dist * distscale) * angle; } if (add <= 0) continue; } else //normal radial point light { VectorSubtract(light->origin, base, dir); dist = VectorNormalize(dir, dir); if ( dist < 16 ) { dist = 16; } angle = DotProduct( normal, dir ); if (angle > 1) angle = 1; if (angle > 0) { if ( light->atten_angletype == LAAT_QUADRATIC ) { angle = 1 - angle; angle *= angle; angle = 1 - angle; } else if ( light->atten_angletype == LAAT_DOUBLEQUADRATIC ) { angle = 1 - angle; angle *= angle * angle; angle = 1 - angle; } } if (light->atten_anglescale > 0) { angle /= light->atten_anglescale; if (angle > 1) angle = 1; } if (light->atten_distscale > 0) { distscale = light->atten_distscale; } else { distscale = 1; } if ( light->atten_disttype == LDAT_NOSCALE ) { add = angle; } else if ( light->atten_disttype == LDAT_LINEAR ) { add = angle * light->photons * lightLinearScale - dist * distscale; if ( add < 0 ) { add = 0; } } else { add = light->photons / ( dist * dist * distscale) * angle; } if (add <= 1.0) continue; } // k = (ds->lightmapNum * LIGHTMAP_HEIGHT + y) * LIGHTMAP_WIDTH + x; //if on one of the edges n = y * LIGHTMAP_SIZE + x; if ((polygonedges[n >> 3] & (1 << (n & 7)) )) { // multiply 'add' by the relative area being lit of the total visible lightmap pixel area // // first create a winding for the lightmap pixel if (ds->surfaceType == MST_PATCH) { mesh = test->detailMesh; if (y-ds->lightmapY >= mesh->height-1) _printf("y outside mesh\n"); if (x-ds->lightmapX >= mesh->width-1) _printf("x outside mesh\n"); VectorCopy( mesh->verts[(y-ds->lightmapY)*mesh->width+x-ds->lightmapX].xyz, w.points[0]); VectorCopy( mesh->verts[(y+1-ds->lightmapY)*mesh->width+x-ds->lightmapX].xyz, w.points[1]); VectorCopy( mesh->verts[(y+1-ds->lightmapY)*mesh->width+x+1-ds->lightmapX].xyz, w.points[2]); VectorCopy( mesh->verts[(y-ds->lightmapY)*mesh->width+x+1-ds->lightmapX].xyz, w.points[3]); w.numpoints = 4; } else { VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT - ds->lightmapX, ds->lightmapVecs[0], w.points[0]); VectorMA(w.points[0], (float) y - LIGHTMAP_PIXELSHIFT - ds->lightmapY, ds->lightmapVecs[1], w.points[0]); VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT - ds->lightmapX, ds->lightmapVecs[0], w.points[1]); VectorMA(w.points[1], (float) y - LIGHTMAP_PIXELSHIFT + 1 - ds->lightmapY, ds->lightmapVecs[1], w.points[1]); VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT + 1 - ds->lightmapX, ds->lightmapVecs[0], w.points[2]); VectorMA(w.points[2], (float) y - LIGHTMAP_PIXELSHIFT + 1 - ds->lightmapY, ds->lightmapVecs[1], w.points[2]); VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT + 1 - ds->lightmapX, ds->lightmapVecs[0], w.points[3]); VectorMA(w.points[3], (float) y - LIGHTMAP_PIXELSHIFT - ds->lightmapY, ds->lightmapVecs[1], w.points[3]); w.numpoints = 4; } // // take the visible area of the lightmap pixel into account // //area = WindingArea(&w); area = lightmappixelarea[k]; if (area <= 0) continue; // chop the lightmap pixel winding with the light volume for (i = 0; i < volume->numplanes; i++) { //if totally on the back if (VL_ChopWinding(&w, &volume->planes[i], 0) == SIDE_BACK) break; } // if the lightmap pixel is partly inside the light volume if (i >= volume->numplanes) { insidearea = WindingArea(&w); if (insidearea <= 0) i = 0; add = add * insidearea / area; } else { //DebugNet_DrawWinding(&w, 2); continue; // this shouldn't happen } } // get the light filter from all the translucent surfaces the light volume went through VL_GetFilter(light, volume, base, filter); // color = &lightFloats[k*3]; color[0] += add * light->color[0] * filter[0]; color[1] += add * light->color[1] * filter[1]; color[2] += add * light->color[2] * filter[2]; } } MutexUnlock(test->mutex); } #endif /* ============= VL_SplitLightVolume ============= */ int VL_SplitLightVolume(lightvolume_t *volume, lightvolume_t *back, plane_t *split, float epsilon) { lightvolume_t f, b; vec_t dists[128]; int sides[128]; int counts[3]; vec_t dot; int i, j; vec_t *p1, *p2; vec3_t mid; counts[0] = counts[1] = counts[2] = 0; // determine sides for each point for (i = 0; i < volume->numplanes; i++) { dot = DotProduct (volume->points[i], split->normal); dot -= split->dist; dists[i] = dot; if (dot > epsilon) sides[i] = SIDE_FRONT; else if (dot < -epsilon) sides[i] = SIDE_BACK; else { sides[i] = SIDE_ON; } counts[sides[i]]++; } if (!counts[1]) return 0; // completely on front side if (!counts[0]) return 1; // completely on back side sides[i] = sides[0]; dists[i] = dists[0]; f.numplanes = 0; b.numplanes = 0; for (i = 0; i < volume->numplanes; i++) { p1 = volume->points[i]; if (f.numplanes >= MAX_POINTS_ON_FIXED_WINDING) { _printf("WARNING: VL_SplitLightVolume -> MAX_POINTS_ON_FIXED_WINDING overflowed\n"); return 0; // can't chop -- fall back to original } if (b.numplanes >= MAX_POINTS_ON_FIXED_WINDING) { _printf("WARNING: VL_SplitLightVolume -> MAX_POINTS_ON_FIXED_WINDING overflowed\n"); return 0; // can't chop -- fall back to original } if (sides[i] == SIDE_ON) { VectorCopy(p1, f.points[f.numplanes]); VectorCopy(p1, b.points[b.numplanes]); if (sides[i+1] == SIDE_BACK) { f.planes[f.numplanes] = *split; b.planes[b.numplanes] = volume->planes[i]; } else if (sides[i+1] == SIDE_FRONT) { f.planes[f.numplanes] = volume->planes[i]; b.planes[b.numplanes] = *split; VectorInverse(b.planes[b.numplanes].normal); b.planes[b.numplanes].dist = -b.planes[b.numplanes].dist; } else //this shouldn't happen { f.planes[f.numplanes] = *split; b.planes[b.numplanes] = *split; VectorInverse(b.planes[b.numplanes].normal); b.planes[b.numplanes].dist = -b.planes[b.numplanes].dist; } f.numplanes++; b.numplanes++; continue; } if (sides[i] == SIDE_FRONT) { VectorCopy (p1, f.points[f.numplanes]); f.planes[f.numplanes] = volume->planes[i]; f.numplanes++; } if (sides[i] == SIDE_BACK) { VectorCopy (p1, b.points[b.numplanes]); b.planes[b.numplanes] = volume->planes[i]; b.numplanes++; } if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue; if (f.numplanes >= MAX_POINTS_ON_FIXED_WINDING) { _printf("WARNING: VL_SplitLightVolume -> MAX_POINTS_ON_FIXED_WINDING overflowed\n"); return 0; // can't chop -- fall back to original } if (b.numplanes >= MAX_POINTS_ON_FIXED_WINDING) { _printf("WARNING: VL_SplitLightVolume -> MAX_POINTS_ON_FIXED_WINDING overflowed\n"); return 0; // can't chop -- fall back to original } // generate a split point p2 = volume->points[(i+1)%volume->numplanes]; dot = dists[i] / (dists[i]-dists[i+1]); for (j=0 ; j<3 ; j++) { // avoid round off error when possible if (split->normal[j] == 1) mid[j] = split->dist; else if (split->normal[j] == -1) mid[j] = -split->dist; else mid[j] = p1[j] + dot*(p2[j]-p1[j]); } VectorCopy (mid, f.points[f.numplanes]); VectorCopy(mid, b.points[b.numplanes]); if (sides[i+1] == SIDE_BACK) { f.planes[f.numplanes] = *split; b.planes[b.numplanes] = volume->planes[i]; } else { f.planes[f.numplanes] = volume->planes[i]; b.planes[b.numplanes] = *split; VectorInverse(b.planes[b.numplanes].normal); b.planes[b.numplanes].dist = -b.planes[b.numplanes].dist; } f.numplanes++; b.numplanes++; } memcpy(volume->points, f.points, sizeof(vec3_t) * f.numplanes); memcpy(volume->planes, f.planes, sizeof(plane_t) * f.numplanes); volume->numplanes = f.numplanes; memcpy(back->points, b.points, sizeof(vec3_t) * b.numplanes); memcpy(back->planes, b.planes, sizeof(plane_t) * b.numplanes); back->numplanes = b.numplanes; return 2; } /* ============= VL_PlaneForEdgeToWinding ============= */ void VL_PlaneForEdgeToWinding(vec3_t p1, vec3_t p2, winding_t *w, int windingonfront, plane_t *plane) { int i, j; float length, d; vec3_t v1, v2; VectorSubtract(p2, p1, v1); for (i = 0; i < w->numpoints; i++) { VectorSubtract (w->points[i], p1, v2); plane->normal[0] = v1[1]*v2[2] - v1[2]*v2[1]; plane->normal[1] = v1[2]*v2[0] - v1[0]*v2[2]; plane->normal[2] = v1[0]*v2[1] - v1[1]*v2[0]; // if points don't make a valid plane, skip it length = plane->normal[0] * plane->normal[0] + plane->normal[1] * plane->normal[1] + plane->normal[2] * plane->normal[2]; if (length < ON_EPSILON) continue; length = 1/sqrt(length); plane->normal[0] *= length; plane->normal[1] *= length; plane->normal[2] *= length; plane->dist = DotProduct (w->points[i], plane->normal); // for (j = 0; j < w->numpoints; j++) { if (j == i) continue; d = DotProduct(w->points[j], plane->normal) - plane->dist; if (windingonfront) { if (d < -ON_EPSILON) break; } else { if (d > ON_EPSILON) break; } } if (j >= w->numpoints) return; } } /* ============= VL_R_CastLightAtSurface ============= */ void VL_R_FloodLight(vlight_t *light, lightvolume_t *volume, int cluster, int firstportal); void VL_R_CastLightAtSurface(vlight_t *light, lightvolume_t *volume) { lsurfaceTest_t *test; int i, n; // light the surface with this volume VL_LightSurfaceWithVolume(volume->surfaceNum, volume->facetNum, light, volume); // test = lsurfaceTest[ volume->surfaceNum ]; // if this is not a translucent surface if ( !(test->shader->surfaceFlags & SURF_ALPHASHADOW) && !(test->shader->contents & CONTENTS_TRANSLUCENT)) return; // if (volume->numtransFacets >= MAX_TRANSLUCENTFACETS) Error("a light valume went through more than %d translucent facets", MAX_TRANSLUCENTFACETS); //add this translucent surface to the list volume->transSurfaces[volume->numtransFacets] = volume->surfaceNum; volume->transFacets[volume->numtransFacets] = volume->facetNum; volume->numtransFacets++; //clear the tested facets except the translucent ones memset(volume->facetTested, 0, sizeof(volume->facetTested)); for (i = 0; i < volume->numtransFacets; i++) { test = lsurfaceTest[ volume->transSurfaces[i] ]; n = test->facets[volume->transFacets[i]].num; volume->facetTested[n >> 3] |= 1 << (n & 7); } memset(volume->clusterTested, 0, sizeof(volume->clusterTested)); volume->endplane = volume->farplane; volume->surfaceNum = -1; volume->facetNum = 0; VL_R_FloodLight(light, volume, volume->cluster, 0); if (volume->surfaceNum >= 0) { VL_R_CastLightAtSurface(light, volume); } } /* ============= VL_R_SplitLightVolume ============= */ int numvolumes = 0; int VL_R_SplitLightVolume(vlight_t *light, lightvolume_t *volume, plane_t *split, int cluster, int firstportal) { lightvolume_t back; int res; // res = VL_SplitLightVolume(volume, &back, split, 0.1); // if the volume was split if (res == 2) { memcpy(back.clusterTested, volume->clusterTested, sizeof(back.clusterTested)); memcpy(back.facetTested, volume->facetTested, sizeof(back.facetTested)); back.num = numvolumes++; back.endplane = volume->endplane; back.surfaceNum = volume->surfaceNum; back.facetNum = volume->facetNum; back.type = volume->type; back.cluster = volume->cluster; back.farplane = volume->farplane; if (volume->numtransFacets > 0) { memcpy(back.transFacets, volume->transFacets, sizeof(back.transFacets)); memcpy(back.transSurfaces, volume->transSurfaces, sizeof(back.transSurfaces)); } back.numtransFacets = volume->numtransFacets; // // flood the volume at the back of the split plane VL_R_FloodLight(light, &back, cluster, firstportal); // if the back volume hit a surface if (back.surfaceNum >= 0) { VL_R_CastLightAtSurface(light, &back); } } return res; } /* ============= VL_R_FloodLight ============= */ void VL_R_FloodLight(vlight_t *light, lightvolume_t *volume, int cluster, int firstportal) { int i, j, k, res, surfaceNum, backfaceculled, testculled; float d; winding_t winding, tmpwinding; lleaf_t *leaf; lportal_t *p; lsurfaceTest_t *test; lFacet_t *facet; vec3_t dir1, dir2; plane_t plane; // DebugNet_RemoveAllPolys(); // VL_DrawLightVolume(light, volume); // if the first portal is not zero then we've checked all occluders in this leaf already if (firstportal == 0) { // check all potential occluders in this leaf for (i = 0; i < leafs[cluster].numSurfaces; i++) { surfaceNum = clustersurfaces[leafs[cluster].firstSurface + i]; // test = lsurfaceTest[ surfaceNum ]; if ( !test ) continue; // testculled = qfalse; // use surface as an occluder for (j = 0; j < test->numFacets; j++) { // use each facet as an occluder facet = &test->facets[j]; // // memcpy(winding.points, facet->points, sizeof(vec3_t) * facet->numpoints); // winding.numpoints = facet->numpoints; // DebugNet_DrawWinding(&winding, 5); // // if the facet was tested already if ( volume->facetTested[facet->num >> 3] & (1 << (facet->num & 7)) ) continue; volume->facetTested[facet->num >> 3] |= 1 << (facet->num & 7); // backface culling for planar surfaces backfaceculled = qfalse; if (!test->patch && !test->trisoup) { if (volume->type == VOLUME_NORMAL) { // facet backface culling d = DotProduct(light->origin, facet->plane.normal) - facet->plane.dist; if (d < 0) { // NOTE: this doesn't work too great because of sometimes very bad tesselation // of surfaces that are supposed to be flat // FIXME: to work around this problem we should make sure that all facets // created from planar surfaces use the lightmapVecs normal vector /* if ( !test->shader->twoSided ) { // skip all other facets of this surface as well because they are in the same plane for (k = 0; k < test->numFacets; k++) { facet = &test->facets[k]; volume->facetTested[facet->num >> 3] |= 1 << (facet->num & 7); } }*/ backfaceculled = qtrue; } } else { // FIXME: if all light source winding points are at the back of the facet // plane then backfaceculled = qtrue } } else // backface culling per facet for patches and triangle soups { if (volume->type == VOLUME_NORMAL) { // facet backface culling d = DotProduct(light->origin, facet->plane.normal) - facet->plane.dist; if (d < 0) backfaceculled = qtrue; } else { // FIXME: if all light source winding points are at the back of the facet // plane then backfaceculled = qtrue } } /* chopping does this already // check if this facet is totally or partly in front of the volume end plane for (k = 0; k < facet->numpoints; k++) { d = DotProduct(volume->endplane.normal, facet->points[k]) - volume->endplane.dist; if (d > ON_EPSILON) break; } // if this facet is outside the light volume if (k >= facet->numpoints) continue; */ // if (backfaceculled) { // if the facet is not two sided if ( !nobackfaceculling && !test->shader->twoSided ) continue; // flip the winding for (k = 0; k < facet->numpoints; k++) VectorCopy(facet->points[k], winding.points[facet->numpoints - k - 1]); winding.numpoints = facet->numpoints; } else { memcpy(winding.points, facet->points, sizeof(vec3_t) * facet->numpoints); winding.numpoints = facet->numpoints; } // if (!testculled) { testculled = qtrue; // fast check if the surface sphere is totally behind the volume end plane d = DotProduct(volume->endplane.normal, test->origin) - volume->endplane.dist; if (d < -test->radius) { for (k = 0; k < test->numFacets; k++) { facet = &test->facets[k]; volume->facetTested[facet->num >> 3] |= 1 << (facet->num & 7); } break; } for (k = 0; k < volume->numplanes; k++) { d = DotProduct(volume->planes[k].normal, test->origin) - volume->planes[k].dist; if (d < - test->radius) { for (k = 0; k < test->numFacets; k++) { facet = &test->facets[k]; volume->facetTested[facet->num >> 3] |= 1 << (facet->num & 7); } break; } } if (k < volume->numplanes) break; } //NOTE: we have to chop the facet winding with the volume end plane because // the faces in Q3 are not stitched together nicely res = VL_ChopWinding(&winding, &volume->endplane, 0.01); // if the facet is on or at the back of the volume end plane if (res == SIDE_BACK || res == SIDE_ON) continue; // check if the facet winding is totally or partly inside the light volume memcpy(&tmpwinding, &winding, sizeof(winding_t)); for (k = 0; k < volume->numplanes; k++) { res = VL_ChopWinding(&tmpwinding, &volume->planes[k], 0.01); if (res == SIDE_BACK || res == SIDE_ON) break; } // if no part of the light volume is occluded by this facet if (k < volume->numplanes) continue; // for (k = 0; k < winding.numpoints; k++) { if (volume->type == VOLUME_DIRECTED) { VectorSubtract(winding.points[(k+1) % winding.numpoints], winding.points[k], dir1); CrossProduct(light->normal, dir1, plane.normal); VectorNormalize(plane.normal, plane.normal); plane.dist = DotProduct(plane.normal, winding.points[k]); } else { VectorSubtract(winding.points[(k+1) % winding.numpoints], winding.points[k], dir1); VectorSubtract(light->origin, winding.points[k], dir2); CrossProduct(dir1, dir2, plane.normal); VectorNormalize(plane.normal, plane.normal); plane.dist = DotProduct(plane.normal, winding.points[k]); } res = VL_R_SplitLightVolume(light, volume, &plane, cluster, 0); if (res == 1) break; //the facet wasn't really inside the volume } if (k >= winding.numpoints) { volume->endplane = facet->plane; if (backfaceculled) { VectorInverse(volume->endplane.normal); volume->endplane.dist = -volume->endplane.dist; } volume->surfaceNum = surfaceNum; volume->facetNum = j; } } } } // we've tested all occluders in this cluster volume->clusterTested[cluster >> 3] |= 1 << (cluster & 7); // flood light through the portals of the current leaf leaf = &leafs[cluster]; for (i = firstportal; i < leaf->numportals; i++) { p = leaf->portals[i]; // // memcpy(&winding, p->winding, sizeof(winding_t)); // DebugNet_DrawWinding(&winding, 5); // if already flooded into the cluster this portal leads to if ( volume->clusterTested[p->leaf >> 3] & (1 << (p->leaf & 7)) ) continue; // if (volume->type == VOLUME_NORMAL) { // portal backface culling d = DotProduct(light->origin, p->plane.normal) - p->plane.dist; if (d > 0) // portal plane normal points into neighbour cluster continue; } else { // FIXME: if all light source winding points are at the back of this portal // plane then there's no need to flood through } // check if this portal is totally or partly in front of the volume end plane // fast check with portal sphere d = DotProduct(volume->endplane.normal, p->origin) - volume->endplane.dist; if (d < -p->radius) continue; for (j = 0; j < p->winding->numpoints; j++) { d = DotProduct(volume->endplane.normal, p->winding->points[j]) - volume->endplane.dist; if (d > -0.01) break; } // if this portal is totally behind the light volume end plane if (j >= p->winding->numpoints) continue; //distance from point light to portal d = DotProduct(p->plane.normal, light->origin) - p->plane.dist; // only check if a point light is Not *on* the portal if (volume->type != VOLUME_NORMAL || fabs(d) > 0.1) { // check if the portal is partly or totally inside the light volume memcpy(&winding, p->winding, sizeof(winding_t)); for (j = 0; j < volume->numplanes; j++) { res = VL_ChopWinding(&winding, &volume->planes[j], 0.01); if (res == SIDE_BACK || res == SIDE_ON) break; } // if the light volume does not go through this portal at all if (j < volume->numplanes) continue; } // chop the light volume with the portal for (k = 0; k < p->winding->numpoints; k++) { if (volume->type == VOLUME_DIRECTED) { VectorSubtract(p->winding->points[(k+1) % p->winding->numpoints], p->winding->points[k], dir1); CrossProduct(light->normal, dir1, plane.normal); VectorNormalize(plane.normal, plane.normal); plane.dist = DotProduct(plane.normal, p->winding->points[k]); } else { VectorSubtract(p->winding->points[(k+1) % p->winding->numpoints], p->winding->points[k], dir1); VectorSubtract(light->origin, p->winding->points[k], dir2); CrossProduct(dir1, dir2, plane.normal); VectorNormalize(plane.normal, plane.normal); plane.dist = DotProduct(plane.normal, p->winding->points[k]); } res = VL_R_SplitLightVolume(light, volume, &plane, cluster, i+1); if (res == 1) break; //volume didn't really go through the portal } // if the light volume went through the portal if (k >= p->winding->numpoints) { // flood through the portal VL_R_FloodLight(light, volume, p->leaf, 0); } } } /* ============= VL_R_FloodAreaSpotLight ============= */ void VL_FloodAreaSpotLight(vlight_t *light, winding_t *w, int leafnum) { } /* ============= VL_R_SubdivideAreaSpotLight ============= */ void VL_R_SubdivideAreaSpotLight(vlight_t *light, int nodenum, winding_t *w) { int leafnum, res; dnode_t *node; dplane_t *plane; winding_t back; plane_t split; while(nodenum >= 0) { node = &dnodes[nodenum]; plane = &dplanes[node->planeNum]; VectorCopy(plane->normal, split.normal); split.dist = plane->dist; res = VL_SplitWinding (w, &back, &split, 0.1); if (res == SIDE_FRONT) { nodenum = node->children[0]; } else if (res == SIDE_BACK) { nodenum = node->children[1]; } else if (res == SIDE_ON) { memcpy(&back, w, sizeof(winding_t)); VL_R_SubdivideAreaSpotLight(light, node->children[1], &back); nodenum = node->children[0]; } else { VL_R_SubdivideAreaSpotLight(light, node->children[1], &back); nodenum = node->children[0]; } } leafnum = -nodenum - 1; if (dleafs[leafnum].cluster != -1) { VL_FloodAreaSpotLight(light, w, leafnum); } } /* ============= VL_R_FloodRadialAreaLight ============= */ void VL_FloodRadialAreaLight(vlight_t *light, winding_t *w, int leafnum) { } /* ============= VL_R_SubdivideRadialAreaLight ============= */ void VL_R_SubdivideRadialAreaLight(vlight_t *light, int nodenum, winding_t *w) { int leafnum, res; dnode_t *node; dplane_t *plane; winding_t back; plane_t split; while(nodenum >= 0) { node = &dnodes[nodenum]; plane = &dplanes[node->planeNum]; VectorCopy(plane->normal, split.normal); split.dist = plane->dist; res = VL_SplitWinding (w, &back, &split, 0.1); if (res == SIDE_FRONT) { nodenum = node->children[0]; } else if (res == SIDE_BACK) { nodenum = node->children[1]; } else if (res == SIDE_ON) { memcpy(&back, w, sizeof(winding_t)); VL_R_SubdivideRadialAreaLight(light, node->children[1], &back); nodenum = node->children[0]; } else { VL_R_SubdivideRadialAreaLight(light, node->children[1], &back); nodenum = node->children[0]; } } leafnum = -nodenum - 1; if (dleafs[leafnum].cluster != -1) { VL_FloodRadialAreaLight(light, w, leafnum); } } /* ============= VL_R_FloodDirectedLight ============= */ void VL_FloodDirectedLight(vlight_t *light, winding_t *w, int leafnum) { int i; float dist; lightvolume_t volume; vec3_t dir; if (light->atten_disttype == LDAT_NOSCALE) { // light travels without decrease in intensity over distance dist = MAX_WORLD_COORD; } else { if ( light->atten_disttype == LDAT_LINEAR ) dist = light->photons * lightLinearScale; else dist = sqrt(light->photons); } memset(&volume, 0, sizeof(lightvolume_t)); for (i = 0; i < w->numpoints; i++) { VectorMA(w->points[i], dist, light->normal, volume.points[i]); VectorSubtract(w->points[(i+1)%w->numpoints], w->points[i], dir); CrossProduct(light->normal, dir, volume.planes[i].normal); VectorNormalize(volume.planes[i].normal, volume.planes[i].normal); volume.planes[i].dist = DotProduct(volume.planes[i].normal, w->points[i]); } volume.numplanes = w->numpoints; VectorCopy(light->normal, volume.endplane.normal); VectorInverse(volume.endplane.normal); volume.endplane.dist = DotProduct(volume.endplane.normal, volume.points[0]); volume.farplane = volume.endplane; volume.surfaceNum = -1; volume.type = VOLUME_DIRECTED; volume.cluster = dleafs[leafnum].cluster; VL_R_FloodLight(light, &volume, volume.cluster, 0); if (volume.surfaceNum >= 0) { VL_R_CastLightAtSurface(light, &volume); } } /* ============= VL_R_SubdivideDirectedAreaLight ============= */ void VL_R_SubdivideDirectedAreaLight(vlight_t *light, int nodenum, winding_t *w) { int leafnum, res; dnode_t *node; dplane_t *plane; winding_t back; plane_t split; while(nodenum >= 0) { node = &dnodes[nodenum]; plane = &dplanes[node->planeNum]; VectorCopy(plane->normal, split.normal); split.dist = plane->dist; res = VL_SplitWinding (w, &back, &split, 0.1); if (res == SIDE_FRONT) { nodenum = node->children[0]; } else if (res == SIDE_BACK) { nodenum = node->children[1]; } else if (res == SIDE_ON) { memcpy(&back, w, sizeof(winding_t)); VL_R_SubdivideDirectedAreaLight(light, node->children[1], &back); nodenum = node->children[0]; } else { VL_R_SubdivideDirectedAreaLight(light, node->children[1], &back); nodenum = node->children[0]; } } leafnum = -nodenum - 1; if (dleafs[leafnum].cluster != -1) { VL_FloodDirectedLight(light, w, leafnum); } } /* ============= VL_FloodLight ============= */ void VL_FloodLight(vlight_t *light) { lightvolume_t volume; dleaf_t *leaf; int leafnum, i, j, k, dir[2][4] = {{1, 1, -1, -1}, {1, -1, -1, 1}}; float a, step, dist, radius, windingdist; vec3_t vec, r, p, temp; winding_t winding; switch(light->type) { case LIGHT_POINTRADIAL: { // source is a point // light radiates in all directions // creates sharp shadows // // create 6 volumes shining in the axis directions // what about: 4 tetrahedrons instead? // if ( light->atten_disttype == LDAT_LINEAR ) dist = light->photons * lightLinearScale; else dist = sqrt(light->photons); //always put the winding at a large distance to avoid epsilon issues windingdist = MAX_WORLD_COORD; if (dist > windingdist) windingdist = dist; // leafnum = VL_LightLeafnum(light->origin); leaf = &dleafs[leafnum]; if (leaf->cluster == -1) { light->insolid = qtrue; break; } // for each axis for (i = 0; i < 3; i++) { // for both directions on the axis for (j = -1; j <= 1; j += 2) { memset(&volume, 0, sizeof(lightvolume_t)); volume.numplanes = 0; for (k = 0; k < 4; k ++) { volume.points[volume.numplanes][i] = light->origin[i] + j * windingdist; volume.points[volume.numplanes][(i+1)%3] = light->origin[(i+1)%3] + dir[0][k] * windingdist; volume.points[volume.numplanes][(i+2)%3] = light->origin[(i+2)%3] + dir[1][k] * windingdist; volume.numplanes++; } if (j >= 0) { VectorCopy(volume.points[0], temp); VectorCopy(volume.points[2], volume.points[0]); VectorCopy(temp, volume.points[2]); } for (k = 0; k < volume.numplanes; k++) { VL_PlaneFromPoints(&volume.planes[k], light->origin, volume.points[(k+1)%volume.numplanes], volume.points[k]); } VectorCopy(light->origin, temp); temp[i] += (float) j * dist; VectorClear(volume.endplane.normal); volume.endplane.normal[i] = -j; volume.endplane.dist = DotProduct(volume.endplane.normal, temp); //DotProduct(volume.endplane.normal, volume.points[0]); volume.farplane = volume.endplane; volume.cluster = leaf->cluster; volume.surfaceNum = -1; volume.type = VOLUME_NORMAL; // memset(volume.facetTested, 0, sizeof(volume.facetTested)); memset(volume.clusterTested, 0, sizeof(volume.clusterTested)); VL_R_FloodLight(light, &volume, leaf->cluster, 0); if (volume.surfaceNum >= 0) { VL_R_CastLightAtSurface(light, &volume); } } } break; } case LIGHT_POINTSPOT: { // source is a point // light is targetted // creates sharp shadows // // what about using brushes to shape spot lights? that'd be pretty cool // if ( light->atten_disttype == LDAT_LINEAR ) dist = light->photons * lightLinearScale; else dist = sqrt(light->photons); dist *= 2; // windingdist = 4096; if (dist > windingdist) windingdist = dist; //take 8 times the cone radius because the spotlight also lights outside the cone radius = 8 * windingdist * light->radiusByDist; // memset(&volume, 0, sizeof(lightvolume_t)); leafnum = VL_LightLeafnum(light->origin); leaf = &dleafs[leafnum]; if (leaf->cluster == -1) { light->insolid = qtrue; break; } // VectorClear(vec); for (i = 0; i < 3; i++) { if (light->normal[i] > -0.9 && light->normal[i] < 0.9) { vec[i] = 1; break; } } CrossProduct(light->normal, vec, r); VectorScale(r, radius, p); volume.numplanes = 0; step = 45; for (a = step / 2; a < 360 + step / 2; a += step) { RotatePointAroundVector(volume.points[volume.numplanes], light->normal, p, a); VectorAdd(light->origin, volume.points[volume.numplanes], volume.points[volume.numplanes]); VectorMA(volume.points[volume.numplanes], windingdist, light->normal, volume.points[volume.numplanes]); volume.numplanes++; } for (i = 0; i < volume.numplanes; i++) { VL_PlaneFromPoints(&volume.planes[i], light->origin, volume.points[(i+1)%volume.numplanes], volume.points[i]); } VectorMA(light->origin, dist, light->normal, temp); VectorCopy(light->normal, volume.endplane.normal); VectorInverse(volume.endplane.normal); volume.endplane.dist = DotProduct(volume.endplane.normal, temp);//DotProduct(volume.endplane.normal, volume.points[0]); volume.farplane = volume.endplane; volume.cluster = leaf->cluster; volume.surfaceNum = -1; volume.type = VOLUME_NORMAL; // memset(volume.facetTested, 0, sizeof(volume.facetTested)); memset(volume.clusterTested, 0, sizeof(volume.clusterTested)); VL_R_FloodLight(light, &volume, leaf->cluster, 0); if (volume.surfaceNum >= 0) { VL_R_CastLightAtSurface(light, &volume); } break; } case LIGHT_POINTFAKESURFACE: { float value; int n, axis; vec3_t v, vecs[2]; if ( light->atten_disttype == LDAT_LINEAR ) dist = light->photons * lightLinearScale; else dist = sqrt(light->photons); //always put the winding at a large distance to avoid epsilon issues windingdist = 4096; if (dist > windingdist) windingdist = dist; // VectorMA(light->origin, 0.1, light->normal, light->origin); // leafnum = VL_LightLeafnum(light->origin); leaf = &dleafs[leafnum]; if (leaf->cluster == -1) { light->insolid = qtrue; break; } value = 0; for (i = 0; i < 3; i++) { if (fabs(light->normal[i]) > value) { value = fabs(light->normal[i]); axis = i; } } for (i = 0; i < 2; i++) { VectorClear(v); v[(axis + 1 + i) % 3] = 1; CrossProduct(light->normal, v, vecs[i]); } //cast 4 volumes at the front of the surface for (i = -1; i <= 1; i += 2) { for (j = -1; j <= 1; j += 2) { for (n = 0; n < 2; n++) { memset(&volume, 0, sizeof(lightvolume_t)); volume.numplanes = 3; VectorMA(light->origin, i * windingdist, vecs[0], volume.points[(i == j) == n]); VectorMA(light->origin, j * windingdist, vecs[1], volume.points[(i != j) == n]); VectorMA(light->origin, windingdist, light->normal, volume.points[2]); for (k = 0; k < volume.numplanes; k++) { VL_PlaneFromPoints(&volume.planes[k], light->origin, volume.points[(k+1)%volume.numplanes], volume.points[k]); } VL_PlaneFromPoints(&volume.endplane, volume.points[0], volume.points[1], volume.points[2]); VectorMA(light->origin, dist, light->normal, temp); volume.endplane.dist = DotProduct(volume.endplane.normal, temp); if (DotProduct(light->origin, volume.endplane.normal) - volume.endplane.dist > 0) break; } volume.farplane = volume.endplane; volume.cluster = leaf->cluster; volume.surfaceNum = -1; volume.type = VOLUME_NORMAL; // memset(volume.facetTested, 0, sizeof(volume.facetTested)); memset(volume.clusterTested, 0, sizeof(volume.clusterTested)); VL_R_FloodLight(light, &volume, leaf->cluster, 0); if (volume.surfaceNum >= 0) { VL_R_CastLightAtSurface(light, &volume); } } } break; } case LIGHT_SURFACEDIRECTED: { // source is an area defined by a winding // the light is unidirectional // creates sharp shadows // for instance sun light or laser light // memcpy(&winding, &light->w, sizeof(winding_t)); VL_R_SubdivideDirectedAreaLight(light, 0, &winding); break; } case LIGHT_SURFACERADIAL: { // source is an area defined by a winding // the light radiates in all directions at the front of the winding plane // memcpy(&winding, &light->w, sizeof(winding_t)); VL_R_SubdivideRadialAreaLight(light, 0, &winding); break; } case LIGHT_SURFACESPOT: { // source is an area defined by a winding // light is targetted but not unidirectional // memcpy(&winding, &light->w, sizeof(winding_t)); VL_R_SubdivideAreaSpotLight(light, 0, &winding); break; } } } /* ============= VL_FloodLightThread ============= */ void VL_FloodLightThread(int num) { VL_FloodLight(vlights[num]); } /* ============= VL_TestLightLeafs ============= */ void VL_TestLightLeafs(void) { int leafnum, i; vlight_t *light; dleaf_t *leaf; for (i = 0; i < numvlights; i++) { light = vlights[i]; if (light->type != LIGHT_POINTRADIAL && light->type != LIGHT_POINTSPOT) continue; leafnum = VL_LightLeafnum(light->origin); leaf = &dleafs[leafnum]; if (leaf->cluster == -1) if (light->type == LIGHT_POINTRADIAL) qprintf("light in solid at %1.1f %1.1f %1.1f\n", light->origin[0], light->origin[1], light->origin[2]); else if (light->type == LIGHT_POINTSPOT) qprintf("spot light in solid at %1.1f %1.1f %1.1f\n", light->origin[0], light->origin[1], light->origin[2]); } } /* ============= VL_DoForcedTraceLight ============= */ // from light.c void TraceLtm( int num ); void VL_DoForcedTraceLight(int num) { dsurface_t *ds; shaderInfo_t *si; ds = &drawSurfaces[num]; if ( ds->surfaceType == MST_TRIANGLE_SOUP ) return; if ( ds->lightmapNum < 0 ) return; // always light entity surfaces with the old light algorithm if ( !entitySurface[num] ) { si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader ); if (defaulttracelight) { if (si->forceVLight) return; } else { if (!si->forceTraceLight) return; } } TraceLtm(num); } /* ============= VL_DoForcedTraceLightSurfaces ============= */ void VL_DoForcedTraceLightSurfaces(void) { _printf( "forced trace light\n" ); RunThreadsOnIndividual( numDrawSurfaces, qtrue, VL_DoForcedTraceLight ); } float *oldLightFloats; /* ============= VL_SurfaceRadiosity ============= */ void VL_SurfaceRadiosity( int num ) { dsurface_t *ds; mesh_t *mesh; shaderInfo_t *si; lsurfaceTest_t *test; int x, y, k; vec3_t base, normal; float *color, area; vlight_t vlight; ds = &drawSurfaces[num]; if ( ds->lightmapNum < 0 ) { return; // doesn't have a lightmap } // vertex-lit triangle model if ( ds->surfaceType == MST_TRIANGLE_SOUP ) { return; } si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader ); test = lsurfaceTest[ num ]; if (!test) { return; } for (x = 0; x < ds->lightmapWidth; x++) { for (y = 0; y < ds->lightmapHeight; y++) { // k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x; area = lightmappixelarea[k]; if (area <= 0) continue; // if (ds->surfaceType == MST_PATCH) { mesh = test->detailMesh; VectorCopy( mesh->verts[y*mesh->width+x].xyz, base); VectorCopy( mesh->verts[y*mesh->width+x].normal, normal); } else { VectorMA(ds->lightmapOrigin, (float) x, ds->lightmapVecs[0], base); VectorMA(base, (float) y, ds->lightmapVecs[1], base); VectorCopy(test->facets[0].plane.normal, normal); } // create ligth from base memset(&vlight, 0, sizeof(vlight_t)); color = &oldLightFloats[k*3]; // a few units away from the surface VectorMA(base, 5, normal, vlight.origin); ColorNormalize(color, vlight.color); // ok this is crap vlight.photons = VectorLength(color) * 0.05 * lightPointScale / (area * radiosity_scale); // what about using a front facing light only ? vlight.type = LIGHT_POINTRADIAL; // flood the light from this lightmap pixel VL_FloodLight(&vlight); // only one thread at a time may write to the lightmap of this surface MutexLock(test->mutex); // don't light the lightmap pixel itself lightFloats[k*3] = oldLightFloats[k*3]; lightFloats[k*3+1] = oldLightFloats[k*3+1]; lightFloats[k*3+2] = oldLightFloats[k*3+2]; // MutexUnlock(test->mutex); } } } /* ============= VL_Radiosity this aint working real well but it's fun to play with. ============= */ void VL_Radiosity(void) { oldLightFloats = lightFloats; lightFloats = (float *) malloc(numLightBytes * sizeof(float)); memcpy(lightFloats, oldLightFloats, numLightBytes * sizeof(float)); _printf("%7i surfaces\n", numDrawSurfaces); RunThreadsOnIndividual( numDrawSurfaces, qtrue, VL_SurfaceRadiosity ); free(oldLightFloats); } /* ============= VL_LightWorld ============= */ void VL_LightWorld(void) { int i, numcastedvolumes, numvlightsinsolid; float f; // find the optional world ambient GetVectorForKey( &entities[0], "_color", lightAmbientColor ); f = FloatForKey( &entities[0], "ambient" ); VectorScale( lightAmbientColor, f, lightAmbientColor ); /* _printf("\r%6d lights out of %d", 0, numvlights); for (i = 0; i < numvlights; i++) { _printf("\r%6d", i); VL_FloodLight(vlights[i]); } _printf("\r%6d lights out of %d\n", i, numvlights); */ _printf("%7i lights\n", numvlights); RunThreadsOnIndividual( numvlights, qtrue, VL_FloodLightThread ); numcastedvolumes = 0; for ( i = 0 ; i < numDrawSurfaces ; i++ ) { if (lsurfaceTest[i]) numcastedvolumes += lsurfaceTest[i]->numvolumes; } _printf("%7i light volumes casted\n", numcastedvolumes); numvlightsinsolid = 0; for (i = 0; i < numvlights; i++) { if (vlights[i]->insolid) numvlightsinsolid++; } _printf("%7i lights in solid\n", numvlightsinsolid); // radiosity_scale = 1; for (i = 0; i < radiosity; i++) { VL_Radiosity(); radiosity_scale <<= 1; } // VL_StoreLightmap(); // redo surfaces with the old light algorithm when needed VL_DoForcedTraceLightSurfaces(); } /* ============= VL_CreateEntityLights ============= */ entity_t *FindTargetEntity( const char *target ); void VL_CreateEntityLights (void) { int i, c_entityLights; vlight_t *dl; entity_t *e, *e2; const char *name; const char *target; vec3_t dest; const char *_color; float intensity; int spawnflags; // c_entityLights = 0; _printf("Creating entity lights...\n"); // for ( i = 0 ; i < num_entities ; i++ ) { e = &entities[i]; name = ValueForKey (e, "classname"); if (strncmp (name, "light", 5)) continue; dl = malloc(sizeof(*dl)); memset (dl, 0, sizeof(*dl)); spawnflags = FloatForKey (e, "spawnflags"); if ( spawnflags & 1 ) { dl->atten_disttype = LDAT_LINEAR; } if ( spawnflags & 2 ) { dl->atten_disttype = LDAT_NOSCALE; } if ( spawnflags & 4 ) { dl->atten_angletype = LAAT_QUADRATIC; } if ( spawnflags & 8 ) { dl->atten_angletype = LAAT_DOUBLEQUADRATIC; } dl->atten_distscale = FloatForKey(e, "atten_distscale"); dl->atten_anglescale = FloatForKey(e, "atten_anglescale"); GetVectorForKey (e, "origin", dl->origin); dl->style = FloatForKey (e, "_style"); if (!dl->style) dl->style = FloatForKey (e, "style"); if (dl->style < 0) dl->style = 0; intensity = FloatForKey (e, "light"); if (!intensity) intensity = FloatForKey (e, "_light"); if (!intensity) intensity = 300; _color = ValueForKey (e, "_color"); if (_color && _color[0]) { sscanf (_color, "%f %f %f", &dl->color[0],&dl->color[1],&dl->color[2]); ColorNormalize (dl->color, dl->color); } else dl->color[0] = dl->color[1] = dl->color[2] = 1.0; intensity = intensity * lightPointScale; dl->photons = intensity; dl->type = LIGHT_POINTRADIAL; // lights with a target will be spotlights target = ValueForKey (e, "target"); if ( target[0] ) { float radius; float dist; e2 = FindTargetEntity (target); if (!e2) { _printf ("WARNING: light at (%i %i %i) has missing target\n", (int)dl->origin[0], (int)dl->origin[1], (int)dl->origin[2]); } else { GetVectorForKey (e2, "origin", dest); VectorSubtract (dest, dl->origin, dl->normal); dist = VectorNormalize (dl->normal, dl->normal); radius = FloatForKey (e, "radius"); if ( !radius ) { radius = 64; } if ( !dist ) { dist = 64; } dl->radiusByDist = (radius + 16) / dist; dl->type = LIGHT_POINTSPOT; } } vlights[numvlights++] = dl; c_entityLights++; } _printf("%7i entity lights\n", c_entityLights); } /* ================== VL_SubdivideAreaLight ================== */ void VL_SubdivideAreaLight( shaderInfo_t *ls, winding_t *w, vec3_t normal, float areaSubdivide, qboolean backsplash ) { float area, value, intensity; vlight_t *dl, *dl2; vec3_t mins, maxs; int axis; winding_t *front, *back; vec3_t planeNormal; float planeDist; if ( !w ) { return; } WindingBounds( w, mins, maxs ); // check for subdivision for ( axis = 0 ; axis < 3 ; axis++ ) { if ( maxs[axis] - mins[axis] > areaSubdivide ) { VectorClear( planeNormal ); planeNormal[axis] = 1; planeDist = ( maxs[axis] + mins[axis] ) * 0.5; ClipWindingEpsilon ( w, planeNormal, planeDist, ON_EPSILON, &front, &back ); VL_SubdivideAreaLight( ls, front, normal, areaSubdivide, qfalse ); VL_SubdivideAreaLight( ls, back, normal, areaSubdivide, qfalse ); FreeWinding( w ); return; } } // create a light from this area = WindingArea (w); if ( area <= 0 || area > 20000000 ) { return; } dl = malloc(sizeof(*dl)); memset (dl, 0, sizeof(*dl)); dl->type = LIGHT_POINTFAKESURFACE; WindingCenter( w, dl->origin ); memcpy(dl->w.points, w->points, sizeof(vec3_t) * w->numpoints); dl->w.numpoints = w->numpoints; VectorCopy ( normal, dl->normal); VectorCopy ( normal, dl->plane); dl->plane[3] = DotProduct( dl->origin, normal ); value = ls->value; intensity = value * area * lightAreaScale; VectorAdd( dl->origin, dl->normal, dl->origin ); VectorCopy( ls->color, dl->color ); dl->photons = intensity; // emitColor is irrespective of the area VectorScale( ls->color, value*lightFormFactorValueScale*lightAreaScale, dl->emitColor ); // VectorCopy(dl->emitColor, dl->color); dl->si = ls; if ( ls->contents & CONTENTS_FOG ) { dl->twosided = qtrue; } vlights[numvlights++] = dl; // optionally create a point backsplash light if ( backsplash && ls->backsplashFraction > 0 ) { dl2 = malloc(sizeof(*dl)); memset (dl2, 0, sizeof(*dl2)); dl2->type = LIGHT_POINTRADIAL; VectorMA( dl->origin, ls->backsplashDistance, normal, dl2->origin ); VectorCopy( ls->color, dl2->color ); dl2->photons = dl->photons * ls->backsplashFraction; dl2->si = ls; vlights[numvlights++] = dl2; } } /* ================== VL_CreateFakeSurfaceLights ================== */ void VL_CreateFakeSurfaceLights( void ) { int i, j, side; dsurface_t *ds; shaderInfo_t *ls; winding_t *w; lFacet_t *f; vlight_t *dl; vec3_t origin; drawVert_t *dv; int c_surfaceLights; float lightSubdivide; vec3_t normal; c_surfaceLights = 0; _printf ("Creating surface lights...\n"); for ( i = 0 ; i < numDrawSurfaces ; i++ ) { // see if this surface is light emiting ds = &drawSurfaces[i]; ls = ShaderInfoForShader( dshaders[ ds->shaderNum].shader ); if ( ls->value == 0 ) { continue; } // determine how much we need to chop up the surface if ( ls->lightSubdivide ) { lightSubdivide = ls->lightSubdivide; } else { lightSubdivide = lightDefaultSubdivide; } c_surfaceLights++; // an autosprite shader will become // a point light instead of an area light if ( ls->autosprite ) { // autosprite geometry should only have four vertexes if ( lsurfaceTest[i] ) { // curve or misc_model f = lsurfaceTest[i]->facets; if ( lsurfaceTest[i]->numFacets != 1 || f->numpoints != 4 ) { _printf( "WARNING: surface at (%i %i %i) has autosprite shader but isn't a quad\n", (int)f->points[0], (int)f->points[1], (int)f->points[2] ); } VectorAdd( f->points[0], f->points[1], origin ); VectorAdd( f->points[2], origin, origin ); VectorAdd( f->points[3], origin, origin ); VectorScale( origin, 0.25, origin ); } else { // normal polygon dv = &drawVerts[ ds->firstVert ]; if ( ds->numVerts != 4 ) { _printf( "WARNING: surface at (%i %i %i) has autosprite shader but %i verts\n", (int)dv->xyz[0], (int)dv->xyz[1], (int)dv->xyz[2] ); continue; } VectorAdd( dv[0].xyz, dv[1].xyz, origin ); VectorAdd( dv[2].xyz, origin, origin ); VectorAdd( dv[3].xyz, origin, origin ); VectorScale( origin, 0.25, origin ); } dl = malloc(sizeof(*dl)); memset (dl, 0, sizeof(*dl)); VectorCopy( origin, dl->origin ); VectorCopy( ls->color, dl->color ); dl->photons = ls->value * lightPointScale; dl->type = LIGHT_POINTRADIAL; vlights[numvlights++] = dl; continue; } // possibly create for both sides of the polygon for ( side = 0 ; side <= ls->twoSided ; side++ ) { // create area lights if ( lsurfaceTest[i] ) { // curve or misc_model for ( j = 0 ; j < lsurfaceTest[i]->numFacets ; j++ ) { f = lsurfaceTest[i]->facets + j; w = AllocWinding( f->numpoints ); w->numpoints = f->numpoints; memcpy( w->points, f->points, f->numpoints * 12 ); VectorCopy( f->plane.normal, normal ); if ( side ) { winding_t *t; t = w; w = ReverseWinding( t ); FreeWinding( t ); VectorSubtract( vec3_origin, normal, normal ); } VL_SubdivideAreaLight( ls, w, normal, lightSubdivide, qtrue ); } } else { // normal polygon w = AllocWinding( ds->numVerts ); w->numpoints = ds->numVerts; for ( j = 0 ; j < ds->numVerts ; j++ ) { VectorCopy( drawVerts[ds->firstVert+j].xyz, w->points[j] ); } VectorCopy( ds->lightmapVecs[2], normal ); if ( side ) { winding_t *t; t = w; w = ReverseWinding( t ); FreeWinding( t ); VectorSubtract( vec3_origin, normal, normal ); } VL_SubdivideAreaLight( ls, w, normal, lightSubdivide, qtrue ); } } } _printf( "%7i light emitting surfaces\n", c_surfaceLights ); } /* ================== VL_WindingForBrushSide ================== */ winding_t *VL_WindingForBrushSide(dbrush_t *brush, int side, winding_t *w) { int i, res; winding_t *tmpw; plane_t plane; VectorCopy(dplanes[ dbrushsides[ brush->firstSide + side ].planeNum ].normal, plane.normal); VectorInverse(plane.normal); plane.dist = -dplanes[ dbrushsides[ brush->firstSide + side ].planeNum ].dist; tmpw = BaseWindingForPlane( plane.normal, plane.dist ); memcpy(w->points, tmpw->points, sizeof(vec3_t) * tmpw->numpoints); w->numpoints = tmpw->numpoints; for (i = 0; i < brush->numSides; i++) { if (i == side) continue; VectorCopy(dplanes[ dbrushsides[ brush->firstSide + i ].planeNum ].normal, plane.normal); VectorInverse(plane.normal); plane.dist = -dplanes[ dbrushsides[ brush->firstSide + i ].planeNum ].dist; res = VL_ChopWinding(w, &plane, 0.1); if (res == SIDE_BACK) return NULL; } return w; } /* ================== VL_CreateSkyLights ================== */ void VL_CreateSkyLights(void) { int i, j, c_skyLights; dbrush_t *b; shaderInfo_t *si; dbrushside_t *s; vlight_t *dl; vec3_t sunColor, sunDir = { 0.45, 0.3, 0.9 }; float d; VectorNormalize(sunDir, sunDir); VectorInverse(sunDir); c_skyLights = 0; _printf("Creating sky lights...\n"); // find the sky shader for ( i = 0 ; i < numDrawSurfaces ; i++ ) { si = ShaderInfoForShader( dshaders[ drawSurfaces[i].shaderNum ].shader ); if ( si->surfaceFlags & SURF_SKY ) { VectorCopy( si->sunLight, sunColor ); VectorCopy( si->sunDirection, sunDir ); VectorInverse(sunDir); break; } } // find the brushes for ( i = 0 ; i < numbrushes ; i++ ) { b = &dbrushes[i]; for ( j = 0 ; j < b->numSides ; j++ ) { s = &dbrushsides[ b->firstSide + j ]; if ( dshaders[ s->shaderNum ].surfaceFlags & SURF_SKY ) { //if this surface doesn't face in the same direction as the sun d = DotProduct(dplanes[ s->planeNum ].normal, sunDir); if (d <= 0) continue; // dl = malloc(sizeof(*dl)); memset (dl, 0, sizeof(*dl)); VectorCopy(sunColor, dl->color); VectorCopy(sunDir, dl->normal); VectorCopy(dplanes[ s->planeNum ].normal, dl->plane); dl->plane[3] = dplanes[ s->planeNum ].dist; dl->type = LIGHT_SURFACEDIRECTED; dl->atten_disttype = LDAT_NOSCALE; VL_WindingForBrushSide(b, j, &dl->w); // DebugNet_DrawWinding(&dl->w, 2); // vlights[numvlights++] = dl; c_skyLights++; } } } _printf("%7i light emitting sky surfaces\n", c_skyLights); } /* ================== VL_SetPortalSphere ================== */ void VL_SetPortalSphere (lportal_t *p) { int i; vec3_t total, dist; winding_t *w; float r, bestr; w = p->winding; VectorCopy (vec3_origin, total); for (i=0 ; inumpoints ; i++) { VectorAdd (total, w->points[i], total); } for (i=0 ; i<3 ; i++) total[i] /= w->numpoints; bestr = 0; for (i=0 ; inumpoints ; i++) { VectorSubtract (w->points[i], total, dist); r = VectorLength (dist); if (r > bestr) bestr = r; } VectorCopy (total, p->origin); p->radius = bestr; } /* ================== VL_PlaneFromWinding ================== */ void VL_PlaneFromWinding (winding_t *w, plane_t *plane) { vec3_t v1, v2; //calc plane VectorSubtract (w->points[2], w->points[1], v1); VectorSubtract (w->points[0], w->points[1], v2); CrossProduct (v2, v1, plane->normal); VectorNormalize (plane->normal, plane->normal); plane->dist = DotProduct (w->points[0], plane->normal); } /* ================== VL_AllocWinding ================== */ winding_t *VL_AllocWinding (int points) { winding_t *w; int size; if (points > MAX_POINTS_ON_WINDING) Error ("NewWinding: %i points", points); size = (int)((winding_t *)0)->points[points]; w = malloc (size); memset (w, 0, size); return w; } /* ============ VL_LoadPortals ============ */ void VL_LoadPortals (char *name) { int i, j, hint; lportal_t *p; lleaf_t *l; char magic[80]; FILE *f; int numpoints; winding_t *w; int leafnums[2]; plane_t plane; // if (!strcmp(name,"-")) f = stdin; else { f = fopen(name, "r"); if (!f) Error ("LoadPortals: couldn't read %s\n",name); } if (fscanf (f,"%79s\n%i\n%i\n%i\n",magic, &portalclusters, &numportals, &numfaces) != 4) Error ("LoadPortals: failed to read header"); if (strcmp(magic, PORTALFILE)) Error ("LoadPortals: not a portal file"); _printf ("%6i portalclusters\n", portalclusters); _printf ("%6i numportals\n", numportals); _printf ("%6i numfaces\n", numfaces); if (portalclusters >= MAX_CLUSTERS) Error ("more than %d clusters in portal file\n", MAX_CLUSTERS); // each file portal is split into two memory portals portals = malloc(2*numportals*sizeof(lportal_t)); memset (portals, 0, 2*numportals*sizeof(lportal_t)); leafs = malloc(portalclusters*sizeof(lleaf_t)); memset (leafs, 0, portalclusters*sizeof(lleaf_t)); for (i=0, p=portals ; i MAX_POINTS_ON_WINDING) Error ("LoadPortals: portal %i has too many points", i); if ( (unsigned)leafnums[0] > portalclusters || (unsigned)leafnums[1] > portalclusters) Error ("LoadPortals: reading portal %i", i); if (fscanf (f, "%i ", &hint) != 1) Error ("LoadPortals: reading hint state"); w = p->winding = VL_AllocWinding (numpoints); w->numpoints = numpoints; for (j=0 ; jpoints[j][k] = v[k]; } fscanf (f, "\n"); // calc plane VL_PlaneFromWinding (w, &plane); // create forward portal l = &leafs[leafnums[0]]; if (l->numportals == MAX_PORTALS_ON_LEAF) Error ("Leaf with too many portals"); l->portals[l->numportals] = p; l->numportals++; p->winding = w; VectorSubtract (vec3_origin, plane.normal, p->plane.normal); p->plane.dist = -plane.dist; p->leaf = leafnums[1]; VL_SetPortalSphere (p); p++; // create backwards portal l = &leafs[leafnums[1]]; if (l->numportals == MAX_PORTALS_ON_LEAF) Error ("Leaf with too many portals"); l->portals[l->numportals] = p; l->numportals++; p->winding = VL_AllocWinding(w->numpoints); p->winding->numpoints = w->numpoints; for (j=0 ; jnumpoints ; j++) { VectorCopy (w->points[w->numpoints-1-j], p->winding->points[j]); } p->plane = plane; p->leaf = leafnums[0]; VL_SetPortalSphere (p); p++; } fclose (f); } /* ============ VLightMain ============ */ int VLightMain (int argc, char **argv) { int i; double start, end; const char *value; _printf ("----- VLighting ----\n"); for (i=1 ; i [- ...]] \n" "\n" "Switches:\n" " v = verbose output\n" " threads = set number of threads to X\n" " area = set the area light scale to V\n" " point = set the point light scale to W\n" " novertex = don't calculate vertex lighting\n" " nogrid = don't calculate light grid for dynamic model lighting\n" " nostitching = no polygon stitching before lighting\n" " noalphashading = don't use alpha shading\n" " nocolorshading = don't use color alpha shading\n" " tracelight = use old light algorithm by default\n" " samplesize = set the lightmap pixel size to NxN units\n"); exit(0); } SetQdirFromPath (argv[i]); #ifdef _WIN32 InitPakFile(gamedir, NULL); #endif strcpy (source, ExpandArg(argv[i])); StripExtension (source); DefaultExtension (source, ".bsp"); LoadShaderInfo(); _printf ("reading %s\n", source); LoadBSPFile (source); ParseEntities(); value = ValueForKey( &entities[0], "gridsize" ); if (strlen(value)) { sscanf( value, "%f %f %f", &gridSize[0], &gridSize[1], &gridSize[2] ); _printf("grid size = {%1.1f, %1.1f, %1.1f}\n", gridSize[0], gridSize[1], gridSize[2]); } CountLightmaps(); StripExtension (source); DefaultExtension (source, ".prt"); VL_LoadPortals(source); // set surfaceOrigin SetEntityOrigins(); // grid and vertex lighting GridAndVertexLighting(); #ifdef DEBUGNET DebugNet_Setup(); #endif start = clock(); lightFloats = (float *) malloc(numLightBytes * sizeof(float)); memset(lightFloats, 0, numLightBytes * sizeof(float)); VL_InitSurfacesForTesting(); VL_CalcVisibleLightmapPixelArea(); numvlights = 0; VL_CreateEntityLights(); VL_CreateFakeSurfaceLights(); VL_CreateSkyLights(); VL_TestLightLeafs(); VL_LightWorld(); #ifndef LIGHTPOLYS StripExtension (source); DefaultExtension (source, ".bsp"); _printf ("writing %s\n", source); WriteBSPFile (source); #endif end = clock(); _printf ("%5.2f seconds elapsed\n", (end-start) / CLK_TCK); #ifdef LIGHTPOLYS VL_DrawLightWindings(); #endif #ifdef DEBUGNET DebugNet_Shutdown(); #endif return 0; }