//===========================================================================//
// File:	l4video.cpp																			  //
// Project: MUNGA           Brick: Video Renderer Manager	                 //                                        
// Contents: Interface specification Video Renderer Manager       			  //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 01/11/95  GAC  Initial coding.                                            //
// 07/11/95  KEO  Modify video resource format.                              //
// 02/08/96  KEO  Add rubble entry to video resource.                        //
//---------------------------------------------------------------------------//
// Copyright (C) 1995-1996, Virtual World Entertainment, Inc.                //
//   All Rights reserved worldwide.                                          //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL.              //
//===========================================================================//

#include <mungal4.hpp>
#pragma hdrstop

#if !defined(DOOR_HPP)
#	include <door.hpp>
#endif

#if !defined(DOORFRAM_HPP)
#	include <doorfram.hpp>
#endif

#if !defined(EYECANDY_HPP)
#	include <eyecandy.hpp>
#endif

#if !defined(L4VIDRND_HPP)
#	include <l4vidrnd.hpp>
#endif

#if !defined(L4VIDEO_HPP)
#	include <l4video.hpp>
#endif

#if !defined(MATRIX_HPP)
#	include <matrix.hpp>
#endif

#if !defined(MOVER_HPP)
#	include <mover.hpp>
#endif

#if !defined(JMOVER_HPP)
#	include <jmover.hpp>
#endif

#if !defined(PLAYER_HPP)
#	include	<player.hpp>
#endif

#if !defined(CAMSHIP_HPP)
#	include	<camship.hpp>
#endif

#if !defined(DIRECTOR_HPP)
#	include	<director.hpp>
#endif

#if !defined(MISSION_HPP)
#	include	<mission.hpp>
#endif

#if !defined(CULTURAL_HPP)
#	include	<cultural.hpp>
#endif

#if !defined(NTTMGR_HPP)
#	include <nttmgr.hpp>
#endif

#if !defined(APP_HPP)
#	include <app.hpp>
#endif

#include <conio.h>

//
// Includes for the DPL libraries
//
#include	<dpl.h>
#include <dpl_2d.h>
#include <dpl_vpx.h>
#include <dplutils.h>
#include <matrix.h>
//
// You can enable or disable these traces here for quicker rebuilding during
// profile testing, but for production they should be all turned off!
//
//#define	TRACE_VIDEO_CULL_SETUP
//#define	TRACE_VIDEO_VEHICLE_RENDERABLES
// #define	TRACE_VIDEO_ALL_RENDERABLES
//
// These are other traces you can use to extract data
//
#if defined(TRACE_VIDEO_LOAD_OBJECT)
	static BitTrace Video_Load_Object("Video Load Object");
	#define SET_VIDEO_LOAD_OBJECT() Video_Load_Object.Set()
	#define CLEAR_VIDEO_LOAD_OBJECT() Video_Load_Object.Clear()
#else
	#define SET_VIDEO_LOAD_OBJECT()
	#define CLEAR_VIDEO_LOAD_OBJECT()
#endif

#if defined(TRACE_VIDEO_CONSTRUCT_ROOT)
	static BitTrace Video_Construct_Root("Video Construct Root");
	#define SET_VIDEO_CONSTRUCT_ROOT() Video_Construct_Root.Set()
	#define CLEAR_VIDEO_CONSTRUCT_ROOT() Video_Construct_Root.Clear()
#else
	#define SET_VIDEO_CONSTRUCT_ROOT()
	#define CLEAR_VIDEO_CONSTRUCT_ROOT()
#endif
//
// This is the time spent fetching and processing the pickpoint information
// from the DPL renderer.
//
#if defined(TRACE_VIDEO_PICKPOINT)
	static BitTrace Video_Pickpoint("Video Pickpoint");
	#define SET_VIDEO_PICKPOINT() Video_Pickpoint.Set()
	#define CLEAR_VIDEO_PICKPOINT() Video_Pickpoint.Clear()
#else
	#define SET_VIDEO_PICKPOINT()
	#define CLEAR_VIDEO_PICKPOINT()
#endif
//
// Traces the time spent setting up the video culling parameters
//
#if defined(TRACE_VIDEO_CULL_SETUP)
	static BitTrace Video_Cull_Setup("Video Cull Setup");
#	define SET_VIDEO_CULL_SETUP() Video_Cull_Setup.Set()
#	define CLEAR_VIDEO_CULL_SETUP() Video_Cull_Setup.Clear()
#else
#	define SET_VIDEO_CULL_SETUP()
#	define CLEAR_VIDEO_CULL_SETUP()
#endif
//
// This trace covers the entire time spent executing renderables as one
// transition.  It goes up when the execute implimentation starts running
// over the all iterator and goes down when it's done.
//
#if defined(TRACE_VIDEO_RENDERABLES)
	static BitTrace Video_Renderables("Video Renderables");
	#define SET_VIDEO_RENDERABLES() Video_Renderables.Set()
	#define CLEAR_VIDEO_RENDERABLES() Video_Renderables.Clear()
#else
	#define SET_VIDEO_RENDERABLES()
	#define CLEAR_VIDEO_RENDERABLES()
#endif
//
// Traces time spent executing renderables that belong to a vehicle entity
// (currently Mech, VTV or BTPlayer)
//
#if defined(TRACE_VIDEO_VEHICLE_RENDERABLES)
	static BitTrace Video_Vehicle_Renderables("Video Vehicle Renderables");
#	define SET_VIDEO_VEHICLE_RENDERABLES() Video_Vehicle_Renderables.Set()
#	define CLEAR_VIDEO_VEHICLE_RENDERABLES() Video_Vehicle_Renderables.Clear()
#else
#	define SET_VIDEO_VEHICLE_RENDERABLES()
#	define CLEAR_VIDEO_VEHICLE_RENDERABLES()
#endif
//
// This trace will transition as each individual renderable is executed.
// It will buzz and cause serious impact to the accuracy of the trace but is
// good for identifying time-eating individual renderables
//
#if defined(TRACE_VIDEO_ALL_RENDERABLES)
	static BitTrace Video_All_Renderables("Video All Renderables");
#	define SET_VIDEO_ALL_RENDERABLES() Video_All_Renderables.Set()
#	define CLEAR_VIDEO_ALL_RENDERABLES() Video_All_Renderables.Clear()
#else
#	define SET_VIDEO_ALL_RENDERABLES()
#	define CLEAR_VIDEO_ALL_RENDERABLES()
#endif
//
// This is the routine that does a DPL_FlushArticulations on the batch of
// DCS's that we've been holding during rendering, flushing them all to
// the card in a big batch
//
#if defined(TRACE_VIDEO_BATCH_FLUSH)
	static BitTrace Video_Batch_Flush("Video Batch Flush");
#	define SET_VIDEO_BATCH_FLUSH() Video_Batch_Flush.Set()
#	define CLEAR_VIDEO_BATCH_FLUSH() Video_Batch_Flush.Clear()
#else
#	define SET_VIDEO_BATCH_FLUSH()
#	define CLEAR_VIDEO_BATCH_FLUSH()
#endif
//
// This trace shows the time spent doing the "frame done" processing where the
// renderer waits for the DPL card to become ready and then issues a command
// to start the scene drawing.  An additional batch flush can happen here.
// If USE_ONE_VIDEO_TRACE is turned on, data from this trace will show up as
// part of the Video_Renderer trace.
//
#if defined(TRACE_VIDEO_RENDERER_FRAME_DONE)
#	if defined(USE_ONE_VIDEO_TRACE)
#		define SET_VIDEO_RENDERER_FRAME_DONE() Video_Renderer.Set()
#		define CLEAR_VIDEO_RENDERER_FRAME_DONE() Video_Renderer.Clear()
#	else
		static BitTrace Video_Renderer_Frame_Done("Video Renderer Frame Done");
#		define SET_VIDEO_RENDERER_FRAME_DONE() Video_Renderer_Frame_Done.Set()
#		define CLEAR_VIDEO_RENDERER_FRAME_DONE() Video_Renderer_Frame_Done.Clear()
#	endif
#else
#	define SET_VIDEO_RENDERER_FRAME_DONE()
#	define CLEAR_VIDEO_RENDERER_FRAME_DONE()
#endif
//
// The following traces are specifically designed for profileing the low level
// DPL routines in Division's libraries.  You need to link to a special "profile"
// version of the division libraries for these to work.  If you enable them
// with a production libdpl you will get NO traces.  If you disable them and
// try to link to a profile version of libdpl you will get link errors.  Sorry
// but this is the most efficient way of connecting the c++ trace routines to
// the C code of division's libraries.
//
// There are switches in the DPL libraries that handle turning these functions
// on and off in the profile version.
//
//#define 	TRACE_DPL_LOAD_OBJECT
//#define 	TRACE_DPL_TRANSPUTER_LINK			// will buzz
//#define	TRACE_DPL_DO_OUTSW               // will buzz
//#define	TRACE_DPL_OUTINT32               // will buzz

#if defined(TRACE_DPL_LOAD_OBJECT)
	static BitTrace DPL_Load_Object("DPL Load Object");

	extern "C"
	{
		void Set_DPL_Load_Object();
		void Clear_DPL_Load_Object();
	}

	void
		Set_DPL_Load_Object()
	{
		DPL_Load_Object.Set();
	}

	void
		Clear_DPL_Load_Object()
	{
		DPL_Load_Object.Clear();
	}
#endif

#if defined(TRACE_DPL_TRANSPUTER_LINK)
	static BitTrace DPL_Transputer_Link("DPL Transputer Link");

	extern "C"
	{
		void Set_DPL_Transputer_Link();
		void Clear_DPL_Transputer_Link();
	}

	void
		Set_DPL_Transputer_Link()
	{
		DPL_Transputer_Link.Set();
	}

	void
		Clear_DPL_Transputer_Link()
	{
		DPL_Transputer_Link.Clear();
	}
#endif

#if defined(TRACE_DPL_DO_OUTSW)
	static BitTrace DPL_do_outsw("DPL do_outsw");

	extern "C"
	{
		void Set_DPL_do_outsw();
		void Clear_DPL_do_outsw();
	}

	void
		Set_DPL_do_outsw()
	{
		DPL_do_outsw.Set();
	}

	void
		Clear_DPL_do_outsw()
	{
		DPL_do_outsw.Clear();
	}
#endif

#if defined(TRACE_DPL_OUTINT32)
	static BitTrace DPL_outint32("DPL outint32");

	extern "C"
	{
		void Set_DPL_outint32();
		void Clear_DPL_outint32();
	}

	void
		Set_DPL_outint32()
	{
		DPL_outint32.Set();
	}

	void
		Clear_DPL_outint32()
	{
		DPL_outint32.Clear();
	}
#endif

//
// The following trace will probably be obsolete soon
//

#if 0
#if defined(TRACE_VIDEO_FIRST_FRAME_DONE)
	static BitTrace Video_First_Frame_Done("Video First Frame Done");
#	define SET_VIDEO_FIRST_FRAME_DONE() Video_First_Frame_Done.Set()
#	define CLEAR_VIDEO_FIRST_FRAME_DONE() Video_First_Frame_Done.Clear()
#else
#	define SET_VIDEO_FIRST_FRAME_DONE()
#	define CLEAR_VIDEO_FIRST_FRAME_DONE()
#endif
#endif

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Definitions of various debugging switches
//
#define	DEBUG_SPECIAL_CALLBACK	0			// 0 = none, 1 = print special, 2 = real noisy
#define	DEBUG_CREATE_CALLBACK	0			//	0 = none,
#define	PRINT_THE_PFX				False		// Set to true and the pfx definition will print when it is read in.
#define 	RAPID_SECT_PIXEL			True		// True if you want to use the rapidsectpixel call for intersections
#define 	PARTICLE_TEST				False		// True to issue a stream of particles at the detected intersect point
#define	PRINT_PICKPOINT_TEST		False		// True to print data whenever the intersect changes
#define	NOISY_RENDERER				False		// True enables a lot of printouts as the renderer makes/kills object
#define	USE_TRACKER_STRUCTURE	False		// True to enable the full tracker structure that holds damage zone names & such
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Definitions of system constants used only in this file
//
#define MAX_SPECIAL_ARGUMENTS 25				// Maximum number of arguments a "special" callback can handle
#define MAX_SPECIAL_SIZE 512              // Maximum size (characters) of a "special"
#define dpl_arg_sep '~' 						// seperator used when parsing dpl argument string
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity
	*Entity_Being_Created = 0;		// !!! temp, till callback handlers become a class
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// These extern "c" routines and constants are from startdpl or from dpl
// itself and need to be here.
//
extern "C" {
	extern const int
		x_size,						// x size of the screen (from startdpl)
		y_size;             		// y size of the screen (startdpl)
	void
		dpl_TexmapTexels2D (		// A routine from DPL we normally couldn't access
			dpl_TEXMAP *tm,
			uint32 *texels,
			int32  u_size,
			int32  v_size,
			int32  bytes_per_texel);
	int
		screen_resolution(			// Sets up the screen resolution (startdpl)
			char *dpl_argv ),
		explode_args( 					// Parses DPLARG system environmental (startdpl)
			char **argv,
			char *dpl_args,
			char sep );
	char
		*dpl_TypeToString(			// Converts a DPL type code into a string (libdpl)
			dpl_TYPE t);
	int32
		dpl_DrawSceneComplete(void);	// missing definition (libdpl)
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// see DPLReportPerfStats()
extern "C" const int32 __sect_time;
extern "C" const int32 __last_cull_time;
extern "C" const int32 __last_draw_time;
extern "C" const int32 __last_frame_time;
extern "C" const int32 __last_pxpl_time;
extern "C" const int32 __last_frame_prims;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// dump_frame_buffer() declaration
void dump_frame_buffer(
		dpl_VIEW *eye,
		int32 x_size,
		int32 y_size,
		Logical antialias);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// The following are definitions of DPL callback functions.  These are called
// by the dpl file loader as it is loading graphics files so we can do material
// substitution and set special attributes on geometry & such.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// The "special" callback is called whenever a "special" field is processed in
// incomming geometry.
// dPL special callback function type.
//	typedef void (dpl_SPECIAL_CALLBACK)(dpl_TYPE type, void *handle,
//												char8 *special, uint32 special_len);

static void
	TestSpecialCallBack(
		dpl_TYPE type,
		void *handle,
		char8 *special,
		uint32 special_len)
{
	char
		*args[MAX_SPECIAL_ARGUMENTS],
		**argptr,
		*argstring;
	int
		argcount;
	float
		u0,
		v0,
		du,
		dv;
	int32
		immunity;
	char
//		NameBuff[80],
		TempBuff[MAX_SPECIAL_SIZE + 1];
	#if USE_TRACKER_STRUCTURE
	dpl_tracker
		*this_tracker;
	#endif

	if(special_len > MAX_SPECIAL_SIZE)
	{
		DEBUG_STREAM<<"SPECIAL was bigger than "<<MAX_SPECIAL_SIZE<<"\n";
		Verify(special_len > MAX_SPECIAL_SIZE);
	}
	strncpy(TempBuff, special,special_len);
	TempBuff[special_len] = 0;
	if(TempBuff[special_len-1] != 0)
	{
		DEBUG_STREAM<<"Caution, the following SPECIAL was not null terminated\n";
		printf("SPECIAL->%08x: %s, %s\n", handle, dpl_TypeToString(type), TempBuff);
	}
	#if DEBUG_SPECIAL_CALLBACK >= 1
	printf("SPECIAL->%08x: %s, %s\n", handle, dpl_TypeToString(type), TempBuff);
	#endif
//
// Scan all the argument start points into an array for easier processing.
// also avoids non-reentrand problem with strtok
//
	argstring = TempBuff;
	for(argcount = 0; argcount<MAX_SPECIAL_ARGUMENTS; argcount++)
	{
		args[argcount] = strtok(argstring," ");
		if(!args[argcount])
			break;
		argstring = NULL;
	}
	args[MAX_SPECIAL_ARGUMENTS-1] = 0;
//
// Decode the dpl_type the special was found in, all I know of are listed here
// though some we may never see.
//
	switch(type)
	{
		case dpl_type_error:
			break;
		case dpl_type_scene:
			break;
		case dpl_type_zones:
			break;
		case dpl_type_view:
			break;
		case dpl_type_instance:
			break;
		case dpl_type_dcs:
			break;
		case dpl_type_light:
			break;
		case dpl_type_object:
			for(argptr = args;
				 *argptr;
				 argptr++)
			{
				if (strcmp(*argptr, "ADDITIVE_LODS") == 0)
				{
					#if DEBUG_SPECIAL_CALLBACK >= 2
					Tell("Calling SetObjectAdditiveLODs on an object\n");
					#endif
					// REMOVE dpl_SetObjectAdditiveLODs to run with older renderers
					dpl_SetObjectAdditiveLODs ( (dpl_OBJECT *)handle);
				}
			}
			break;
		case dpl_type_lod:
			break;
		case dpl_type_geogroup:
			#if USE_TRACKER_STRUCTURE
			this_tracker	=	(dpl_tracker *)dpl_GetAppSpecific(handle);
			#endif
			for(argptr = args;
				 *argptr;
				 argptr++)
			{
				if (strncmp(*argptr, "dz_", 3) == 0)
				{
					#if DEBUG_SPECIAL_CALLBACK >= 2
					Tell("Doing SPECIAL GEOGROUP Damage zone ");
					#endif

					#if USE_TRACKER_STRUCTURE
					if (this_tracker)
					{
						#if DEBUG_SPECIAL_CALLBACK >= 2
						Tell("marked\n");
						#endif
						strncpy(this_tracker->dz_name, *argptr, MAX_DZ_NAME_LENGTH);
						this_tracker->dz_name[MAX_DZ_NAME_LENGTH - 1] = 0;
					#endif
						//---------------------------------------------
						// lookup damage zone index in global namelist
						//---------------------------------------------
						if (Entity_Being_Created->damageZones)
						{
							Check_Pointer(Entity_Being_Created->damageZones);

							int
								damage_zone_index;

							damage_zone_index =
								Entity_Being_Created->GetDamageZoneIndex(*argptr);

							if (damage_zone_index != -1)
							{
								#if USE_TRACKER_STRUCTURE
								this_tracker->Damage_Zone_Number = damage_zone_index;
								#else
								if(dpl_GetAppSpecific(handle))
									Fail("AppSpecific already set\n");
								dpl_PutAppSpecific(handle, (void *)(damage_zone_index+1));
								#endif
							}
							else
							{
								DEBUG_STREAM << endl << "Damage zone '" <<
									*argptr << "' not in table." << endl;
							}
						}
					#if USE_TRACKER_STRUCTURE
					}
					else
					{
						#if DEBUG_SPECIAL_CALLBACK >= 2
						Tell("NOT marked\n");
						#endif
					}
               #endif
				}
				else if(strcmp(*argptr,"PUNCH") == 0)
				{
					#if DEBUG_SPECIAL_CALLBACK >= 2
					Tell("Doing SPECIAL GEOGROUP Punchize\n");
					#endif
					dpl_Punchize((dpl_GEOGROUP *)handle);
				}
				else if(strcmp(*argptr,"DAMAGE") == 0)
				{
					#if DEBUG_SPECIAL_CALLBACK >= 2
					Tell("Doing SPECIAL GEOGROUP damagize\n");
					#endif
					if(*(++argptr))
					{
						int
							status;
						dpl_MATERIAL *damagize_material = dpl_LookupMaterial ( *argptr, dpl_lookup_normal, &status );
						if(damagize_material)
						{
							dpl_Damagize((dpl_GEOGROUP *)handle, damagize_material);
						}
						else
						{
							DEBUG_STREAM<<"SPECIAL GEOGROUP DAMAGE couldn't find material "<<*argptr<<"\n";
							Verify(damagize_material);
						}
					}
					else
					{
						DEBUG_STREAM<<"SPECIAL GEOGROUP DAMAGE no material name \n";
						Verify(*argptr);
					}
				}
				else if(strcmp(*argptr,"WIREFRAME") == 0)
				{
					#if DEBUG_SPECIAL_CALLBACK >= 2
					Tell("Doing SPECIAL GEOGROUP WIREFRAME\n");
					#endif
					dpl_SetGeogroupWireframe((dpl_GEOGROUP *)handle, True);
				}
				else if(strcmp(*argptr,"GEOMETRIZE") == 0)
				{
					#if DEBUG_SPECIAL_CALLBACK >= 2
					Tell("Doing SPECIAL GEOGROUP geometrize\n");
					#endif
					if(*(++argptr))
					{
						dpl_GEOMETRY
							*geometry_ptr;
						long
							geometrize_code;
						int
							geometry_id	= 0;
						sscanf(
							*argptr,
							"%lx",
							&geometrize_code);
						do
						{
							geometry_ptr = dpl_GetGeogroupGeometry((dpl_GEOGROUP *)handle,geometry_id);
							if(geometry_ptr)
							{
								dpl_Geometrize(geometry_ptr,geometrize_code);
							}
							geometry_id++;
						} while(geometry_ptr);
					}
					else
					{
						DEBUG_STREAM<<"SPECIAL GEOGROUP GEOMETRIZE has no geometrize code\n";
						Verify(*argptr);
					}
				}
				else if(strcmp(*argptr,"DRAWLAST") == 0)
				{
					#if DEBUG_SPECIAL_CALLBACK >= 2
					Tell("Doing SPECIAL GEOGROUP DRAWLAST\n");
					#endif
					dpl_SetGeogroupDrawLast((dpl_GEOGROUP *)handle, True);
					dpl_FlushGeogroup((dpl_GEOGROUP *)handle);
				}
				else if(strcmp(*argptr,"BLINK") == 0)
				{
					#if DEBUG_SPECIAL_CALLBACK >= 2
					Tell("SPECIAL GEOGROUP BLINK is not supported\n");
					#endif
				}
				else
				{
					DEBUG_STREAM<<"SPECIAL GEOGROUP "<<*argptr<<" is not a geogroup modifier\n";
				}
			}
			break;
		case dpl_type_geometry:
			break;
		case dpl_type_material:
			for(argptr = args;
				 *argptr;
				 argptr++)
			{
				if(strcmp(*argptr,"IMMUNE") == 0)
				{
					#if DEBUG_SPECIAL_CALLBACK >= 2
					Tell("Doing SPECIAL MATERIAL IMMUNE\n");
					#endif
					if(*(++argptr))
					{
						immunity = atol(*argptr);
						dpl_SetMaterialFogImmunity ( (dpl_MATERIAL *)handle, immunity );
						dpl_FlushMaterial((dpl_MATERIAL *)handle);
					}
					else
					{
						DEBUG_STREAM<<"SPECIAL MATERIAL IMMUNE has no immune code\n";
						Verify(*argptr);
					}
				}
				else
				{
					DEBUG_STREAM<<"SPECIAL MATERIAL "<<*argptr<<" is not a material modifier\n";
				}
			}
			break;
		case dpl_type_texture:
			for(argptr = args;
				 *argptr;
				 argptr++)
			{
				if(strcmp(*argptr,"SCROLL") == 0)
				{
					#if DEBUG_SPECIAL_CALLBACK >= 2
					Tell("Doing SCROLL\n");
					#endif
					u0 = v0 = du = dv = 0.0;
					if(*(++argptr))
						u0 = atof(*argptr);
					else
					{
						DEBUG_STREAM<<"SPECIAL TECTURE SCROLL missing u0\n";
						Verify(*argptr);
					}
					if(*(++argptr))
						v0 = atof(*argptr);
					else
					{
						DEBUG_STREAM<<"SPECIAL TECTURE SCROLL missing v0\n";
						Verify(*argptr);
					}
					if(*(++argptr))
						du = atof(*argptr);
					else
					{
						DEBUG_STREAM<<"SPECIAL TECTURE SCROLL missing du\n";
						Verify(*argptr);
					}
					if(*(++argptr))
						dv = atof(*argptr);
					else
					{
						DEBUG_STREAM<<"SPECIAL TECTURE SCROLL missing dv\n";
						Verify(*argptr);
					}
					dpl_SetTextureScroll (
						(dpl_TEXTURE *)handle,
						u0,
						v0,
						du,
						dv);
					dpl_FlushTexture((dpl_TEXTURE *)handle);
				}
				else if(strcmp(*argptr,"FAKESIZE") == 0)
				{
					#if DEBUG_SPECIAL_CALLBACK >= 2
					Tell("Doing SPECIAL TEXURE FAKESIZE\n");
					#endif
					int
						size, offset;
					if(*(++argptr))
						size = atoi(*argptr);
					else
					{
						DEBUG_STREAM<<"SPECIAL TEXTURE FAKESIZE missing size\n";
						Verify(*argptr);
					}
					if(*(++argptr))
						offset = atoi(*argptr);
					else
					{
						DEBUG_STREAM<<"SPECIAL TEXTURE FAKESIZE missing offset\n";
						Verify(*argptr);
					}
					dpl_FakeTextureSize((dpl_TEXTURE *)handle,size,offset);
					dpl_FlushTexture((dpl_TEXTURE *)handle);
				}
				else
				{
					DEBUG_STREAM<<"SPECIAL TEXTURE "<<*argptr<<" is not a texture modifier\n";
				}
			}
			break;
		case dpl_type_texmap:
			break;
		case dpl_type_ramp:
			break;
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// The TestCreateCallBack callback is called whenever a dpl node of the indicated
// type is created while loading a DPL graphics file.
/* dPL create/delete callback function type. */
//typedef void (dpl_CREATE_DELETE_CALLBACK)(dpl_TYPE type, void *handle);

static void
	TestCreateCallBack(
		dpl_TYPE type,
		#if DEBUG_CREATE_CALLBACK>=1
			void *handle
		#else
			void *
		#endif
	)
{
	//
	// get a pointer to the node structure (first part of most dpl types)
	//
	#if DEBUG_CREATE_CALLBACK >=1
	printf("CREATE->%08x: %s\n", handle, dpl_TypeToString(type));
	#endif
	switch(type)
	{
		case dpl_type_error:
			break;
		case dpl_type_scene:
			break;
		case dpl_type_zones:
			break;
		case dpl_type_view:
			break;
		case dpl_type_instance:
			break;
		case dpl_type_dcs:
			break;
		case dpl_type_light:
			break;
		case dpl_type_object:
			break;
		case dpl_type_lod:
			break;
		case dpl_type_geogroup:
		#if USE_TRACKER_STRUCTURE
		{
			dpl_tracker *this_tracker			=	new dpl_tracker;
			this_tracker->This_Entity			=	Entity_Being_Created;
			this_tracker->dz_name[0]			=	0;
			this_tracker->Damage_Zone_Number	=	-1;	// no damage zone (default)
			if(dpl_GetAppSpecific(handle))
				DEBUG_STREAM<<"app_specific hook already set!\n";
			dpl_PutAppSpecific(handle, this_tracker);

			break;
		}
		#endif
		case dpl_type_geometry:
			break;
		case dpl_type_material:
			break;
		case dpl_type_texture:
			break;
		case dpl_type_texmap:
			break;
		case dpl_type_ramp:
			break;
	}
}

//#############################################################################
// Code to setup and handle material substitutions.
//#############################################################################
//
NameList
	*materialSubstitutionList = NULL;

/* dPL material name callback function type. */
//typedef char8 *(dpl_MATERIAL_NAME_CALLBACK)(char8 *mat_name);
char8*
	substituteMaterial(
		char8 *source
	)
{
	static char
		buffer[MATERIAL_NAME_BUFFER_LENGTH];
	NameList::Entry
		*entry;
	const char
		*search,
		*replace,
		*pc;
	int
		len;
	//----------------------------------------------
	// perform text substitution
	// first match in the sub list gets substituted
	// materialSubstitutionList  is a pre-prepared global namelist.
	//----------------------------------------------
	entry = materialSubstitutionList->GetFirstEntry();
	while (entry)
	{
		search = entry->GetName();
		if (search && *search && (pc = strstr(source, search)) != NULL)
		{
			replace = entry->GetChar();
			*buffer = '\0';

			len = pc - source;
			while (*replace == '<')
			{
				++replace;
				--len;
			}
			if (len > 0)
			{
				strncat(buffer, source, len);
			}

			pc += strlen(search);
			len = strlen(replace);
			while (len && *(replace+len-1) == '>')
			{
				--len;
				if (*pc)  { ++pc; }
			}
			if (len > 0)
			{
				strncat(buffer, replace, len);
			}

			Str_Cat(buffer, pc, MATERIAL_NAME_BUFFER_LENGTH);
			Str_Copy(source, buffer, MATERIAL_NAME_BUFFER_LENGTH);
			break;
		}
		entry = entry->GetNextEntry();
	}
	return source;
}
//-----------------------------------------------------------------------------
//--------------------------DPL video resource object--------------------------
//-----------------------------------------------------------------------------
//#############################################################################
//############################    L4VideoObject    ############################
//#############################################################################

L4VideoObject::L4VideoObject(
	const char *filename,
	ResourceType resource_type,
	Enumeration renderer_modes,	// RendererModes
	float blink_period,
	float percent_time_on
)
{
	Check_Pointer(filename);

	Str_Copy(objectFilename, filename, sizeof(objectFilename));
	//---------------------------------------------------------------
	// pad objectFilename with nulls so all .res files are identical
	//---------------------------------------------------------------
	char
		*p = objectFilename + strlen(filename) + 1,
		*c = objectFilename + sizeof(objectFilename);

	for (; p < c; ++p)
		{ *p = '\0'; }

	resourceType = resource_type;
	rendererModes = renderer_modes;
	blinkPeriod = blink_period;
	percentTimeOn = percent_time_on;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
L4VideoObject::~L4VideoObject()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	L4VideoObject::TestInstance() const
{
	Verify( strlen(objectFilename) < sizeof(objectFilename) );
	return True;
}

//#############################################################################
//########################    L4VideoObjectWrapper    #########################
//#############################################################################

L4VideoObjectWrapper::L4VideoObjectWrapper(
	L4VideoObject *video_object,
	Logical delete_object
)
{
	Check_Pointer(video_object);	// do not use Check()

	videoObject = video_object;
	deleteObject = delete_object;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
L4VideoObjectWrapper::~L4VideoObjectWrapper()
{
	if (deleteObject)
	{
		Unregister_Pointer(videoObject);
		delete videoObject;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	L4VideoObjectWrapper::TestInstance() const
{
	if (videoObject)
	{
		Check_Pointer(videoObject);	// do not use Check()
	}
	return True;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// static
int
	L4VideoObjectWrapper::BuildVideoObjectChainFromResource(
		ChainOf<L4VideoObjectWrapper*> *video_chain,
		ResourceDescription *video_resource
	)
{
	//do not Check(this) - static
	Check(video_chain);
	Check(video_resource);

	const char
		*video_pointer;
	int
		object_count,
		index;
	long
		object_size;
	L4VideoObject
		*video_object;
	L4VideoObjectWrapper
		*video_wrapper;

	//----------------------------------------------------
	// convert video resource into chain of video objects
	//----------------------------------------------------
	video_pointer = (char *)video_resource->resourceAddress;
	Check_Pointer(video_pointer);

	object_count = *((int *)video_pointer);
	video_pointer += sizeof(int);

	object_size = sizeof(L4VideoObject);
	Verify( video_resource->resourceSize == sizeof(int) + object_count * object_size );

	//Tell("video chain: ("<<object_count<<")");

	for (index = 0; index < object_count; ++index)
	{
		video_object = (L4VideoObject *)video_pointer;
		//do not register (done through resource)
		video_pointer += object_size;

		//Tell("  '"<<video_object->GetObjectFilename()<<"' 0x"<<hex<<video_object->GetRendererModes());

		video_wrapper = new L4VideoObjectWrapper(video_object, False);
		Register_Object(video_wrapper);

		video_chain->Add(video_wrapper);
	}

	//Tell(dec<<"\n");

	return object_count;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// static
void
	L4VideoObjectWrapper::DeleteVideoObjectChain(
		ChainOf<L4VideoObjectWrapper*> *video_chain
	)
{
	//do not Check(this) - static
	Check(video_chain);

	ChainIteratorOf<L4VideoObjectWrapper*>
		video_iterator(video_chain);
	L4VideoObjectWrapper
		*video_wrapper;

	while ((video_wrapper = video_iterator.ReadAndNext()) != NULL)
	{
		Check(video_wrapper);

		Unregister_Object(video_wrapper);
		delete video_wrapper;
	}
	return;
}

//-----------------------------------------------------------------------------
//-------------------Actual DPL video renderer starts below--------------------
//-----------------------------------------------------------------------------

UserHeap* DPLRenderer::DPLHeap = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Constructor for DPLRenderer
//
DPLRenderer::DPLRenderer(
	RendererRate calibration_rate,
	RendererComplexity calibration_complexity,
	RendererPriority calibration_priority,
	InterestType interest_type,
	InterestDepth depth_calibration
):
	VideoRenderer(
		 calibration_rate,
		 calibration_complexity,
		 calibration_priority,
		 interest_type,
		 depth_calibration
		),
	projectile_list(NULL),
	dplObjectCacheSocket(NULL, False),
	dplJointToDCSTranslatorSocket(NULL,False),
	dplRenderableSocket(NULL)
{
	char
		Eye_Type[50],
		*Eye_Arg,			// Controls generation of the eye point
		*DPL_Arg;	  		// Points to DPLARG environment pointer once we get it
	int
		psfx_number;
//	int
//		i;            		// Temporary loop counter
	float
		eye_x,
		eye_y,
		eye_z,
		eye_x_rot,
		eye_y_rot,
		eye_z_rot;
//
// Initialize a bunch of the variables to their starting values
//
	eyeRelative				=	False;
	completeCycleNeeded	=	False;
	lastAppState			=	0;
	fogUpdating 			= True;
	fogRed					=	0.0f;
	fogBlue					=	0.0f;
	fogGreen					=	0.0f;
	fogNear					=	0.0f;
	fogFar					=	0.0f;
	searchLightFogRed			=	0.0f;
	searchLightFogGreen		=	0.0f;
	searchLightFogBlue		=	0.0f;
	searchLightFogNear		=	0.0f;
	searchLightFogFar			=	0.0f;
	noSearchLightFogRed		=	0.0f;
	noSearchLightFogGreen	=	0.0f;
	noSearchLightFogBlue		=	0.0f;
	noSearchLightFogNear		=	0.0f;
	noSearchLightFogFar		=	0.0f;
	clipNear					=	0.0f;
	clipFar					=	0.0f;
	backgroundRed			=	0.0f;
	backgroundGreen		=	0.0f;
	backgroundBlue			=	0.0f;
	viewAngle				=	30.0f;
	dplMainView				=	NULL;
	dplDeathZone			=	NULL;
	dplMainZone				=	NULL;
	vehicleReticle			=	NULL;
	dplHitInstance			=	NULL;
	dplHitDCS				=	NULL;
	dplHitGeoGroup			=	NULL;
	dplHitGeometry			=	NULL;
	delayedDCSCount		=	0;
	myUniqueID				=	0;
	ambientLight 			= NULL;
	sceneLightDCS			= NULL;
	sceneLight				= NULL;
	sceneLightCount		=	0;

	worldToEyeMatrix		=	LinearMatrix::Identity;		// the current world to eye transform for our linked entity
	currentFrameTime		=	Now();							// the time at the start of renderable execution

	Verify(!DPLHeap);
	DPLHeap = new UserHeap("DPL Heap", 2000000);
	Register_Object(DPLHeap);

	//
	// Clear the myPSFXDescriptons array to all zeros so we can detect attempts
	// to use uninitialized items.
	//
	for(	psfx_number = 0; psfx_number < MAX_PSFX_COUNT; psfx_number++)
	{
		myPSFXDescriptons[psfx_number]	=	NULL;
	}
//
// These guys will come out of a world environment variable eventually
//
	eye_x				=	0.0f;
	eye_y				=	10.0f;
	eye_z				=	0.0f;
	eye_x_rot		=	0.0f;
	eye_y_rot		=	0.0f;
	eye_z_rot		=	0.0f;
//
// Get pointers to environmentals containing DPL startup arguments and
// eye positioning information.  If L4EYES is present set the renderer
// variable that controls the hooking up of the eye position.
//
	Eye_Type[0] =	0;
	DPL_Arg		=	getenv("DPLARG");
	Eye_Arg		=	getenv("L4EYES");
	if(Eye_Arg)
	{
		sscanf(
			Eye_Arg,
			"%f %f %f %f %f %f %s",
			&eye_x,
			&eye_y,
			&eye_z,
			&eye_x_rot,
			&eye_y_rot,
			&eye_z_rot,
			Eye_Type);

		printf("%f, %f, %f  %f, %f, %f\n",
				  eye_x,
				  eye_y,
				  eye_z,
				  eye_x_rot,
				  eye_y_rot,
				  eye_z_rot);

		Disconnected_Eye	=	True;		// tells vidrend:: to request outside view of linked entity
		if(*Eye_Type == 'r')
		{
			DEBUG_STREAM<<"DPLRenderer::DPLRenderer Eye will be offset relative to vehicle\n";
			eyeRelative	=	True;
		}
	}
//
// If the argument was empty, we can't render !!! should exit sensiblly
//
	if(!DPL_Arg)
	{
		DEBUG_STREAM << "DPLARG must be set for the Division card to come up\n";
		Verify(DPL_Arg);
	}
//
// If we're still here, try to interpret and setup the screen resolution
// requested by DPLARG.  The screen_resolution function is Phil's routine
// in startdpl.
//
	if (screen_resolution ( DPL_Arg ) == 0)
	{
		DEBUG_STREAM << "DPLARG is bad, I couldn't understand video format\n";
		Verify(DPL_Arg);
	}
//
// Breakdown the DPLARGS string using expload_args from startdpl, then feed
// the results to the dpl_Init routine to get DPL up and running.
//
	char *argv[32];
	int argc = explode_args ( argv, DPL_Arg, dpl_arg_sep );
	dpl_Init							( argc, argv );
	//
	// Setup the file paths for geometry and texture loading
	//
//	dpl_SetObjectFilePath   ( ".\\video", "\\video", "..\\video" );
//	dpl_SetTexmapFilePath   ( ".\\video", "\\video", "..\\video" );
//	dpl_SetMaterialFilePath ( ".\\video", "\\video", "..\\video" );
//	dpl_SetObjectFilePath   ( ".\\video", "", "" );
//	cout<<dpl_GetObjectFilePath()<<"\n";
//	dpl_SetTexmapFilePath   ( ".\\video", "", "" );
//	cout<<dpl_GetTexmapFilePath()<<"\n";
//	dpl_SetMaterialFilePath ( ".\\video", "", "" );
//	cout<<dpl_GetMaterialFilePath()<<"\n";
	//
	// Set the extensions and the DPL routines that will load them
	//
	dpl_SetExtnObjectLoadFunc (".bgf",  dpl_bgfRead);
	dpl_SetExtnTexmapLoadFunc (".vtx",  dpl_vtxRead);
	dpl_SetExtnTexmapLoadFunc (".tga",  dpl_tgaRead);
//	dpl_SetExtnTexmapLoadFunc (".int",  dpl_sgiRead);
//	dpl_SetExtnTexmapLoadFunc (".inta", dpl_sgiRead);
//	dpl_SetExtnTexmapLoadFunc (".rgb",  dpl_sgiRead);
//	dpl_SetExtnTexmapLoadFunc (".rgba", dpl_sgiRead);
	//
	// Setup the dpl callback routines for handling specials, object creation and
	// material name munging.
	//
	dpl_SetSpecialCallback(TestSpecialCallBack);
	dpl_SetCreateNodeCallback(TestCreateCallBack);
	//	dpl_SetMaterialNameCallback(TestMaterialCallBack);
	//
	// Create the one zone everything will live in !!!! more zones later for smart culling
	//
	dplMainZone		=	dpl_NewZone();
	Check_Pointer(dplMainZone);
	dplDeathZone	=	dpl_NewZone();
	Check_Pointer(dplDeathZone);
//
// Create the view we will use for our eyepoint
//
	dplMainView	=	dpl_NewView();
	Check_Pointer(dplMainView);
//
// This creates a DCS and links it to the view, I probably should just let the
// eye renderable do this but it's convenient since it can be used as the
// eye point for testing.
//
	dplTestEyeDCS	=	dpl_NewDCS();
	Check_Pointer		( dplTestEyeDCS);
	dpl_RotateDCS   	( dplTestEyeDCS, eye_z_rot, dpl_Z );
	dpl_RotateDCS   	( dplTestEyeDCS, eye_x_rot, dpl_X );
	dpl_RotateDCS   	( dplTestEyeDCS, eye_y_rot, dpl_Y );
	dpl_TranslateDCS	( dplTestEyeDCS, eye_x, eye_y, eye_z);

	dpl_SetDCSZone		( dplTestEyeDCS, dplMainZone );
	dpl_SetViewDCS		( dplMainView,   dplTestEyeDCS);
//
// Do some setup work on the zone
//
	dpl_SetZoneAllViewsOn 	( dplMainZone );
	dpl_SetZoneAllViewsOn 	( dplDeathZone );
//
// Setup some defaults so we can safely render a blank screen now
//
	dpl_SetViewClipPlanes	( dplMainView, 1.0f, 2.0f );
	dpl_SetViewBackGround	( dplMainView, 0.0f, 0.0f, 0.0f);
	dpl_SetPipeWindow			(0, 0, 0, x_size, y_size);
	dpl_SetViewProjection	( dplMainView, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f/(tan( 0.5235987666667f)));
	dpl_SetViewPipe			( dplMainView, 0);
	dpl_AddViewToScene		( dplMainView );
	dpl_SetViewFog				(dplMainView, dpl_fog_type_pixel_lin, 0.0, 0.0, 0.0, 0.01, 0.05 );
//
//	DPLReadEnvironment();
//
	dpl_FlushDCS				( dplTestEyeDCS);
	dpl_FlushView				( dplMainView);
	dpl_FlushZone				( dplMainZone);
	dpl_FlushZone				( dplDeathZone);
//
// Call DrawScene to force the system do draw a blank screen
//
	dpl_DrawScene();
//
// Start a clock used to keep track of the frame rate
//
	StartSample		= Now();
	FrameCount		= 0;
	ResetStatistics();
   statistics_started = False;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dpl_ZONE*
	DPLRenderer::MakeNewZone()
{
	dpl_ZONE
		*temp_zone;
	//
	// Make a new zone for use by someone constructing an object, switch it on
	// and flush it out to the card.
	//
	temp_zone		=	dpl_NewZone();
	Check_Pointer(temp_zone);
	dpl_SetZoneAllViewsOn 	(temp_zone);
	dpl_FlushZone				(temp_zone);
	//
	// HACK !!! at this point we should put the zone into a chain so we can
	// find it to switch it on and off later.
	//
   return(temp_zone);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// AddDynamicRenderable puts a renderable into this renderer's dynamic execution
// socket so it will be run every frame.
//
void
	DPLRenderer::AddDynamicRenderable(Component *my_renderable)
{
	Check(my_renderable);
	Check(&dplRenderableSocket);
	dplRenderableSocket.Add(my_renderable);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// RemoveDynamicRenderable removes a renderable from the dynamic execution
// socket.
//
void
	DPLRenderer::RemoveDynamicRenderable(Component *my_renderable)
{
	Check(my_renderable);
	Check(&dplRenderableSocket);
	PlugIterator	remover(my_renderable);
	remover.RemoveSocket(&dplRenderableSocket);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
	DPLRenderer::SetFogStyle(FogStyle my_fog)
{
	switch(my_fog)
	{
		case updateFogSetting:
			fogUpdating = True;
			break;
		case noUpdateFogSetting:
			fogUpdating = False;
			break;
		case searchLightOnFogStyle:
			fogRed	=	searchLightFogRed;
			fogGreen =	searchLightFogGreen;
			fogBlue	=	searchLightFogBlue;
			fogNear	=	searchLightFogNear;
			fogFar	=	searchLightFogFar;
			if(fogUpdating)
			{
				dpl_SetViewFog(
					dplMainView,
					dpl_fog_type_pixel_lin,
					fogRed,
					fogGreen,
					fogBlue,
					fogNear,
					fogFar );
				dpl_FlushView(dplMainView);
			}
			break;
		case searchLightOffFogStyle:
			fogRed	=	noSearchLightFogRed;
			fogGreen =	noSearchLightFogGreen;
			fogBlue	=	noSearchLightFogBlue;
			fogNear	=	noSearchLightFogNear;
			fogFar	=	noSearchLightFogFar;
			if(fogUpdating)
			{
				dpl_SetViewFog(
					dplMainView,
					dpl_fog_type_pixel_lin,
					fogRed,
					fogGreen,
					fogBlue,
					fogNear,
					fogFar );
				dpl_FlushView(dplMainView);
			}
			break;
		case winnersCircleFogStyle:
			//
			// HACK!!  This really shouldn't reset the clip planes, but since
			// it only happens at the end of the review, it should be safe for now.
			//
			dpl_SetViewClipPlanes	( dplMainView, 0.25f, 1100.0f );
			dpl_SetViewFog(
						dplMainView,
						dpl_fog_type_pixel_lin,
						0.32,
						0.3,
						0.65,
						100.0f,
						1050.0f );
			dpl_FlushView(dplMainView);
			break;
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
	DPLRenderer::GetCurrentFogSettings(
		float	*fog_Red,
		float	*fog_Green,
		float *fog_Blue,
		float *fog_Near,
		float *fog_Far)
{
	*fog_Red	=	fogRed;
	*fog_Green =	fogGreen;
	*fog_Blue	=	fogBlue;
	*fog_Near	=	fogNear;
	*fog_Far	=	fogFar;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	MarkDCSHiearchy	This will decend a DCS tree and set the appSpecific hook
// in every DCS equal to the entity pointer "entity", it calls itself recursively
// to do this.
//
void
	DPLRenderer::MarkDCSHiearchy(
		dpl_DCS	*root_DCS,				// Root of the hiearchy to mark
		Entity	*entity)             // Entity pointer to mark it with
{
	int
		dcs_counter;
	dpl_DCS
		*child_DCS;
	//
	// First, mark this DCS
	//
	if(dpl_GetAppSpecific(root_DCS))
		Fail("DPLRenderer::MarkDCSHiearchy tried to mark a DCS that was already marked!\n");
	dpl_PutAppSpecific(root_DCS,entity);
	//
	// Now call this routine on all this dcs's children
	//
	dcs_counter = 0;
	while((child_DCS=dpl_GetDCSChildDCS(root_DCS,dcs_counter)) != NULL)
	{
		MarkDCSHiearchy(child_DCS, entity);
		dcs_counter++;
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ReadPSFX
// reads a pfx file into a supplied array
// Most of this routine was a direct copy of phil's code and as such is a bit
// on the messy side.
//
dpl_PARTICLESTART_EFFECT_INFO*
	DPLRenderer::ReadPSFX(
		const char	*file_name)		// Name of the file containing the PSFX description
{
	FILE
		*fp;
	int
		i,
		status;
	char
		line[256],
		*cp;
	//
	// Open the file containing the psfx, fail if it doesn't exist
	//
	strcpy(line,"video\\");
	strcat(line,file_name);
	fp	=	fopen ( line, "rt" );
	if(!fp)
	{
		DEBUG_STREAM<<"Could not open pfx file "<<line<<" effects using it will fail\n";
		return(NULL);
//		Fail("Required PFX file not found\n");
	}
	//
	//	Allocate the memory for the pfx to be stored in.
	//
	dpl_PARTICLESTART_EFFECT_INFO	*psfx_info	=	new  dpl_PARTICLESTART_EFFECT_INFO;
	if(!psfx_info)
	{
		Fail("ran out of RAM trying to read a pfx file\n");
	}
	//
	// This is the format of the psfx file we are going to read
	//
	#if 0
	format of psfx file is (NO BLANK LINES UNTIL END!)
	%s texture
	%d identifier %d maximum_issue %f release_period %f rate
	%f px %f py %f pz %f pv
	%f velx %f vely %f velz %f velxv %f velyv %f velzv
	%f rad %f radv %f exp %f expv %f dexp %f dexpv
	%f accelx %f accely %f accelz %f accelxv %f accelyv %f accelzv
	%f atten %f attenv
	%f sRi %f sGi %f sBi %f sAi %f sRiv %f sGiv %f sBiv %f sAiv
	%f sRo %f sGo %f sBo %f sAo %f sRov %f sGov %f sBov %f sAov
	%f eRi %f eGi %f eBi %f eAi %f eRiv %f eGiv %f eBiv %f eAiv
	%f eRo %f eGo %f eBo %f eAo %f eRov %f eGov %f eBov %f eAov
	%f color_warp %f alpha_warp
	%f duration %f durationv
	#endif
	//
	// Read in the texture name, null terminate it and look it up,
	// we don't complain if it fails since it is possible to do one of these
	// effects with no texture on it.
	//
	cp = fgets ( line, 255, fp );
	for (i=0; i<255; i++ )
	{
		if (cp[i] == '\n')
			cp[i]=0x0;
	}
	psfx_info->tex = dpl_LookupTexture ( cp, dpl_lookup_normal, &status );
	#if PRINT_THE_PFX
	printf ( "texture => %s\n", cp );
	#endif
	//
	// The remander of these statements get the rest of the PFX data
	//
	cp = fgets ( line, 255, fp );
	status = sscanf  ( cp,
		"%x %d %f %f\n",
		&psfx_info->identifier, &psfx_info->maximum_issue, &psfx_info->release_period, &psfx_info->rate );
	if(status < 4)
	{
		cout<<"pfx file "<<file_name<<" did not read correctly\n";
		return(NULL);
	}

	cp = fgets ( line, 255, fp );
	status = sscanf  ( cp, "%f %f %f %f\n", &psfx_info->px, &psfx_info->py, &psfx_info->pz, &psfx_info->pv );
	if(status < 4)
	{
		cout<<"pfx file "<<file_name<<" did not read correctly\n";
		return(NULL);
	}

	cp = fgets ( line, 255, fp );
	status = sscanf  ( cp, "%f %f %f %f %f %f\n",
	&psfx_info->velx, &psfx_info->vely, &psfx_info->velz,
	&psfx_info->velxv, &psfx_info->velyv, &psfx_info->velzv );
	if(status < 6)
	{
		cout<<"pfx file "<<file_name<<" did not read correctly\n";
		return(NULL);
	}

	cp = fgets ( line, 255, fp );
	status = sscanf  ( cp, "%f %f %f %f %f %f\n",
	&psfx_info->rad, &psfx_info->radv, &psfx_info->exp, &psfx_info->expv, &psfx_info->dexp, &psfx_info->dexpv );
	if(status < 6)
	{
		cout<<"pfx file "<<file_name<<" did not read correctly\n";
		return(NULL);
	}

	cp = fgets ( line, 255, fp );
	status = sscanf  ( cp, "%f %f %f %f %f %f\n",
	&psfx_info->accelx, &psfx_info->accely, &psfx_info->accelz,
	&psfx_info->accelxv, &psfx_info->accelyv, &psfx_info->accelzv );
	if(status < 6)
	{
		cout<<"pfx file "<<file_name<<" did not read correctly\n";
		return(NULL);
	}

	cp = fgets ( line, 255, fp );
	status = sscanf  ( cp, "%f %f\n", &psfx_info->atten, &psfx_info->attenv );
	if(status < 2)
	{
		cout<<"pfx file "<<file_name<<" did not read correctly\n";
		return(NULL);
	}

	cp = fgets ( line, 255, fp );
	status = sscanf  ( cp, "%f %f %f %f %f %f %f %f\n",
	&psfx_info->sRi,&psfx_info->sGi,&psfx_info->sBi,&psfx_info->sAi,&psfx_info->sRiv,&psfx_info->sGiv,&psfx_info->sBiv,&psfx_info->sAiv );
	if(status < 8)
	{
		cout<<"pfx file "<<file_name<<" did not read correctly\n";
		return(NULL);
	}

	cp = fgets ( line, 255, fp );
	status = sscanf  ( cp, "%f %f %f %f %f %f %f %f\n",
	&psfx_info->sRo,&psfx_info->sGo,&psfx_info->sBo,&psfx_info->sAo,&psfx_info->sRov,&psfx_info->sGov,&psfx_info->sBov,&psfx_info->sAov );
	if(status < 8)
	{
		cout<<"pfx file "<<file_name<<" did not read correctly\n";
		return(NULL);
	}

	cp = fgets ( line, 255, fp );
	status = sscanf  ( cp, "%f %f %f %f %f %f %f %f\n",
	&psfx_info->eRi,&psfx_info->eGi,&psfx_info->eBi,&psfx_info->eAi,&psfx_info->eRiv,&psfx_info->eGiv,&psfx_info->eBiv,&psfx_info->eAiv );
	if(status < 8)
	{
		cout<<"pfx file "<<file_name<<" did not read correctly\n";
		return(NULL);
	}

	cp = fgets ( line, 255, fp );
	status = sscanf  ( cp, "%f %f %f %f %f %f %f %f\n",
	&psfx_info->eRo,&psfx_info->eGo,&psfx_info->eBo,&psfx_info->eAo,&psfx_info->eRov,&psfx_info->eGov,&psfx_info->eBov,&psfx_info->eAov );
	if(status < 8)
	{
		cout<<"pfx file "<<file_name<<" did not read correctly\n";
		return(NULL);
	}

	cp = fgets ( line, 255, fp );
	status = sscanf  ( cp, "%f %f\n", &psfx_info->colour_warp, &psfx_info->alpha_warp );
	if(status < 2)
	{
		cout<<"pfx file "<<file_name<<" did not read correctly\n";
		return(NULL);
	}

	cp = fgets ( line, 255, fp );
	status = sscanf  ( cp, "%f %f\n", &psfx_info->dur, &psfx_info->durv );
	if(status < 2)
	{
		cout<<"pfx file "<<file_name<<" did not read correctly\n";
		return(NULL);
	}

	fclose (fp);
	#if PRINT_THE_PFX
	printf ( "Read psfx ->\n" );
	/*{{{  trace the psfx*/
	printf ( "psfx_info->tex = 0x%x\n",
	psfx_info->tex );
	printf ( "identifier = 0x%x ",
	psfx_info->identifier );
	printf ( "maximum_issue = %d ",
	psfx_info->maximum_issue );
	printf ( "release_period = %f\n",
	psfx_info->release_period );
	printf ( "rate = %f ",
	psfx_info->rate );
	printf ( "px = %f ",
	psfx_info->px );
	printf ( "py = %f ",
	psfx_info->py );
	printf ( "pz = %f ",
	psfx_info->pz );
	printf ( "pv = %f\n",
	psfx_info->pv );
	printf ( "velx = %f ",
	psfx_info->velx );
	printf ( "vely = %f ",
	psfx_info->vely );
	printf ( "velz = %f\n",
	psfx_info->velz );
	printf ( "velxv = %f ",
	psfx_info->velxv );
	printf ( "velyv = %f ",
	psfx_info->velyv );
	printf ( "velzv = %f\n",
	psfx_info->velzv );
	printf ( "rad = %f ",
	psfx_info->rad );
	printf ( "radv = %f\n",
	psfx_info->radv );
	printf ( "exp = %f ",
	psfx_info->exp );
	printf ( "expv = %f ",
	psfx_info->expv );
	printf ( "dexp = %f ",
	psfx_info->dexp );
	printf ( "dexpv = %f\n",
	psfx_info->dexpv );
	printf ( "accelx = %f ",
	psfx_info->accelx );
	printf ( "accely = %f ",
	psfx_info->accely );
	printf ( "accelz = %f ",
	psfx_info->accelz );
	printf ( "accelxv = %f ",
	psfx_info->accelxv );
	printf ( "accelyv = %f ",
	psfx_info->accelyv );
	printf ( "accelzv = %f\n",
	psfx_info->accelzv );
	printf ( "atten = %f ",
	psfx_info->atten );
	printf ( "attenv = %f\n",
	psfx_info->attenv );
	printf ( "sRi = %f ",
	psfx_info->sRi );
	printf ( "sGi = %f ",
	psfx_info->sGi );
	printf ( "sBi = %f ",
	psfx_info->sBi );
	printf ( "sAi = %f\n",
	psfx_info->sAi );
	printf ( "sRiv = %f ",
	psfx_info->sRiv );
	printf ( "sGiv = %f ",
	psfx_info->sGiv );
	printf ( "sBiv = %f ",
	psfx_info->sBiv );
	printf ( "sAiv = %f\n",
	psfx_info->sAiv );
	printf ( "sRo = %f ",
	psfx_info->sRo );
	printf ( "sGo = %f ",
	psfx_info->sGo );
	printf ( "sBo = %f ",
	psfx_info->sBo );
	printf ( "sAo = %f\n",
	psfx_info->sAo );
	printf ( "sRov = %f ",
	psfx_info->sRov );
	printf ( "sGov = %f ",
	psfx_info->sGov );
	printf ( "sBov = %f ",
	psfx_info->sBov );
	printf ( "sAov = %f\n",
	psfx_info->sAov );
	printf ( "eRi = %f ",
	psfx_info->eRi );
	printf ( "eGi = %f ",
	psfx_info->eGi );
	printf ( "eBi = %f ",
	psfx_info->eBi );
	printf ( "eAi = %f\n",
	psfx_info->eAi );
	printf ( "eRiv = %f ",
	psfx_info->eRiv );
	printf ( "eGiv = %f ",
	psfx_info->eGiv );
	printf ( "eBiv = %f ",
	psfx_info->eBiv );
	printf ( "eAiv = %f\n",
	psfx_info->eAiv );
	printf ( "eRo = %f ",
	psfx_info->eRo );
	printf ( "eGo = %f ",
	psfx_info->eGo );
	printf ( "eBo = %f ",
	psfx_info->eBo );
	printf ( "eAo = %f\n",
	psfx_info->eAo );
	printf ( "eRov = %f ",
	psfx_info->eRov );
	printf ( "eGov = %f ",
	psfx_info->eGov );
	printf ( "eBov = %f ",
	psfx_info->eBov );
	printf ( "eAov = %f\n",
	psfx_info->eAov );
	printf ( "colour_warp = %f ",
	psfx_info->colour_warp );
	printf ( "alpha_warp = %f ",
	psfx_info->alpha_warp );
	printf ( "dur = %f ",
	psfx_info->dur );
	printf ( "durv = %f\n",
	psfx_info->durv );
   #endif
	return(psfx_info);
}
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// New INI file handler
// routine to read in an environment from a notation file and setup lights,
// spfx and such...
//
void
	DPLRenderer::DPLReadINIPage(
		NotationFile		*master_notation_file,
		const char			*starting_page_name,
		Mission 				*mission,
		Logical				debug_printing)
{
	float
		red,
		green,
		blue,
		x_rotate,
		y_rotate,
		z_rotate;
	NameList
		*cache_namelist,
		*light_namelist,
		*psfx_namelist,
		*specialfx_namelist,
		*include_pages,
		*path_pages;
	NameList::Entry
		*entry;
	const char
		*next_include_page_name,
		*TempStringPtr,
		*compare_source;

	if (debug_printing)
		cout<<"DPLReadINIPage processing "<<starting_page_name<<"\n";
	//
	//---------------------------------------------------
	// Process all the DPL gunk found on this page first
	//---------------------------------------------------
   //
	if ((path_pages = master_notation_file->MakeEntryList(starting_page_name, "objectpath")) != NULL)
	{
		Register_Object(path_pages);
		//
		// Recursively process all the include pages
		//
		for(	entry = path_pages->GetFirstEntry();
				entry;
				entry = entry->GetNextEntry())
		{
			dpl_AddToObjectFilePath((char *)entry->dataReference, dpl_path_user);
			if (debug_printing)
			{
				DEBUG_STREAM << "objectpath '" << dpl_GetObjectFilePath() << "'" << endl;
			}
		}
		Unregister_Object(path_pages);
		delete path_pages;
	}
	if ((path_pages = master_notation_file->MakeEntryList(starting_page_name, "texmappath")) != NULL)
	{
		Register_Object(path_pages);
		//
		// Recursively process all the include pages
		//
		for(	entry = path_pages->GetFirstEntry();
				entry;
				entry = entry->GetNextEntry())
		{
			dpl_AddToTexmapFilePath((char *)entry->dataReference, dpl_path_user);
			if (debug_printing)
			{
				DEBUG_STREAM << "texmappath '" << dpl_GetTexmapFilePath() << "'" << endl;
			}
		}
		Unregister_Object(path_pages);
		delete path_pages;
	}
	if ((path_pages = master_notation_file->MakeEntryList(starting_page_name, "materialpath")) != NULL)
	{
		Register_Object(path_pages);
		//
		// Recursively process all the include pages
		//
		for(	entry = path_pages->GetFirstEntry();
				entry;
				entry = entry->GetNextEntry())
		{
			dpl_AddToMaterialFilePath((char *)entry->dataReference, dpl_path_user);
			if (debug_printing)
			{
				DEBUG_STREAM << "materialpath '" << dpl_GetMaterialFilePath() << "'" << endl;
			}
		}
		Unregister_Object(path_pages);
		delete path_pages;
	}

	if (master_notation_file->GetEntry(starting_page_name, "priorityobjectpath", &TempStringPtr))
	{
		dpl_AddToObjectFilePath((char *)TempStringPtr, dpl_path_system);
		if (debug_printing)
			{ DEBUG_STREAM << "priorityobjectpath '" << TempStringPtr << "'" << endl; }
	}
	if (master_notation_file->GetEntry(starting_page_name, "prioritytexmappath", &TempStringPtr))
	{
		dpl_AddToTexmapFilePath((char *)TempStringPtr, dpl_path_system);
		if (debug_printing)
			{ DEBUG_STREAM << "prioritytexmappath '" << TempStringPtr << "'" << endl; }
	}
	if (master_notation_file->GetEntry(starting_page_name, "prioritymaterialpath", &TempStringPtr))
	{
		dpl_AddToMaterialFilePath((char *)TempStringPtr, dpl_path_system);
		if (debug_printing)
			{ DEBUG_STREAM << "prioritymaterialpath '" << TempStringPtr << "'" << endl; }
	}
	//
	// Get a the list of dpl objects that should be loaded into cache and load them
	//
	if ((cache_namelist = master_notation_file->MakeEntryList(starting_page_name, "cache")) != NULL)
	{
		dpl_OBJECT
			*dpl_a_object;
		Register_Object(cache_namelist);
		for(	entry = cache_namelist->GetFirstEntry();
				entry;
				entry = entry->GetNextEntry())
		{
			dpl_a_object	=	dpl_LoadObject((char *)entry->dataReference, dpl_load_normal);
			if(!dpl_a_object)
				DEBUG_STREAM<<"Unable to cache "<<(char *)entry->dataReference<<"\n";
			else
			{
				if(debug_printing)
					cout<<"Caching "<<(char *)entry->dataReference<<"\n";
			}
		}
		Unregister_Object(cache_namelist);
		delete cache_namelist;
	}
	//
	// Setup the clip range
	//
	if(master_notation_file->GetEntry(starting_page_name,	"clip"	,&TempStringPtr))
	{
		sscanf(
			TempStringPtr,
			"%f %f",
			&clipNear,
			&clipFar);
		dpl_SetViewClipPlanes	( dplMainView, clipNear, clipFar );
		if(debug_printing)
			cout<<"Clip Range "<<TempStringPtr<<"\n";
	}
	//
	// Background color
	//
	if(master_notation_file->GetEntry(starting_page_name,	"backgnd"	,&TempStringPtr))
	{
		sscanf(
			TempStringPtr,
			"%f %f %f",
			&backgroundRed,
			&backgroundGreen,
			&backgroundBlue);
		dpl_SetViewBackGround	( dplMainView, backgroundRed, backgroundGreen, backgroundBlue);
		if(debug_printing)
			cout<<"Background Color "<<TempStringPtr<<"\n";
	}
	//
	// setup the projection, pipe and other view related junk
	//
	if(master_notation_file->GetEntry(starting_page_name,	"viewangle"	,&TempStringPtr))
	{
		sscanf(
			TempStringPtr,
			"%f",
			&viewAngle);
		dpl_SetPipeWindow			(0, 0, 0, x_size, y_size);
		SetViewAngle(viewAngle);
		dpl_SetViewPipe			( dplMainView, 0);
		if(debug_printing)
			cout<<"View Angle "<<TempStringPtr<<"\n";
	}
	//
	// setup the fog
	//
	if(master_notation_file->GetEntry(starting_page_name,	"fog"	,&TempStringPtr))
	{
		sscanf(
			TempStringPtr,
			"%f %f %f %f %f",
			&fogNear,
			&fogFar,
			&fogRed,
			&fogGreen,
			&fogBlue);
			searchLightFogRed			=	fogRed;
			searchLightFogGreen		=	fogGreen;
			searchLightFogBlue		=	fogBlue;
			searchLightFogNear		=	fogNear;
			searchLightFogFar			=	fogFar;
			noSearchLightFogRed		=	fogRed;
			noSearchLightFogGreen	=	fogGreen;
			noSearchLightFogBlue		=	fogBlue;
			noSearchLightFogNear		=	fogNear;
			noSearchLightFogFar		=	fogFar;
		// Force a 0-0 black fog on startup
		dpl_SetViewFog	(dplMainView, dpl_fog_type_pixel_lin, 0.0, 0.0, 0.0, 0.01, 0.05 );
		if(debug_printing)
			cout<<"Fog "<<TempStringPtr<<"\n";
	}
	//
	// setup the no searchlight fog if any
	//
	if(master_notation_file->GetEntry(starting_page_name,	"nosearchlightfog"	,&TempStringPtr))
	{
		sscanf(
			TempStringPtr,
			"%f %f %f %f %f",
			&noSearchLightFogNear,
			&noSearchLightFogFar,
			&noSearchLightFogRed,
			&noSearchLightFogGreen,
			&noSearchLightFogBlue);
		if(debug_printing)
			cout<<"nosearchlightfog "<<TempStringPtr<<"\n";
	}
	//
	// setup the ambient light
	//
	if(master_notation_file->GetEntry(starting_page_name,	"ambient"	,&TempStringPtr))
	{
		sscanf(
			TempStringPtr,
			"%f %f %f ",
			&red,
			&green,
			&blue);
		ambientLight	=	dpl_NewLight();
		Register_Pointer(ambientLight);

		dpl_SetLightType  ( ambientLight, dpl_light_type_ambient );
		dpl_SetLightColor ( ambientLight, red, green, blue );
		dpl_FlushLight		( ambientLight );
		if(debug_printing)
			cout<<"Ambient Light "<<TempStringPtr<<"\n";
	}
	//
	// Get a the list of lights from this page
	//
	if ((light_namelist = master_notation_file->MakeEntryList(starting_page_name, "light")) != NULL)
	{
		Register_Object(light_namelist);
		//
		// HACK !!! All the lights must (temporarily) be defined on a single page.
		// The lights and their DCS's should really be stored in a light object on
		// a chain, or as an actual entity in the simulation.  This line will throw
		// an exception if this rule is violated
		//
		if(light_namelist->EntryCount() != 0)
		{
			if(sceneLightCount != 0)
			{
				Fail("All lights must be defined on a single INI file page!\n");
			}
			//
			// Create storage space for the dpl_LIGHT and dpl_DCS structures
			//
			sceneLightCount	=	light_namelist->EntryCount();
			if (sceneLightCount)
			{
				sceneLightDCS	= new (dpl_DCS (*[sceneLightCount]));
				Register_Pointer(sceneLightDCS);
				sceneLight		= new (dpl_LIGHT (*[sceneLightCount]));
				Register_Pointer(sceneLight);
			}
			int current_entry(0);
			for(	entry = light_namelist->GetFirstEntry();
					entry;
					entry = entry->GetNextEntry(), ++current_entry)
			{
				//
				// Read the parameters for the light
				//
				sscanf(
					(char *)entry->dataReference,
					"%f %f %f %f %f %f",
					&red,
					&green,
					&blue,
					&x_rotate,
					&y_rotate,
					&z_rotate);
				if(debug_printing)
					cout<<"Light "<<(char *)entry->dataReference<<"\n";
				//
				// Create and rotate a dcs to hold the light, and the light itself
				//
				Verify(current_entry <= sceneLightCount);
				sceneLight[current_entry]		=	dpl_NewLight();
				Register_Pointer(sceneLight[current_entry]);
				sceneLightDCS[current_entry]	=	dpl_NewDCS();
				Register_Pointer(sceneLightDCS[current_entry]);

				dpl_AddDCSToScene	( sceneLightDCS[current_entry] );
				dpl_SetLightType  ( sceneLight[current_entry],	dpl_light_type_directional );
				dpl_SetLightColor ( sceneLight[current_entry],	red, green, blue);
				dpl_SetLightDCS 	( sceneLight[current_entry],	sceneLightDCS[current_entry] );
				dpl_RotateDCS   	( sceneLightDCS[current_entry],	z_rotate, dpl_Z );
				dpl_RotateDCS   	( sceneLightDCS[current_entry],	x_rotate, dpl_X );
				dpl_RotateDCS   	( sceneLightDCS[current_entry],	y_rotate, dpl_Y );
				dpl_FlushLight		( sceneLight[current_entry] );
				dpl_FlushDCS		( sceneLightDCS[current_entry] );
			}
		}
		//
		// Get rid of the light entry list
		//
		Unregister_Object(light_namelist);
		delete light_namelist;
	}
	//
	// Get a the list of PSFX effects we should load into RAM
	//
	if ((psfx_namelist = master_notation_file->MakeEntryList(starting_page_name, "psfx")) != NULL)
	{
		const char
      	*psfx_file_name;
		int
			psfx_number;
		Register_Object(psfx_namelist);
		//
		// create all the PSFX on the current page
		//
		for(	entry = psfx_namelist->GetFirstEntry();
				entry;
				entry = entry->GetNextEntry())
		{
			psfx_number		=	atoi(entry->GetName()+4);
			psfx_file_name	=	entry->GetChar();
			if(debug_printing)
				cout<<"psfx"<<psfx_number<<" "<<psfx_file_name<<"\n";
			if(psfx_number < 0 || psfx_number > MAX_PSFX_COUNT-1)
			{
				Fail("PSFX id number was not in the allowed range");
			}
			//
			// See if we are overwriting an existing psfx
			//
			if(myPSFXDescriptons[psfx_number])
			{
				cout<<"psfx#"<<psfx_number<<" already existed from an earlier page, redefining it\n";
				delete myPSFXDescriptons[psfx_number];
				Unregister_Pointer(myPSFXDescriptons[psfx_number]);
			}
	//		DEBUG_STREAM<<"Loading PSFX# "<<psfx_number<<" from file "<<psfx_file_name<<"\n";
			myPSFXDescriptons[psfx_number]	=	ReadPSFX(psfx_file_name);
			Register_Pointer(myPSFXDescriptons[psfx_number]);
		}
		//
		// Get rid of the PSFX entry list
		//
		Unregister_Object(psfx_namelist);
		delete psfx_namelist;
	}
	//
	// Get a the list of special effects
	//
	if ((specialfx_namelist = master_notation_file->MakeEntryList(starting_page_name, "specialfx")) != NULL)
	{
		char
			*texture_name;
		const char
			*effect_page_name;
		int
			status,
			spfx_number,
			spfx_type,
			spfx_count,
			spfx_repeats;
		float
			spfx_size,
			spfx_velocity,
			spfx_bias,
			spfx_y_off,
			spfx_cook_r,
			spfx_cook_g,
			spfx_cook_b,
			spfx_varience,
			spfx_gravity,
			spfx_cool,
			spfx_ocool;
		dpl_TEXTURE
			*effect_texture;

		Register_Object(specialfx_namelist);
		//
		// create all the effects on the current page
		//
		for(	entry = specialfx_namelist->GetFirstEntry();
				entry;
				entry = entry->GetNextEntry())
		{
			spfx_number = atoi(entry->GetName()+9);
			//
			// Get the page with this effect on it
			//
			effect_page_name = entry->GetChar();
			if(debug_printing)
				cout<<"specialfx"<<spfx_number<<" "<<effect_page_name<<"\n";
			master_notation_file->GetEntry(effect_page_name,	"type",		&spfx_type);
			master_notation_file->GetEntry(effect_page_name,	"size",		&spfx_size);
			master_notation_file->GetEntry(effect_page_name,	"velocity",	&spfx_velocity);
			master_notation_file->GetEntry(effect_page_name,	"v_bias",	&spfx_bias);
			master_notation_file->GetEntry(effect_page_name,	"y_off",		&spfx_y_off);
			master_notation_file->GetEntry(effect_page_name,	"variance",	&spfx_varience);
			master_notation_file->GetEntry(effect_page_name,	"gravity",	&spfx_gravity);
			master_notation_file->GetEntry(effect_page_name,	"count",		&spfx_count);
			master_notation_file->GetEntry(effect_page_name,	"repeats",	&spfx_repeats);
			master_notation_file->GetEntry(effect_page_name,	"cook",		&TempStringPtr);
			sscanf(
				TempStringPtr,
				"%f %f %f",
				&spfx_cook_r,
				&spfx_cook_g,
				&spfx_cook_b);
			master_notation_file->GetEntry(effect_page_name,	"cool",		&TempStringPtr);
			sscanf(
				TempStringPtr,
				"%f %f",
				&spfx_cool,
				&spfx_ocool);
			//
			// install the effect
			//
			master_notation_file->GetEntry(effect_page_name,	"texture"	,&TempStringPtr);
			texture_name = SKIPPY_CAST(char*, TempStringPtr);
			effect_texture = dpl_LookupTexture ( texture_name, dpl_lookup_normal, &status );

			if (effect_texture == NULL)
				DEBUG_STREAM<<"couldn't find texture "<<texture_name<<" for an effect\n";

			dpl_install_sfx ( spfx_number,			// sfx_code
									effect_texture,		// texture
									spfx_type,          	// type
									spfx_size,				// size
									spfx_velocity, 		// velocity
									spfx_bias,				// bias
									spfx_y_off, 			//	y-off
									spfx_cook_r, 			// R,G,B
									spfx_cook_g,
									spfx_cook_b,
									spfx_varience,			// varience
									spfx_gravity,			// gravity
									spfx_cool,           // cool
									spfx_ocool,				// ocool
									spfx_count,          // count
									spfx_repeats,			//	reps
									0x3);						// Fog Immunity
		}
		//
		// Get rid of the effect entry list
		//
		Unregister_Object(specialfx_namelist);
		delete specialfx_namelist;
	}
	//--------------------------------------------------
	// Make a list of all the include pages on this page
	//--------------------------------------------------
	if ((include_pages = master_notation_file->MakeEntryList(starting_page_name, "include")) != NULL)
	{
		Register_Object(include_pages);
		//
		// Recursively process all the include pages
		//
		for(	entry = include_pages->GetFirstEntry();
				entry;
				entry = entry->GetNextEntry())
		{
			// Get the name of the next include page
			next_include_page_name = (char *)entry->dataReference;
			// Process the next page recursively through this routine
			DPLReadINIPage(
				master_notation_file,
				next_include_page_name,
				mission,
				debug_printing);
			if(debug_printing)
				cout<<"DPLReadINIPage returned to "<<starting_page_name<<"\n";
		}
		Unregister_Object(include_pages);
		delete include_pages;
	}
	//
	// Process a compare and branch if there is one on this page
	//
	if(master_notation_file->GetEntry(starting_page_name,	"compare"	,&compare_source))
	{
		Logical
			match;
		NameList
			*branch_pages;
		CString
			compare_strings[10],
			wild_card("*"),
			token_string,
         target_string,
			master_compare_string(compare_source);
		int
			token_count;
		ResourceFile
			*this_resource_file;
		//
		// Get a pointer to our resource file so we can find the name of the map
		// resource later in this process.
		//
		Check(application);
		this_resource_file	=	application->GetResourceFile();
		Check(this_resource_file);
		//
		// Print the compare string
		//
		if(debug_printing)
			cout<<"compare = "<<master_compare_string<<"\n";
		//
		// Break the compare string apart, fetch the mission variables for each
		// item in it and store them in the compare_strings array for later.
		//
		token_count = 0;
		while(target_string = master_compare_string.GetNthToken(token_count,","))
		{
//			cout<<"Mission variable "<<token_count<<": ";
			if(strcmp(target_string,"location") == 0)
			{
				compare_strings[token_count]	= (this_resource_file->FindResourceDescription(mission->GetMapID()))->resourceName;
//				cout<<target_string<<" = "<<compare_strings[token_count]<<"\n";
			}
			else if(strcmp(target_string,"time") == 0)
			{
				compare_strings[token_count]	= mission->GetMissionTime();
//				cout<<target_string<<" = "<<compare_strings[token_count]<<"\n";
			}
			else if(strcmp(target_string,"weather") == 0)
			{
				compare_strings[token_count]	= mission->GetMissionWeather();
//				cout<<target_string<<" = "<<compare_strings[token_count]<<"\n";
			}
			else if(strcmp(target_string,"scenario") == 0)
			{
				compare_strings[token_count]	= mission->GetScenarioName();
//				cout<<target_string<<" = "<<compare_strings[token_count]<<"\n";
			}
			else
			{
				cout<<"Mission variable "<<token_count<<": "<<target_string<<" IS AN UNKNOWN MISSION VARIABLE\n";
			}
			token_count++;
		}
		//
		// Print the version of the compare string with the actual mission variables
		// in it
		//
		int i;
		if(debug_printing)
		{
			cout<<"comparing to: ";
			for(i = 0; i<token_count; i++)
			{
				cout<<compare_strings[i]<<", ";
			}
			cout<<"\n";
		}
		//
		// Now compare all the branch pages till we have done them all or found a match
		//
		branch_pages = master_notation_file->MakeEntryList(starting_page_name,"branch");
		Register_Object(branch_pages);
		for(	entry = branch_pages->GetFirstEntry();
				entry;
				entry = entry->GetNextEntry())
		{
			CString
				branch_command,
				match_string;
			int
				compare_token;
			//
			// Get the text of the branch command and print it
			//
			branch_command = (char *)entry->dataReference;
			if(debug_printing)
				cout<<"branch = "<<branch_command<<"\n";
			//
			// Grab each token out of the branch string and compare it to the
			// corrisponding item in compare_strings.  This also checks for the
			// wildcard cararcter.  Any failure to match skips this branch string.
			//
			match = True;
			for(compare_token = 0; compare_token<token_count; compare_token++)
			{
				match_string = branch_command.GetNthToken(compare_token,",");
				if(match_string != compare_strings[compare_token] && match_string != wild_card)
				{
					match = False;
//					cout<<match_string<<"!="<<compare_strings[compare_token]<<"\n";
					break;
				}
				else
				{
//					cout<<match_string<<"=="<<compare_strings[compare_token]<<"\n";
				}
			}
			//
			// See if we had a successful match
			//
			if(match)
			{
				// Match! Get the name of the page to branch to and do it.
				match_string = branch_command.GetNthToken(compare_token,",");
				DPLReadINIPage(
					master_notation_file,
					match_string,
					mission,
					debug_printing);
				if(debug_printing)
					cout<<"DPLReadINIPage returned to "<<starting_page_name<<"\n";
				// now break out of the loop because we only allow one page to match
				break;
			}
		}
		Unregister_Object(branch_pages);
		delete branch_pages;

		//
		// If we found no match, yell about it
		//
		if(!match)
		{
			DEBUG_STREAM << "BRANCH ERROR IN '" <<
				master_notation_file->GetFileName() <<
				"' NO MATCH FOUND!!" <<	endl;
		}
	}
}
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Read environment
// routine to read in an environment from a notation file and setup lights,
// spfx and such...
//
void
	DPLRenderer::DPLReadEnvironment(Mission 				*mission)
{
	char
		*L4DPLcfg;
//	const char
//		*cc;

	NotationFile
		*renderer_environment;

	Logical
		debug_printing;
	//
	// Get the name of the notation file from an environmental
	//
	L4DPLcfg		=	getenv("L4DPLCFG");
	if (!L4DPLcfg)
	{
		DEBUG_STREAM<<"L4DPLCFG not defined, using dpldflt.ini!\n";
		L4DPLcfg = "dpldflt.ini";
	}
	//
	// Read the environment out of a notation file
	//
	renderer_environment = new NotationFile(L4DPLcfg);
	Register_Object(renderer_environment);
	if (renderer_environment->PageCount() == 0)
	{
		DEBUG_STREAM<<"Renderer config notation file "<<L4DPLcfg<<"was empty, can't continue\n";
		Verify(renderer_environment->PageCount() == 0);
	}
	//
	// Register the notation file and check it
	//
	Check(renderer_environment);
	if (renderer_environment->PageExists("main"))
	{
		if (!(renderer_environment->GetLogicalEntry("main", "debug", &debug_printing)))
		{
			debug_printing=False;
		}
		DPLReadINIPage(
			renderer_environment,
			"main",
			mission,
			debug_printing);
#if 0
		if ((cc = dpl_GetObjectFilePath()) == NULL || *cc == NULL)
		{
			dpl_SetObjectFilePath	( ".\\video", NULL, NULL );
		}
		if ((cc = dpl_GetTexmapFilePath()) == NULL || *cc == NULL)
		{
			dpl_SetTexmapFilePath	( ".\\video", NULL, NULL );
		}
		if ((cc = dpl_GetMaterialFilePath()) == NULL || *cc == NULL)
		{
			dpl_SetMaterialFilePath	( ".\\video", NULL, NULL );
		}
#endif
	}
	else
	{
		if (!(renderer_environment->GetLogicalEntry("dpl_config", "debug", &debug_printing)))
		{
			debug_printing=False;
		}
		cout<<"READING OLD FORMAT INI FILE '"<<
			renderer_environment->GetFileName()<<
			"'---hope this works\n";
		dpl_SetObjectFilePath   ( ".\\video", NULL, NULL );
		dpl_SetTexmapFilePath   ( ".\\video", NULL, NULL );
		dpl_SetMaterialFilePath ( ".\\video", NULL, NULL );
		DPLReadINIPage(
			renderer_environment,
			"dpl_config",
			mission,
			debug_printing);
	}
	if (debug_printing)
	{
		DEBUG_STREAM << "ObjectFilePath   '" << dpl_GetObjectFilePath() << "'" << endl;
		DEBUG_STREAM << "TexmapFilePath   '" << dpl_GetTexmapFilePath() << "'" << endl;
		DEBUG_STREAM << "MaterialFilePath '" << dpl_GetMaterialFilePath() << "'" << endl;
	}
	//
	// Close the notation file
	//
	Verify( !renderer_environment->IsDirty() );
	Unregister_Object(renderer_environment);
	delete renderer_environment;
	//
	// Flush all the things we've set/changed here
	//
	dpl_FlushView				( dplMainView);
	return;
}
//
//#############################################################################
// Destructor for the video renderer
//#############################################################################
//
DPLRenderer::~DPLRenderer()
{
	int
		psfx_number;
	//------------------------------------------------------
	// release allocated memory used by dump_frame_buffer()
	//------------------------------------------------------
	dump_frame_buffer(NULL, NULL, NULL, NULL);
	//
	// Delete any memory allocated to hold psfx effect descriptions
	//
	for(	psfx_number = 0; psfx_number < MAX_PSFX_COUNT; psfx_number++)
	{
		if(myPSFXDescriptons[psfx_number])
		{
			delete myPSFXDescriptons[psfx_number];
			Unregister_Pointer(myPSFXDescriptons[psfx_number]);
			myPSFXDescriptons[psfx_number]	=	NULL;
		}
	}
	//
	//~~~~~~~~~~~~~~~~~~
	// Delete all Lights
	//~~~~~~~~~~~~~~~~~~
	//
	if (ambientLight)
	{
		Check_Pointer(ambientLight);
		Unregister_Pointer(ambientLight);
		dpl_DeleteLight(ambientLight);
	}
	if (sceneLightDCS && sceneLight)
	{
		Check_Pointer(sceneLightDCS);
		for(int ii=0;ii<sceneLightCount;++ii)
		{
			Check_Pointer(sceneLight[ii]);
			Check_Pointer(sceneLightDCS[ii]);
			dpl_RemoveDCSFromScene(sceneLightDCS[ii]);
			dpl_DeleteDCS(sceneLightDCS[ii]);
			Unregister_Pointer(sceneLight[ii]);
			Unregister_Pointer(sceneLightDCS[ii]);
		}
		Unregister_Pointer(sceneLight);
		delete[] sceneLight;
		Unregister_Pointer(sceneLightDCS);
		delete[] sceneLightDCS;
	}

	Unregister_Object(DPLHeap);
	delete DPLHeap;
   DPLHeap = NULL;
}
//
//#############################################################################
// TestInstance
//#############################################################################
//
Logical
	DPLRenderer::TestInstance() const
{
	VideoRenderer::TestInstance();

	Verify(dplMainView != NULL);
	Verify(dplMainZone != NULL);

	return True;
}
//
//#############################################################################
// FlushBitSliceTexture
// Sends the bitslice texture to the division card.
// LoadBitSliceTexture
// Handles building of a bitmap into a texture map for the Division card, this
// specifically deals with merging a 1 bit bitmap into a 4 bit bitslice texmap.
//#############################################################################
//
void
	DPLRenderer::FlushBitSliceTexture(
		unsigned int	*local_storage)
{
	int
		status;
	dpl_TEXTURE *temp_texture =
		dpl_LookupTexture("bmap:bmap1_tex", dpl_lookup_normal, &status);
	if(temp_texture == NULL)
	{
		DEBUG_STREAM<<"WARNING, textures for player names not defined\n";
	}
	else
	{
		dpl_TEXMAP *temp_texmap =
			dpl_GetTextureTexmap(temp_texture);
		if(temp_texmap != NULL)
		{
			dpl_TexmapTexels2D ( temp_texmap, local_storage,
										128,
										64,
										4 );
			dpl_FlushTexmap(temp_texmap);
		}
		else
		{
			DEBUG_STREAM<<"WARNING, texmap for player names not defined\n";
		}
	}
}
void
	DPLRenderer::LoadBitSliceTexture(
		BitMap	*bitmap_to_load,			// MUNGA 1 bit bitmap to load
		int		load_position,          // Position to load bitmap into
		unsigned int		*local_storage)			// 8192 longword array for storage of the texmap
{
	int
		x,
		y,
		bit_number;
	Word
		*word_pointer;
	uint32
		turn_on,
		turn_off,
		*pixel_pointer;

	Verify(load_position >= 1);
	Verify(load_position <=12);
	Check(bitmap_to_load);
	Check_Pointer(local_storage);
	//
	// Figure out the position to start copying the data at
	//
	word_pointer	= 	bitmap_to_load->Data.MapPointer;
	if(load_position & 0x1)
	{
		pixel_pointer	= 	local_storage + 4096;
	}
	else
	{
		pixel_pointer	= 	local_storage;
	}
	//
	// Find the bit position to merge the data into
	//
	switch(load_position)
	{
		case 	1:
		case	2:
			turn_on	=	0x0000f000;
			turn_off	=	0xffff0fff;
			break;
		case	3:
		case	4:
			turn_on	=	0x00000f00;
			turn_off	=	0xfffff0ff;
			break;
		case	5:
		case	6:
			turn_on	=	0x00f00000;
			turn_off	=	0xff0fffff;
			break;
		case	7:
		case	8:
			turn_on	=	0x000f0000;
			turn_off	=	0xfff0ffff;
			break;
		case	9:
		case	10:
			turn_on	=	0xf0000000;
			turn_off	=	0x0fffffff;
			break;
		case	11:
		case	12:
			turn_on	=	0x0f000000;
			turn_off	=	0xf0ffffff;
			break;
	}
	//
	// Actually process the texels
	//
	bit_number		=	15;
	for(y = 0; y < 32; y++)
	{
		for(x = 0; x < 128; x++)
		{
			if((*word_pointer	& (1<<bit_number)))
			{
				*(pixel_pointer++) |= turn_on;
			}
			else
			{
				*(pixel_pointer++) &= turn_off;
			}
			bit_number--;
			if(bit_number < 0)
			{
				bit_number = 15;
				word_pointer++;
			}
		}
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CacheExplosionScripts
// Handles code to preload the morph and other caches with things needed by
// explosion scripts.  Should be called at startup, once for each.
//
void
	DPLRenderer::CacheExplosionScripts(
		int	script_select)						// The script to be cached
{
	dpl_OBJECT
		*object;
	//
	// Load the morph targets into the MUNGA object cache
	//
	switch(script_select)
	{
		case	104:		// Thor death explosion
		case	106:		// Thor death explosion
		{
			dpl_LoadObject("flamebig.bgf", dpl_load_normal);
			dpl_LoadObject("thrdbr.bgf"	, dpl_load_normal);
			dpl_LoadObject("ldbr.bgf"		, dpl_load_normal);
			dpl_LoadObject("flamesml.bgf", dpl_load_normal);
//			DEBUG_STREAM << "explosion "<< script_select<<" cached\n";
			break;
		}
	}
}
//
//#############################################################################
// ExplosionScripts
// Contains several scripts for running various types of explosion effects.
//#############################################################################
//
void
	DPLRenderer::ExplosionScripts(
		Entity					*entity,				// The entity we are dealing with
		ResourceDescription*  ,//model_resource,	// Pointer to the video resource
		ViewFrom					 ,//view_type, 			// Type of reference (inside/outside...etc.)
		int						script_select)
{

	dpl_OBJECT
		*object_1;
	//
	// Construct a root renderable to hang the explosion effects on
	//
	RootRenderable *this_root =
		new RootRenderable(
			entity,							// Entity to attach the renderable to
			RootRenderable::Dynamic,	// How/when to execute the renderable
			NULL,								// object to hang on the DCS, may be a list later <NULL>
			dplMainZone,					// DPL Zone this stuff will live in (for culling)
			dpl_isect_mode_obj,			// type of intersections to do on this object
			NULL);							// intersection mask for the object
	Register_Object(this_root);
	//
	// Select which script to construct
	//
	switch(script_select)
	{
		case	0:		// Shock wave
		{
			//
			// Load object(s) we will be using for the explosions
			//
			object_1		 = dpl_LoadObject("shock.bgf", dpl_load_normal);
			//
			// Setup control variables and transforms we need
			//
			Vector3D			scaling_velocity_1(3.5, 3.5, 3.5);
			Vector3D			velocity_accel_1(0.0, 0.0, 0.0);
			LinearMatrix	explosion_1_offset(True);
			//
			// Create the scaling explosion renderables
			//
			ScalingExplosionRenderable  *explosion_1 =
				new ScalingExplosionRenderable(
					entity,											// Entity to attach the renderable to
					ScalingExplosionRenderable::Dynamic,	// How/when to execute the renderable
					object_1,											// This will be the scaling explosion object
					dplMainZone,									// DPL Zone this stuff will live in (for culling)
					dpl_isect_mode_obj,							// type of intersections to do on this object
					NULL,												// intersection mask for the object
					this_root->GetDCS(),							// the parent DCS we will be offsetting from
					&explosion_1_offset,								// offset matrix to be applied prior to joint DCS
					&scaling_velocity_1,      					// Effect control vector, Y is acceleration, X, Z are velocity
					&velocity_accel_1,
					0.0f,
					NULL);                     				// -.25 gravity is normal
			Register_Object(explosion_1);
			//
			// Create a sweep renderable to drive the material morph
			//
			SweepRenderable *sweep_1 =
				new SweepRenderable(
					entity,							// Entity to attach the renderable to
					SweepRenderable::Dynamic,	// How/when to execute the renderable
					0.5f,								// How long to take to sweep from 0 to 1
					1,									// number of times to cycle before stopping
					NULL);
			Register_Object(sweep_1);
			//
			// Lookup a texture that we can use for the morphing materials
			//
			int
				status;
			dpl_TEXTURE *effect_texture =
				dpl_LookupTexture ( "btfx:firesmoke1_scr_tex", dpl_lookup_normal, &status );
			if (effect_texture == NULL)
				DEBUG_STREAM<<"couldn't find texture btfx:firesmoke1_scr_tex for an effect\n";
			//
			// Setup a morph material renderable for each explosion shape
			//
			MorphMaterialRenderable *morph_material_1 =
				new MorphMaterialRenderable(
					entity,										// Entity to attach the renderable to
					MorphMaterialRenderable::Dynamic,	// How/when to execute the renderable
					1.0f,1.0f,1.0f,							// Material's ambient component
					1.0f,1.0f,1.0f,							// Material's emissive component
					1.0f,1.0f,1.0f,							// Material's diffuse component
					0.0f,0.0f,0.0f,0.0,						// Material's specular component
					4.0f,0.2f,-0.05f,							// Material's opacity
					effect_texture,							// Material's texture pointer
					0.0f,											// Material's Z dither value
					0,												// Material's Fog Imunity value
					0.5f,0.5f,0.5f,							// Material's ambient component
					0.5f,0.5f,0.5f,							// Material's emissive component
					0.5f,0.5f,0.5f,							// Material's diffuse component
					0.0f,0.0f,0.0f,0.0,						// Material's specular component
//					0.25f,0.25f,-0.05f,						// Material's opacity
					0.1f,0.1f,-0.05f,						// Material's opacity
					0.0f,											// Material's Z dither value
					sweep_1->GetSweepAttribute());
			Register_Object(morph_material_1);
			dpl_INSTANCE *explosion_1_instance = dpl_GetDCSInstance(explosion_1->GetDCS(), 1);
			if(!explosion_1_instance)
				Fail("explosion_1_instance came back null\n");
			dpl_SetInstanceFrontMaterial(explosion_1_instance,	morph_material_1->GetMaterial());
			dpl_FlushInstance(explosion_1_instance);
			break;
		}
		case	1:		// Big explosion
		{
			//
			// Load object(s) we will be using for the explosions
			//
			object_1		 = dpl_LoadObject("exp.bgf", dpl_load_normal);
			//
			// Setup control variables and transforms we need
			//
			Vector3D			scaling_velocity_1(0.15, 0.15, 0.15);
			Vector3D			scaling_velocity_2(0.18, 0.22, 0.18);
			Vector3D			velocity_accel_1(0.0, 0.0, 0.0);
			Vector3D			velocity_accel_2(0.0, 0.0, 0.0);
			LinearMatrix	explosion_1_offset(True);
			LinearMatrix	explosion_2_offset(True);
			Point3D			explosion_2_translate(0.0f, 0.0f, 0.0f);
			EulerAngles		explosion_2_rotate(0.0f, 1.0f, 0.0f);
			explosion_2_offset = explosion_2_rotate;
         explosion_2_offset = explosion_2_translate;
			//
			// Create the scaling explosion renderables
			//
			ScalingExplosionRenderable  *explosion_1 =
				new ScalingExplosionRenderable(
					entity,											// Entity to attach the renderable to
					ScalingExplosionRenderable::Dynamic,	// How/when to execute the renderable
					object_1,											// This will be the scaling explosion object
					dplMainZone,									// DPL Zone this stuff will live in (for culling)
					dpl_isect_mode_obj,							// type of intersections to do on this object
					NULL,												// intersection mask for the object
					this_root->GetDCS(),							// the parent DCS we will be offsetting from
					&explosion_1_offset,								// offset matrix to be applied prior to joint DCS
					&scaling_velocity_1,      					// Effect control vector, Y is acceleration, X, Z are velocity
					&velocity_accel_1,
					0.0f,
					NULL);                     				// -.25 gravity is normal
			Register_Object(explosion_1);
			ScalingExplosionRenderable  *explosion_2 =
				new ScalingExplosionRenderable(
					entity,											// Entity to attach the renderable to
					ScalingExplosionRenderable::Dynamic,	// How/when to execute the renderable
					object_1,											// This will be the scaling explosion object
					dplMainZone,									// DPL Zone this stuff will live in (for culling)
					dpl_isect_mode_obj,							// type of intersections to do on this object
					NULL,												// intersection mask for the object
					this_root->GetDCS(),							// the parent DCS we will be offsetting from
					&explosion_2_offset,								// offset matrix to be applied prior to joint DCS
					&scaling_velocity_2,      					// Effect control vector, Y is acceleration, X, Z are velocity
					&velocity_accel_2,
					0.0f,
					NULL);
			Register_Object(explosion_2);
			//
			// Create a sweep renderable to drive the material morph
			//
			SweepRenderable *sweep_1 =
				new SweepRenderable(
					entity,							// Entity to attach the renderable to
					SweepRenderable::Dynamic,	// How/when to execute the renderable
					3.0f,
					1,									// number of times to cycle before stopping
					NULL);							// How long to take to sweep from 0 to 1
			Register_Object(sweep_1);
			SweepRenderable *sweep_2 =
				new SweepRenderable(
					entity,							// Entity to attach the renderable to
					SweepRenderable::Dynamic,	// How/when to execute the renderable
					2.0f,
					1,									// number of times to cycle before stopping
					NULL);							// How long to take to sweep from 0 to 1
			Register_Object(sweep_2);
			//
			// Lookup a texture that we can use for the morphing materials
			//
			int
				status;
			dpl_TEXTURE *effect_texture =
				dpl_LookupTexture ( "btfx:firesmoke1_scr_tex", dpl_lookup_normal, &status );
			if (effect_texture == NULL)
				DEBUG_STREAM<<"couldn't find texture btfx:firesmoke1_scr_tex for an effect\n";
			//
			// Setup a morph material renderable for each explosion shape
			//
			#if 0
					0.9f,0.0f,0.0f,							// Material's ambient component
					1.0f,1.0f,0.0f,							// Material's emissive component
					1.0f,0.7f,0.2f,							// Material's diffuse component
					0.0f,0.0f,0.0f,0.0,						// Material's specular component
					8.0f,0.2f,-0.05f,							// Material's opacity
			#endif
			MorphMaterialRenderable *morph_material_1 =
				new MorphMaterialRenderable(
					entity,										// Entity to attach the renderable to
					MorphMaterialRenderable::Dynamic,	// How/when to execute the renderable
					0.9f,0.0f,0.0f,							// Material's ambient component
					1.0f,0.2f,0.0f,							// Material's emissive component
					1.0f,0.2f,0.0f,							// Material's diffuse component
					0.0f,0.0f,0.0f,0.0,						// Material's specular component
					10.0f,0.6f,-0.05f,							// Material's opacity
					effect_texture,							// Material's texture pointer
					0.0f,											// Material's Z dither value
					0,												// Material's Fog Imunity value
					0.3f,0.0f,0.0f,							// Material's ambient component
					0.5f,0.0f,0.0f,							// Material's emissive component
					0.2f,0.0f,0.0f,							// Material's diffuse component
					0.0f,0.0f,0.0f,0.0,						// Material's specular component
					0.25f,0.25f,-0.05f,							// Material's opacity
					15.0f,											// Material's Z dither value
					sweep_1->GetSweepAttribute());
			Register_Object(morph_material_1);
			MorphMaterialRenderable *morph_material_2 =
				new MorphMaterialRenderable(
					entity,										// Entity to attach the renderable to
					MorphMaterialRenderable::Dynamic,	// How/when to execute the renderable
					0.9f,0.0f,0.0f,							// Material's ambient component
					1.0f,0.6f,0.3f,							// Material's emissive component
					1.0f,0.4f,0.2f,							// Material's diffuse component
					0.0f,0.0f,0.0f,0.0,						// Material's specular component
					8.0f,0.2f,-0.05f,							// Material's opacity
					effect_texture,							// Material's texture pointer
					0.0f,											// Material's Z dither value
					0,												// Material's Fog Imunity value
					0.1f,0.0f,0.0f,							// Material's ambient component
					0.0f,0.0f,0.0f,							// Material's emissive component
					0.1f,0.0f,0.0f,							// Material's diffuse component
					0.0f,0.0f,0.0f,0.0,						// Material's specular component
					0.25f,0.25f,-0.05f,						// Material's opacity
					15.0f,											// Material's Z dither value
					sweep_2->GetSweepAttribute());
			Register_Object(morph_material_2);
			//
			// Override the materials in the two explosion shapes using these morph materials
			//
			dpl_INSTANCE *explosion_1_instance = dpl_GetDCSInstance(explosion_1->GetDCS(), 1);
			dpl_INSTANCE *explosion_2_instance = dpl_GetDCSInstance(explosion_2->GetDCS(), 1);

			if(!explosion_1_instance)
				Fail("explosion_1_instance came back null\n");
			if(!explosion_2_instance)
				Fail("explosion_2_instance came back null\n");

			dpl_SetInstanceFrontMaterial(explosion_1_instance,	morph_material_1->GetMaterial());
			dpl_SetInstanceFrontMaterial(explosion_2_instance,	morph_material_2->GetMaterial());

			dpl_FlushInstance(explosion_1_instance);
			dpl_FlushInstance(explosion_2_instance);
			break;
		}
		case	2:		// Big explosion
		{
			//
			// Load object(s) we will be using for the explosions
			//
			object_1		 = dpl_LoadObject("exp.bgf", dpl_load_normal);
			//
			// Setup control variables and transforms we need
			//
			Vector3D			scaling_velocity_1(1.0, 0.2, 1.0);
			Vector3D			scaling_velocity_2(0.75, 0.75, 0.75);
			Vector3D			velocity_accel_1(-0.00725, -0.004, -0.00725);
//			Vector3D			velocity_accel_1(-0.00925, -0.00925, -0.00925);
			Vector3D			velocity_accel_2(-0.01388, -0.01388, -0.01388);
			LinearMatrix	explosion_1_offset(True);
			LinearMatrix	explosion_2_offset(True);
			Point3D			explosion_2_translate(0.0f, 0.0f, 0.0f);
			EulerAngles		explosion_2_rotate(0.0f, 1.0f, 0.0f);
			explosion_2_offset = explosion_2_rotate;
         explosion_2_offset = explosion_2_translate;
			//
			// Create the scaling explosion renderables
			//
			ScalingExplosionRenderable  *explosion_1 =
				new ScalingExplosionRenderable(
					entity,											// Entity to attach the renderable to
					ScalingExplosionRenderable::Dynamic,	// How/when to execute the renderable
					object_1,											// This will be the scaling explosion object
					dplMainZone,									// DPL Zone this stuff will live in (for culling)
					dpl_isect_mode_obj,							// type of intersections to do on this object
					NULL,												// intersection mask for the object
					this_root->GetDCS(),							// the parent DCS we will be offsetting from
					&explosion_1_offset,								// offset matrix to be applied prior to joint DCS
					&scaling_velocity_1,      					// Effect control vector, Y is acceleration, X, Z are velocity
					&velocity_accel_1,
					0.0f,
					NULL);                     				// -.25 gravity is normal
			Register_Object(explosion_1);
			ScalingExplosionRenderable  *explosion_2 =
				new ScalingExplosionRenderable(
					entity,											// Entity to attach the renderable to
					ScalingExplosionRenderable::Dynamic,	// How/when to execute the renderable
					object_1,											// This will be the scaling explosion object
					dplMainZone,									// DPL Zone this stuff will live in (for culling)
					dpl_isect_mode_obj,							// type of intersections to do on this object
					NULL,												// intersection mask for the object
					this_root->GetDCS(),							// the parent DCS we will be offsetting from
					&explosion_2_offset,								// offset matrix to be applied prior to joint DCS
					&scaling_velocity_2,      					// Effect control vector, Y is acceleration, X, Z are velocity
					&velocity_accel_2,
					0.0f,
					NULL);
			Register_Object(explosion_2);
			//
			// Create a sweep renderable to drive the material morph
			//
			SweepRenderable *sweep_1 =
				new SweepRenderable(
					entity,							// Entity to attach the renderable to
					SweepRenderable::Dynamic,	// How/when to execute the renderable
					3.0f,                      // How long to take to sweep from 0 to 1
					1,									// number of times to cycle before stopping
					NULL);
			Register_Object(sweep_1);
			SweepRenderable *sweep_2 =
				new SweepRenderable(
					entity,							// Entity to attach the renderable to
					SweepRenderable::Dynamic,	// How/when to execute the renderable
					2.0f,								// How long to take to sweep from 0 to 1
					1,									// number of times to cycle before stopping
					NULL);
			Register_Object(sweep_2);
			//
			// Lookup a texture that we can use for the morphing materials
			//
			int
				status;
			dpl_TEXTURE *effect_texture =
				dpl_LookupTexture ( "btfx:firesmoke1_scr_tex", dpl_lookup_normal, &status );
			if (effect_texture == NULL)
				DEBUG_STREAM<<"couldn't find texture btfx:firesmoke1_scr_tex for an effect\n";
			//
			// Setup a morph material renderable for each explosion shape
			//
			#if 0
					0.9f,0.0f,0.0f,							// Material's ambient component
					1.0f,1.0f,0.0f,							// Material's emissive component
					1.0f,0.7f,0.2f,							// Material's diffuse component
					0.0f,0.0f,0.0f,0.0,						// Material's specular component
					8.0f,0.2f,-0.05f,							// Material's opacity
			#endif
			MorphMaterialRenderable *morph_material_1 =
				new MorphMaterialRenderable(
					entity,										// Entity to attach the renderable to
					MorphMaterialRenderable::Dynamic,	// How/when to execute the renderable
					0.9f,0.0f,0.0f,							// Material's ambient component
					1.0f,0.2f,0.0f,							// Material's emissive component
					1.0f,0.2f,0.0f,							// Material's diffuse component
					0.0f,0.0f,0.0f,0.0,						// Material's specular component
					8.0f,0.2f,-0.05f,							// Material's opacity
					effect_texture,							// Material's texture pointer
					0.0f,											// Material's Z dither value
					0,												// Material's Fog Imunity value
					0.3f,0.0f,0.0f,							// Material's ambient component
					0.5f,0.0f,0.0f,							// Material's emissive component
					0.2f,0.0f,0.0f,							// Material's diffuse component
					0.0f,0.0f,0.0f,0.0,						// Material's specular component
					0.25f,0.25f,-0.05f,							// Material's opacity
					15.0f,											// Material's Z dither value
					sweep_1->GetSweepAttribute());
			Register_Object(morph_material_1);
			MorphMaterialRenderable *morph_material_2 =
				new MorphMaterialRenderable(
					entity,										// Entity to attach the renderable to
					MorphMaterialRenderable::Dynamic,	// How/when to execute the renderable
					0.9f,0.0f,0.0f,							// Material's ambient component
					1.0f,0.6f,0.4f,							// Material's emissive component
					1.0f,0.3f,0.2f,							// Material's diffuse component
					0.0f,0.0f,0.0f,0.0,						// Material's specular component
					8.0f,0.2f,-0.05f,							// Material's opacity
					effect_texture,							// Material's texture pointer
					0.0f,											// Material's Z dither value
					0,												// Material's Fog Imunity value
					0.1f,0.0f,0.0f,							// Material's ambient component
					0.0f,0.0f,0.0f,							// Material's emissive component
					0.0f,0.0f,0.0f,							// Material's diffuse component
					0.0f,0.0f,0.0f,0.0,						// Material's specular component
					0.25f,0.25f,-0.05f,						// Material's opacity
					15.0f,											// Material's Z dither value
					sweep_2->GetSweepAttribute());
			Register_Object(morph_material_2);
			//
			// Override the materials in the two explosion shapes using these morph materials
			//
			dpl_INSTANCE *explosion_1_instance = dpl_GetDCSInstance(explosion_1->GetDCS(), 1);
			dpl_INSTANCE *explosion_2_instance = dpl_GetDCSInstance(explosion_2->GetDCS(), 1);

			if(!explosion_1_instance)
				Fail("explosion_1_instance came back null\n");
			if(!explosion_2_instance)
				Fail("explosion_2_instance came back null\n");

			dpl_SetInstanceFrontMaterial(explosion_1_instance,	morph_material_1->GetMaterial());
			dpl_SetInstanceFrontMaterial(explosion_2_instance,	morph_material_2->GetMaterial());

			dpl_FlushInstance(explosion_1_instance);
			dpl_FlushInstance(explosion_2_instance);
			break;
		}
		case	3:		// Sparks
		{
			//
			// Load object(s) we will be using for the explosions
			//
			object_1		 = dpl_LoadObject("spk1.bgf", dpl_load_normal);
			//
			// Setup control variables and transforms we need
			//
			Vector3D			scaling_velocity_1(0.1, 0.1, 0.1);
			Vector3D			velocity_accel_1(0.0, 0.0, 0.0);
			LinearMatrix	explosion_1_offset(True);
			//
			// Create the scaling explosion renderables
			//
			ScalingExplosionRenderable  *explosion_1 =
				new ScalingExplosionRenderable(
					entity,											// Entity to attach the renderable to
					ScalingExplosionRenderable::Dynamic,	// How/when to execute the renderable
					object_1,											// This will be the scaling explosion object
					dplMainZone,									// DPL Zone this stuff will live in (for culling)
					dpl_isect_mode_obj,							// type of intersections to do on this object
					NULL,												// intersection mask for the object
					this_root->GetDCS(),							// the parent DCS we will be offsetting from
					&explosion_1_offset,								// offset matrix to be applied prior to joint DCS
					&scaling_velocity_1,      					// Effect control vector, Y is acceleration, X, Z are velocity
					&velocity_accel_1,
					-0.15f,
					NULL);                     				// -.25 gravity is normal
			Register_Object(explosion_1);
			//
			// Create a sweep renderable to drive the material morph
			//
			SweepRenderable *sweep_1 =
				new SweepRenderable(
					entity,							// Entity to attach the renderable to
					SweepRenderable::Dynamic,	// How/when to execute the renderable
					0.5f,								// How long to take to sweep from 0 to 1
					1,									// number of times to cycle before stopping
					NULL);
			Register_Object(sweep_1);
			//
			// Lookup a texture that we can use for the morphing materials
			//
			int
				status;
			dpl_TEXTURE *effect_texture =
				dpl_LookupTexture ( "btfx:firesmoke1_scr_tex", dpl_lookup_normal, &status );
			if (effect_texture == NULL)
				DEBUG_STREAM<<"couldn't find texture btfx:firesmoke1_scr_tex for an effect\n";
			//
			// Setup a morph material renderable for each explosion shape
			//
			MorphMaterialRenderable *morph_material_1 =
				new MorphMaterialRenderable(
					entity,										// Entity to attach the renderable to
					MorphMaterialRenderable::Dynamic,	// How/when to execute the renderable
					1.0f,1.0f,0.0f,							// Material's ambient component
					1.0f,1.0f,0.0f,							// Material's emissive component
					1.0f,1.0f,0.0f,							// Material's diffuse component
					0.0f,0.0f,0.0f,0.0,						// Material's specular component
					1.0f,1.0f,1.0f,							// Material's opacity
					effect_texture,							// Material's texture pointer
					0.0f,											// Material's Z dither value
					2,												// Material's Fog Imunity value
					0.9f,0.0f,0.0f,							// Material's ambient component
					0.9f,0.0f,0.0f,							// Material's emissive component
					0.9f,0.0f,0.0f,							// Material's diffuse component
					0.0f,0.0f,0.0f,0.0,						// Material's specular component
					1.0f,1.0f,1.0f,							// Material's opacity
					0.0f,											// Material's Z dither value
					sweep_1->GetSweepAttribute());
			Register_Object(morph_material_1);
			dpl_INSTANCE *explosion_1_instance = dpl_GetDCSInstance(explosion_1->GetDCS(), 1);
			if(!explosion_1_instance)
				Fail("explosion_1_instance came back null\n");
			dpl_SetInstanceFrontMaterial(explosion_1_instance,	morph_material_1->GetMaterial());
			dpl_FlushInstance(explosion_1_instance);
			break;
		}
		case 4:	// A huge mech explodes (yawn)
		{
			Point3D			null_offset(0.0f, 0.0f, 0.0f);
			LinearMatrix	null_offset_matrix(True);
			Point3D			local_height(0.0f, 6.2f, 0.0f);	// height of Thor
			LinearMatrix	local_offset(True);
			local_offset = local_height;
			DPLStaticChildRenderable *local_root =
				new DPLStaticChildRenderable(
					entity,
					dplMainZone,
					NULL,
					dpl_isect_mode_obj,
					NULL,
					local_offset,
					this_root->GetDCS());
			Register_Object(local_root);
			//
			// Load morph sources we need for the effect
			//
			dpl_OBJECT	*thor_debris_object	= dpl_LoadObject("thrdbr.bgf", dpl_load_normal);
			dpl_OBJECT	*large_debris_object	= dpl_LoadObject("ldbr.bgf", dpl_load_normal);
			dpl_OBJECT	*small_flames_object	= dpl_LoadObject("flamesml.bgf", dpl_load_normal);
			//
			// Load morph targets we need for the effect
			//
			//------------------------------------------------
			// Renderables to handle the chunks and fireballs
			//------------------------------------------------
#if 0
			OneShotDelayRenderable *fireball_delay =
				new OneShotDelayRenderable(
					entity,									// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,	// How/when to execute the renderable
					0.25f);									// How long to wait before raising the trigger
			Register_Object(fireball_delay);
			Point3D my_offset(0.0, 4.0, 0.0);
			#if DEBUG_LEVEL > 0
			DPLPSFXRenderable*		initial_boom	=
			#endif
				new DPLPSFXRenderable(
					entity,								// Entity to attach the renderable to
					DPLPSFXRenderable::Dynamic,	// How/when to execute the renderable
					fireball_delay->GetTriggerAttribute(),							// address containing the trigger
					myPSFXDescriptons[7],			// pointer to the PFX description
					this_root->GetDCS(),				// DCS the effect is relative to (may be NULL)
					&my_offset);						// Offset (or world coordinants if DCS is NULL)
			Register_Object(initial_boom);
			#if DEBUG_LEVEL > 0
			DPLEffectRenderable *chunks_effect	=
			#endif
				new DPLEffectRenderable(
					entity,											// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,			// How/when to execute the renderable
					fireball_delay->GetTriggerAttribute(),	// address containing the trigger
					3,	// Chunks									// DPL effect number to trigger
					local_root->GetDCS(),						// DCS the effect is relative to (may be NULL)
					&null_offset); 	   						// Offset (or world coordinants if DCS is NULL)
			Register_Object(chunks_effect);
			Point3D	fireball_offset(1.0f, 5.0f, 1.0f);
			#if DEBUG_LEVEL > 0
			DPLEffectRenderable *fireball_effect	=
			#endif
				new DPLEffectRenderable(
					entity,											// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,			// How/when to execute the renderable
					fireball_delay->GetTriggerAttribute(),	// address containing the trigger
					15,	// Fireball								// DPL effect number to trigger
					local_root->GetDCS(),						// DCS the effect is relative to (may be NULL)
					&fireball_offset); 	   						// Offset (or world coordinants if DCS is NULL)
			Register_Object(fireball_effect);
#endif
			OneShotDelayRenderable *fireball_delay_2 =
				new OneShotDelayRenderable(
					entity,									// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,	// How/when to execute the renderable
					0.55f);									// How long to wait before raising the trigger
			Register_Object(fireball_delay_2);
			Point3D	fireball_offset_2(-1.0f, 5.0f, -1.0f);
			#if DEBUG_LEVEL > 0
			DPLEffectRenderable *fireball_effect_2	=
			#endif
				new DPLEffectRenderable(
					entity,											// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,			// How/when to execute the renderable
					fireball_delay_2->GetTriggerAttribute(),	// address containing the trigger
					15,	// Fireball								// DPL effect number to trigger
					local_root->GetDCS(),						// DCS the effect is relative to (may be NULL)
					&fireball_offset_2); 						// Offset (or world coordinants if DCS is NULL)
			Register_Object(fireball_effect_2);
			//-----------------------------------------------
			// Renderables to handle static debris
			//-----------------------------------------------
			OneShotDelayRenderable	*static_debris_delay =
				new OneShotDelayRenderable(
					entity,												// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,				// How/when to execute the renderable
					0.25f);												// How long to wait before raising the trigger
			Register_Object(static_debris_delay);
			DPLStaticChildRenderable *mech_debris =
				new DPLStaticChildRenderable(
					entity,
					dplMainZone,
					thor_debris_object,
					dpl_isect_mode_obj,
					NULL,
					null_offset_matrix,
					this_root->GetDCS());
			Register_Object(mech_debris);
			#if DEBUG_LEVEL > 0
			MakeDCSFall	*mech_debris_fall =
			#endif
				new MakeDCSFall(
					entity,												// Entity to attach the renderable to
					MakeDCSFall::Dynamic,							// How/when to execute the renderable
					mech_debris->GetDCS(),							// the DCS to control
					-0.025f,												// Gravity in meters/sec squared
					static_debris_delay->GetTriggerAttribute());		// true if the instance is on, false if off
			Register_Object(mech_debris_fall);
			// Find the instance with the static_thr in it and hook up the instance switch
			dpl_INSTANCE *mech_debris_instance = dpl_GetDCSInstance(mech_debris->GetDCS(), 1);
			#if DEBUG_LEVEL > 0
			InstanceSwitchRenderable	*mech_debris_instance_switch =
			#endif
				new InstanceSwitchRenderable(
					entity,									 		// Entity to attach the renderable to
					InstanceSwitchRenderable::Dynamic,		// How/when to execute the renderable
					mech_debris_instance,				 		// the instance to control
					True,										 		// Instance is on when trigger is...
					static_debris_delay->GetTriggerAttribute());
			Register_Object(mech_debris_instance_switch);

			DPLStaticChildRenderable *large_debris =
				new DPLStaticChildRenderable(
					entity,
					dplMainZone,
					large_debris_object,
					dpl_isect_mode_obj,
					NULL,
					null_offset_matrix,
					mech_debris->GetDCS());
			Register_Object(large_debris);
			// Find the instance with the static_thr in it and hook up the instance switch
			dpl_INSTANCE *large_debris_instance = dpl_GetDCSInstance(large_debris->GetDCS(), 1);
			#if DEBUG_LEVEL > 0
			InstanceSwitchRenderable	*large_debris_instance_switch =
         #endif
				new InstanceSwitchRenderable(
					entity,									 		// Entity to attach the renderable to
					InstanceSwitchRenderable::Dynamic,		// How/when to execute the renderable
					large_debris_instance,				 		// the instance to control
					True,										 		// Instance is on when trigger is...
					static_debris_delay->GetTriggerAttribute());
			Register_Object(large_debris_instance_switch);
			//-----------------------------------------------
			// Renderables to handle fires
			//-----------------------------------------------
			OneShotDelayRenderable	*fires_delay =
				new OneShotDelayRenderable(
					entity,												// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,				// How/when to execute the renderable
					0.25f);												// How long to wait before raising the trigger
			Register_Object(fires_delay);
			DPLStaticChildRenderable *fires =
				new DPLStaticChildRenderable(
					entity,
					dplMainZone,
					NULL,
					dpl_isect_mode_obj,
					NULL,
					null_offset_matrix,
					this_root->GetDCS());
			Register_Object(fires);
			#if DEBUG_LEVEL > 0
			MakeDCSFall	*fires_fall =
			#endif
				new MakeDCSFall(
					entity,												// Entity to attach the renderable to
					MakeDCSFall::Dynamic,							// How/when to execute the renderable
					fires->GetDCS(),							// the DCS to control
					-0.01f,												// Gravity in meters/sec squared
					fires_delay->GetTriggerAttribute());		// true if the instance is on, false if off
			Register_Object(fires_fall);
			DPLStaticChildRenderable *small_flames =
				new DPLStaticChildRenderable(
					entity,
					dplMainZone,
					small_flames_object,
					dpl_isect_mode_obj,
					NULL,
					null_offset_matrix,
					fires->GetDCS());
			Register_Object(small_flames);
			// Find the instance with the static_thr in it and hook up the instance switch
			dpl_INSTANCE *small_flames_instance = dpl_GetDCSInstance(small_flames->GetDCS(), 1);
			#if DEBUG_LEVEL > 0
			InstanceSwitchRenderable	*small_flames_instance_switch =
			#endif
				new InstanceSwitchRenderable(
					entity,									 		// Entity to attach the renderable to
					InstanceSwitchRenderable::Dynamic,		// How/when to execute the renderable
					small_flames_instance,				 		// the instance to control
					True,										 		// Instance is on when trigger is...
					fires_delay->GetTriggerAttribute());
			Register_Object(small_flames_instance_switch);

			dpl_OBJECT	*big_flames_object	= dpl_LoadObject("flamebig.bgf", dpl_load_normal);
			DPLStaticChildRenderable *big_flames =
				new DPLStaticChildRenderable(
					entity,
					dplMainZone,
					big_flames_object,
					dpl_isect_mode_obj,
					NULL,
					null_offset_matrix,
					fires->GetDCS());
			Register_Object(big_flames);
			// Make big flames object billboard along y-axis
			dpl_SetDCSReorientAxes(big_flames->GetDCS(), dpl_reorient_axes_y);
			dpl_FlushDCS(big_flames->GetDCS());
			// Find the instance with the static_thr in it and hook up the instance switch
			dpl_INSTANCE *big_flames_instance = dpl_GetDCSInstance(big_flames->GetDCS(), 1);
			#if DEBUG_LEVEL > 0
			InstanceSwitchRenderable	*big_flames_instance_switch =
			#endif
				new InstanceSwitchRenderable(
					entity,									 		// Entity to attach the renderable to
					InstanceSwitchRenderable::Dynamic,		// How/when to execute the renderable
					big_flames_instance,					 		// the instance to control
					True,										 		// Instance is on when trigger is...
					fires_delay->GetTriggerAttribute());
			Register_Object(big_flames_instance_switch);
			//
			// These renderables create the rising smoke column using a pfx effect.
         //
#if 0
			Point3D my_offset2(0.0, 5.0, 0.0);
			#if DEBUG_LEVEL > 0
			OnePSFXRenderable	*this_effect=
			#endif
				new OnePSFXRenderable(
					entity,								// Entity to attach the renderable to
					OnePSFXRenderable::Static,		// How/when to execute the renderable
					myPSFXDescriptons[1],			// name of file with the PFX description in it
					this_root->GetDCS(),				// DCS the effect is relative to (may be NULL)
					&my_offset2);						// Offset (or world coordinants if DCS is NULL)
			Register_Object(this_effect);
#endif
			break;
		}
		//----------------------------------------
		case 5:	// Sfx #105 - Delayed ground hit
		//----------------------------------------
		{
			//
			// Create the delayed ground hit renderables
			//
			Point3D	null_offset(0.0f, 0.0f, 0.0f);
			OneShotDelayRenderable *ground_hit_delay =
				new OneShotDelayRenderable(
					entity,									// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,	// How/when to execute the renderable
					0.15f);									// How long to wait before raising the trigger
			Register_Object(ground_hit_delay);
			#if DEBUG_LEVEL > 0
			DPLEffectRenderable *ground_hit_effect	=
         #endif
				new DPLEffectRenderable(
					entity,											// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,			// How/when to execute the renderable
					ground_hit_delay->GetTriggerAttribute(),	// address containing the trigger
					12,	// GroundHit							// DPL effect number to trigger
					this_root->GetDCS(),							// DCS the effect is relative to (may be NULL)
					&null_offset); 		 						// Offset (or world coordinants if DCS is NULL)
			Register_Object(ground_hit_effect);
			break;
		}
		//------------------------------------------
		case 6:	// Sfx #106 - Thor death explosion
		//------------------------------------------
		{
			#if 0
			int status;		// used with dpl calls
			Point3D			null_offset(0.0f, 0.0f, 0.0f);
			LinearMatrix	null_offset_matrix(True);
			Point3D			local_height(0.0f, 6.2f, 0.0f);	// height of Thor
			LinearMatrix	local_offset(True);
			local_offset = local_height;
			DPLStaticChildRenderable *local_root =
				new DPLStaticChildRenderable(
					entity,
					dplMainZone,
					NULL,
					dpl_isect_mode_obj,
					NULL,
					local_offset,
					this_root->GetDCS());
			Register_Object(local_root);
#if 0
			//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
			//
			// Show static thor (TEMPORARY for testing)
			//
			dpl_OBJECT *static_thr_object = dpl_LoadObject("thr.bgf", dpl_load_normal);
			DPLStaticChildRenderable *static_thr =
				new DPLStaticChildRenderable(
					entity,
					dplMainZone,
					static_thr_object,
					dpl_isect_mode_obj,
					NULL,
					null_offset_matrix,
					local_root->GetDCS());
			Register_Object(static_thr);
			// This defines how long the static_thr stays visible
			OneShotDelayRenderable	*static_thr_delay =
				new OneShotDelayRenderable(
					entity,										// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,		// How/when to execute the renderable
					0.5f);										// How long to wait before raising the trigger
			// Find the instance with the static_thr in it and hook up the instance switch
			dpl_INSTANCE *static_thr_instance = dpl_GetDCSInstance(static_thr->GetDCS(), 1);
			InstanceSwitchRenderable	*static_thr_instance_switch =
				new InstanceSwitchRenderable(
					entity,									 		// Entity to attach the renderable to
					InstanceSwitchRenderable::Dynamic,		// How/when to execute the renderable
					static_thr_instance,					 		// the instance to control
					False,									 		// Instance is on when trigger is...
					static_thr_delay->GetTriggerAttribute());
			//
			//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#endif
			//
			// Load objects we will be using for the explosion effect
			//
			DPLObjectWrapper *flash_object_wrapper =
				new DPLObjectWrapper(
					entity,						// Entity to attach the renderable to
					"exdisk_A.bgf",					// Name of the DPL object to load into the wrapper
					dpl_load_nocache);		// Type of loading to perform on this object
			Register_Object(flash_object_wrapper);
			dpl_OBJECT	*flash_object_a	=	flash_object_wrapper->GetDPLObject();
			dpl_OBJECT	*flash_object_b	= dpl_LoadObject("exdisk_B.bgf", dpl_load_normal);
			dpl_OBJECT	*flash_object_c	= dpl_LoadObject("exdisk_C.bgf", dpl_load_normal);
			DPLObjectWrapper *debris_object_wrapper =
				new DPLObjectWrapper(
					entity,						// Entity to attach the renderable to
					"thrtor_A.bgf",					// Name of the DPL object to load into the wrapper
					dpl_load_nocache);		// Type of loading to perform on this object
			Register_Object(debris_object_wrapper);
			dpl_OBJECT	*morph_debris_a	=	debris_object_wrapper->GetDPLObject();
			dpl_OBJECT	*morph_debris_b	= dpl_LoadObject("thrtor_B.bgf", dpl_load_normal);
			dpl_OBJECT	*morph_debris_c	= dpl_LoadObject("thrtor_C.bgf", dpl_load_normal);
			DPLObjectWrapper *hips_object_wrapper =
				new DPLObjectWrapper(
					entity,						// Entity to attach the renderable to
					"thrhip_A.bgf",					// Name of the DPL object to load into the wrapper
					dpl_load_nocache);		// Type of loading to perform on this object
			Register_Object(hips_object_wrapper);
			dpl_OBJECT	*morph_hips_a	=	hips_object_wrapper->GetDPLObject();
			dpl_OBJECT	*morph_hips_b		= dpl_LoadObject("thrhip_B.bgf", dpl_load_normal);
			dpl_OBJECT	*morph_hips_c		= dpl_LoadObject("thrhip_C.bgf", dpl_load_normal);
			dpl_OBJECT	*thor_debris_object	= dpl_LoadObject("thrdbr.bgf", dpl_load_normal);
			dpl_OBJECT	*large_debris_object	= dpl_LoadObject("ldbr.bgf", dpl_load_normal);
			dpl_OBJECT	*small_flames_object	= dpl_LoadObject("flamesml.bgf", dpl_load_normal);
			dpl_OBJECT	*big_flames_object	= dpl_LoadObject("flamebig.bgf", dpl_load_normal);
			//----------------------------------------------
			// Renderables to perform morphing flash object
			//----------------------------------------------
			SweepRenderable *flash_morph_sweep =
				new SweepRenderable(
					entity,												// Entity to attach the renderable to
					SweepRenderable::Dynamic,						// How/when to execute the renderable
					1.0f,													// How long to take to sweep from 0 to 1
					1,														// number of times to cycle before stopping
					NULL,													// trigger
					-0.5f,												// Initial value
					2.0f);												// Final value
			ChildMorphRenderable	*flash_morph =
				new ChildMorphRenderable(
					entity,												// Entity to attach the renderable to
					ChildMorphRenderable::Dynamic,				// How/when to execute the renderable
					flash_object_a,									// destination
					flash_object_b,									// start object
					flash_object_c,									// end object
					flash_morph_sweep->GetSweepAttribute(),	// pointer to control variable
					dpl_morph_vertices | dpl_morph_colors, 	// Defines type of morph to do
					dplMainZone,										// DPL Zone this stuff will live in (for culling)
					dpl_isect_mode_obj,								// type of intersections to do on this object
					NULL,													// intersection mask for the object
					this_root->GetDCS());							// the parent DCS we will be offsetting from
			// Make flash object billboard along y-axis
			dpl_SetDCSReorientAxes(flash_morph->GetDCS(), dpl_reorient_axes_y);
			dpl_FlushDCS(flash_morph->GetDCS());
			// This defines how long the flash stays up
			OneShotDelayRenderable	*flash_delay =
				new OneShotDelayRenderable(
					entity,										// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,		// How/when to execute the renderable
					1.0f);										// How long to wait before raising the trigger
			Register_Object(flash_delay);
			// Find the instance with the flash in it and hook up the instance switch
			dpl_INSTANCE *flash_morph_instance = dpl_GetDCSInstance(flash_morph->GetDCS(), 1);
			InstanceSwitchRenderable	*flash_instance_switch =
				new InstanceSwitchRenderable(
					entity,									 		// Entity to attach the renderable to
					InstanceSwitchRenderable::Dynamic,		// How/when to execute the renderable
					flash_morph_instance,				 		// the instance to control
					False,									 		// Instance is on when trigger is...
					flash_delay->GetTriggerAttribute());
			//---------------------------------------------------
			// Create a material morph to fade flash object away
			//---------------------------------------------------
			OneShotDelayRenderable	*flash_fade_delay =
				new OneShotDelayRenderable(
					entity,												// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,				// How/when to execute the renderable
					0.5f);												// How long to wait before raising the trigger
			SweepRenderable *flash_fade_sweep =
				new SweepRenderable(
					entity,								// Entity to attach the renderable to
					SweepRenderable::Dynamic,		// How/when to execute the renderable
					0.4f, 								// How long to take to sweep from 0 to 1
					1,										// number of times to cycle before stopping
					flash_fade_delay->GetTriggerAttribute());		// trigger variable
			Register_Object(flash_fade_sweep);
			// Lookup texture for smoke column
			dpl_TEXTURE *flash_texture =
				dpl_LookupTexture("btfx:bexp9_tex", dpl_lookup_normal, &status);
			if (flash_texture == NULL)
				DEBUG_STREAM<<"couldn't find texture btfx:bexp9_tex for an effect\n";
			//
			MorphMaterialRenderable *flash_fade_material =
				new MorphMaterialRenderable(
					entity,										// Entity to attach the renderable to
					MorphMaterialRenderable::Dynamic,	// How/when to execute the renderable
					1.0f,1.0f,1.0f,							// Material's ambient component
					1.0f,1.0f,1.0f,							// Material's emissive component
					1.0f,1.0f,1.0f,							// Material's diffuse component
					0.0f,0.0f,0.0f,0.0f,						// Material's specular component
					1.0f,1.0f,1.0f,							// Material's opacity
					flash_texture,								// Material's texture pointer
					0.0f,											// Material's Z dither value
					1,												// Material's Fog Imunity value
					1.0f,1.0f,1.0f,							// Material's ambient component
					1.0f,1.0f,1.0f,							// Material's emissive component
					1.0f,1.0f,1.0f,							// Material's diffuse component
					0.0f,0.0f,0.0f,0.0f,						// Material's specular component
					0.0f,0.0f,0.0f,							// Material's opacity
					0.0f, 										// Material's Z dither value
					flash_fade_sweep->GetSweepAttribute());
			Register_Object(flash_fade_material);
//			dpl_INSTANCE *flash_morph_instance = dpl_GetDCSInstance(flash_morph->GetDCS(), 1);
			if (!flash_morph_instance)
				Fail("flash_morph_instance came back null\n");
			dpl_SetInstanceFrontMaterial(flash_morph_instance, flash_fade_material->GetMaterial());
			dpl_FlushInstance(flash_morph_instance);
			//------------------------------------------------
			// Renderables to handle the chunks and fireballs
			//------------------------------------------------
			OneShotDelayRenderable *fireball_delay =
				new OneShotDelayRenderable(
					entity,									// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,	// How/when to execute the renderable
					0.25f);									// How long to wait before raising the trigger
			Register_Object(fireball_delay);
			DPLEffectRenderable *chunks_effect	=
				new DPLEffectRenderable(
					entity,											// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,			// How/when to execute the renderable
					fireball_delay->GetTriggerAttribute(),	// address containing the trigger
					3,	// Chunks									// DPL effect number to trigger
					local_root->GetDCS(),						// DCS the effect is relative to (may be NULL)
					&null_offset); 	   						// Offset (or world coordinants if DCS is NULL)
			Register_Object(chunks_effect);
			Point3D	fireball_offset(1.0f, 5.0f, 1.0f);
			DPLEffectRenderable *fireball_effect	=
				new DPLEffectRenderable(
					entity,											// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,			// How/when to execute the renderable
					fireball_delay->GetTriggerAttribute(),	// address containing the trigger
					15,	// Fireball								// DPL effect number to trigger
					local_root->GetDCS(),						// DCS the effect is relative to (may be NULL)
					&fireball_offset); 	   						// Offset (or world coordinants if DCS is NULL)
			Register_Object(fireball_effect);
			OneShotDelayRenderable *fireball_delay_2 =
				new OneShotDelayRenderable(
					entity,									// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,	// How/when to execute the renderable
					0.55f);									// How long to wait before raising the trigger
			Register_Object(fireball_delay_2);
			Point3D	fireball_offset_2(-1.0f, 5.0f, -1.0f);
			DPLEffectRenderable *fireball_effect_2	=
				new DPLEffectRenderable(
					entity,											// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,			// How/when to execute the renderable
					fireball_delay_2->GetTriggerAttribute(),	// address containing the trigger
					15,	// Fireball								// DPL effect number to trigger
					local_root->GetDCS(),						// DCS the effect is relative to (may be NULL)
					&fireball_offset_2); 						// Offset (or world coordinants if DCS is NULL)
			Register_Object(fireball_effect_2);
			//------------------------------------------------------
			// Renderables to handle the torso debris explode morph
			//------------------------------------------------------
			OneShotDelayRenderable	*morph_debris_delay =
				new OneShotDelayRenderable(
					entity,												// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,				// How/when to execute the renderable
					0.25f,												// How long to wait before raising the trigger
					4.25f);												// Duration of trigger
			SweepRenderable *debris_morph_sweep =
				new SweepRenderable(
					entity,												// Entity to attach the renderable to
					SweepRenderable::Dynamic,						// How/when to execute the renderable
					4.0f,													// How long to take to sweep from 0 to 1
					1,														// number of times to cycle before stopping
					morph_debris_delay->GetTriggerAttribute(),		// trigger variable
					0.0f,													// initial value of sweep
					9.0f,													// final value of sweep
					SweepRenderable::Y_SQR_X);						// function to apply
			ChildMorphRenderable	*debris_morph	=
				new ChildMorphRenderable(
					entity,												// Entity to attach the renderable to
					ChildMorphRenderable::Dynamic,				// How/when to execute the renderable
					morph_debris_a,									// destination
					morph_debris_b,									// start object
					morph_debris_c,									// end object
					debris_morph_sweep->GetSweepAttribute(),	// pointer to control variable
					dpl_morph_vertices, 								// Defines type of morph to do
					dplMainZone,										// DPL Zone this stuff will live in (for culling)
					dpl_isect_mode_obj,								// type of intersections to do on this object
					NULL,													// intersection mask for the object
					local_root->GetDCS());							// the parent DCS we will be offsetting from
			MakeDCSFall	*debris_make_fall =
				new MakeDCSFall(
					entity,												// Entity to attach the renderable to
					MakeDCSFall::Dynamic,							// How/when to execute the renderable
					debris_morph->GetDCS(),							// the DCS to control
					-9.81f,												// Gravity in meters/sec squared
					morph_debris_delay->GetTriggerAttribute());		// true if the instance is on, false if off
			// Find the instance with the debris in it and hook up the instance switch
			dpl_INSTANCE *debris_morph_instance = dpl_GetDCSInstance(debris_morph->GetDCS(), 1);
			InstanceSwitchRenderable	*debris_morph_instance_switch =
				new InstanceSwitchRenderable(
					entity,												// Entity to attach the renderable to
					InstanceSwitchRenderable::Dynamic,			// How/when to execute the renderable
					debris_morph_instance,							// the instance to control
					True,													// Instance is on when trigger is...
					morph_debris_delay->GetTriggerAttribute());
			//-----------------------------------------------------
			// Renderables to handle the hips debris explode morph
			//-----------------------------------------------------
			ChildMorphRenderable	*hips_morph	=
				new ChildMorphRenderable(
					entity,												// Entity to attach the renderable to
					ChildMorphRenderable::Dynamic,				// How/when to execute the renderable
					morph_hips_a,										// destination
					morph_hips_b,										// start object
					morph_hips_c,										// end object
					debris_morph_sweep->GetSweepAttribute(),	// pointer to control variable
					dpl_morph_vertices, 								// Defines type of morph to do
					dplMainZone,										// DPL Zone this stuff will live in (for culling)
					dpl_isect_mode_obj,								// type of intersections to do on this object
					NULL,													// intersection mask for the object
					local_root->GetDCS());							// the parent DCS we will be offsetting from
			MakeDCSFall	*hips_make_fall =
				new MakeDCSFall(
					entity,												// Entity to attach the renderable to
					MakeDCSFall::Dynamic,							// How/when to execute the renderable
					hips_morph->GetDCS(),							// the DCS to control
					-9.81f,												// Gravity in meters/sec squared
					morph_debris_delay->GetTriggerAttribute());		// true if the instance is on, false if off
			// Find the instance with the debris in it and hook up the instance switch
			dpl_INSTANCE *hips_morph_instance = dpl_GetDCSInstance(hips_morph->GetDCS(), 1);
			InstanceSwitchRenderable	*hips_morph_instance_switch =
				new InstanceSwitchRenderable(
					entity,												// Entity to attach the renderable to
					InstanceSwitchRenderable::Dynamic,			// How/when to execute the renderable
					hips_morph_instance,								// the instance to control
					True,													// Instance is on when trigger is...
					morph_debris_delay->GetTriggerAttribute());
			//-----------------------------------------------
			// Renderables to handle static debris and fires
			//-----------------------------------------------
			OneShotDelayRenderable	*static_debris_delay =
				new OneShotDelayRenderable(
					entity,												// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,				// How/when to execute the renderable
					0.25f);												// How long to wait before raising the trigger
			DPLStaticChildRenderable *mech_debris =
				new DPLStaticChildRenderable(
					entity,
					dplMainZone,
					thor_debris_object,
					dpl_isect_mode_obj,
					NULL,
					null_offset_matrix,
					this_root->GetDCS());
			Register_Object(mech_debris);
			MakeDCSFall	*mech_debris_fall =
				new MakeDCSFall(
					entity,												// Entity to attach the renderable to
					MakeDCSFall::Dynamic,							// How/when to execute the renderable
					mech_debris->GetDCS(),							// the DCS to control
					-0.01f,												// Gravity in meters/sec squared
					static_debris_delay->GetTriggerAttribute());		// true if the instance is on, false if off
			// Find the instance with the static_thr in it and hook up the instance switch
			dpl_INSTANCE *mech_debris_instance = dpl_GetDCSInstance(mech_debris->GetDCS(), 1);
			InstanceSwitchRenderable	*mech_debris_instance_switch =
				new InstanceSwitchRenderable(
					entity,									 		// Entity to attach the renderable to
					InstanceSwitchRenderable::Dynamic,		// How/when to execute the renderable
					mech_debris_instance,				 		// the instance to control
					True,										 		// Instance is on when trigger is...
					static_debris_delay->GetTriggerAttribute());

			DPLStaticChildRenderable *large_debris =
				new DPLStaticChildRenderable(
					entity,
					dplMainZone,
					large_debris_object,
					dpl_isect_mode_obj,
					NULL,
					null_offset_matrix,
					mech_debris->GetDCS());
			Register_Object(large_debris);
			// Find the instance with the static_thr in it and hook up the instance switch
			dpl_INSTANCE *large_debris_instance = dpl_GetDCSInstance(large_debris->GetDCS(), 1);
			InstanceSwitchRenderable	*large_debris_instance_switch =
				new InstanceSwitchRenderable(
					entity,									 		// Entity to attach the renderable to
					InstanceSwitchRenderable::Dynamic,		// How/when to execute the renderable
					large_debris_instance,				 		// the instance to control
					True,										 		// Instance is on when trigger is...
					static_debris_delay->GetTriggerAttribute());

			DPLStaticChildRenderable *small_flames =
				new DPLStaticChildRenderable(
					entity,
					dplMainZone,
					small_flames_object,
					dpl_isect_mode_obj,
					NULL,
					null_offset_matrix,
					mech_debris->GetDCS());
			Register_Object(small_flames);
			// Find the instance with the static_thr in it and hook up the instance switch
			dpl_INSTANCE *small_flames_instance = dpl_GetDCSInstance(small_flames->GetDCS(), 1);
			InstanceSwitchRenderable	*small_flames_instance_switch =
				new InstanceSwitchRenderable(
					entity,									 		// Entity to attach the renderable to
					InstanceSwitchRenderable::Dynamic,		// How/when to execute the renderable
					small_flames_instance,				 		// the instance to control
					True,										 		// Instance is on when trigger is...
					static_debris_delay->GetTriggerAttribute());

			DPLStaticChildRenderable *big_flames =
				new DPLStaticChildRenderable(
					entity,
					dplMainZone,
					big_flames_object,
					dpl_isect_mode_obj,
					NULL,
					null_offset_matrix,
					mech_debris->GetDCS());
			Register_Object(big_flames);
			// Make big flames object billboard along y-axis
			dpl_SetDCSReorientAxes(big_flames->GetDCS(), dpl_reorient_axes_y);
			dpl_FlushDCS(big_flames->GetDCS());
			// Find the instance with the static_thr in it and hook up the instance switch
			dpl_INSTANCE *big_flames_instance = dpl_GetDCSInstance(big_flames->GetDCS(), 1);
			InstanceSwitchRenderable	*big_flames_instance_switch =
				new InstanceSwitchRenderable(
					entity,									 		// Entity to attach the renderable to
					InstanceSwitchRenderable::Dynamic,		// How/when to execute the renderable
					big_flames_instance,					 		// the instance to control
					True,										 		// Instance is on when trigger is...
					static_debris_delay->GetTriggerAttribute());
			//-------------------------------------------------------------
			// Renderables to handle the rising smoke column (first shape)
			//-------------------------------------------------------------
#if 1
			OneShotDelayRenderable *first_one_shot =
				new OneShotDelayRenderable(
					entity,									// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,	// How/when to execute the renderable
					0.0f);									// How long to wait before raising the trigger
			Register_Object(first_one_shot);
			Point3D my_offset(0.0, 6.0, 0.0);
			#if DEBUG_LEVEL > 0
			DPLPSFXRenderable*		this_effect	=
         #endif
				new DPLPSFXRenderable(
					entity,								// Entity to attach the renderable to
					DPLPSFXRenderable::Dynamic,	// How/when to execute the renderable
					first_one_shot->GetTriggerAttribute(),							// address containing the trigger
					myPSFXDescriptons[1],			// pointer to the PFX description
					this_root->GetDCS(),				// DCS the effect is relative to (may be NULL)
					&my_offset);						// Offset (or world coordinants if DCS is NULL)
			Register_Object(this_effect);

// the below code will be removed in favor of PFX effects once they are tested
#else
//			OneShotDelayRenderable	*smoke_1_morph_delay =
//				new OneShotDelayRenderable(
//					entity,												// Entity to attach the renderable to
//					OneShotDelayRenderable::Dynamic,				// How/when to execute the renderable
//					0.25f);												// How long to wait before raising the trigger
			SweepRenderable *smoke_1_sweep =
				new SweepRenderable(
					entity,												// Entity to attach the renderable to
					SweepRenderable::Dynamic,						// How/when to execute the renderable
					40.0f,												// How long to take to sweep from 0 to 1
					1,														// number of times to cycle before stopping
					static_debris_delay->GetTriggerAttribute(),
					0.2f,
					2.5f);
			ChildMorphRenderable	*smoke_1_morph	=
				new ChildMorphRenderable(
					entity,												// Entity to attach the renderable to
					ChildMorphRenderable::Dynamic,				// How/when to execute the renderable
					smoke_1_a,											// destination
					smoke_b,												// start object
					smoke_c,												// end object
					smoke_1_sweep->GetSweepAttribute(),		  	// pointer to control variable
					dpl_morph_vertices, 								// Defines type of morph to do
					dplMainZone,										// DPL Zone this stuff will live in (for culling)
					dpl_isect_mode_obj,								// type of intersections to do on this object
					NULL,													// intersection mask for the object
					this_root->GetDCS());							// the parent DCS we will be offsetting from
			// Find the instance with the flash in it and hook up the instance switch
			dpl_INSTANCE *smoke_1_instance = dpl_GetDCSInstance(smoke_1_morph->GetDCS(), 1);
			InstanceSwitchRenderable	*smoke_1_instance_switch =
				new InstanceSwitchRenderable(
					entity,												// Entity to attach the renderable to
					InstanceSwitchRenderable::Dynamic,			// How/when to execute the renderable
					smoke_1_instance,									// the instance to control
					True,													// Instance is on when trigger is...
					static_debris_delay->GetTriggerAttribute());
			//--------------------------------------------------------------
			// Renderables to handle the rising smoke column (second shape)
			//--------------------------------------------------------------
#if 0
			OneShotDelayRenderable	*smoke_2_morph_delay =
				new OneShotDelayRenderable(
					entity,												// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,				// How/when to execute the renderable
					6.0f);												// How long to wait before raising the trigger
			SweepRenderable *smoke_2_sweep =
				new SweepRenderable(
					entity,												// Entity to attach the renderable to
					SweepRenderable::Dynamic,						// How/when to execute the renderable
					30.0f,  												// How long to take to sweep from 0 to 1
					1,														// number of times to cycle before stopping
					smoke_2_morph_delay->GetTriggerAttribute(),
					0.1f,
					1.0f);
			ChildMorphRenderable	*smoke_2_morph	=
				new ChildMorphRenderable(
					entity,												// Entity to attach the renderable to
					ChildMorphRenderable::Dynamic,				// How/when to execute the renderable
					smoke_2_a,											// destination
					smoke_b,												// start object
					smoke_c,												// end object
					smoke_2_sweep->GetSweepAttribute(),			// pointer to control variable
					dpl_morph_vertices,								// Defines type of morph to do
					dplMainZone,										// DPL Zone this stuff will live in (for culling)
					dpl_isect_mode_obj,								// type of intersections to do on this object
					NULL,													// intersection mask for the object
					this_root->GetDCS());							// the parent DCS we will be offsetting from
			// Find the instance with the flash in it and hook up the instance switch
			dpl_INSTANCE *smoke_2_instance = dpl_GetDCSInstance(smoke_2_morph->GetDCS(), 1);
			InstanceSwitchRenderable	*smoke_2_morph_instance_switch =
				new InstanceSwitchRenderable(
					entity,												// Entity to attach the renderable to
					InstanceSwitchRenderable::Dynamic,			// How/when to execute the renderable
					smoke_2_instance,									// the instance to control
					True,													// Instance is on when trigger is...
					smoke_2_morph_delay->GetTriggerAttribute());
#endif
			//-------------------------------------------
			// Set dither Z on material for smoke column
			//-------------------------------------------
			dpl_MATERIAL *damage_material =
				dpl_LookupMaterial ("btfx:smoke1_mtl",
					dpl_lookup_normal,
					&status);
			if (damage_material == 0)
			{
				cout << "couldn't find material\n";
			}
			else
			{
				dpl_SetMaterialDitherZ(damage_material, 10.0f);
				dpl_FlushMaterial(damage_material);
			}
			//---------------------------------------------------
			// Create a material morph to fade smoke column away
			//---------------------------------------------------
			OneShotDelayRenderable	*smoke_fade_delay =
				new OneShotDelayRenderable(
					entity,												// Entity to attach the renderable to
					OneShotDelayRenderable::Dynamic,				// How/when to execute the renderable
					25.0f);												// How long to wait before raising the trigger
			SweepRenderable *smoke_fade_sweep =
				new SweepRenderable(
					entity,								// Entity to attach the renderable to
					SweepRenderable::Dynamic,		// How/when to execute the renderable
					18.5f, 								// How long to take to sweep from 0 to 1
					1,										// number of times to cycle before stopping
					smoke_fade_delay->GetTriggerAttribute());		// trigger variable
			Register_Object(smoke_fade_sweep);
			// Lookup texture for smoke column
			dpl_TEXTURE *smoke_texture =
				dpl_LookupTexture("btfx:smoke1_scr_tex", dpl_lookup_normal, &status);
			if (smoke_texture == NULL)
				DEBUG_STREAM<<"couldn't find texture btfx:smoke1_scr_tex for an effect\n";
			//
			MorphMaterialRenderable *smoke_fade_material =
				new MorphMaterialRenderable(
					entity,										// Entity to attach the renderable to
					MorphMaterialRenderable::Dynamic,	// How/when to execute the renderable
					1.0f,1.0f,1.0f,							// Material's ambient component
					0.7f,0.4f,0.4f,							// Material's emissive component
					0.3714f,0.2899f,0.3714f,				// Material's diffuse component
					0.0f,0.0f,0.0f,0.0f,						// Material's specular component
					0.7f,0.99f,0.0f,							// Material's opacity
					smoke_texture,								// Material's texture pointer
					10.0f,										// Material's Z dither value
					3,												// Material's Fog Imunity value
					1.0f,1.0f,1.0f,							// Material's ambient component
					0.7f,0.4f,0.4f,							// Material's emissive component
					0.3714f,0.2899f,0.3714f,				// Material's diffuse component
					0.0f,0.0f,0.0f,0.0f,						// Material's specular component
					0.0f,0.99f,0.0f,							// Material's opacity
					10.0f, 										// Material's Z dither value
					smoke_fade_sweep->GetSweepAttribute());
			Register_Object(smoke_fade_material);
//			dpl_INSTANCE *smoke_1_instance = dpl_GetDCSInstance(smoke_1_morph->GetDCS(), 1);
			if (!smoke_1_instance)
				Fail("smoke_1_instance came back null\n");
			dpl_SetInstanceFrontMaterial(smoke_1_instance, smoke_fade_material->GetMaterial());
			dpl_FlushInstance(smoke_1_instance);
#endif
			#else
			DEBUG_STREAM <<"Explosion effect 104 called, this is disabled and shouldn't be used!\n";
			#endif
			break;
		}
		default:
		{
			break;
		}
	}
}
//
//#############################################################################
// MakeEntityRenderables handles creating all the renderables necessary to
// display an object.  This routine contains default behaviors for creating
// some types of simple objects.  The idea is that if a higher level routine
// can't figure out how to make renderables for something this routine will be
// called and the default behavior will be used.
//#############################################################################
//
void
	DPLRenderer::MakeEntityRenderables(
		Entity					*entity,				// The entity we are dealing with
		ResourceDescription	*model_resource,	// Pointer to the video resource
		ViewFrom					view_type) 			// Type of reference (inside/outside...etc.)
{
	char
		*object_filename;
	#if DEBUG_LEVEL > 0
	int
		object_count;
	#endif
	L4VideoObject	// should be const
		*video_object;
	L4VideoObjectWrapper
		*video_wrapper;
	ChainOf<L4VideoObjectWrapper*>
		video_chain(NULL);
	L4VideoObject::ResourceType
		resource_type;
	dpl_DCS
		*my_root_dcs;
	Enumeration	// L4VideoObject::RendererModes
		renderer_modes;
	//
	// Set the Entity_Being_Created global so the C language callback will
	// be able to mark the geometry with damage zone values if necessary.
	//
	Entity_Being_Created =	entity;
	my_root_dcs				=	NULL;
	//
	// convert video resource into chain of video objects and make an iterator
	// for that chain
	//
	if (model_resource)
	{
		#if DEBUG_LEVEL > 0
		object_count =
		#endif
			L4VideoObjectWrapper::BuildVideoObjectChainFromResource(
				&video_chain,
				model_resource
			);
	}
	ChainIteratorOf<L4VideoObjectWrapper*>
		video_iterator(video_chain);
	//
	// Switch to allow us to have scripts at this level for constructing certain
	// types of video objects (whether or not they have a video resource)
	//
	switch (entity->GetClassID())
	{
		//
		// Dropzones have no graphical appearance so we do nothing
		//
		case	DropZoneClassID:
			break;
		//
		// Player objects have no graphical appearance unless one is created
		// at a higher (game specific) level, so we do nothing here.
		//
		case  PlayerClassID:
			break;
		// this entity has no visual appearance
		case	AudioEntityClassID:
			break;
		//
		// Cultural and landmark are the only ones with "destroyed" processing
		// This is similar to the default case but does not allow us to be
		// inside the entity, does not allow us to be a mover,
		//
		case	CulturalIconClassID:
		case	LandmarkClassID:
		{
			dpl_INSTANCE
				*this_instance;
			dpl_OBJECT
				*this_object;
			dpl_ISECT_MODE
				intersect_mode;
			uint32
				intersect_mask;
			LinearMatrix
				offset_matrix = LinearMatrix::Identity;
			dpl_DCS
				*root_DCS,
				*this_DCS;
			//
			// Make sure the object has a video resource
			//
			if (!model_resource)
			{
				VideoRenderer::MakeEntityRenderables(entity, model_resource, view_type);
				Entity_Being_Created = NULL;
				return;
			}
			intersect_mode =	dpl_isect_mode_geometry;
			intersect_mask	=  INTERSECT_ALL;
			Logical
				first_object = True;
			video_iterator.First();
			while ((video_wrapper = video_iterator.ReadAndNext()) != NULL)
			{
				video_object		= video_wrapper->GetVideoObject();
				object_filename	= video_object->GetObjectFilename();
				resource_type		= video_object->GetResourceType();
				renderer_modes		= video_object->GetRendererModes();

				#if NOISY_RENDERER
				Tell("L4VIDEO.cpp loading object " << object_filename);
				Tell(" type " << resource_type);
				Tell(" mode 0x" <<hex<<renderer_modes<<dec<< "." << endl);
				#endif

				if ((resource_type != L4VideoObject::Object) &&
					 (resource_type != L4VideoObject::Rubble))
				{
					DEBUG_STREAM << "L4VIDEO.cpp wrong video resource type for object "<<object_filename<<"\n";
					continue;	// next object
				}

				SET_VIDEO_LOAD_OBJECT();
				// HACK !!! load the object as uncached so entity identification will work right
				this_object = dpl_LoadObject(object_filename, dpl_load_normal);
				CLEAR_VIDEO_LOAD_OBJECT();

				if (this_object == NULL)
				{
					DEBUG_STREAM << "L4VIDEO.cpp couldn't load object "<<object_filename<<"\n";
					if (!first_object)
					{
						continue;	// next object
					}
				}
				if (first_object)
				{
					first_object = False;
					RootRenderable *this_static_root =
						new RootRenderable(
							entity,							// Entity to attach the renderable to
							RootRenderable::Static,		// How/when to execute the renderable
							this_object,					// object to hang on the DCS, may be a list later <NULL>
							dplMainZone,					// DPL Zone this stuff will live in (for culling)
							intersect_mode,				// type of intersections to do on this object
							intersect_mask);				// intersection mask for the object
					Register_Object(this_static_root);
					root_DCS = this_static_root->GetDCS();
					//------------------------------------------------------
					// if root object billboards (all others billboard too)
					//------------------------------------------------------
					this_DCS = root_DCS;
					this_instance = this_static_root->GetInstance();
					//
					// Set my_root_dcs because we want this guy's hiearchy to be marked
					// with his entity pointer
					//
					my_root_dcs = root_DCS;
				}
				else
				{
					if (renderer_modes | L4VideoObject::BillboardObject)
					{
						//------------------------------------------
						// attach additional object to separate DCS
						// because it is billboarded
						//------------------------------------------
						DPLStaticChildRenderable *this_child =
							new DPLStaticChildRenderable(
								entity,
								dplMainZone,
								this_object,
								intersect_mode,
								intersect_mask,
								offset_matrix,
								root_DCS);
						Register_Object(this_child);
						this_DCS = this_child->GetDCS();
						this_instance = this_child->GetInstance();
					}
					else
					{
						//----------------------------------------
						// attach additional objects to root_DCS
						// (HACK) temporary implementation (HACK)
						//----------------------------------------
						DCSInstanceRenderable *another_instance =
							new DCSInstanceRenderable(
								entity,									// Entity to attach the renderable to
								DCSInstanceRenderable::Static,	// How/when to execute the renderable
								this_object,							// object to connect to the instance
								root_DCS,								// the DCS to add the instance to
								intersect_mode,						// type of intersections to do on this object
								intersect_mask,						// intersection mask for the object
								True);            					// initial visibility setting
						Register_Object(another_instance);
						this_DCS = NULL;
						this_instance = another_instance->GetInstance();
					}
				}
				//
				// Hook up the object to an instance switch renderable responsive to state
				//
				StateIndicator* simulation_state =
					(StateIndicator *)entity->GetAttributePointer("SimulationState");
				switch(resource_type)
				{
					case	L4VideoObject::Object:
					{
						#if DEBUG_LEVEL > 0
						StateInstanceSwitchRenderable *undamaged_state =
						#endif
							new StateInstanceSwitchRenderable(
								entity,													// Entity to attach the renderable to
								StateInstanceSwitchRenderable::Watcher,		// How/when to execute the renderable
								this_instance,											// the instance to control
								False,													// true to turn on in this state, false for off
								simulation_state,										// State dial we use to control the on/off
								CulturalIcon::BurningState);						//	State that we look for
						Register_Object(undamaged_state);
						break;
					}
					case  L4VideoObject::Rubble:
					{
						#if DEBUG_LEVEL > 0
						StateInstanceSwitchRenderable *undamaged_state =
						#endif
							new StateInstanceSwitchRenderable(
								entity,													// Entity to attach the renderable to
								StateInstanceSwitchRenderable::Watcher,		// How/when to execute the renderable
								this_instance,											// the instance to control
								True,														// true to turn on in this state, false for off
								simulation_state,										// State dial we use to control the on/off
								CulturalIcon::BurningState);						//	State that we look for
						Register_Object(undamaged_state);
						break;
					}
				}
				//----------------------------------
				// billboard object if so indicated
				//----------------------------------
				if (this_DCS && (renderer_modes & L4VideoObject::BillboardObject))
				{
					int
						axes = dpl_reorient_axes_none;

					if (renderer_modes & L4VideoObject::BillboardXAxis)
					{
						axes |= dpl_reorient_axes_x;
					}
					if (renderer_modes & L4VideoObject::BillboardYAxis)
					{
						axes |= dpl_reorient_axes_y;
					}
					if (renderer_modes & L4VideoObject::BillboardZAxis)
					{
						axes |= dpl_reorient_axes_z;
					}
					dpl_SetDCSReorientAxes(this_DCS, (dpl_REORIENT_AXES)axes);
					dpl_FlushDCS(this_DCS);
				}
			}
			break;
		}
		//
		// Case to handle rivets using the fast projectile code
		//
		case	RivetClassID:
		{
			dpl_OBJECT
				*this_object;
			video_iterator.First();
				video_wrapper 		= video_iterator.ReadAndNext();
				Check(video_wrapper);
				video_object 		= video_wrapper->GetVideoObject();
				Check_Pointer(video_object);
				object_filename	= video_object->GetObjectFilename();
				this_object 		= dpl_LoadObject(object_filename, dpl_load_normal);
			#if DEBUG_LEVEL > 0
			ProjectileRootRenderable *projectile =
         #endif
				new ProjectileRootRenderable(
					entity,										// Entity to attach the renderable to
					ProjectileRootRenderable::Dynamic,	// How/when to execute the renderable
					this_object,								// object to hang on the DCS, may be a list later <NULL>
					dplMainZone);								// DPL Zone this stuff will live in (for culling)
			Register_Object(projectile);
			break;
		}
		//
		// Script for generating renderables for eyecandy
		//
		case	EyeCandyClassID:
		{
			int effect_number;

			RootRenderable *this_root =
				new RootRenderable(
					entity,							// Entity to attach the renderable to
					RootRenderable::Dynamic,	// How/when to execute the renderable
					NULL,								// object to hang on the DCS, may be a list later <NULL>
					dplMainZone,					// DPL Zone this stuff will live in (for culling)
					dpl_isect_mode_obj,			// type of intersections to do on this object
					NULL);							// intersection mask for the object
			Register_Object(this_root);
			StateIndicator* simulation_state = (StateIndicator *)entity->GetAttributePointer("SimulationState");

			video_iterator.First();
			while ((video_wrapper = video_iterator.ReadAndNext()) != NULL)
			{
				video_object = video_wrapper->GetVideoObject();
				effect_number = atoi(video_object->GetObjectFilename());
				#if DEBUG_LEVEL > 0
				DPLSFXRenderable *this_effect =
				#endif
					new DPLSFXRenderable(
						entity,							// Entity to attach the effect to
						dplMainZone,					// DPL zone everything will be in
						Point3D::Identity,			// Point offset from the parent DCS
						this_root->GetDCS(),			// Parent DCS (can be NULL for world)
						simulation_state,				// Trigger effect when this state changes
						EyeCandy::effectOn,			// Trigger effect when in this state
						effect_number,					// Type of effect to trigger
						.01);								// Effect repeat speed.
				Register_Object(this_effect);
			}
			break;
		}
		//
		// Script for generating an explosion of type specified in the resource file
		//
		case ExplosionClassID:
		{
			int effect_number;

			video_iterator.First();
			while ((video_wrapper = video_iterator.ReadAndNext()) != NULL)
			{
				video_object = video_wrapper->GetVideoObject();
				effect_number = atoi(video_object->GetObjectFilename());
				if(effect_number < 100 || effect_number >= 1000)
				{
					DPLIndependantEffect(
						entity->localOrigin.linearPosition,
						effect_number);
				}
				else
				{
				ExplosionScripts(
					entity,				// The entity we are dealing with
					model_resource,	// Pointer to the video resource
					view_type, 			// Type of reference (inside/outside...etc.)
					effect_number - 100);
				}
			}
			break;
		}
		case CameraDirectorClassID:
		{
			//
			//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			// Create the CameraDirector HUD Renderable if not a Replicant
			//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			//
			if (entity->GetInstance() == CameraDirector::MasterInstance)
			{
				CameraDirector *camera_director = (CameraDirector*) entity;
				Check_Pointer(camera_director);

				int *player_index = &camera_director->goalPlayerIndex;
				Check_Pointer(player_index);

				Logical *display_rank_window = &camera_director->displayRankingWindow;
				Check_Pointer(display_rank_window);

				#if DEBUG_LEVEL > 0
				CameraShipHUDRenderable *my_camera_hud =
				#endif
					new CameraShipHUDRenderable(
						entity,
						CameraShipHUDRenderable::Dynamic,
						player_index,
						display_rank_window
					);
				Register_Object(my_camera_hud);
			}
			break;
		}
		//
		//	Script for a drivable camera, the camera is invisible to other players
		//
		case CameraShipClassID:
		{
			if(view_type == insideEntity)
			{
				//
				// Build an empty root renderable and an eye renderable
				//
				RootRenderable *this_root =
					new RootRenderable(
						entity,							// Entity to attach the renderable to
						RootRenderable::Dynamic,	// How/when to execute the renderable
						NULL,								// object to hang on the DCS, may be a list later <NULL>
						dplMainZone,					// DPL Zone this stuff will live in (for culling)
						dpl_isect_mode_obj,			// type of intersections to do on this object
						NULL);							// intersection mask for the object
				Register_Object(this_root);

				#if DEBUG_LEVEL > 0
				DPLEyeRenderable* this_eye	=
				#endif
						new DPLEyeRenderable(
							entity,
							dplMainZone,
							LinearMatrix::Identity,
							this_root->GetDCS(),
							dplMainView,
							NULL
						);
				Register_Object(this_eye);
			}
			break;
		}
		//
		// Script for doorframe, so it can be used in any game
		//
		case	DoorFrameClassID:
		{
			Verify( object_count == 6 );
			//------------------------------------------------
			// First video object is root shape for the door
			// followed by left door shape, right door shape,
			// door lights, left door lights, and right door
			// lights.
			//------------------------------------------------
			dpl_OBJECT
				*object,
				*left,
				*right,
				*object_lights,
				*left_lights,
				*right_lights;
			int object_number = 0;

			video_iterator.First();
			while ((video_wrapper = video_iterator.ReadAndNext()) != NULL)
			{
				video_object = video_wrapper->GetVideoObject();
				object_filename = video_object->GetObjectFilename();

				#if NOISY_RENDERER
				Tell("L4VIDEO.cpp loading door object "<<object_filename<<"\n");
				#endif
				switch (object_number)
				{
					case 0:
						object		 = dpl_LoadObject(object_filename, dpl_load_normal);
						break;
					case 1:
						left			 = dpl_LoadObject(object_filename, dpl_load_normal);
						break;
					case 2:
						right			 = dpl_LoadObject(object_filename, dpl_load_normal);
						break;
					case 3:
						object_lights = dpl_LoadObject(object_filename, dpl_load_normal);
						break;
					case 4:
						left_lights	 = dpl_LoadObject(object_filename, dpl_load_normal);
						break;
					case 5:
						right_lights = dpl_LoadObject(object_filename, dpl_load_normal);
						break;
					default:
						// ignore any additional objects
						break;
				}
				++object_number;
			}
			//
			//	HACK Hard Coded Doorframes SubsystemArray access
			//
			Door *left_door_sub = (Door*) entity->GetSubsystem(0);
			Point3D*	left_door_position = &left_door_sub->currentPosition;
			Door *right_door_sub = (Door*) entity->GetSubsystem(1);
			Point3D*	right_door_position = &right_door_sub->currentPosition;
			RootRenderable *door_sill =
				new RootRenderable(
					entity,							// Entity to attach the renderable to
					RootRenderable::Static,		// How/when to execute the renderable
					object,							// object to hang on the DCS, may be a list later <NULL>
					dplMainZone,					// DPL Zone this stuff will live in (for culling)
					dpl_isect_mode_obj,			// type of intersections to do on this object
					NULL);							// intersection mask for the object
			Register_Object(door_sill);
			//
			// Set my_root_dcs because we want this guy's hiearchy to be marked
			// with his entity pointer
			//
			my_root_dcs = door_sill->GetDCS();
			LinearMatrix	my_ident(True);
			DPLChildPointRenderable* left_door =
				new DPLChildPointRenderable(
					entity,
					dplMainZone,
					left,
					dpl_isect_mode_obj,
					NULL,
					my_ident,
					door_sill->GetDCS(),
					left_door_position);
			Register_Object(left_door);
			DPLChildPointRenderable* right_door =
				new DPLChildPointRenderable(
					entity,
					dplMainZone,
					right,
					dpl_isect_mode_obj,
					NULL,
					my_ident,
					door_sill->GetDCS(),
					right_door_position);
			Register_Object(right_door);
			#if DEBUG_LEVEL > 0
			DCSInstanceRenderable *door_sill_lights_instance =
			#endif
				new DCSInstanceRenderable(
					entity,									// Entity to attach the renderable to
					DCSInstanceRenderable::Static,	// How/when to execute the renderable
					object_lights,							// object to connect to the instance
					door_sill->GetDCS(),					// the DCS to add the instance to
					dpl_isect_mode_obj,					// type of intersections to do on this object
					NULL,										// intersection mask for the object
					True);            					// initial visibility setting
			Register_Object(door_sill_lights_instance);
			#if DEBUG_LEVEL > 0
			DCSInstanceRenderable *left_door_lights_instance =
			#endif
				new DCSInstanceRenderable(
					entity,									// Entity to attach the renderable to
					DCSInstanceRenderable::Static,	// How/when to execute the renderable
					left_lights,							// object to connect to the instance
					left_door->GetDCS(),					// the DCS to add the instance to
					dpl_isect_mode_obj,					// type of intersections to do on this object
					NULL,										// intersection mask for the object
					True);            					// initial visibility setting
			Register_Object(left_door_lights_instance);
			#if DEBUG_LEVEL > 0
			DCSInstanceRenderable *right_door_lights_instance =
			#endif
				new DCSInstanceRenderable(
					entity,									// Entity to attach the renderable to
					DCSInstanceRenderable::Static,	// How/when to execute the renderable
					right_lights,							// object to connect to the instance
					right_door->GetDCS(),				// the DCS to add the instance to
					dpl_isect_mode_obj,					// type of intersections to do on this object
					NULL,										// intersection mask for the object
					True);            					// initial visibility setting
			Register_Object(right_door_lights_instance);
			break;
		}
		//
		// This is the script run on anything that isn't already listed above
		//
		default:
		{
			dpl_OBJECT
				*this_object;
			dpl_ISECT_MODE
				intersect_mode;
			uint32
				intersect_mask;
			LinearMatrix
				offset_matrix = LinearMatrix::Identity;
			dpl_DCS
				*root_DCS,
				*this_DCS;
			//
			// First, establish that this level doesn't know what to do
			// if the object has no video resource.
			//
			if (!model_resource)
			{
				VideoRenderer::MakeEntityRenderables(entity, model_resource, view_type);
				Entity_Being_Created = NULL;
				return;
			}
			//
			// If we're inside the entity, set up the intersect mode and mask so we
			// won't intersect ourselves with the pickpoint.  Other items get full
			// geometry intersection.
			//
			if (view_type == insideEntity)
			{
				intersect_mode =	dpl_isect_mode_obj;
				intersect_mask	=  NULL;
			}
			else
			{
				intersect_mode =	dpl_isect_mode_geometry;
				intersect_mask	=  INTERSECT_ALL;
			}

			Logical
				first_object = True;

			video_iterator.First();
			while ((video_wrapper = video_iterator.ReadAndNext()) != NULL)
			{
				video_object = video_wrapper->GetVideoObject();
				object_filename = video_object->GetObjectFilename();
				resource_type = video_object->GetResourceType();
				renderer_modes = video_object->GetRendererModes();

				#if NOISY_RENDERER
				Tell("L4VIDEO.cpp loading object " << object_filename);
				Tell(" type " << resource_type);
				Tell(" mode 0x" <<hex<<renderer_modes<<dec<< "." << endl);
				#endif

				if ((resource_type != L4VideoObject::Object) &&
					 (resource_type != L4VideoObject::Rubble))
				{
					DEBUG_STREAM << "L4VIDEO.cpp wrong video resource type for object "<<object_filename<<"\n";
					continue;	// next object
				}

				SET_VIDEO_LOAD_OBJECT();
				this_object = dpl_LoadObject(object_filename, dpl_load_normal);
				CLEAR_VIDEO_LOAD_OBJECT();

				if (this_object == NULL)
				{
					DEBUG_STREAM << "L4VIDEO.cpp couldn't load object "<<object_filename<<"\n";
					if (!first_object)
					{
						continue;	// next object
					}
				}
				if (first_object)
				{
					first_object = False;
					//
					// Determine if the entity is a mover or not, which tells us what type of
					// renderable to use to construct it.
					//
					if (entity->IsDerivedFrom(Mover::ClassDerivations))
					{
						//
						// It's a mover, construct it with a dynamic root renderable so it can move
						// also remember it's DCS so we can hook other shapes to it later.
						//
						SET_VIDEO_CONSTRUCT_ROOT();
						RootRenderable *this_root =
							new RootRenderable(
								entity,							// Entity to attach the renderable to
								RootRenderable::Dynamic,	// How/when to execute the renderable
								this_object,					// object to hang on the DCS, may be a list later <NULL>
								dplMainZone,					// DPL Zone this stuff will live in (for culling)
								intersect_mode,				// type of intersections to do on this object
								intersect_mask);				// intersection mask for the object
						Register_Object(this_root);
						CLEAR_VIDEO_CONSTRUCT_ROOT();

						root_DCS = this_root->GetDCS();
					}
					else
					{
						//
						// It's a static, construct it with a static RootRenderable and
						// remember it's DCS so we can hook other shapes to it later.
						//
						RootRenderable *this_static_root =
							new RootRenderable(
								entity,							// Entity to attach the renderable to
								RootRenderable::Static,		// How/when to execute the renderable
								this_object,					// object to hang on the DCS, may be a list later <NULL>
								dplMainZone,					// DPL Zone this stuff will live in (for culling)
								intersect_mode,				// type of intersections to do on this object
								intersect_mask);				// intersection mask for the object
						Register_Object(this_static_root);
						root_DCS = this_static_root->GetDCS();
					}
					//
					// Set my_root_dcs because we want this guy's hiearchy to be marked
					// with his entity pointer
					//
					my_root_dcs = root_DCS;
					//
					// If we are inside the entity, then we must build an eyepoint for it too.
					// since there is no special construction, we build the eyepoint with a
					// zero offset.
					//
					if (view_type == insideEntity)
					{
						EulerAngles *eyepoint_rotation =
							(EulerAngles *)entity->GetAttributePointer("EyepointRotation");
						#if DEBUG_LEVEL > 0
						DPLEyeRenderable *this_eye	=
						#endif
							new DPLEyeRenderable(
								entity,
								dplMainZone,
								LinearMatrix::Identity,
								root_DCS,
								dplMainView,
								eyepoint_rotation);
						Register_Object(this_eye);
					}
					//------------------------------------------------------
					// if root object billboards (all others billboard too)
					//------------------------------------------------------
					this_DCS = root_DCS;
				}
				else
				{
					if (renderer_modes | L4VideoObject::BillboardObject)
					{
						//------------------------------------------
						// attach additional object to separate DCS
						// because it is billboarded
						//------------------------------------------
						DPLStaticChildRenderable *this_child =
							new DPLStaticChildRenderable(
								entity,
								dplMainZone,
								this_object,
								intersect_mode,
								intersect_mask,
								offset_matrix,
								root_DCS);
						Register_Object(this_child);
						this_DCS = this_child->GetDCS();
					}
					else
					{
						//----------------------------------------
						// attach additional objects to root_DCS
						// (HACK) temporary implementation (HACK)
						//----------------------------------------
						#if DEBUG_LEVEL > 0
						DCSInstanceRenderable *another_instance =
						#endif
							new DCSInstanceRenderable(
								entity,									// Entity to attach the renderable to
								DCSInstanceRenderable::Static,	// How/when to execute the renderable
								this_object,							// object to connect to the instance
								root_DCS,								// the DCS to add the instance to
								intersect_mode,						// type of intersections to do on this object
								intersect_mask,						// intersection mask for the object
								True);            					// initial visibility setting
						Register_Object(another_instance);
						this_DCS = NULL;
					}
				}
				//----------------------------------
				// billboard object if so indicated
				//----------------------------------
				if (this_DCS && (renderer_modes & L4VideoObject::BillboardObject))
				{
					int
						axes = dpl_reorient_axes_none;

					if (renderer_modes & L4VideoObject::BillboardXAxis)
					{
						axes |= dpl_reorient_axes_x;
					}
					if (renderer_modes & L4VideoObject::BillboardYAxis)
					{
						axes |= dpl_reorient_axes_y;
					}
					if (renderer_modes & L4VideoObject::BillboardZAxis)
					{
						axes |= dpl_reorient_axes_z;
					}
					dpl_SetDCSReorientAxes(this_DCS, (dpl_REORIENT_AXES)axes);
					dpl_FlushDCS(this_DCS);
				}
			}
			break;
		}
	}
	//
	// release the video chain memory
	//
	L4VideoObjectWrapper::DeleteVideoObjectChain(&video_chain);
	//
	// Clear the entity pointer passed to the callback system
	//
	Entity_Being_Created = NULL;
	//
	// If my_root_dcs is non-null, mark the entity's DCS hiearchy with it's
	// entity pointer.
	//
	if(my_root_dcs)
	{
		MarkDCSHiearchy(my_root_dcs,entity);
#if 0
		if(entity->GetClassID() == DemolitionPackClassID)
		{
			Point3D temp_point(0.0,0.0,0.0);
			#if DEBUG_LEVEL > 0
			OnePSFXRenderable	*this_effect=
			#endif
				new OnePSFXRenderable(
					entity,								// Entity to attach the renderable to
					OnePSFXRenderable::Static,		// How/when to execute the renderable
					myPSFXDescriptons[3],			// name of file with the PFX description in it
					my_root_dcs,						// DCS the effect is relative to (may be NULL)
					&temp_point);						// Offset (or world coordinants if DCS is NULL)
			Register_Object(this_effect);
		}
#endif
	}
	Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// This should be called prior to the rendering of a frame to setup the culling
// data the renderer keeps (mostly the world to eye transform)
//
void
	DPLRenderer::SetupCull()
{
	Entity
		*linked_entity;
	LinearMatrix
		site_to_world,
		world_to_site,
		eye_rotation,
		eye_inverted;
	EntitySegment
		*eyepoint_segment;
	EulerAngles
		*eyepoint_rotation;
	//
	// To test the functionality, this is not as efficient as it could be.
	// when it gets fully patched into the renderer it will be improved.
	//
	// Figure out what type of entity this is.
	//
	linked_entity		=	GetLinkedEntity();
	eyepoint_rotation	= (EulerAngles*)linked_entity->GetAttributePointer("EyepointRotation");
	if(linked_entity->IsDerivedFrom(JointedMover::ClassDerivations))
	{
		JointedMover
			*linked_jointed_mover;

		linked_jointed_mover 	= Cast_Object(JointedMover*, linked_entity);
		eyepoint_segment			= linked_jointed_mover->GetSegment("siteeyepoint");
		if(!eyepoint_segment)
		{
			Fail("DPLRenderer::SetupCull jointed mover had no siteeyepoint\n");
		}
		if(!eyepoint_rotation)
		{
			Fail("DPLRenderer::SetupCull jointed mover had no EyepointRotation attribute\n");
		}
		linked_jointed_mover->GetSegmentToWorld(*eyepoint_segment, &site_to_world);
		if(eyepoint_rotation)
		{
			world_to_site.Invert(site_to_world);
			eye_rotation 				= *eyepoint_rotation;
			eye_inverted.Invert(eye_rotation);
			worldToEyeMatrix.Multiply(world_to_site, eye_inverted);
		}
		else
		{
			worldToEyeMatrix.Invert(site_to_world);
		}
	}
	else
	{
		site_to_world = linked_entity->localOrigin;
		if(eyepoint_rotation)
		{
			world_to_site.Invert(site_to_world);
			eye_rotation 				= *eyepoint_rotation;
			eye_inverted.Invert(eye_rotation);
			worldToEyeMatrix.Multiply(world_to_site, eye_inverted);
		}
		else
		{
			worldToEyeMatrix.Invert(site_to_world);
		}
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
	DPLRenderer::ResetStatistics()
{
	total_cull 			= 0;
	total_draw 			= 0;
	total_pixelplanes = 0;
	total_frame_time 	= 0;
	frame_count 		= 0;
	target_frame_time	= 56000;
	target_frame_count= 0;
	report_time = currentFrameTime + 60.0f;
}
void
	DPLRenderer::ReportStatistics()
{
	if(frame_count != 0)
	{
		cout<<"Frames "<<frame_count<<"\n";
		cout<<((float)target_frame_count/(float)frame_count)*100.0f<<"% of the frames were under the target frame time\n";
		cout<<"Average Cull "<<(unsigned long)((float)total_cull/(float)frame_count)<<"\n";
		cout<<"Average Draw "<<(unsigned long)((float)total_draw/(float)frame_count)<<"\n";
		cout<<"Average Pixel Planes "<<(unsigned long)((float)total_pixelplanes/(float)frame_count)<<"\n";
		cout<<"Average Frame Time "<<(unsigned long)((float)total_frame_time/(float)frame_count)<<"\n";
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Execute Method, performs the rendering of one frame
//
void
	DPLRenderer::ExecuteImplementation(
		RendererComplexity,
		RendererOrigin::InterestingEntityIterator* all_iterator
	)
{
#define VIDEO_CULLING			True			// Turns video culling on and off
#define COUNT_RENDERABLES		False        // Counts renderables per entity and frame
#define COUNT_ENTITIES			False			// Counts the entities per frame
#define CROSS_CHECK_ENTITIES  False			// Checks entity dynamic socket for renderables

	SET_VIDEO_RENDERER();

	Entity
		*entity;
	Component
		*component;

	#if PRINT_PICKPOINT_TEST
	dpl_INSTANCE
		*old_instance;
	int
		old_damage_zone;
	#endif

	#if USE_TRACKER_STRUCTURE
	dpl_tracker
		*this_tracker;
	#endif

	int
		current_app_state;
	//
	// Update the renderer's frame timer
	//
	currentFrameTime = Now();
	//
	// Simple HACK to hold us on a blank screen till the mission is actually started.
	//
	current_app_state = application->GetApplicationState();
	if(application->GetApplicationState() != lastAppState)
	{
		switch(current_app_state)
		{
			case	Application::RunningMission:
				dpl_SetViewFog(
					dplMainView,
					dpl_fog_type_pixel_lin,
					fogRed,
					fogGreen,
					fogBlue,
					fogNear,
					fogFar );
				dpl_FlushView(dplMainView);
				dpl_DisableSyncOnCreate();
				break;
			case  Application::StoppingMission:
#if 0
				dpl_SetViewFog(
					dplMainView,
					dpl_fog_type_pixel_lin,
					0.0,
					0.0,
					0.0,
					0.1,
					0.2 );
				dpl_FlushView(dplMainView);
#endif
				break;
		}
		lastAppState = current_app_state;
	}
//
// !!!! This may be the WRONG place to do this, but we'll try it
//
  // extern dpl_INSTANCE *dpl_SectPixel(dpl_VIEW *, float32, float32, uint32,
  //                 float32 *, float32 *, float32 *,
  //                 dpl_DCS **, dpl_GEOGROUP **, dpl_GEOMETRY **);
  // extern dpl_INSTANCE *dpl_RapidSectPixel(float32, float32, float32, uint32,
  //                 float32 *, float32 *, float32 *,
  //                 dpl_DCS **, dpl_GEOGROUP **, dpl_GEOMETRY **);
	//
	// If there is a reticle pointer defined, do the pickpoint processing
	//
	SET_VIDEO_PICKPOINT();
	if(vehicleReticle)
	{
		//
		// Check the reticle structure to make sure it's ok, then see if pick
		// point processing is turned on.
		//
		Check(vehicleReticle);
		if(vehicleReticle->pickPointingOn)
		{
			//
			// Remember the last thing we were targeted on, then go ahead and do the
			// intersection call.
			//
			#if PRINT_PICKPOINT_TEST
			old_instance 		= dplHitInstance;
			old_damage_zone 	= vehicleReticle->targetDamageZone;
			#endif
			#if RAPID_SECT_PIXEL
			dplHitInstance = dpl_RapidSectPixel (
				(((vehicleReticle->reticlePosition.x*aspectRatio)/2.0) + 0.5),		// go from reticle coordinant system
				(((vehicleReticle->reticlePosition.y*aspectRatio)/2.0) + 0.5),      // to the screen coordinant system.
				1.0e5,
				INTERSECT_ALL,
				&vehicleReticle->rayIntersection.x,
				&vehicleReticle->rayIntersection.y,
				&vehicleReticle->rayIntersection.z,
				&dplHitDCS,
				&dplHitGeoGroup,
				&dplHitGeometry);
			#else
			dplHitInstance = dpl_SectPixel (
				dplMainView,
				(vehicleReticle->reticlePosition.x/2.0) + 0.5,		// go from reticle coordinant system
				(vehicleReticle->reticlePosition.y/2.0) + 0.5,      // to the screen coordinant system.
				INTERSECT_ALL,
				&vehicleReticle->rayIntersection.x,
				&vehicleReticle->rayIntersection.y,
				&vehicleReticle->rayIntersection.z,
				&dplHitDCS,
				&dplHitGeoGroup,
				&dplHitGeometry);
			#endif
		}
		else
		{
			//
			// When pickpoint is off, we should return something sensable
			//
			dplHitInstance = 0;
		}
		//
		// Set the reticle variables based on whether we hit anything or not
		//
		if(dplHitInstance)
		{
			#if PARTICLE_TEST
			DPLIndependantEffect(
			vehicleReticle->rayIntersection,
			1);
			#endif
			vehicleReticle->targetEntity = (Entity *)dpl_GetAppSpecific(dplHitDCS);
			#if USE_TRACKER_STRUCTURE
			this_tracker =	(dpl_tracker *)dpl_GetAppSpecific(dplHitGeoGroup);
			#endif
			if(dpl_GetAppSpecific(dplHitGeoGroup))
			{
				#if USE_TRACKER_STRUCTURE
				vehicleReticle->targetDamageZone = this_tracker->Damage_Zone_Number;
				#else
				vehicleReticle->targetDamageZone = ((int)dpl_GetAppSpecific(dplHitGeoGroup))-1;
				#endif

//				vehicleReticle->targetEntity		= this_tracker->This_Entity;
			}
			else
			{
				vehicleReticle->targetDamageZone = -1;
//				vehicleReticle->targetEntity		= NULL;
			}
		}
		else
		{
			vehicleReticle->targetDamageZone = -1;
			vehicleReticle->targetEntity		= NULL;
		}

		#if PRINT_PICKPOINT_TEST
		//------------------------------------------
		// code for testing and debugging pickpoint
		//------------------------------------------
		// If the object we are targeted on changes, we will print out the data
		if(dplHitInstance)
		{
			if( old_instance != dplHitInstance || old_damage_zone !=  vehicleReticle->targetDamageZone)
			{
				cout<<"instance app specific="<<dpl_GetAppSpecific(dplHitInstance)<<"\n";
				cout<<"DCS app specific="<<dpl_GetAppSpecific(dplHitDCS)<<"\n";
				cout<<"AppSpecific="<<this_tracker<<"\n";
				cout<<"DCS="<<dplHitDCS<<"Instance="<<dplHitInstance<<" geogroup="<<dplHitGeoGroup<<" geometry="<<dplHitGeometry<<"\n";
				cout<<"Intersect="<<vehicleReticle->rayIntersection<<"\n";
				cout<<"Target Entity="<<vehicleReticle->targetEntity<<"\n";
				if(vehicleReticle->targetEntity)
					cout<<"Target Class="<<vehicleReticle->targetEntity->GetClassID()<<"\n";
				#if USE_TRACKER_STRUCTURE
				cout<<"Damage zone #="<<vehicleReticle->targetDamageZone<<" "<<this_tracker->dz_name<<"\n";
				#else
				cout<<"Damage zone #="<<vehicleReticle->targetDamageZone<<"\n";
				#endif
			}
		}
		#endif
	}
	CLEAR_VIDEO_PICKPOINT();
	//
	// Setup the video culling system, this generates the world to eye matrix for
	// the currently linked entity and sets the current frame time to Now()
	//
	#if VIDEO_CULLING
		SET_VIDEO_CULL_SETUP();
		SetupCull();
		CLEAR_VIDEO_CULL_SETUP();
	#endif
	//
	// If we've asked the system to count renderables, setup the total count
	//
	SET_VIDEO_RENDERABLES();
	#if COUNT_RENDERABLES
	int
		renderable_socket_count;
	renderable_socket_count =	0;
	#endif
	//
	// Execute all the dynamic renderables attached to this renderer.
	//
	SChainIteratorOf<Component*> renderable_iterator(&dplRenderableSocket);
	while ((component = renderable_iterator.ReadAndNext()) != NULL)
	{
		#if COUNT_RENDERABLES
		renderable_socket_count++;
		#endif
		SET_VIDEO_ALL_RENDERABLES();
		component->Execute();
		CLEAR_VIDEO_ALL_RENDERABLES();
	}
	//
	// Flip trace for all renderables and report renderable count if enabled
	//
	CLEAR_VIDEO_RENDERABLES();
	#if COUNT_RENDERABLES
	cout<<"dplRenderableSocket "<<renderable_socket_count<<"\n";
	#endif
	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// The block of code below is dead, it remains here only so it can be turned
	// on to confirm no renderables have "leaked" back into an entity
	// dynamic socket.  It should be removed once Red Planet has been scrubbed.
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	#if CROSS_CHECK_ENTITIES
	int
		component_count,
		total_components;
	total_components			=	0;
	//
	// Iterate through all the entities that are interesting to us
	//
	while ((entity = all_iterator->ReadAndNext()) != NULL)
	{
		//
		// If enabled, flip this trace on vehicle or player renderables
		//
		#if defined(TRACE_VIDEO_VEHICLE_RENDERABLES)
			if(	entity->GetClassID() == MechClassID ||
					entity->GetClassID() == VTVClassID	||
					entity->GetClassID() == BTPlayerClassID)
			{
				SET_VIDEO_VEHICLE_RENDERABLES();
			}
		#endif
		//
		// Create an iterator to iterate over all the dynamic video components
		// in this entity.  If renderable counting is turned on, initialize
		// a counter for this entities renderables
		//
		Entity::DynamicVideoSocketIterator component_iterator(entity);
		component_count=0;
		//
		// Iterate through all the dynamic video components for this entity
		//
		while ((component = component_iterator.ReadAndNext()) != NULL)
		{
			//
			// Execute each of the entities components
			//
			SET_VIDEO_ALL_RENDERABLES();
			component->Execute();
			CLEAR_VIDEO_ALL_RENDERABLES();
			component_count++;
		}
		//
		// If renderable counting is on, report how many renderables this entity
		// had and update the totals
		//
		if(component_count != 0)
		{
			cout<<"Class "<<entity->GetClassID()<<" had "<<component_count<<" renderables\n";
			total_components +=component_count;
		}
		//
		// If enabled, flip this trace off for vehicle or player renderables
		//
		#if defined(TRACE_VIDEO_VEHICLE_RENDERABLES)
			if(	entity->GetClassID() == MechClassID ||
					entity->GetClassID() == VTVClassID	||
					entity->GetClassID() == BTPlayerClassID)
			{
				CLEAR_VIDEO_VEHICLE_RENDERABLES();
			}
		#endif
	}
	if(total_components != 0)
		cout<<"Renderables in entity sockets "<<total_components<<"\n";
	#endif
	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// The block of code above is dead, it remains here only so it can be turned
	// on to confirm no renderables have "leaked" back into an entity
	// dynamic socket.  It should be removed once Red Planet has been scrubbed.
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	//
	// Tell the user how many entities are interesting.
	//
	#if COUNT_ENTITIES
	cout<<"There were "<<all_iterator->GetSize()<<" in the all iterator\n";
	#endif
	//
	// Do a batch DCS flush if we need to
	//
	DPLDoDCSBatchFlush();
	//
	// We use the complete cycle handler to take care of calling DrawScene as soon as
	// the renderer completes the the frame it's working on.
	//
	completeCycleNeeded = True;
	CLEAR_VIDEO_RENDERER();

	CompleteCycle();
}
//
//#############################################################################
// DPLDelayDCSFlush and DPLDoDCSBatchFlush queue up a list of DCS pointers
// for later flushing in one big batch.
//#############################################################################
//
void
	DPLRenderer::DPLDelayDCSFlush(
		dpl_DCS	*my_dcs)			// The DCS we want to remember for later
{
	//
	// Make sure the array hasn't become overfilled somehow and make
	// sure the DCS is valid.
	//
	Verify(delayedDCSCount <= DELAY_DCS_FLUSH_ARRAY_SIZE);
	Check_Pointer(my_dcs);
	//
	// If the array is full, flush it out to make space for this DCS
	//
	if(delayedDCSCount == DELAY_DCS_FLUSH_ARRAY_SIZE)
	{
		DPLDoDCSBatchFlush();
	}
	//
	// Add the New DCS to the list
	//
	delayDCSFlushArray[delayedDCSCount++]	=	my_dcs;
}
void
	DPLRenderer::DPLDoDCSBatchFlush()			// Flush the dcs's remembered by DPLDelayDCSFlush
{
	SET_VIDEO_BATCH_FLUSH();
	//
	// Make sure the array hasn't become overfilled somehow
	//
	Verify(delayedDCSCount <= DELAY_DCS_FLUSH_ARRAY_SIZE);
	//
	// Flush the array and reset the counters that go with it
	//
	if(delayedDCSCount != 0)
	{
		delayDCSFlushArray[delayedDCSCount] = NULL;
		dpl_FlushDCSArticulations(delayDCSFlushArray);
		delayedDCSCount							= 0;
	}
	CLEAR_VIDEO_BATCH_FLUSH();
}
//
//#############################################################################
// DPLReportFreeMemory writes current free memory in graphics card to any
// output stream.
//#############################################################################
//
void
	DPLRenderer::DPLReportFreeMemory(ostream &output)
{
	output << "Free memory in card:  " << dpl_FreeMemory() << " bytes." << endl;
	return;
}
//
//#############################################################################
// DPLReportPerfStats writes performance statistics to cout (stdout).
//#############################################################################
//
void
	DPLRenderer::DPLReportPerfStats(ostream &output)
{
	//-----------------------------------------------------
	// HACK - copied from camera.c must re-copy if changed
	//
	// case '?':
	//		printf ("sect time %d dcs 0x%x inst 0x%x\n",
	//			 __sect_time, sect_dcs, sect_inst );
	//-----------------------------------------------------
	output << "sect time " << __sect_time <<
		" dcs and inst not available" << endl;

	//------------------------------------------------------------
	// HACK - copied from dpl_vpx.c must re-copy if changed
	//
	// void dpl_PerfStats(void)
	// 	printf ( "cull %d draw %d frame %d pxpl %d prims %d\n",
	// 			__last_cull_time,
	// 			__last_draw_time,
	// 			__last_frame_time,
	// 			__last_pxpl_time,
	// 			__last_frame_prims );
	//------------------------------------------------------------
	output <<
		"cull "   << __last_cull_time <<
		" draw "  << __last_draw_time <<
		" frame " << __last_frame_time <<
		" pxpl "  << __last_pxpl_time <<
		" prims " << __last_frame_prims <<
		endl;
	if(statistics_started)
	{
		ReportStatistics();
	}
	else
	{
		ResetStatistics();
      statistics_started	=	True;
	}
	return;
}
//
//#############################################################################
// DPLToggleWireframe toggles the state of dpl global wireframe.
//#############################################################################
//
void
	DPLRenderer::DPLToggleWireframe()
{
	static Logical wireframe_on = 0;

	if ((wireframe_on ^= 1) != 0)
	{
		DEBUG_STREAM << "wireframe ON" << endl;
		dpl_SetRenderProperty(dpl_render_prop_wireframe, dpl_render_value_on, NULL );
	}
	else
	{
		DEBUG_STREAM << "wireframe OFF" << endl;
		dpl_SetRenderProperty(dpl_render_prop_wireframe, dpl_render_value_off, NULL );
	}
}
//
//#############################################################################
// DPLTogglePVision toggles the state of dpl "predator" vision.
//#############################################################################
//
void
	DPLRenderer::DPLTogglePVision()
{
	static Logical pvision_on = 0;
	dpl_EXPLOSION_EFFECT_INFO sfx_info;

	sfx_info.x = sfx_info.y = sfx_info.z = 0;
	if ((pvision_on ^= 1) != 0)
	{
		DEBUG_STREAM << "pvision ON" << endl;
		sfx_info.type = -1;
	}
	else
	{
		DEBUG_STREAM << "pvision OFF" << endl;
		sfx_info.type = -2;
	}
	dpl_Effect(dpl_effect_type_explosion, NULL, &sfx_info);
	return;
}
//
//#############################################################################
// DPLFrameDump writes screen image to targa file.
//#############################################################################
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// oneD_filter()  antialiased framegrab support
void
	oneD_filter(
		uint32 *linebuffer,
		unsigned short *rframe,
		unsigned short *gframe,
		unsigned short *bframe,
		int32 y,
		int32 x_size
	)
{
	int32
		pos,
		i,
		r, g, b;
	unsigned char
		*clinebuf = (unsigned char *)linebuffer;

	pos = y * x_size;

	for (i=0; i<x_size; ++i)
	{
		r = rframe[pos];
		g = gframe[pos];
		b = bframe[pos];
		++pos;

		*clinebuf++ = (unsigned char)((r >> 8) & 0xff);
		*clinebuf++ = (unsigned char)((g >> 8) & 0xff);
		*clinebuf++ = (unsigned char)((b >> 8) & 0xff);
	}
	return;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// dump_frame_buffer()
void
	dump_frame_buffer(
		dpl_VIEW *eye,
		int32 x_size,
		int32 y_size,
		Logical antialias
	)
{
	//do not check eye here

	//----------------------------------------
	// control variable and operating buffers
	//----------------------------------------
	static uint32
		dumped_frames = 0,
		*line_buffer = NULL;
	static unsigned short
		*rframebuffer = NULL,
		*gframebuffer = NULL,
		*bframebuffer = NULL;

	if (eye == NULL)
	{
		//--------------------------
		// release allocated memory
		//--------------------------
		if (line_buffer)
		{
			Unregister_Pointer(line_buffer);
			delete line_buffer;
		}
		if (rframebuffer)
		{
			Unregister_Pointer(rframebuffer);
			delete rframebuffer;
		}
		if (gframebuffer)
		{
			Unregister_Pointer(gframebuffer);
			delete gframebuffer;
		}
		if (bframebuffer)
		{
			Unregister_Pointer(bframebuffer);
			delete bframebuffer;
		}
		return;
	}
	Check_Pointer(eye);

	//-----------------------------------------------------------------
	// AA kernel definition:
	//
	//		normalized screen coordinates are -1.0 .. +1.0
	//		these map (in NTSC) to 705 and 512.
	//		So a pixel in x is
	//			2.0 / 704 = 2.841e-3
	//		in y			 = 3.906e-3
	//		for the re-use 5-sample kernel we displace by 0.5 pixels ...
	//-----------------------------------------------------------------
	static const int32 kernel_size = 4;

	static const float xk = 2.0f / x_size;
	static const float yk = 2.0f / y_size;
	static const float ox = xk / 4.0f;
	static const float oy = yk / 4.0f;
	static const float jx = 0.0f;		// ox / 4.0f
	static const float jy = 0.0f;    // oy / 4.0f

	static const float x_kernel[4] = { -(ox+jx), ox-jx, jx-ox,    jx+ox };
	static const float y_kernel[4] = {   oy-jy,  oy+jy, -(jy+oy), jy-oy };
	static const int   kernel_weights[4] = { 64, 64, 64, 64 };

	//---------------------
	// operating variables
	//---------------------
	int32 passes	= (antialias)?kernel_size:1;
	int32 size		= x_size * y_size;
	int32 frameptr;
	int32 i, x, y;

	DEBUG_STREAM << "Dump frame buffer (antialias=" <<
		((antialias)?"True":"False") << ") - press Esc to cancel." << endl;

	//----------------------------
	// allocate operating buffers
	//----------------------------
	if (line_buffer == NULL)
	{
		//-----------------------------------------------------------------------
		// NOTE - must call dump_frame_buffer(NULL, NULL, NULL, NULL) to release
		//-----------------------------------------------------------------------
		line_buffer  = new uint32[1024];
		Register_Pointer(line_buffer);
		rframebuffer = new unsigned short[size];
		Register_Pointer(rframebuffer);
		gframebuffer = new unsigned short[size];
		Register_Pointer(gframebuffer);
		bframebuffer = new unsigned short[size];
		Register_Pointer(bframebuffer);
	}

	//------------------------------
	// clear out r g b framebuffers
	//------------------------------
	frameptr = 0;
	for (i=0; i<size; ++i)
	{
		rframebuffer[frameptr] = 0x0;
		gframebuffer[frameptr] = 0x0;
		bframebuffer[frameptr] = 0x0;
		++frameptr;
	}

	dpl_RedrawScene();
	dpl_RedrawScene();

	//--------------------------------
	// perform AA framecapture passes
	//--------------------------------
	int this_weight;
	float32 x0, y0, x1, y1, zeye;

	for (i=0; i<passes; ++i)
	{
		DEBUG_STREAM << "  pass " << (i+1) << " of " << passes << " ";

		//------------
		// jitter eye
		//------------
		dpl_GetViewProjection(eye, &x0, &y0, &x1, &y1, &zeye);
		dpl_SetViewProjection(eye,
										x0 + x_kernel[i],
										y0 + y_kernel[i],
										x1 + x_kernel[i],
										y1 + y_kernel[i], zeye);
		dpl_FlushView(eye);

		//--------------
		// redraw frame
		//--------------
		dpl_RedrawScene();
		dpl_RedrawScene();

		//--------------------
		// capture accumulate
		//--------------------
		this_weight = kernel_weights[i];
		frameptr = 0;

		for (y=0; y<y_size; ++y )
		{
			unsigned char *linep = (unsigned char *)&line_buffer[0];

			//------------------------------
			// Esc key cancelles frame dump
			//------------------------------
			if (kbhit())
			{
				if (getch() == 27)
				{
					DEBUG_STREAM << endl << "cancelled." << endl;
					return;
				}
			}

			if ((y & 15) == 0)
				{ DEBUG_STREAM << "."; }

			dpl_ReadFrameStore(eye, (uint32 *)linep, 0, y, x_size, 1);

			for (x=0; x<x_size; ++x)
			{
				rframebuffer[frameptr] += (unsigned short)(linep[2] * this_weight);
				gframebuffer[frameptr] += (unsigned short)(linep[1] * this_weight);
				bframebuffer[frameptr] += (unsigned short)(linep[0] * this_weight);
				++frameptr;
				linep += 4;
			}
		}
		DEBUG_STREAM << endl;
	}

	//----------------------------------
	// write frame buffer to targa file
	//----------------------------------
	FILE *fp;
	char fname[64];
	unsigned char tga_hdr[18];

	sprintf(fname, "dump%d.tga", dumped_frames);

	tga_hdr[0] = 0x00;
	tga_hdr[1] = 0x00;
	tga_hdr[2] = 0x02;
	tga_hdr[3] = 0x00;

	tga_hdr[4] = 0x00;
	tga_hdr[5] = 0x00;
	tga_hdr[6] = 0x00;
	tga_hdr[7] = 0x00;

	tga_hdr[8] = 0x00;
	tga_hdr[9] = 0x00;
	tga_hdr[10] = 0x00;
	tga_hdr[11] = 0x00;

	tga_hdr[12] = (unsigned char)(x_size & 0xff);
	tga_hdr[13] = (unsigned char)((x_size >> 8) & 0xff);
	tga_hdr[14] = (unsigned char)(y_size & 0xff);
	tga_hdr[15] = (unsigned char)((y_size >> 8) & 0xff);

	tga_hdr[16] = 0x18;
	tga_hdr[17] = 0x00;

	DEBUG_STREAM << "Writing image to file '" << fname << "' . . . ";
	fp = fopen(fname, "wb");
	fwrite(tga_hdr, 18, 1, fp);

	for (y=0; y<y_size; ++y )
	{
		oneD_filter(line_buffer,
						rframebuffer,
						gframebuffer,
						bframebuffer,
						(y_size-1) - y,
						x_size);
		fwrite(line_buffer, x_size*3, 1, fp);
	}
	fclose(fp);
	DEBUG_STREAM << "done." << endl;

	++dumped_frames;	// increment for next frame dump
	return;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// DPLRenderer::DPLFrameDump()
void
	DPLRenderer::DPLFrameDump(Logical antialias)
{
	dump_frame_buffer(dplMainView, x_size, y_size, antialias);
	return;
}
//
//#############################################################################
// CompleteCycle is a polling routine that is called in the background to see
// if the renderer is done with a frame, if it is this routine will complete
// the cycle by doing a final batch DCS flush and call do DrawScene to start
// the renderer on the next frame.
//#############################################################################
//
Logical
	DPLRenderer::CompleteCycle()
{
	Time
		current;

	//
	// Only do this if it hasn't been done yet.
	//
	if(completeCycleNeeded)
	{
		SET_VIDEO_RENDERER_FRAME_DONE();
		//
		// See if the scene is complete (lets us start another without blocking)
		//
		if(dpl_DrawSceneComplete())
		{
			//
			// Do a batch flush just in case some objects got created during this
			// phase of rendering.
			//
			DPLDoDCSBatchFlush();
			//
			// Start the renderer on the next scene
			//
			dpl_DrawScene();
			//
			// Record statistics
			//
			if(statistics_started)
			{
				total_cull 			+= __last_cull_time;
				total_draw 			+= __last_draw_time;
				total_pixelplanes += __last_pxpl_time;
				total_frame_time 	+= __last_frame_time;
				frame_count++;
				if(__last_frame_time <= target_frame_time)
					target_frame_count++;
				if(currentFrameTime > report_time)
				{
					ReportStatistics();
					report_time = currentFrameTime + 60.0f;
				}
			}
			#if DEBUG_LEVEL > 0
			//
			// Code to try and measure average frame rate
			// Only active for builds above d0
			//
			current	=	Now();
			FrameCount	++;
			if((current - StartSample) > 10.0f)
			{
				float	frame_rate;
				frame_rate				=	FrameCount / (current-StartSample);
				if(frame_rate < 28.0f)
					printf("%4.2f fps\n", frame_rate);
				StartSample				=	Now();
				FrameCount				=	0;
			}
			#endif
			//
			// We've started the next frame, so indicate we don't need to do this again.
			//
			completeCycleNeeded = False;
			CLEAR_VIDEO_RENDERER_FRAME_DONE();
			return True;
		}
		CLEAR_VIDEO_RENDERER_FRAME_DONE();
	}
	return False;
}
//
//#############################################################################
// DPLIndependantPFX lets you start up an effect in absolute space that is
// run to termination automatically by the renderer.
//#############################################################################
//
void
	DPLRenderer::DPLIndependantPFX(
		Point3D									location,			// Location in space to trigger the effect
		dpl_PARTICLESTART_EFFECT_INFO		*psfx_definition,	// Description of the pfx
		dpl_DCS									*my_DCS,          // Optional DCS to link to
		int										subid)				// inserted into third byte of effect id.

{
	// we put our id into the lower 16 bits because (at least) the upper 8 bits are flags
	// HACK...for testing pfx bug

	dpl_PARTICLESTART_EFFECT_INFO
		tempParticle;
	tempParticle 				=	*psfx_definition;
	tempParticle.identifier	=	(psfx_definition->identifier & 0xffff0000) | GetUniqueID() | ((subid<<16) & 0x00ff0000);
	tempParticle.px			=	location.x;
	tempParticle.py			=	location.y;
	tempParticle.pz			=	location.z;
	dpl_Effect ( dpl_effect_type_particlestart, my_DCS, &tempParticle );
//	cout<<"psfx identifier used was "<<tempParticle.identifier<<"\n";
}
//
//#############################################################################
// DPLIndependantEffect lets you start up an effect in absolute space that is
// run to termination automatically by the renderer.
//#############################################################################
//
void
	DPLRenderer::DPLIndependantEffect(
		Point3D	location,
		int		effect_number,
		dpl_DCS	*my_DCS,          // Optional DCS to link to
		int		subid)
{
	if(effect_number >= 1000)
	{
   	effect_number -= 1000;
		if(effect_number < 0 || effect_number > MAX_PSFX_COUNT-1)
		{
			Fail("PSFX id number was not in the allowed range");
		}
		DPLIndependantPFX(location,myPSFXDescriptons[effect_number],my_DCS,subid);
		return;
	}
	dpl_EXPLOSION_EFFECT_INFO
		my_explosion;

	my_explosion.type		=	effect_number;
	my_explosion.x			=	location.x;
	my_explosion.y			=	location.y;
	my_explosion.z			=	location.z;
	dpl_Effect ( dpl_effect_type_explosion, my_DCS, &my_explosion );
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	DPLRenderer::SetViewAngle(Degree new_angle)
{
	Check(this);
	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Convert From Degree To Radian
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	Radian view_angle;
	view_angle = new_angle;
	viewAngle = view_angle;
	viewRatio = tan(viewAngle/2.0f);
	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Calc Aspect Ratio and Set View Projection
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	aspectRatio	=	(float) y_size / (float) x_size;
	dpl_SetViewProjection	( dplMainView, -1.0f, -aspectRatio, 1.0f, aspectRatio, 1.0f/viewRatio);
	dpl_FlushView(dplMainView);
}
//
//#############################################################################
// Startup the implementation of the Division video renderer
//#############################################################################
//
void
	DPLRenderer::LoadMissionImplementation(Mission	*mission)
{
	Check(this);
	Tell("DPLVideoRenderer::StartImplementation has been called\n");
	DPLReadEnvironment(mission);

	LoadNameBitmaps();
}
//##############################################################################
// Name Bitmap Support
//

void
	DPLRenderer::SortAndReloadNameBitmaps()
{
	Check(this);

	uint32 *worst_case_texels = MakeBitSliceStorage();
	Register_Pointer(worst_case_texels);

	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Get the Entity Group of Players
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	EntityGroup *player_group = application->GetEntityManager()->FindGroup("Players");
	Check(player_group);
	ChainIteratorOf<Node*> player_iterator(player_group->groupMembers);
	Player *current_player;
   BitMap *name_bitmap;
	while((current_player = (Player*) player_iterator.ReadAndNext() ) != NULL)
	{
		Check(current_player);
		name_bitmap = application->GetCurrentMission()->
			GetLargeNameBitmap(current_player->playerBitmapIndex);
		if (name_bitmap && current_player->IsScoringPlayer())
		{
			Check(name_bitmap);
			LoadBitSliceTexture(
				name_bitmap,									// MUNGA 1 bit bitmap to load
				(current_player->playerRanking + 1), 	// Position to load bitmap into
				worst_case_texels								// 8192 longword array for storage of the texmap
			);
		}
	}
   LoadOrdinalBitmaps(worst_case_texels);
	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Flush all bitmaps down to the card
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	FlushBitSliceTexture(worst_case_texels);
	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Free the temporary memory the texmaps used
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	Unregister_Pointer(worst_case_texels);
	free(worst_case_texels);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	DPLRenderer::LoadOrdinalBitmaps(unsigned int *worst_case_texels)
{
	Check(this);
   //
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Check if we have a Director on this machine,
	// if so, create load the ordinal Bitmaps only
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	//
	if (
			(application->GetMissionPlayer()->GetInstance() == CameraDirector::MasterInstance) &&
			(application->GetMissionPlayer()->IsDerivedFrom(CameraDirector::ClassDerivations))
	)
	{
		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Make Sure index is in the right place
		// in case < 8 players!!!!
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		int index = 9;
		for(int ii=1;ii<5;++ii)
		{
			//
			//~~~~~~~~~~~~~~~~~~
			// Index Starts at 1
			//~~~~~~~~~~~~~~~~~~
			//
			BitMap *ordinal_bitmap = application->GetCurrentMission()->GetOrdinalBitmap(ii);
			Check(ordinal_bitmap);
			LoadBitSliceTexture(
				ordinal_bitmap,				// MUNGA 1 bit bitmap to load
				index,      					// Position to load bitmap into
				worst_case_texels          // 8192 longword array for storage of the texmap
			);
			++index;
		}
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
unsigned int*
	DPLRenderer::MakeBitSliceStorage()
{
	Check(this);
	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Allocate some temporary memory
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	uint32 *worst_case_texels = new uint32[128*64];
	if(!worst_case_texels)
	{
		Fail("Could not allocate RAM for worst case texels\n");
	}
	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Zero Out Memory so Empty Texture space is Black
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	for(int ii=0;ii<8192;++ii)
	{
		worst_case_texels[ii] = 0;
	}

	return worst_case_texels;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	DPLRenderer::LoadNameBitmaps()
{
	Check(this);

	uint32 *worst_case_texels = MakeBitSliceStorage();
	Register_Pointer(worst_case_texels);

	int player_count;
	player_count = application->GetCurrentMission()->GetPlayerCount();
	Check(application);
	Check(application->GetCurrentMission());

	BitMap *name_bitmap;
	for (int ii=0;ii<player_count;++ii)
	{
		name_bitmap =
			application->GetCurrentMission()->GetLargeNameBitmap(ii+1);
		if (name_bitmap)
		{
			Check(name_bitmap);
			LoadBitSliceTexture(
				name_bitmap,					// MUNGA 1 bit bitmap to load
				ii+1, 							// Position to load bitmap into
				worst_case_texels				// 8192 longword array for storage of the texmap
			);
		}
	}
	LoadOrdinalBitmaps(worst_case_texels);
	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Flush all bitmaps down to the card
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	FlushBitSliceTexture(worst_case_texels);
	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Free the temporary memory the texmaps used
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	Unregister_Pointer(worst_case_texels);
	delete[] worst_case_texels;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	DPLRenderer::ShutdownImplementation()
{
	Tell("DPLVideoRenderer::StopImplementation has been called\n");
	dpl_EnableSyncOnCreate();
	SChainIteratorOf<InnerProjectileRenderable*> projectile_iterator(&projectile_list);
	TreeIteratorOf<DPLObjectCacheLine*, CString> cache_iterator(&dplObjectCacheSocket);
#if defined(LAB_ONLY)
	cout<<"max projectiles "<<projectile_iterator.GetSize()<<"\n";
	cout<<"max cached objects "<<cache_iterator.GetSize()<<"\n";
#endif
//	cout<<"DPL renderer has "<<dpl_FreeMemory()<<" free\n";
}
void
	DPLRenderer::SuspendImplementation()
{
	Tell("DPLVideoRenderer::SuspendImplementation has been called\n");
}
void
	DPLRenderer::ResumeImplementation()
{
	Tell("DPLVideoRenderer::ResumeImplementation has been called\n");
}
//-----------------------------------------------------------------------------
//--------------------------Id's for psfx effects---------------------------------
//-----------------------------------------------------------------------------
int
	DPLRenderer::GetUniqueID()
{
	myUniqueID = (myUniqueID + 1) & 0x0000ffff;
	return(myUniqueID);
}
//-----------------------------------------------------------------------------
//--------------------------MUNGA caching of objects---------------------------
//-----------------------------------------------------------------------------
dpl_OBJECT*
	DPLRenderer::GetCachedObject(
		const CString 	&object_name)					// Name of the object we want to get
{
	dpl_OBJECT
		*object_pointer;
	//
	// Get this object from the cache by name
	//
	DPLObjectCacheLine *my_cache_line;

	if ((my_cache_line = dplObjectCacheSocket.Find(object_name)) == NULL)
		return NULL;

	//
	// Remove the cache line from the cache
	//
	object_pointer = my_cache_line->objectPointer;
	delete my_cache_line;

	//
	// Return the object pointer
	//
	return(object_pointer);
}

void
	DPLRenderer::PutCachedObject(
		const CString  &object_name,					// Name of the object we will cache
		dpl_OBJECT		*object_pointer)				// pointer to the object being cached
{
	//
	// Create the cache line data structure
	//
	#if 0
	typedef PlugOf<dpl_OBJECT*> DPLObjectCacheLine;

	DPLObjectCacheLine *my_cache_line = new DPLObjectCacheLine(object_pointer);
	#endif

	DPLObjectCacheLine	*my_cache_line =
		new DPLObjectCacheLine(
			object_name,
			object_pointer);
	Register_Object(my_cache_line);

	//
	// Store this cache line in the cache
	//
	dplObjectCacheSocket.AddValue(my_cache_line, object_name);

}
//-----------------------------------------------------------------------------
//--------------------------Projectile Speedup---------------------------------
//-----------------------------------------------------------------------------
InnerProjectileRenderable*
	DPLRenderer::GetProjectile(
		dpl_OBJECT				*graphical_object,	// object to hang on the DCS, may be a list later <NULL>
		dpl_ZONE					*this_zone)				// DPL Zone this stuff will live in (for culling)
{
	InnerProjectileRenderable
		*return_projectile;
	dpl_INSTANCE
		*temp_instance;
	//
	// Are there projectiles in the list?
	//
	SChainIteratorOf<InnerProjectileRenderable*> iterator(&projectile_list);
	if ((return_projectile = iterator.GetCurrent()) != NULL)
	{
		// Yes, remove it from the list set it's instance and return it
		iterator.Remove();
		temp_instance = return_projectile->GetInstance();
		if(graphical_object != dpl_GetInstanceObject(temp_instance))
		{
			dpl_SetInstanceObject		(temp_instance,	graphical_object);
			dpl_FlushInstance				(temp_instance);
		}
		return(return_projectile);
	}
	else
	{
		// no, make a new one and return that
		InnerProjectileRenderable	*projectile =
			new InnerProjectileRenderable(
				graphical_object,		// object to hang on the DCS, may be a list later <NULL>
				this_zone);				// DPL Zone this stuff will live in (for culling)
		return(projectile);
	}

}
void
	DPLRenderer::ReleaseProjectile(
		InnerProjectileRenderable* inner_projectile)
{
	// add the projectile back to the list
	projectile_list.Add(inner_projectile);
}
//-----------------------------------------------------------------------------
//--------------------------Joint to DCS translator----------------------------
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// DPLJointToDCSTranslator is a class that contains a joint number to dcs
// pointer translation table.  This lets a renderer find the dcs that goes
// with a particular segment.
//
	DPLJointToDCSTranslator::DPLJointToDCSTranslator(
		Entity	*entity,						// The entity to translate
		dpl_DCS	*dcs_array[])				// Array of DCS's to translate
{
	JointedMover
		*my_jointed_mover;
	//
	// Make sure this is a jointed mover, then cast the entity pointer over
	//
	if (entity->IsDerivedFrom(JointedMover::ClassDerivations))
	{
		my_jointed_mover = (JointedMover*)entity;
	}
	else
	{
		Fail("DPLJointToDCSTranslator was called on an entity NOT a JointedMover\n");
	}
	//
	// Find out how many joints the entity has, then allocate enough RAM for a
	// DCS pointer to every joint.
	//
	JointSubsystem* joint_subsystem = my_jointed_mover->GetJointSubsystem();
	Check(joint_subsystem);
	translation_array = new (dpl_DCS(*[joint_subsystem->GetJointCount()]));
	Register_Pointer(translation_array);
	//
	// Setup to iterate the entity segment table
	//
	EntitySegment::SegmentTableIterator segment_iterator(my_jointed_mover->segmentTable);
	EntitySegment *current_segment;
	while ((current_segment = segment_iterator.ReadAndNext()) != NULL)
	{
		//
		// For each segment, see if it has a joint index, if it does, put the
		// DCS for that joint into the translation array
      //
		Check(current_segment);
		int joint_index = current_segment->GetJointIndex();
		if(joint_index != -1)
		{
			translation_array[joint_index] = dcs_array[current_segment->GetIndex()];
		}
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DPLJointToDCSTranslator::~DPLJointToDCSTranslator()
{
	Unregister_Pointer(translation_array);
	delete[] translation_array;
	translation_array = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Logical
	DPLJointToDCSTranslator::TestInstance() const
{
	return True;
}
//-----------------------------------------------------------------------------
//--------------------------Resource creation support--------------------------
//-----------------------------------------------------------------------------
//#############################################################################
//#############    DPLRenderer::CreateModelVideoStreamResource    #############
//#############################################################################

ResourceDescription::ResourceID
	DPLRenderer::CreateModelVideoStreamResource(
		ResourceFile *resource_file,
		const char *model_name,
		NotationFile *model_file,
		const ResourceDirectories * /*directories*/
	)
{
	ResourceDescription *res_description =
		resource_file->FindResourceDescription(
			model_name,
			ResourceDescription::VideoModelResourceType
		);

	if (res_description == NULL)
	{
		NameList *video_entries = model_file->MakeEntryList("video");

		if (video_entries != NULL)
		{
			Register_Object(video_entries);

			//----------------------------------------------
			// parse video model data and store in resource
			//----------------------------------------------
			Tell("Building video resource for model '" << model_name << "'.\n");

			NameList::Entry
				*entry,
				*next_entry;
			const char
				*entry_name,
				*entry_pointer,
				*argument_start;
			int
				length;
			char
				argument[40],
				*argument_pointer;
			L4VideoObject::ResourceType
				resource_type;
			Enumeration	//(see L4VideoObject::RendererModes)
				renderer_modes;
			enum
			{
				parsing_filename,
				seeking_switch,
				parsing_switch,
				parsing_billboard
			}	state;
			L4VideoObject
				*video_object;
			L4VideoObjectWrapper
				*video_wrapper;
			ChainOf<L4VideoObjectWrapper*>
				video_chain(NULL);
			char
				*video_stream,
				*video_pointer;
			int
				object_count;
			long
				object_size,
				stream_length;

			//------------------------------------------------
			// parse each entry in [video] page of model file
			//------------------------------------------------
			next_entry = video_entries->GetFirstEntry();
			while (next_entry)
			{
				entry = next_entry;
				Check(entry);
				next_entry = entry->GetNextEntry();	// so 'continue' works
				entry_name = entry->GetName();
				if (entry_name && *entry_name)
				{
					//--------------------------------------------
					// could be "skeleton" or "object" or comment
					//--------------------------------------------
					if (strcmp(entry_name, "object") == 0)
					{
						resource_type = L4VideoObject::Object;
					}
					else if (strcmp(entry_name, "rubble") == 0)
					{
						resource_type = L4VideoObject::Rubble;
					}
					else if (strcmp(entry_name, "skeleton") == 0)
					{
						resource_type = L4VideoObject::Skeleton;
					}
					else if (Comment_Line(entry_name))
					{
						// do nothing - skip comments
						continue;
					}
					else if (strncmp(entry_name, "skeleton", 8) == 0)
					{
						// do nothing - skip temporary skeleton entries
						continue;
					}
					else if (strncmp(entry_name, "destroyed", 9) == 0)
					{
						// do nothing - skip destroyed skeleton entries
						continue;
					}
					else if (strncmp(entry_name, "dzm", 3) == 0)
					{
						// do nothing - skip damage zone material entries
						continue;
					}
					else
					{
//						resource_type = L4VideoObject::Unknown;
						DEBUG_STREAM << "Unknown entry '" << entry_name <<
							"' in model '" << model_name << "' ignored." << endl;
						continue;
					}
					//---------------------------------
					// parse and process each argument
					//---------------------------------
					entry_pointer = entry->GetChar();
					while (entry_pointer && *entry_pointer)
					{
						//-------------------------
						// skip leading whitespace
						//-------------------------
						while (*entry_pointer != '\0' && isspace(*entry_pointer))
						{
							++entry_pointer;
						}
						//---------------------------
						// exit loop if nothing left
						//---------------------------
						if (*entry_pointer == '\0')
						{
							break;
						}
						//-------------------------------------------
						// parse the next argument into local buffer
						//-------------------------------------------
						argument_start	= entry_pointer;
						while (*entry_pointer != '\0' && !isspace(*entry_pointer))
						{
							++entry_pointer;
						}
						length = entry_pointer - argument_start;
						Verify( length < sizeof(argument) );
						strncpy(argument, argument_start, length);
						argument[length] = '\0';
						//----------------------
						// parse local argument
						//----------------------
						argument_pointer 	= argument;
						renderer_modes		= L4VideoObject::Normal;
						state 				= parsing_filename;
						while (*argument_pointer != '\0')
						{
							switch (state)
							{
								case parsing_filename:
									switch (*argument_pointer)
									{
										#if 0
										case	'.':
											if ((resource_type == L4VideoObject::Object) ||
												 (resource_type == L4VideoObject::Rubble))
											{
												*argument_pointer = '\0';	// terminate filename
												state = seeking_switch;
											}
											break;
										#endif
										case	'/':
											*argument_pointer = '\0';	// terminate filename
											state = parsing_switch;
											break;
										default:
											// later check for valid filename character...
											break;
									}
									break;
								case seeking_switch:
									if (*argument_pointer == '/')
										{ state = parsing_switch; }
									break;
								case parsing_switch:
									switch (*argument_pointer)
									{
										case	'b':
										case	'B':
											state = parsing_billboard;
											break;
										case	'i':
										case	'I':
											renderer_modes |= L4VideoObject::IntersectImmune;
											state = seeking_switch;
											break;
										default:
											state = seeking_switch;
											break;
									}
									break;
								case parsing_billboard:
									switch (*argument_pointer)
									{
										case	'x':
										case	'X':
											renderer_modes |= L4VideoObject::BillboardXAxis;
											break;
										case	'y':
										case	'Y':
											renderer_modes |= L4VideoObject::BillboardYAxis;
											break;
										case	'z':
										case	'Z':
											renderer_modes |= L4VideoObject::BillboardZAxis;
											break;
										case	'/':
											state = parsing_switch;
											break;
										default:
											state = seeking_switch;
											break;
									}
									break;
								// no default:
							}
							++argument_pointer;
						}
						//-----------------------------------------------
						// create video resource object and add to chain
						//-----------------------------------------------
						video_object =
							new L4VideoObject(
								argument,
								resource_type,
								renderer_modes
							);
						Register_Pointer(video_object);	// not _Object!

						video_wrapper =
							new L4VideoObjectWrapper(
								video_object,
								True	// delete object when done
							);
						Register_Object(video_wrapper);

						//Tell("  adding video object '"<<argument<<"' type ");
						//Tell(resource_type<<" mode 0x"<<hex<<renderer_modes<<"\n");
						video_chain.Add(video_wrapper);

					}	// arguments processed for this entry
				}
			}	// all arguments processed

			//----------------------------------------------
			// convert chain of video resources into stream
			//----------------------------------------------
			ChainIteratorOf<L4VideoObjectWrapper*>
				video_iterator(video_chain);

			object_count = video_iterator.GetSize();

			object_size = sizeof(L4VideoObject);
			stream_length = sizeof(int) + object_count * object_size;

			video_stream = new char[stream_length];
			Register_Pointer(video_stream);

			video_pointer = video_stream;
			*((int *)video_pointer) = object_count;
			video_pointer += sizeof(int);

			Tell("  count " <<dec<< object_count << ", resource size " << stream_length << "\n");

			while ((video_wrapper = video_iterator.ReadAndNext()) != NULL)
			{
				video_object = video_wrapper->GetVideoObject();
				*((L4VideoObject *)video_pointer) = *video_object;
				video_pointer += object_size;
			}
			//--------------------------------------------------
			// store stream of video resources in resource file
			//--------------------------------------------------
			res_description =
				resource_file->AddResource(
					model_name,
					ResourceDescription::VideoModelResourceType,
					1,
					ResourceDescription::Preload,
					video_stream,
					stream_length
				);
			//--------------------------
			// release allocated memory
			//--------------------------
			// video_chain
			L4VideoObjectWrapper::DeleteVideoObjectChain(&video_chain);
			// video_stream
			Unregister_Pointer(video_stream);
			delete video_stream;
			// video_entries
			Unregister_Object(video_entries);
			delete video_entries;
		}
		else
		{
			//-------------------------------
			// no [video] page in model file
			//-------------------------------
			return (ResourceDescription::NullResourceID);
		}
	}
	return (res_description->resourceID);
}
//===========================================================================//

