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.

2960 lines
72 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // vrad.c
  9. #include "vrad.h"
  10. #include "physdll.h"
  11. #include "lightmap.h"
  12. #include "tier1/strtools.h"
  13. #include "vmpi.h"
  14. #include "macro_texture.h"
  15. #include "vmpi_tools_shared.h"
  16. #include "leaf_ambient_lighting.h"
  17. #include "tools_minidump.h"
  18. #include "loadcmdline.h"
  19. #include "byteswap.h"
  20. #define ALLOWDEBUGOPTIONS (0 || _DEBUG)
  21. static FileHandle_t pFpTrans = NULL;
  22. /*
  23. NOTES
  24. -----
  25. every surface must be divided into at least two patches each axis
  26. */
  27. CUtlVector<CPatch> g_Patches;
  28. CUtlVector<int> g_FacePatches; // contains all patches, children first
  29. CUtlVector<int> faceParents; // contains only root patches, use next parent to iterate
  30. CUtlVector<int> clusterChildren;
  31. CUtlVector<Vector> emitlight;
  32. CUtlVector<bumplights_t> addlight;
  33. int num_sky_cameras;
  34. sky_camera_t sky_cameras[MAX_MAP_AREAS];
  35. int area_sky_cameras[MAX_MAP_AREAS];
  36. entity_t *face_entity[MAX_MAP_FACES];
  37. Vector face_offset[MAX_MAP_FACES]; // for rotating bmodels
  38. int fakeplanes;
  39. unsigned numbounce = 100; // 25; /* Originally this was 8 */
  40. float maxchop = 4; // coarsest allowed number of luxel widths for a patch
  41. float minchop = 4; // "-chop" tightest number of luxel widths for a patch, used on edges
  42. float dispchop = 8.0f; // number of luxel widths for a patch
  43. float g_MaxDispPatchRadius = 1500.0f; // Maximum radius allowed for displacement patches
  44. qboolean g_bDumpPatches;
  45. bool bDumpNormals = false;
  46. bool g_bDumpRtEnv = false;
  47. bool bRed2Black = true;
  48. bool g_bFastAmbient = false;
  49. bool g_bNoSkyRecurse = false;
  50. bool g_bDumpPropLightmaps = false;
  51. int junk;
  52. Vector ambient( 0, 0, 0 );
  53. float lightscale = 1.0;
  54. float dlight_threshold = 0.1; // was DIRECT_LIGHT constant
  55. char source[MAX_PATH] = "";
  56. char level_name[MAX_PATH] = ""; // map filename, without extension or path info
  57. char global_lights[MAX_PATH] = "";
  58. char designer_lights[MAX_PATH] = "";
  59. char level_lights[MAX_PATH] = "";
  60. char vismatfile[_MAX_PATH] = "";
  61. char incrementfile[_MAX_PATH] = "";
  62. IIncremental *g_pIncremental = 0;
  63. bool g_bInterrupt = false; // Wsed with background lighting in WC. Tells VRAD
  64. // to stop lighting.
  65. float g_SunAngularExtent=0.0;
  66. float g_flSkySampleScale = 1.0;
  67. bool g_bLargeDispSampleRadius = false;
  68. bool g_bOnlyStaticProps = false;
  69. bool g_bShowStaticPropNormals = false;
  70. float gamma = 0.5;
  71. float indirect_sun = 1.0;
  72. float reflectivityScale = 1.0;
  73. qboolean do_extra = true;
  74. bool debug_extra = false;
  75. qboolean do_fast = false;
  76. qboolean do_centersamples = false;
  77. int extrapasses = 4;
  78. float smoothing_threshold = 0.7071067; // cos(45.0*(M_PI/180))
  79. // Cosine of smoothing angle(in radians)
  80. float coring = 1.0; // Light threshold to force to blackness(minimizes lightmaps)
  81. qboolean texscale = true;
  82. int dlight_map = 0; // Setting to 1 forces direct lighting into different lightmap than radiosity
  83. float luxeldensity = 1.0;
  84. unsigned num_degenerate_faces;
  85. qboolean g_bLowPriority = false;
  86. qboolean g_bLogHashData = false;
  87. bool g_bNoDetailLighting = false;
  88. double g_flStartTime;
  89. bool g_bStaticPropLighting = false;
  90. bool g_bStaticPropPolys = false;
  91. bool g_bTextureShadows = false;
  92. bool g_bDisablePropSelfShadowing = false;
  93. CUtlVector<byte> g_FacesVisibleToLights;
  94. RayTracingEnvironment g_RtEnv;
  95. dface_t *g_pFaces=0;
  96. // this is a list of material names used on static props which shouldn't cast shadows. a
  97. // sequential search is used since we allow substring matches. its not time critical, and this
  98. // functionality is a stopgap until vrad starts reading .vmt files.
  99. CUtlVector<char const *> g_NonShadowCastingMaterialStrings;
  100. /*
  101. ===================================================================
  102. MISC
  103. ===================================================================
  104. */
  105. int leafparents[MAX_MAP_LEAFS];
  106. int nodeparents[MAX_MAP_NODES];
  107. void MakeParents (int nodenum, int parent)
  108. {
  109. int i, j;
  110. dnode_t *node;
  111. nodeparents[nodenum] = parent;
  112. node = &dnodes[nodenum];
  113. for (i=0 ; i<2 ; i++)
  114. {
  115. j = node->children[i];
  116. if (j < 0)
  117. leafparents[-j - 1] = nodenum;
  118. else
  119. MakeParents (j, nodenum);
  120. }
  121. }
  122. /*
  123. ===================================================================
  124. TEXTURE LIGHT VALUES
  125. ===================================================================
  126. */
  127. typedef struct
  128. {
  129. char name[256];
  130. Vector value;
  131. char *filename;
  132. } texlight_t;
  133. #define MAX_TEXLIGHTS 128
  134. texlight_t texlights[MAX_TEXLIGHTS];
  135. int num_texlights;
  136. /*
  137. ============
  138. ReadLightFile
  139. ============
  140. */
  141. void ReadLightFile (char *filename)
  142. {
  143. char buf[1024];
  144. int file_texlights = 0;
  145. FileHandle_t f = g_pFileSystem->Open( filename, "r" );
  146. if (!f)
  147. {
  148. Warning("Warning: Couldn't open texlight file %s.\n", filename);
  149. return;
  150. }
  151. Msg("[Reading texlights from '%s']\n", filename);
  152. while ( CmdLib_FGets( buf, sizeof( buf ), f ) )
  153. {
  154. // check ldr/hdr
  155. char * scan = buf;
  156. if ( !strnicmp( "hdr:", scan, 4) )
  157. {
  158. scan += 4;
  159. if ( ! g_bHDR )
  160. {
  161. continue;
  162. }
  163. }
  164. if ( !strnicmp( "ldr:", scan, 4) )
  165. {
  166. scan += 4;
  167. if ( g_bHDR )
  168. {
  169. continue;
  170. }
  171. }
  172. scan += strspn( scan, " \t" );
  173. char NoShadName[1024];
  174. if ( sscanf(scan,"noshadow %s",NoShadName)==1)
  175. {
  176. char * dot = strchr( NoShadName, '.' );
  177. if ( dot ) // if they specify .vmt, kill it
  178. * dot = 0;
  179. //printf("add %s as a non shadow casting material\n",NoShadName);
  180. g_NonShadowCastingMaterialStrings.AddToTail( strdup( NoShadName ));
  181. }
  182. else if ( sscanf( scan, "forcetextureshadow %s", NoShadName ) == 1 )
  183. {
  184. //printf("add %s as a non shadow casting material\n",NoShadName);
  185. ForceTextureShadowsOnModel( NoShadName );
  186. }
  187. else
  188. {
  189. char szTexlight[256];
  190. Vector value;
  191. if ( num_texlights == MAX_TEXLIGHTS )
  192. Error ("Too many texlights, max = %d", MAX_TEXLIGHTS);
  193. int argCnt = sscanf (scan, "%s ",szTexlight );
  194. if( argCnt != 1 )
  195. {
  196. if ( strlen( scan ) > 4 )
  197. Msg( "ignoring bad texlight '%s' in %s", scan, filename );
  198. continue;
  199. }
  200. LightForString( scan + strlen( szTexlight ) + 1, value );
  201. int j = 0;
  202. for( j; j < num_texlights; j ++ )
  203. {
  204. if ( strcmp( texlights[j].name, szTexlight ) == 0 )
  205. {
  206. if ( strcmp( texlights[j].filename, filename ) == 0 )
  207. {
  208. Msg( "ERROR\a: Duplication of '%s' in file '%s'!\n",
  209. texlights[j].name, texlights[j].filename );
  210. }
  211. else if ( texlights[j].value[0] != value[0]
  212. || texlights[j].value[1] != value[1]
  213. || texlights[j].value[2] != value[2] )
  214. {
  215. Warning( "Warning: Overriding '%s' from '%s' with '%s'!\n",
  216. texlights[j].name, texlights[j].filename, filename );
  217. }
  218. else
  219. {
  220. Warning( "Warning: Redundant '%s' def in '%s' AND '%s'!\n",
  221. texlights[j].name, texlights[j].filename, filename );
  222. }
  223. break;
  224. }
  225. }
  226. strcpy( texlights[j].name, szTexlight );
  227. VectorCopy( value, texlights[j].value );
  228. texlights[j].filename = filename;
  229. file_texlights ++;
  230. num_texlights = max( num_texlights, j + 1 );
  231. }
  232. }
  233. qprintf ( "[%i texlights parsed from '%s']\n\n", file_texlights, filename);
  234. g_pFileSystem->Close( f );
  235. }
  236. /*
  237. ============
  238. LightForTexture
  239. ============
  240. */
  241. void LightForTexture( const char *name, Vector& result )
  242. {
  243. int i;
  244. result[ 0 ] = result[ 1 ] = result[ 2 ] = 0;
  245. char baseFilename[ MAX_PATH ];
  246. if ( Q_strncmp( "maps/", name, 5 ) == 0 )
  247. {
  248. // this might be a patch texture for cubemaps. try to parse out the original filename.
  249. if ( Q_strncmp( level_name, name + 5, Q_strlen( level_name ) ) == 0 )
  250. {
  251. const char *base = name + 5 + Q_strlen( level_name );
  252. if ( *base == '/' )
  253. {
  254. ++base; // step past the path separator
  255. // now we've gotten rid of the 'maps/level_name/' part, so we're left with
  256. // 'originalName_%d_%d_%d'.
  257. strcpy( baseFilename, base );
  258. bool foundSeparators = true;
  259. for ( int i=0; i<3; ++i )
  260. {
  261. char *underscore = Q_strrchr( baseFilename, '_' );
  262. if ( underscore && *underscore )
  263. {
  264. *underscore = '\0';
  265. }
  266. else
  267. {
  268. foundSeparators = false;
  269. }
  270. }
  271. if ( foundSeparators )
  272. {
  273. name = baseFilename;
  274. }
  275. }
  276. }
  277. }
  278. for (i=0 ; i<num_texlights ; i++)
  279. {
  280. if (!Q_strcasecmp (name, texlights[i].name))
  281. {
  282. VectorCopy( texlights[i].value, result );
  283. return;
  284. }
  285. }
  286. }
  287. /*
  288. =======================================================================
  289. MAKE FACES
  290. =======================================================================
  291. */
  292. /*
  293. =============
  294. WindingFromFace
  295. =============
  296. */
  297. winding_t *WindingFromFace (dface_t *f, Vector& origin )
  298. {
  299. int i;
  300. int se;
  301. dvertex_t *dv;
  302. int v;
  303. winding_t *w;
  304. w = AllocWinding (f->numedges);
  305. w->numpoints = f->numedges;
  306. for (i=0 ; i<f->numedges ; i++)
  307. {
  308. se = dsurfedges[f->firstedge + i];
  309. if (se < 0)
  310. v = dedges[-se].v[1];
  311. else
  312. v = dedges[se].v[0];
  313. dv = &dvertexes[v];
  314. VectorAdd (dv->point, origin, w->p[i]);
  315. }
  316. RemoveColinearPoints (w);
  317. return w;
  318. }
  319. /*
  320. =============
  321. BaseLightForFace
  322. =============
  323. */
  324. void BaseLightForFace( dface_t *f, Vector& light, float *parea, Vector& reflectivity )
  325. {
  326. texinfo_t *tx;
  327. dtexdata_t *texdata;
  328. //
  329. // check for light emited by texture
  330. //
  331. tx = &texinfo[f->texinfo];
  332. texdata = &dtexdata[tx->texdata];
  333. LightForTexture (TexDataStringTable_GetString( texdata->nameStringTableID ), light);
  334. *parea = texdata->height * texdata->width;
  335. VectorScale( texdata->reflectivity, reflectivityScale, reflectivity );
  336. // always keep this less than 1 or the solution will not converge
  337. for ( int i = 0; i < 3; i++ )
  338. {
  339. if ( reflectivity[i] > 0.99 )
  340. reflectivity[i] = 0.99;
  341. }
  342. }
  343. qboolean IsSky (dface_t *f)
  344. {
  345. texinfo_t *tx;
  346. tx = &texinfo[f->texinfo];
  347. if (tx->flags & SURF_SKY)
  348. return true;
  349. return false;
  350. }
  351. #ifdef STATIC_FOG
  352. /*=============
  353. IsFog
  354. =============*/
  355. qboolean IsFog( dface_t *f )
  356. {
  357. texinfo_t *tx;
  358. tx = &texinfo[f->texinfo];
  359. // % denotes a fog texture
  360. if( tx->texture[0] == '%' )
  361. return true;
  362. return false;
  363. }
  364. #endif
  365. void ProcessSkyCameras()
  366. {
  367. int i;
  368. num_sky_cameras = 0;
  369. for (i = 0; i < numareas; ++i)
  370. {
  371. area_sky_cameras[i] = -1;
  372. }
  373. for (i = 0; i < num_entities; ++i)
  374. {
  375. entity_t *e = &entities[i];
  376. const char *name = ValueForKey (e, "classname");
  377. if (stricmp (name, "sky_camera"))
  378. continue;
  379. Vector origin;
  380. GetVectorForKey( e, "origin", origin );
  381. int node = PointLeafnum( origin );
  382. int area = -1;
  383. if (node >= 0 && node < numleafs) area = dleafs[node].area;
  384. float scale = FloatForKey( e, "scale" );
  385. if (scale > 0.0f)
  386. {
  387. sky_cameras[num_sky_cameras].origin = origin;
  388. sky_cameras[num_sky_cameras].sky_to_world = scale;
  389. sky_cameras[num_sky_cameras].world_to_sky = 1.0f / scale;
  390. sky_cameras[num_sky_cameras].area = area;
  391. if (area >= 0 && area < numareas)
  392. {
  393. area_sky_cameras[area] = num_sky_cameras;
  394. }
  395. ++num_sky_cameras;
  396. }
  397. }
  398. }
  399. /*
  400. =============
  401. MakePatchForFace
  402. =============
  403. */
  404. float totalarea;
  405. void MakePatchForFace (int fn, winding_t *w)
  406. {
  407. dface_t *f = g_pFaces + fn;
  408. float area;
  409. CPatch *patch;
  410. Vector centroid(0,0,0);
  411. int i, j;
  412. texinfo_t *tx;
  413. // get texture info
  414. tx = &texinfo[f->texinfo];
  415. // No patches at all for fog!
  416. #ifdef STATIC_FOG
  417. if ( IsFog( f ) )
  418. return;
  419. #endif
  420. // the sky needs patches or the form factors don't work out correctly
  421. // if (IsSky( f ) )
  422. // return;
  423. area = WindingArea (w);
  424. if (area <= 0)
  425. {
  426. num_degenerate_faces++;
  427. // Msg("degenerate face\n");
  428. return;
  429. }
  430. totalarea += area;
  431. // get a patch
  432. int ndxPatch = g_Patches.AddToTail();
  433. patch = &g_Patches[ndxPatch];
  434. memset( patch, 0, sizeof( CPatch ) );
  435. patch->ndxNext = g_Patches.InvalidIndex();
  436. patch->ndxNextParent = g_Patches.InvalidIndex();
  437. patch->ndxNextClusterChild = g_Patches.InvalidIndex();
  438. patch->child1 = g_Patches.InvalidIndex();
  439. patch->child2 = g_Patches.InvalidIndex();
  440. patch->parent = g_Patches.InvalidIndex();
  441. patch->needsBumpmap = tx->flags & SURF_BUMPLIGHT ? true : false;
  442. // link and save patch data
  443. patch->ndxNext = g_FacePatches.Element( fn );
  444. g_FacePatches[fn] = ndxPatch;
  445. // patch->next = face_g_Patches[fn];
  446. // face_g_Patches[fn] = patch;
  447. // compute a separate scale for chop - since the patch "scale" is the texture scale
  448. // we want textures with higher resolution lighting to be chopped up more
  449. float chopscale[2];
  450. chopscale[0] = chopscale[1] = 16.0f;
  451. if ( texscale )
  452. {
  453. // Compute the texture "scale" in s,t
  454. for( i=0; i<2; i++ )
  455. {
  456. patch->scale[i] = 0.0f;
  457. chopscale[i] = 0.0f;
  458. for( j=0; j<3; j++ )
  459. {
  460. patch->scale[i] +=
  461. tx->textureVecsTexelsPerWorldUnits[i][j] *
  462. tx->textureVecsTexelsPerWorldUnits[i][j];
  463. chopscale[i] +=
  464. tx->lightmapVecsLuxelsPerWorldUnits[i][j] *
  465. tx->lightmapVecsLuxelsPerWorldUnits[i][j];
  466. }
  467. patch->scale[i] = sqrt( patch->scale[i] );
  468. chopscale[i] = sqrt( chopscale[i] );
  469. }
  470. }
  471. else
  472. {
  473. patch->scale[0] = patch->scale[1] = 1.0f;
  474. }
  475. patch->area = area;
  476. patch->sky = IsSky( f );
  477. // chop scaled up lightmaps coarser
  478. patch->luxscale = ((chopscale[0]+chopscale[1])/2);
  479. patch->chop = maxchop;
  480. #ifdef STATIC_FOG
  481. patch->fog = FALSE;
  482. #endif
  483. patch->winding = w;
  484. patch->plane = &dplanes[f->planenum];
  485. // make a new plane to adjust for origined bmodels
  486. if (face_offset[fn][0] || face_offset[fn][1] || face_offset[fn][2] )
  487. {
  488. dplane_t *pl;
  489. // origin offset faces must create new planes
  490. if (numplanes + fakeplanes >= MAX_MAP_PLANES)
  491. {
  492. Error ("numplanes + fakeplanes >= MAX_MAP_PLANES");
  493. }
  494. pl = &dplanes[numplanes + fakeplanes];
  495. fakeplanes++;
  496. *pl = *(patch->plane);
  497. pl->dist += DotProduct (face_offset[fn], pl->normal);
  498. patch->plane = pl;
  499. }
  500. patch->faceNumber = fn;
  501. WindingCenter (w, patch->origin);
  502. // Save "center" for generating the face normals later.
  503. VectorSubtract( patch->origin, face_offset[fn], face_centroids[fn] );
  504. VectorCopy( patch->plane->normal, patch->normal );
  505. WindingBounds (w, patch->face_mins, patch->face_maxs);
  506. VectorCopy( patch->face_mins, patch->mins );
  507. VectorCopy( patch->face_maxs, patch->maxs );
  508. BaseLightForFace( f, patch->baselight, &patch->basearea, patch->reflectivity );
  509. // Chop all texlights very fine.
  510. if ( !VectorCompare( patch->baselight, vec3_origin ) )
  511. {
  512. // patch->chop = do_extra ? maxchop / 2 : maxchop;
  513. tx->flags |= SURF_LIGHT;
  514. }
  515. // get rid of do extra functionality on displacement surfaces
  516. if( ValidDispFace( f ) )
  517. {
  518. patch->chop = maxchop;
  519. }
  520. // FIXME: If we wanted to add a dependency from vrad to the material system,
  521. // we could do this. It would add a bunch of file accesses, though:
  522. /*
  523. // Check for a material var which would override the patch chop
  524. bool bFound;
  525. const char *pMaterialName = TexDataStringTable_GetString( dtexdata[ tx->texdata ].nameStringTableID );
  526. MaterialSystemMaterial_t hMaterial = FindMaterial( pMaterialName, &bFound, false );
  527. if ( bFound )
  528. {
  529. const char *pChopValue = GetMaterialVar( hMaterial, "%chop" );
  530. if ( pChopValue )
  531. {
  532. float flChopValue;
  533. if ( sscanf( pChopValue, "%f", &flChopValue ) > 0 )
  534. {
  535. patch->chop = flChopValue;
  536. }
  537. }
  538. }
  539. */
  540. }
  541. entity_t *EntityForModel (int modnum)
  542. {
  543. int i;
  544. char *s;
  545. char name[16];
  546. sprintf (name, "*%i", modnum);
  547. // search the entities for one using modnum
  548. for (i=0 ; i<num_entities ; i++)
  549. {
  550. s = ValueForKey (&entities[i], "model");
  551. if (!strcmp (s, name))
  552. return &entities[i];
  553. }
  554. return &entities[0];
  555. }
  556. /*
  557. =============
  558. MakePatches
  559. =============
  560. */
  561. void MakePatches (void)
  562. {
  563. int i, j;
  564. dface_t *f;
  565. int fn;
  566. winding_t *w;
  567. dmodel_t *mod;
  568. Vector origin;
  569. entity_t *ent;
  570. ParseEntities ();
  571. qprintf ("%i faces\n", numfaces);
  572. for (i=0 ; i<nummodels ; i++)
  573. {
  574. mod = dmodels+i;
  575. ent = EntityForModel (i);
  576. VectorCopy (vec3_origin, origin);
  577. // bmodels with origin brushes need to be offset into their
  578. // in-use position
  579. GetVectorForKey (ent, "origin", origin);
  580. for (j=0 ; j<mod->numfaces ; j++)
  581. {
  582. fn = mod->firstface + j;
  583. face_entity[fn] = ent;
  584. VectorCopy (origin, face_offset[fn]);
  585. f = &g_pFaces[fn];
  586. if( f->dispinfo == -1 )
  587. {
  588. w = WindingFromFace (f, origin );
  589. MakePatchForFace( fn, w );
  590. }
  591. }
  592. }
  593. if (num_degenerate_faces > 0)
  594. {
  595. qprintf("%d degenerate faces\n", num_degenerate_faces );
  596. }
  597. qprintf ("%i square feet [%.2f square inches]\n", (int)(totalarea/144), totalarea );
  598. // make the displacement surface patches
  599. StaticDispMgr()->MakePatches();
  600. }
  601. /*
  602. =======================================================================
  603. SUBDIVIDE
  604. =======================================================================
  605. */
  606. //-----------------------------------------------------------------------------
  607. // Purpose: does this surface take/emit light
  608. //-----------------------------------------------------------------------------
  609. bool PreventSubdivision( CPatch *patch )
  610. {
  611. dface_t *f = g_pFaces + patch->faceNumber;
  612. texinfo_t *tx = &texinfo[f->texinfo];
  613. if (tx->flags & SURF_NOCHOP)
  614. return true;
  615. if (tx->flags & SURF_NOLIGHT && !(tx->flags & SURF_LIGHT))
  616. return true;
  617. return false;
  618. }
  619. //-----------------------------------------------------------------------------
  620. // Purpose: subdivide the "parent" patch
  621. //-----------------------------------------------------------------------------
  622. int CreateChildPatch( int nParentIndex, winding_t *pWinding, float flArea, const Vector &vecCenter )
  623. {
  624. int nChildIndex = g_Patches.AddToTail();
  625. CPatch *child = &g_Patches[nChildIndex];
  626. CPatch *parent = &g_Patches[nParentIndex];
  627. // copy all elements of parent patch to children
  628. *child = *parent;
  629. // Set up links
  630. child->ndxNext = g_Patches.InvalidIndex();
  631. child->ndxNextParent = g_Patches.InvalidIndex();
  632. child->ndxNextClusterChild = g_Patches.InvalidIndex();
  633. child->child1 = g_Patches.InvalidIndex();
  634. child->child2 = g_Patches.InvalidIndex();
  635. child->parent = nParentIndex;
  636. child->m_IterationKey = 0;
  637. child->winding = pWinding;
  638. child->area = flArea;
  639. VectorCopy( vecCenter, child->origin );
  640. if ( ValidDispFace( g_pFaces + child->faceNumber ) )
  641. {
  642. // shouldn't get here anymore!!
  643. Msg( "SubdividePatch: Error - Should not be here!\n" );
  644. StaticDispMgr()->GetDispSurfNormal( child->faceNumber, child->origin, child->normal, true );
  645. }
  646. else
  647. {
  648. GetPhongNormal( child->faceNumber, child->origin, child->normal );
  649. }
  650. child->planeDist = child->plane->dist;
  651. WindingBounds(child->winding, child->mins, child->maxs);
  652. if ( !VectorCompare( child->baselight, vec3_origin ) )
  653. {
  654. // don't check edges on surf lights
  655. return nChildIndex;
  656. }
  657. // Subdivide patch towards minchop if on the edge of the face
  658. Vector total;
  659. VectorSubtract( child->maxs, child->mins, total );
  660. VectorScale( total, child->luxscale, total );
  661. if ( child->chop > minchop && (total[0] < child->chop) && (total[1] < child->chop) && (total[2] < child->chop) )
  662. {
  663. for ( int i=0; i<3; ++i )
  664. {
  665. if ( (child->face_maxs[i] == child->maxs[i] || child->face_mins[i] == child->mins[i] )
  666. && total[i] > minchop )
  667. {
  668. child->chop = max( minchop, child->chop / 2 );
  669. break;
  670. }
  671. }
  672. }
  673. return nChildIndex;
  674. }
  675. //-----------------------------------------------------------------------------
  676. // Purpose: subdivide the "parent" patch
  677. //-----------------------------------------------------------------------------
  678. void SubdividePatch( int ndxPatch )
  679. {
  680. winding_t *w, *o1, *o2;
  681. Vector total;
  682. Vector split;
  683. vec_t dist;
  684. vec_t widest = -1;
  685. int i, widest_axis = -1;
  686. bool bSubdivide = false;
  687. // get the current patch
  688. CPatch *patch = &g_Patches.Element( ndxPatch );
  689. if ( !patch )
  690. return;
  691. // never subdivide sky patches
  692. if ( patch->sky )
  693. return;
  694. // get the patch winding
  695. w = patch->winding;
  696. // subdivide along the widest axis
  697. VectorSubtract (patch->maxs, patch->mins, total);
  698. VectorScale( total, patch->luxscale, total );
  699. for (i=0 ; i<3 ; i++)
  700. {
  701. if ( total[i] > widest )
  702. {
  703. widest_axis = i;
  704. widest = total[i];
  705. }
  706. if ( (total[i] >= patch->chop) && (total[i] >= minchop) )
  707. {
  708. bSubdivide = true;
  709. }
  710. }
  711. if ((!bSubdivide) && widest_axis != -1)
  712. {
  713. // make more square
  714. if (total[widest_axis] > total[(widest_axis + 1) % 3] * 2 && total[widest_axis] > total[(widest_axis + 2) % 3] * 2)
  715. {
  716. if (patch->chop > minchop)
  717. {
  718. bSubdivide = true;
  719. patch->chop = max( minchop, patch->chop / 2 );
  720. }
  721. }
  722. }
  723. if ( !bSubdivide )
  724. return;
  725. // split the winding
  726. VectorCopy (vec3_origin, split);
  727. split[widest_axis] = 1;
  728. dist = (patch->mins[widest_axis] + patch->maxs[widest_axis])*0.5f;
  729. ClipWindingEpsilon (w, split, dist, ON_EPSILON, &o1, &o2);
  730. // calculate the area of the patches to see if they are "significant"
  731. Vector center1, center2;
  732. float area1 = WindingAreaAndBalancePoint( o1, center1 );
  733. float area2 = WindingAreaAndBalancePoint( o2, center2 );
  734. if( area1 == 0 || area2 == 0 )
  735. {
  736. Msg( "zero area child patch\n" );
  737. return;
  738. }
  739. // create new child patches
  740. int ndxChild1Patch = CreateChildPatch( ndxPatch, o1, area1, center1 );
  741. int ndxChild2Patch = CreateChildPatch( ndxPatch, o2, area2, center2 );
  742. // FIXME: This could go into CreateChildPatch if child1, child2 were stored in the patch as child[0], child[1]
  743. patch = &g_Patches.Element( ndxPatch );
  744. patch->child1 = ndxChild1Patch;
  745. patch->child2 = ndxChild2Patch;
  746. SubdividePatch( ndxChild1Patch );
  747. SubdividePatch( ndxChild2Patch );
  748. }
  749. /*
  750. =============
  751. SubdividePatches
  752. =============
  753. */
  754. void SubdividePatches (void)
  755. {
  756. unsigned i, num;
  757. if (numbounce == 0)
  758. return;
  759. unsigned int uiPatchCount = g_Patches.Size();
  760. qprintf ("%i patches before subdivision\n", uiPatchCount);
  761. for (i = 0; i < uiPatchCount; i++)
  762. {
  763. CPatch *pCur = &g_Patches.Element( i );
  764. pCur->planeDist = pCur->plane->dist;
  765. pCur->ndxNextParent = faceParents.Element( pCur->faceNumber );
  766. faceParents[pCur->faceNumber] = pCur - g_Patches.Base();
  767. }
  768. for (i=0 ; i< uiPatchCount; i++)
  769. {
  770. CPatch *patch = &g_Patches.Element( i );
  771. patch->parent = -1;
  772. if ( PreventSubdivision(patch) )
  773. continue;
  774. if (!do_fast)
  775. {
  776. if( g_pFaces[patch->faceNumber].dispinfo == -1 )
  777. {
  778. SubdividePatch( i );
  779. }
  780. else
  781. {
  782. StaticDispMgr()->SubdividePatch( i );
  783. }
  784. }
  785. }
  786. // fixup next pointers
  787. for (i = 0; i < (unsigned)numfaces; i++)
  788. {
  789. g_FacePatches[i] = g_FacePatches.InvalidIndex();
  790. }
  791. uiPatchCount = g_Patches.Size();
  792. for (i = 0; i < uiPatchCount; i++)
  793. {
  794. CPatch *pCur = &g_Patches.Element( i );
  795. pCur->ndxNext = g_FacePatches.Element( pCur->faceNumber );
  796. g_FacePatches[pCur->faceNumber] = pCur - g_Patches.Base();
  797. #if 0
  798. CPatch *prev;
  799. prev = face_g_Patches[g_Patches[i].faceNumber];
  800. g_Patches[i].next = prev;
  801. face_g_Patches[g_Patches[i].faceNumber] = &g_Patches[i];
  802. #endif
  803. }
  804. // Cache off the leaf number:
  805. // We have to do this after subdivision because some patches span leaves.
  806. // (only the faces for model #0 are split by it's BSP which is what governs the PVS, and the leaves we're interested in)
  807. // Sub models (1-255) are only split for the BSP that their model forms.
  808. // When those patches are subdivided their origins can end up in a different leaf.
  809. // The engine will split (clip) those faces at run time to the world BSP because the models
  810. // are dynamic and can be moved. In the software renderer, they must be split exactly in order
  811. // to sort per polygon.
  812. for ( i = 0; i < uiPatchCount; i++ )
  813. {
  814. g_Patches[i].clusterNumber = ClusterFromPoint( g_Patches[i].origin );
  815. //
  816. // test for point in solid space (can happen with detail and displacement surfaces)
  817. //
  818. if( g_Patches[i].clusterNumber == -1 )
  819. {
  820. for( int j = 0; j < g_Patches[i].winding->numpoints; j++ )
  821. {
  822. int clusterNumber = ClusterFromPoint( g_Patches[i].winding->p[j] );
  823. if( clusterNumber != -1 )
  824. {
  825. g_Patches[i].clusterNumber = clusterNumber;
  826. break;
  827. }
  828. }
  829. }
  830. }
  831. // build the list of patches that need to be lit
  832. for ( num = 0; num < uiPatchCount; num++ )
  833. {
  834. // do them in reverse order
  835. i = uiPatchCount - num - 1;
  836. // skip patches with children
  837. CPatch *pCur = &g_Patches.Element( i );
  838. if( pCur->child1 == g_Patches.InvalidIndex() )
  839. {
  840. if( pCur->clusterNumber != - 1 )
  841. {
  842. pCur->ndxNextClusterChild = clusterChildren.Element( pCur->clusterNumber );
  843. clusterChildren[pCur->clusterNumber] = pCur - g_Patches.Base();
  844. }
  845. }
  846. #if 0
  847. if (g_Patches[i].child1 == g_Patches.InvalidIndex() )
  848. {
  849. if( g_Patches[i].clusterNumber != -1 )
  850. {
  851. g_Patches[i].nextclusterchild = cluster_children[g_Patches[i].clusterNumber];
  852. cluster_children[g_Patches[i].clusterNumber] = &g_Patches[i];
  853. }
  854. }
  855. #endif
  856. }
  857. qprintf ("%i patches after subdivision\n", uiPatchCount);
  858. }
  859. //=====================================================================
  860. /*
  861. =============
  862. MakeScales
  863. This is the primary time sink.
  864. It can be run multi threaded.
  865. =============
  866. */
  867. int total_transfer;
  868. int max_transfer;
  869. //-----------------------------------------------------------------------------
  870. // Purpose: Computes the form factor from a polygon patch to a differential patch
  871. // using formula 81 of Philip Dutre's Global Illumination Compendium,
  872. // [email protected], http://www.graphics.cornell.edu/~phil/GI/
  873. //-----------------------------------------------------------------------------
  874. float FormFactorPolyToDiff ( CPatch *pPolygon, CPatch* pDifferential )
  875. {
  876. winding_t *pWinding = pPolygon->winding;
  877. float flFormFactor = 0.0f;
  878. for ( int iPoint = 0; iPoint < pWinding->numpoints; iPoint++ )
  879. {
  880. int iNextPoint = ( iPoint < pWinding->numpoints - 1 ) ? iPoint + 1 : 0;
  881. Vector vGammaVector, vVector1, vVector2;
  882. VectorSubtract( pWinding->p[ iPoint ], pDifferential->origin, vVector1 );
  883. VectorSubtract( pWinding->p[ iNextPoint ], pDifferential->origin, vVector2 );
  884. VectorNormalize( vVector1 );
  885. VectorNormalize( vVector2 );
  886. CrossProduct( vVector1, vVector2, vGammaVector );
  887. float flSinAlpha = VectorNormalize( vGammaVector );
  888. if (flSinAlpha < -1.0f || flSinAlpha > 1.0f)
  889. return 0.0f;
  890. vGammaVector *= asin( flSinAlpha );
  891. flFormFactor += DotProduct( vGammaVector, pDifferential->normal );
  892. }
  893. flFormFactor *= ( 0.5f / pPolygon->area ); // divide by pi later, multiply by area later
  894. return flFormFactor;
  895. }
  896. //-----------------------------------------------------------------------------
  897. // Purpose: Computes the form factor from a differential element to a differential
  898. // element. This is okay when the distance between patches is 5 times
  899. // greater than patch size. Lecture slides by Pat Hanrahan,
  900. // http://graphics.stanford.edu/courses/cs348b-00/lectures/lecture17/radiosity.2.pdf
  901. //-----------------------------------------------------------------------------
  902. float FormFactorDiffToDiff ( CPatch *pDiff1, CPatch* pDiff2 )
  903. {
  904. Vector vDelta;
  905. VectorSubtract( pDiff1->origin, pDiff2->origin, vDelta );
  906. float flLength = VectorNormalize( vDelta );
  907. return -DotProduct( vDelta, pDiff1->normal ) * DotProduct( vDelta, pDiff2->normal ) / ( flLength * flLength );
  908. }
  909. void MakeTransfer( int ndxPatch1, int ndxPatch2, transfer_t *all_transfers )
  910. //void MakeTransfer (CPatch *patch, CPatch *patch2, transfer_t *all_transfers )
  911. {
  912. Vector delta;
  913. vec_t scale;
  914. float trans;
  915. transfer_t *transfer;
  916. //
  917. // get patches
  918. //
  919. if( ndxPatch1 == g_Patches.InvalidIndex() || ndxPatch2 == g_Patches.InvalidIndex() )
  920. return;
  921. CPatch *pPatch1 = &g_Patches.Element( ndxPatch1 );
  922. CPatch *pPatch2 = &g_Patches.Element( ndxPatch2 );
  923. if (IsSky( &g_pFaces[ pPatch2->faceNumber ] ) )
  924. return;
  925. // overflow check!
  926. if ( pPatch1->numtransfers >= MAX_PATCHES)
  927. {
  928. return;
  929. }
  930. // hack for patch areas that area <= 0 (degenerate)
  931. if ( pPatch2->area <= 0)
  932. {
  933. return;
  934. }
  935. transfer = &all_transfers[pPatch1->numtransfers];
  936. scale = FormFactorDiffToDiff( pPatch2, pPatch1 );
  937. // patch normals may be > 90 due to smoothing groups
  938. if (scale <= 0)
  939. {
  940. //Msg("scale <= 0\n");
  941. return;
  942. }
  943. // Test 5 times rule
  944. Vector vDelta;
  945. VectorSubtract( pPatch1->origin, pPatch2->origin, vDelta );
  946. float flThreshold = ( M_PI * 0.04 ) * DotProduct( vDelta, vDelta );
  947. if (flThreshold < pPatch2->area)
  948. {
  949. scale = FormFactorPolyToDiff( pPatch2, pPatch1 );
  950. if (scale <= 0.0)
  951. return;
  952. }
  953. trans = (pPatch2->area*scale);
  954. if (trans <= TRANSFER_EPSILON)
  955. {
  956. return;
  957. }
  958. transfer->patch = pPatch2 - g_Patches.Base();
  959. // FIXME: why is this not trans?
  960. transfer->transfer = trans;
  961. #if 0
  962. // DEBUG! Dump patches and transfer connection for displacements. This creates a lot of data, so only
  963. // use it when you really want it - that is why it is #if-ed out.
  964. if ( g_bDumpPatches )
  965. {
  966. if ( !pFpTrans )
  967. {
  968. pFpTrans = g_pFileSystem->Open( "trans.txt", "w" );
  969. }
  970. Vector light = pPatch1->totallight.light[0] + pPatch1->directlight;
  971. WriteWinding( pFpTrans, pPatch1->winding, light );
  972. light = pPatch2->totallight.light[0] + pPatch2->directlight;
  973. WriteWinding( pFpTrans, pPatch2->winding, light );
  974. WriteLine( pFpTrans, pPatch1->origin, pPatch2->origin, Vector( 255, 0, 255 ) );
  975. }
  976. #endif
  977. pPatch1->numtransfers++;
  978. }
  979. void MakeScales ( int ndxPatch, transfer_t *all_transfers )
  980. {
  981. int j;
  982. float total;
  983. transfer_t *t, *t2;
  984. total = 0;
  985. if( ndxPatch == g_Patches.InvalidIndex() )
  986. return;
  987. CPatch *patch = &g_Patches.Element( ndxPatch );
  988. // copy the transfers out
  989. if (patch->numtransfers)
  990. {
  991. if (patch->numtransfers > max_transfer)
  992. {
  993. max_transfer = patch->numtransfers;
  994. }
  995. patch->transfers = ( transfer_t* )calloc (1, patch->numtransfers * sizeof(transfer_t));
  996. if (!patch->transfers)
  997. Error ("Memory allocation failure");
  998. // get total transfer energy
  999. t2 = all_transfers;
  1000. // overflow check!
  1001. for (j=0 ; j<patch->numtransfers ; j++, t2++)
  1002. {
  1003. total += t2->transfer;
  1004. }
  1005. // the total transfer should be PI, but we need to correct errors due to overlaping surfaces
  1006. if (total > M_PI)
  1007. total = 1.0f/total;
  1008. else
  1009. total = 1.0f/M_PI;
  1010. t = patch->transfers;
  1011. t2 = all_transfers;
  1012. for (j=0 ; j<patch->numtransfers ; j++, t++, t2++)
  1013. {
  1014. t->transfer = t2->transfer*total;
  1015. t->patch = t2->patch;
  1016. }
  1017. if (patch->numtransfers > max_transfer)
  1018. {
  1019. max_transfer = patch->numtransfers;
  1020. }
  1021. }
  1022. else
  1023. {
  1024. // Error - patch has no transfers
  1025. // patch->totallight[2] = 255;
  1026. }
  1027. ThreadLock ();
  1028. total_transfer += patch->numtransfers;
  1029. ThreadUnlock ();
  1030. }
  1031. /*
  1032. =============
  1033. WriteWorld
  1034. =============
  1035. */
  1036. void WriteWorld (char *name, int iBump)
  1037. {
  1038. unsigned j;
  1039. FileHandle_t out;
  1040. CPatch *patch;
  1041. out = g_pFileSystem->Open( name, "w" );
  1042. if (!out)
  1043. Error ("Couldn't open %s", name);
  1044. unsigned int uiPatchCount = g_Patches.Size();
  1045. for (j=0; j<uiPatchCount; j++)
  1046. {
  1047. patch = &g_Patches.Element( j );
  1048. // skip parent patches
  1049. if (patch->child1 != g_Patches.InvalidIndex() )
  1050. continue;
  1051. if( patch->clusterNumber == -1 )
  1052. {
  1053. Vector vGreen;
  1054. VectorClear( vGreen );
  1055. vGreen[1] = 256.0f;
  1056. WriteWinding( out, patch->winding, vGreen );
  1057. }
  1058. else
  1059. {
  1060. Vector light = patch->totallight.light[iBump] + patch->directlight;
  1061. WriteWinding( out, patch->winding, light );
  1062. if( bDumpNormals )
  1063. {
  1064. WriteNormal( out, patch->origin, patch->plane->normal, 15.0f, patch->plane->normal * 255.0f );
  1065. }
  1066. }
  1067. }
  1068. g_pFileSystem->Close( out );
  1069. }
  1070. void WriteRTEnv (char *name)
  1071. {
  1072. FileHandle_t out;
  1073. out = g_pFileSystem->Open( name, "w" );
  1074. if (!out)
  1075. Error ("Couldn't open %s", name);
  1076. winding_t *triw = AllocWinding( 3 );
  1077. triw->numpoints = 3;
  1078. for( int i = 0; i < g_RtEnv.OptimizedTriangleList.Size(); i++ )
  1079. {
  1080. triw->p[0] = g_RtEnv.OptimizedTriangleList[i].Vertex( 0);
  1081. triw->p[1] = g_RtEnv.OptimizedTriangleList[i].Vertex( 1);
  1082. triw->p[2] = g_RtEnv.OptimizedTriangleList[i].Vertex( 2);
  1083. int id = g_RtEnv.OptimizedTriangleList[i].m_Data.m_GeometryData.m_nTriangleID;
  1084. Vector color(0, 0, 0);
  1085. if (id & TRACE_ID_OPAQUE) color.Init(0, 255, 0);
  1086. if (id & TRACE_ID_SKY) color.Init(0, 0, 255);
  1087. if (id & TRACE_ID_STATICPROP) color.Init(255, 0, 0);
  1088. WriteWinding(out, triw, color);
  1089. }
  1090. FreeWinding(triw);
  1091. g_pFileSystem->Close( out );
  1092. }
  1093. void WriteWinding (FileHandle_t out, winding_t *w, Vector& color )
  1094. {
  1095. int i;
  1096. CmdLib_FPrintf (out, "%i\n", w->numpoints);
  1097. for (i=0 ; i<w->numpoints ; i++)
  1098. {
  1099. CmdLib_FPrintf (out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
  1100. w->p[i][0],
  1101. w->p[i][1],
  1102. w->p[i][2],
  1103. color[ 0 ] / 256,
  1104. color[ 1 ] / 256,
  1105. color[ 2 ] / 256 );
  1106. }
  1107. }
  1108. void WriteNormal( FileHandle_t out, Vector const &nPos, Vector const &nDir,
  1109. float length, Vector const &color )
  1110. {
  1111. CmdLib_FPrintf( out, "2\n" );
  1112. CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
  1113. nPos.x, nPos.y, nPos.z,
  1114. color.x / 256, color.y / 256, color.z / 256 );
  1115. CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
  1116. nPos.x + ( nDir.x * length ),
  1117. nPos.y + ( nDir.y * length ),
  1118. nPos.z + ( nDir.z * length ),
  1119. color.x / 256, color.y / 256, color.z / 256 );
  1120. }
  1121. void WriteLine( FileHandle_t out, const Vector &vecPos1, const Vector &vecPos2, const Vector &color )
  1122. {
  1123. CmdLib_FPrintf( out, "2\n" );
  1124. CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
  1125. vecPos1.x, vecPos1.y, vecPos1.z,
  1126. color.x / 256, color.y / 256, color.z / 256 );
  1127. CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
  1128. vecPos2.x, vecPos2.y, vecPos2.z,
  1129. color.x / 256, color.y / 256, color.z / 256 );
  1130. }
  1131. void WriteTrace( const char *pFileName, const FourRays &rays, const RayTracingResult& result )
  1132. {
  1133. FileHandle_t out;
  1134. out = g_pFileSystem->Open( pFileName, "a" );
  1135. if (!out)
  1136. Error ("Couldn't open %s", pFileName);
  1137. // Draws rays
  1138. for ( int i = 0; i < 4; ++i )
  1139. {
  1140. Vector vecOrigin = rays.origin.Vec(i);
  1141. Vector vecEnd = rays.direction.Vec(i);
  1142. VectorNormalize( vecEnd );
  1143. vecEnd *= SubFloat( result.HitDistance, i );
  1144. vecEnd += vecOrigin;
  1145. WriteLine( out, vecOrigin, vecEnd, Vector( 256, 0, 0 ) );
  1146. WriteNormal( out, vecEnd, result.surface_normal.Vec(i), 10.0f, Vector( 256, 265, 0 ) );
  1147. }
  1148. g_pFileSystem->Close( out );
  1149. }
  1150. /*
  1151. =============
  1152. CollectLight
  1153. =============
  1154. */
  1155. // patch's totallight += new light received to each patch
  1156. // patch's emitlight = addlight (newly received light from GatherLight)
  1157. // patch's addlight = 0
  1158. // pull received light from children.
  1159. void CollectLight( Vector& total )
  1160. {
  1161. int i, j;
  1162. CPatch *patch;
  1163. VectorFill( total, 0 );
  1164. // process patches in reverse order so that children are processed before their parents
  1165. unsigned int uiPatchCount = g_Patches.Size();
  1166. for( i = uiPatchCount - 1; i >= 0; i-- )
  1167. {
  1168. patch = &g_Patches.Element( i );
  1169. int normalCount = patch->needsBumpmap ? NUM_BUMP_VECTS+1 : 1;
  1170. // sky's never collect light, it is just dropped
  1171. if (patch->sky)
  1172. {
  1173. VectorFill( emitlight[ i ], 0 );
  1174. }
  1175. else if ( patch->child1 == g_Patches.InvalidIndex() )
  1176. {
  1177. // This is a leaf node.
  1178. for ( j = 0; j < normalCount; j++ )
  1179. {
  1180. VectorAdd( patch->totallight.light[j], addlight[i].light[j], patch->totallight.light[j] );
  1181. }
  1182. VectorCopy( addlight[i].light[0], emitlight[i] );
  1183. VectorAdd( total, emitlight[i], total );
  1184. }
  1185. else
  1186. {
  1187. // This is an interior node.
  1188. // Pull received light from children.
  1189. float s1, s2;
  1190. CPatch *child1;
  1191. CPatch *child2;
  1192. child1 = &g_Patches[patch->child1];
  1193. child2 = &g_Patches[patch->child2];
  1194. // BUG: This doesn't do anything?
  1195. if ((int)patch->area != (int)(child1->area + child2->area))
  1196. s1 = 0;
  1197. s1 = child1->area / (child1->area + child2->area);
  1198. s2 = child2->area / (child1->area + child2->area);
  1199. // patch->totallight = s1 * child1->totallight + s2 * child2->totallight
  1200. for ( j = 0; j < normalCount; j++ )
  1201. {
  1202. VectorScale( child1->totallight.light[j], s1, patch->totallight.light[j] );
  1203. VectorMA( patch->totallight.light[j], s2, child2->totallight.light[j], patch->totallight.light[j] );
  1204. }
  1205. // patch->emitlight = s1 * child1->emitlight + s2 * child2->emitlight
  1206. VectorScale( emitlight[patch->child1], s1, emitlight[i] );
  1207. VectorMA( emitlight[i], s2, emitlight[patch->child2], emitlight[i] );
  1208. }
  1209. for ( j = 0; j < NUM_BUMP_VECTS+1; j++ )
  1210. {
  1211. VectorFill( addlight[ i ].light[j], 0 );
  1212. }
  1213. }
  1214. }
  1215. /*
  1216. =============
  1217. GatherLight
  1218. Get light from other patches
  1219. Run multi-threaded
  1220. =============
  1221. */
  1222. #ifdef _WIN32
  1223. #pragma warning (disable:4701)
  1224. #endif
  1225. extern void GetBumpNormals( const float* sVect, const float* tVect, const Vector& flatNormal,
  1226. const Vector& phongNormal, Vector bumpNormals[NUM_BUMP_VECTS] );
  1227. void PreGetBumpNormalsForDisp( texinfo_t *pTexinfo, Vector &vecU, Vector &vecV, Vector &vecNormal )
  1228. {
  1229. Vector vecTexU( pTexinfo->textureVecsTexelsPerWorldUnits[0][0], pTexinfo->textureVecsTexelsPerWorldUnits[0][1], pTexinfo->textureVecsTexelsPerWorldUnits[0][2] );
  1230. Vector vecTexV( pTexinfo->textureVecsTexelsPerWorldUnits[1][0], pTexinfo->textureVecsTexelsPerWorldUnits[1][1], pTexinfo->textureVecsTexelsPerWorldUnits[1][2] );
  1231. Vector vecLightU( pTexinfo->lightmapVecsLuxelsPerWorldUnits[0][0], pTexinfo->lightmapVecsLuxelsPerWorldUnits[0][1], pTexinfo->lightmapVecsLuxelsPerWorldUnits[0][2] );
  1232. Vector vecLightV( pTexinfo->lightmapVecsLuxelsPerWorldUnits[1][0], pTexinfo->lightmapVecsLuxelsPerWorldUnits[1][1], pTexinfo->lightmapVecsLuxelsPerWorldUnits[1][2] );
  1233. VectorNormalize( vecTexU );
  1234. VectorNormalize( vecTexV );
  1235. VectorNormalize( vecLightU );
  1236. VectorNormalize( vecLightV );
  1237. bool bDoConversion = false;
  1238. if ( fabs( vecTexU.Dot( vecLightU ) ) < 0.999f )
  1239. {
  1240. bDoConversion = true;
  1241. }
  1242. if ( fabs( vecTexV.Dot( vecLightV ) ) < 0.999f )
  1243. {
  1244. bDoConversion = true;
  1245. }
  1246. if ( bDoConversion )
  1247. {
  1248. matrix3x4_t matTex( vecTexU, vecTexV, vecNormal, vec3_origin );
  1249. matrix3x4_t matLight( vecLightU, vecLightV, vecNormal, vec3_origin );
  1250. matrix3x4_t matTmp;
  1251. ConcatTransforms ( matLight, matTex, matTmp );
  1252. MatrixGetColumn( matTmp, 0, vecU );
  1253. MatrixGetColumn( matTmp, 1, vecV );
  1254. MatrixGetColumn( matTmp, 2, vecNormal );
  1255. Assert( fabs( vecTexU.Dot( vecTexV ) ) <= 0.001f );
  1256. return;
  1257. }
  1258. vecU = vecTexU;
  1259. vecV = vecTexV;
  1260. }
  1261. void GatherLight (int threadnum, void *pUserData)
  1262. {
  1263. int i, j, k;
  1264. transfer_t *trans;
  1265. int num;
  1266. CPatch *patch;
  1267. Vector sum, v;
  1268. while (1)
  1269. {
  1270. j = GetThreadWork ();
  1271. if (j == -1)
  1272. break;
  1273. patch = &g_Patches[j];
  1274. trans = patch->transfers;
  1275. num = patch->numtransfers;
  1276. if ( patch->needsBumpmap )
  1277. {
  1278. Vector delta;
  1279. Vector bumpSum[NUM_BUMP_VECTS+1];
  1280. Vector normals[NUM_BUMP_VECTS+1];
  1281. // Disps
  1282. bool bDisp = ( g_pFaces[patch->faceNumber].dispinfo != -1 );
  1283. if ( bDisp )
  1284. {
  1285. normals[0] = patch->normal;
  1286. texinfo_t *pTexinfo = &texinfo[g_pFaces[patch->faceNumber].texinfo];
  1287. Vector vecTexU, vecTexV;
  1288. PreGetBumpNormalsForDisp( pTexinfo, vecTexU, vecTexV, normals[0] );
  1289. // use facenormal along with the smooth normal to build the three bump map vectors
  1290. GetBumpNormals( vecTexU, vecTexV, normals[0], normals[0], &normals[1] );
  1291. }
  1292. else
  1293. {
  1294. GetPhongNormal( patch->faceNumber, patch->origin, normals[0] );
  1295. texinfo_t *pTexinfo = &texinfo[g_pFaces[patch->faceNumber].texinfo];
  1296. // use facenormal along with the smooth normal to build the three bump map vectors
  1297. GetBumpNormals( pTexinfo->textureVecsTexelsPerWorldUnits[0],
  1298. pTexinfo->textureVecsTexelsPerWorldUnits[1], patch->normal,
  1299. normals[0], &normals[1] );
  1300. }
  1301. // force the base lightmap to use the flat normal instead of the phong normal
  1302. // FIXME: why does the patch not use the phong normal?
  1303. normals[0] = patch->normal;
  1304. for ( i = 0; i < NUM_BUMP_VECTS+1; i++ )
  1305. {
  1306. VectorFill( bumpSum[i], 0 );
  1307. }
  1308. float dot;
  1309. for (k=0 ; k<num ; k++, trans++)
  1310. {
  1311. CPatch *patch2 = &g_Patches[trans->patch];
  1312. // get vector to other patch
  1313. VectorSubtract (patch2->origin, patch->origin, delta);
  1314. VectorNormalize (delta);
  1315. // find light emitted from other patch
  1316. for(i=0; i<3; i++)
  1317. {
  1318. v[i] = emitlight[trans->patch][i] * patch2->reflectivity[i];
  1319. }
  1320. // remove normal already factored into transfer steradian
  1321. float scale = 1.0f / DotProduct (delta, patch->normal);
  1322. VectorScale( v, trans->transfer * scale, v );
  1323. Vector bumpTransfer;
  1324. for ( i = 0; i < NUM_BUMP_VECTS+1; i++ )
  1325. {
  1326. dot = DotProduct( delta, normals[i] );
  1327. if ( dot <= 0 )
  1328. {
  1329. // Assert( i > 0 ); // if this hits, then the transfer shouldn't be here. It doesn't face the flat normal of this face!
  1330. continue;
  1331. }
  1332. bumpTransfer = v * dot;
  1333. VectorAdd( bumpSum[i], bumpTransfer, bumpSum[i] );
  1334. }
  1335. }
  1336. for ( i = 0; i < NUM_BUMP_VECTS+1; i++ )
  1337. {
  1338. VectorCopy( bumpSum[i], addlight[j].light[i] );
  1339. }
  1340. }
  1341. else
  1342. {
  1343. VectorFill( sum, 0 );
  1344. for (k=0 ; k<num ; k++, trans++)
  1345. {
  1346. for(i=0; i<3; i++)
  1347. {
  1348. v[i] = emitlight[trans->patch][i] * g_Patches[trans->patch].reflectivity[i];
  1349. }
  1350. VectorScale( v, trans->transfer, v );
  1351. VectorAdd( sum, v, sum );
  1352. }
  1353. VectorCopy( sum, addlight[j].light[0] );
  1354. }
  1355. }
  1356. }
  1357. #ifdef _WIN32
  1358. #pragma warning (default:4701)
  1359. #endif
  1360. /*
  1361. =============
  1362. BounceLight
  1363. =============
  1364. */
  1365. void BounceLight (void)
  1366. {
  1367. unsigned i;
  1368. Vector added;
  1369. char name[64];
  1370. qboolean bouncing = numbounce > 0;
  1371. unsigned int uiPatchCount = g_Patches.Size();
  1372. for (i=0 ; i<uiPatchCount; i++)
  1373. {
  1374. // totallight has a copy of the direct lighting. Move it to the emitted light and zero it out (to integrate bounces only)
  1375. VectorCopy( g_Patches[i].totallight.light[0], emitlight[i] );
  1376. // NOTE: This means that only the bounced light is integrated into totallight!
  1377. VectorFill( g_Patches[i].totallight.light[0], 0 );
  1378. }
  1379. #if 0
  1380. FileHandle_t dFp = g_pFileSystem->Open( "lightemit.txt", "w" );
  1381. unsigned int uiPatchCount = g_Patches.Size();
  1382. for (i=0 ; i<uiPatchCount; i++)
  1383. {
  1384. CmdLib_FPrintf( dFp, "Emit %d: %f %f %f\n", i, emitlight[i].x, emitlight[i].y, emitlight[i].z );
  1385. }
  1386. g_pFileSystem->Close( dFp );
  1387. for (i=0; i<num_patches ; i++)
  1388. {
  1389. Vector total;
  1390. VectorSubtract (g_Patches[i].maxs, g_Patches[i].mins, total);
  1391. Msg("%4d %4d %4d %4d (%d) %.0f", i, g_Patches[i].parent, g_Patches[i].child1, g_Patches[i].child2, g_Patches[i].samples, g_Patches[i].area );
  1392. Msg(" [%.0f %.0f %.0f]", total[0], total[1], total[2] );
  1393. if (g_Patches[i].child1 != g_Patches.InvalidIndex() )
  1394. {
  1395. Vector tmp;
  1396. VectorScale( g_Patches[i].totallight.light[0], g_Patches[i].area, tmp );
  1397. VectorMA( tmp, -g_Patches[g_Patches[i].child1].area, g_Patches[g_Patches[i].child1].totallight.light[0], tmp );
  1398. VectorMA( tmp, -g_Patches[g_Patches[i].child2].area, g_Patches[g_Patches[i].child2].totallight.light[0], tmp );
  1399. // Msg("%.0f ", VectorLength( tmp ) );
  1400. // Msg("%d ", g_Patches[i].samples - g_Patches[g_Patches[i].child1].samples - g_Patches[g_Patches[i].child2].samples );
  1401. // Msg("%d ", g_Patches[i].samples );
  1402. }
  1403. Msg("\n");
  1404. }
  1405. #endif
  1406. i = 0;
  1407. while ( bouncing )
  1408. {
  1409. // transfer light from to the leaf patches from other patches via transfers
  1410. // this moves shooter->emitlight to receiver->addlight
  1411. unsigned int uiPatchCount = g_Patches.Size();
  1412. RunThreadsOn (uiPatchCount, true, GatherLight);
  1413. // move newly received light (addlight) to light to be sent out (emitlight)
  1414. // start at children and pull light up to parents
  1415. // light is always received to leaf patches
  1416. CollectLight( added );
  1417. qprintf ("\tBounce #%i added RGB(%.0f, %.0f, %.0f)\n", i+1, added[0], added[1], added[2] );
  1418. if ( i+1 == numbounce || (added[0] < 1.0 && added[1] < 1.0 && added[2] < 1.0) )
  1419. bouncing = false;
  1420. i++;
  1421. if ( g_bDumpPatches && !bouncing && i != 1)
  1422. {
  1423. sprintf (name, "bounce%i.txt", i);
  1424. WriteWorld (name, 0);
  1425. }
  1426. }
  1427. }
  1428. //-----------------------------------------------------------------------------
  1429. // Purpose: Counts the number of clusters in a map with no visibility
  1430. // Output : int
  1431. //-----------------------------------------------------------------------------
  1432. int CountClusters( void )
  1433. {
  1434. int clusterCount = 0;
  1435. for ( int i = 0; i < numleafs; i++ )
  1436. {
  1437. if ( dleafs[i].cluster > clusterCount )
  1438. clusterCount = dleafs[i].cluster;
  1439. }
  1440. return clusterCount + 1;
  1441. }
  1442. /*
  1443. =============
  1444. RadWorld
  1445. =============
  1446. */
  1447. void RadWorld_Start()
  1448. {
  1449. unsigned i;
  1450. if (luxeldensity < 1.0)
  1451. {
  1452. // Remember the old lightmap vectors.
  1453. float oldLightmapVecs[MAX_MAP_TEXINFO][2][4];
  1454. for (i = 0; i < texinfo.Count(); i++)
  1455. {
  1456. for( int j=0; j < 2; j++ )
  1457. {
  1458. for( int k=0; k < 3; k++ )
  1459. {
  1460. oldLightmapVecs[i][j][k] = texinfo[i].lightmapVecsLuxelsPerWorldUnits[j][k];
  1461. }
  1462. }
  1463. }
  1464. // rescale luxels to be no denser than "luxeldensity"
  1465. for (i = 0; i < texinfo.Count(); i++)
  1466. {
  1467. texinfo_t *tx = &texinfo[i];
  1468. for (int j = 0; j < 2; j++ )
  1469. {
  1470. Vector tmp( tx->lightmapVecsLuxelsPerWorldUnits[j][0], tx->lightmapVecsLuxelsPerWorldUnits[j][1], tx->lightmapVecsLuxelsPerWorldUnits[j][2] );
  1471. float scale = VectorNormalize( tmp );
  1472. // only rescale them if the current scale is "tighter" than the desired scale
  1473. // FIXME: since this writes out to the BSP file every run, once it's set high it can't be reset
  1474. // to a lower value.
  1475. if (fabs( scale ) > luxeldensity)
  1476. {
  1477. if (scale < 0)
  1478. {
  1479. scale = -luxeldensity;
  1480. }
  1481. else
  1482. {
  1483. scale = luxeldensity;
  1484. }
  1485. VectorScale( tmp, scale, tmp );
  1486. tx->lightmapVecsLuxelsPerWorldUnits[j][0] = tmp.x;
  1487. tx->lightmapVecsLuxelsPerWorldUnits[j][1] = tmp.y;
  1488. tx->lightmapVecsLuxelsPerWorldUnits[j][2] = tmp.z;
  1489. }
  1490. }
  1491. }
  1492. UpdateAllFaceLightmapExtents();
  1493. }
  1494. MakeParents (0, -1);
  1495. BuildClusterTable();
  1496. // turn each face into a single patch
  1497. MakePatches ();
  1498. PairEdges ();
  1499. // store the vertex normals calculated in PairEdges
  1500. // so that the can be written to the bsp file for
  1501. // use in the engine
  1502. SaveVertexNormals();
  1503. // subdivide patches to a maximum dimension
  1504. SubdividePatches ();
  1505. // add displacement faces to cluster table
  1506. AddDispsToClusterTable();
  1507. // create directlights out of patches and lights
  1508. CreateDirectLights ();
  1509. // set up sky cameras
  1510. ProcessSkyCameras();
  1511. }
  1512. // This function should fill in the indices into g_pFaces[] for the faces
  1513. // with displacements that touch the specified leaf.
  1514. void STUB_GetDisplacementsTouchingLeaf( int iLeaf, CUtlVector<int> &dispFaces )
  1515. {
  1516. }
  1517. void BuildFacesVisibleToLights( bool bAllVisible )
  1518. {
  1519. g_FacesVisibleToLights.SetSize( numfaces/8 + 1 );
  1520. if( bAllVisible )
  1521. {
  1522. memset( g_FacesVisibleToLights.Base(), 0xFF, g_FacesVisibleToLights.Count() );
  1523. return;
  1524. }
  1525. // First merge all the light PVSes.
  1526. CUtlVector<byte> aggregate;
  1527. aggregate.SetSize( (dvis->numclusters/8) + 1 );
  1528. memset( aggregate.Base(), 0, aggregate.Count() );
  1529. int nDWords = aggregate.Count() / 4;
  1530. int nBytes = aggregate.Count() - nDWords*4;
  1531. for( directlight_t *dl = activelights; dl != NULL; dl = dl->next )
  1532. {
  1533. byte *pIn = dl->pvs;
  1534. byte *pOut = aggregate.Base();
  1535. for( int iDWord=0; iDWord < nDWords; iDWord++ )
  1536. {
  1537. *((unsigned long*)pOut) |= *((unsigned long*)pIn);
  1538. pIn += 4;
  1539. pOut += 4;
  1540. }
  1541. for( int iByte=0; iByte < nBytes; iByte++ )
  1542. {
  1543. *pOut |= *pIn;
  1544. ++pOut;
  1545. ++pIn;
  1546. }
  1547. }
  1548. // Now tag any faces that are visible to this monster PVS.
  1549. for( int iCluster=0; iCluster < dvis->numclusters; iCluster++ )
  1550. {
  1551. if( g_ClusterLeaves[iCluster].leafCount )
  1552. {
  1553. if( aggregate[iCluster>>3] & (1 << (iCluster & 7)) )
  1554. {
  1555. for ( int i = 0; i < g_ClusterLeaves[iCluster].leafCount; i++ )
  1556. {
  1557. int iLeaf = g_ClusterLeaves[iCluster].leafs[i];
  1558. // Tag all the faces.
  1559. int iFace;
  1560. for( iFace=0; iFace < dleafs[iLeaf].numleaffaces; iFace++ )
  1561. {
  1562. int index = dleafs[iLeaf].firstleafface + iFace;
  1563. index = dleaffaces[index];
  1564. assert( index < numfaces );
  1565. g_FacesVisibleToLights[index >> 3] |= (1 << (index & 7));
  1566. }
  1567. // Fill in STUB_GetDisplacementsTouchingLeaf when it's available
  1568. // so displacements get relit.
  1569. CUtlVector<int> dispFaces;
  1570. STUB_GetDisplacementsTouchingLeaf( iLeaf, dispFaces );
  1571. for( iFace=0; iFace < dispFaces.Count(); iFace++ )
  1572. {
  1573. int index = dispFaces[iFace];
  1574. g_FacesVisibleToLights[index >> 3] |= (1 << (index & 7));
  1575. }
  1576. }
  1577. }
  1578. }
  1579. }
  1580. // For stats.. figure out how many faces it's going to touch.
  1581. int nFacesToProcess = 0;
  1582. for( int i=0; i < numfaces; i++ )
  1583. {
  1584. if( g_FacesVisibleToLights[i>>3] & (1 << (i & 7)) )
  1585. ++nFacesToProcess;
  1586. }
  1587. }
  1588. void MakeAllScales (void)
  1589. {
  1590. // determine visibility between patches
  1591. BuildVisMatrix ();
  1592. // release visibility matrix
  1593. FreeVisMatrix ();
  1594. Msg("transfers %d, max %d\n", total_transfer, max_transfer );
  1595. qprintf ("transfer lists: %5.1f megs\n"
  1596. , (float)total_transfer * sizeof(transfer_t) / (1024*1024));
  1597. }
  1598. // Helper function. This can be useful to visualize the world and faces and see which face
  1599. // corresponds to which dface.
  1600. #if 0
  1601. #include "iscratchpad3d.h"
  1602. void ScratchPad_DrawWorld()
  1603. {
  1604. IScratchPad3D *pPad = ScratchPad3D_Create();
  1605. pPad->SetAutoFlush( false );
  1606. for ( int i=0; i < numfaces; i++ )
  1607. {
  1608. dface_t *f = &g_pFaces[i];
  1609. // Draw the face's outline, then put text for its face index on it too.
  1610. CUtlVector<Vector> points;
  1611. for ( int iEdge = 0; iEdge < f->numedges; iEdge++ )
  1612. {
  1613. int v;
  1614. int se = dsurfedges[f->firstedge + iEdge];
  1615. if ( se < 0 )
  1616. v = dedges[-se].v[1];
  1617. else
  1618. v = dedges[se].v[0];
  1619. dvertex_t *dv = &dvertexes[v];
  1620. points.AddToTail( dv->point );
  1621. }
  1622. // Draw the outline.
  1623. Vector vCenter( 0, 0, 0 );
  1624. for ( iEdge=0; iEdge < points.Count(); iEdge++ )
  1625. {
  1626. pPad->DrawLine( CSPVert( points[iEdge] ), CSPVert( points[(iEdge+1)%points.Count()] ) );
  1627. vCenter += points[iEdge];
  1628. }
  1629. vCenter /= points.Count();
  1630. // Draw the text.
  1631. char str[512];
  1632. Q_snprintf( str, sizeof( str ), "%d", i );
  1633. CTextParams params;
  1634. params.m_bCentered = true;
  1635. params.m_bOutline = true;
  1636. params.m_flLetterWidth = 2;
  1637. params.m_vColor.Init( 1, 0, 0 );
  1638. VectorAngles( dplanes[f->planenum].normal, params.m_vAngles );
  1639. params.m_bTwoSided = true;
  1640. params.m_vPos = vCenter;
  1641. pPad->DrawText( str, params );
  1642. }
  1643. pPad->Release();
  1644. }
  1645. #endif
  1646. bool RadWorld_Go()
  1647. {
  1648. g_iCurFace = 0;
  1649. InitMacroTexture( source );
  1650. if( g_pIncremental )
  1651. {
  1652. g_pIncremental->PrepareForLighting();
  1653. // Cull out faces that aren't visible to any of the lights that we're updating with.
  1654. BuildFacesVisibleToLights( false );
  1655. }
  1656. else
  1657. {
  1658. // Mark all faces visible.. when not doing incremental lighting, it's highly
  1659. // likely that all faces are going to be touched by at least one light so don't
  1660. // waste time here.
  1661. BuildFacesVisibleToLights( true );
  1662. }
  1663. // build initial facelights
  1664. if (g_bUseMPI)
  1665. {
  1666. // RunThreadsOnIndividual (numfaces, true, BuildFacelights);
  1667. RunMPIBuildFacelights();
  1668. }
  1669. else
  1670. {
  1671. RunThreadsOnIndividual (numfaces, true, BuildFacelights);
  1672. }
  1673. // Was the process interrupted?
  1674. if( g_pIncremental && (g_iCurFace != numfaces) )
  1675. return false;
  1676. // Figure out the offset into lightmap data for each face.
  1677. PrecompLightmapOffsets();
  1678. // If we're doing incremental lighting, stop here.
  1679. if( g_pIncremental )
  1680. {
  1681. g_pIncremental->Finalize();
  1682. }
  1683. else
  1684. {
  1685. // free up the direct lights now that we have facelights
  1686. ExportDirectLightsToWorldLights();
  1687. if ( g_bDumpPatches )
  1688. {
  1689. for( int iBump = 0; iBump < 4; ++iBump )
  1690. {
  1691. char szName[64];
  1692. sprintf ( szName, "bounce0_%d.txt", iBump );
  1693. WriteWorld( szName, iBump );
  1694. }
  1695. }
  1696. if (numbounce > 0)
  1697. {
  1698. // allocate memory for emitlight/addlight
  1699. emitlight.SetSize( g_Patches.Size() );
  1700. memset( emitlight.Base(), 0, g_Patches.Size() * sizeof( Vector ) );
  1701. addlight.SetSize( g_Patches.Size() );
  1702. memset( addlight.Base(), 0, g_Patches.Size() * sizeof( bumplights_t ) );
  1703. MakeAllScales ();
  1704. // spread light around
  1705. BounceLight ();
  1706. }
  1707. //
  1708. // displacement surface luxel accumulation (make threaded!!!)
  1709. //
  1710. StaticDispMgr()->StartTimer( "Build Patch/Sample Hash Table(s)....." );
  1711. StaticDispMgr()->InsertSamplesDataIntoHashTable();
  1712. StaticDispMgr()->InsertPatchSampleDataIntoHashTable();
  1713. StaticDispMgr()->EndTimer();
  1714. // blend bounced light into direct light and save
  1715. VMPI_SetCurrentStage( "FinalLightFace" );
  1716. if ( !g_bUseMPI || g_bMPIMaster )
  1717. RunThreadsOnIndividual (numfaces, true, FinalLightFace);
  1718. // Distribute the lighting data to workers.
  1719. VMPI_DistributeLightData();
  1720. Msg("FinalLightFace Done\n"); fflush(stdout);
  1721. }
  1722. return true;
  1723. }
  1724. // declare the sample file pointer -- the whole debug print system should
  1725. // be reworked at some point!!
  1726. FileHandle_t pFileSamples[4][4];
  1727. void LoadPhysicsDLL( void )
  1728. {
  1729. PhysicsDLLPath( "VPHYSICS.DLL" );
  1730. }
  1731. void InitDumpPatchesFiles()
  1732. {
  1733. for( int iStyle = 0; iStyle < 4; ++iStyle )
  1734. {
  1735. for ( int iBump = 0; iBump < 4; ++iBump )
  1736. {
  1737. char szFilename[MAX_PATH];
  1738. sprintf( szFilename, "samples_style%d_bump%d.txt", iStyle, iBump );
  1739. pFileSamples[iStyle][iBump] = g_pFileSystem->Open( szFilename, "w" );
  1740. if( !pFileSamples[iStyle][iBump] )
  1741. {
  1742. Error( "Can't open %s for -dump.\n", szFilename );
  1743. }
  1744. }
  1745. }
  1746. }
  1747. extern IFileSystem *g_pOriginalPassThruFileSystem;
  1748. void VRAD_LoadBSP( char const *pFilename )
  1749. {
  1750. ThreadSetDefault ();
  1751. g_flStartTime = Plat_FloatTime();
  1752. if( g_bLowPriority )
  1753. {
  1754. SetLowPriority();
  1755. }
  1756. strcpy( level_name, source );
  1757. // This must come after InitFileSystem because the file system pointer might change.
  1758. if ( g_bDumpPatches )
  1759. InitDumpPatchesFiles();
  1760. // This part is just for VMPI. VMPI's file system needs the basedir in front of all filenames,
  1761. // so we prepend qdir here.
  1762. strcpy( source, ExpandPath( source ) );
  1763. if ( !g_bUseMPI )
  1764. {
  1765. // Setup the logfile.
  1766. char logFile[512];
  1767. _snprintf( logFile, sizeof(logFile), "%s.log", source );
  1768. SetSpewFunctionLogFile( logFile );
  1769. }
  1770. LoadPhysicsDLL();
  1771. // Set the required global lights filename and try looking in qproject
  1772. strcpy( global_lights, "lights.rad" );
  1773. if ( !g_pFileSystem->FileExists( global_lights ) )
  1774. {
  1775. // Otherwise, try looking in the BIN directory from which we were run from
  1776. Msg( "Could not find lights.rad in %s.\nTrying VRAD BIN directory instead...\n",
  1777. global_lights );
  1778. GetModuleFileName( NULL, global_lights, sizeof( global_lights ) );
  1779. Q_ExtractFilePath( global_lights, global_lights, sizeof( global_lights ) );
  1780. strcat( global_lights, "lights.rad" );
  1781. }
  1782. // Set the optional level specific lights filename
  1783. strcpy( level_lights, source );
  1784. Q_DefaultExtension( level_lights, ".rad", sizeof( level_lights ) );
  1785. if ( !g_pFileSystem->FileExists( level_lights ) )
  1786. *level_lights = 0;
  1787. ReadLightFile(global_lights); // Required
  1788. if ( *designer_lights ) ReadLightFile(designer_lights); // Command-line
  1789. if ( *level_lights ) ReadLightFile(level_lights); // Optional & implied
  1790. strcpy(incrementfile, source);
  1791. Q_DefaultExtension(incrementfile, ".r0", sizeof(incrementfile));
  1792. Q_DefaultExtension(source, ".bsp", sizeof( source ));
  1793. Msg( "Loading %s\n", source );
  1794. VMPI_SetCurrentStage( "LoadBSPFile" );
  1795. LoadBSPFile (source);
  1796. // Add this bsp to our search path so embedded resources can be found
  1797. if ( g_bUseMPI && g_bMPIMaster )
  1798. {
  1799. // MPI Master, MPI workers don't need to do anything
  1800. g_pOriginalPassThruFileSystem->AddSearchPath(source, "GAME", PATH_ADD_TO_HEAD);
  1801. g_pOriginalPassThruFileSystem->AddSearchPath(source, "MOD", PATH_ADD_TO_HEAD);
  1802. }
  1803. else if ( !g_bUseMPI )
  1804. {
  1805. // Non-MPI
  1806. g_pFullFileSystem->AddSearchPath(source, "GAME", PATH_ADD_TO_HEAD);
  1807. g_pFullFileSystem->AddSearchPath(source, "MOD", PATH_ADD_TO_HEAD);
  1808. }
  1809. // now, set whether or not static prop lighting is present
  1810. if (g_bStaticPropLighting)
  1811. g_LevelFlags |= g_bHDR? LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_HDR : LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_NONHDR;
  1812. else
  1813. {
  1814. g_LevelFlags &= ~( LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_HDR | LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_NONHDR );
  1815. }
  1816. // now, we need to set our face ptr depending upon hdr, and if hdr, init it
  1817. if (g_bHDR)
  1818. {
  1819. g_pFaces = dfaces_hdr;
  1820. if (numfaces_hdr==0)
  1821. {
  1822. numfaces_hdr = numfaces;
  1823. memcpy( dfaces_hdr, dfaces, numfaces*sizeof(dfaces[0]) );
  1824. }
  1825. }
  1826. else
  1827. {
  1828. g_pFaces = dfaces;
  1829. }
  1830. ParseEntities ();
  1831. ExtractBrushEntityShadowCasters();
  1832. StaticPropMgr()->Init();
  1833. StaticDispMgr()->Init();
  1834. if (!visdatasize)
  1835. {
  1836. Msg("No vis information, direct lighting only.\n");
  1837. numbounce = 0;
  1838. ambient[0] = ambient[1] = ambient[2] = 0.1f;
  1839. dvis->numclusters = CountClusters();
  1840. }
  1841. //
  1842. // patches and referencing data (ensure capacity)
  1843. //
  1844. // TODO: change the maxes to the amount from the bsp!!
  1845. //
  1846. // g_Patches.EnsureCapacity( MAX_PATCHES );
  1847. g_FacePatches.SetSize( MAX_MAP_FACES );
  1848. faceParents.SetSize( MAX_MAP_FACES );
  1849. clusterChildren.SetSize( MAX_MAP_CLUSTERS );
  1850. int ndx;
  1851. for ( ndx = 0; ndx < MAX_MAP_FACES; ndx++ )
  1852. {
  1853. g_FacePatches[ndx] = g_FacePatches.InvalidIndex();
  1854. faceParents[ndx] = faceParents.InvalidIndex();
  1855. }
  1856. for ( ndx = 0; ndx < MAX_MAP_CLUSTERS; ndx++ )
  1857. {
  1858. clusterChildren[ndx] = clusterChildren.InvalidIndex();
  1859. }
  1860. // Setup ray tracer
  1861. AddBrushesForRayTrace();
  1862. StaticDispMgr()->AddPolysForRayTrace();
  1863. StaticPropMgr()->AddPolysForRayTrace();
  1864. // Dump raytracer for glview
  1865. if ( g_bDumpRtEnv )
  1866. WriteRTEnv("trace.txt");
  1867. // Build acceleration structure
  1868. printf ( "Setting up ray-trace acceleration structure... ");
  1869. float start = Plat_FloatTime();
  1870. g_RtEnv.SetupAccelerationStructure();
  1871. float end = Plat_FloatTime();
  1872. printf ( "Done (%.2f seconds)\n", end-start );
  1873. #if 0 // To test only k-d build
  1874. exit(0);
  1875. #endif
  1876. RadWorld_Start();
  1877. // Setup incremental lighting.
  1878. if( g_pIncremental )
  1879. {
  1880. if( !g_pIncremental->Init( source, incrementfile ) )
  1881. {
  1882. Error( "Unable to load incremental lighting file in %s.\n", incrementfile );
  1883. return;
  1884. }
  1885. }
  1886. }
  1887. void VRAD_ComputeOtherLighting()
  1888. {
  1889. // Compute lighting for the bsp file
  1890. if ( !g_bNoDetailLighting )
  1891. {
  1892. ComputeDetailPropLighting( THREADINDEX_MAIN );
  1893. }
  1894. ComputePerLeafAmbientLighting();
  1895. // bake the static props high quality vertex lighting into the bsp
  1896. if ( !do_fast && g_bStaticPropLighting )
  1897. {
  1898. StaticPropMgr()->ComputeLighting( THREADINDEX_MAIN );
  1899. }
  1900. }
  1901. extern void CloseDispLuxels();
  1902. void VRAD_Finish()
  1903. {
  1904. Msg( "Ready to Finish\n" );
  1905. fflush( stdout );
  1906. if ( verbose )
  1907. {
  1908. PrintBSPFileSizes();
  1909. }
  1910. Msg( "Writing %s\n", source );
  1911. VMPI_SetCurrentStage( "WriteBSPFile" );
  1912. WriteBSPFile(source);
  1913. if ( g_bDumpPatches )
  1914. {
  1915. for ( int iStyle = 0; iStyle < 4; ++iStyle )
  1916. {
  1917. for ( int iBump = 0; iBump < 4; ++iBump )
  1918. {
  1919. g_pFileSystem->Close( pFileSamples[iStyle][iBump] );
  1920. }
  1921. }
  1922. }
  1923. CloseDispLuxels();
  1924. StaticPropMgr()->Shutdown();
  1925. double end = Plat_FloatTime();
  1926. char str[512];
  1927. GetHourMinuteSecondsString( (int)( end - g_flStartTime ), str, sizeof( str ) );
  1928. Msg( "%s elapsed\n", str );
  1929. ReleasePakFileLumps();
  1930. }
  1931. // Run startup code like initialize mathlib (called from main() and from the
  1932. // WorldCraft interface into vrad).
  1933. void VRAD_Init()
  1934. {
  1935. MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
  1936. InstallAllocationFunctions();
  1937. InstallSpewFunction();
  1938. }
  1939. int ParseCommandLine( int argc, char **argv, bool *onlydetail )
  1940. {
  1941. *onlydetail = false;
  1942. int mapArg = -1;
  1943. // default to LDR
  1944. SetHDRMode( false );
  1945. int i;
  1946. for( i=1 ; i<argc ; i++ )
  1947. {
  1948. if ( !Q_stricmp( argv[i], "-StaticPropLighting" ) )
  1949. {
  1950. g_bStaticPropLighting = true;
  1951. }
  1952. else if ( !stricmp( argv[i], "-StaticPropNormals" ) )
  1953. {
  1954. g_bShowStaticPropNormals = true;
  1955. }
  1956. else if ( !stricmp( argv[i], "-OnlyStaticProps" ) )
  1957. {
  1958. g_bOnlyStaticProps = true;
  1959. }
  1960. else if ( !Q_stricmp( argv[i], "-StaticPropPolys" ) )
  1961. {
  1962. g_bStaticPropPolys = true;
  1963. }
  1964. else if ( !Q_stricmp( argv[i], "-nossprops" ) )
  1965. {
  1966. g_bDisablePropSelfShadowing = true;
  1967. }
  1968. else if ( !Q_stricmp( argv[i], "-textureshadows" ) )
  1969. {
  1970. g_bTextureShadows = true;
  1971. }
  1972. else if ( !strcmp(argv[i], "-dump") )
  1973. {
  1974. g_bDumpPatches = true;
  1975. }
  1976. else if ( !Q_stricmp( argv[i], "-nodetaillight" ) )
  1977. {
  1978. g_bNoDetailLighting = true;
  1979. }
  1980. else if ( !Q_stricmp( argv[i], "-rederrors" ) )
  1981. {
  1982. bRed2Black = false;
  1983. }
  1984. else if ( !Q_stricmp( argv[i], "-dumpnormals" ) )
  1985. {
  1986. bDumpNormals = true;
  1987. }
  1988. else if ( !Q_stricmp( argv[i], "-dumptrace" ) )
  1989. {
  1990. g_bDumpRtEnv = true;
  1991. }
  1992. else if ( !Q_stricmp( argv[i], "-LargeDispSampleRadius" ) )
  1993. {
  1994. g_bLargeDispSampleRadius = true;
  1995. }
  1996. else if (!Q_stricmp( argv[i], "-dumppropmaps"))
  1997. {
  1998. g_bDumpPropLightmaps = true;
  1999. }
  2000. else if (!Q_stricmp(argv[i],"-bounce"))
  2001. {
  2002. if ( ++i < argc )
  2003. {
  2004. int bounceParam = atoi (argv[i]);
  2005. if ( bounceParam < 0 )
  2006. {
  2007. Warning("Error: expected non-negative value after '-bounce'\n" );
  2008. return -1;
  2009. }
  2010. numbounce = (unsigned)bounceParam;
  2011. }
  2012. else
  2013. {
  2014. Warning("Error: expected a value after '-bounce'\n" );
  2015. return -1;
  2016. }
  2017. }
  2018. else if (!Q_stricmp(argv[i],"-verbose") || !Q_stricmp(argv[i],"-v"))
  2019. {
  2020. verbose = true;
  2021. }
  2022. else if (!Q_stricmp(argv[i],"-threads"))
  2023. {
  2024. if ( ++i < argc )
  2025. {
  2026. numthreads = atoi (argv[i]);
  2027. if ( numthreads <= 0 )
  2028. {
  2029. Warning("Error: expected positive value after '-threads'\n" );
  2030. return -1;
  2031. }
  2032. }
  2033. else
  2034. {
  2035. Warning("Error: expected a value after '-threads'\n" );
  2036. return -1;
  2037. }
  2038. }
  2039. else if ( !Q_stricmp(argv[i], "-lights" ) )
  2040. {
  2041. if ( ++i < argc && *argv[i] )
  2042. {
  2043. strcpy( designer_lights, argv[i] );
  2044. }
  2045. else
  2046. {
  2047. Warning("Error: expected a filepath after '-lights'\n" );
  2048. return -1;
  2049. }
  2050. }
  2051. else if (!Q_stricmp(argv[i],"-noextra"))
  2052. {
  2053. do_extra = false;
  2054. }
  2055. else if (!Q_stricmp(argv[i],"-debugextra"))
  2056. {
  2057. debug_extra = true;
  2058. }
  2059. else if ( !Q_stricmp(argv[i], "-fastambient") )
  2060. {
  2061. g_bFastAmbient = true;
  2062. }
  2063. else if (!Q_stricmp(argv[i],"-fast"))
  2064. {
  2065. do_fast = true;
  2066. }
  2067. else if (!Q_stricmp(argv[i],"-noskyboxrecurse"))
  2068. {
  2069. g_bNoSkyRecurse = true;
  2070. }
  2071. else if (!Q_stricmp(argv[i],"-final"))
  2072. {
  2073. g_flSkySampleScale = 16.0;
  2074. }
  2075. else if (!Q_stricmp(argv[i],"-extrasky"))
  2076. {
  2077. if ( ++i < argc && *argv[i] )
  2078. {
  2079. g_flSkySampleScale = atof( argv[i] );
  2080. }
  2081. else
  2082. {
  2083. Warning("Error: expected a scale factor after '-extrasky'\n" );
  2084. return -1;
  2085. }
  2086. }
  2087. else if (!Q_stricmp(argv[i],"-centersamples"))
  2088. {
  2089. do_centersamples = true;
  2090. }
  2091. else if (!Q_stricmp(argv[i],"-smooth"))
  2092. {
  2093. if ( ++i < argc )
  2094. {
  2095. smoothing_threshold = (float)cos(atof(argv[i])*(M_PI/180.0));
  2096. }
  2097. else
  2098. {
  2099. Warning("Error: expected an angle after '-smooth'\n" );
  2100. return -1;
  2101. }
  2102. }
  2103. else if (!Q_stricmp(argv[i],"-dlightmap"))
  2104. {
  2105. dlight_map = 1;
  2106. }
  2107. else if (!Q_stricmp(argv[i],"-luxeldensity"))
  2108. {
  2109. if ( ++i < argc )
  2110. {
  2111. luxeldensity = (float)atof (argv[i]);
  2112. if (luxeldensity > 1.0)
  2113. luxeldensity = 1.0 / luxeldensity;
  2114. }
  2115. else
  2116. {
  2117. Warning("Error: expected a value after '-luxeldensity'\n" );
  2118. return -1;
  2119. }
  2120. }
  2121. else if( !Q_stricmp( argv[i], "-low" ) )
  2122. {
  2123. g_bLowPriority = true;
  2124. }
  2125. else if( !Q_stricmp( argv[i], "-loghash" ) )
  2126. {
  2127. g_bLogHashData = true;
  2128. }
  2129. else if( !Q_stricmp( argv[i], "-onlydetail" ) )
  2130. {
  2131. *onlydetail = true;
  2132. }
  2133. else if (!Q_stricmp(argv[i],"-softsun"))
  2134. {
  2135. if ( ++i < argc )
  2136. {
  2137. g_SunAngularExtent=atof(argv[i]);
  2138. g_SunAngularExtent=sin((M_PI/180.0)*g_SunAngularExtent);
  2139. printf("sun extent=%f\n",g_SunAngularExtent);
  2140. }
  2141. else
  2142. {
  2143. Warning("Error: expected an angular extent value (0..180) '-softsun'\n" );
  2144. return -1;
  2145. }
  2146. }
  2147. else if ( !Q_stricmp( argv[i], "-maxdispsamplesize" ) )
  2148. {
  2149. if ( ++i < argc )
  2150. {
  2151. g_flMaxDispSampleSize = ( float )atof( argv[i] );
  2152. }
  2153. else
  2154. {
  2155. Warning( "Error: expected a sample size after '-maxdispsamplesize'\n" );
  2156. return -1;
  2157. }
  2158. }
  2159. else if ( stricmp( argv[i], "-StopOnExit" ) == 0 )
  2160. {
  2161. g_bStopOnExit = true;
  2162. }
  2163. else if ( stricmp( argv[i], "-steam" ) == 0 )
  2164. {
  2165. }
  2166. else if ( stricmp( argv[i], "-allowdebug" ) == 0 )
  2167. {
  2168. // Don't need to do anything, just don't error out.
  2169. }
  2170. else if ( !Q_stricmp( argv[i], CMDLINEOPTION_NOVCONFIG ) )
  2171. {
  2172. }
  2173. else if ( !Q_stricmp( argv[i], "-vproject" ) || !Q_stricmp( argv[i], "-game" ) || !Q_stricmp( argv[i], "-insert_search_path" ) )
  2174. {
  2175. ++i;
  2176. }
  2177. else if ( !Q_stricmp( argv[i], "-FullMinidumps" ) )
  2178. {
  2179. EnableFullMinidumps( true );
  2180. }
  2181. else if ( !Q_stricmp( argv[i], "-hdr" ) )
  2182. {
  2183. SetHDRMode( true );
  2184. }
  2185. else if ( !Q_stricmp( argv[i], "-ldr" ) )
  2186. {
  2187. SetHDRMode( false );
  2188. }
  2189. else if (!Q_stricmp(argv[i],"-maxchop"))
  2190. {
  2191. if ( ++i < argc )
  2192. {
  2193. maxchop = (float)atof (argv[i]);
  2194. if ( maxchop < 1 )
  2195. {
  2196. Warning("Error: expected positive value after '-maxchop'\n" );
  2197. return -1;
  2198. }
  2199. }
  2200. else
  2201. {
  2202. Warning("Error: expected a value after '-maxchop'\n" );
  2203. return -1;
  2204. }
  2205. }
  2206. else if (!Q_stricmp(argv[i],"-chop"))
  2207. {
  2208. if ( ++i < argc )
  2209. {
  2210. minchop = (float)atof (argv[i]);
  2211. if ( minchop < 1 )
  2212. {
  2213. Warning("Error: expected positive value after '-chop'\n" );
  2214. return -1;
  2215. }
  2216. minchop = min( minchop, maxchop );
  2217. }
  2218. else
  2219. {
  2220. Warning("Error: expected a value after '-chop'\n" );
  2221. return -1;
  2222. }
  2223. }
  2224. else if ( !Q_stricmp( argv[i], "-dispchop" ) )
  2225. {
  2226. if ( ++i < argc )
  2227. {
  2228. dispchop = ( float )atof( argv[i] );
  2229. if ( dispchop < 1.0f )
  2230. {
  2231. Warning( "Error: expected positive value after '-dipschop'\n" );
  2232. return -1;
  2233. }
  2234. }
  2235. else
  2236. {
  2237. Warning( "Error: expected a value after '-dispchop'\n" );
  2238. return -1;
  2239. }
  2240. }
  2241. else if ( !Q_stricmp( argv[i], "-disppatchradius" ) )
  2242. {
  2243. if ( ++i < argc )
  2244. {
  2245. g_MaxDispPatchRadius = ( float )atof( argv[i] );
  2246. if ( g_MaxDispPatchRadius < 10.0f )
  2247. {
  2248. Warning( "Error: g_MaxDispPatchRadius < 10.0\n" );
  2249. return -1;
  2250. }
  2251. }
  2252. else
  2253. {
  2254. Warning( "Error: expected a value after '-disppatchradius'\n" );
  2255. return -1;
  2256. }
  2257. }
  2258. #if ALLOWDEBUGOPTIONS
  2259. else if (!Q_stricmp(argv[i],"-scale"))
  2260. {
  2261. if ( ++i < argc )
  2262. {
  2263. lightscale = (float)atof (argv[i]);
  2264. }
  2265. else
  2266. {
  2267. Warning("Error: expected a value after '-scale'\n" );
  2268. return -1;
  2269. }
  2270. }
  2271. else if (!Q_stricmp(argv[i],"-ambient"))
  2272. {
  2273. if ( i+3 < argc )
  2274. {
  2275. ambient[0] = (float)atof (argv[++i]) * 128;
  2276. ambient[1] = (float)atof (argv[++i]) * 128;
  2277. ambient[2] = (float)atof (argv[++i]) * 128;
  2278. }
  2279. else
  2280. {
  2281. Warning("Error: expected three color values after '-ambient'\n" );
  2282. return -1;
  2283. }
  2284. }
  2285. else if (!Q_stricmp(argv[i],"-dlight"))
  2286. {
  2287. if ( ++i < argc )
  2288. {
  2289. dlight_threshold = (float)atof (argv[i]);
  2290. }
  2291. else
  2292. {
  2293. Warning("Error: expected a value after '-dlight'\n" );
  2294. return -1;
  2295. }
  2296. }
  2297. else if (!Q_stricmp(argv[i],"-sky"))
  2298. {
  2299. if ( ++i < argc )
  2300. {
  2301. indirect_sun = (float)atof (argv[i]);
  2302. }
  2303. else
  2304. {
  2305. Warning("Error: expected a value after '-sky'\n" );
  2306. return -1;
  2307. }
  2308. }
  2309. else if (!Q_stricmp(argv[i],"-notexscale"))
  2310. {
  2311. texscale = false;
  2312. }
  2313. else if (!Q_stricmp(argv[i],"-coring"))
  2314. {
  2315. if ( ++i < argc )
  2316. {
  2317. coring = (float)atof( argv[i] );
  2318. }
  2319. else
  2320. {
  2321. Warning("Error: expected a light threshold after '-coring'\n" );
  2322. return -1;
  2323. }
  2324. }
  2325. #endif
  2326. // NOTE: the -mpi checks must come last here because they allow the previous argument
  2327. // to be -mpi as well. If it game before something else like -game, then if the previous
  2328. // argument was -mpi and the current argument was something valid like -game, it would skip it.
  2329. else if ( !Q_strncasecmp( argv[i], "-mpi", 4 ) || !Q_strncasecmp( argv[i-1], "-mpi", 4 ) )
  2330. {
  2331. if ( stricmp( argv[i], "-mpi" ) == 0 )
  2332. g_bUseMPI = true;
  2333. // Any other args that start with -mpi are ok too.
  2334. if ( i == argc - 1 && V_stricmp( argv[i], "-mpi_ListParams" ) != 0 )
  2335. break;
  2336. }
  2337. else if ( mapArg == -1 )
  2338. {
  2339. mapArg = i;
  2340. }
  2341. else
  2342. {
  2343. return -1;
  2344. }
  2345. }
  2346. return mapArg;
  2347. }
  2348. void PrintCommandLine( int argc, char **argv )
  2349. {
  2350. Warning( "Command line: " );
  2351. for ( int z=0; z < argc; z++ )
  2352. {
  2353. Warning( "\"%s\" ", argv[z] );
  2354. }
  2355. Warning( "\n\n" );
  2356. }
  2357. void PrintUsage( int argc, char **argv )
  2358. {
  2359. PrintCommandLine( argc, argv );
  2360. Warning(
  2361. "usage : vrad [options...] bspfile\n"
  2362. "example: vrad c:\\hl2\\hl2\\maps\\test\n"
  2363. "\n"
  2364. "Common options:\n"
  2365. "\n"
  2366. " -v (or -verbose): Turn on verbose output (also shows more command\n"
  2367. " -bounce # : Set max number of bounces (default: 100).\n"
  2368. " -fast : Quick and dirty lighting.\n"
  2369. " -fastambient : Per-leaf ambient sampling is lower quality to save compute time.\n"
  2370. " -final : High quality processing. equivalent to -extrasky 16.\n"
  2371. " -extrasky n : trace N times as many rays for indirect light and sky ambient.\n"
  2372. " -low : Run as an idle-priority process.\n"
  2373. " -mpi : Use VMPI to distribute computations.\n"
  2374. " -rederror : Show errors in red.\n"
  2375. "\n"
  2376. " -vproject <directory> : Override the VPROJECT environment variable.\n"
  2377. " -game <directory> : Same as -vproject.\n"
  2378. "\n"
  2379. "Other options:\n"
  2380. " -novconfig : Don't bring up graphical UI on vproject errors.\n"
  2381. " -dump : Write debugging .txt files.\n"
  2382. " -dumpnormals : Write normals to debug files.\n"
  2383. " -dumptrace : Write ray-tracing environment to debug files.\n"
  2384. " -threads : Control the number of threads vbsp uses (defaults to the #\n"
  2385. " or processors on your machine).\n"
  2386. " -lights <file> : Load a lights file in addition to lights.rad and the\n"
  2387. " level lights file.\n"
  2388. " -noextra : Disable supersampling.\n"
  2389. " -debugextra : Places debugging data in lightmaps to visualize\n"
  2390. " supersampling.\n"
  2391. " -smooth # : Set the threshold for smoothing groups, in degrees\n"
  2392. " (default 45).\n"
  2393. " -dlightmap : Force direct lighting into different lightmap than\n"
  2394. " radiosity.\n"
  2395. " -stoponexit : Wait for a keypress on exit.\n"
  2396. " -mpi_pw <pw> : Use a password to choose a specific set of VMPI workers.\n"
  2397. " -nodetaillight : Don't light detail props.\n"
  2398. " -centersamples : Move sample centers.\n"
  2399. " -luxeldensity # : Rescale all luxels by the specified amount (default: 1.0).\n"
  2400. " The number specified must be less than 1.0 or it will be\n"
  2401. " ignored.\n"
  2402. " -loghash : Log the sample hash table to samplehash.txt.\n"
  2403. " -onlydetail : Only light detail props and per-leaf lighting.\n"
  2404. " -maxdispsamplesize #: Set max displacement sample size (default: 512).\n"
  2405. " -softsun <n> : Treat the sun as an area light source of size <n> degrees."
  2406. " Produces soft shadows.\n"
  2407. " Recommended values are between 0 and 5. Default is 0.\n"
  2408. " -FullMinidumps : Write large minidumps on crash.\n"
  2409. " -chop : Smallest number of luxel widths for a bounce patch, used on edges\n"
  2410. " -maxchop : Coarsest allowed number of luxel widths for a patch, used in face interiors\n"
  2411. "\n"
  2412. " -LargeDispSampleRadius: This can be used if there are splotches of bounced light\n"
  2413. " on terrain. The compile will take longer, but it will gather\n"
  2414. " light across a wider area.\n"
  2415. " -StaticPropLighting : generate backed static prop vertex lighting\n"
  2416. " -StaticPropPolys : Perform shadow tests of static props at polygon precision\n"
  2417. " -OnlyStaticProps : Only perform direct static prop lighting (vrad debug option)\n"
  2418. " -StaticPropNormals : when lighting static props, just show their normal vector\n"
  2419. " -textureshadows : Allows texture alpha channels to block light - rays intersecting alpha surfaces will sample the texture\n"
  2420. " -noskyboxrecurse : Turn off recursion into 3d skybox (skybox shadows on world)\n"
  2421. " -nossprops : Globally disable self-shadowing on static props\n"
  2422. "\n"
  2423. #if 1 // Disabled for the initial SDK release with VMPI so we can get feedback from selected users.
  2424. );
  2425. #else
  2426. " -mpi_ListParams : Show a list of VMPI parameters.\n"
  2427. "\n"
  2428. );
  2429. // Show VMPI parameters?
  2430. for ( int i=1; i < argc; i++ )
  2431. {
  2432. if ( V_stricmp( argv[i], "-mpi_ListParams" ) == 0 )
  2433. {
  2434. Warning( "VMPI-specific options:\n\n" );
  2435. bool bIsSDKMode = VMPI_IsSDKMode();
  2436. for ( int i=k_eVMPICmdLineParam_FirstParam+1; i < k_eVMPICmdLineParam_LastParam; i++ )
  2437. {
  2438. if ( (VMPI_GetParamFlags( (EVMPICmdLineParam)i ) & VMPI_PARAM_SDK_HIDDEN) && bIsSDKMode )
  2439. continue;
  2440. Warning( "[%s]\n", VMPI_GetParamString( (EVMPICmdLineParam)i ) );
  2441. Warning( VMPI_GetParamHelpString( (EVMPICmdLineParam)i ) );
  2442. Warning( "\n\n" );
  2443. }
  2444. break;
  2445. }
  2446. }
  2447. #endif
  2448. }
  2449. int RunVRAD( int argc, char **argv )
  2450. {
  2451. #if defined(_MSC_VER) && ( _MSC_VER >= 1310 )
  2452. Msg("Valve Software - vrad.exe SSE (" __DATE__ ")\n" );
  2453. #else
  2454. Msg("Valve Software - vrad.exe (" __DATE__ ")\n" );
  2455. #endif
  2456. Msg("\n Valve Radiosity Simulator \n");
  2457. verbose = true; // Originally FALSE
  2458. bool onlydetail;
  2459. int i = ParseCommandLine( argc, argv, &onlydetail );
  2460. if (i == -1)
  2461. {
  2462. PrintUsage( argc, argv );
  2463. DeleteCmdLine( argc, argv );
  2464. CmdLib_Exit( 1 );
  2465. }
  2466. // Initialize the filesystem, so additional commandline options can be loaded
  2467. Q_StripExtension( argv[ i ], source, sizeof( source ) );
  2468. CmdLib_InitFileSystem( argv[ i ] );
  2469. Q_FileBase( source, source, sizeof( source ) );
  2470. VRAD_LoadBSP( argv[i] );
  2471. if ( (! onlydetail) && (! g_bOnlyStaticProps ) )
  2472. {
  2473. RadWorld_Go();
  2474. }
  2475. VRAD_ComputeOtherLighting();
  2476. VRAD_Finish();
  2477. VMPI_SetCurrentStage( "master done" );
  2478. DeleteCmdLine( argc, argv );
  2479. CmdLib_Cleanup();
  2480. return 0;
  2481. }
  2482. int VRAD_Main(int argc, char **argv)
  2483. {
  2484. g_pFileSystem = NULL; // Safeguard against using it before it's properly initialized.
  2485. VRAD_Init();
  2486. // This must come first.
  2487. VRAD_SetupMPI( argc, argv );
  2488. #if !defined( _DEBUG )
  2489. if ( g_bUseMPI && !g_bMPIMaster )
  2490. {
  2491. SetupToolsMinidumpHandler( VMPI_ExceptionFilter );
  2492. }
  2493. else
  2494. #endif
  2495. {
  2496. LoadCmdLineFromFile( argc, argv, source, "vrad" ); // Don't do this if we're a VMPI worker..
  2497. SetupDefaultToolsMinidumpHandler();
  2498. }
  2499. return RunVRAD( argc, argv );
  2500. }