]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/d3xp/Light.cpp
hello world
[icculus/iodoom3.git] / neo / d3xp / 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 #ifdef CTF
360     // Midnight CTF
361     if ( gameLocal.mpGame.IsGametypeFlagBased() && gameLocal.serverInfo.GetBool("si_midnight") && !spawnArgs.GetBool("midnight_override") ) {
362         Off();
363     }
364 #endif
365     
366         health = spawnArgs.GetInt( "health", "0" );
367         spawnArgs.GetString( "broken", "", brokenModel );
368         spawnArgs.GetBool( "break", "0", breakOnTrigger );
369         spawnArgs.GetInt( "count", "1", count );
370
371         triggercount = 0;
372
373         fadeFrom.Set( 1, 1, 1, 1 );
374         fadeTo.Set( 1, 1, 1, 1 );
375         fadeStart                       = 0;
376         fadeEnd                         = 0;
377
378         // if we have a health make light breakable
379         if ( health ) {
380                 idStr model = spawnArgs.GetString( "model" );           // get the visual model
381                 if ( !model.Length() ) {
382                         gameLocal.Error( "Breakable light without a model set on entity #%d(%s)", entityNumber, name.c_str() );
383                 }
384
385                 fl.takedamage   = true;
386
387                 // see if we need to create a broken model name
388                 needBroken = true;
389                 if ( model.Length() && !brokenModel.Length() ) {
390                         int     pos;
391
392                         needBroken = false;
393                 
394                         pos = model.Find( "." );
395                         if ( pos < 0 ) {
396                                 pos = model.Length();
397                         }
398                         if ( pos > 0 ) {
399                                 model.Left( pos, brokenModel );
400                         }
401                         brokenModel += "_broken";
402                         if ( pos > 0 ) {
403                                 brokenModel += &model[ pos ];
404                         }
405                 }
406         
407                 // make sure the model gets cached
408                 if ( !renderModelManager->CheckModel( brokenModel ) ) {
409                         if ( needBroken ) {
410                                 gameLocal.Error( "Model '%s' not found for entity %d(%s)", brokenModel.c_str(), entityNumber, name.c_str() );
411                         } else {
412                                 brokenModel = "";
413                         }
414                 }
415
416                 GetPhysics()->SetContents( spawnArgs.GetBool( "nonsolid" ) ? 0 : CONTENTS_SOLID );
417         
418                 // make sure the collision model gets cached
419                 idClipModel::CheckModel( brokenModel );
420         }
421
422         PostEventMS( &EV_PostSpawn, 0 );
423
424         UpdateVisuals();
425 }
426
427 /*
428 ================
429 idLight::SetLightLevel
430 ================
431 */
432 void idLight::SetLightLevel( void ) {
433         idVec3  color;
434         float   intensity;
435
436         intensity = ( float )currentLevel / ( float )levels;
437         color = baseColor * intensity;
438         renderLight.shaderParms[ SHADERPARM_RED ]       = color[ 0 ];
439         renderLight.shaderParms[ SHADERPARM_GREEN ]     = color[ 1 ];
440         renderLight.shaderParms[ SHADERPARM_BLUE ]      = color[ 2 ];
441         renderEntity.shaderParms[ SHADERPARM_RED ]      = color[ 0 ];
442         renderEntity.shaderParms[ SHADERPARM_GREEN ]= color[ 1 ];
443         renderEntity.shaderParms[ SHADERPARM_BLUE ]     = color[ 2 ];
444         PresentLightDefChange();
445         PresentModelDefChange();
446 }
447
448 /*
449 ================
450 idLight::GetColor
451 ================
452 */
453 void idLight::GetColor( idVec3 &out ) const {
454         out[ 0 ] = renderLight.shaderParms[ SHADERPARM_RED ];
455         out[ 1 ] = renderLight.shaderParms[ SHADERPARM_GREEN ];
456         out[ 2 ] = renderLight.shaderParms[ SHADERPARM_BLUE ];
457 }
458
459 /*
460 ================
461 idLight::GetColor
462 ================
463 */
464 void idLight::GetColor( idVec4 &out ) const {
465         out[ 0 ] = renderLight.shaderParms[ SHADERPARM_RED ];
466         out[ 1 ] = renderLight.shaderParms[ SHADERPARM_GREEN ];
467         out[ 2 ] = renderLight.shaderParms[ SHADERPARM_BLUE ];
468         out[ 3 ] = renderLight.shaderParms[ SHADERPARM_ALPHA ];
469 }
470
471 /*
472 ================
473 idLight::SetColor
474 ================
475 */
476 void idLight::SetColor( float red, float green, float blue ) {
477         baseColor.Set( red, green, blue );
478         SetLightLevel();
479 }
480
481 /*
482 ================
483 idLight::SetColor
484 ================
485 */
486 void idLight::SetColor( const idVec4 &color ) {
487         baseColor = color.ToVec3();
488         renderLight.shaderParms[ SHADERPARM_ALPHA ]             = color[ 3 ];
489         renderEntity.shaderParms[ SHADERPARM_ALPHA ]    = color[ 3 ];
490         SetLightLevel();
491 }
492
493 /*
494 ================
495 idLight::SetShader
496 ================
497 */
498 void idLight::SetShader( const char *shadername ) {
499         // allow this to be NULL
500         renderLight.shader = declManager->FindMaterial( shadername, false );
501         PresentLightDefChange();
502 }
503
504 /*
505 ================
506 idLight::SetLightParm
507 ================
508 */
509 void idLight::SetLightParm( int parmnum, float value ) {
510         if ( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) ) {
511                 gameLocal.Error( "shader parm index (%d) out of range", parmnum );
512         }
513
514         renderLight.shaderParms[ parmnum ] = value;
515         PresentLightDefChange();
516 }
517
518 /*
519 ================
520 idLight::SetLightParms
521 ================
522 */
523 void idLight::SetLightParms( float parm0, float parm1, float parm2, float parm3 ) {
524         renderLight.shaderParms[ SHADERPARM_RED ]               = parm0;
525         renderLight.shaderParms[ SHADERPARM_GREEN ]             = parm1;
526         renderLight.shaderParms[ SHADERPARM_BLUE ]              = parm2;
527         renderLight.shaderParms[ SHADERPARM_ALPHA ]             = parm3;
528         renderEntity.shaderParms[ SHADERPARM_RED ]              = parm0;
529         renderEntity.shaderParms[ SHADERPARM_GREEN ]    = parm1;
530         renderEntity.shaderParms[ SHADERPARM_BLUE ]             = parm2;
531         renderEntity.shaderParms[ SHADERPARM_ALPHA ]    = parm3;
532         PresentLightDefChange();
533         PresentModelDefChange();
534 }
535
536 /*
537 ================
538 idLight::SetRadiusXYZ
539 ================
540 */
541 void idLight::SetRadiusXYZ( float x, float y, float z ) {
542         renderLight.lightRadius[0] = x;
543         renderLight.lightRadius[1] = y;
544         renderLight.lightRadius[2] = z;
545         PresentLightDefChange();
546 }
547
548 /*
549 ================
550 idLight::SetRadius
551 ================
552 */
553 void idLight::SetRadius( float radius ) {
554         renderLight.lightRadius[0] = renderLight.lightRadius[1] = renderLight.lightRadius[2] = radius;
555         PresentLightDefChange();
556 }
557
558 /*
559 ================
560 idLight::On
561 ================
562 */
563 void idLight::On( void ) {
564         currentLevel = levels;
565         // offset the start time of the shader to sync it to the game time
566         renderLight.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
567         if ( ( soundWasPlaying || refSound.waitfortrigger ) && refSound.shader ) {
568                 StartSoundShader( refSound.shader, SND_CHANNEL_ANY, 0, false, NULL );
569                 soundWasPlaying = false;
570         }
571         SetLightLevel();
572         BecomeActive( TH_UPDATEVISUALS );
573 }
574
575 /*
576 ================
577 idLight::Off
578 ================
579 */
580 void idLight::Off( void ) {
581         currentLevel = 0;
582         // kill any sound it was making
583         if ( refSound.referenceSound && refSound.referenceSound->CurrentlyPlaying() ) {
584                 StopSound( SND_CHANNEL_ANY, false );
585                 soundWasPlaying = true;
586         }
587         SetLightLevel();
588         BecomeActive( TH_UPDATEVISUALS );
589 }
590
591 /*
592 ================
593 idLight::Fade
594 ================
595 */
596 void idLight::Fade( const idVec4 &to, float fadeTime ) {
597         GetColor( fadeFrom );
598         fadeTo = to;
599         fadeStart = gameLocal.time;
600         fadeEnd = gameLocal.time + SEC2MS( fadeTime );
601         BecomeActive( TH_THINK );
602 }
603
604 /*
605 ================
606 idLight::FadeOut
607 ================
608 */
609 void idLight::FadeOut( float time ) {
610         Fade( colorBlack, time );
611 }
612
613 /*
614 ================
615 idLight::FadeIn
616 ================
617 */
618 void idLight::FadeIn( float time ) {
619         idVec3 color;
620         idVec4 color4;
621
622         currentLevel = levels;
623         spawnArgs.GetVector( "_color", "1 1 1", color );
624         color4.Set( color.x, color.y, color.z, 1.0f );
625         Fade( color4, time );
626 }
627
628 /*
629 ================
630 idLight::Killed
631 ================
632 */
633 void idLight::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
634         BecomeBroken( attacker );
635 }
636
637 /*
638 ================
639 idLight::BecomeBroken
640 ================
641 */
642 void idLight::BecomeBroken( idEntity *activator ) {
643         const char *damageDefName;
644
645         fl.takedamage = false;
646
647         if ( brokenModel.Length() ) {
648                 SetModel( brokenModel );
649
650                 if ( !spawnArgs.GetBool( "nonsolid" ) ) {
651                         GetPhysics()->SetClipModel( new idClipModel( brokenModel.c_str() ), 1.0f );
652                         GetPhysics()->SetContents( CONTENTS_SOLID );
653                 }
654         } else if ( spawnArgs.GetBool( "hideModelOnBreak" ) ) {
655                 SetModel( "" );
656                 GetPhysics()->SetContents( 0 );
657         }
658
659         if ( gameLocal.isServer ) {
660
661                 ServerSendEvent( EVENT_BECOMEBROKEN, NULL, true, -1 );
662
663                 if ( spawnArgs.GetString( "def_damage", "", &damageDefName ) ) {
664                         idVec3 origin = renderEntity.origin + renderEntity.bounds.GetCenter() * renderEntity.axis;
665                         gameLocal.RadiusDamage( origin, activator, activator, this, this, damageDefName );
666                 }
667
668         }
669
670                 ActivateTargets( activator );
671
672         // offset the start time of the shader to sync it to the game time
673         renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
674         renderLight.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
675
676         // set the state parm
677         renderEntity.shaderParms[ SHADERPARM_MODE ] = 1;
678         renderLight.shaderParms[ SHADERPARM_MODE ] = 1;
679
680         // if the light has a sound, either start the alternate (broken) sound, or stop the sound
681         const char *parm = spawnArgs.GetString( "snd_broken" );
682         if ( refSound.shader || ( parm && *parm ) ) {
683                 StopSound( SND_CHANNEL_ANY, false );
684                 const idSoundShader *alternate = refSound.shader ? refSound.shader->GetAltSound() : declManager->FindSound( parm );
685                 if ( alternate ) {
686                         // start it with no diversity, so the leadin break sound plays
687                         refSound.referenceSound->StartSound( alternate, SND_CHANNEL_ANY, 0.0, 0 );
688                 }
689         }
690
691         parm = spawnArgs.GetString( "mtr_broken" );
692         if ( parm && *parm ) {
693                 SetShader( parm );
694         }
695
696         UpdateVisuals();
697 }
698
699 /*
700 ================
701 idLight::PresentLightDefChange
702 ================
703 */
704 void idLight::PresentLightDefChange( void ) {
705         // let the renderer apply it to the world
706         if ( ( lightDefHandle != -1 ) ) {
707                 gameRenderWorld->UpdateLightDef( lightDefHandle, &renderLight );
708         } else {
709                 lightDefHandle = gameRenderWorld->AddLightDef( &renderLight );
710         }
711 }
712
713 /*
714 ================
715 idLight::PresentModelDefChange
716 ================
717 */
718 void idLight::PresentModelDefChange( void ) {
719
720         if ( !renderEntity.hModel || IsHidden() ) {
721                 return;
722         }
723
724         // add to refresh list
725         if ( modelDefHandle == -1 ) {
726                 modelDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
727         } else {
728                 gameRenderWorld->UpdateEntityDef( modelDefHandle, &renderEntity );
729         }
730 }
731
732 /*
733 ================
734 idLight::Present
735 ================
736 */
737 void idLight::Present( void ) {
738         // don't present to the renderer if the entity hasn't changed
739         if ( !( thinkFlags & TH_UPDATEVISUALS ) ) {
740                 return;
741         }
742
743         // add the model
744         idEntity::Present();
745
746         // current transformation
747         renderLight.axis        = localLightAxis * GetPhysics()->GetAxis();
748         renderLight.origin  = GetPhysics()->GetOrigin() + GetPhysics()->GetAxis() * localLightOrigin;
749
750         // reference the sound for shader synced effects
751         if ( lightParent ) {
752                 renderLight.referenceSound = lightParent->GetSoundEmitter();
753                 renderEntity.referenceSound = lightParent->GetSoundEmitter();
754         }
755         else {
756                 renderLight.referenceSound = refSound.referenceSound;
757                 renderEntity.referenceSound = refSound.referenceSound;
758         }
759
760         // update the renderLight and renderEntity to render the light and flare
761         PresentLightDefChange();
762         PresentModelDefChange();
763 }
764
765 /*
766 ================
767 idLight::Think
768 ================
769 */
770 void idLight::Think( void ) {
771         idVec4 color;
772
773         if ( thinkFlags & TH_THINK ) {
774                 if ( fadeEnd > 0 ) {
775                         if ( gameLocal.time < fadeEnd ) {
776                                 color.Lerp( fadeFrom, fadeTo, ( float )( gameLocal.time - fadeStart ) / ( float )( fadeEnd - fadeStart ) );
777                         } else {
778                                 color = fadeTo;
779                                 fadeEnd = 0;
780                                 BecomeInactive( TH_THINK );
781                         }
782                         SetColor( color );
783                 }
784         }
785
786         RunPhysics();
787         Present();
788 }
789
790 /*
791 ================
792 idLight::GetPhysicsToSoundTransform
793 ================
794 */
795 bool idLight::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
796         origin = localLightOrigin + renderLight.lightCenter;
797         axis = localLightAxis * GetPhysics()->GetAxis();
798         return true;
799 }
800
801 /*
802 ================
803 idLight::FreeLightDef
804 ================
805 */
806 void idLight::FreeLightDef( void ) {
807         if ( lightDefHandle != -1 ) {
808                 gameRenderWorld->FreeLightDef( lightDefHandle );
809                 lightDefHandle = -1;
810         }
811 }
812
813 /*
814 ================
815 idLight::SaveState
816 ================
817 */
818 void idLight::SaveState( idDict *args ) {
819         int i, c = spawnArgs.GetNumKeyVals();
820         for ( i = 0; i < c; i++ ) {
821                 const idKeyValue *pv = spawnArgs.GetKeyVal(i);
822                 if ( pv->GetKey().Find( "editor_", false ) >= 0 || pv->GetKey().Find( "parse_", false ) >= 0 ) {
823                         continue;
824                 }
825                 args->Set( pv->GetKey(), pv->GetValue() );
826         }
827 }
828
829 /*
830 ===============
831 idLight::ShowEditingDialog
832 ===============
833 */
834 void idLight::ShowEditingDialog( void ) {
835         if ( g_editEntityMode.GetInteger() == 1 ) {
836                 common->InitTool( EDITOR_LIGHT, &spawnArgs );
837         } else {
838                 common->InitTool( EDITOR_SOUND, &spawnArgs );
839         }
840 }
841
842 /*
843 ================
844 idLight::Event_SetShader
845 ================
846 */
847 void idLight::Event_SetShader( const char *shadername ) {
848         SetShader( shadername );
849 }
850
851 /*
852 ================
853 idLight::Event_GetLightParm
854 ================
855 */
856 void idLight::Event_GetLightParm( int parmnum ) {
857         if ( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) ) {
858                 gameLocal.Error( "shader parm index (%d) out of range", parmnum );
859         }
860
861         idThread::ReturnFloat( renderLight.shaderParms[ parmnum ] );
862 }
863
864 /*
865 ================
866 idLight::Event_SetLightParm
867 ================
868 */
869 void idLight::Event_SetLightParm( int parmnum, float value ) {
870         SetLightParm( parmnum, value );
871 }
872
873 /*
874 ================
875 idLight::Event_SetLightParms
876 ================
877 */
878 void idLight::Event_SetLightParms( float parm0, float parm1, float parm2, float parm3 ) {
879         SetLightParms( parm0, parm1, parm2, parm3 );
880 }
881
882 /*
883 ================
884 idLight::Event_SetRadiusXYZ
885 ================
886 */
887 void idLight::Event_SetRadiusXYZ( float x, float y, float z ) {
888         SetRadiusXYZ( x, y, z );
889 }
890
891 /*
892 ================
893 idLight::Event_SetRadius
894 ================
895 */
896 void idLight::Event_SetRadius( float radius ) {
897         SetRadius( radius );
898 }
899
900 /*
901 ================
902 idLight::Event_Hide
903 ================
904 */
905 void idLight::Event_Hide( void ) {
906         Hide();
907         PresentModelDefChange();
908         Off();
909 }
910
911 /*
912 ================
913 idLight::Event_Show
914 ================
915 */
916 void idLight::Event_Show( void ) {
917         Show();
918         PresentModelDefChange();
919         On();
920 }
921
922 /*
923 ================
924 idLight::Event_On
925 ================
926 */
927 void idLight::Event_On( void ) {
928         On();
929 }
930
931 /*
932 ================
933 idLight::Event_Off
934 ================
935 */
936 void idLight::Event_Off( void ) {
937         Off();
938 }
939
940 /*
941 ================
942 idLight::Event_ToggleOnOff
943 ================
944 */
945 void idLight::Event_ToggleOnOff( idEntity *activator ) {
946         triggercount++;
947         if ( triggercount < count ) {
948                 return;
949         }
950
951         // reset trigger count
952         triggercount = 0;
953
954         if ( breakOnTrigger ) {
955                 BecomeBroken( activator );
956                 breakOnTrigger = false;
957                 return;
958         }
959
960         if ( !currentLevel ) {
961                 On();
962         }
963         else {
964                 currentLevel--;
965                 if ( !currentLevel ) {
966                         Off();
967                 }
968                 else {
969                         SetLightLevel();
970                 }
971         }
972 }
973
974 /*
975 ================
976 idLight::Event_SetSoundHandles
977
978   set the same sound def handle on all targeted lights
979 ================
980 */
981 void idLight::Event_SetSoundHandles( void ) {
982         int i;
983         idEntity *targetEnt;
984
985         if ( !refSound.referenceSound ) {
986                 return;
987         }
988
989         for ( i = 0; i < targets.Num(); i++ ) {
990                 targetEnt = targets[ i ].GetEntity();
991                 if ( targetEnt && targetEnt->IsType( idLight::Type ) ) {
992                         idLight *light = static_cast<idLight*>(targetEnt);
993                         light->lightParent = this;
994
995                         // explicitly delete any sounds on the entity
996                         light->FreeSoundEmitter( true );
997
998                         // manually set the refSound to this light's refSound
999                         light->renderEntity.referenceSound = renderEntity.referenceSound;
1000
1001                         // update the renderEntity to the renderer
1002                         light->UpdateVisuals();
1003                 }
1004         }
1005 }
1006
1007 /*
1008 ================
1009 idLight::Event_FadeOut
1010 ================
1011 */
1012 void idLight::Event_FadeOut( float time ) {
1013         FadeOut( time );
1014 }
1015
1016 /*
1017 ================
1018 idLight::Event_FadeIn
1019 ================
1020 */
1021 void idLight::Event_FadeIn( float time ) {
1022         FadeIn( time );
1023 }
1024
1025 /*
1026 ================
1027 idLight::ClientPredictionThink
1028 ================
1029 */
1030 void idLight::ClientPredictionThink( void ) {
1031         Think();
1032 }
1033
1034 /*
1035 ================
1036 idLight::WriteToSnapshot
1037 ================
1038 */
1039 void idLight::WriteToSnapshot( idBitMsgDelta &msg ) const {
1040
1041         GetPhysics()->WriteToSnapshot( msg );
1042         WriteBindToSnapshot( msg );
1043
1044         msg.WriteByte( currentLevel );
1045         msg.WriteLong( PackColor( baseColor ) );
1046         // msg.WriteBits( lightParent.GetEntityNum(), GENTITYNUM_BITS );
1047
1048 /*      // only helps prediction
1049         msg.WriteLong( PackColor( fadeFrom ) );
1050         msg.WriteLong( PackColor( fadeTo ) );
1051         msg.WriteLong( fadeStart );
1052         msg.WriteLong( fadeEnd );
1053 */
1054
1055         // FIXME: send renderLight.shader
1056         msg.WriteFloat( renderLight.lightRadius[0], 5, 10 );
1057         msg.WriteFloat( renderLight.lightRadius[1], 5, 10 );
1058         msg.WriteFloat( renderLight.lightRadius[2], 5, 10 );
1059
1060         msg.WriteLong( PackColor( idVec4( renderLight.shaderParms[SHADERPARM_RED],
1061                                                                           renderLight.shaderParms[SHADERPARM_GREEN],
1062                                                                           renderLight.shaderParms[SHADERPARM_BLUE],
1063                                                                           renderLight.shaderParms[SHADERPARM_ALPHA] ) ) );
1064
1065         msg.WriteFloat( renderLight.shaderParms[SHADERPARM_TIMESCALE], 5, 10 );
1066         msg.WriteLong( renderLight.shaderParms[SHADERPARM_TIMEOFFSET] );
1067         //msg.WriteByte( renderLight.shaderParms[SHADERPARM_DIVERSITY] );
1068         msg.WriteShort( renderLight.shaderParms[SHADERPARM_MODE] );
1069
1070         WriteColorToSnapshot( msg );
1071 }
1072
1073 /*
1074 ================
1075 idLight::ReadFromSnapshot
1076 ================
1077 */
1078 void idLight::ReadFromSnapshot( const idBitMsgDelta &msg ) {
1079         idVec4  shaderColor;
1080         int             oldCurrentLevel = currentLevel;
1081         idVec3  oldBaseColor = baseColor;
1082
1083         GetPhysics()->ReadFromSnapshot( msg );
1084         ReadBindFromSnapshot( msg );
1085
1086         currentLevel = msg.ReadByte();
1087         if ( currentLevel != oldCurrentLevel ) {
1088                 // need to call On/Off for flickering lights to start/stop the sound
1089                 // while doing it this way rather than through events, the flickering is out of sync between clients
1090                 // but at least there is no question about saving the event and having them happening globally in the world
1091                 if ( currentLevel ) {
1092                         On();
1093                 } else {
1094                         Off();
1095                 }
1096         }
1097         UnpackColor( msg.ReadLong(), baseColor );
1098         // lightParentEntityNum = msg.ReadBits( GENTITYNUM_BITS );      
1099
1100 /*      // only helps prediction
1101         UnpackColor( msg.ReadLong(), fadeFrom );
1102         UnpackColor( msg.ReadLong(), fadeTo );
1103         fadeStart = msg.ReadLong();
1104         fadeEnd = msg.ReadLong();
1105 */
1106
1107         // FIXME: read renderLight.shader
1108         renderLight.lightRadius[0] = msg.ReadFloat( 5, 10 );
1109         renderLight.lightRadius[1] = msg.ReadFloat( 5, 10 );
1110         renderLight.lightRadius[2] = msg.ReadFloat( 5, 10 );
1111
1112         UnpackColor( msg.ReadLong(), shaderColor );
1113         renderLight.shaderParms[SHADERPARM_RED] = shaderColor[0];
1114         renderLight.shaderParms[SHADERPARM_GREEN] = shaderColor[1];
1115         renderLight.shaderParms[SHADERPARM_BLUE] = shaderColor[2];
1116         renderLight.shaderParms[SHADERPARM_ALPHA] = shaderColor[3];
1117
1118         renderLight.shaderParms[SHADERPARM_TIMESCALE] = msg.ReadFloat( 5, 10 );
1119         renderLight.shaderParms[SHADERPARM_TIMEOFFSET] = msg.ReadLong();
1120         //renderLight.shaderParms[SHADERPARM_DIVERSITY] = msg.ReadFloat();
1121         renderLight.shaderParms[SHADERPARM_MODE] = msg.ReadShort();
1122
1123         ReadColorFromSnapshot( msg );
1124
1125         if ( msg.HasChanged() ) {
1126                 if ( ( currentLevel != oldCurrentLevel ) || ( baseColor != oldBaseColor ) ) {
1127                         SetLightLevel();
1128                 } else {
1129                         PresentLightDefChange();
1130                         PresentModelDefChange();
1131                 }
1132         }
1133 }
1134
1135 /*
1136 ================
1137 idLight::ClientReceiveEvent
1138 ================
1139 */
1140 bool idLight::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
1141
1142         switch( event ) {
1143                 case EVENT_BECOMEBROKEN: {
1144                         BecomeBroken( NULL );
1145                         return true;
1146                 }
1147                 default: {
1148                         return idEntity::ClientReceiveEvent( event, time, msg );
1149                 }
1150         }
1151         return false;
1152 }