Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3598 lines
105 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "vrad.h"
  9. #include "lightmap.h"
  10. #include "radial.h"
  11. #include "mathlib/bumpvects.h"
  12. #include "tier1/utlvector.h"
  13. #include "vmpi.h"
  14. #include "mathlib/anorms.h"
  15. #include "map_utils.h"
  16. #include "mathlib/halton.h"
  17. #include "imagepacker.h"
  18. #include "tier1/utlrbtree.h"
  19. #include "tier1/utlbuffer.h"
  20. #include "bitmap/tgawriter.h"
  21. #include "mathlib/quantize.h"
  22. #include "bitmap/imageformat.h"
  23. #include "coordsize.h"
  24. enum
  25. {
  26. AMBIENT_ONLY = 0x1,
  27. NON_AMBIENT_ONLY = 0x2,
  28. };
  29. #define SMOOTHING_GROUP_HARD_EDGE 0xff000000
  30. //==========================================================================//
  31. // CNormalList.
  32. //==========================================================================//
  33. // This class keeps a list of unique normals and provides a fast
  34. class CNormalList
  35. {
  36. public:
  37. CNormalList();
  38. // Adds the normal if unique. Otherwise, returns the normal's index into m_Normals.
  39. int FindOrAddNormal( Vector const &vNormal );
  40. public:
  41. CUtlVector<Vector> m_Normals;
  42. private:
  43. // This represents a grid from (-1,-1,-1) to (1,1,1).
  44. enum {NUM_SUBDIVS = 8};
  45. CUtlVector<int> m_NormalGrid[NUM_SUBDIVS][NUM_SUBDIVS][NUM_SUBDIVS];
  46. };
  47. int g_iCurFace;
  48. edgeshare_t edgeshare[MAX_MAP_EDGES];
  49. Vector face_centroids[MAX_MAP_EDGES];
  50. int vertexref[MAX_MAP_VERTS];
  51. int *vertexface[MAX_MAP_VERTS];
  52. faceneighbor_t faceneighbor[MAX_MAP_FACES];
  53. static directlight_t *gSkyLight = NULL;
  54. static directlight_t *gAmbient = NULL;
  55. //==========================================================================//
  56. // CNormalList implementation.
  57. //==========================================================================//
  58. CNormalList::CNormalList() : m_Normals( 128 )
  59. {
  60. for( int i=0; i < sizeof(m_NormalGrid)/sizeof(m_NormalGrid[0][0][0]); i++ )
  61. {
  62. (&m_NormalGrid[0][0][0] + i)->SetGrowSize( 16 );
  63. }
  64. }
  65. int CNormalList::FindOrAddNormal( Vector const &vNormal )
  66. {
  67. int gi[3];
  68. // See which grid element it's in.
  69. for( int iDim=0; iDim < 3; iDim++ )
  70. {
  71. gi[iDim] = (int)( ((vNormal[iDim] + 1.0f) * 0.5f) * NUM_SUBDIVS - 0.000001f );
  72. gi[iDim] = min( gi[iDim], NUM_SUBDIVS );
  73. gi[iDim] = max( gi[iDim], 0 );
  74. }
  75. // Look for a matching vector in there.
  76. CUtlVector<int> *pGridElement = &m_NormalGrid[gi[0]][gi[1]][gi[2]];
  77. for( int i=0; i < pGridElement->Size(); i++ )
  78. {
  79. int iNormal = pGridElement->Element(i);
  80. Vector *pVec = &m_Normals[iNormal];
  81. //if( pVec->DistToSqr(vNormal) < 0.00001f )
  82. if( *pVec == vNormal )
  83. return iNormal;
  84. }
  85. // Ok, add a new one.
  86. pGridElement->AddToTail( m_Normals.Size() );
  87. return m_Normals.AddToTail( vNormal );
  88. }
  89. // FIXME: HACK until the plane normals are made more happy
  90. void GetBumpNormals( const float* sVect, const float* tVect, const Vector& flatNormal,
  91. const Vector& phongNormal, Vector bumpNormals[NUM_BUMP_VECTS] )
  92. {
  93. Vector stmp( sVect[0], sVect[1], sVect[2] );
  94. Vector ttmp( tVect[0], tVect[1], tVect[2] );
  95. GetBumpNormals( stmp, ttmp, flatNormal, phongNormal, bumpNormals );
  96. }
  97. int EdgeVertex( dface_t *f, int edge )
  98. {
  99. int k;
  100. if (edge < 0)
  101. edge += f->numedges;
  102. else if (edge >= f->numedges)
  103. edge = edge % f->numedges;
  104. k = dsurfedges[f->firstedge + edge];
  105. if (k < 0)
  106. {
  107. // Msg("(%d %d) ", dedges[-k].v[1], dedges[-k].v[0] );
  108. return dedges[-k].v[1];
  109. }
  110. else
  111. {
  112. // Msg("(%d %d) ", dedges[k].v[0], dedges[k].v[1] );
  113. return dedges[k].v[0];
  114. }
  115. }
  116. /*
  117. ============
  118. PairEdges
  119. ============
  120. */
  121. void PairEdges (void)
  122. {
  123. int i, j, k, n, m;
  124. dface_t *f;
  125. int numneighbors;
  126. int tmpneighbor[64];
  127. faceneighbor_t *fn;
  128. // count number of faces that reference each vertex
  129. for (i=0, f = g_pFaces; i<numfaces ; i++, f++)
  130. {
  131. for (j=0 ; j<f->numedges ; j++)
  132. {
  133. // Store the count in vertexref
  134. vertexref[EdgeVertex(f,j)]++;
  135. }
  136. }
  137. // allocate room
  138. for (i = 0; i < numvertexes; i++)
  139. {
  140. // use the count from above to allocate a big enough array
  141. vertexface[i] = ( int* )calloc( vertexref[i], sizeof( vertexface[0] ) );
  142. // clear the temporary data
  143. vertexref[i] = 0;
  144. }
  145. // store a list of every face that uses a particular vertex
  146. for (i=0, f = g_pFaces ; i<numfaces ; i++, f++)
  147. {
  148. for (j=0 ; j<f->numedges ; j++)
  149. {
  150. n = EdgeVertex(f,j);
  151. for (k = 0; k < vertexref[n]; k++)
  152. {
  153. if (vertexface[n][k] == i)
  154. break;
  155. }
  156. if (k >= vertexref[n])
  157. {
  158. // add the face to the list
  159. vertexface[n][k] = i;
  160. vertexref[n]++;
  161. }
  162. }
  163. }
  164. // calc normals and set displacement surface flag
  165. for (i=0, f = g_pFaces; i<numfaces ; i++, f++)
  166. {
  167. fn = &faceneighbor[i];
  168. // get face normal
  169. VectorCopy( dplanes[f->planenum].normal, fn->facenormal );
  170. // set displacement surface flag
  171. fn->bHasDisp = false;
  172. if( ValidDispFace( f ) )
  173. {
  174. fn->bHasDisp = true;
  175. }
  176. }
  177. // find neighbors
  178. for (i=0, f = g_pFaces ; i<numfaces ; i++, f++)
  179. {
  180. numneighbors = 0;
  181. fn = &faceneighbor[i];
  182. // allocate room for vertex normals
  183. fn->normal = ( Vector* )calloc( f->numedges, sizeof( fn->normal[0] ) );
  184. // look up all faces sharing vertices and add them to the list
  185. for (j=0 ; j<f->numedges ; j++)
  186. {
  187. n = EdgeVertex(f,j);
  188. for (k = 0; k < vertexref[n]; k++)
  189. {
  190. double cos_normals_angle;
  191. Vector *pNeighbornormal;
  192. // skip self
  193. if (vertexface[n][k] == i)
  194. continue;
  195. // if this face doens't have a displacement -- don't consider displacement neighbors
  196. if( ( !fn->bHasDisp ) && ( faceneighbor[vertexface[n][k]].bHasDisp ) )
  197. continue;
  198. pNeighbornormal = &faceneighbor[vertexface[n][k]].facenormal;
  199. cos_normals_angle = DotProduct( *pNeighbornormal, fn->facenormal );
  200. // add normal if >= threshold or its a displacement surface (this is only if the original
  201. // face is a displacement)
  202. if ( fn->bHasDisp )
  203. {
  204. // Always smooth with and against a displacement surface.
  205. VectorAdd( fn->normal[j], *pNeighbornormal, fn->normal[j] );
  206. }
  207. else
  208. {
  209. // No smoothing - use of method (backwards compatibility).
  210. if ( ( f->smoothingGroups == 0 ) && ( g_pFaces[vertexface[n][k]].smoothingGroups == 0 ) )
  211. {
  212. if ( cos_normals_angle >= smoothing_threshold )
  213. {
  214. VectorAdd( fn->normal[j], *pNeighbornormal, fn->normal[j] );
  215. }
  216. else
  217. {
  218. // not considered a neighbor
  219. continue;
  220. }
  221. }
  222. else
  223. {
  224. unsigned int smoothingGroup = ( f->smoothingGroups & g_pFaces[vertexface[n][k]].smoothingGroups );
  225. // Hard edge.
  226. if ( ( smoothingGroup & SMOOTHING_GROUP_HARD_EDGE ) != 0 )
  227. continue;
  228. if ( smoothingGroup != 0 )
  229. {
  230. VectorAdd( fn->normal[j], *pNeighbornormal, fn->normal[j] );
  231. }
  232. else
  233. {
  234. // not considered a neighbor
  235. continue;
  236. }
  237. }
  238. }
  239. // look to see if we've already added this one
  240. for (m = 0; m < numneighbors; m++)
  241. {
  242. if (tmpneighbor[m] == vertexface[n][k])
  243. break;
  244. }
  245. if (m >= numneighbors)
  246. {
  247. // add to neighbor list
  248. tmpneighbor[m] = vertexface[n][k];
  249. numneighbors++;
  250. if ( numneighbors > ARRAYSIZE(tmpneighbor) )
  251. {
  252. Error("Stack overflow in neighbors\n");
  253. }
  254. }
  255. }
  256. }
  257. if (numneighbors)
  258. {
  259. // copy over neighbor list
  260. fn->numneighbors = numneighbors;
  261. fn->neighbor = ( int* )calloc( numneighbors, sizeof( fn->neighbor[0] ) );
  262. for (m = 0; m < numneighbors; m++)
  263. {
  264. fn->neighbor[m] = tmpneighbor[m];
  265. }
  266. }
  267. // fixup normals
  268. for (j = 0; j < f->numedges; j++)
  269. {
  270. VectorAdd( fn->normal[j], fn->facenormal, fn->normal[j] );
  271. VectorNormalize( fn->normal[j] );
  272. }
  273. }
  274. }
  275. void SaveVertexNormals( void )
  276. {
  277. faceneighbor_t *fn;
  278. int i, j;
  279. dface_t *f;
  280. CNormalList normalList;
  281. g_numvertnormalindices = 0;
  282. for( i = 0 ;i<numfaces ; i++ )
  283. {
  284. fn = &faceneighbor[i];
  285. f = &g_pFaces[i];
  286. for( j = 0; j < f->numedges; j++ )
  287. {
  288. Vector vNormal;
  289. if( fn->normal )
  290. {
  291. vNormal = fn->normal[j];
  292. }
  293. else
  294. {
  295. // original faces don't have normals
  296. vNormal.Init( 0, 0, 0 );
  297. }
  298. if( g_numvertnormalindices == MAX_MAP_VERTNORMALINDICES )
  299. {
  300. Error( "g_numvertnormalindices == MAX_MAP_VERTNORMALINDICES" );
  301. }
  302. g_vertnormalindices[g_numvertnormalindices] = (unsigned short)normalList.FindOrAddNormal( vNormal );
  303. g_numvertnormalindices++;
  304. }
  305. }
  306. if( normalList.m_Normals.Size() > MAX_MAP_VERTNORMALS )
  307. {
  308. Error( "g_numvertnormals > MAX_MAP_VERTNORMALS" );
  309. }
  310. // Copy the list of unique vert normals into g_vertnormals.
  311. g_numvertnormals = normalList.m_Normals.Size();
  312. memcpy( g_vertnormals, normalList.m_Normals.Base(), sizeof(g_vertnormals[0]) * normalList.m_Normals.Size() );
  313. }
  314. /*
  315. =================================================================
  316. LIGHTMAP SAMPLE GENERATION
  317. =================================================================
  318. */
  319. //-----------------------------------------------------------------------------
  320. // Purpose: Spits out an error message with information about a lightinfo_t.
  321. // Input : s - Error message string.
  322. // l - lightmap info struct.
  323. //-----------------------------------------------------------------------------
  324. void ErrorLightInfo(const char *s, lightinfo_t *l)
  325. {
  326. texinfo_t *tex = &texinfo[l->face->texinfo];
  327. winding_t *w = WindingFromFace(&g_pFaces[l->facenum], l->modelorg);
  328. //
  329. // Show the face center and material name if possible.
  330. //
  331. if (w != NULL)
  332. {
  333. // Don't exit, we'll try to recover...
  334. Vector vecCenter;
  335. WindingCenter(w, vecCenter);
  336. // FreeWinding(w);
  337. Warning("%s at (%g, %g, %g)\n\tmaterial=%s\n", s, (double)vecCenter.x, (double)vecCenter.y, (double)vecCenter.z, TexDataStringTable_GetString( dtexdata[tex->texdata].nameStringTableID ) );
  338. }
  339. //
  340. // If not, just show the material name.
  341. //
  342. else
  343. {
  344. Warning("%s at (degenerate face)\n\tmaterial=%s\n", s, TexDataStringTable_GetString( dtexdata[tex->texdata].nameStringTableID ));
  345. }
  346. }
  347. void CalcFaceVectors(lightinfo_t *l)
  348. {
  349. texinfo_t *tex;
  350. int i, j;
  351. tex = &texinfo[l->face->texinfo];
  352. // move into lightinfo_t
  353. for (i=0 ; i<2 ; i++)
  354. {
  355. for (j=0 ; j<3 ; j++)
  356. {
  357. l->worldToLuxelSpace[i][j] = tex->lightmapVecsLuxelsPerWorldUnits[i][j];
  358. }
  359. }
  360. //Solve[ { x * w00 + y * w01 + z * w02 - s == 0, x * w10 + y * w11 + z * w12 - t == 0, A * x + B * y + C * z + D == 0 }, { x, y, z } ]
  361. //Rule(x,( C*s*w11 - B*s*w12 + B*t*w02 - C*t*w01 + D*w02*w11 - D*w01*w12) / (+ A*w01*w12 - A*w02*w11 + B*w02*w10 - B*w00*w12 + C*w00*w11 - C*w01*w10 )),
  362. //Rule(y,( A*s*w12 - C*s*w10 + C*t*w00 - A*t*w02 + D*w00*w12 - D*w02*w10) / (+ A*w01*w12 - A*w02*w11 + B*w02*w10 - B*w00*w12 + C*w00*w11 - C*w01*w10 )),
  363. //Rule(z,( B*s*w10 - A*s*w11 + A*t*w01 - B*t*w00 + D*w01*w10 - D*w00*w11) / (+ A*w01*w12 - A*w02*w11 + B*w02*w10 - B*w00*w12 + C*w00*w11 - C*w01*w10 ))))
  364. Vector luxelSpaceCross;
  365. luxelSpaceCross[0] =
  366. tex->lightmapVecsLuxelsPerWorldUnits[1][1] * tex->lightmapVecsLuxelsPerWorldUnits[0][2] -
  367. tex->lightmapVecsLuxelsPerWorldUnits[1][2] * tex->lightmapVecsLuxelsPerWorldUnits[0][1];
  368. luxelSpaceCross[1] =
  369. tex->lightmapVecsLuxelsPerWorldUnits[1][2] * tex->lightmapVecsLuxelsPerWorldUnits[0][0] -
  370. tex->lightmapVecsLuxelsPerWorldUnits[1][0] * tex->lightmapVecsLuxelsPerWorldUnits[0][2];
  371. luxelSpaceCross[2] =
  372. tex->lightmapVecsLuxelsPerWorldUnits[1][0] * tex->lightmapVecsLuxelsPerWorldUnits[0][1] -
  373. tex->lightmapVecsLuxelsPerWorldUnits[1][1] * tex->lightmapVecsLuxelsPerWorldUnits[0][0];
  374. float det = -DotProduct( l->facenormal, luxelSpaceCross );
  375. if ( fabs( det ) < 1.0e-20 )
  376. {
  377. Warning(" warning - face vectors parallel to face normal. bad lighting will be produced\n" );
  378. l->luxelOrigin = vec3_origin;
  379. }
  380. else
  381. {
  382. // invert the matrix
  383. l->luxelToWorldSpace[0][0] = (l->facenormal[2] * l->worldToLuxelSpace[1][1] - l->facenormal[1] * l->worldToLuxelSpace[1][2]) / det;
  384. l->luxelToWorldSpace[1][0] = (l->facenormal[1] * l->worldToLuxelSpace[0][2] - l->facenormal[2] * l->worldToLuxelSpace[0][1]) / det;
  385. l->luxelOrigin[0] = -(l->facedist * luxelSpaceCross[0]) / det;
  386. l->luxelToWorldSpace[0][1] = (l->facenormal[0] * l->worldToLuxelSpace[1][2] - l->facenormal[2] * l->worldToLuxelSpace[1][0]) / det;
  387. l->luxelToWorldSpace[1][1] = (l->facenormal[2] * l->worldToLuxelSpace[0][0] - l->facenormal[0] * l->worldToLuxelSpace[0][2]) / det;
  388. l->luxelOrigin[1] = -(l->facedist * luxelSpaceCross[1]) / det;
  389. l->luxelToWorldSpace[0][2] = (l->facenormal[1] * l->worldToLuxelSpace[1][0] - l->facenormal[0] * l->worldToLuxelSpace[1][1]) / det;
  390. l->luxelToWorldSpace[1][2] = (l->facenormal[0] * l->worldToLuxelSpace[0][1] - l->facenormal[1] * l->worldToLuxelSpace[0][0]) / det;
  391. l->luxelOrigin[2] = -(l->facedist * luxelSpaceCross[2]) / det;
  392. // adjust for luxel offset
  393. VectorMA( l->luxelOrigin, -tex->lightmapVecsLuxelsPerWorldUnits[0][3], l->luxelToWorldSpace[0], l->luxelOrigin );
  394. VectorMA( l->luxelOrigin, -tex->lightmapVecsLuxelsPerWorldUnits[1][3], l->luxelToWorldSpace[1], l->luxelOrigin );
  395. }
  396. // compensate for org'd bmodels
  397. VectorAdd (l->luxelOrigin, l->modelorg, l->luxelOrigin);
  398. }
  399. winding_t *LightmapCoordWindingForFace( lightinfo_t *l )
  400. {
  401. int i;
  402. winding_t *w;
  403. w = WindingFromFace( l->face, l->modelorg );
  404. for (i = 0; i < w->numpoints; i++)
  405. {
  406. Vector2D coord;
  407. WorldToLuxelSpace( l, w->p[i], coord );
  408. w->p[i].x = coord.x;
  409. w->p[i].y = coord.y;
  410. w->p[i].z = 0;
  411. }
  412. return w;
  413. }
  414. void WriteCoordWinding (FILE *out, lightinfo_t *l, winding_t *w, Vector& color )
  415. {
  416. int i;
  417. Vector pos;
  418. fprintf (out, "%i\n", w->numpoints);
  419. for (i=0 ; i<w->numpoints ; i++)
  420. {
  421. LuxelSpaceToWorld( l, w->p[i][0], w->p[i][1], pos );
  422. fprintf (out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
  423. pos[0],
  424. pos[1],
  425. pos[2],
  426. color[ 0 ] / 256,
  427. color[ 1 ] / 256,
  428. color[ 2 ] / 256 );
  429. }
  430. }
  431. //-----------------------------------------------------------------------------
  432. //-----------------------------------------------------------------------------
  433. void DumpFaces( lightinfo_t *pLightInfo, int ndxFace )
  434. {
  435. static FileHandle_t out;
  436. // get face data
  437. faceneighbor_t *fn = &faceneighbor[ndxFace];
  438. Vector &centroid = face_centroids[ndxFace];
  439. // disable threading (not a multi-threadable function!)
  440. ThreadLock();
  441. if( !out )
  442. {
  443. // open the file
  444. out = g_pFileSystem->Open( "face.txt", "w" );
  445. if( !out )
  446. return;
  447. }
  448. //
  449. // write out face
  450. //
  451. for( int ndxEdge = 0; ndxEdge < pLightInfo->face->numedges; ndxEdge++ )
  452. {
  453. // int edge = dsurfedges[pLightInfo->face->firstedge+ndxEdge];
  454. Vector p1, p2;
  455. VectorAdd( dvertexes[EdgeVertex( pLightInfo->face, ndxEdge )].point, pLightInfo->modelorg, p1 );
  456. VectorAdd( dvertexes[EdgeVertex( pLightInfo->face, ndxEdge+1 )].point, pLightInfo->modelorg, p2 );
  457. Vector &n1 = fn->normal[ndxEdge];
  458. Vector &n2 = fn->normal[(ndxEdge+1)%pLightInfo->face->numedges];
  459. CmdLib_FPrintf( out, "3\n");
  460. CmdLib_FPrintf(out, "%f %f %f %f %f %f\n", p1[0], p1[1], p1[2], n1[0] * 0.5 + 0.5, n1[1] * 0.5 + 0.5, n1[2] * 0.5 + 0.5 );
  461. CmdLib_FPrintf(out, "%f %f %f %f %f %f\n", p2[0], p2[1], p2[2], n2[0] * 0.5 + 0.5, n2[1] * 0.5 + 0.5, n2[2] * 0.5 + 0.5 );
  462. CmdLib_FPrintf(out, "%f %f %f %f %f %f\n", centroid[0] + pLightInfo->modelorg[0],
  463. centroid[1] + pLightInfo->modelorg[1],
  464. centroid[2] + pLightInfo->modelorg[2],
  465. fn->facenormal[0] * 0.5 + 0.5,
  466. fn->facenormal[1] * 0.5 + 0.5,
  467. fn->facenormal[2] * 0.5 + 0.5 );
  468. }
  469. // enable threading
  470. ThreadUnlock();
  471. }
  472. //-----------------------------------------------------------------------------
  473. //-----------------------------------------------------------------------------
  474. bool BuildFacesamplesAndLuxels_DoFast( lightinfo_t *pLightInfo, facelight_t *pFaceLight )
  475. {
  476. // lightmap size
  477. int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
  478. int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
  479. // ratio of world area / lightmap area
  480. texinfo_t *pTex = &texinfo[pLightInfo->face->texinfo];
  481. pFaceLight->worldAreaPerLuxel = 1.0 / ( sqrt( DotProduct( pTex->lightmapVecsLuxelsPerWorldUnits[0],
  482. pTex->lightmapVecsLuxelsPerWorldUnits[0] ) ) *
  483. sqrt( DotProduct( pTex->lightmapVecsLuxelsPerWorldUnits[1],
  484. pTex->lightmapVecsLuxelsPerWorldUnits[1] ) ) );
  485. //
  486. // quickly create samples and luxels (copy over samples)
  487. //
  488. pFaceLight->numsamples = width * height;
  489. pFaceLight->sample = ( sample_t* )calloc( pFaceLight->numsamples, sizeof( *pFaceLight->sample ) );
  490. if( !pFaceLight->sample )
  491. return false;
  492. pFaceLight->numluxels = width * height;
  493. pFaceLight->luxel = ( Vector* )calloc( pFaceLight->numluxels, sizeof( *pFaceLight->luxel ) );
  494. if( !pFaceLight->luxel )
  495. return false;
  496. sample_t *pSamples = pFaceLight->sample;
  497. Vector *pLuxels = pFaceLight->luxel;
  498. for( int t = 0; t < height; t++ )
  499. {
  500. for( int s = 0; s < width; s++ )
  501. {
  502. pSamples->s = s;
  503. pSamples->t = t;
  504. pSamples->coord[0] = s;
  505. pSamples->coord[1] = t;
  506. // unused but initialized anyway
  507. pSamples->mins[0] = s - 0.5;
  508. pSamples->mins[1] = t - 0.5;
  509. pSamples->maxs[0] = s + 0.5;
  510. pSamples->maxs[1] = t + 0.5;
  511. pSamples->area = pFaceLight->worldAreaPerLuxel;
  512. LuxelSpaceToWorld( pLightInfo, pSamples->coord[0], pSamples->coord[1], pSamples->pos );
  513. VectorCopy( pSamples->pos, *pLuxels );
  514. pSamples++;
  515. pLuxels++;
  516. }
  517. }
  518. return true;
  519. }
  520. //-----------------------------------------------------------------------------
  521. //-----------------------------------------------------------------------------
  522. bool BuildSamplesAndLuxels_DoFast( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
  523. {
  524. // build samples for a "face"
  525. if( pLightInfo->face->dispinfo == -1 )
  526. {
  527. return BuildFacesamplesAndLuxels_DoFast( pLightInfo, pFaceLight );
  528. }
  529. // build samples for a "displacement"
  530. else
  531. {
  532. return StaticDispMgr()->BuildDispSamplesAndLuxels_DoFast( pLightInfo, pFaceLight, ndxFace );
  533. }
  534. }
  535. //-----------------------------------------------------------------------------
  536. //-----------------------------------------------------------------------------
  537. bool BuildFacesamples( lightinfo_t *pLightInfo, facelight_t *pFaceLight )
  538. {
  539. // lightmap size
  540. int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
  541. int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
  542. // ratio of world area / lightmap area
  543. texinfo_t *pTex = &texinfo[pLightInfo->face->texinfo];
  544. pFaceLight->worldAreaPerLuxel = 1.0 / ( sqrt( DotProduct( pTex->lightmapVecsLuxelsPerWorldUnits[0],
  545. pTex->lightmapVecsLuxelsPerWorldUnits[0] ) ) *
  546. sqrt( DotProduct( pTex->lightmapVecsLuxelsPerWorldUnits[1],
  547. pTex->lightmapVecsLuxelsPerWorldUnits[1] ) ) );
  548. // allocate a large number of samples for creation -- get copied later!
  549. CUtlVector<sample_t> sampleData;
  550. sampleData.SetCount( SINGLE_BRUSH_MAP * 2 );
  551. sample_t *samples = sampleData.Base();
  552. sample_t *pSamples = samples;
  553. // lightmap space winding
  554. winding_t *pLightmapWinding = LightmapCoordWindingForFace( pLightInfo );
  555. //
  556. // build vector pointing along the lightmap cutting planes
  557. //
  558. Vector sNorm( 1.0f, 0.0f, 0.0f );
  559. Vector tNorm( 0.0f, 1.0f, 0.0f );
  560. // sample center offset
  561. float sampleOffset = ( do_centersamples ) ? 0.5 : 1.0;
  562. //
  563. // clip the lightmap "spaced" winding by the lightmap cutting planes
  564. //
  565. winding_t *pWindingT1, *pWindingT2;
  566. winding_t *pWindingS1, *pWindingS2;
  567. float dist;
  568. for( int t = 0; t < height && pLightmapWinding; t++ )
  569. {
  570. dist = t + sampleOffset;
  571. // lop off a sample in the t dimension
  572. // hack - need a separate epsilon for lightmap space since ON_EPSILON is for texture space
  573. ClipWindingEpsilon( pLightmapWinding, tNorm, dist, ON_EPSILON / 16.0f, &pWindingT1, &pWindingT2 );
  574. for( int s = 0; s < width && pWindingT2; s++ )
  575. {
  576. dist = s + sampleOffset;
  577. // lop off a sample in the s dimension, and put it in ws2
  578. // hack - need a separate epsilon for lightmap space since ON_EPSILON is for texture space
  579. ClipWindingEpsilon( pWindingT2, sNorm, dist, ON_EPSILON / 16.0f, &pWindingS1, &pWindingS2 );
  580. //
  581. // s2 winding is a single sample worth of winding
  582. //
  583. if( pWindingS2 )
  584. {
  585. // save the s, t positions
  586. pSamples->s = s;
  587. pSamples->t = t;
  588. // get the lightmap space area of ws2 and convert to world area
  589. // and find the center (then convert it to 2D)
  590. Vector center;
  591. pSamples->area = WindingAreaAndBalancePoint( pWindingS2, center ) * pFaceLight->worldAreaPerLuxel;
  592. pSamples->coord[0] = center.x;
  593. pSamples->coord[1] = center.y;
  594. // find winding bounds (then convert it to 2D)
  595. Vector minbounds, maxbounds;
  596. WindingBounds( pWindingS2, minbounds, maxbounds );
  597. pSamples->mins[0] = minbounds.x;
  598. pSamples->mins[1] = minbounds.y;
  599. pSamples->maxs[0] = maxbounds.x;
  600. pSamples->maxs[1] = maxbounds.y;
  601. // convert from lightmap space to world space
  602. LuxelSpaceToWorld( pLightInfo, pSamples->coord[0], pSamples->coord[1], pSamples->pos );
  603. if (g_bDumpPatches || (do_extra && pSamples->area < pFaceLight->worldAreaPerLuxel - EQUAL_EPSILON))
  604. {
  605. //
  606. // convert the winding from lightmaps space to world for debug rendering and sub-sampling
  607. //
  608. Vector worldPos;
  609. for( int ndxPt = 0; ndxPt < pWindingS2->numpoints; ndxPt++ )
  610. {
  611. LuxelSpaceToWorld( pLightInfo, pWindingS2->p[ndxPt].x, pWindingS2->p[ndxPt].y, worldPos );
  612. VectorCopy( worldPos, pWindingS2->p[ndxPt] );
  613. }
  614. pSamples->w = pWindingS2;
  615. }
  616. else
  617. {
  618. // winding isn't needed, free it.
  619. pSamples->w = NULL;
  620. FreeWinding( pWindingS2 );
  621. }
  622. pSamples++;
  623. }
  624. //
  625. // if winding T2 still exists free it and set it equal S1 (the rest of the row minus the sample just created)
  626. //
  627. if( pWindingT2 )
  628. {
  629. FreeWinding( pWindingT2 );
  630. }
  631. // clip the rest of "s"
  632. pWindingT2 = pWindingS1;
  633. }
  634. //
  635. // if the original lightmap winding exists free it and set it equal to T1 (the rest of the winding not cut into samples)
  636. //
  637. if( pLightmapWinding )
  638. {
  639. FreeWinding( pLightmapWinding );
  640. }
  641. if( pWindingT2 )
  642. {
  643. FreeWinding( pWindingT2 );
  644. }
  645. pLightmapWinding = pWindingT1;
  646. }
  647. //
  648. // copy over samples
  649. //
  650. pFaceLight->numsamples = pSamples - samples;
  651. pFaceLight->sample = ( sample_t* )calloc( pFaceLight->numsamples, sizeof( *pFaceLight->sample ) );
  652. if( !pFaceLight->sample )
  653. return false;
  654. memcpy( pFaceLight->sample, samples, pFaceLight->numsamples * sizeof( *pFaceLight->sample ) );
  655. // supply a default sample normal (face normal - assumed flat)
  656. for( int ndxSample = 0; ndxSample < pFaceLight->numsamples; ndxSample++ )
  657. {
  658. Assert ( VectorLength ( pLightInfo->facenormal ) > 1.0e-20);
  659. pFaceLight->sample[ndxSample].normal = pLightInfo->facenormal;
  660. }
  661. // statistics - warning?!
  662. if( pFaceLight->numsamples == 0 )
  663. {
  664. Msg( "no samples %d\n", pLightInfo->face - g_pFaces );
  665. }
  666. return true;
  667. }
  668. //-----------------------------------------------------------------------------
  669. // Purpose: Free any windings used by this facelight. It's currently assumed they're not needed again
  670. //-----------------------------------------------------------------------------
  671. void FreeSampleWindings( facelight_t *fl )
  672. {
  673. int i;
  674. for (i = 0; i < fl->numsamples; i++)
  675. {
  676. if (fl->sample[i].w)
  677. {
  678. FreeWinding( fl->sample[i].w );
  679. fl->sample[i].w = NULL;
  680. }
  681. }
  682. }
  683. //-----------------------------------------------------------------------------
  684. // Purpose: build the sample data for each lightmapped primitive type
  685. //-----------------------------------------------------------------------------
  686. bool BuildSamples( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
  687. {
  688. // build samples for a "face"
  689. if( pLightInfo->face->dispinfo == -1 )
  690. {
  691. return BuildFacesamples( pLightInfo, pFaceLight );
  692. }
  693. // build samples for a "displacement"
  694. else
  695. {
  696. return StaticDispMgr()->BuildDispSamples( pLightInfo, pFaceLight, ndxFace );
  697. }
  698. }
  699. //-----------------------------------------------------------------------------
  700. //-----------------------------------------------------------------------------
  701. bool BuildFaceLuxels( lightinfo_t *pLightInfo, facelight_t *pFaceLight )
  702. {
  703. // lightmap size
  704. int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
  705. int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
  706. // calcuate actual luxel points
  707. pFaceLight->numluxels = width * height;
  708. pFaceLight->luxel = ( Vector* )calloc( pFaceLight->numluxels, sizeof( *pFaceLight->luxel ) );
  709. if( !pFaceLight->luxel )
  710. return false;
  711. for( int t = 0; t < height; t++ )
  712. {
  713. for( int s = 0; s < width; s++ )
  714. {
  715. LuxelSpaceToWorld( pLightInfo, s, t, pFaceLight->luxel[s+t*width] );
  716. }
  717. }
  718. return true;
  719. }
  720. //-----------------------------------------------------------------------------
  721. // Purpose: build the luxels (find the luxel centers) for each lightmapped
  722. // primitive type
  723. //-----------------------------------------------------------------------------
  724. bool BuildLuxels( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
  725. {
  726. // build luxels for a "face"
  727. if( pLightInfo->face->dispinfo == -1 )
  728. {
  729. return BuildFaceLuxels( pLightInfo, pFaceLight );
  730. }
  731. // build luxels for a "displacement"
  732. else
  733. {
  734. return StaticDispMgr()->BuildDispLuxels( pLightInfo, pFaceLight, ndxFace );
  735. }
  736. }
  737. //-----------------------------------------------------------------------------
  738. // Purpose: for each face, find the center of each luxel; for each texture
  739. // aligned grid point, back project onto the plane and get the world
  740. // xyz value of the sample point
  741. // NOTE: ndxFace = facenum
  742. //-----------------------------------------------------------------------------
  743. void CalcPoints( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
  744. {
  745. // debugging!
  746. if( g_bDumpPatches )
  747. {
  748. DumpFaces( pLightInfo, ndxFace );
  749. }
  750. // quick and dirty!
  751. if( do_fast )
  752. {
  753. if( !BuildSamplesAndLuxels_DoFast( pLightInfo, pFaceLight, ndxFace ) )
  754. {
  755. Msg( "Face %d: (Fast)Error Building Samples and Luxels\n", ndxFace );
  756. }
  757. return;
  758. }
  759. // build the samples
  760. if( !BuildSamples( pLightInfo, pFaceLight, ndxFace ) )
  761. {
  762. Msg( "Face %d: Error Building Samples\n", ndxFace );
  763. }
  764. // build the luxels
  765. if( !BuildLuxels( pLightInfo, pFaceLight, ndxFace ) )
  766. {
  767. Msg( "Face %d: Error Building Luxels\n", ndxFace );
  768. }
  769. }
  770. //==============================================================
  771. directlight_t *activelights;
  772. directlight_t *freelights;
  773. facelight_t facelight[MAX_MAP_FACES];
  774. int numdlights;
  775. /*
  776. ==================
  777. FindTargetEntity
  778. ==================
  779. */
  780. entity_t *FindTargetEntity (char *target)
  781. {
  782. int i;
  783. char *n;
  784. for (i=0 ; i<num_entities ; i++)
  785. {
  786. n = ValueForKey (&entities[i], "targetname");
  787. if (!strcmp (n, target))
  788. return &entities[i];
  789. }
  790. return NULL;
  791. }
  792. /*
  793. =============
  794. AllocDLight
  795. =============
  796. */
  797. int GetVisCache( int lastoffset, int cluster, byte *pvs );
  798. void SetDLightVis( directlight_t *dl, int cluster );
  799. void MergeDLightVis( directlight_t *dl, int cluster );
  800. directlight_t *AllocDLight( Vector& origin, bool bAddToList )
  801. {
  802. directlight_t *dl;
  803. dl = ( directlight_t* )calloc(1, sizeof(directlight_t));
  804. dl->index = numdlights++;
  805. VectorCopy( origin, dl->light.origin );
  806. dl->light.cluster = ClusterFromPoint(dl->light.origin);
  807. SetDLightVis( dl, dl->light.cluster );
  808. dl->facenum = -1;
  809. if ( bAddToList )
  810. {
  811. dl->next = activelights;
  812. activelights = dl;
  813. }
  814. return dl;
  815. }
  816. void AddDLightToActiveList( directlight_t *dl )
  817. {
  818. dl->next = activelights;
  819. activelights = dl;
  820. }
  821. void FreeDLights()
  822. {
  823. gSkyLight = NULL;
  824. gAmbient = NULL;
  825. directlight_t *pNext;
  826. for( directlight_t *pCur=activelights; pCur; pCur=pNext )
  827. {
  828. pNext = pCur->next;
  829. free( pCur );
  830. }
  831. activelights = 0;
  832. }
  833. void SetDLightVis( directlight_t *dl, int cluster )
  834. {
  835. if (dl->pvs == NULL)
  836. {
  837. dl->pvs = (byte *)calloc( 1, (dvis->numclusters / 8) + 1 );
  838. }
  839. GetVisCache( -1, cluster, dl->pvs );
  840. }
  841. void MergeDLightVis( directlight_t *dl, int cluster )
  842. {
  843. if (dl->pvs == NULL)
  844. {
  845. SetDLightVis( dl, cluster );
  846. }
  847. else
  848. {
  849. byte pvs[MAX_MAP_CLUSTERS/8];
  850. GetVisCache( -1, cluster, pvs );
  851. // merge both vis graphs
  852. for (int i = 0; i < (dvis->numclusters / 8) + 1; i++)
  853. {
  854. dl->pvs[i] |= pvs[i];
  855. }
  856. }
  857. }
  858. /*
  859. =============
  860. LightForKey
  861. =============
  862. */
  863. int LightForKey (entity_t *ent, char *key, Vector& intensity )
  864. {
  865. char *pLight;
  866. pLight = ValueForKey( ent, key );
  867. return LightForString( pLight, intensity );
  868. }
  869. int LightForString( char *pLight, Vector& intensity )
  870. {
  871. double r, g, b, scaler;
  872. int argCnt;
  873. VectorFill( intensity, 0 );
  874. // scanf into doubles, then assign, so it is vec_t size independent
  875. r = g = b = scaler = 0;
  876. double r_hdr,g_hdr,b_hdr,scaler_hdr;
  877. argCnt = sscanf ( pLight, "%lf %lf %lf %lf %lf %lf %lf %lf",
  878. &r, &g, &b, &scaler, &r_hdr,&g_hdr,&b_hdr,&scaler_hdr );
  879. if (argCnt==8) // 2 4-tuples
  880. {
  881. if (g_bHDR)
  882. {
  883. r=r_hdr;
  884. g=g_hdr;
  885. b=b_hdr;
  886. scaler=scaler_hdr;
  887. }
  888. argCnt=4;
  889. }
  890. // make sure light is legal
  891. if( r < 0.0f || g < 0.0f || b < 0.0f || scaler < 0.0f )
  892. {
  893. intensity.Init( 0.0f, 0.0f, 0.0f );
  894. return false;
  895. }
  896. intensity[0] = pow( r / 255.0, 2.2 ) * 255; // convert to linear
  897. switch( argCnt)
  898. {
  899. case 1:
  900. // The R,G,B values are all equal.
  901. intensity[1] = intensity[2] = intensity[0];
  902. break;
  903. case 3:
  904. case 4:
  905. // Save the other two G,B values.
  906. intensity[1] = pow( g / 255.0, 2.2 ) * 255;
  907. intensity[2] = pow( b / 255.0, 2.2 ) * 255;
  908. // Did we also get an "intensity" scaler value too?
  909. if ( argCnt == 4 )
  910. {
  911. // Scale the normalized 0-255 R,G,B values by the intensity scaler
  912. VectorScale( intensity, scaler / 255.0, intensity );
  913. }
  914. break;
  915. default:
  916. printf("unknown light specifier type - %s\n",pLight);
  917. return false;
  918. }
  919. // scale up source lights by scaling factor
  920. VectorScale( intensity, lightscale, intensity );
  921. return true;
  922. }
  923. //-----------------------------------------------------------------------------
  924. // Various parsing methods
  925. //-----------------------------------------------------------------------------
  926. static void ParseLightGeneric( entity_t *e, directlight_t *dl )
  927. {
  928. entity_t *e2;
  929. char *target;
  930. Vector dest;
  931. dl->light.style = (int)FloatForKey (e, "style");
  932. // get intenfsity
  933. if( g_bHDR && LightForKey( e, "_lightHDR", dl->light.intensity ) )
  934. {
  935. }
  936. else
  937. {
  938. LightForKey( e, "_light", dl->light.intensity );
  939. }
  940. // check angle, targets
  941. target = ValueForKey (e, "target");
  942. if (target[0])
  943. { // point towards target
  944. e2 = FindTargetEntity (target);
  945. if (!e2)
  946. Warning("WARNING: light at (%i %i %i) has missing target\n",
  947. (int)dl->light.origin[0], (int)dl->light.origin[1], (int)dl->light.origin[2]);
  948. else
  949. {
  950. GetVectorForKey (e2, "origin", dest);
  951. VectorSubtract (dest, dl->light.origin, dl->light.normal);
  952. VectorNormalize (dl->light.normal);
  953. }
  954. }
  955. else
  956. {
  957. // point down angle
  958. Vector angles;
  959. GetVectorForKey( e, "angles", angles );
  960. float pitch = FloatForKey (e, "pitch");
  961. float angle = FloatForKey (e, "angle");
  962. SetupLightNormalFromProps( QAngle( angles.x, angles.y, angles.z ), angle, pitch, dl->light.normal );
  963. }
  964. if ( g_bHDR )
  965. VectorScale( dl->light.intensity,
  966. FloatForKeyWithDefault( e, "_lightscaleHDR", 1.0 ),
  967. dl->light.intensity );
  968. }
  969. static void SetLightFalloffParams( entity_t * e, directlight_t * dl )
  970. {
  971. float d50=FloatForKey( e, "_fifty_percent_distance" );
  972. dl->m_flStartFadeDistance = 0;
  973. dl->m_flEndFadeDistance = - 1;
  974. dl->m_flCapDist = 1.0e22;
  975. if ( d50 )
  976. {
  977. float d0 = FloatForKey( e, "_zero_percent_distance" );
  978. if ( d0 < d50 )
  979. {
  980. Warning( "light has _fifty_percent_distance of %f but _zero_percent_distance of %f\n", d50, d0);
  981. d0 = 2.0 * d50;
  982. }
  983. float a = 0, b = 1, c = 0;
  984. if ( ! SolveInverseQuadraticMonotonic( 0, 1.0, d50, 2.0, d0, 256.0, a, b, c ))
  985. {
  986. Warning( "can't solve quadratic for light %f %f\n", d50, d0 );
  987. }
  988. // it it possible that the parameters couldn't be used because of enforing monoticity. If so, rescale so at
  989. // least the 50 percent value is right
  990. // printf("50 percent=%f 0 percent=%f\n",d50,d0);
  991. // printf("a=%f b=%f c=%f\n",a,b,c);
  992. float v50 = c + d50 * ( b + d50 * a );
  993. float scale = 2.0 / v50;
  994. a *= scale;
  995. b *= scale;
  996. c *= scale;
  997. // printf("scaled=%f a=%f b=%f c=%f\n",scale,a,b,c);
  998. // for(float d=0;d<1000;d+=20)
  999. // printf("at %f, %f\n",d,1.0/(c+d*(b+d*a)));
  1000. dl->light.quadratic_attn = a;
  1001. dl->light.linear_attn = b;
  1002. dl->light.constant_attn = c;
  1003. if ( IntForKey(e, "_hardfalloff" ) )
  1004. {
  1005. dl->m_flEndFadeDistance = d0;
  1006. dl->m_flStartFadeDistance = 0.75 * d0 + 0.25 * d50; // start fading 3/4 way between 50 and 0. could allow adjust
  1007. }
  1008. else
  1009. {
  1010. // now, we will find the point at which the 1/x term reaches its maximum value, and
  1011. // prevent the light from going past there. If a user specifes an extreme falloff, the
  1012. // quadratic will start making the light brighter at some distance. We handle this by
  1013. // fading it from the minimum brightess point down to zero at 10x the minimum distance
  1014. if ( fabs( a ) > 0. )
  1015. {
  1016. float flMax = b / ( - 2.0 * a ); // where f' = 0
  1017. if ( flMax > 0.0 )
  1018. {
  1019. dl->m_flCapDist = flMax;
  1020. dl->m_flStartFadeDistance = flMax;
  1021. dl->m_flEndFadeDistance = 10.0 * flMax;
  1022. }
  1023. }
  1024. }
  1025. }
  1026. else
  1027. {
  1028. dl->light.constant_attn = FloatForKey (e, "_constant_attn" );
  1029. dl->light.linear_attn = FloatForKey (e, "_linear_attn" );
  1030. dl->light.quadratic_attn = FloatForKey (e, "_quadratic_attn" );
  1031. dl->light.radius = FloatForKey (e, "_distance");
  1032. // clamp values to >= 0
  1033. if ( dl->light.constant_attn < EQUAL_EPSILON )
  1034. dl->light.constant_attn = 0;
  1035. if ( dl->light.linear_attn < EQUAL_EPSILON )
  1036. dl->light.linear_attn = 0;
  1037. if ( dl->light.quadratic_attn < EQUAL_EPSILON )
  1038. dl->light.quadratic_attn = 0;
  1039. if ( dl->light.constant_attn < EQUAL_EPSILON && dl->light.linear_attn < EQUAL_EPSILON && dl->light.quadratic_attn < EQUAL_EPSILON )
  1040. dl->light.constant_attn = 1;
  1041. // scale intensity for unit 100 distance
  1042. float ratio = ( dl->light.constant_attn + 100 * dl->light.linear_attn + 100 * 100 * dl->light.quadratic_attn );
  1043. if ( ratio > 0 )
  1044. {
  1045. VectorScale( dl->light.intensity, ratio, dl->light.intensity );
  1046. }
  1047. }
  1048. }
  1049. static void ParseLightSpot( entity_t* e, directlight_t* dl )
  1050. {
  1051. Vector dest;
  1052. GetVectorForKey (e, "origin", dest );
  1053. dl = AllocDLight( dest, true );
  1054. ParseLightGeneric( e, dl );
  1055. dl->light.type = emit_spotlight;
  1056. dl->light.stopdot = FloatForKey (e, "_inner_cone");
  1057. if (!dl->light.stopdot)
  1058. dl->light.stopdot = 10;
  1059. dl->light.stopdot2 = FloatForKey (e, "_cone");
  1060. if (!dl->light.stopdot2)
  1061. dl->light.stopdot2 = dl->light.stopdot;
  1062. if (dl->light.stopdot2 < dl->light.stopdot)
  1063. dl->light.stopdot2 = dl->light.stopdot;
  1064. // This is a point light if stop dots are 180...
  1065. if ((dl->light.stopdot == 180) && (dl->light.stopdot2 == 180))
  1066. {
  1067. dl->light.stopdot = dl->light.stopdot2 = 0;
  1068. dl->light.type = emit_point;
  1069. dl->light.exponent = 0;
  1070. }
  1071. else
  1072. {
  1073. // Clamp to 90, that's all DX8 can handle!
  1074. if (dl->light.stopdot > 90)
  1075. {
  1076. Warning("WARNING: light_spot at (%i %i %i) has inner angle larger than 90 degrees! Clamping to 90...\n",
  1077. (int)dl->light.origin[0], (int)dl->light.origin[1], (int)dl->light.origin[2]);
  1078. dl->light.stopdot = 90;
  1079. }
  1080. if (dl->light.stopdot2 > 90)
  1081. {
  1082. Warning("WARNING: light_spot at (%i %i %i) has outer angle larger than 90 degrees! Clamping to 90...\n",
  1083. (int)dl->light.origin[0], (int)dl->light.origin[1], (int)dl->light.origin[2]);
  1084. dl->light.stopdot2 = 90;
  1085. }
  1086. dl->light.stopdot2 = (float)cos(dl->light.stopdot2/180*M_PI);
  1087. dl->light.stopdot = (float)cos(dl->light.stopdot/180*M_PI);
  1088. dl->light.exponent = FloatForKey (e, "_exponent");
  1089. }
  1090. SetLightFalloffParams(e,dl);
  1091. }
  1092. // NOTE: This is just a heuristic. It traces a finite number of rays to find sky
  1093. // NOTE: Full vis is necessary to make this 100% correct.
  1094. bool CanLeafTraceToSky( int iLeaf )
  1095. {
  1096. // UNDONE: Really want a point inside the leaf here. Center is a guess, may not be in the leaf
  1097. // UNDONE: Clip this to each plane bounding the leaf to guarantee
  1098. Vector center = vec3_origin;
  1099. for ( int i = 0; i < 3; i++ )
  1100. {
  1101. center[i] = ( (float)(dleafs[iLeaf].mins[i] + dleafs[iLeaf].maxs[i]) ) * 0.5f;
  1102. }
  1103. FourVectors center4, delta;
  1104. fltx4 fractionVisible;
  1105. for ( int j = 0; j < NUMVERTEXNORMALS; j+=4 )
  1106. {
  1107. // search back to see if we can hit a sky brush
  1108. delta.LoadAndSwizzle( g_anorms[j], g_anorms[min( j+1, NUMVERTEXNORMALS-1 )],
  1109. g_anorms[min( j+2, NUMVERTEXNORMALS-1 )], g_anorms[min( j+3, NUMVERTEXNORMALS-1 )] );
  1110. delta *= -MAX_TRACE_LENGTH;
  1111. delta += center4;
  1112. // return true if any hits sky
  1113. TestLine_DoesHitSky ( center4, delta, &fractionVisible );
  1114. if ( TestSignSIMD ( CmpGtSIMD ( fractionVisible, Four_Zeros ) ) )
  1115. return true;
  1116. }
  1117. return false;
  1118. }
  1119. void BuildVisForLightEnvironment( void )
  1120. {
  1121. // Create the vis.
  1122. for ( int iLeaf = 0; iLeaf < numleafs; ++iLeaf )
  1123. {
  1124. dleafs[iLeaf].flags &= ~( LEAF_FLAGS_SKY | LEAF_FLAGS_SKY2D );
  1125. unsigned int iFirstFace = dleafs[iLeaf].firstleafface;
  1126. for ( int iLeafFace = 0; iLeafFace < dleafs[iLeaf].numleaffaces; ++iLeafFace )
  1127. {
  1128. unsigned int iFace = dleaffaces[iFirstFace+iLeafFace];
  1129. texinfo_t &tex = texinfo[g_pFaces[iFace].texinfo];
  1130. if ( tex.flags & SURF_SKY )
  1131. {
  1132. if ( tex.flags & SURF_SKY2D )
  1133. {
  1134. dleafs[iLeaf].flags |= LEAF_FLAGS_SKY2D;
  1135. }
  1136. else
  1137. {
  1138. dleafs[iLeaf].flags |= LEAF_FLAGS_SKY;
  1139. }
  1140. MergeDLightVis( gSkyLight, dleafs[iLeaf].cluster );
  1141. MergeDLightVis( gAmbient, dleafs[iLeaf].cluster );
  1142. break;
  1143. }
  1144. }
  1145. }
  1146. // Second pass to set flags on leaves that don't contain sky, but touch leaves that
  1147. // contain sky.
  1148. byte pvs[MAX_MAP_CLUSTERS / 8];
  1149. int nLeafBytes = (numleafs >> 3) + 1;
  1150. unsigned char *pLeafBits = (unsigned char *)stackalloc( nLeafBytes * sizeof(unsigned char) );
  1151. unsigned char *pLeaf2DBits = (unsigned char *)stackalloc( nLeafBytes * sizeof(unsigned char) );
  1152. memset( pLeafBits, 0, nLeafBytes );
  1153. memset( pLeaf2DBits, 0, nLeafBytes );
  1154. for ( int iLeaf = 0; iLeaf < numleafs; ++iLeaf )
  1155. {
  1156. // If this leaf has light (3d skybox) in it, then don't bother
  1157. if ( dleafs[iLeaf].flags & LEAF_FLAGS_SKY )
  1158. continue;
  1159. // Don't bother with this leaf if it's solid
  1160. if ( dleafs[iLeaf].contents & CONTENTS_SOLID )
  1161. continue;
  1162. // See what other leaves are visible from this leaf
  1163. GetVisCache( -1, dleafs[iLeaf].cluster, pvs );
  1164. // Now check out all other leaves
  1165. int nByte = iLeaf >> 3;
  1166. int nBit = 1 << ( iLeaf & 0x7 );
  1167. for ( int iLeaf2 = 0; iLeaf2 < numleafs; ++iLeaf2 )
  1168. {
  1169. if ( iLeaf2 == iLeaf )
  1170. continue;
  1171. if ( !(dleafs[iLeaf2].flags & ( LEAF_FLAGS_SKY | LEAF_FLAGS_SKY2D ) ) )
  1172. continue;
  1173. // Can this leaf see into the leaf with the sky in it?
  1174. if ( !PVSCheck( pvs, dleafs[iLeaf2].cluster ) )
  1175. continue;
  1176. if ( dleafs[iLeaf2].flags & LEAF_FLAGS_SKY2D )
  1177. {
  1178. pLeaf2DBits[ nByte ] |= nBit;
  1179. }
  1180. if ( dleafs[iLeaf2].flags & LEAF_FLAGS_SKY )
  1181. {
  1182. pLeafBits[ nByte ] |= nBit;
  1183. // As soon as we know this leaf needs to draw the 3d skybox, we're done
  1184. break;
  1185. }
  1186. }
  1187. }
  1188. // Must set the bits in a separate pass so as to not flood-fill LEAF_FLAGS_SKY everywhere
  1189. // pLeafbits is a bit array of all leaves that need to be marked as seeing sky
  1190. for ( int iLeaf = 0; iLeaf < numleafs; ++iLeaf )
  1191. {
  1192. // If this leaf has light (3d skybox) in it, then don't bother
  1193. if ( dleafs[iLeaf].flags & LEAF_FLAGS_SKY )
  1194. continue;
  1195. // Don't bother with this leaf if it's solid
  1196. if ( dleafs[iLeaf].contents & CONTENTS_SOLID )
  1197. continue;
  1198. // Check to see if this is a 2D skybox leaf
  1199. if ( pLeaf2DBits[ iLeaf >> 3 ] & (1 << ( iLeaf & 0x7 )) )
  1200. {
  1201. dleafs[iLeaf].flags |= LEAF_FLAGS_SKY2D;
  1202. }
  1203. // If this is a 3D skybox leaf, then we don't care if it was previously a 2D skybox leaf
  1204. if ( pLeafBits[ iLeaf >> 3 ] & (1 << ( iLeaf & 0x7 )) )
  1205. {
  1206. dleafs[iLeaf].flags |= LEAF_FLAGS_SKY;
  1207. dleafs[iLeaf].flags &= ~LEAF_FLAGS_SKY2D;
  1208. }
  1209. else
  1210. {
  1211. // if radial vis was used on this leaf some of the portals leading
  1212. // to sky may have been culled. Try tracing to find sky.
  1213. if ( dleafs[iLeaf].flags & LEAF_FLAGS_RADIAL )
  1214. {
  1215. if ( CanLeafTraceToSky(iLeaf) )
  1216. {
  1217. // FIXME: Should make a version that checks if we hit 2D skyboxes.. oh well.
  1218. dleafs[iLeaf].flags |= LEAF_FLAGS_SKY;
  1219. }
  1220. }
  1221. }
  1222. }
  1223. }
  1224. static char *ValueForKeyWithDefault (entity_t *ent, char *key, char *default_value = NULL)
  1225. {
  1226. epair_t *ep;
  1227. for (ep=ent->epairs ; ep ; ep=ep->next)
  1228. if (!strcmp (ep->key, key) )
  1229. return ep->value;
  1230. return default_value;
  1231. }
  1232. static void ParseLightEnvironment( entity_t* e, directlight_t* dl )
  1233. {
  1234. Vector dest;
  1235. GetVectorForKey (e, "origin", dest );
  1236. dl = AllocDLight( dest, false );
  1237. ParseLightGeneric( e, dl );
  1238. char *angle_str=ValueForKeyWithDefault( e, "SunSpreadAngle" );
  1239. if (angle_str)
  1240. {
  1241. g_SunAngularExtent=atof(angle_str);
  1242. g_SunAngularExtent=sin((M_PI/180.0)*g_SunAngularExtent);
  1243. printf("sun extent from map=%f\n",g_SunAngularExtent);
  1244. }
  1245. if ( !gSkyLight )
  1246. {
  1247. // Sky light.
  1248. gSkyLight = dl;
  1249. dl->light.type = emit_skylight;
  1250. // Sky ambient light.
  1251. gAmbient = AllocDLight( dl->light.origin, false );
  1252. gAmbient->light.type = emit_skyambient;
  1253. if( g_bHDR && LightForKey( e, "_ambientHDR", gAmbient->light.intensity ) )
  1254. {
  1255. // we have a valid HDR ambient light value
  1256. }
  1257. else if ( !LightForKey( e, "_ambient", gAmbient->light.intensity ) )
  1258. {
  1259. VectorScale( dl->light.intensity, 0.5, gAmbient->light.intensity );
  1260. }
  1261. if ( g_bHDR )
  1262. {
  1263. VectorScale( gAmbient->light.intensity,
  1264. FloatForKeyWithDefault( e, "_AmbientScaleHDR", 1.0 ),
  1265. gAmbient->light.intensity );
  1266. }
  1267. BuildVisForLightEnvironment();
  1268. // Add sky and sky ambient lights to the list.
  1269. AddDLightToActiveList( gSkyLight );
  1270. AddDLightToActiveList( gAmbient );
  1271. }
  1272. }
  1273. static void ParseLightPoint( entity_t* e, directlight_t* dl )
  1274. {
  1275. Vector dest;
  1276. GetVectorForKey (e, "origin", dest );
  1277. dl = AllocDLight( dest, true );
  1278. ParseLightGeneric( e, dl );
  1279. dl->light.type = emit_point;
  1280. SetLightFalloffParams(e,dl);
  1281. }
  1282. /*
  1283. =============
  1284. CreateDirectLights
  1285. =============
  1286. */
  1287. #define DIRECT_SCALE (100.0*100.0)
  1288. void CreateDirectLights (void)
  1289. {
  1290. unsigned i;
  1291. CPatch *p = NULL;
  1292. directlight_t *dl = NULL;
  1293. entity_t *e = NULL;
  1294. char *name;
  1295. Vector dest;
  1296. numdlights = 0;
  1297. FreeDLights();
  1298. //
  1299. // surfaces
  1300. //
  1301. unsigned int uiPatchCount = g_Patches.Count();
  1302. for (i=0; i< uiPatchCount; i++)
  1303. {
  1304. p = &g_Patches.Element( i );
  1305. // skip parent patches
  1306. if (p->child1 != g_Patches.InvalidIndex() )
  1307. continue;
  1308. if (p->basearea < 1e-6)
  1309. continue;
  1310. if( VectorAvg( p->baselight ) >= dlight_threshold )
  1311. {
  1312. dl = AllocDLight( p->origin, true );
  1313. dl->light.type = emit_surface;
  1314. VectorCopy (p->normal, dl->light.normal);
  1315. Assert( VectorLength( p->normal ) > 1.0e-20 );
  1316. // scale intensity by number of texture instances
  1317. VectorScale( p->baselight, lightscale * p->area * p->scale[0] * p->scale[1] / p->basearea, dl->light.intensity );
  1318. // scale to a range that results in actual light
  1319. VectorScale( dl->light.intensity, DIRECT_SCALE, dl->light.intensity );
  1320. }
  1321. }
  1322. //
  1323. // entities
  1324. //
  1325. for (i=0 ; i<(unsigned)num_entities ; i++)
  1326. {
  1327. e = &entities[i];
  1328. name = ValueForKey (e, "classname");
  1329. if (strncmp (name, "light", 5))
  1330. continue;
  1331. // Light_dynamic is actually a real entity; not to be included here...
  1332. if (!strcmp (name, "light_dynamic"))
  1333. continue;
  1334. if (!strcmp (name, "light_spot"))
  1335. {
  1336. ParseLightSpot( e, dl );
  1337. }
  1338. else if (!strcmp(name, "light_environment"))
  1339. {
  1340. ParseLightEnvironment( e, dl );
  1341. }
  1342. else if (!strcmp(name, "light"))
  1343. {
  1344. ParseLightPoint( e, dl );
  1345. }
  1346. else
  1347. {
  1348. qprintf( "unsupported light entity: \"%s\"\n", name );
  1349. }
  1350. }
  1351. qprintf ("%i direct lights\n", numdlights);
  1352. // exit(1);
  1353. }
  1354. /*
  1355. =============
  1356. ExportDirectLightsToWorldLights
  1357. =============
  1358. */
  1359. void ExportDirectLightsToWorldLights()
  1360. {
  1361. directlight_t *dl;
  1362. // In case the level has already been VRADed.
  1363. *pNumworldlights = 0;
  1364. for (dl = activelights; dl != NULL; dl = dl->next )
  1365. {
  1366. dworldlight_t *wl = &dworldlights[(*pNumworldlights)++];
  1367. if (*pNumworldlights > MAX_MAP_WORLDLIGHTS)
  1368. {
  1369. Error("too many lights %d / %d\n", *pNumworldlights, MAX_MAP_WORLDLIGHTS );
  1370. }
  1371. wl->cluster = dl->light.cluster;
  1372. wl->type = dl->light.type;
  1373. wl->style = dl->light.style;
  1374. VectorCopy( dl->light.origin, wl->origin );
  1375. // FIXME: why does vrad want 0 to 255 and not 0 to 1??
  1376. VectorScale( dl->light.intensity, (1.0 / 255.0), wl->intensity );
  1377. VectorCopy( dl->light.normal, wl->normal );
  1378. wl->stopdot = dl->light.stopdot;
  1379. wl->stopdot2 = dl->light.stopdot2;
  1380. wl->exponent = dl->light.exponent;
  1381. wl->radius = dl->light.radius;
  1382. wl->constant_attn = dl->light.constant_attn;
  1383. wl->linear_attn = dl->light.linear_attn;
  1384. wl->quadratic_attn = dl->light.quadratic_attn;
  1385. wl->flags = 0;
  1386. }
  1387. }
  1388. /*
  1389. =============
  1390. GatherSampleLight
  1391. =============
  1392. */
  1393. #define NORMALFORMFACTOR 40.156979 // accumuated dot products for hemisphere
  1394. #define CONSTANT_DOT (.7/2)
  1395. #define NSAMPLES_SUN_AREA_LIGHT 30 // number of samples to take for an
  1396. // non-point sun light
  1397. // Helper function - gathers light from sun (emit_skylight)
  1398. void GatherSampleSkyLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
  1399. FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
  1400. int nLFlags, int static_prop_index_to_ignore,
  1401. float flEpsilon )
  1402. {
  1403. bool bIgnoreNormals = ( nLFlags & GATHERLFLAGS_IGNORE_NORMALS ) != 0;
  1404. bool force_fast = ( nLFlags & GATHERLFLAGS_FORCE_FAST ) != 0;
  1405. fltx4 dot;
  1406. if ( bIgnoreNormals )
  1407. dot = ReplicateX4( CONSTANT_DOT );
  1408. else
  1409. dot = NegSIMD( pNormals[0] * dl->light.normal );
  1410. dot = MaxSIMD( dot, Four_Zeros );
  1411. int zeroMask = TestSignSIMD ( CmpEqSIMD( dot, Four_Zeros ) );
  1412. if (zeroMask == 0xF)
  1413. return;
  1414. int nsamples = 1;
  1415. if ( g_SunAngularExtent > 0.0f )
  1416. {
  1417. nsamples = NSAMPLES_SUN_AREA_LIGHT;
  1418. if ( do_fast || force_fast )
  1419. nsamples /= 4;
  1420. }
  1421. fltx4 totalFractionVisible = Four_Zeros;
  1422. fltx4 fractionVisible = Four_Zeros;
  1423. DirectionalSampler_t sampler;
  1424. for ( int d = 0; d < nsamples; d++ )
  1425. {
  1426. // determine visibility of skylight
  1427. // serach back to see if we can hit a sky brush
  1428. Vector delta;
  1429. VectorScale( dl->light.normal, -MAX_TRACE_LENGTH, delta );
  1430. if ( d )
  1431. {
  1432. // jitter light source location
  1433. Vector ofs = sampler.NextValue();
  1434. ofs *= MAX_TRACE_LENGTH * g_SunAngularExtent;
  1435. delta += ofs;
  1436. }
  1437. FourVectors delta4;
  1438. delta4.DuplicateVector ( delta );
  1439. delta4 += pos;
  1440. TestLine_DoesHitSky ( pos, delta4, &fractionVisible, true, static_prop_index_to_ignore );
  1441. totalFractionVisible = AddSIMD ( totalFractionVisible, fractionVisible );
  1442. }
  1443. fltx4 seeAmount = MulSIMD ( totalFractionVisible, ReplicateX4 ( 1.0f / nsamples ) );
  1444. out.m_flDot[0] = MulSIMD ( dot, seeAmount );
  1445. out.m_flFalloff = Four_Ones;
  1446. out.m_flSunAmount = MulSIMD ( seeAmount, ReplicateX4( 10000.0f ) );
  1447. for ( int i = 1; i < normalCount; i++ )
  1448. {
  1449. if ( bIgnoreNormals )
  1450. out.m_flDot[i] = ReplicateX4 ( CONSTANT_DOT );
  1451. else
  1452. {
  1453. out.m_flDot[i] = NegSIMD( pNormals[i] * dl->light.normal );
  1454. out.m_flDot[i] = MulSIMD( out.m_flDot[i], seeAmount );
  1455. }
  1456. }
  1457. }
  1458. // Helper function - gathers light from ambient sky light
  1459. void GatherSampleAmbientSkySSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
  1460. FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
  1461. int nLFlags, int static_prop_index_to_ignore,
  1462. float flEpsilon )
  1463. {
  1464. bool bIgnoreNormals = ( nLFlags & GATHERLFLAGS_IGNORE_NORMALS ) != 0;
  1465. bool force_fast = ( nLFlags & GATHERLFLAGS_FORCE_FAST ) != 0;
  1466. fltx4 sumdot = Four_Zeros;
  1467. fltx4 ambient_intensity[NUM_BUMP_VECTS+1];
  1468. fltx4 possibleHitCount[NUM_BUMP_VECTS+1];
  1469. fltx4 dots[NUM_BUMP_VECTS+1];
  1470. for ( int i = 0; i < normalCount; i++ )
  1471. {
  1472. ambient_intensity[i] = Four_Zeros;
  1473. possibleHitCount[i] = Four_Zeros;
  1474. }
  1475. DirectionalSampler_t sampler;
  1476. int nsky_samples = NUMVERTEXNORMALS;
  1477. if (do_fast || force_fast )
  1478. nsky_samples /= 4;
  1479. else
  1480. nsky_samples *= g_flSkySampleScale;
  1481. for (int j = 0; j < nsky_samples; j++)
  1482. {
  1483. FourVectors anorm;
  1484. anorm.DuplicateVector( sampler.NextValue() );
  1485. if ( bIgnoreNormals )
  1486. dots[0] = ReplicateX4( CONSTANT_DOT );
  1487. else
  1488. dots[0] = NegSIMD( pNormals[0] * anorm );
  1489. fltx4 validity = CmpGtSIMD( dots[0], ReplicateX4( EQUAL_EPSILON ) );
  1490. // No possibility of anybody getting lit
  1491. if ( !TestSignSIMD( validity ) )
  1492. continue;
  1493. dots[0] = AndSIMD( validity, dots[0] );
  1494. sumdot = AddSIMD( dots[0], sumdot );
  1495. possibleHitCount[0] = AddSIMD( AndSIMD( validity, Four_Ones ), possibleHitCount[0] );
  1496. for ( int i = 1; i < normalCount; i++ )
  1497. {
  1498. if ( bIgnoreNormals )
  1499. dots[i] = ReplicateX4( CONSTANT_DOT );
  1500. else
  1501. dots[i] = NegSIMD( pNormals[i] * anorm );
  1502. fltx4 validity2 = CmpGtSIMD( dots[i], ReplicateX4 ( EQUAL_EPSILON ) );
  1503. dots[i] = AndSIMD( validity2, dots[i] );
  1504. possibleHitCount[i] = AddSIMD( AndSIMD( AndSIMD( validity, validity2 ), Four_Ones ), possibleHitCount[i] );
  1505. }
  1506. // search back to see if we can hit a sky brush
  1507. FourVectors delta = anorm;
  1508. delta *= -MAX_TRACE_LENGTH;
  1509. delta += pos;
  1510. FourVectors surfacePos = pos;
  1511. FourVectors offset = anorm;
  1512. offset *= -flEpsilon;
  1513. surfacePos -= offset;
  1514. fltx4 fractionVisible = Four_Ones;
  1515. TestLine_DoesHitSky( surfacePos, delta, &fractionVisible, true, static_prop_index_to_ignore );
  1516. for ( int i = 0; i < normalCount; i++ )
  1517. {
  1518. fltx4 addedAmount = MulSIMD( fractionVisible, dots[i] );
  1519. ambient_intensity[i] = AddSIMD( ambient_intensity[i], addedAmount );
  1520. }
  1521. }
  1522. out.m_flFalloff = Four_Ones;
  1523. for ( int i = 0; i < normalCount; i++ )
  1524. {
  1525. // now scale out the missing parts of the hemisphere of this bump basis vector
  1526. fltx4 factor = ReciprocalSIMD( possibleHitCount[0] );
  1527. factor = MulSIMD( factor, possibleHitCount[i] );
  1528. out.m_flDot[i] = MulSIMD( factor, sumdot );
  1529. out.m_flDot[i] = ReciprocalSIMD( out.m_flDot[i] );
  1530. out.m_flDot[i] = MulSIMD( ambient_intensity[i], out.m_flDot[i] );
  1531. }
  1532. }
  1533. // Helper function - gathers light from area lights, spot lights, and point lights
  1534. void GatherSampleStandardLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
  1535. FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
  1536. int nLFlags, int static_prop_index_to_ignore,
  1537. float flEpsilon )
  1538. {
  1539. bool bIgnoreNormals = ( nLFlags & GATHERLFLAGS_IGNORE_NORMALS ) != 0;
  1540. FourVectors src;
  1541. src.DuplicateVector( vec3_origin );
  1542. if (dl->facenum == -1)
  1543. {
  1544. src.DuplicateVector( dl->light.origin );
  1545. }
  1546. // Find light vector
  1547. FourVectors delta;
  1548. delta = src;
  1549. delta -= pos;
  1550. fltx4 dist2 = delta.length2();
  1551. fltx4 rpcDist = ReciprocalSqrtSIMD( dist2 );
  1552. delta *= rpcDist;
  1553. fltx4 dist = SqrtEstSIMD( dist2 );//delta.VectorNormalize();
  1554. // Compute dot
  1555. fltx4 dot = ReplicateX4( (float) CONSTANT_DOT );
  1556. if ( !bIgnoreNormals )
  1557. dot = delta * pNormals[0];
  1558. dot = MaxSIMD( Four_Zeros, dot );
  1559. // Affix dot to zero if past fade distz
  1560. bool bHasHardFalloff = ( dl->m_flEndFadeDistance > dl->m_flStartFadeDistance );
  1561. if ( bHasHardFalloff )
  1562. {
  1563. fltx4 notPastFadeDist = CmpLeSIMD ( dist, ReplicateX4 ( dl->m_flEndFadeDistance ) );
  1564. dot = AndSIMD( dot, notPastFadeDist ); // dot = 0 if past fade distance
  1565. if ( !TestSignSIMD ( notPastFadeDist ) )
  1566. return;
  1567. }
  1568. dist = MaxSIMD( dist, Four_Ones );
  1569. fltx4 falloffEvalDist = MinSIMD( dist, ReplicateX4( dl->m_flCapDist ) );
  1570. fltx4 constant, linear, quadratic;
  1571. fltx4 dot2, inCone, inFringe, mult;
  1572. FourVectors offset;
  1573. switch (dl->light.type)
  1574. {
  1575. case emit_point:
  1576. constant = ReplicateX4( dl->light.constant_attn );
  1577. linear = ReplicateX4( dl->light.linear_attn );
  1578. quadratic = ReplicateX4( dl->light.quadratic_attn );
  1579. out.m_flFalloff = MulSIMD( falloffEvalDist, falloffEvalDist );
  1580. out.m_flFalloff = MulSIMD( out.m_flFalloff, quadratic );
  1581. out.m_flFalloff = AddSIMD( out.m_flFalloff, MulSIMD( linear, falloffEvalDist ) );
  1582. out.m_flFalloff = AddSIMD( out.m_flFalloff, constant );
  1583. out.m_flFalloff = ReciprocalSIMD( out.m_flFalloff );
  1584. break;
  1585. case emit_surface:
  1586. dot2 = delta * dl->light.normal;
  1587. dot2 = NegSIMD( dot2 );
  1588. // Light behind surface yields zero dot
  1589. dot2 = MaxSIMD( Four_Zeros, dot2 );
  1590. if ( TestSignSIMD( CmpEqSIMD( Four_Zeros, dot ) ) == 0xF )
  1591. return;
  1592. out.m_flFalloff = ReciprocalSIMD ( dist2 );
  1593. out.m_flFalloff = MulSIMD( out.m_flFalloff, dot2 );
  1594. // move the endpoint away from the surface by epsilon to prevent hitting the surface with the trace
  1595. offset.DuplicateVector ( dl->light.normal );
  1596. offset *= DIST_EPSILON;
  1597. src += offset;
  1598. break;
  1599. case emit_spotlight:
  1600. dot2 = delta * dl->light.normal;
  1601. dot2 = NegSIMD( dot2 );
  1602. // Affix dot2 to zero if outside light cone
  1603. inCone = CmpGtSIMD( dot2, ReplicateX4( dl->light.stopdot2 ) );
  1604. if ( !TestSignSIMD ( inCone ) )
  1605. return;
  1606. dot = AndSIMD( inCone, dot );
  1607. constant = ReplicateX4( dl->light.constant_attn );
  1608. linear = ReplicateX4( dl->light.linear_attn );
  1609. quadratic = ReplicateX4( dl->light.quadratic_attn );
  1610. out.m_flFalloff = MulSIMD( falloffEvalDist, falloffEvalDist );
  1611. out.m_flFalloff = MulSIMD( out.m_flFalloff, quadratic );
  1612. out.m_flFalloff = AddSIMD( out.m_flFalloff, MulSIMD( linear, falloffEvalDist ) );
  1613. out.m_flFalloff = AddSIMD( out.m_flFalloff, constant );
  1614. out.m_flFalloff = ReciprocalSIMD( out.m_flFalloff );
  1615. out.m_flFalloff = MulSIMD( out.m_flFalloff, dot2 );
  1616. // outside the inner cone
  1617. inFringe = CmpLeSIMD( dot2, ReplicateX4( dl->light.stopdot ) );
  1618. mult = ReplicateX4( dl->light.stopdot - dl->light.stopdot2 );
  1619. mult = ReciprocalSIMD( mult );
  1620. mult = MulSIMD( mult, SubSIMD( dot2, ReplicateX4( dl->light.stopdot2 ) ) );
  1621. mult = MinSIMD( mult, Four_Ones );
  1622. mult = MaxSIMD( mult, Four_Zeros );
  1623. // pow is fixed point, so this isn't the most accurate, but it doesn't need to be
  1624. if ( (dl->light.exponent != 0.0f ) && ( dl->light.exponent != 1.0f ) )
  1625. mult = PowSIMD( mult, dl->light.exponent );
  1626. // if not in between inner and outer cones, mult by 1
  1627. mult = AndSIMD( inFringe, mult );
  1628. mult = AddSIMD( mult, AndNotSIMD( inFringe, Four_Ones ) );
  1629. out.m_flFalloff = MulSIMD( mult, out.m_flFalloff );
  1630. break;
  1631. }
  1632. // we may be in the fade region - modulate lighting by the fade curve
  1633. //float t = ( dist - dl->m_flStartFadeDistance ) /
  1634. // ( dl->m_flEndFadeDistance - dl->m_flStartFadeDistance );
  1635. if ( bHasHardFalloff )
  1636. {
  1637. fltx4 t = ReplicateX4( dl->m_flEndFadeDistance - dl->m_flStartFadeDistance );
  1638. t = ReciprocalSIMD( t );
  1639. t = MulSIMD( t, SubSIMD( dist, ReplicateX4( dl->m_flStartFadeDistance ) ) );
  1640. // clamp t to [0...1]
  1641. t = MinSIMD( t, Four_Ones );
  1642. t = MaxSIMD( t, Four_Zeros );
  1643. t = SubSIMD( Four_Ones, t );
  1644. // Using QuinticInterpolatingPolynomial, SSE-ified
  1645. // t * t * t *( t * ( t* 6.0 - 15.0 ) + 10.0 )
  1646. mult = SubSIMD( MulSIMD( ReplicateX4( 6.0f ), t ), ReplicateX4( 15.0f ) );
  1647. mult = AddSIMD( MulSIMD( mult, t ), ReplicateX4( 10.0f ) );
  1648. mult = MulSIMD( MulSIMD( t, t), mult );
  1649. mult = MulSIMD( t, mult );
  1650. out.m_flFalloff = MulSIMD( mult, out.m_flFalloff );
  1651. }
  1652. // Raytrace for visibility function
  1653. fltx4 fractionVisible = Four_Ones;
  1654. TestLine( pos, src, &fractionVisible, static_prop_index_to_ignore);
  1655. dot = MulSIMD( fractionVisible, dot );
  1656. out.m_flDot[0] = dot;
  1657. for ( int i = 1; i < normalCount; i++ )
  1658. {
  1659. if ( bIgnoreNormals )
  1660. out.m_flDot[i] = ReplicateX4( (float) CONSTANT_DOT );
  1661. else
  1662. {
  1663. out.m_flDot[i] = pNormals[i] * delta;
  1664. out.m_flDot[i] = MaxSIMD( Four_Zeros, out.m_flDot[i] );
  1665. }
  1666. }
  1667. }
  1668. // returns dot product with normal and delta
  1669. // dl - light
  1670. // pos - position of sample
  1671. // normal - surface normal of sample
  1672. // out.m_flDot[] - returned dot products with light vector and each normal
  1673. // out.m_flFalloff - amount of light falloff
  1674. void GatherSampleLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
  1675. FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
  1676. int nLFlags,
  1677. int static_prop_index_to_ignore,
  1678. float flEpsilon )
  1679. {
  1680. for ( int b = 0; b < normalCount; b++ )
  1681. out.m_flDot[b] = Four_Zeros;
  1682. out.m_flFalloff = Four_Zeros;
  1683. out.m_flSunAmount = Four_Zeros;
  1684. Assert( normalCount <= (NUM_BUMP_VECTS+1) );
  1685. // skylights work fundamentally differently than normal lights
  1686. switch( dl->light.type )
  1687. {
  1688. case emit_skylight:
  1689. GatherSampleSkyLightSSE( out, dl, facenum, pos, pNormals, normalCount,
  1690. iThread, nLFlags, static_prop_index_to_ignore, flEpsilon );
  1691. break;
  1692. case emit_skyambient:
  1693. GatherSampleAmbientSkySSE( out, dl, facenum, pos, pNormals, normalCount,
  1694. iThread, nLFlags, static_prop_index_to_ignore, flEpsilon );
  1695. break;
  1696. case emit_point:
  1697. case emit_surface:
  1698. case emit_spotlight:
  1699. GatherSampleStandardLightSSE( out, dl, facenum, pos, pNormals, normalCount,
  1700. iThread, nLFlags, static_prop_index_to_ignore, flEpsilon );
  1701. break;
  1702. default:
  1703. Error ("Bad dl->light.type");
  1704. return;
  1705. }
  1706. // NOTE: Notice here that if the light is on the back side of the face
  1707. // (tested by checking the dot product of the face normal and the light position)
  1708. // we don't want it to contribute to *any* of the bumped lightmaps. It glows
  1709. // in disturbing ways if we don't do this.
  1710. out.m_flDot[0] = MaxSIMD ( out.m_flDot[0], Four_Zeros );
  1711. fltx4 notZero = CmpGtSIMD( out.m_flDot[0], Four_Zeros );
  1712. for ( int n = 1; n < normalCount; n++ )
  1713. {
  1714. out.m_flDot[n] = MaxSIMD( out.m_flDot[n], Four_Zeros );
  1715. out.m_flDot[n] = AndSIMD( out.m_flDot[n], notZero );
  1716. }
  1717. }
  1718. /*
  1719. =============
  1720. AddSampleToPatch
  1721. Take the sample's collected light and
  1722. add it back into the apropriate patch
  1723. for the radiosity pass.
  1724. =============
  1725. */
  1726. void AddSampleToPatch (sample_t *s, LightingValue_t& light, int facenum)
  1727. {
  1728. CPatch *patch;
  1729. Vector mins, maxs;
  1730. int i;
  1731. if (numbounce == 0)
  1732. return;
  1733. if( VectorAvg( light.m_vecLighting ) < 1)
  1734. return;
  1735. //
  1736. // fixed the sample position and normal -- need to find the equiv pos, etc to set up
  1737. // patches
  1738. //
  1739. if( g_FacePatches.Element( facenum ) == g_FacePatches.InvalidIndex() )
  1740. return;
  1741. float radius = sqrt( s->area ) / 2.0;
  1742. CPatch *pNextPatch = NULL;
  1743. for( patch = &g_Patches.Element( g_FacePatches.Element( facenum ) ); patch; patch = pNextPatch )
  1744. {
  1745. // next patch
  1746. pNextPatch = NULL;
  1747. if( patch->ndxNext != g_Patches.InvalidIndex() )
  1748. {
  1749. pNextPatch = &g_Patches.Element( patch->ndxNext );
  1750. }
  1751. if (patch->sky)
  1752. continue;
  1753. // skip patches with children
  1754. if ( patch->child1 != g_Patches.InvalidIndex() )
  1755. continue;
  1756. // see if the point is in this patch (roughly)
  1757. WindingBounds (patch->winding, mins, maxs);
  1758. for (i=0 ; i<3 ; i++)
  1759. {
  1760. if (mins[i] > s->pos[i] + radius)
  1761. goto nextpatch;
  1762. if (maxs[i] < s->pos[i] - radius)
  1763. goto nextpatch;
  1764. }
  1765. // add the sample to the patch
  1766. patch->samplearea += s->area;
  1767. VectorMA( patch->samplelight, s->area, light.m_vecLighting, patch->samplelight );
  1768. nextpatch:;
  1769. }
  1770. // don't worry if some samples don't find a patch
  1771. }
  1772. void GetPhongNormal( int facenum, Vector const& spot, Vector& phongnormal )
  1773. {
  1774. int j;
  1775. dface_t *f = &g_pFaces[facenum];
  1776. // dplane_t *p = &dplanes[f->planenum];
  1777. Vector facenormal, vspot;
  1778. VectorCopy( dplanes[f->planenum].normal, facenormal );
  1779. VectorCopy( facenormal, phongnormal );
  1780. if ( smoothing_threshold != 1 )
  1781. {
  1782. faceneighbor_t *fn = &faceneighbor[facenum];
  1783. // Calculate modified point normal for surface
  1784. // Use the edge normals iff they are defined. Bend the surface towards the edge normal(s)
  1785. // Crude first attempt: find nearest edge normal and do a simple interpolation with facenormal.
  1786. // Second attempt: find edge points+center that bound the point and do a three-point triangulation(baricentric)
  1787. // Better third attempt: generate the point normals for all vertices and do baricentric triangulation.
  1788. for (j=0 ; j<f->numedges ; j++)
  1789. {
  1790. Vector v1, v2;
  1791. //int e = dsurfedges[f->firstedge + j];
  1792. //int e1 = dsurfedges[f->firstedge + ((j+f->numedges-1)%f->numedges)];
  1793. //int e2 = dsurfedges[f->firstedge + ((j+1)%f->numedges)];
  1794. //edgeshare_t *es = &edgeshare[abs(e)];
  1795. //edgeshare_t *es1 = &edgeshare[abs(e1)];
  1796. //edgeshare_t *es2 = &edgeshare[abs(e2)];
  1797. // dface_t *f2;
  1798. float a1, a2, aa, bb, ab;
  1799. int vert1, vert2;
  1800. Vector& n1 = fn->normal[j];
  1801. Vector& n2 = fn->normal[(j+1)%f->numedges];
  1802. /*
  1803. if (VectorCompare( n1, fn->facenormal )
  1804. && VectorCompare( n2, fn->facenormal) )
  1805. continue;
  1806. */
  1807. vert1 = EdgeVertex( f, j );
  1808. vert2 = EdgeVertex( f, j+1 );
  1809. Vector& p1 = dvertexes[vert1].point;
  1810. Vector& p2 = dvertexes[vert2].point;
  1811. // Build vectors from the middle of the face to the edge vertexes and the sample pos.
  1812. VectorSubtract( p1, face_centroids[facenum], v1 );
  1813. VectorSubtract( p2, face_centroids[facenum], v2 );
  1814. VectorSubtract( spot, face_centroids[facenum], vspot );
  1815. aa = DotProduct( v1, v1 );
  1816. bb = DotProduct( v2, v2 );
  1817. ab = DotProduct( v1, v2 );
  1818. a1 = (bb * DotProduct( v1, vspot ) - ab * DotProduct( vspot, v2 )) / (aa * bb - ab * ab);
  1819. a2 = (DotProduct( vspot, v2 ) - a1 * ab) / bb;
  1820. // Test center to sample vector for inclusion between center to vertex vectors (Use dot product of vectors)
  1821. if ( a1 >= 0.0 && a2 >= 0.0)
  1822. {
  1823. // calculate distance from edge to pos
  1824. Vector temp;
  1825. float scale;
  1826. // Interpolate between the center and edge normals based on sample position
  1827. scale = 1.0 - a1 - a2;
  1828. VectorScale( fn->facenormal, scale, phongnormal );
  1829. VectorScale( n1, a1, temp );
  1830. VectorAdd( phongnormal, temp, phongnormal );
  1831. VectorScale( n2, a2, temp );
  1832. VectorAdd( phongnormal, temp, phongnormal );
  1833. Assert( VectorLength( phongnormal ) > 1.0e-20 );
  1834. VectorNormalize( phongnormal );
  1835. /*
  1836. if (a1 > 1 || a2 > 1 || a1 + a2 > 1)
  1837. {
  1838. Msg("\n%.2f %.2f\n", a1, a2 );
  1839. Msg("%.2f %.2f %.2f\n", v1[0], v1[1], v1[2] );
  1840. Msg("%.2f %.2f %.2f\n", v2[0], v2[1], v2[2] );
  1841. Msg("%.2f %.2f %.2f\n", vspot[0], vspot[1], vspot[2] );
  1842. exit(1);
  1843. a1 = 0;
  1844. }
  1845. */
  1846. /*
  1847. phongnormal[0] = (((j + 1) & 4) != 0) * 255;
  1848. phongnormal[1] = (((j + 1) & 2) != 0) * 255;
  1849. phongnormal[2] = (((j + 1) & 1) != 0) * 255;
  1850. */
  1851. return;
  1852. }
  1853. }
  1854. }
  1855. }
  1856. void GetPhongNormal( int facenum, FourVectors const& spot, FourVectors& phongnormal )
  1857. {
  1858. int j;
  1859. dface_t *f = &g_pFaces[facenum];
  1860. // dplane_t *p = &dplanes[f->planenum];
  1861. Vector facenormal;
  1862. FourVectors vspot;
  1863. VectorCopy( dplanes[f->planenum].normal, facenormal );
  1864. phongnormal.DuplicateVector( facenormal );
  1865. FourVectors faceCentroid;
  1866. faceCentroid.DuplicateVector( face_centroids[facenum] );
  1867. if ( smoothing_threshold != 1 )
  1868. {
  1869. faceneighbor_t *fn = &faceneighbor[facenum];
  1870. // Calculate modified point normal for surface
  1871. // Use the edge normals iff they are defined. Bend the surface towards the edge normal(s)
  1872. // Crude first attempt: find nearest edge normal and do a simple interpolation with facenormal.
  1873. // Second attempt: find edge points+center that bound the point and do a three-point triangulation(baricentric)
  1874. // Better third attempt: generate the point normals for all vertices and do baricentric triangulation.
  1875. for ( j = 0; j < f->numedges; ++j )
  1876. {
  1877. Vector v1, v2;
  1878. fltx4 a1, a2;
  1879. float aa, bb, ab;
  1880. int vert1, vert2;
  1881. Vector& n1 = fn->normal[j];
  1882. Vector& n2 = fn->normal[(j+1)%f->numedges];
  1883. vert1 = EdgeVertex( f, j );
  1884. vert2 = EdgeVertex( f, j+1 );
  1885. Vector& p1 = dvertexes[vert1].point;
  1886. Vector& p2 = dvertexes[vert2].point;
  1887. // Build vectors from the middle of the face to the edge vertexes and the sample pos.
  1888. VectorSubtract( p1, face_centroids[facenum], v1 );
  1889. VectorSubtract( p2, face_centroids[facenum], v2 );
  1890. //VectorSubtract( spot, face_centroids[facenum], vspot );
  1891. vspot = spot;
  1892. vspot -= faceCentroid;
  1893. aa = DotProduct( v1, v1 );
  1894. bb = DotProduct( v2, v2 );
  1895. ab = DotProduct( v1, v2 );
  1896. //a1 = (bb * DotProduct( v1, vspot ) - ab * DotProduct( vspot, v2 )) / (aa * bb - ab * ab);
  1897. a1 = ReciprocalSIMD( ReplicateX4( aa * bb - ab * ab ) );
  1898. a1 = MulSIMD( a1, SubSIMD( MulSIMD( ReplicateX4( bb ), vspot * v1 ), MulSIMD( ReplicateX4( ab ), vspot * v2 ) ) );
  1899. //a2 = (DotProduct( vspot, v2 ) - a1 * ab) / bb;
  1900. a2 = ReciprocalSIMD( ReplicateX4( bb ) );
  1901. a2 = MulSIMD( a2, SubSIMD( vspot * v2, MulSIMD( a1, ReplicateX4( ab ) ) ) );
  1902. fltx4 resultMask = AndSIMD( CmpGeSIMD( a1, Four_Zeros ), CmpGeSIMD( a2, Four_Zeros ) );
  1903. if ( !TestSignSIMD( resultMask ) )
  1904. continue;
  1905. // Store the old phong normal to avoid overwriting already computed phong normals
  1906. FourVectors oldPhongNormal = phongnormal;
  1907. // calculate distance from edge to pos
  1908. FourVectors temp;
  1909. fltx4 scale;
  1910. // Interpolate between the center and edge normals based on sample position
  1911. scale = SubSIMD( SubSIMD( Four_Ones, a1 ), a2 );
  1912. phongnormal.DuplicateVector( fn->facenormal );
  1913. phongnormal *= scale;
  1914. temp.DuplicateVector( n1 );
  1915. temp *= a1;
  1916. phongnormal += temp;
  1917. temp.DuplicateVector( n2 );
  1918. temp *= a2;
  1919. phongnormal += temp;
  1920. // restore the old phong normals
  1921. phongnormal.x = AddSIMD( AndSIMD( resultMask, phongnormal.x ), AndNotSIMD( resultMask, oldPhongNormal.x ) );
  1922. phongnormal.y = AddSIMD( AndSIMD( resultMask, phongnormal.y ), AndNotSIMD( resultMask, oldPhongNormal.y ) );
  1923. phongnormal.z = AddSIMD( AndSIMD( resultMask, phongnormal.z ), AndNotSIMD( resultMask, oldPhongNormal.z ) );
  1924. }
  1925. phongnormal.VectorNormalize();
  1926. }
  1927. }
  1928. int GetVisCache( int lastoffset, int cluster, byte *pvs )
  1929. {
  1930. // get the PVS for the pos to limit the number of checks
  1931. if ( !visdatasize )
  1932. {
  1933. memset (pvs, 255, (dvis->numclusters+7)/8 );
  1934. lastoffset = -1;
  1935. }
  1936. else
  1937. {
  1938. if (cluster < 0)
  1939. {
  1940. // Error, point embedded in wall
  1941. // sampled[0][1] = 255;
  1942. memset (pvs, 255, (dvis->numclusters+7)/8 );
  1943. lastoffset = -1;
  1944. }
  1945. else
  1946. {
  1947. int thisoffset = dvis->bitofs[ cluster ][DVIS_PVS];
  1948. if ( thisoffset != lastoffset )
  1949. {
  1950. if ( thisoffset == -1 )
  1951. {
  1952. Error ("visofs == -1");
  1953. }
  1954. DecompressVis (&dvisdata[thisoffset], pvs);
  1955. }
  1956. lastoffset = thisoffset;
  1957. }
  1958. }
  1959. return lastoffset;
  1960. }
  1961. void BuildPatchLights( int facenum );
  1962. void DumpSamples( int ndxFace, facelight_t *pFaceLight )
  1963. {
  1964. ThreadLock();
  1965. dface_t *pFace = &g_pFaces[ndxFace];
  1966. if( pFace )
  1967. {
  1968. bool bBumpped = ( ( texinfo[pFace->texinfo].flags & SURF_BUMPLIGHT ) != 0 );
  1969. for( int iStyle = 0; iStyle < 4; ++iStyle )
  1970. {
  1971. if( pFace->styles[iStyle] != 255 )
  1972. {
  1973. for ( int iBump = 0; iBump < 4; ++iBump )
  1974. {
  1975. if ( iBump == 0 || ( iBump > 0 && bBumpped ) )
  1976. {
  1977. for( int iSample = 0; iSample < pFaceLight->numsamples; ++iSample )
  1978. {
  1979. sample_t *pSample = &pFaceLight->sample[iSample];
  1980. WriteWinding( pFileSamples[iStyle][iBump], pSample->w, pFaceLight->light[iStyle][iBump][iSample].m_vecLighting );
  1981. if( bDumpNormals )
  1982. {
  1983. WriteNormal( pFileSamples[iStyle][iBump], pSample->pos, pSample->normal, 15.0f, pSample->normal * 255.0f );
  1984. }
  1985. }
  1986. }
  1987. }
  1988. }
  1989. }
  1990. }
  1991. ThreadUnlock();
  1992. }
  1993. //-----------------------------------------------------------------------------
  1994. // Allocates light sample data
  1995. //-----------------------------------------------------------------------------
  1996. static inline void AllocateLightstyleSamples( facelight_t* fl, int styleIndex, int numnormals )
  1997. {
  1998. for (int n = 0; n < numnormals; ++n)
  1999. {
  2000. fl->light[styleIndex][n] = ( LightingValue_t* )calloc( fl->numsamples, sizeof(LightingValue_t ) );
  2001. }
  2002. }
  2003. //-----------------------------------------------------------------------------
  2004. // Used to find an existing lightstyle on a face
  2005. //-----------------------------------------------------------------------------
  2006. static inline int FindLightstyle( dface_t* f, int lightstyle )
  2007. {
  2008. for (int k = 0; k < MAXLIGHTMAPS; k++)
  2009. {
  2010. if (f->styles[k] == lightstyle)
  2011. return k;
  2012. }
  2013. return -1;
  2014. }
  2015. static int FindOrAllocateLightstyleSamples( dface_t* f, facelight_t *fl, int lightstyle, int numnormals )
  2016. {
  2017. // Search the lightstyles associated with the face for a match
  2018. int k;
  2019. for (k = 0; k < MAXLIGHTMAPS; k++)
  2020. {
  2021. if (f->styles[k] == lightstyle)
  2022. break;
  2023. // Found an empty entry, we can use it for a new lightstyle
  2024. if (f->styles[k] == 255)
  2025. {
  2026. AllocateLightstyleSamples( fl, k, numnormals );
  2027. f->styles[k] = lightstyle;
  2028. break;
  2029. }
  2030. }
  2031. // Check for overflow
  2032. if (k >= MAXLIGHTMAPS)
  2033. return -1;
  2034. return k;
  2035. }
  2036. //-----------------------------------------------------------------------------
  2037. // Compute the illumination point + normal for the sample
  2038. //-----------------------------------------------------------------------------
  2039. static void ComputeIlluminationPointAndNormalsSSE( lightinfo_t const& l, FourVectors const &pos, FourVectors const &norm, SSE_SampleInfo_t* pInfo, int numSamples )
  2040. {
  2041. Vector v[4];
  2042. pInfo->m_Points = pos;
  2043. bool computeNormals = ( pInfo->m_NormalCount > 1 && ( pInfo->m_IsDispFace || !l.isflat ) );
  2044. // FIXME: move sample point off the surface a bit, this is done so that
  2045. // light sampling will not be affected by a bug where raycasts will
  2046. // intersect with the face being lit. We really should just have that
  2047. // logic in GatherSampleLight
  2048. FourVectors faceNormal;
  2049. faceNormal.DuplicateVector( l.facenormal );
  2050. pInfo->m_Points += faceNormal;
  2051. if ( pInfo->m_IsDispFace )
  2052. {
  2053. pInfo->m_PointNormals[0] = norm;
  2054. }
  2055. else if ( !l.isflat )
  2056. {
  2057. // If the face isn't flat, use a phong-based normal instead
  2058. FourVectors modelorg;
  2059. modelorg.DuplicateVector( l.modelorg );
  2060. FourVectors vecSample = pos;
  2061. vecSample -= modelorg;
  2062. GetPhongNormal( pInfo->m_FaceNum, vecSample, pInfo->m_PointNormals[0] );
  2063. }
  2064. if ( computeNormals )
  2065. {
  2066. Vector bv[4][NUM_BUMP_VECTS];
  2067. for ( int i = 0; i < 4; ++i )
  2068. {
  2069. // TODO: using Vec may slow things down a bit
  2070. GetBumpNormals( pInfo->m_pTexInfo->textureVecsTexelsPerWorldUnits[0],
  2071. pInfo->m_pTexInfo->textureVecsTexelsPerWorldUnits[1],
  2072. l.facenormal, pInfo->m_PointNormals[0].Vec( i ), bv[i] );
  2073. }
  2074. for ( int b = 0; b < NUM_BUMP_VECTS; ++b )
  2075. {
  2076. pInfo->m_PointNormals[b+1].LoadAndSwizzle ( bv[0][b], bv[1][b], bv[2][b], bv[3][b] );
  2077. }
  2078. }
  2079. // TODO: this may slow things down a bit ( using Vec )
  2080. for ( int i = 0; i < 4; ++i )
  2081. pInfo->m_Clusters[i] = ClusterFromPoint( pos.Vec( i ) );
  2082. }
  2083. //-----------------------------------------------------------------------------
  2084. // Iterates over all lights and computes lighting at up to 4 sample points
  2085. //-----------------------------------------------------------------------------
  2086. static void GatherSampleLightAt4Points( SSE_SampleInfo_t& info, int sampleIdx, int numSamples )
  2087. {
  2088. SSE_sampleLightOutput_t out;
  2089. // Iterate over all direct lights and add them to the particular sample
  2090. for (directlight_t *dl = activelights; dl != NULL; dl = dl->next)
  2091. {
  2092. // is this lights cluster visible?
  2093. fltx4 dotMask = Four_Zeros;
  2094. bool skipLight = true;
  2095. for( int s = 0; s < numSamples; s++ )
  2096. {
  2097. if( PVSCheck( dl->pvs, info.m_Clusters[s] ) )
  2098. {
  2099. dotMask = SetComponentSIMD( dotMask, s, 1.0f );
  2100. skipLight = false;
  2101. }
  2102. }
  2103. if ( skipLight )
  2104. continue;
  2105. GatherSampleLightSSE( out, dl, info.m_FaceNum, info.m_Points, info.m_PointNormals, info.m_NormalCount, info.m_iThread );
  2106. // Apply the PVS check filter and compute falloff x dot
  2107. fltx4 fxdot[NUM_BUMP_VECTS + 1];
  2108. skipLight = true;
  2109. for ( int b = 0; b < info.m_NormalCount; b++ )
  2110. {
  2111. fxdot[b] = MulSIMD( out.m_flDot[b], dotMask );
  2112. fxdot[b] = MulSIMD( fxdot[b], out.m_flFalloff );
  2113. if ( !IsAllZeros( fxdot[b] ) )
  2114. {
  2115. skipLight = false;
  2116. }
  2117. }
  2118. if ( skipLight )
  2119. continue;
  2120. // Figure out the lightstyle for this particular sample
  2121. int lightStyleIndex = FindOrAllocateLightstyleSamples( info.m_pFace, info.m_pFaceLight,
  2122. dl->light.style, info.m_NormalCount );
  2123. if (lightStyleIndex < 0)
  2124. {
  2125. if (info.m_WarnFace != info.m_FaceNum)
  2126. {
  2127. Warning ("\nWARNING: Too many light styles on a face at (%f, %f, %f)\n",
  2128. info.m_Points.x.m128_f32[0], info.m_Points.y.m128_f32[0], info.m_Points.z.m128_f32[0] );
  2129. info.m_WarnFace = info.m_FaceNum;
  2130. }
  2131. continue;
  2132. }
  2133. // pLightmaps is an array of the lightmaps for each normal direction,
  2134. // here's where the result of the sample gathering goes
  2135. LightingValue_t** pLightmaps = info.m_pFaceLight->light[lightStyleIndex];
  2136. // Incremental lighting only cares about lightstyle zero
  2137. if( g_pIncremental && (dl->light.style == 0) )
  2138. {
  2139. for ( int i = 0; i < numSamples; i++ )
  2140. {
  2141. g_pIncremental->AddLightToFace( dl->m_IncrementalID, info.m_FaceNum, sampleIdx + i,
  2142. info.m_LightmapSize, SubFloat( fxdot[0], i ), info.m_iThread );
  2143. }
  2144. }
  2145. for( int n = 0; n < info.m_NormalCount; ++n )
  2146. {
  2147. for ( int i = 0; i < numSamples; i++ )
  2148. {
  2149. pLightmaps[n][sampleIdx + i].AddLight( SubFloat( fxdot[n], i ), dl->light.intensity, SubFloat( out.m_flSunAmount, i ) );
  2150. }
  2151. }
  2152. }
  2153. }
  2154. //-----------------------------------------------------------------------------
  2155. // Iterates over all lights and computes lighting at a sample point
  2156. //-----------------------------------------------------------------------------
  2157. static void ResampleLightAt4Points( SSE_SampleInfo_t& info, int lightStyleIndex, int flags, LightingValue_t pLightmap[4][NUM_BUMP_VECTS+1] )
  2158. {
  2159. SSE_sampleLightOutput_t out;
  2160. // Clear result
  2161. for ( int i = 0; i < 4; ++i )
  2162. {
  2163. for ( int n = 0; n < info.m_NormalCount; ++n )
  2164. {
  2165. pLightmap[i][n].Zero();
  2166. }
  2167. }
  2168. // Iterate over all direct lights and add them to the particular sample
  2169. for (directlight_t *dl = activelights; dl != NULL; dl = dl->next)
  2170. {
  2171. if ((flags & AMBIENT_ONLY) && (dl->light.type != emit_skyambient))
  2172. continue;
  2173. if ((flags & NON_AMBIENT_ONLY) && (dl->light.type == emit_skyambient))
  2174. continue;
  2175. // Only add contributions that match the lightstyle
  2176. Assert( lightStyleIndex <= MAXLIGHTMAPS );
  2177. Assert( info.m_pFace->styles[lightStyleIndex] != 255 );
  2178. if (dl->light.style != info.m_pFace->styles[lightStyleIndex])
  2179. continue;
  2180. // is this lights cluster visible?
  2181. fltx4 dotMask = Four_Zeros;
  2182. bool skipLight = true;
  2183. for( int s = 0; s < 4; s++ )
  2184. {
  2185. if( PVSCheck( dl->pvs, info.m_Clusters[s] ) )
  2186. {
  2187. dotMask = SetComponentSIMD( dotMask, s, 1.0f );
  2188. skipLight = false;
  2189. }
  2190. }
  2191. if ( skipLight )
  2192. continue;
  2193. // NOTE: Notice here that if the light is on the back side of the face
  2194. // (tested by checking the dot product of the face normal and the light position)
  2195. // we don't want it to contribute to *any* of the bumped lightmaps. It glows
  2196. // in disturbing ways if we don't do this.
  2197. GatherSampleLightSSE( out, dl, info.m_FaceNum, info.m_Points, info.m_PointNormals, info.m_NormalCount, info.m_iThread );
  2198. // Apply the PVS check filter and compute falloff x dot
  2199. fltx4 fxdot[NUM_BUMP_VECTS + 1];
  2200. for ( int b = 0; b < info.m_NormalCount; b++ )
  2201. {
  2202. fxdot[b] = MulSIMD( out.m_flFalloff, out.m_flDot[b] );
  2203. fxdot[b] = MulSIMD( fxdot[b], dotMask );
  2204. }
  2205. // Compute the contributions to each of the bumped lightmaps
  2206. // The first sample is for non-bumped lighting.
  2207. // The other sample are for bumpmapping.
  2208. for( int i = 0; i < 4; ++i )
  2209. {
  2210. for( int n = 0; n < info.m_NormalCount; ++n )
  2211. {
  2212. pLightmap[i][n].AddLight( SubFloat( fxdot[n], i ), dl->light.intensity, SubFloat( out.m_flSunAmount, i ) );
  2213. }
  2214. }
  2215. }
  2216. }
  2217. bool PointsInWinding ( FourVectors const & point, winding_t *w, int &invalidBits )
  2218. {
  2219. FourVectors edge, toPt, cross, testCross, p0, p1;
  2220. fltx4 invalidMask;
  2221. //
  2222. // get the first normal to test
  2223. //
  2224. p0.DuplicateVector( w->p[0] );
  2225. p1.DuplicateVector( w->p[1] );
  2226. toPt = point;
  2227. toPt -= p0;
  2228. edge = p1;
  2229. edge -= p0;
  2230. testCross = edge ^ toPt;
  2231. testCross.VectorNormalizeFast();
  2232. for( int ndxPt = 1; ndxPt < w->numpoints; ndxPt++ )
  2233. {
  2234. p0.DuplicateVector( w->p[ndxPt] );
  2235. p1.DuplicateVector( w->p[(ndxPt+1)%w->numpoints] );
  2236. toPt = point;
  2237. toPt -= p0;
  2238. edge = p1;
  2239. edge -= p0;
  2240. cross = edge ^ toPt;
  2241. cross.VectorNormalizeFast();
  2242. fltx4 dot = cross * testCross;
  2243. invalidMask = OrSIMD( invalidMask, CmpLtSIMD( dot, Four_Zeros ) );
  2244. invalidBits = TestSignSIMD ( invalidMask );
  2245. if ( invalidBits == 0xF )
  2246. return false;
  2247. }
  2248. return true;
  2249. }
  2250. //-----------------------------------------------------------------------------
  2251. // Perform supersampling at a particular point
  2252. //-----------------------------------------------------------------------------
  2253. static int SupersampleLightAtPoint( lightinfo_t& l, SSE_SampleInfo_t& info,
  2254. int sampleIndex, int lightStyleIndex, LightingValue_t *pLight, int flags )
  2255. {
  2256. sample_t& sample = info.m_pFaceLight->sample[sampleIndex];
  2257. // Get the position of the original sample in lightmapspace
  2258. Vector2D temp;
  2259. WorldToLuxelSpace( &l, sample.pos, temp );
  2260. Vector sampleLightOrigin( temp[0], temp[1], 0.0f );
  2261. // Some parameters related to supersampling
  2262. float sampleWidth = ( flags & NON_AMBIENT_ONLY ) ? 4 : 2;
  2263. float cscale = 1.0f / sampleWidth;
  2264. float csshift = -((sampleWidth - 1) * cscale) / 2.0;
  2265. // Clear out the light values
  2266. for (int i = 0; i < info.m_NormalCount; ++i )
  2267. pLight[i].Zero();
  2268. int subsampleCount = 0;
  2269. FourVectors superSampleNormal;
  2270. superSampleNormal.DuplicateVector( sample.normal );
  2271. FourVectors superSampleLightCoord;
  2272. FourVectors superSamplePosition;
  2273. if ( flags & NON_AMBIENT_ONLY )
  2274. {
  2275. float aRow[4];
  2276. for ( int coord = 0; coord < 4; ++coord )
  2277. aRow[coord] = csshift + coord * cscale;
  2278. fltx4 sseRow = LoadUnalignedSIMD( aRow );
  2279. for (int s = 0; s < 4; ++s)
  2280. {
  2281. // make sure the coordinate is inside of the sample's winding and when normalizing
  2282. // below use the number of samples used, not just numsamples and some of them
  2283. // will be skipped if they are not inside of the winding
  2284. superSampleLightCoord.DuplicateVector( sampleLightOrigin );
  2285. superSampleLightCoord.x = AddSIMD( superSampleLightCoord.x, ReplicateX4( aRow[s] ) );
  2286. superSampleLightCoord.y = AddSIMD( superSampleLightCoord.y, sseRow );
  2287. // Figure out where the supersample exists in the world, and make sure
  2288. // it lies within the sample winding
  2289. LuxelSpaceToWorld( &l, superSampleLightCoord[0], superSampleLightCoord[1], superSamplePosition );
  2290. // A winding should exist only if the sample wasn't a uniform luxel, or if g_bDumpPatches is true.
  2291. int invalidBits = 0;
  2292. if ( sample.w && !PointsInWinding( superSamplePosition, sample.w, invalidBits ) )
  2293. continue;
  2294. // Compute the super-sample illumination point and normal
  2295. // We're assuming the flat normal is the same for all supersamples
  2296. ComputeIlluminationPointAndNormalsSSE( l, superSamplePosition, superSampleNormal, &info, 4 );
  2297. // Resample the non-ambient light at this point...
  2298. LightingValue_t result[4][NUM_BUMP_VECTS+1];
  2299. ResampleLightAt4Points( info, lightStyleIndex, NON_AMBIENT_ONLY, result );
  2300. // Got more subsamples
  2301. for ( int i = 0; i < 4; i++ )
  2302. {
  2303. if ( !( ( invalidBits >> i ) & 0x1 ) )
  2304. {
  2305. for ( int n = 0; n < info.m_NormalCount; ++n )
  2306. {
  2307. pLight[n].AddLight( result[i][n] );
  2308. }
  2309. ++subsampleCount;
  2310. }
  2311. }
  2312. }
  2313. }
  2314. else
  2315. {
  2316. FourVectors superSampleOffsets;
  2317. superSampleOffsets.LoadAndSwizzle( Vector( csshift, csshift, 0 ), Vector( csshift, csshift + cscale, 0),
  2318. Vector( csshift + cscale, csshift, 0 ), Vector( csshift + cscale, csshift + cscale, 0 ) );
  2319. superSampleLightCoord.DuplicateVector( sampleLightOrigin );
  2320. superSampleLightCoord += superSampleOffsets;
  2321. LuxelSpaceToWorld( &l, superSampleLightCoord[0], superSampleLightCoord[1], superSamplePosition );
  2322. int invalidBits = 0;
  2323. if ( sample.w && !PointsInWinding( superSamplePosition, sample.w, invalidBits ) )
  2324. return 0;
  2325. ComputeIlluminationPointAndNormalsSSE( l, superSamplePosition, superSampleNormal, &info, 4 );
  2326. LightingValue_t result[4][NUM_BUMP_VECTS+1];
  2327. ResampleLightAt4Points( info, lightStyleIndex, AMBIENT_ONLY, result );
  2328. // Got more subsamples
  2329. for ( int i = 0; i < 4; i++ )
  2330. {
  2331. if ( !( ( invalidBits >> i ) & 0x1 ) )
  2332. {
  2333. for ( int n = 0; n < info.m_NormalCount; ++n )
  2334. {
  2335. pLight[n].AddLight( result[i][n] );
  2336. }
  2337. ++subsampleCount;
  2338. }
  2339. }
  2340. }
  2341. return subsampleCount;
  2342. }
  2343. //-----------------------------------------------------------------------------
  2344. // Compute gradients of a lightmap
  2345. //-----------------------------------------------------------------------------
  2346. static void ComputeLightmapGradients( SSE_SampleInfo_t& info, bool const* pHasProcessedSample,
  2347. float* pIntensity, float* gradient )
  2348. {
  2349. int w = info.m_LightmapWidth;
  2350. int h = info.m_LightmapHeight;
  2351. facelight_t* fl = info.m_pFaceLight;
  2352. for (int i=0 ; i<fl->numsamples ; i++)
  2353. {
  2354. // Don't supersample the same sample twice
  2355. if (pHasProcessedSample[i])
  2356. continue;
  2357. gradient[i] = 0.0f;
  2358. sample_t& sample = fl->sample[i];
  2359. // Choose the maximum gradient of all bumped lightmap intensities
  2360. for ( int n = 0; n < info.m_NormalCount; ++n )
  2361. {
  2362. int j = n * info.m_LightmapSize + sample.s + sample.t * w;
  2363. if (sample.t > 0)
  2364. {
  2365. if (sample.s > 0) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j-1-w] ) );
  2366. gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j-w] ) );
  2367. if (sample.s < w-1) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j+1-w] ) );
  2368. }
  2369. if (sample.t < h-1)
  2370. {
  2371. if (sample.s > 0) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j-1+w] ) );
  2372. gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j+w] ) );
  2373. if (sample.s < w-1) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j+1+w] ) );
  2374. }
  2375. if (sample.s > 0) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j-1] ) );
  2376. if (sample.s < w-1) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j+1] ) );
  2377. }
  2378. }
  2379. }
  2380. //-----------------------------------------------------------------------------
  2381. // ComputeLuxelIntensity...
  2382. //-----------------------------------------------------------------------------
  2383. static inline void ComputeLuxelIntensity( SSE_SampleInfo_t& info, int sampleIdx,
  2384. LightingValue_t **ppLightSamples, float* pSampleIntensity )
  2385. {
  2386. // Compute a separate intensity for each
  2387. sample_t& sample = info.m_pFaceLight->sample[sampleIdx];
  2388. int destIdx = sample.s + sample.t * info.m_LightmapWidth;
  2389. for (int n = 0; n < info.m_NormalCount; ++n)
  2390. {
  2391. float intensity = ppLightSamples[n][sampleIdx].Intensity();
  2392. // convert to a linear perception space
  2393. pSampleIntensity[n * info.m_LightmapSize + destIdx] = pow( intensity / 256.0, 1.0 / 2.2 );
  2394. }
  2395. }
  2396. //-----------------------------------------------------------------------------
  2397. // Compute the maximum intensity based on all bumped lighting
  2398. //-----------------------------------------------------------------------------
  2399. static void ComputeSampleIntensities( SSE_SampleInfo_t& info, LightingValue_t **ppLightSamples, float* pSampleIntensity )
  2400. {
  2401. for (int i=0; i<info.m_pFaceLight->numsamples; i++)
  2402. {
  2403. ComputeLuxelIntensity( info, i, ppLightSamples, pSampleIntensity );
  2404. }
  2405. }
  2406. //-----------------------------------------------------------------------------
  2407. // Perform supersampling on a particular lightstyle
  2408. //-----------------------------------------------------------------------------
  2409. static void BuildSupersampleFaceLights( lightinfo_t& l, SSE_SampleInfo_t& info, int lightstyleIndex )
  2410. {
  2411. LightingValue_t pAmbientLight[NUM_BUMP_VECTS+1];
  2412. LightingValue_t pDirectLight[NUM_BUMP_VECTS+1];
  2413. // This is used to make sure we don't supersample a light sample more than once
  2414. int processedSampleSize = info.m_LightmapSize * sizeof(bool);
  2415. bool* pHasProcessedSample = (bool*)stackalloc( processedSampleSize );
  2416. memset( pHasProcessedSample, 0, processedSampleSize );
  2417. // This is used to compute a simple gradient computation of the light samples
  2418. // We're going to store the maximum intensity of all bumped samples at each sample location
  2419. float* pGradient = (float*)stackalloc( info.m_pFaceLight->numsamples * sizeof(float) );
  2420. float* pSampleIntensity = (float*)stackalloc( info.m_NormalCount * info.m_LightmapSize * sizeof(float) );
  2421. // Compute the maximum intensity of all lighting associated with this lightstyle
  2422. // for all bumped lighting
  2423. LightingValue_t **ppLightSamples = info.m_pFaceLight->light[lightstyleIndex];
  2424. ComputeSampleIntensities( info, ppLightSamples, pSampleIntensity );
  2425. Vector *pVisualizePass = NULL;
  2426. if (debug_extra)
  2427. {
  2428. int visualizationSize = info.m_pFaceLight->numsamples * sizeof(Vector);
  2429. pVisualizePass = (Vector*)stackalloc( visualizationSize );
  2430. memset( pVisualizePass, 0, visualizationSize );
  2431. }
  2432. // What's going on here is that we're looking for large lighting discontinuities
  2433. // (large light intensity gradients) as a clue that we should probably be supersampling
  2434. // in that area. Because the supersampling operation will cause lighting changes,
  2435. // we've found that it's good to re-check the gradients again and see if any other
  2436. // areas should be supersampled as a result of the previous pass. Keep going
  2437. // until all the gradients are reasonable or until we hit a max number of passes
  2438. bool do_anotherpass = true;
  2439. int pass = 1;
  2440. while (do_anotherpass && pass <= extrapasses)
  2441. {
  2442. // Look for lighting discontinuities to see what we should be supersampling
  2443. ComputeLightmapGradients( info, pHasProcessedSample, pSampleIntensity, pGradient );
  2444. do_anotherpass = false;
  2445. // Now check all of the samples and supersample those which we have
  2446. // marked as having high gradients
  2447. for (int i=0 ; i<info.m_pFaceLight->numsamples; ++i)
  2448. {
  2449. // Don't supersample the same sample twice
  2450. if (pHasProcessedSample[i])
  2451. continue;
  2452. // Don't supersample if the lighting is pretty uniform near the sample
  2453. if (pGradient[i] < 0.0625)
  2454. continue;
  2455. // Joy! We're supersampling now, and we therefore must do another pass
  2456. // Also, we need never bother with this sample again
  2457. pHasProcessedSample[i] = true;
  2458. do_anotherpass = true;
  2459. if (debug_extra)
  2460. {
  2461. // Mark the little visualization bitmap with a color indicating
  2462. // which pass it was updated on.
  2463. pVisualizePass[i][0] = (pass & 1) * 255;
  2464. pVisualizePass[i][1] = (pass & 2) * 128;
  2465. pVisualizePass[i][2] = (pass & 4) * 64;
  2466. }
  2467. // Supersample the ambient light for each bump direction vector
  2468. int ambientSupersampleCount = SupersampleLightAtPoint( l, info, i, lightstyleIndex, pAmbientLight, AMBIENT_ONLY );
  2469. // Supersample the non-ambient light for each bump direction vector
  2470. int directSupersampleCount = SupersampleLightAtPoint( l, info, i, lightstyleIndex, pDirectLight, NON_AMBIENT_ONLY );
  2471. // Because of sampling problems, small area triangles may have no samples.
  2472. // In this case, just use what we already have
  2473. if ( ambientSupersampleCount > 0 && directSupersampleCount > 0 )
  2474. {
  2475. // Add the ambient + directional terms together, stick it back into the lightmap
  2476. for (int n = 0; n < info.m_NormalCount; ++n)
  2477. {
  2478. ppLightSamples[n][i].Zero();
  2479. ppLightSamples[n][i].AddWeighted( pDirectLight[n],1.0f / directSupersampleCount );
  2480. ppLightSamples[n][i].AddWeighted( pAmbientLight[n], 1.0f / ambientSupersampleCount );
  2481. }
  2482. // Recompute the luxel intensity based on the supersampling
  2483. ComputeLuxelIntensity( info, i, ppLightSamples, pSampleIntensity );
  2484. }
  2485. }
  2486. // We've finished another pass
  2487. pass++;
  2488. }
  2489. if (debug_extra)
  2490. {
  2491. // Copy colors representing which supersample pass the sample was messed with
  2492. // into the actual lighting values so we can visualize it
  2493. for (int i=0 ; i<info.m_pFaceLight->numsamples ; ++i)
  2494. {
  2495. for (int j = 0; j <info.m_NormalCount; ++j)
  2496. {
  2497. VectorCopy( pVisualizePass[i], ppLightSamples[j][i].m_vecLighting );
  2498. }
  2499. }
  2500. }
  2501. }
  2502. void InitLightinfo( lightinfo_t *pl, int facenum )
  2503. {
  2504. dface_t *f;
  2505. f = &g_pFaces[facenum];
  2506. memset (pl, 0, sizeof(*pl));
  2507. pl->facenum = facenum;
  2508. pl->face = f;
  2509. //
  2510. // rotate plane
  2511. //
  2512. VectorCopy (dplanes[f->planenum].normal, pl->facenormal);
  2513. pl->facedist = dplanes[f->planenum].dist;
  2514. // get the origin offset for rotating bmodels
  2515. VectorCopy (face_offset[facenum], pl->modelorg);
  2516. CalcFaceVectors( pl );
  2517. // figure out if the surface is flat
  2518. pl->isflat = true;
  2519. if (smoothing_threshold != 1)
  2520. {
  2521. faceneighbor_t *fn = &faceneighbor[facenum];
  2522. for (int j=0 ; j<f->numedges ; j++)
  2523. {
  2524. float dot = DotProduct( pl->facenormal, fn->normal[j] );
  2525. if (dot < 1.0 - EQUAL_EPSILON)
  2526. {
  2527. pl->isflat = false;
  2528. break;
  2529. }
  2530. }
  2531. }
  2532. }
  2533. static void InitSampleInfo( lightinfo_t const& l, int iThread, SSE_SampleInfo_t& info )
  2534. {
  2535. info.m_LightmapWidth = l.face->m_LightmapTextureSizeInLuxels[0]+1;
  2536. info.m_LightmapHeight = l.face->m_LightmapTextureSizeInLuxels[1]+1;
  2537. info.m_LightmapSize = info.m_LightmapWidth * info.m_LightmapHeight;
  2538. // How many lightmaps are we going to need?
  2539. info.m_pTexInfo = &texinfo[l.face->texinfo];
  2540. info.m_NormalCount = (info.m_pTexInfo->flags & SURF_BUMPLIGHT) ? NUM_BUMP_VECTS + 1 : 1;
  2541. info.m_FaceNum = l.facenum;
  2542. info.m_pFace = l.face;
  2543. info.m_pFaceLight = &facelight[info.m_FaceNum];
  2544. info.m_IsDispFace = ValidDispFace( info.m_pFace );
  2545. info.m_iThread = iThread;
  2546. info.m_WarnFace = -1;
  2547. info.m_NumSamples = info.m_pFaceLight->numsamples;
  2548. info.m_NumSampleGroups = ( info.m_NumSamples & 0x3) ? ( info.m_NumSamples / 4 ) + 1 : ( info.m_NumSamples / 4 );
  2549. // initialize normals if the surface is flat
  2550. if (l.isflat)
  2551. {
  2552. info.m_PointNormals[0].DuplicateVector( l.facenormal );
  2553. // use facenormal along with the smooth normal to build the three bump map vectors
  2554. if( info.m_NormalCount > 1 )
  2555. {
  2556. Vector bumpVects[NUM_BUMP_VECTS];
  2557. GetBumpNormals( info.m_pTexInfo->textureVecsTexelsPerWorldUnits[0],
  2558. info.m_pTexInfo->textureVecsTexelsPerWorldUnits[1], l.facenormal,
  2559. l.facenormal, bumpVects );//&info.m_PointNormal[1] );
  2560. for ( int b = 0; b < NUM_BUMP_VECTS; ++b )
  2561. {
  2562. info.m_PointNormals[b + 1].DuplicateVector( bumpVects[b] );
  2563. }
  2564. }
  2565. }
  2566. }
  2567. void BuildFacelights (int iThread, int facenum)
  2568. {
  2569. int i, j;
  2570. lightinfo_t l;
  2571. dface_t *f;
  2572. facelight_t *fl;
  2573. SSE_SampleInfo_t sampleInfo;
  2574. directlight_t *dl;
  2575. Vector spot;
  2576. Vector v[4], n[4];
  2577. if( g_bInterrupt )
  2578. return;
  2579. // FIXME: Is there a better way to do this? Like, in RunThreadsOn, for instance?
  2580. // Don't pay this cost unless we have to; this is super perf-critical code.
  2581. if (g_pIncremental)
  2582. {
  2583. // Both threads will be accessing this so it needs to be protected or else thread A
  2584. // will load it in and thread B will increment it but its increment will be
  2585. // overwritten by thread A when thread A writes it back.
  2586. ThreadLock();
  2587. ++g_iCurFace;
  2588. ThreadUnlock();
  2589. }
  2590. // some surfaces don't need lightmaps
  2591. f = &g_pFaces[facenum];
  2592. f->lightofs = -1;
  2593. for (j=0 ; j<MAXLIGHTMAPS ; j++)
  2594. f->styles[j] = 255;
  2595. // Trivial-reject the whole face?
  2596. if( !( g_FacesVisibleToLights[facenum>>3] & (1 << (facenum & 7)) ) )
  2597. return;
  2598. if ( texinfo[f->texinfo].flags & TEX_SPECIAL)
  2599. return; // non-lit texture
  2600. // check for patches for this face. If none it must be degenerate. Ignore.
  2601. if( g_FacePatches.Element( facenum ) == g_FacePatches.InvalidIndex() )
  2602. return;
  2603. fl = &facelight[facenum];
  2604. InitLightinfo( &l, facenum );
  2605. CalcPoints( &l, fl, facenum );
  2606. InitSampleInfo( l, iThread, sampleInfo );
  2607. // Allocate sample positions/normals to SSE
  2608. int numGroups = ( fl->numsamples & 0x3) ? ( fl->numsamples / 4 ) + 1 : ( fl->numsamples / 4 );
  2609. // always allocate style 0 lightmap
  2610. f->styles[0] = 0;
  2611. AllocateLightstyleSamples( fl, 0, sampleInfo.m_NormalCount );
  2612. // sample the lights at each sample location
  2613. for ( int grp = 0; grp < numGroups; ++grp )
  2614. {
  2615. int nSample = 4 * grp;
  2616. sample_t *sample = sampleInfo.m_pFaceLight->sample + nSample;
  2617. int numSamples = min ( 4, sampleInfo.m_pFaceLight->numsamples - nSample );
  2618. FourVectors positions;
  2619. FourVectors normals;
  2620. for ( int i = 0; i < 4; i++ )
  2621. {
  2622. v[i] = ( i < numSamples ) ? sample[i].pos : sample[numSamples - 1].pos;
  2623. n[i] = ( i < numSamples ) ? sample[i].normal : sample[numSamples - 1].normal;
  2624. }
  2625. positions.LoadAndSwizzle( v[0], v[1], v[2], v[3] );
  2626. normals.LoadAndSwizzle( n[0], n[1], n[2], n[3] );
  2627. ComputeIlluminationPointAndNormalsSSE( l, positions, normals, &sampleInfo, numSamples );
  2628. // Fixup sample normals in case of smooth faces
  2629. if ( !l.isflat )
  2630. {
  2631. for ( int i = 0; i < numSamples; i++ )
  2632. sample[i].normal = sampleInfo.m_PointNormals[0].Vec( i );
  2633. }
  2634. // Iterate over all the lights and add their contribution to this group of spots
  2635. GatherSampleLightAt4Points( sampleInfo, nSample, numSamples );
  2636. }
  2637. // Tell the incremental light manager that we're done with this face.
  2638. if( g_pIncremental )
  2639. {
  2640. for (dl = activelights; dl != NULL; dl = dl->next)
  2641. {
  2642. // Only deal with lightstyle 0 for incremental lighting
  2643. if (dl->light.style == 0)
  2644. g_pIncremental->FinishFace( dl->m_IncrementalID, facenum, iThread );
  2645. }
  2646. // Don't have to deal with patch lights (only direct lighting is used)
  2647. // or supersampling
  2648. return;
  2649. }
  2650. // get rid of the -extra functionality on displacement surfaces
  2651. if (do_extra && !sampleInfo.m_IsDispFace)
  2652. {
  2653. // For each lightstyle, perform a supersampling pass
  2654. for ( i = 0; i < MAXLIGHTMAPS; ++i )
  2655. {
  2656. // Stop when we run out of lightstyles
  2657. if (f->styles[i] == 255)
  2658. break;
  2659. BuildSupersampleFaceLights( l, sampleInfo, i );
  2660. }
  2661. }
  2662. if (!g_bUseMPI)
  2663. {
  2664. //
  2665. // This is done on the master node when MPI is used
  2666. //
  2667. BuildPatchLights( facenum );
  2668. }
  2669. if( g_bDumpPatches )
  2670. {
  2671. DumpSamples( facenum, fl );
  2672. }
  2673. else
  2674. {
  2675. FreeSampleWindings( fl );
  2676. }
  2677. }
  2678. void BuildPatchLights( int facenum )
  2679. {
  2680. int i, k;
  2681. CPatch *patch;
  2682. dface_t *f = &g_pFaces[facenum];
  2683. facelight_t *fl = &facelight[facenum];
  2684. for( k = 0; k < MAXLIGHTMAPS; k++ )
  2685. {
  2686. if (f->styles[k] == 0)
  2687. break;
  2688. }
  2689. if (k >= MAXLIGHTMAPS)
  2690. return;
  2691. for (i = 0; i < fl->numsamples; i++)
  2692. {
  2693. AddSampleToPatch( &fl->sample[i], fl->light[k][0][i], facenum);
  2694. }
  2695. // check for a valid face
  2696. if( g_FacePatches.Element( facenum ) == g_FacePatches.InvalidIndex() )
  2697. return;
  2698. // push up sampled light to parents (children always exist first in the list)
  2699. CPatch *pNextPatch;
  2700. for( patch = &g_Patches.Element( g_FacePatches.Element( facenum ) ); patch; patch = pNextPatch )
  2701. {
  2702. // next patch
  2703. pNextPatch = NULL;
  2704. if( patch->ndxNext != g_Patches.InvalidIndex() )
  2705. {
  2706. pNextPatch = &g_Patches.Element( patch->ndxNext );
  2707. }
  2708. // skip patches without parents
  2709. if( patch->parent == g_Patches.InvalidIndex() )
  2710. // if (patch->parent == -1)
  2711. continue;
  2712. CPatch *parent = &g_Patches.Element( patch->parent );
  2713. parent->samplearea += patch->samplearea;
  2714. VectorAdd( parent->samplelight, patch->samplelight, parent->samplelight );
  2715. }
  2716. // average up the direct light on each patch for radiosity
  2717. if (numbounce > 0)
  2718. {
  2719. for( patch = &g_Patches.Element( g_FacePatches.Element( facenum ) ); patch; patch = pNextPatch )
  2720. {
  2721. // next patch
  2722. pNextPatch = NULL;
  2723. if( patch->ndxNext != g_Patches.InvalidIndex() )
  2724. {
  2725. pNextPatch = &g_Patches.Element( patch->ndxNext );
  2726. }
  2727. if (patch->samplearea)
  2728. {
  2729. float scale;
  2730. Vector v;
  2731. scale = 1.0 / patch->samplearea;
  2732. VectorScale( patch->samplelight, scale, v );
  2733. VectorAdd( patch->totallight.light[0], v, patch->totallight.light[0] );
  2734. VectorAdd( patch->directlight, v, patch->directlight );
  2735. }
  2736. }
  2737. }
  2738. // pull totallight from children (children always exist first in the list)
  2739. for( patch = &g_Patches.Element( g_FacePatches.Element( facenum ) ); patch; patch = pNextPatch )
  2740. {
  2741. // next patch
  2742. pNextPatch = NULL;
  2743. if( patch->ndxNext != g_Patches.InvalidIndex() )
  2744. {
  2745. pNextPatch = &g_Patches.Element( patch->ndxNext );
  2746. }
  2747. if ( patch->child1 != g_Patches.InvalidIndex() )
  2748. {
  2749. float s1, s2;
  2750. CPatch *child1;
  2751. CPatch *child2;
  2752. child1 = &g_Patches.Element( patch->child1 );
  2753. child2 = &g_Patches.Element( patch->child2 );
  2754. s1 = child1->area / (child1->area + child2->area);
  2755. s2 = child2->area / (child1->area + child2->area);
  2756. VectorScale( child1->totallight.light[0], s1, patch->totallight.light[0] );
  2757. VectorMA( patch->totallight.light[0], s2, child2->totallight.light[0], patch->totallight.light[0] );
  2758. VectorCopy( patch->totallight.light[0], patch->directlight );
  2759. }
  2760. }
  2761. bool needsBumpmap = false;
  2762. if( texinfo[f->texinfo].flags & SURF_BUMPLIGHT )
  2763. {
  2764. needsBumpmap = true;
  2765. }
  2766. // add an ambient term if desired
  2767. if (ambient[0] || ambient[1] || ambient[2])
  2768. {
  2769. for( int j=0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++ )
  2770. {
  2771. if ( f->styles[j] == 0 )
  2772. {
  2773. for (i = 0; i < fl->numsamples; i++)
  2774. {
  2775. fl->light[j][0][i].m_vecLighting += ambient;
  2776. if( needsBumpmap )
  2777. {
  2778. fl->light[j][1][i].m_vecLighting += ambient;
  2779. fl->light[j][2][i].m_vecLighting += ambient;
  2780. fl->light[j][3][i].m_vecLighting += ambient;
  2781. }
  2782. }
  2783. break;
  2784. }
  2785. }
  2786. }
  2787. // light from dlight_threshold and above is sent out, but the
  2788. // texture itself should still be full bright
  2789. #if 0
  2790. // if( VectorAvg( g_FacePatches[facenum]->baselight ) >= dlight_threshold) // Now all lighted surfaces glow
  2791. {
  2792. for( j=0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++ )
  2793. {
  2794. if ( f->styles[j] == 0 )
  2795. {
  2796. // BUG: shouldn't this be done for all patches on the face?
  2797. for (i=0 ; i<fl->numsamples ; i++)
  2798. {
  2799. // garymctchange
  2800. VectorAdd( fl->light[j][0][i], g_FacePatches[facenum]->baselight, fl->light[j][0][i] );
  2801. if( needsBumpmap )
  2802. {
  2803. for( bumpSample = 1; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
  2804. {
  2805. VectorAdd( fl->light[j][bumpSample][i], g_FacePatches[facenum]->baselight, fl->light[j][bumpSample][i] );
  2806. }
  2807. }
  2808. }
  2809. break;
  2810. }
  2811. }
  2812. }
  2813. #endif
  2814. }
  2815. /*
  2816. =============
  2817. PrecompLightmapOffsets
  2818. =============
  2819. */
  2820. void PrecompLightmapOffsets()
  2821. {
  2822. int facenum;
  2823. dface_t *f;
  2824. int lightstyles;
  2825. int lightdatasize = 0;
  2826. // NOTE: We store avg face light data in this lump *before* the lightmap data itself
  2827. // in *reverse order* of the way the lightstyles appear in the styles array.
  2828. for( facenum = 0; facenum < numfaces; facenum++ )
  2829. {
  2830. f = &g_pFaces[facenum];
  2831. if ( texinfo[f->texinfo].flags & TEX_SPECIAL)
  2832. continue; // non-lit texture
  2833. if ( dlight_map != 0 )
  2834. f->styles[1] = 0;
  2835. for (lightstyles=0; lightstyles < MAXLIGHTMAPS; lightstyles++ )
  2836. {
  2837. if ( f->styles[lightstyles] == 255 )
  2838. break;
  2839. }
  2840. if ( !lightstyles )
  2841. continue;
  2842. // Reserve room for the avg light color data
  2843. lightdatasize += lightstyles * 4;
  2844. f->lightofs = lightdatasize;
  2845. bool needsBumpmap = false;
  2846. if( texinfo[f->texinfo].flags & SURF_BUMPLIGHT )
  2847. {
  2848. needsBumpmap = true;
  2849. }
  2850. int nLuxels = (f->m_LightmapTextureSizeInLuxels[0]+1) * (f->m_LightmapTextureSizeInLuxels[1]+1);
  2851. if( needsBumpmap )
  2852. {
  2853. lightdatasize += nLuxels * 4 * lightstyles * ( NUM_BUMP_VECTS + 1 );
  2854. }
  2855. else
  2856. {
  2857. lightdatasize += nLuxels * 4 * lightstyles;
  2858. }
  2859. }
  2860. // The incremental lighting code needs us to preserve the contents of dlightdata
  2861. // since it only recomposites lighting for faces that have lights that touch them.
  2862. if( g_pIncremental && pdlightdata->Count() )
  2863. return;
  2864. pdlightdata->SetSize( lightdatasize );
  2865. }
  2866. // Clamp the three values for bumped lighting such that we trade off directionality for brightness.
  2867. static void ColorClampBumped( Vector& color1, Vector& color2, Vector& color3 )
  2868. {
  2869. Vector maxs;
  2870. Vector *colors[3] = { &color1, &color2, &color3 };
  2871. maxs[0] = VectorMaximum( color1 );
  2872. maxs[1] = VectorMaximum( color2 );
  2873. maxs[2] = VectorMaximum( color3 );
  2874. // HACK! Clean this up, and add some else statements
  2875. #define CONDITION(a,b,c) do { if( maxs[a] >= maxs[b] && maxs[b] >= maxs[c] ) { order[0] = a; order[1] = b; order[2] = c; } } while( 0 )
  2876. int order[3];
  2877. CONDITION(0,1,2);
  2878. CONDITION(0,2,1);
  2879. CONDITION(1,0,2);
  2880. CONDITION(1,2,0);
  2881. CONDITION(2,0,1);
  2882. CONDITION(2,1,0);
  2883. int i;
  2884. for( i = 0; i < 3; i++ )
  2885. {
  2886. float max = VectorMaximum( *colors[order[i]] );
  2887. if( max <= 1.0f )
  2888. {
  2889. continue;
  2890. }
  2891. // This channel is too bright. . take half of the amount that we are over and
  2892. // add it to the other two channel.
  2893. float factorToRedist = ( max - 1.0f ) / max;
  2894. Vector colorToRedist = factorToRedist * *colors[order[i]];
  2895. *colors[order[i]] -= colorToRedist;
  2896. colorToRedist *= 0.5f;
  2897. *colors[order[(i+1)%3]] += colorToRedist;
  2898. *colors[order[(i+2)%3]] += colorToRedist;
  2899. }
  2900. ColorClamp( color1 );
  2901. ColorClamp( color2 );
  2902. ColorClamp( color3 );
  2903. if( color1[0] < 0.f ) color1[0] = 0.f;
  2904. if( color1[1] < 0.f ) color1[1] = 0.f;
  2905. if( color1[2] < 0.f ) color1[2] = 0.f;
  2906. if( color2[0] < 0.f ) color2[0] = 0.f;
  2907. if( color2[1] < 0.f ) color2[1] = 0.f;
  2908. if( color2[2] < 0.f ) color2[2] = 0.f;
  2909. if( color3[0] < 0.f ) color3[0] = 0.f;
  2910. if( color3[1] < 0.f ) color3[1] = 0.f;
  2911. if( color3[2] < 0.f ) color3[2] = 0.f;
  2912. }
  2913. static void LinearToBumpedLightmap(
  2914. const float *linearColor,
  2915. const float *linearBumpColor1,
  2916. const float *linearBumpColor2,
  2917. const float *linearBumpColor3,
  2918. unsigned char *ret,
  2919. unsigned char *retBump1,
  2920. unsigned char *retBump2,
  2921. unsigned char *retBump3 )
  2922. {
  2923. const Vector &linearBump1 = *( ( const Vector * )linearBumpColor1 );
  2924. const Vector &linearBump2 = *( ( const Vector * )linearBumpColor2 );
  2925. const Vector &linearBump3 = *( ( const Vector * )linearBumpColor3 );
  2926. Vector gammaGoal;
  2927. // gammaGoal is premultiplied by 1/overbright, which we want
  2928. gammaGoal[0] = LinearToVertexLight( linearColor[0] );
  2929. gammaGoal[1] = LinearToVertexLight( linearColor[1] );
  2930. gammaGoal[2] = LinearToVertexLight( linearColor[2] );
  2931. Vector bumpAverage = linearBump1;
  2932. bumpAverage += linearBump2;
  2933. bumpAverage += linearBump3;
  2934. bumpAverage *= ( 1.0f / 3.0f );
  2935. Vector correctionScale;
  2936. if( *( int * )&bumpAverage[0] != 0 && *( int * )&bumpAverage[1] != 0 && *( int * )&bumpAverage[2] != 0 )
  2937. {
  2938. // fast path when we know that we don't have to worry about divide by zero.
  2939. VectorDivide( gammaGoal, bumpAverage, correctionScale );
  2940. // correctionScale = gammaGoal / bumpSum;
  2941. }
  2942. else
  2943. {
  2944. correctionScale.Init( 0.0f, 0.0f, 0.0f );
  2945. if( bumpAverage[0] != 0.0f )
  2946. {
  2947. correctionScale[0] = gammaGoal[0] / bumpAverage[0];
  2948. }
  2949. if( bumpAverage[1] != 0.0f )
  2950. {
  2951. correctionScale[1] = gammaGoal[1] / bumpAverage[1];
  2952. }
  2953. if( bumpAverage[2] != 0.0f )
  2954. {
  2955. correctionScale[2] = gammaGoal[2] / bumpAverage[2];
  2956. }
  2957. }
  2958. Vector correctedBumpColor1;
  2959. Vector correctedBumpColor2;
  2960. Vector correctedBumpColor3;
  2961. VectorMultiply( linearBump1, correctionScale, correctedBumpColor1 );
  2962. VectorMultiply( linearBump2, correctionScale, correctedBumpColor2 );
  2963. VectorMultiply( linearBump3, correctionScale, correctedBumpColor3 );
  2964. Vector check = ( correctedBumpColor1 + correctedBumpColor2 + correctedBumpColor3 ) / 3.0f;
  2965. ColorClampBumped( correctedBumpColor1, correctedBumpColor2, correctedBumpColor3 );
  2966. ret[0] = RoundFloatToByte( gammaGoal[0] * 255.0f );
  2967. ret[1] = RoundFloatToByte( gammaGoal[1] * 255.0f );
  2968. ret[2] = RoundFloatToByte( gammaGoal[2] * 255.0f );
  2969. retBump1[0] = RoundFloatToByte( correctedBumpColor1[0] * 255.0f );
  2970. retBump1[1] = RoundFloatToByte( correctedBumpColor1[1] * 255.0f );
  2971. retBump1[2] = RoundFloatToByte( correctedBumpColor1[2] * 255.0f );
  2972. retBump2[0] = RoundFloatToByte( correctedBumpColor2[0] * 255.0f );
  2973. retBump2[1] = RoundFloatToByte( correctedBumpColor2[1] * 255.0f );
  2974. retBump2[2] = RoundFloatToByte( correctedBumpColor2[2] * 255.0f );
  2975. retBump3[0] = RoundFloatToByte( correctedBumpColor3[0] * 255.0f );
  2976. retBump3[1] = RoundFloatToByte( correctedBumpColor3[1] * 255.0f );
  2977. retBump3[2] = RoundFloatToByte( correctedBumpColor3[2] * 255.0f );
  2978. }
  2979. //-----------------------------------------------------------------------------
  2980. // Convert a RGBExp32 to a RGBA8888
  2981. // This matches the engine's conversion, so the lighting result is consistent.
  2982. //-----------------------------------------------------------------------------
  2983. void ConvertRGBExp32ToRGBA8888( const ColorRGBExp32 *pSrc, unsigned char *pDst, Vector* _optOutLinear )
  2984. {
  2985. Vector linearColor;
  2986. // convert from ColorRGBExp32 to linear space
  2987. linearColor[0] = TexLightToLinear( ((ColorRGBExp32 *)pSrc)->r, ((ColorRGBExp32 *)pSrc)->exponent );
  2988. linearColor[1] = TexLightToLinear( ((ColorRGBExp32 *)pSrc)->g, ((ColorRGBExp32 *)pSrc)->exponent );
  2989. linearColor[2] = TexLightToLinear( ((ColorRGBExp32 *)pSrc)->b, ((ColorRGBExp32 *)pSrc)->exponent );
  2990. ConvertLinearToRGBA8888( &linearColor, pDst );
  2991. if ( _optOutLinear )
  2992. *_optOutLinear = linearColor;
  2993. }
  2994. //-----------------------------------------------------------------------------
  2995. // Converts a RGBExp32 to a linear color value.
  2996. //-----------------------------------------------------------------------------
  2997. void ConvertRGBExp32ToLinear(const ColorRGBExp32 *pSrc, Vector* pDst)
  2998. {
  2999. (*pDst)[0] = TexLightToLinear(((ColorRGBExp32 *)pSrc)->r, ((ColorRGBExp32 *)pSrc)->exponent);
  3000. (*pDst)[1] = TexLightToLinear(((ColorRGBExp32 *)pSrc)->g, ((ColorRGBExp32 *)pSrc)->exponent);
  3001. (*pDst)[2] = TexLightToLinear(((ColorRGBExp32 *)pSrc)->b, ((ColorRGBExp32 *)pSrc)->exponent);
  3002. }
  3003. //-----------------------------------------------------------------------------
  3004. // Converts a linear color value (suitable for combining linearly) to an RBGA8888 value expected by the engine.
  3005. //-----------------------------------------------------------------------------
  3006. void ConvertLinearToRGBA8888(const Vector *pSrcLinear, unsigned char *pDst)
  3007. {
  3008. Vector vertexColor;
  3009. // convert from linear space to lightmap space
  3010. // cannot use mathlib routine directly because it doesn't match
  3011. // the colorspace version found in the engine, which *is* the same sequence here
  3012. vertexColor[0] = LinearToVertexLight((*pSrcLinear)[0]);
  3013. vertexColor[1] = LinearToVertexLight((*pSrcLinear)[1]);
  3014. vertexColor[2] = LinearToVertexLight((*pSrcLinear)[2]);
  3015. // this is really a color normalization with a floor
  3016. ColorClamp(vertexColor);
  3017. // final [0..255] scale
  3018. pDst[0] = RoundFloatToByte(vertexColor[0] * 255.0f);
  3019. pDst[1] = RoundFloatToByte(vertexColor[1] * 255.0f);
  3020. pDst[2] = RoundFloatToByte(vertexColor[2] * 255.0f);
  3021. pDst[3] = 255;
  3022. }