Light.cpp 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #include "../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #include "Game_local.h"
  23. /*
  24. ===============================================================================
  25. idLight
  26. ===============================================================================
  27. */
  28. const idEventDef EV_Light_SetShader( "setShader", "s" );
  29. const idEventDef EV_Light_GetLightParm( "getLightParm", "d", 'f' );
  30. const idEventDef EV_Light_SetLightParm( "setLightParm", "df" );
  31. const idEventDef EV_Light_SetLightParms( "setLightParms", "ffff" );
  32. const idEventDef EV_Light_SetRadiusXYZ( "setRadiusXYZ", "fff" );
  33. const idEventDef EV_Light_SetRadius( "setRadius", "f" );
  34. const idEventDef EV_Light_On( "On", NULL );
  35. const idEventDef EV_Light_Off( "Off", NULL );
  36. const idEventDef EV_Light_FadeOut( "fadeOutLight", "f" );
  37. const idEventDef EV_Light_FadeIn( "fadeInLight", "f" );
  38. CLASS_DECLARATION( idEntity, idLight )
  39. EVENT( EV_Light_SetShader, idLight::Event_SetShader )
  40. EVENT( EV_Light_GetLightParm, idLight::Event_GetLightParm )
  41. EVENT( EV_Light_SetLightParm, idLight::Event_SetLightParm )
  42. EVENT( EV_Light_SetLightParms, idLight::Event_SetLightParms )
  43. EVENT( EV_Light_SetRadiusXYZ, idLight::Event_SetRadiusXYZ )
  44. EVENT( EV_Light_SetRadius, idLight::Event_SetRadius )
  45. EVENT( EV_Hide, idLight::Event_Hide )
  46. EVENT( EV_Show, idLight::Event_Show )
  47. EVENT( EV_Light_On, idLight::Event_On )
  48. EVENT( EV_Light_Off, idLight::Event_Off )
  49. EVENT( EV_Activate, idLight::Event_ToggleOnOff )
  50. EVENT( EV_PostSpawn, idLight::Event_SetSoundHandles )
  51. EVENT( EV_Light_FadeOut, idLight::Event_FadeOut )
  52. EVENT( EV_Light_FadeIn, idLight::Event_FadeIn )
  53. END_CLASS
  54. /*
  55. ================
  56. idGameEdit::ParseSpawnArgsToRenderLight
  57. parse the light parameters
  58. this is the canonical renderLight parm parsing,
  59. which should be used by dmap and the editor
  60. ================
  61. */
  62. void idGameEdit::ParseSpawnArgsToRenderLight( const idDict *args, renderLight_t *renderLight ) {
  63. bool gotTarget, gotUp, gotRight;
  64. const char *texture;
  65. idVec3 color;
  66. memset( renderLight, 0, sizeof( *renderLight ) );
  67. if (!args->GetVector("light_origin", "", renderLight->origin)) {
  68. args->GetVector( "origin", "", renderLight->origin );
  69. }
  70. gotTarget = args->GetVector( "light_target", "", renderLight->target );
  71. gotUp = args->GetVector( "light_up", "", renderLight->up );
  72. gotRight = args->GetVector( "light_right", "", renderLight->right );
  73. args->GetVector( "light_start", "0 0 0", renderLight->start );
  74. if ( !args->GetVector( "light_end", "", renderLight->end ) ) {
  75. renderLight->end = renderLight->target;
  76. }
  77. // we should have all of the target/right/up or none of them
  78. if ( ( gotTarget || gotUp || gotRight ) != ( gotTarget && gotUp && gotRight ) ) {
  79. gameLocal.Printf( "Light at (%f,%f,%f) has bad target info\n",
  80. renderLight->origin[0], renderLight->origin[1], renderLight->origin[2] );
  81. return;
  82. }
  83. if ( !gotTarget ) {
  84. renderLight->pointLight = true;
  85. // allow an optional relative center of light and shadow offset
  86. args->GetVector( "light_center", "0 0 0", renderLight->lightCenter );
  87. // create a point light
  88. if (!args->GetVector( "light_radius", "300 300 300", renderLight->lightRadius ) ) {
  89. float radius;
  90. args->GetFloat( "light", "300", radius );
  91. renderLight->lightRadius[0] = renderLight->lightRadius[1] = renderLight->lightRadius[2] = radius;
  92. }
  93. }
  94. // get the rotation matrix in either full form, or single angle form
  95. idAngles angles;
  96. idMat3 mat;
  97. if ( !args->GetMatrix( "light_rotation", "1 0 0 0 1 0 0 0 1", mat ) ) {
  98. if ( !args->GetMatrix( "rotation", "1 0 0 0 1 0 0 0 1", mat ) ) {
  99. args->GetFloat( "angle", "0", angles[ 1 ] );
  100. angles[ 0 ] = 0;
  101. angles[ 1 ] = idMath::AngleNormalize360( angles[ 1 ] );
  102. angles[ 2 ] = 0;
  103. mat = angles.ToMat3();
  104. }
  105. }
  106. // fix degenerate identity matrices
  107. mat[0].FixDegenerateNormal();
  108. mat[1].FixDegenerateNormal();
  109. mat[2].FixDegenerateNormal();
  110. renderLight->axis = mat;
  111. // check for other attributes
  112. args->GetVector( "_color", "1 1 1", color );
  113. renderLight->shaderParms[ SHADERPARM_RED ] = color[0];
  114. renderLight->shaderParms[ SHADERPARM_GREEN ] = color[1];
  115. renderLight->shaderParms[ SHADERPARM_BLUE ] = color[2];
  116. args->GetFloat( "shaderParm3", "1", renderLight->shaderParms[ SHADERPARM_TIMESCALE ] );
  117. if ( !args->GetFloat( "shaderParm4", "0", renderLight->shaderParms[ SHADERPARM_TIMEOFFSET ] ) ) {
  118. // offset the start time of the shader to sync it to the game time
  119. renderLight->shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  120. }
  121. args->GetFloat( "shaderParm5", "0", renderLight->shaderParms[5] );
  122. args->GetFloat( "shaderParm6", "0", renderLight->shaderParms[6] );
  123. args->GetFloat( "shaderParm7", "0", renderLight->shaderParms[ SHADERPARM_MODE ] );
  124. args->GetBool( "noshadows", "0", renderLight->noShadows );
  125. args->GetBool( "nospecular", "0", renderLight->noSpecular );
  126. args->GetBool( "parallel", "0", renderLight->parallel );
  127. args->GetString( "texture", "lights/squarelight1", &texture );
  128. // allow this to be NULL
  129. renderLight->shader = declManager->FindMaterial( texture, false );
  130. }
  131. /*
  132. ================
  133. idLight::UpdateChangeableSpawnArgs
  134. ================
  135. */
  136. void idLight::UpdateChangeableSpawnArgs( const idDict *source ) {
  137. idEntity::UpdateChangeableSpawnArgs( source );
  138. if ( source ) {
  139. source->Print();
  140. }
  141. FreeSoundEmitter( true );
  142. gameEdit->ParseSpawnArgsToRefSound( source ? source : &spawnArgs, &refSound );
  143. if ( refSound.shader && !refSound.waitfortrigger ) {
  144. StartSoundShader( refSound.shader, SND_CHANNEL_ANY, 0, false, NULL );
  145. }
  146. gameEdit->ParseSpawnArgsToRenderLight( source ? source : &spawnArgs, &renderLight );
  147. UpdateVisuals();
  148. }
  149. /*
  150. ================
  151. idLight::idLight
  152. ================
  153. */
  154. idLight::idLight():
  155. previousBaseColor( vec3_zero ) ,
  156. nextBaseColor( vec3_zero ) {
  157. memset( &renderLight, 0, sizeof( renderLight ) );
  158. localLightOrigin = vec3_zero;
  159. localLightAxis = mat3_identity;
  160. lightDefHandle = -1;
  161. levels = 0;
  162. currentLevel = 0;
  163. baseColor = vec3_zero;
  164. breakOnTrigger = false;
  165. count = 0;
  166. triggercount = 0;
  167. lightParent = NULL;
  168. fadeFrom.Set( 1, 1, 1, 1 );
  169. fadeTo.Set( 1, 1, 1, 1 );
  170. fadeStart = 0;
  171. fadeEnd = 0;
  172. soundWasPlaying = false;
  173. }
  174. /*
  175. ================
  176. idLight::~idLight
  177. ================
  178. */
  179. idLight::~idLight() {
  180. if ( lightDefHandle != -1 ) {
  181. gameRenderWorld->FreeLightDef( lightDefHandle );
  182. }
  183. }
  184. /*
  185. ================
  186. idLight::Save
  187. archives object for save game file
  188. ================
  189. */
  190. void idLight::Save( idSaveGame *savefile ) const {
  191. savefile->WriteRenderLight( renderLight );
  192. savefile->WriteBool( renderLight.prelightModel != NULL );
  193. savefile->WriteVec3( localLightOrigin );
  194. savefile->WriteMat3( localLightAxis );
  195. savefile->WriteString( brokenModel );
  196. savefile->WriteInt( levels );
  197. savefile->WriteInt( currentLevel );
  198. savefile->WriteVec3( baseColor );
  199. savefile->WriteBool( breakOnTrigger );
  200. savefile->WriteInt( count );
  201. savefile->WriteInt( triggercount );
  202. savefile->WriteObject( lightParent );
  203. savefile->WriteVec4( fadeFrom );
  204. savefile->WriteVec4( fadeTo );
  205. savefile->WriteInt( fadeStart );
  206. savefile->WriteInt( fadeEnd );
  207. savefile->WriteBool( soundWasPlaying );
  208. }
  209. /*
  210. ================
  211. idLight::Restore
  212. unarchives object from save game file
  213. ================
  214. */
  215. void idLight::Restore( idRestoreGame *savefile ) {
  216. bool hadPrelightModel;
  217. savefile->ReadRenderLight( renderLight );
  218. savefile->ReadBool( hadPrelightModel );
  219. renderLight.prelightModel = renderModelManager->CheckModel( va( "_prelight_%s", name.c_str() ) );
  220. if ( ( renderLight.prelightModel == NULL ) && hadPrelightModel ) {
  221. assert( 0 );
  222. if ( developer.GetBool() ) {
  223. // we really want to know if this happens
  224. gameLocal.Error( "idLight::Restore: prelightModel '_prelight_%s' not found", name.c_str() );
  225. } else {
  226. // but let it slide after release
  227. gameLocal.Warning( "idLight::Restore: prelightModel '_prelight_%s' not found", name.c_str() );
  228. }
  229. }
  230. savefile->ReadVec3( localLightOrigin );
  231. savefile->ReadMat3( localLightAxis );
  232. savefile->ReadString( brokenModel );
  233. savefile->ReadInt( levels );
  234. savefile->ReadInt( currentLevel );
  235. savefile->ReadVec3( baseColor );
  236. savefile->ReadBool( breakOnTrigger );
  237. savefile->ReadInt( count );
  238. savefile->ReadInt( triggercount );
  239. savefile->ReadObject( reinterpret_cast<idClass *&>( lightParent ) );
  240. savefile->ReadVec4( fadeFrom );
  241. savefile->ReadVec4( fadeTo );
  242. savefile->ReadInt( fadeStart );
  243. savefile->ReadInt( fadeEnd );
  244. savefile->ReadBool( soundWasPlaying );
  245. lightDefHandle = -1;
  246. SetLightLevel();
  247. }
  248. /*
  249. ================
  250. idLight::Spawn
  251. ================
  252. */
  253. void idLight::Spawn() {
  254. bool start_off;
  255. bool needBroken;
  256. const char *demonic_shader;
  257. // do the parsing the same way dmap and the editor do
  258. gameEdit->ParseSpawnArgsToRenderLight( &spawnArgs, &renderLight );
  259. // we need the origin and axis relative to the physics origin/axis
  260. localLightOrigin = ( renderLight.origin - GetPhysics()->GetOrigin() ) * GetPhysics()->GetAxis().Transpose();
  261. localLightAxis = renderLight.axis * GetPhysics()->GetAxis().Transpose();
  262. // set the base color from the shader parms
  263. baseColor.Set( renderLight.shaderParms[ SHADERPARM_RED ], renderLight.shaderParms[ SHADERPARM_GREEN ], renderLight.shaderParms[ SHADERPARM_BLUE ] );
  264. previousBaseColor.Set( renderLight.shaderParms[ SHADERPARM_RED ], renderLight.shaderParms[ SHADERPARM_GREEN ], renderLight.shaderParms[ SHADERPARM_BLUE ] );
  265. nextBaseColor.Set( renderLight.shaderParms[ SHADERPARM_RED ], renderLight.shaderParms[ SHADERPARM_GREEN ], renderLight.shaderParms[ SHADERPARM_BLUE ] );
  266. // set the number of light levels
  267. spawnArgs.GetInt( "levels", "1", levels );
  268. currentLevel = levels;
  269. if ( levels <= 0 ) {
  270. gameLocal.Error( "Invalid light level set on entity #%d(%s)", entityNumber, name.c_str() );
  271. }
  272. // make sure the demonic shader is cached
  273. if ( spawnArgs.GetString( "mat_demonic", NULL, &demonic_shader ) ) {
  274. declManager->FindType( DECL_MATERIAL, demonic_shader );
  275. }
  276. // game specific functionality, not mirrored in
  277. // editor or dmap light parsing
  278. // also put the light texture on the model, so light flares
  279. // can get the current intensity of the light
  280. renderEntity.referenceShader = renderLight.shader;
  281. lightDefHandle = -1; // no static version yet
  282. // see if an optimized shadow volume exists
  283. // the renderer will ignore this value after a light has been moved,
  284. // but there may still be a chance to get it wrong if the game moves
  285. // a light before the first present, and doesn't clear the prelight
  286. renderLight.prelightModel = 0;
  287. if ( name[ 0 ] ) {
  288. // this will return 0 if not found
  289. renderLight.prelightModel = renderModelManager->CheckModel( va( "_prelight_%s", name.c_str() ) );
  290. }
  291. spawnArgs.GetBool( "start_off", "0", start_off );
  292. if ( start_off ) {
  293. Off();
  294. }
  295. // Midnight CTF
  296. if ( gameLocal.mpGame.IsGametypeFlagBased() && gameLocal.serverInfo.GetBool("si_midnight") && !spawnArgs.GetBool("midnight_override") ) {
  297. Off();
  298. }
  299. health = spawnArgs.GetInt( "health", "0" );
  300. spawnArgs.GetString( "broken", "", brokenModel );
  301. spawnArgs.GetBool( "break", "0", breakOnTrigger );
  302. spawnArgs.GetInt( "count", "1", count );
  303. triggercount = 0;
  304. fadeFrom.Set( 1, 1, 1, 1 );
  305. fadeTo.Set( 1, 1, 1, 1 );
  306. fadeStart = 0;
  307. fadeEnd = 0;
  308. // if we have a health make light breakable
  309. if ( health ) {
  310. idStr model = spawnArgs.GetString( "model" ); // get the visual model
  311. if ( !model.Length() ) {
  312. gameLocal.Error( "Breakable light without a model set on entity #%d(%s)", entityNumber, name.c_str() );
  313. }
  314. fl.takedamage = true;
  315. // see if we need to create a broken model name
  316. needBroken = true;
  317. if ( model.Length() && !brokenModel.Length() ) {
  318. int pos;
  319. needBroken = false;
  320. pos = model.Find( "." );
  321. if ( pos < 0 ) {
  322. pos = model.Length();
  323. }
  324. if ( pos > 0 ) {
  325. model.Left( pos, brokenModel );
  326. }
  327. brokenModel += "_broken";
  328. if ( pos > 0 ) {
  329. brokenModel += &model[ pos ];
  330. }
  331. }
  332. // make sure the model gets cached
  333. if ( !renderModelManager->CheckModel( brokenModel ) ) {
  334. if ( needBroken ) {
  335. gameLocal.Error( "Model '%s' not found for entity %d(%s)", brokenModel.c_str(), entityNumber, name.c_str() );
  336. } else {
  337. brokenModel = "";
  338. }
  339. }
  340. GetPhysics()->SetContents( spawnArgs.GetBool( "nonsolid" ) ? 0 : CONTENTS_SOLID );
  341. // make sure the collision model gets cached
  342. idClipModel::CheckModel( brokenModel );
  343. }
  344. PostEventMS( &EV_PostSpawn, 0 );
  345. UpdateVisuals();
  346. }
  347. /*
  348. ================
  349. idLight::SetLightLevel
  350. ================
  351. */
  352. void idLight::SetLightLevel() {
  353. idVec3 color;
  354. float intensity;
  355. intensity = ( float )currentLevel / ( float )levels;
  356. color = baseColor * intensity;
  357. renderLight.shaderParms[ SHADERPARM_RED ] = color[ 0 ];
  358. renderLight.shaderParms[ SHADERPARM_GREEN ] = color[ 1 ];
  359. renderLight.shaderParms[ SHADERPARM_BLUE ] = color[ 2 ];
  360. renderEntity.shaderParms[ SHADERPARM_RED ] = color[ 0 ];
  361. renderEntity.shaderParms[ SHADERPARM_GREEN ]= color[ 1 ];
  362. renderEntity.shaderParms[ SHADERPARM_BLUE ] = color[ 2 ];
  363. PresentLightDefChange();
  364. PresentModelDefChange();
  365. }
  366. /*
  367. ================
  368. idLight::GetColor
  369. ================
  370. */
  371. void idLight::GetColor( idVec3 &out ) const {
  372. out[ 0 ] = renderLight.shaderParms[ SHADERPARM_RED ];
  373. out[ 1 ] = renderLight.shaderParms[ SHADERPARM_GREEN ];
  374. out[ 2 ] = renderLight.shaderParms[ SHADERPARM_BLUE ];
  375. }
  376. /*
  377. ================
  378. idLight::GetColor
  379. ================
  380. */
  381. void idLight::GetColor( idVec4 &out ) const {
  382. out[ 0 ] = renderLight.shaderParms[ SHADERPARM_RED ];
  383. out[ 1 ] = renderLight.shaderParms[ SHADERPARM_GREEN ];
  384. out[ 2 ] = renderLight.shaderParms[ SHADERPARM_BLUE ];
  385. out[ 3 ] = renderLight.shaderParms[ SHADERPARM_ALPHA ];
  386. }
  387. /*
  388. ================
  389. idLight::SetColor
  390. ================
  391. */
  392. void idLight::SetColor( float red, float green, float blue ) {
  393. baseColor.Set( red, green, blue );
  394. SetLightLevel();
  395. }
  396. /*
  397. ================
  398. idLight::SetColor
  399. ================
  400. */
  401. void idLight::SetColor( const idVec4 &color ) {
  402. baseColor = color.ToVec3();
  403. renderLight.shaderParms[ SHADERPARM_ALPHA ] = color[ 3 ];
  404. renderEntity.shaderParms[ SHADERPARM_ALPHA ] = color[ 3 ];
  405. SetLightLevel();
  406. }
  407. /*
  408. ================
  409. idLight::SetColor
  410. ================
  411. */
  412. void idLight::SetColor( const idVec3 &color ) {
  413. baseColor = color;
  414. SetLightLevel();
  415. }
  416. /*
  417. ================
  418. idLight::SetShader
  419. ================
  420. */
  421. void idLight::SetShader( const char *shadername ) {
  422. // allow this to be NULL
  423. renderLight.shader = declManager->FindMaterial( shadername, false );
  424. PresentLightDefChange();
  425. }
  426. /*
  427. ================
  428. idLight::SetLightParm
  429. ================
  430. */
  431. void idLight::SetLightParm( int parmnum, float value ) {
  432. if ( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) ) {
  433. gameLocal.Error( "shader parm index (%d) out of range", parmnum );
  434. return;
  435. }
  436. renderLight.shaderParms[ parmnum ] = value;
  437. PresentLightDefChange();
  438. }
  439. /*
  440. ================
  441. idLight::SetLightParms
  442. ================
  443. */
  444. void idLight::SetLightParms( float parm0, float parm1, float parm2, float parm3 ) {
  445. renderLight.shaderParms[ SHADERPARM_RED ] = parm0;
  446. renderLight.shaderParms[ SHADERPARM_GREEN ] = parm1;
  447. renderLight.shaderParms[ SHADERPARM_BLUE ] = parm2;
  448. renderLight.shaderParms[ SHADERPARM_ALPHA ] = parm3;
  449. renderEntity.shaderParms[ SHADERPARM_RED ] = parm0;
  450. renderEntity.shaderParms[ SHADERPARM_GREEN ] = parm1;
  451. renderEntity.shaderParms[ SHADERPARM_BLUE ] = parm2;
  452. renderEntity.shaderParms[ SHADERPARM_ALPHA ] = parm3;
  453. PresentLightDefChange();
  454. PresentModelDefChange();
  455. }
  456. /*
  457. ================
  458. idLight::SetRadiusXYZ
  459. ================
  460. */
  461. void idLight::SetRadiusXYZ( float x, float y, float z ) {
  462. renderLight.lightRadius[0] = x;
  463. renderLight.lightRadius[1] = y;
  464. renderLight.lightRadius[2] = z;
  465. PresentLightDefChange();
  466. }
  467. /*
  468. ================
  469. idLight::SetRadius
  470. ================
  471. */
  472. void idLight::SetRadius( float radius ) {
  473. renderLight.lightRadius[0] = renderLight.lightRadius[1] = renderLight.lightRadius[2] = radius;
  474. PresentLightDefChange();
  475. }
  476. /*
  477. ================
  478. idLight::On
  479. ================
  480. */
  481. void idLight::On() {
  482. currentLevel = levels;
  483. // offset the start time of the shader to sync it to the game time
  484. renderLight.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  485. if ( ( soundWasPlaying || refSound.waitfortrigger ) && refSound.shader ) {
  486. StartSoundShader( refSound.shader, SND_CHANNEL_ANY, 0, false, NULL );
  487. soundWasPlaying = false;
  488. }
  489. SetLightLevel();
  490. BecomeActive( TH_UPDATEVISUALS );
  491. }
  492. /*
  493. ================
  494. idLight::Off
  495. ================
  496. */
  497. void idLight::Off() {
  498. currentLevel = 0;
  499. // kill any sound it was making
  500. if ( refSound.referenceSound && refSound.referenceSound->CurrentlyPlaying() ) {
  501. StopSound( SND_CHANNEL_ANY, false );
  502. soundWasPlaying = true;
  503. }
  504. SetLightLevel();
  505. BecomeActive( TH_UPDATEVISUALS );
  506. }
  507. /*
  508. ================
  509. idLight::Fade
  510. ================
  511. */
  512. void idLight::Fade( const idVec4 &to, float fadeTime ) {
  513. GetColor( fadeFrom );
  514. fadeTo = to;
  515. fadeStart = gameLocal.time;
  516. fadeEnd = gameLocal.time + SEC2MS( fadeTime );
  517. BecomeActive( TH_THINK );
  518. }
  519. /*
  520. ================
  521. idLight::FadeOut
  522. ================
  523. */
  524. void idLight::FadeOut( float time ) {
  525. Fade( colorBlack, time );
  526. }
  527. /*
  528. ================
  529. idLight::FadeIn
  530. ================
  531. */
  532. void idLight::FadeIn( float time ) {
  533. idVec3 color;
  534. idVec4 color4;
  535. currentLevel = levels;
  536. spawnArgs.GetVector( "_color", "1 1 1", color );
  537. color4.Set( color.x, color.y, color.z, 1.0f );
  538. Fade( color4, time );
  539. }
  540. /*
  541. ================
  542. idLight::Killed
  543. ================
  544. */
  545. void idLight::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
  546. BecomeBroken( attacker );
  547. }
  548. /*
  549. ================
  550. idLight::BecomeBroken
  551. ================
  552. */
  553. void idLight::BecomeBroken( idEntity *activator ) {
  554. const char *damageDefName;
  555. fl.takedamage = false;
  556. if ( brokenModel.Length() ) {
  557. SetModel( brokenModel );
  558. if ( !spawnArgs.GetBool( "nonsolid" ) ) {
  559. GetPhysics()->SetClipModel( new (TAG_PHYSICS_CLIP_ENTITY) idClipModel( brokenModel.c_str() ), 1.0f );
  560. GetPhysics()->SetContents( CONTENTS_SOLID );
  561. }
  562. } else if ( spawnArgs.GetBool( "hideModelOnBreak" ) ) {
  563. SetModel( "" );
  564. GetPhysics()->SetContents( 0 );
  565. }
  566. if ( common->IsServer() ) {
  567. ServerSendEvent( EVENT_BECOMEBROKEN, NULL, true );
  568. if ( spawnArgs.GetString( "def_damage", "", &damageDefName ) ) {
  569. idVec3 origin = renderEntity.origin + renderEntity.bounds.GetCenter() * renderEntity.axis;
  570. gameLocal.RadiusDamage( origin, activator, activator, this, this, damageDefName );
  571. }
  572. }
  573. ActivateTargets( activator );
  574. // offset the start time of the shader to sync it to the game time
  575. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  576. renderLight.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  577. // set the state parm
  578. renderEntity.shaderParms[ SHADERPARM_MODE ] = 1;
  579. renderLight.shaderParms[ SHADERPARM_MODE ] = 1;
  580. // if the light has a sound, either start the alternate (broken) sound, or stop the sound
  581. const char *parm = spawnArgs.GetString( "snd_broken" );
  582. if ( refSound.shader || ( parm != NULL && *parm != NULL ) ) {
  583. StopSound( SND_CHANNEL_ANY, false );
  584. const idSoundShader *alternate = refSound.shader ? refSound.shader->GetAltSound() : declManager->FindSound( parm );
  585. if ( alternate ) {
  586. // start it with no diversity, so the leadin break sound plays
  587. refSound.referenceSound->StartSound( alternate, SND_CHANNEL_ANY, 0.0, 0 );
  588. }
  589. }
  590. parm = spawnArgs.GetString( "mtr_broken" );
  591. if ( parm != NULL && *parm != NULL ) {
  592. SetShader( parm );
  593. }
  594. UpdateVisuals();
  595. }
  596. /*
  597. ================
  598. idLight::PresentLightDefChange
  599. ================
  600. */
  601. void idLight::PresentLightDefChange() {
  602. // let the renderer apply it to the world
  603. if ( ( lightDefHandle != -1 ) ) {
  604. gameRenderWorld->UpdateLightDef( lightDefHandle, &renderLight );
  605. } else {
  606. lightDefHandle = gameRenderWorld->AddLightDef( &renderLight );
  607. }
  608. }
  609. /*
  610. ================
  611. idLight::PresentModelDefChange
  612. ================
  613. */
  614. void idLight::PresentModelDefChange() {
  615. if ( !renderEntity.hModel || IsHidden() ) {
  616. return;
  617. }
  618. // add to refresh list
  619. if ( modelDefHandle == -1 ) {
  620. modelDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
  621. } else {
  622. gameRenderWorld->UpdateEntityDef( modelDefHandle, &renderEntity );
  623. }
  624. }
  625. /*
  626. ================
  627. idLight::Present
  628. ================
  629. */
  630. void idLight::Present() {
  631. // don't present to the renderer if the entity hasn't changed
  632. if ( !( thinkFlags & TH_UPDATEVISUALS ) ) {
  633. return;
  634. }
  635. // add the model
  636. idEntity::Present();
  637. // current transformation
  638. renderLight.axis = localLightAxis * GetPhysics()->GetAxis();
  639. renderLight.origin = GetPhysics()->GetOrigin() + GetPhysics()->GetAxis() * localLightOrigin;
  640. // reference the sound for shader synced effects
  641. if ( lightParent ) {
  642. renderLight.referenceSound = lightParent->GetSoundEmitter();
  643. renderEntity.referenceSound = lightParent->GetSoundEmitter();
  644. }
  645. else {
  646. renderLight.referenceSound = refSound.referenceSound;
  647. renderEntity.referenceSound = refSound.referenceSound;
  648. }
  649. // update the renderLight and renderEntity to render the light and flare
  650. PresentLightDefChange();
  651. PresentModelDefChange();
  652. }
  653. /*
  654. ================
  655. idLight::Think
  656. ================
  657. */
  658. void idLight::Think() {
  659. idVec4 color;
  660. if ( thinkFlags & TH_THINK ) {
  661. if ( fadeEnd > 0 ) {
  662. if ( gameLocal.time < fadeEnd ) {
  663. color.Lerp( fadeFrom, fadeTo, ( float )( gameLocal.time - fadeStart ) / ( float )( fadeEnd - fadeStart ) );
  664. } else {
  665. color = fadeTo;
  666. fadeEnd = 0;
  667. BecomeInactive( TH_THINK );
  668. }
  669. SetColor( color );
  670. }
  671. }
  672. RunPhysics();
  673. Present();
  674. }
  675. /*
  676. ================
  677. idLight::ClientThink
  678. ================
  679. */
  680. void idLight::ClientThink( const int curTime, const float fraction, const bool predict ) {
  681. InterpolatePhysics( fraction );
  682. if( baseColor != nextBaseColor ) {
  683. baseColor = Lerp( previousBaseColor, nextBaseColor, fraction );
  684. SetColor( baseColor );
  685. BecomeActive( TH_UPDATEVISUALS );
  686. }
  687. Present();
  688. }
  689. /*
  690. ================
  691. idLight::GetPhysicsToSoundTransform
  692. ================
  693. */
  694. bool idLight::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
  695. origin = localLightOrigin + renderLight.lightCenter;
  696. axis = localLightAxis * GetPhysics()->GetAxis();
  697. return true;
  698. }
  699. /*
  700. ================
  701. idLight::FreeLightDef
  702. ================
  703. */
  704. void idLight::FreeLightDef() {
  705. if ( lightDefHandle != -1 ) {
  706. gameRenderWorld->FreeLightDef( lightDefHandle );
  707. lightDefHandle = -1;
  708. }
  709. }
  710. /*
  711. ================
  712. idLight::SaveState
  713. ================
  714. */
  715. void idLight::SaveState( idDict *args ) {
  716. int i, c = spawnArgs.GetNumKeyVals();
  717. for ( i = 0; i < c; i++ ) {
  718. const idKeyValue *pv = spawnArgs.GetKeyVal(i);
  719. if ( pv->GetKey().Find( "editor_", false ) >= 0 || pv->GetKey().Find( "parse_", false ) >= 0 ) {
  720. continue;
  721. }
  722. args->Set( pv->GetKey(), pv->GetValue() );
  723. }
  724. }
  725. /*
  726. ===============
  727. idLight::ShowEditingDialog
  728. ===============
  729. */
  730. void idLight::ShowEditingDialog() {
  731. }
  732. /*
  733. ================
  734. idLight::Event_SetShader
  735. ================
  736. */
  737. void idLight::Event_SetShader( const char *shadername ) {
  738. SetShader( shadername );
  739. }
  740. /*
  741. ================
  742. idLight::Event_GetLightParm
  743. ================
  744. */
  745. void idLight::Event_GetLightParm( int parmnum ) {
  746. if ( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) ) {
  747. gameLocal.Error( "shader parm index (%d) out of range", parmnum );
  748. return;
  749. }
  750. idThread::ReturnFloat( renderLight.shaderParms[ parmnum ] );
  751. }
  752. /*
  753. ================
  754. idLight::Event_SetLightParm
  755. ================
  756. */
  757. void idLight::Event_SetLightParm( int parmnum, float value ) {
  758. SetLightParm( parmnum, value );
  759. }
  760. /*
  761. ================
  762. idLight::Event_SetLightParms
  763. ================
  764. */
  765. void idLight::Event_SetLightParms( float parm0, float parm1, float parm2, float parm3 ) {
  766. SetLightParms( parm0, parm1, parm2, parm3 );
  767. }
  768. /*
  769. ================
  770. idLight::Event_SetRadiusXYZ
  771. ================
  772. */
  773. void idLight::Event_SetRadiusXYZ( float x, float y, float z ) {
  774. SetRadiusXYZ( x, y, z );
  775. }
  776. /*
  777. ================
  778. idLight::Event_SetRadius
  779. ================
  780. */
  781. void idLight::Event_SetRadius( float radius ) {
  782. SetRadius( radius );
  783. }
  784. /*
  785. ================
  786. idLight::Event_Hide
  787. ================
  788. */
  789. void idLight::Event_Hide() {
  790. Hide();
  791. PresentModelDefChange();
  792. Off();
  793. }
  794. /*
  795. ================
  796. idLight::Event_Show
  797. ================
  798. */
  799. void idLight::Event_Show() {
  800. Show();
  801. PresentModelDefChange();
  802. On();
  803. }
  804. /*
  805. ================
  806. idLight::Event_On
  807. ================
  808. */
  809. void idLight::Event_On() {
  810. On();
  811. }
  812. /*
  813. ================
  814. idLight::Event_Off
  815. ================
  816. */
  817. void idLight::Event_Off() {
  818. Off();
  819. }
  820. /*
  821. ================
  822. idLight::Event_ToggleOnOff
  823. ================
  824. */
  825. void idLight::Event_ToggleOnOff( idEntity *activator ) {
  826. triggercount++;
  827. if ( triggercount < count ) {
  828. return;
  829. }
  830. // reset trigger count
  831. triggercount = 0;
  832. if ( breakOnTrigger ) {
  833. BecomeBroken( activator );
  834. breakOnTrigger = false;
  835. return;
  836. }
  837. if ( !currentLevel ) {
  838. On();
  839. }
  840. else {
  841. currentLevel--;
  842. if ( !currentLevel ) {
  843. Off();
  844. }
  845. else {
  846. SetLightLevel();
  847. }
  848. }
  849. }
  850. /*
  851. ================
  852. idLight::Event_SetSoundHandles
  853. set the same sound def handle on all targeted lights
  854. ================
  855. */
  856. void idLight::Event_SetSoundHandles() {
  857. int i;
  858. idEntity *targetEnt;
  859. if ( !refSound.referenceSound ) {
  860. return;
  861. }
  862. for ( i = 0; i < targets.Num(); i++ ) {
  863. targetEnt = targets[ i ].GetEntity();
  864. if ( targetEnt != NULL && targetEnt->IsType( idLight::Type ) ) {
  865. idLight *light = static_cast<idLight*>(targetEnt);
  866. light->lightParent = this;
  867. // explicitly delete any sounds on the entity
  868. light->FreeSoundEmitter( true );
  869. // manually set the refSound to this light's refSound
  870. light->renderEntity.referenceSound = renderEntity.referenceSound;
  871. // update the renderEntity to the renderer
  872. light->UpdateVisuals();
  873. }
  874. }
  875. }
  876. /*
  877. ================
  878. idLight::Event_FadeOut
  879. ================
  880. */
  881. void idLight::Event_FadeOut( float time ) {
  882. FadeOut( time );
  883. }
  884. /*
  885. ================
  886. idLight::Event_FadeIn
  887. ================
  888. */
  889. void idLight::Event_FadeIn( float time ) {
  890. FadeIn( time );
  891. }
  892. /*
  893. ================
  894. idLight::ClientPredictionThink
  895. ================
  896. */
  897. void idLight::ClientPredictionThink() {
  898. Think();
  899. }
  900. /*
  901. ================
  902. idLight::WriteToSnapshot
  903. ================
  904. */
  905. void idLight::WriteToSnapshot( idBitMsg &msg ) const {
  906. GetPhysics()->WriteToSnapshot( msg );
  907. WriteBindToSnapshot( msg );
  908. msg.WriteByte( currentLevel );
  909. msg.WriteLong( PackColor( baseColor ) );
  910. // msg.WriteBits( lightParent.GetEntityNum(), GENTITYNUM_BITS );
  911. /* // only helps prediction
  912. msg.WriteLong( PackColor( fadeFrom ) );
  913. msg.WriteLong( PackColor( fadeTo ) );
  914. msg.WriteLong( fadeStart );
  915. msg.WriteLong( fadeEnd );
  916. */
  917. // FIXME: send renderLight.shader
  918. msg.WriteFloat( renderLight.lightRadius[0], 5, 10 );
  919. msg.WriteFloat( renderLight.lightRadius[1], 5, 10 );
  920. msg.WriteFloat( renderLight.lightRadius[2], 5, 10 );
  921. msg.WriteLong( PackColor( idVec4( renderLight.shaderParms[SHADERPARM_RED],
  922. renderLight.shaderParms[SHADERPARM_GREEN],
  923. renderLight.shaderParms[SHADERPARM_BLUE],
  924. renderLight.shaderParms[SHADERPARM_ALPHA] ) ) );
  925. msg.WriteFloat( renderLight.shaderParms[SHADERPARM_TIMESCALE], 5, 10 );
  926. msg.WriteLong( renderLight.shaderParms[SHADERPARM_TIMEOFFSET] );
  927. //msg.WriteByte( renderLight.shaderParms[SHADERPARM_DIVERSITY] );
  928. msg.WriteShort( renderLight.shaderParms[SHADERPARM_MODE] );
  929. WriteColorToSnapshot( msg );
  930. }
  931. /*
  932. ================
  933. idLight::ReadFromSnapshot
  934. ================
  935. */
  936. void idLight::ReadFromSnapshot( const idBitMsg &msg ) {
  937. idVec4 shaderColor;
  938. int oldCurrentLevel = currentLevel;
  939. idVec3 oldBaseColor = baseColor;
  940. previousBaseColor = nextBaseColor;
  941. GetPhysics()->ReadFromSnapshot( msg );
  942. ReadBindFromSnapshot( msg );
  943. currentLevel = msg.ReadByte();
  944. if ( currentLevel != oldCurrentLevel ) {
  945. // need to call On/Off for flickering lights to start/stop the sound
  946. // while doing it this way rather than through events, the flickering is out of sync between clients
  947. // but at least there is no question about saving the event and having them happening globally in the world
  948. if ( currentLevel ) {
  949. On();
  950. } else {
  951. Off();
  952. }
  953. }
  954. UnpackColor( msg.ReadLong(), nextBaseColor );
  955. // lightParentEntityNum = msg.ReadBits( GENTITYNUM_BITS );
  956. /* // only helps prediction
  957. UnpackColor( msg.ReadLong(), fadeFrom );
  958. UnpackColor( msg.ReadLong(), fadeTo );
  959. fadeStart = msg.ReadLong();
  960. fadeEnd = msg.ReadLong();
  961. */
  962. // FIXME: read renderLight.shader
  963. renderLight.lightRadius[0] = msg.ReadFloat( 5, 10 );
  964. renderLight.lightRadius[1] = msg.ReadFloat( 5, 10 );
  965. renderLight.lightRadius[2] = msg.ReadFloat( 5, 10 );
  966. UnpackColor( msg.ReadLong(), shaderColor );
  967. renderLight.shaderParms[SHADERPARM_RED] = shaderColor[0];
  968. renderLight.shaderParms[SHADERPARM_GREEN] = shaderColor[1];
  969. renderLight.shaderParms[SHADERPARM_BLUE] = shaderColor[2];
  970. renderLight.shaderParms[SHADERPARM_ALPHA] = shaderColor[3];
  971. renderLight.shaderParms[SHADERPARM_TIMESCALE] = msg.ReadFloat( 5, 10 );
  972. renderLight.shaderParms[SHADERPARM_TIMEOFFSET] = msg.ReadLong();
  973. //renderLight.shaderParms[SHADERPARM_DIVERSITY] = msg.ReadFloat();
  974. renderLight.shaderParms[SHADERPARM_MODE] = msg.ReadShort();
  975. ReadColorFromSnapshot( msg );
  976. if ( msg.HasChanged() ) {
  977. if ( ( currentLevel != oldCurrentLevel ) || ( previousBaseColor != nextBaseColor ) ) {
  978. SetLightLevel();
  979. } else {
  980. PresentLightDefChange();
  981. PresentModelDefChange();
  982. }
  983. }
  984. }
  985. /*
  986. ================
  987. idLight::ClientReceiveEvent
  988. ================
  989. */
  990. bool idLight::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
  991. switch( event ) {
  992. case EVENT_BECOMEBROKEN: {
  993. BecomeBroken( NULL );
  994. return true;
  995. }
  996. default: {
  997. return idEntity::ClientReceiveEvent( event, time, msg );
  998. }
  999. }
  1000. }