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