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 "sys/platform.h"
30 #include "idlib/geometry/JointTransform.h"
31 #include "framework/DeclSkin.h"
32 #include "framework/DemoFile.h"
33 #include "framework/EventLoop.h"
34 #include "framework/Session.h"
35 #include "renderer/GuiModel.h"
36 #include "renderer/ModelManager.h"
37 #include "renderer/RenderWorld_local.h"
38 #include "ui/UserInterface.h"
39 #include "sound/sound.h"
40 
41 #include "renderer/tr_local.h"
42 
43 //#define WRITE_GUIS
44 
45 typedef struct {
46 	int		version;
47 	int		sizeofRenderEntity;
48 	int		sizeofRenderLight;
49 	char	mapname[256];
50 } demoHeader_t;
51 
52 
53 /*
54 ==============
55 StartWritingDemo
56 ==============
57 */
StartWritingDemo(idDemoFile * demo)58 void		idRenderWorldLocal::StartWritingDemo( idDemoFile *demo ) {
59 	int		i;
60 
61 	// FIXME: we should track the idDemoFile locally, instead of snooping into session for it
62 
63 	WriteLoadMap();
64 
65 	// write the door portal state
66 	for ( i = 0 ; i < numInterAreaPortals ; i++ ) {
67 		if ( doublePortals[i].blockingBits ) {
68 			SetPortalState( i+1, doublePortals[i].blockingBits );
69 		}
70 	}
71 
72 	// clear the archive counter on all defs
73 	for ( i = 0 ; i < lightDefs.Num() ; i++ ) {
74 		if ( lightDefs[i] ) {
75 			lightDefs[i]->archived = false;
76 		}
77 	}
78 	for ( i = 0 ; i < entityDefs.Num() ; i++ ) {
79 		if ( entityDefs[i] ) {
80 			entityDefs[i]->archived = false;
81 		}
82 	}
83 }
84 
StopWritingDemo()85 void idRenderWorldLocal::StopWritingDemo() {
86 //	writeDemo = NULL;
87 }
88 
89 /*
90 ==============
91 ProcessDemoCommand
92 ==============
93 */
ProcessDemoCommand(idDemoFile * readDemo,renderView_t * renderView,int * demoTimeOffset)94 bool		idRenderWorldLocal::ProcessDemoCommand( idDemoFile *readDemo, renderView_t *renderView, int *demoTimeOffset ) {
95 	bool	newMap = false;
96 
97 	if ( !readDemo ) {
98 		return false;
99 	}
100 
101 	int				dc;
102 	qhandle_t		h;
103 
104 	if ( !readDemo->ReadInt( dc ) ) {
105 		// a demoShot may not have an endFrame, but it is still valid
106 		return false;
107 	}
108 
109 	switch( (demoCommand_t)dc ) {
110 	case DC_LOADMAP:
111 		// read the initial data
112 		demoHeader_t	header;
113 
114 		readDemo->ReadInt( header.version );
115 		readDemo->ReadInt( header.sizeofRenderEntity );
116 		readDemo->ReadInt( header.sizeofRenderLight );
117 		for ( int i = 0; i < 256; i++ )
118 			readDemo->ReadChar( header.mapname[i] );
119 		// the internal version value got replaced by DS_VERSION at toplevel
120 		if ( header.version != 4 ) {
121 				common->Error( "Demo version mismatch.\n" );
122 		}
123 
124 		if ( r_showDemo.GetBool() ) {
125 			common->Printf( "DC_LOADMAP: %s\n", header.mapname );
126 		}
127 		InitFromMap( header.mapname );
128 
129 		newMap = true;		// we will need to set demoTimeOffset
130 
131 		break;
132 
133 	case DC_RENDERVIEW:
134 		readDemo->ReadInt( renderView->viewID );
135 		readDemo->ReadInt( renderView->x );
136 		readDemo->ReadInt( renderView->y );
137 		readDemo->ReadInt( renderView->width );
138 		readDemo->ReadInt( renderView->height );
139 		readDemo->ReadFloat( renderView->fov_x );
140 		readDemo->ReadFloat( renderView->fov_y );
141 		readDemo->ReadVec3( renderView->vieworg );
142 		readDemo->ReadMat3( renderView->viewaxis );
143 		readDemo->ReadBool( renderView->cramZNear );
144 		readDemo->ReadBool( renderView->forceUpdate );
145 		// binary compatibility with win32 padded structures
146 		char tmp;
147 		int i;
148 		readDemo->ReadChar( tmp );
149 		readDemo->ReadChar( tmp );
150 		readDemo->ReadInt( renderView->time );
151 		for ( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ )
152 			readDemo->ReadFloat( renderView->shaderParms[i] );
153 
154 		renderView->globalMaterial = NULL;
155 		if ( !readDemo->ReadInt( i ) ) {
156 			 return false;
157 		 }
158 
159 		if ( r_showDemo.GetBool() ) {
160 			common->Printf( "DC_RENDERVIEW: %i\n", renderView->time );
161 		}
162 
163 		// possibly change the time offset if this is from a new map
164 		if ( newMap && demoTimeOffset ) {
165 			*demoTimeOffset = renderView->time - eventLoop->Milliseconds();
166 		}
167 		return false;
168 
169 	case DC_UPDATE_ENTITYDEF:
170 		ReadRenderEntity();
171 		break;
172 	case DC_DELETE_ENTITYDEF:
173 		if ( !readDemo->ReadInt( h ) ) {
174 			return false;
175 		}
176 		if ( r_showDemo.GetBool() ) {
177 			common->Printf( "DC_DELETE_ENTITYDEF: %i\n", h );
178 		}
179 		FreeEntityDef( h );
180 		break;
181 	case DC_UPDATE_LIGHTDEF:
182 		ReadRenderLight();
183 		break;
184 	case DC_DELETE_LIGHTDEF:
185 		if ( !readDemo->ReadInt( h ) ) {
186 			return false;
187 		}
188 		if ( r_showDemo.GetBool() ) {
189 			common->Printf( "DC_DELETE_LIGHTDEF: %i\n", h );
190 		}
191 		FreeLightDef( h );
192 		break;
193 
194 	case DC_CAPTURE_RENDER:
195 		if ( r_showDemo.GetBool() ) {
196 			common->Printf( "DC_CAPTURE_RENDER\n" );
197 		}
198 		renderSystem->CaptureRenderToImage( readDemo->ReadHashString() );
199 		break;
200 
201 	case DC_CROP_RENDER:
202 		if ( r_showDemo.GetBool() ) {
203 			common->Printf( "DC_CROP_RENDER\n" );
204 		}
205 		int	size[3];
206 		readDemo->ReadInt( size[0] );
207 		readDemo->ReadInt( size[1] );
208 		readDemo->ReadInt( size[2] );
209 		renderSystem->CropRenderSize( size[0], size[1], size[2] != 0 );
210 		break;
211 
212 	case DC_UNCROP_RENDER:
213 		if ( r_showDemo.GetBool() ) {
214 			common->Printf( "DC_UNCROP\n" );
215 		}
216 		renderSystem->UnCrop();
217 		break;
218 
219 	case DC_GUI_MODEL:
220 		if ( r_showDemo.GetBool() ) {
221 			common->Printf( "DC_GUI_MODEL\n" );
222 		}
223 		tr.demoGuiModel->ReadFromDemo( readDemo );
224 		break;
225 
226 	case DC_DEFINE_MODEL:
227 		{
228 		idRenderModel	*model = renderModelManager->AllocModel();
229 		model->ReadFromDemoFile( session->readDemo );
230 		// add to model manager, so we can find it
231 		renderModelManager->AddModel( model );
232 
233 		// save it in the list to free when clearing this map
234 		localModels.Append( model );
235 
236 		if ( r_showDemo.GetBool() ) {
237 			common->Printf( "DC_DEFINE_MODEL\n" );
238 		}
239 		break;
240 		}
241 	case DC_SET_PORTAL_STATE:
242 		{
243 			int		data[2];
244 			readDemo->ReadInt( data[0] );
245 			readDemo->ReadInt( data[1] );
246 			SetPortalState( data[0], data[1] );
247 			if ( r_showDemo.GetBool() ) {
248 				common->Printf( "DC_SET_PORTAL_STATE: %i %i\n", data[0], data[1] );
249 			}
250 		}
251 
252 		break;
253 	case DC_END_FRAME:
254 		return true;
255 
256 	default:
257 		common->Error( "Bad token in demo stream" );
258 	}
259 
260 	return false;
261 }
262 
263 /*
264 ================
265 WriteLoadMap
266 ================
267 */
WriteLoadMap()268 void	idRenderWorldLocal::WriteLoadMap() {
269 
270 	// only the main renderWorld writes stuff to demos, not the wipes or
271 	// menu renders
272 	if ( this != session->rw ) {
273 		return;
274 	}
275 
276 	session->writeDemo->WriteInt( DS_RENDER );
277 	session->writeDemo->WriteInt( DC_LOADMAP );
278 
279 	demoHeader_t	header;
280 	strncpy( header.mapname, mapName.c_str(), sizeof( header.mapname ) - 1 );
281 	header.version = 4;
282 	header.sizeofRenderEntity = sizeof( renderEntity_t );
283 	header.sizeofRenderLight = sizeof( renderLight_t );
284 	session->writeDemo->WriteInt( header.version );
285 	session->writeDemo->WriteInt( header.sizeofRenderEntity );
286 	session->writeDemo->WriteInt( header.sizeofRenderLight );
287 	for ( int i = 0; i < 256; i++ )
288 		session->writeDemo->WriteChar( header.mapname[i] );
289 
290 	if ( r_showDemo.GetBool() ) {
291 		common->Printf( "write DC_DELETE_LIGHTDEF: %s\n", mapName.c_str() );
292 	}
293 }
294 
295 /*
296 ================
297 WriteVisibleDefs
298 
299 ================
300 */
WriteVisibleDefs(const viewDef_t * viewDef)301 void	idRenderWorldLocal::WriteVisibleDefs( const viewDef_t *viewDef ) {
302 	// only the main renderWorld writes stuff to demos, not the wipes or
303 	// menu renders
304 	if ( this != session->rw ) {
305 		return;
306 	}
307 
308 	// make sure all necessary entities and lights are updated
309 	for ( viewEntity_t *viewEnt = viewDef->viewEntitys ; viewEnt ; viewEnt = viewEnt->next ) {
310 		idRenderEntityLocal *ent = viewEnt->entityDef;
311 
312 		if ( ent->archived ) {
313 			// still up to date
314 			continue;
315 		}
316 
317 		// write it out
318 		WriteRenderEntity( ent->index, &ent->parms );
319 		ent->archived = true;
320 	}
321 
322 	for ( viewLight_t *viewLight = viewDef->viewLights ; viewLight ; viewLight = viewLight->next ) {
323 		idRenderLightLocal *light = viewLight->lightDef;
324 
325 		if ( light->archived ) {
326 			// still up to date
327 			continue;
328 		}
329 		// write it out
330 		WriteRenderLight( light->index, &light->parms );
331 		light->archived = true;
332 	}
333 }
334 
335 
336 /*
337 ================
338 WriteRenderView
339 ================
340 */
WriteRenderView(const renderView_t * renderView)341 void	idRenderWorldLocal::WriteRenderView( const renderView_t *renderView ) {
342 	int i;
343 
344 	// only the main renderWorld writes stuff to demos, not the wipes or
345 	// menu renders
346 	if ( this != session->rw ) {
347 		return;
348 	}
349 
350 	// write the actual view command
351 	session->writeDemo->WriteInt( DS_RENDER );
352 	session->writeDemo->WriteInt( DC_RENDERVIEW );
353 	session->writeDemo->WriteInt( renderView->viewID );
354 	session->writeDemo->WriteInt( renderView->x );
355 	session->writeDemo->WriteInt( renderView->y );
356 	session->writeDemo->WriteInt( renderView->width );
357 	session->writeDemo->WriteInt( renderView->height );
358 	session->writeDemo->WriteFloat( renderView->fov_x );
359 	session->writeDemo->WriteFloat( renderView->fov_y );
360 	session->writeDemo->WriteVec3( renderView->vieworg );
361 	session->writeDemo->WriteMat3( renderView->viewaxis );
362 	session->writeDemo->WriteBool( renderView->cramZNear );
363 	session->writeDemo->WriteBool( renderView->forceUpdate );
364 	// binary compatibility with old win32 version writing padded structures directly to disk
365 	session->writeDemo->WriteUnsignedChar( 0 );
366 	session->writeDemo->WriteUnsignedChar( 0 );
367 	session->writeDemo->WriteInt( renderView->time );
368 	for ( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ )
369 		session->writeDemo->WriteFloat( renderView->shaderParms[i] );
370 	session->writeDemo->WriteInt( 0 ); //renderView->globalMaterial
371 
372 	if ( r_showDemo.GetBool() ) {
373 		common->Printf( "write DC_RENDERVIEW: %i\n", renderView->time );
374 	}
375 }
376 
377 /*
378 ================
379 WriteFreeEntity
380 ================
381 */
WriteFreeEntity(qhandle_t handle)382 void	idRenderWorldLocal::WriteFreeEntity( qhandle_t handle ) {
383 
384 	// only the main renderWorld writes stuff to demos, not the wipes or
385 	// menu renders
386 	if ( this != session->rw ) {
387 		return;
388 	}
389 
390 	session->writeDemo->WriteInt( DS_RENDER );
391 	session->writeDemo->WriteInt( DC_DELETE_ENTITYDEF );
392 	session->writeDemo->WriteInt( handle );
393 
394 	if ( r_showDemo.GetBool() ) {
395 		common->Printf( "write DC_DELETE_ENTITYDEF: %i\n", handle );
396 	}
397 }
398 
399 /*
400 ================
401 WriteFreeLightEntity
402 ================
403 */
WriteFreeLight(qhandle_t handle)404 void	idRenderWorldLocal::WriteFreeLight( qhandle_t handle ) {
405 
406 	// only the main renderWorld writes stuff to demos, not the wipes or
407 	// menu renders
408 	if ( this != session->rw ) {
409 		return;
410 	}
411 
412 	session->writeDemo->WriteInt( DS_RENDER );
413 	session->writeDemo->WriteInt( DC_DELETE_LIGHTDEF );
414 	session->writeDemo->WriteInt( handle );
415 
416 	if ( r_showDemo.GetBool() ) {
417 		common->Printf( "write DC_DELETE_LIGHTDEF: %i\n", handle );
418 	}
419 }
420 
421 /*
422 ================
423 WriteRenderLight
424 ================
425 */
WriteRenderLight(qhandle_t handle,const renderLight_t * light)426 void	idRenderWorldLocal::WriteRenderLight( qhandle_t handle, const renderLight_t *light ) {
427 
428 	// only the main renderWorld writes stuff to demos, not the wipes or
429 	// menu renders
430 	if ( this != session->rw ) {
431 		return;
432 	}
433 
434 	session->writeDemo->WriteInt( DS_RENDER );
435 	session->writeDemo->WriteInt( DC_UPDATE_LIGHTDEF );
436 	session->writeDemo->WriteInt( handle );
437 
438 	session->writeDemo->WriteMat3( light->axis );
439 	session->writeDemo->WriteVec3( light->origin );
440 	session->writeDemo->WriteInt( light->suppressLightInViewID );
441 	session->writeDemo->WriteInt( light->allowLightInViewID );
442 	session->writeDemo->WriteBool( light->noShadows );
443 	session->writeDemo->WriteBool( light->noSpecular );
444 	session->writeDemo->WriteBool( light->pointLight );
445 	session->writeDemo->WriteBool( light->parallel );
446 	session->writeDemo->WriteVec3( light->lightRadius );
447 	session->writeDemo->WriteVec3( light->lightCenter );
448 	session->writeDemo->WriteVec3( light->target );
449 	session->writeDemo->WriteVec3( light->right );
450 	session->writeDemo->WriteVec3( light->up );
451 	session->writeDemo->WriteVec3( light->start );
452 	session->writeDemo->WriteVec3( light->end );
453 	session->writeDemo->WriteInt( light->prelightModel ? 1 : 0 );
454 	session->writeDemo->WriteInt( light->lightId );
455 	session->writeDemo->WriteInt( light->shader ? 1 : 0);
456 	for ( int i = 0; i < MAX_ENTITY_SHADER_PARMS; i++)
457 		session->writeDemo->WriteFloat( light->shaderParms[i] );
458 	session->writeDemo->WriteInt( light->referenceSound ? 1 : 0 );
459 
460 	if ( light->prelightModel ) {
461 		session->writeDemo->WriteHashString( light->prelightModel->Name() );
462 	}
463 	if ( light->shader ) {
464 		session->writeDemo->WriteHashString( light->shader->GetName() );
465 	}
466 	if ( light->referenceSound ) {
467 		int	index = light->referenceSound->Index();
468 		session->writeDemo->WriteInt( index );
469 	}
470 
471 	if ( r_showDemo.GetBool() ) {
472 		common->Printf( "write DC_UPDATE_LIGHTDEF: %i\n", handle );
473 	}
474 }
475 
476 /*
477 ================
478 ReadRenderLight
479 ================
480 */
ReadRenderLight()481 void	idRenderWorldLocal::ReadRenderLight( ) {
482 	renderLight_t	light;
483 	int				index, i;
484 	int				prelightModel, shader, referenceSound;
485 
486 	session->readDemo->ReadInt( index );
487 	if ( index < 0 ) {
488 		common->Error( "ReadRenderLight: index < 0 " );
489 	}
490 
491 	session->readDemo->ReadMat3( light.axis );
492 	session->readDemo->ReadVec3( light.origin );
493 	session->readDemo->ReadInt( light.suppressLightInViewID );
494 	session->readDemo->ReadInt( light.allowLightInViewID );
495 	session->readDemo->ReadBool( light.noShadows );
496 	session->readDemo->ReadBool( light.noSpecular );
497 	session->readDemo->ReadBool( light.pointLight );
498 	session->readDemo->ReadBool( light.parallel );
499 	session->readDemo->ReadVec3( light.lightRadius );
500 	session->readDemo->ReadVec3( light.lightCenter );
501 	session->readDemo->ReadVec3( light.target );
502 	session->readDemo->ReadVec3( light.right );
503 	session->readDemo->ReadVec3( light.up );
504 	session->readDemo->ReadVec3( light.start );
505 	session->readDemo->ReadVec3( light.end );
506 	session->readDemo->ReadInt( prelightModel );
507 	session->readDemo->ReadInt( light.lightId );
508 	session->readDemo->ReadInt( shader );
509 	for ( i = 0; i < MAX_ENTITY_SHADER_PARMS; i++)
510 		session->readDemo->ReadFloat( light.shaderParms[i] );
511 	session->readDemo->ReadInt( referenceSound );
512 	if ( prelightModel )
513 		light.prelightModel = renderModelManager->FindModel( session->readDemo->ReadHashString() );
514 	else
515 		light.prelightModel = NULL;
516 	if ( shader )
517 		light.shader = declManager->FindMaterial( session->readDemo->ReadHashString() );
518 	else
519 		light.shader = NULL;
520 	if ( referenceSound ) {
521 		session->readDemo->ReadInt( i );
522 		light.referenceSound = session->sw->EmitterForIndex( i );
523 	} else {
524 		light.referenceSound = NULL;
525 	}
526 
527 	UpdateLightDef( index, &light );
528 
529 	if ( r_showDemo.GetBool() ) {
530 		common->Printf( "DC_UPDATE_LIGHTDEF: %i\n", index );
531 	}
532 }
533 
534 /*
535 ================
536 WriteRenderEntity
537 ================
538 */
WriteRenderEntity(qhandle_t handle,const renderEntity_t * ent)539 void	idRenderWorldLocal::WriteRenderEntity( qhandle_t handle, const renderEntity_t *ent ) {
540 
541 	// only the main renderWorld writes stuff to demos, not the wipes or
542 	// menu renders
543 	if ( this != session->rw ) {
544 		return;
545 	}
546 
547 	session->writeDemo->WriteInt( DS_RENDER );
548 	session->writeDemo->WriteInt( DC_UPDATE_ENTITYDEF );
549 	session->writeDemo->WriteInt( handle );
550 
551 	session->writeDemo->WriteInt( ent->hModel ? 1 : 0);
552 	session->writeDemo->WriteInt( ent->entityNum );
553 	session->writeDemo->WriteInt( ent->bodyId );
554 	session->writeDemo->WriteVec3( ent->bounds[0] );
555 	session->writeDemo->WriteVec3( ent->bounds[1] );
556 	session->writeDemo->WriteInt( 0 ); //ent->callback
557 	session->writeDemo->WriteInt( 0 ); //ent->callbackData
558 	session->writeDemo->WriteInt( ent->suppressSurfaceInViewID );
559 	session->writeDemo->WriteInt( ent->suppressShadowInViewID );
560 	session->writeDemo->WriteInt( ent->suppressShadowInLightID );
561 	session->writeDemo->WriteInt( ent->allowSurfaceInViewID );
562 	session->writeDemo->WriteVec3( ent->origin );
563 	session->writeDemo->WriteMat3( ent->axis );
564 	session->writeDemo->WriteInt( ent->customShader ? 1 : 0 );
565 	session->writeDemo->WriteInt( ent->referenceShader ? 1 : 0 );
566 	session->writeDemo->WriteInt( ent->customSkin ? 1 : 0 );
567 	session->writeDemo->WriteInt( ent->referenceSound ? 1 : 0 );
568 	for ( int i = 0; i < MAX_ENTITY_SHADER_PARMS; i++ )
569 		session->writeDemo->WriteFloat( ent->shaderParms[i] );
570 	for ( int i = 0; i < MAX_RENDERENTITY_GUI; i++ )
571 		session->writeDemo->WriteInt( ent->gui[i] ? 1 : 0 );
572 	session->writeDemo->WriteInt( 0 ); //ent->remoteRenderView
573 	session->writeDemo->WriteInt( ent->numJoints );
574 	session->writeDemo->WriteInt( 0 ); //ent->joints
575 	session->writeDemo->WriteFloat( ent->modelDepthHack );
576 	session->writeDemo->WriteBool( ent->noSelfShadow );
577 	session->writeDemo->WriteBool( ent->noShadow );
578 	session->writeDemo->WriteBool( ent->noDynamicInteractions );
579 	session->writeDemo->WriteBool( ent->weaponDepthHack );
580 	session->writeDemo->WriteInt( ent->forceUpdate );
581 
582 	if ( ent->customShader ) {
583 		session->writeDemo->WriteHashString( ent->customShader->GetName() );
584 	}
585 	if ( ent->customSkin ) {
586 		session->writeDemo->WriteHashString( ent->customSkin->GetName() );
587 	}
588 	if ( ent->hModel ) {
589 		session->writeDemo->WriteHashString( ent->hModel->Name() );
590 	}
591 	if ( ent->referenceShader ) {
592 		session->writeDemo->WriteHashString( ent->referenceShader->GetName() );
593 	}
594 	if ( ent->referenceSound ) {
595 		int	index = ent->referenceSound->Index();
596 		session->writeDemo->WriteInt( index );
597 	}
598 	if ( ent->numJoints ) {
599 		for ( int i = 0; i < ent->numJoints; i++) {
600 			float *data = ent->joints[i].ToFloatPtr();
601 			for ( int j = 0; j < 12; ++j)
602 				session->writeDemo->WriteFloat( data[j] );
603 		}
604 	}
605 
606 	/*
607 	if ( ent->decals ) {
608 		ent->decals->WriteToDemoFile( session->readDemo );
609 	}
610 	if ( ent->overlay ) {
611 		ent->overlay->WriteToDemoFile( session->writeDemo );
612 	}
613 	*/
614 
615 #ifdef WRITE_GUIS
616 	if ( ent->gui ) {
617 		ent->gui->WriteToDemoFile( session->writeDemo );
618 	}
619 	if ( ent->gui2 ) {
620 		ent->gui2->WriteToDemoFile( session->writeDemo );
621 	}
622 	if ( ent->gui3 ) {
623 		ent->gui3->WriteToDemoFile( session->writeDemo );
624 	}
625 #endif
626 
627 	// RENDERDEMO_VERSION >= 2 ( Doom3 1.2 )
628 	session->writeDemo->WriteInt( ent->timeGroup );
629 	session->writeDemo->WriteInt( ent->xrayIndex );
630 
631 	if ( r_showDemo.GetBool() ) {
632 		common->Printf( "write DC_UPDATE_ENTITYDEF: %i = %s\n", handle, ent->hModel ? ent->hModel->Name() : "NULL" );
633 	}
634 }
635 
636 /*
637 ================
638 ReadRenderEntity
639 ================
640 */
ReadRenderEntity()641 void	idRenderWorldLocal::ReadRenderEntity() {
642 	renderEntity_t		ent;
643 	int					index, i, tmp, hModel, customShader, referenceShader;
644 	int					customSkin, referenceSound, gui[MAX_RENDERENTITY_GUI];
645 
646 	session->readDemo->ReadInt( index );
647 	if ( index < 0 ) {
648 		common->Error( "ReadRenderEntity: index < 0" );
649 	}
650 
651 	session->readDemo->ReadInt( hModel );
652 	session->readDemo->ReadInt( ent.entityNum );
653 	session->readDemo->ReadInt( ent.bodyId );
654 	session->readDemo->ReadVec3( ent.bounds[0] );
655 	session->readDemo->ReadVec3( ent.bounds[1] );
656 	session->readDemo->ReadInt( tmp ); //ent.callback
657 	session->readDemo->ReadInt( tmp ); //ent.callbackData
658 	session->readDemo->ReadInt( ent.suppressSurfaceInViewID );
659 	session->readDemo->ReadInt( ent.suppressShadowInViewID );
660 	session->readDemo->ReadInt( ent.suppressShadowInLightID );
661 	session->readDemo->ReadInt( ent.allowSurfaceInViewID );
662 	session->readDemo->ReadVec3( ent.origin );
663 	session->readDemo->ReadMat3( ent.axis );
664 	session->readDemo->ReadInt( customShader );
665 	session->readDemo->ReadInt( referenceShader );
666 	session->readDemo->ReadInt( customSkin );
667 	session->readDemo->ReadInt( referenceSound );
668 	for ( i = 0; i < MAX_ENTITY_SHADER_PARMS; i++ ) {
669 		session->readDemo->ReadFloat( ent.shaderParms[i] );
670 	}
671 	for ( i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
672 		session->readDemo->ReadInt( gui[i] );
673 	}
674 	session->readDemo->ReadInt( tmp ); //ent.remoteRenderView
675 	session->readDemo->ReadInt( ent.numJoints );
676 	session->readDemo->ReadInt( tmp ); //ent.joints
677 	session->readDemo->ReadFloat( ent.modelDepthHack );
678 	session->readDemo->ReadBool( ent.noSelfShadow );
679 	session->readDemo->ReadBool( ent.noShadow );
680 	session->readDemo->ReadBool( ent.noDynamicInteractions );
681 	session->readDemo->ReadBool( ent.weaponDepthHack );
682 	session->readDemo->ReadInt( ent.forceUpdate );
683 	ent.callback = NULL;
684 	if ( customShader )
685 		ent.customShader = declManager->FindMaterial( session->readDemo->ReadHashString() );
686 	else
687 		ent.customShader = NULL;
688 	if ( customSkin )
689 		ent.customSkin = declManager->FindSkin( session->readDemo->ReadHashString() );
690 	else
691 		ent.customSkin = NULL;
692 	if ( hModel )
693 		ent.hModel = renderModelManager->FindModel( session->readDemo->ReadHashString() );
694 	else
695 		ent.hModel = NULL;
696 	if ( referenceShader )
697 		ent.referenceShader = declManager->FindMaterial( session->readDemo->ReadHashString() );
698 	else
699 		ent.referenceShader = NULL;
700 	if ( referenceSound ) {
701 		session->readDemo->ReadInt( tmp );
702 		ent.referenceSound = session->sw->EmitterForIndex( tmp );
703 	} else {
704 		ent.referenceSound = NULL;
705 	}
706 	ent.remoteRenderView = NULL;
707 	if ( ent.numJoints ) {
708 		ent.joints = (idJointMat *)Mem_Alloc16( ent.numJoints * sizeof( ent.joints[0] ) );
709 		for ( int i = 0; i < ent.numJoints; i++) {
710 			float *data = ent.joints[i].ToFloatPtr();
711 			for ( int j = 0; j < 12; ++j)
712 				session->readDemo->ReadFloat( data[j] );
713 		}
714 	} else {
715 		ent.joints = NULL;
716 	}
717 	ent.callbackData = NULL;
718 
719 	/*
720 	if ( ent.decals ) {
721 		ent.decals = idRenderModelDecal::Alloc();
722 		ent.decals->ReadFromDemoFile( session->readDemo );
723 	}
724 	if ( ent.overlay ) {
725 		ent.overlay = idRenderModelOverlay::Alloc();
726 		ent.overlay->ReadFromDemoFile( session->readDemo );
727 	}
728 	*/
729 
730 	for ( i = 0; i < MAX_RENDERENTITY_GUI; i++ ) {
731 		if ( gui[ i ] ) {
732 			ent.gui[ i ] = uiManager->Alloc();
733 #ifdef WRITE_GUIS
734 			ent.gui[ i ]->ReadFromDemoFile( session->readDemo );
735 #endif
736 		} else {
737 			ent.gui[ i ] = NULL;
738 		}
739 	}
740 
741 	// >= Doom3 v1.2 only
742 	if ( session->renderdemoVersion >= 2 ) {
743 		session->readDemo->ReadInt( ent.timeGroup );
744 		session->readDemo->ReadInt( ent.xrayIndex );
745 	} else {
746 		ent.timeGroup = 0;
747 		ent.xrayIndex = 0;
748 	}
749 
750 	UpdateEntityDef( index, &ent );
751 
752 	if ( r_showDemo.GetBool() ) {
753 		common->Printf( "DC_UPDATE_ENTITYDEF: %i = %s\n", index, ent.hModel ? ent.hModel->Name() : "NULL" );
754 	}
755 }
756