Light.cpp 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 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 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 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 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 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. memset( &renderLight, 0, sizeof( renderLight ) );
  156. localLightOrigin = vec3_zero;
  157. localLightAxis = mat3_identity;
  158. lightDefHandle = -1;
  159. levels = 0;
  160. currentLevel = 0;
  161. baseColor = vec3_zero;
  162. breakOnTrigger = false;
  163. count = 0;
  164. triggercount = 0;
  165. lightParent = NULL;
  166. fadeFrom.Set( 1, 1, 1, 1 );
  167. fadeTo.Set( 1, 1, 1, 1 );
  168. fadeStart = 0;
  169. fadeEnd = 0;
  170. soundWasPlaying = false;
  171. }
  172. /*
  173. ================
  174. idLight::~idLight
  175. ================
  176. */
  177. idLight::~idLight() {
  178. if ( lightDefHandle != -1 ) {
  179. gameRenderWorld->FreeLightDef( lightDefHandle );
  180. }
  181. }
  182. /*
  183. ================
  184. idLight::Save
  185. archives object for save game file
  186. ================
  187. */
  188. void idLight::Save( idSaveGame *savefile ) const {
  189. savefile->WriteRenderLight( renderLight );
  190. savefile->WriteBool( renderLight.prelightModel != NULL );
  191. savefile->WriteVec3( localLightOrigin );
  192. savefile->WriteMat3( localLightAxis );
  193. savefile->WriteString( brokenModel );
  194. savefile->WriteInt( levels );
  195. savefile->WriteInt( currentLevel );
  196. savefile->WriteVec3( baseColor );
  197. savefile->WriteBool( breakOnTrigger );
  198. savefile->WriteInt( count );
  199. savefile->WriteInt( triggercount );
  200. savefile->WriteObject( lightParent );
  201. savefile->WriteVec4( fadeFrom );
  202. savefile->WriteVec4( fadeTo );
  203. savefile->WriteInt( fadeStart );
  204. savefile->WriteInt( fadeEnd );
  205. savefile->WriteBool( soundWasPlaying );
  206. }
  207. /*
  208. ================
  209. idLight::Restore
  210. unarchives object from save game file
  211. ================
  212. */
  213. void idLight::Restore( idRestoreGame *savefile ) {
  214. bool hadPrelightModel;
  215. savefile->ReadRenderLight( renderLight );
  216. savefile->ReadBool( hadPrelightModel );
  217. renderLight.prelightModel = renderModelManager->CheckModel( va( "_prelight_%s", name.c_str() ) );
  218. if ( ( renderLight.prelightModel == NULL ) && hadPrelightModel ) {
  219. assert( 0 );
  220. if ( developer.GetBool() ) {
  221. // we really want to know if this happens
  222. gameLocal.Error( "idLight::Restore: prelightModel '_prelight_%s' not found", name.c_str() );
  223. } else {
  224. // but let it slide after release
  225. gameLocal.Warning( "idLight::Restore: prelightModel '_prelight_%s' not found", name.c_str() );
  226. }
  227. }
  228. savefile->ReadVec3( localLightOrigin );
  229. savefile->ReadMat3( localLightAxis );
  230. savefile->ReadString( brokenModel );
  231. savefile->ReadInt( levels );
  232. savefile->ReadInt( currentLevel );
  233. savefile->ReadVec3( baseColor );
  234. savefile->ReadBool( breakOnTrigger );
  235. savefile->ReadInt( count );
  236. savefile->ReadInt( triggercount );
  237. savefile->ReadObject( reinterpret_cast<idClass *&>( lightParent ) );
  238. savefile->ReadVec4( fadeFrom );
  239. savefile->ReadVec4( fadeTo );
  240. savefile->ReadInt( fadeStart );
  241. savefile->ReadInt( fadeEnd );
  242. savefile->ReadBool( soundWasPlaying );
  243. lightDefHandle = -1;
  244. SetLightLevel();
  245. }
  246. /*
  247. ================
  248. idLight::Spawn
  249. ================
  250. */
  251. void idLight::Spawn( void ) {
  252. bool start_off;
  253. bool needBroken;
  254. const char *demonic_shader;
  255. // do the parsing the same way dmap and the editor do
  256. gameEdit->ParseSpawnArgsToRenderLight( &spawnArgs, &renderLight );
  257. // we need the origin and axis relative to the physics origin/axis
  258. localLightOrigin = ( renderLight.origin - GetPhysics()->GetOrigin() ) * GetPhysics()->GetAxis().Transpose();
  259. localLightAxis = renderLight.axis * GetPhysics()->GetAxis().Transpose();
  260. // set the base color from the shader parms
  261. baseColor.Set( renderLight.shaderParms[ SHADERPARM_RED ], renderLight.shaderParms[ SHADERPARM_GREEN ], renderLight.shaderParms[ SHADERPARM_BLUE ] );
  262. // set the number of light levels
  263. spawnArgs.GetInt( "levels", "1", levels );
  264. currentLevel = levels;
  265. if ( levels <= 0 ) {
  266. gameLocal.Error( "Invalid light level set on entity #%d(%s)", entityNumber, name.c_str() );
  267. }
  268. // make sure the demonic shader is cached
  269. if ( spawnArgs.GetString( "mat_demonic", NULL, &demonic_shader ) ) {
  270. declManager->FindType( DECL_MATERIAL, demonic_shader );
  271. }
  272. // game specific functionality, not mirrored in
  273. // editor or dmap light parsing
  274. // also put the light texture on the model, so light flares
  275. // can get the current intensity of the light
  276. renderEntity.referenceShader = renderLight.shader;
  277. lightDefHandle = -1; // no static version yet
  278. // see if an optimized shadow volume exists
  279. // the renderer will ignore this value after a light has been moved,
  280. // but there may still be a chance to get it wrong if the game moves
  281. // a light before the first present, and doesn't clear the prelight
  282. renderLight.prelightModel = 0;
  283. if ( name[ 0 ] ) {
  284. // this will return 0 if not found
  285. renderLight.prelightModel = renderModelManager->CheckModel( va( "_prelight_%s", name.c_str() ) );
  286. }
  287. spawnArgs.GetBool( "start_off", "0", start_off );
  288. if ( start_off ) {
  289. Off();
  290. }
  291. #ifdef CTF
  292. // Midnight CTF
  293. if ( gameLocal.mpGame.IsGametypeFlagBased() && gameLocal.serverInfo.GetBool("si_midnight") && !spawnArgs.GetBool("midnight_override") ) {
  294. Off();
  295. }
  296. #endif
  297. health = spawnArgs.GetInt( "health", "0" );
  298. spawnArgs.GetString( "broken", "", brokenModel );
  299. spawnArgs.GetBool( "break", "0", breakOnTrigger );
  300. spawnArgs.GetInt( "count", "1", count );
  301. triggercount = 0;
  302. fadeFrom.Set( 1, 1, 1, 1 );
  303. fadeTo.Set( 1, 1, 1, 1 );
  304. fadeStart = 0;
  305. fadeEnd = 0;
  306. // if we have a health make light breakable
  307. if ( health ) {
  308. idStr model = spawnArgs.GetString( "model" ); // get the visual model
  309. if ( !model.Length() ) {
  310. gameLocal.Error( "Breakable light without a model set on entity #%d(%s)", entityNumber, name.c_str() );
  311. }
  312. fl.takedamage = true;
  313. // see if we need to create a broken model name
  314. needBroken = true;
  315. if ( model.Length() && !brokenModel.Length() ) {
  316. int pos;
  317. needBroken = false;
  318. pos = model.Find( "." );
  319. if ( pos < 0 ) {
  320. pos = model.Length();
  321. }
  322. if ( pos > 0 ) {
  323. model.Left( pos, brokenModel );
  324. }
  325. brokenModel += "_broken";
  326. if ( pos > 0 ) {
  327. brokenModel += &model[ pos ];
  328. }
  329. }
  330. // make sure the model gets cached
  331. if ( !renderModelManager->CheckModel( brokenModel ) ) {
  332. if ( needBroken ) {
  333. gameLocal.Error( "Model '%s' not found for entity %d(%s)", brokenModel.c_str(), entityNumber, name.c_str() );
  334. } else {
  335. brokenModel = "";
  336. }
  337. }
  338. GetPhysics()->SetContents( spawnArgs.GetBool( "nonsolid" ) ? 0 : CONTENTS_SOLID );
  339. // make sure the collision model gets cached
  340. idClipModel::CheckModel( brokenModel );
  341. }
  342. PostEventMS( &EV_PostSpawn, 0 );
  343. UpdateVisuals();
  344. }
  345. /*
  346. ================
  347. idLight::SetLightLevel
  348. ================
  349. */
  350. void idLight::SetLightLevel( void ) {
  351. idVec3 color;
  352. float intensity;
  353. intensity = ( float )currentLevel / ( float )levels;
  354. color = baseColor * intensity;
  355. renderLight.shaderParms[ SHADERPARM_RED ] = color[ 0 ];
  356. renderLight.shaderParms[ SHADERPARM_GREEN ] = color[ 1 ];
  357. renderLight.shaderParms[ SHADERPARM_BLUE ] = color[ 2 ];
  358. renderEntity.shaderParms[ SHADERPARM_RED ] = color[ 0 ];
  359. renderEntity.shaderParms[ SHADERPARM_GREEN ]= color[ 1 ];
  360. renderEntity.shaderParms[ SHADERPARM_BLUE ] = color[ 2 ];
  361. PresentLightDefChange();
  362. PresentModelDefChange();
  363. }
  364. /*
  365. ================
  366. idLight::GetColor
  367. ================
  368. */
  369. void idLight::GetColor( idVec3 &out ) const {
  370. out[ 0 ] = renderLight.shaderParms[ SHADERPARM_RED ];
  371. out[ 1 ] = renderLight.shaderParms[ SHADERPARM_GREEN ];
  372. out[ 2 ] = renderLight.shaderParms[ SHADERPARM_BLUE ];
  373. }
  374. /*
  375. ================
  376. idLight::GetColor
  377. ================
  378. */
  379. void idLight::GetColor( idVec4 &out ) const {
  380. out[ 0 ] = renderLight.shaderParms[ SHADERPARM_RED ];
  381. out[ 1 ] = renderLight.shaderParms[ SHADERPARM_GREEN ];
  382. out[ 2 ] = renderLight.shaderParms[ SHADERPARM_BLUE ];
  383. out[ 3 ] = renderLight.shaderParms[ SHADERPARM_ALPHA ];
  384. }
  385. /*
  386. ================
  387. idLight::SetColor
  388. ================
  389. */
  390. void idLight::SetColor( float red, float green, float blue ) {
  391. baseColor.Set( red, green, blue );
  392. SetLightLevel();
  393. }
  394. /*
  395. ================
  396. idLight::SetColor
  397. ================
  398. */
  399. void idLight::SetColor( const idVec4 &color ) {
  400. baseColor = color.ToVec3();
  401. renderLight.shaderParms[ SHADERPARM_ALPHA ] = color[ 3 ];
  402. renderEntity.shaderParms[ SHADERPARM_ALPHA ] = color[ 3 ];
  403. SetLightLevel();
  404. }
  405. /*
  406. ================
  407. idLight::SetShader
  408. ================
  409. */
  410. void idLight::SetShader( const char *shadername ) {
  411. // allow this to be NULL
  412. renderLight.shader = declManager->FindMaterial( shadername, false );
  413. PresentLightDefChange();
  414. }
  415. /*
  416. ================
  417. idLight::SetLightParm
  418. ================
  419. */
  420. void idLight::SetLightParm( int parmnum, float value ) {
  421. if ( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) ) {
  422. gameLocal.Error( "shader parm index (%d) out of range", parmnum );
  423. }
  424. renderLight.shaderParms[ parmnum ] = value;
  425. PresentLightDefChange();
  426. }
  427. /*
  428. ================
  429. idLight::SetLightParms
  430. ================
  431. */
  432. void idLight::SetLightParms( float parm0, float parm1, float parm2, float parm3 ) {
  433. renderLight.shaderParms[ SHADERPARM_RED ] = parm0;
  434. renderLight.shaderParms[ SHADERPARM_GREEN ] = parm1;
  435. renderLight.shaderParms[ SHADERPARM_BLUE ] = parm2;
  436. renderLight.shaderParms[ SHADERPARM_ALPHA ] = parm3;
  437. renderEntity.shaderParms[ SHADERPARM_RED ] = parm0;
  438. renderEntity.shaderParms[ SHADERPARM_GREEN ] = parm1;
  439. renderEntity.shaderParms[ SHADERPARM_BLUE ] = parm2;
  440. renderEntity.shaderParms[ SHADERPARM_ALPHA ] = parm3;
  441. PresentLightDefChange();
  442. PresentModelDefChange();
  443. }
  444. /*
  445. ================
  446. idLight::SetRadiusXYZ
  447. ================
  448. */
  449. void idLight::SetRadiusXYZ( float x, float y, float z ) {
  450. renderLight.lightRadius[0] = x;
  451. renderLight.lightRadius[1] = y;
  452. renderLight.lightRadius[2] = z;
  453. PresentLightDefChange();
  454. }
  455. /*
  456. ================
  457. idLight::SetRadius
  458. ================
  459. */
  460. void idLight::SetRadius( float radius ) {
  461. renderLight.lightRadius[0] = renderLight.lightRadius[1] = renderLight.lightRadius[2] = radius;
  462. PresentLightDefChange();
  463. }
  464. /*
  465. ================
  466. idLight::On
  467. ================
  468. */
  469. void idLight::On( void ) {
  470. currentLevel = levels;
  471. // offset the start time of the shader to sync it to the game time
  472. renderLight.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  473. if ( ( soundWasPlaying || refSound.waitfortrigger ) && refSound.shader ) {
  474. StartSoundShader( refSound.shader, SND_CHANNEL_ANY, 0, false, NULL );
  475. soundWasPlaying = false;
  476. }
  477. SetLightLevel();
  478. BecomeActive( TH_UPDATEVISUALS );
  479. }
  480. /*
  481. ================
  482. idLight::Off
  483. ================
  484. */
  485. void idLight::Off( void ) {
  486. currentLevel = 0;
  487. // kill any sound it was making
  488. if ( refSound.referenceSound && refSound.referenceSound->CurrentlyPlaying() ) {
  489. StopSound( SND_CHANNEL_ANY, false );
  490. soundWasPlaying = true;
  491. }
  492. SetLightLevel();
  493. BecomeActive( TH_UPDATEVISUALS );
  494. }
  495. /*
  496. ================
  497. idLight::Fade
  498. ================
  499. */
  500. void idLight::Fade( const idVec4 &to, float fadeTime ) {
  501. GetColor( fadeFrom );
  502. fadeTo = to;
  503. fadeStart = gameLocal.time;
  504. fadeEnd = gameLocal.time + SEC2MS( fadeTime );
  505. BecomeActive( TH_THINK );
  506. }
  507. /*
  508. ================
  509. idLight::FadeOut
  510. ================
  511. */
  512. void idLight::FadeOut( float time ) {
  513. Fade( colorBlack, time );
  514. }
  515. /*
  516. ================
  517. idLight::FadeIn
  518. ================
  519. */
  520. void idLight::FadeIn( float time ) {
  521. idVec3 color;
  522. idVec4 color4;
  523. currentLevel = levels;
  524. spawnArgs.GetVector( "_color", "1 1 1", color );
  525. color4.Set( color.x, color.y, color.z, 1.0f );
  526. Fade( color4, time );
  527. }
  528. /*
  529. ================
  530. idLight::Killed
  531. ================
  532. */
  533. void idLight::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
  534. BecomeBroken( attacker );
  535. }
  536. /*
  537. ================
  538. idLight::BecomeBroken
  539. ================
  540. */
  541. void idLight::BecomeBroken( idEntity *activator ) {
  542. const char *damageDefName;
  543. fl.takedamage = false;
  544. if ( brokenModel.Length() ) {
  545. SetModel( brokenModel );
  546. if ( !spawnArgs.GetBool( "nonsolid" ) ) {
  547. GetPhysics()->SetClipModel( new idClipModel( brokenModel.c_str() ), 1.0f );
  548. GetPhysics()->SetContents( CONTENTS_SOLID );
  549. }
  550. } else if ( spawnArgs.GetBool( "hideModelOnBreak" ) ) {
  551. SetModel( "" );
  552. GetPhysics()->SetContents( 0 );
  553. }
  554. if ( gameLocal.isServer ) {
  555. ServerSendEvent( EVENT_BECOMEBROKEN, NULL, true, -1 );
  556. if ( spawnArgs.GetString( "def_damage", "", &damageDefName ) ) {
  557. idVec3 origin = renderEntity.origin + renderEntity.bounds.GetCenter() * renderEntity.axis;
  558. gameLocal.RadiusDamage( origin, activator, activator, this, this, damageDefName );
  559. }
  560. }
  561. ActivateTargets( activator );
  562. // offset the start time of the shader to sync it to the game time
  563. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  564. renderLight.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  565. // set the state parm
  566. renderEntity.shaderParms[ SHADERPARM_MODE ] = 1;
  567. renderLight.shaderParms[ SHADERPARM_MODE ] = 1;
  568. // if the light has a sound, either start the alternate (broken) sound, or stop the sound
  569. const char *parm = spawnArgs.GetString( "snd_broken" );
  570. if ( refSound.shader || ( parm && *parm ) ) {
  571. StopSound( SND_CHANNEL_ANY, false );
  572. const idSoundShader *alternate = refSound.shader ? refSound.shader->GetAltSound() : declManager->FindSound( parm );
  573. if ( alternate ) {
  574. // start it with no diversity, so the leadin break sound plays
  575. refSound.referenceSound->StartSound( alternate, SND_CHANNEL_ANY, 0.0, 0 );
  576. }
  577. }
  578. parm = spawnArgs.GetString( "mtr_broken" );
  579. if ( parm && *parm ) {
  580. SetShader( parm );
  581. }
  582. UpdateVisuals();
  583. }
  584. /*
  585. ================
  586. idLight::PresentLightDefChange
  587. ================
  588. */
  589. void idLight::PresentLightDefChange( void ) {
  590. // let the renderer apply it to the world
  591. if ( ( lightDefHandle != -1 ) ) {
  592. gameRenderWorld->UpdateLightDef( lightDefHandle, &renderLight );
  593. } else {
  594. lightDefHandle = gameRenderWorld->AddLightDef( &renderLight );
  595. }
  596. }
  597. /*
  598. ================
  599. idLight::PresentModelDefChange
  600. ================
  601. */
  602. void idLight::PresentModelDefChange( void ) {
  603. if ( !renderEntity.hModel || IsHidden() ) {
  604. return;
  605. }
  606. // add to refresh list
  607. if ( modelDefHandle == -1 ) {
  608. modelDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
  609. } else {
  610. gameRenderWorld->UpdateEntityDef( modelDefHandle, &renderEntity );
  611. }
  612. }
  613. /*
  614. ================
  615. idLight::Present
  616. ================
  617. */
  618. void idLight::Present( void ) {
  619. // don't present to the renderer if the entity hasn't changed
  620. if ( !( thinkFlags & TH_UPDATEVISUALS ) ) {
  621. return;
  622. }
  623. // add the model
  624. idEntity::Present();
  625. // current transformation
  626. renderLight.axis = localLightAxis * GetPhysics()->GetAxis();
  627. renderLight.origin = GetPhysics()->GetOrigin() + GetPhysics()->GetAxis() * localLightOrigin;
  628. // reference the sound for shader synced effects
  629. if ( lightParent ) {
  630. renderLight.referenceSound = lightParent->GetSoundEmitter();
  631. renderEntity.referenceSound = lightParent->GetSoundEmitter();
  632. }
  633. else {
  634. renderLight.referenceSound = refSound.referenceSound;
  635. renderEntity.referenceSound = refSound.referenceSound;
  636. }
  637. // update the renderLight and renderEntity to render the light and flare
  638. PresentLightDefChange();
  639. PresentModelDefChange();
  640. }
  641. /*
  642. ================
  643. idLight::Think
  644. ================
  645. */
  646. void idLight::Think( void ) {
  647. idVec4 color;
  648. if ( thinkFlags & TH_THINK ) {
  649. if ( fadeEnd > 0 ) {
  650. if ( gameLocal.time < fadeEnd ) {
  651. color.Lerp( fadeFrom, fadeTo, ( float )( gameLocal.time - fadeStart ) / ( float )( fadeEnd - fadeStart ) );
  652. } else {
  653. color = fadeTo;
  654. fadeEnd = 0;
  655. BecomeInactive( TH_THINK );
  656. }
  657. SetColor( color );
  658. }
  659. }
  660. RunPhysics();
  661. Present();
  662. }
  663. /*
  664. ================
  665. idLight::GetPhysicsToSoundTransform
  666. ================
  667. */
  668. bool idLight::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
  669. origin = localLightOrigin + renderLight.lightCenter;
  670. axis = localLightAxis * GetPhysics()->GetAxis();
  671. return true;
  672. }
  673. /*
  674. ================
  675. idLight::FreeLightDef
  676. ================
  677. */
  678. void idLight::FreeLightDef( void ) {
  679. if ( lightDefHandle != -1 ) {
  680. gameRenderWorld->FreeLightDef( lightDefHandle );
  681. lightDefHandle = -1;
  682. }
  683. }
  684. /*
  685. ================
  686. idLight::SaveState
  687. ================
  688. */
  689. void idLight::SaveState( idDict *args ) {
  690. int i, c = spawnArgs.GetNumKeyVals();
  691. for ( i = 0; i < c; i++ ) {
  692. const idKeyValue *pv = spawnArgs.GetKeyVal(i);
  693. if ( pv->GetKey().Find( "editor_", false ) >= 0 || pv->GetKey().Find( "parse_", false ) >= 0 ) {
  694. continue;
  695. }
  696. args->Set( pv->GetKey(), pv->GetValue() );
  697. }
  698. }
  699. /*
  700. ===============
  701. idLight::ShowEditingDialog
  702. ===============
  703. */
  704. void idLight::ShowEditingDialog( void ) {
  705. if ( g_editEntityMode.GetInteger() == 1 ) {
  706. common->InitTool( EDITOR_LIGHT, &spawnArgs );
  707. } else {
  708. common->InitTool( EDITOR_SOUND, &spawnArgs );
  709. }
  710. }
  711. /*
  712. ================
  713. idLight::Event_SetShader
  714. ================
  715. */
  716. void idLight::Event_SetShader( const char *shadername ) {
  717. SetShader( shadername );
  718. }
  719. /*
  720. ================
  721. idLight::Event_GetLightParm
  722. ================
  723. */
  724. void idLight::Event_GetLightParm( int parmnum ) {
  725. if ( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) ) {
  726. gameLocal.Error( "shader parm index (%d) out of range", parmnum );
  727. }
  728. idThread::ReturnFloat( renderLight.shaderParms[ parmnum ] );
  729. }
  730. /*
  731. ================
  732. idLight::Event_SetLightParm
  733. ================
  734. */
  735. void idLight::Event_SetLightParm( int parmnum, float value ) {
  736. SetLightParm( parmnum, value );
  737. }
  738. /*
  739. ================
  740. idLight::Event_SetLightParms
  741. ================
  742. */
  743. void idLight::Event_SetLightParms( float parm0, float parm1, float parm2, float parm3 ) {
  744. SetLightParms( parm0, parm1, parm2, parm3 );
  745. }
  746. /*
  747. ================
  748. idLight::Event_SetRadiusXYZ
  749. ================
  750. */
  751. void idLight::Event_SetRadiusXYZ( float x, float y, float z ) {
  752. SetRadiusXYZ( x, y, z );
  753. }
  754. /*
  755. ================
  756. idLight::Event_SetRadius
  757. ================
  758. */
  759. void idLight::Event_SetRadius( float radius ) {
  760. SetRadius( radius );
  761. }
  762. /*
  763. ================
  764. idLight::Event_Hide
  765. ================
  766. */
  767. void idLight::Event_Hide( void ) {
  768. Hide();
  769. PresentModelDefChange();
  770. Off();
  771. }
  772. /*
  773. ================
  774. idLight::Event_Show
  775. ================
  776. */
  777. void idLight::Event_Show( void ) {
  778. Show();
  779. PresentModelDefChange();
  780. On();
  781. }
  782. /*
  783. ================
  784. idLight::Event_On
  785. ================
  786. */
  787. void idLight::Event_On( void ) {
  788. On();
  789. }
  790. /*
  791. ================
  792. idLight::Event_Off
  793. ================
  794. */
  795. void idLight::Event_Off( void ) {
  796. Off();
  797. }
  798. /*
  799. ================
  800. idLight::Event_ToggleOnOff
  801. ================
  802. */
  803. void idLight::Event_ToggleOnOff( idEntity *activator ) {
  804. triggercount++;
  805. if ( triggercount < count ) {
  806. return;
  807. }
  808. // reset trigger count
  809. triggercount = 0;
  810. if ( breakOnTrigger ) {
  811. BecomeBroken( activator );
  812. breakOnTrigger = false;
  813. return;
  814. }
  815. if ( !currentLevel ) {
  816. On();
  817. }
  818. else {
  819. currentLevel--;
  820. if ( !currentLevel ) {
  821. Off();
  822. }
  823. else {
  824. SetLightLevel();
  825. }
  826. }
  827. }
  828. /*
  829. ================
  830. idLight::Event_SetSoundHandles
  831. set the same sound def handle on all targeted lights
  832. ================
  833. */
  834. void idLight::Event_SetSoundHandles( void ) {
  835. int i;
  836. idEntity *targetEnt;
  837. if ( !refSound.referenceSound ) {
  838. return;
  839. }
  840. for ( i = 0; i < targets.Num(); i++ ) {
  841. targetEnt = targets[ i ].GetEntity();
  842. if ( targetEnt && targetEnt->IsType( idLight::Type ) ) {
  843. idLight *light = static_cast<idLight*>(targetEnt);
  844. light->lightParent = this;
  845. // explicitly delete any sounds on the entity
  846. light->FreeSoundEmitter( true );
  847. // manually set the refSound to this light's refSound
  848. light->renderEntity.referenceSound = renderEntity.referenceSound;
  849. // update the renderEntity to the renderer
  850. light->UpdateVisuals();
  851. }
  852. }
  853. }
  854. /*
  855. ================
  856. idLight::Event_FadeOut
  857. ================
  858. */
  859. void idLight::Event_FadeOut( float time ) {
  860. FadeOut( time );
  861. }
  862. /*
  863. ================
  864. idLight::Event_FadeIn
  865. ================
  866. */
  867. void idLight::Event_FadeIn( float time ) {
  868. FadeIn( time );
  869. }
  870. /*
  871. ================
  872. idLight::ClientPredictionThink
  873. ================
  874. */
  875. void idLight::ClientPredictionThink( void ) {
  876. Think();
  877. }
  878. /*
  879. ================
  880. idLight::WriteToSnapshot
  881. ================
  882. */
  883. void idLight::WriteToSnapshot( idBitMsgDelta &msg ) const {
  884. GetPhysics()->WriteToSnapshot( msg );
  885. WriteBindToSnapshot( msg );
  886. msg.WriteByte( currentLevel );
  887. msg.WriteLong( PackColor( baseColor ) );
  888. // msg.WriteBits( lightParent.GetEntityNum(), GENTITYNUM_BITS );
  889. /* // only helps prediction
  890. msg.WriteLong( PackColor( fadeFrom ) );
  891. msg.WriteLong( PackColor( fadeTo ) );
  892. msg.WriteLong( fadeStart );
  893. msg.WriteLong( fadeEnd );
  894. */
  895. // FIXME: send renderLight.shader
  896. msg.WriteFloat( renderLight.lightRadius[0], 5, 10 );
  897. msg.WriteFloat( renderLight.lightRadius[1], 5, 10 );
  898. msg.WriteFloat( renderLight.lightRadius[2], 5, 10 );
  899. msg.WriteLong( PackColor( idVec4( renderLight.shaderParms[SHADERPARM_RED],
  900. renderLight.shaderParms[SHADERPARM_GREEN],
  901. renderLight.shaderParms[SHADERPARM_BLUE],
  902. renderLight.shaderParms[SHADERPARM_ALPHA] ) ) );
  903. msg.WriteFloat( renderLight.shaderParms[SHADERPARM_TIMESCALE], 5, 10 );
  904. msg.WriteLong( renderLight.shaderParms[SHADERPARM_TIMEOFFSET] );
  905. //msg.WriteByte( renderLight.shaderParms[SHADERPARM_DIVERSITY] );
  906. msg.WriteShort( renderLight.shaderParms[SHADERPARM_MODE] );
  907. WriteColorToSnapshot( msg );
  908. }
  909. /*
  910. ================
  911. idLight::ReadFromSnapshot
  912. ================
  913. */
  914. void idLight::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  915. idVec4 shaderColor;
  916. int oldCurrentLevel = currentLevel;
  917. idVec3 oldBaseColor = baseColor;
  918. GetPhysics()->ReadFromSnapshot( msg );
  919. ReadBindFromSnapshot( msg );
  920. currentLevel = msg.ReadByte();
  921. if ( currentLevel != oldCurrentLevel ) {
  922. // need to call On/Off for flickering lights to start/stop the sound
  923. // while doing it this way rather than through events, the flickering is out of sync between clients
  924. // but at least there is no question about saving the event and having them happening globally in the world
  925. if ( currentLevel ) {
  926. On();
  927. } else {
  928. Off();
  929. }
  930. }
  931. UnpackColor( msg.ReadLong(), baseColor );
  932. // lightParentEntityNum = msg.ReadBits( GENTITYNUM_BITS );
  933. /* // only helps prediction
  934. UnpackColor( msg.ReadLong(), fadeFrom );
  935. UnpackColor( msg.ReadLong(), fadeTo );
  936. fadeStart = msg.ReadLong();
  937. fadeEnd = msg.ReadLong();
  938. */
  939. // FIXME: read renderLight.shader
  940. renderLight.lightRadius[0] = msg.ReadFloat( 5, 10 );
  941. renderLight.lightRadius[1] = msg.ReadFloat( 5, 10 );
  942. renderLight.lightRadius[2] = msg.ReadFloat( 5, 10 );
  943. UnpackColor( msg.ReadLong(), shaderColor );
  944. renderLight.shaderParms[SHADERPARM_RED] = shaderColor[0];
  945. renderLight.shaderParms[SHADERPARM_GREEN] = shaderColor[1];
  946. renderLight.shaderParms[SHADERPARM_BLUE] = shaderColor[2];
  947. renderLight.shaderParms[SHADERPARM_ALPHA] = shaderColor[3];
  948. renderLight.shaderParms[SHADERPARM_TIMESCALE] = msg.ReadFloat( 5, 10 );
  949. renderLight.shaderParms[SHADERPARM_TIMEOFFSET] = msg.ReadLong();
  950. //renderLight.shaderParms[SHADERPARM_DIVERSITY] = msg.ReadFloat();
  951. renderLight.shaderParms[SHADERPARM_MODE] = msg.ReadShort();
  952. ReadColorFromSnapshot( msg );
  953. if ( msg.HasChanged() ) {
  954. if ( ( currentLevel != oldCurrentLevel ) || ( baseColor != oldBaseColor ) ) {
  955. SetLightLevel();
  956. } else {
  957. PresentLightDefChange();
  958. PresentModelDefChange();
  959. }
  960. }
  961. }
  962. /*
  963. ================
  964. idLight::ClientReceiveEvent
  965. ================
  966. */
  967. bool idLight::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
  968. switch( event ) {
  969. case EVENT_BECOMEBROKEN: {
  970. BecomeBroken( NULL );
  971. return true;
  972. }
  973. default: {
  974. return idEntity::ClientReceiveEvent( event, time, msg );
  975. }
  976. }
  977. return false;
  978. }