/*
**	Command & Conquer Renegade(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/***********************************************************************************************
 ***              C O N F I D E N T I A L  ---  W E S T W O O D  S T U D I O S               ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : WWPhys                                                       *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/wwphys/phys.cpp                              $*
 *                                                                                             *
 *                       Author:: Greg Hjelstrom                                               *
 *                                                                                             *
 *                     $Modtime:: 2/21/02 4:01p                                               $*
 *                                                                                             *
 *                    $Revision:: 79                                                          $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


#include "phys.h"
#include "colmathaabox.h"
#include "rendobj.h"
#include "rinfo.h"
#include "assetmgr.h"
#include "ww3d.h"
#include "wwdebug.h"
#include "pscene.h"
#include "cullsys.h"
#include "chunkio.h"
#include "saveload.h"
#include "persistfactory.h"
#include "physcoltest.h"
#include "lightenvironment.h"
#include "umbrasupport.h"
#if (UMBRASUPPORT)
#include <umbra.hpp>
#endif

#include "dx8wrapper.h"
#include "dx8vertexbuffer.h"
#include "dx8indexbuffer.h"


const float DEBUG_RENDER_DIST2 = (50.0f*50.0f);
const float SUN_CHECK_DISTANCE = 50.0f;	//If a ray this long doesn't intersect, you can see the sun...


/*
** create_render_obj_from_filename
*/
RenderObjClass * create_render_obj_from_filename( const char * filename )
{
	StringClass	render_obj_name(filename,true);
	if ( ::strchr( filename, '\\' ) != 0 ) {
		render_obj_name = ::strrchr( filename, '\\' ) + 1;
	}
	render_obj_name.Erase( render_obj_name.Get_Length() - 4, 4 );

	RenderObjClass *model = WW3DAssetManager::Get_Instance()->Create_Render_Obj( render_obj_name );
	if ( model == NULL ) {
		WWDEBUG_SAY(("Failed to create %s from %s\n", (const char *)render_obj_name, filename));
	}
	return model;
}


/***********************************************************************************************
**
** PhysClass Implementation
**
***********************************************************************************************/

/*
** Chunk ID's used by PhysClass
*/
enum
{
	PHYS_CHUNK_VARIABLES				= 0x00660055,
	PHYS_CHUNK_MODEL,

	PHYS_VARIABLE_CULLABLE_PTR		= 0x00,
	PHYS_VARIABLE_WIDGETUSER_PTR,
	PHYS_VARIABLE_EDITABLE_PTR,
	PHYS_VARIABLE_FLAGS,
	PHYS_VARIABLE_NAME,
	PHYS_VARIABLE_OBSERVER,
	PHYS_VARIABLE_DEFID,
	PHYS_VARIABLE_INSTANCEID,
};




PhysClass::PhysClass(void) :
	Flags(DEFAULT_FLAGS),
	Model(NULL),
	Observer(NULL),
	Definition(NULL),
	InstanceID(0),
	VisObjectID(0),
	LastVisibleFrame(0),	// JANI TEMP TEST
	SunStatusLastUpdated(0),
	StaticLightingCache(NULL)
#if (UMBRASUPPORT)
	,UmbraObject(NULL)
#endif
{
#if (UMBRASUPPORT)
	UmbraObject = new Umbra::Object(UmbraSupport::Peek_Dummy_Sphere());
	UmbraObject->setUserPointer(this);
	UmbraSupport::Install_Umbra_Object(this);
#endif
}

PhysClass::~PhysClass(void)
{
	if (Model) {
		Model->Release_Ref();
		Model = NULL;
	}
	if (StaticLightingCache) {
		delete StaticLightingCache;
	}
#if (UMBRASUPPORT)
	if (UmbraObject) {
		UmbraSupport::Remove_Umbra_Object(this);
		UmbraObject->release();
		UmbraObject = NULL;
	}
#endif
}

void PhysClass::Init(const PhysDefClass & def)
{
	Definition = &def;
	Flags = DEFAULT_FLAGS; 
	if (!def.ModelName.Is_Empty()) {

		RenderObjClass * model = NULL;
	
		if (::strchr(def.ModelName, '.') != NULL) {
			model = ::create_render_obj_from_filename(def.ModelName);
		} else {
			model = WW3DAssetManager::Get_Instance()->Create_Render_Obj(def.ModelName);
		}

		if ( model == NULL ) {
			WWDEBUG_SAY(( "***  FATAL ERROR : Failed to create model %s\n", def.ModelName ));
		}

		Set_Model(model);
		REF_PTR_RELEASE(model);
	}
}

void PhysClass::Set_Model(RenderObjClass * model)		
{ 
	PhysicsSceneClass * the_scene = PhysicsSceneClass::Get_Instance();
	bool in_scene = the_scene->Contains(this);

	if (Model) {
		// If we had an old model, copy the transform
		if ( model ) {		
			model->Set_Transform( Model->Get_Transform() );
		}
		if (in_scene) Model->Notify_Removed(the_scene);
		Model->Release_Ref();
	}
	Model = model; 
	if (Model) {
		Model->Add_Ref(); 
		if (in_scene) Model->Notify_Added(the_scene);
	}

	if ((Definition != NULL) && (Definition->IsPreLit)) {
		Enable_Is_Pre_Lit(true);
	}
	Invalidate_Static_Lighting_Cache ();
}
	
void PhysClass::Set_Model_By_Name(const char * model_type_name)
{
	RenderObjClass * model = WW3DAssetManager::Get_Instance()->Create_Render_Obj(model_type_name);
	if ( model == NULL ) {
		WWDEBUG_SAY(( "%s failed to load\n", model_type_name ));
	}
	WWASSERT(model);		// As above, PhysClasses cannot survive without a model...

	Set_Model(model);
	if (model) {
		model->Release_Ref();
	}
}

RenderObjClass * PhysClass::Get_Model(void)								
{ 
	if (Model) Model->Add_Ref(); 
	return Model; 
}

void PhysClass::Set_Name(const char * name)
{
	Name=name;
}

const char * PhysClass::Get_Name(void)
{
	return Name;
}

void PhysClass::Get_Shadow_Blob_Box(AABoxClass * set_obj_space_box)
{
	WWASSERT(set_obj_space_box != NULL);
	if (set_obj_space_box != NULL) {
		Model->Get_Obj_Space_Bounding_Box(*set_obj_space_box);
		set_obj_space_box->Extent *= 0.75f;
	}
}

void PhysClass::Render(RenderInfoClass & rinfo)
{ 
	Push_Effects(rinfo);

	if (Model) { 
		Model->Render(rinfo); 
	} 

	Pop_Effects(rinfo);
}

void PhysClass::Vis_Render(SpecialRenderInfoClass & rinfo)
{
	if (Model) {
		Model->Special_Render(rinfo);
	}
}

void PhysClass::Invalidate_Static_Lighting_Cache(void)
{
	Set_Flag(STATIC_LIGHTING_DIRTY,true);
}

LightEnvironmentClass * PhysClass::Get_Static_Lighting_Environment(void)
{
	if (Is_Pre_Lit()) {
	
		/*
		** This object doesn't need a lighting cache, make sure it doesn't have one
		*/
		if (StaticLightingCache != NULL) {
			WWDEBUG_SAY(("Pre-Lit object %s has a lighting cache!\r\n",Model->Get_Name()));
			delete StaticLightingCache;
			StaticLightingCache = NULL;
		}
	
	} else if (Get_Flag(STATIC_LIGHTING_DIRTY)) {

		/*
		** First, ensure that we have an allocated lighting cache
		*/
		if (StaticLightingCache == NULL) {
			StaticLightingCache = new LightEnvironmentClass;
		}

		/*
		** Next, update our sunlight status
		*/
		Update_Sun_Status();

		/*
		** Finally, ask the physics scene to re-compute our lighting cache
		*/
		PhysicsSceneClass::Get_Instance()->Compute_Static_Lighting(	StaticLightingCache,
																						Model->Get_Bounding_Sphere().Center,
																						Get_Flag(IS_IN_THE_SUN),
																						Get_Vis_Object_ID() );
										
		Set_Flag(STATIC_LIGHTING_DIRTY,false);
	} 

	/*
	** Return our lighting cache to the caller
	*/
	return StaticLightingCache;
}

void PhysClass::Update_Sun_Status(void)
{
	// Update sun status only four times per second
	unsigned current_time=WW3D::Get_Sync_Time();
	if ((current_time-SunStatusLastUpdated)<250) return;
	SunStatusLastUpdated=current_time;

	PhysicsSceneClass * scene = PhysicsSceneClass::Get_Instance();
	
	Vector3 sunlight;
	scene->Get_Sun_Light_Vector(&sunlight);
	Vector3 center = Model->Get_Bounding_Sphere().Center; 

#pragma message ("(gth) Need a collision group for sun-rays")
	CastResultStruct sunresult;
	LineSegClass sunray(center,center - sunlight * SUN_CHECK_DISTANCE);
	PhysRayCollisionTestClass sunraytest(sunray,&sunresult,0,COLLISION_TYPE_PROJECTILE);
	sunraytest.CheckStaticObjs = true;
	sunraytest.CheckDynamicObjs = false;

	Inc_Ignore_Counter();
	scene->Cast_Ray(sunraytest);

	// if the ray hits a static object which is casting a projected shadow, ignore that object 
	// and check again.
	if (	(sunresult.Fraction < 1.0f) && 
			(sunraytest.CollidedPhysObj != NULL) ) 
	{ 
		PhysClass * obj = sunraytest.CollidedPhysObj;
		if (obj->Is_Casting_Shadow()) {
			obj->Inc_Ignore_Counter();
			sunresult.Reset();
			scene->Cast_Ray(sunraytest);			
			obj->Dec_Ignore_Counter();
		}		
	}	
	Dec_Ignore_Counter();

	Enable_Is_In_The_Sun(sunresult.Fraction == 1.0f);
}

void PhysClass::Push_Effects(RenderInfoClass & rinfo)
{
	if (!MaterialEffectsOnMe.Is_Empty()) {
		RefMaterialEffectListIterator iterator(&MaterialEffectsOnMe);
		for ( ; !iterator.Is_Done() ; iterator.Next()) {
			iterator.Peek_Obj()->Render_Push(rinfo,this);
		}
	}

#if 0
	if (!ProjectionsOnMe.Is_Empty()) {

		ShaderClass shader = ShaderClass::_PresetOpaqueShader;
		VertexMaterialClass * vmtl = VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_NODIFFUSE);
		
		DX8Wrapper::Set_Shader(shader);
		DX8Wrapper::Set_Material(vmtl);

		Matrix4 view,proj;
		Matrix4 identity(true);

		DX8Wrapper::Get_Transform(D3DTS_VIEW,view);
		DX8Wrapper::Get_Transform(D3DTS_PROJECTION,proj);

		DX8Wrapper::Set_Transform(D3DTS_WORLD,identity);
		DX8Wrapper::Set_Transform(D3DTS_VIEW,identity);
		DX8Wrapper::Set_Transform(D3DTS_PROJECTION,identity);

		
		TexProjListIterator iterator(&ProjectionsOnMe);
		for ( ; !iterator.Is_Done() ; iterator.Next()) {
			TextureClass * tex = iterator.Peek_Obj()->Peek_Material_Pass()->Peek_Texture(0);
			if (tex != NULL) {
				DX8Wrapper::Set_Texture(0,tex);

				DynamicVBAccessClass vbaccess(BUFFER_TYPE_DYNAMIC_DX8,4);
				{
					DynamicVBAccessClass::WriteLockClass lock(&vbaccess);
					VertexFormatXYZNDUV2 * verts = lock.Get_Formatted_Vertex_Array();
					verts[0].x = -1.0f;
					verts[0].y = 0.8f;
					verts[0].z = 0.0;
					verts[0].u1 = 0.0f;
					verts[0].v1 = 0.0f;
					verts[0].diffuse = 0xFFFFFFFF;

					verts[1].x = -1.0f;
					verts[1].y = 0.3f;
					verts[1].z = 0.0;
					verts[1].u1 = 0.0f;
					verts[1].v1 = 1.0f;
					verts[1].diffuse = 0xFFFFFFFF;

					verts[2].x = -0.5f;
					verts[2].y = 0.3f;
					verts[2].z = 0.0;
					verts[2].u1 = 1.0f;
					verts[2].v1 = 1.0f;
					verts[2].diffuse = 0xFFFFFFFF;

					verts[3].x = -0.5f;
					verts[3].y = 0.8f;
					verts[3].z = 0.0;
					verts[3].u1 = 1.0f;
					verts[3].v1 = 0.0f;
					verts[3].diffuse = 0xFFFFFFFF;
				}

				DynamicIBAccessClass ibaccess(BUFFER_TYPE_DYNAMIC_DX8,2*3);
				{
					DynamicIBAccessClass::WriteLockClass lock(&ibaccess);
					unsigned short * indices = lock.Get_Index_Array();

					indices[0] = 0;
					indices[1] = 1;
					indices[2] = 2;
					indices[3] = 0;
					indices[4] = 2;
					indices[5] = 3;
				}

				DX8Wrapper::Set_Vertex_Buffer(vbaccess);
				DX8Wrapper::Set_Index_Buffer(ibaccess,0);
				DX8Wrapper::Draw_Triangles(0,2,0,4);
			}
		}

		DX8Wrapper::Set_Transform(D3DTS_VIEW,view);
		DX8Wrapper::Set_Transform(D3DTS_PROJECTION,proj);

		REF_PTR_RELEASE(vmtl);
	}
#endif

}

void PhysClass::Pop_Effects(RenderInfoClass & rinfo)
{
	if (!MaterialEffectsOnMe.Is_Empty()) {
		RefMaterialEffectListIterator iterator(&MaterialEffectsOnMe);
	
		while (!iterator.Is_Done()) {

			MaterialEffectClass * effect = iterator.Peek_Obj();
			effect->Render_Pop(rinfo);

			if (effect->Is_Auto_Remove_Enabled()) {
				iterator.Remove_Current_Object();
			} else {
				iterator.Next();
			}
		}
	}
}


bool PhysClass::Save (ChunkSaveClass &csave)
{
	CullableClass * cullable_ptr = (CullableClass *)this;
	WidgetUserClass * widgetuser_ptr = (WidgetUserClass *)this;
	EditableClass * editable_ptr = (EditableClass *)this;

	csave.Begin_Chunk(PHYS_CHUNK_VARIABLES);
	// (gth) not saving observer pointers any more!
	WRITE_MICRO_CHUNK(csave,PHYS_VARIABLE_CULLABLE_PTR,cullable_ptr);
	WRITE_MICRO_CHUNK(csave,PHYS_VARIABLE_WIDGETUSER_PTR,widgetuser_ptr);
	WRITE_MICRO_CHUNK(csave,PHYS_VARIABLE_EDITABLE_PTR,editable_ptr);
	WRITE_MICRO_CHUNK(csave,PHYS_VARIABLE_FLAGS,Flags);
	WRITE_MICRO_CHUNK(csave,PHYS_VARIABLE_INSTANCEID,InstanceID);	
	if (Name.Get_Length() > 0) {
		csave.Begin_Micro_Chunk(PHYS_VARIABLE_NAME);
		WWASSERT(Name.Get_Length()+1 < 255);
		csave.Write(Name,Name.Get_Length() + 1);
		csave.End_Micro_Chunk();
	}
	if (Definition != NULL) {
		int defid = Definition->Get_ID();
		WRITE_MICRO_CHUNK(csave,PHYS_VARIABLE_DEFID,defid);
	}
	csave.End_Chunk();
	
	csave.Begin_Chunk(PHYS_CHUNK_MODEL);
	csave.Begin_Chunk(Model->Get_Factory().Chunk_ID());
	Model->Get_Factory().Save(csave,Model);
	csave.End_Chunk();
	csave.End_Chunk();

	return true;
}

bool PhysClass::Load (ChunkLoadClass &cload)
{
	PersistFactoryClass * factory = NULL;
	CullableClass * cullable_ptr = NULL;
	WidgetUserClass * widgetuser_ptr = NULL;
	EditableClass * editable_ptr = NULL;
	int defid = -1;
	char tmpstring[256];
	tmpstring[0] = 0;
	RenderObjClass * render_model = NULL;

	while (cload.Open_Chunk()) {
		
		switch(cload.Cur_Chunk_ID()) 
		{
			case PHYS_CHUNK_VARIABLES:
				while (cload.Open_Micro_Chunk()) {
					switch(cload.Cur_Micro_Chunk_ID()) {
						// (gth) not saving observer pointers any more!
						READ_MICRO_CHUNK(cload,PHYS_VARIABLE_CULLABLE_PTR,cullable_ptr);
						READ_MICRO_CHUNK(cload,PHYS_VARIABLE_WIDGETUSER_PTR,widgetuser_ptr);
						READ_MICRO_CHUNK(cload,PHYS_VARIABLE_EDITABLE_PTR,editable_ptr);
						READ_MICRO_CHUNK(cload,PHYS_VARIABLE_FLAGS,Flags);
						READ_MICRO_CHUNK(cload,PHYS_VARIABLE_DEFID,defid);
						READ_MICRO_CHUNK(cload,PHYS_VARIABLE_INSTANCEID,InstanceID);
						
						case PHYS_VARIABLE_NAME:
							cload.Read(tmpstring,cload.Cur_Micro_Chunk_Length());
							break;
					}
					cload.Close_Micro_Chunk();	
				}
				break;

			case PHYS_CHUNK_MODEL:
				cload.Open_Chunk();
				factory = SaveLoadSystemClass::Find_Persist_Factory(cload.Cur_Chunk_ID());
				WWASSERT(factory != NULL);
				if (factory != NULL) {
					render_model = (RenderObjClass *)factory->Load(cload);
					SET_REF_OWNER(render_model);
				}
				cload.Close_Chunk();
				break;

			default:
				WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
				break;
		}
		
		if (cullable_ptr != NULL) {
			SaveLoadSystemClass::Register_Pointer(cullable_ptr,(CullableClass *)this);
		}
		cload.Close_Chunk();
	}

	/*
	** Set our name
	*/
	if (strlen(tmpstring) > 0) {
		Set_Name(tmpstring);
	}

	/*
	** Set our definition pointer
	*/
	if (defid != -1) {
		Definition = (PhysDefClass *)_TheDefinitionMgr.Find_Definition(defid);	
	} else {
		Definition = NULL;
	}

	/*
	** Only install the model after our definition is installed
	*/
	Set_Model(render_model);
	REF_PTR_RELEASE(render_model);

	/*
	** Ask that our Observer pointer is re-mapped
	*/
	if (Observer != NULL) {
		REQUEST_POINTER_REMAP((void**)&Observer);
	}
	
	/*
	** Register all of the multiple-inheritance versions of our this pointer
	*/
	if (cullable_ptr != NULL) {
		SaveLoadSystemClass::Register_Pointer(cullable_ptr,(CullableClass *)this);
	}
	if (widgetuser_ptr != NULL) {
		SaveLoadSystemClass::Register_Pointer(widgetuser_ptr,(WidgetUserClass *)this);
	}
	if (editable_ptr != NULL) {
		SaveLoadSystemClass::Register_Pointer(editable_ptr,(EditableClass *)this);
	}

	/*
	** Mark our static lighting cache dirty
	*/
	Invalidate_Static_Lighting_Cache();

	/*
	** Update our Umbra object
	*/
#if (UMBRASUPPORT)
	UmbraSupport::Update_Umbra_Object(this);
#endif

	return true;
}


#ifdef WWDEBUG
void PhysClass::Add_Debug_Point(const Vector3 & p,const Vector3 & color)
{
	if (Is_Debug_Display_Enabled()) {
		PhysicsSceneClass::Get_Instance()->Add_Debug_Point(p,color);
	}
}
#endif

#ifdef WWDEBUG
void PhysClass::Add_Debug_Vector(const Vector3 & p,const Vector3 & v,const Vector3 & color)
{
	if (Is_Debug_Display_Enabled() && (v.Length2() > 0.0f)) {
		PhysicsSceneClass::Get_Instance()->Add_Debug_Vector(p,v,color);
	}
}
#endif

#ifdef WWDEBUG
void PhysClass::Add_Debug_AABox(const AABoxClass & box,const Vector3 & color,float opacity)
{
	if (Is_Debug_Display_Enabled()) {
		PhysicsSceneClass::Get_Instance()->Add_Debug_AABox(box,color,opacity);
	}
}
#endif

#ifdef WWDEBUG
void PhysClass::Add_Debug_OBBox(const OBBoxClass & box,const Vector3 & color,float opacity)
{
	if (Is_Debug_Display_Enabled()) {
		PhysicsSceneClass::Get_Instance()->Add_Debug_OBBox(box,color,opacity);
	}
}
#endif

#ifdef WWDEBUG
void PhysClass::Add_Debug_Axes(const Matrix3D & transform,const Vector3 & color)
{
	if (Is_Debug_Display_Enabled()) {
		PhysicsSceneClass::Get_Instance()->Add_Debug_Axes(transform,color);
	}
}
#endif

bool PhysClass::Is_Debug_Display_Enabled(void) const					
{ 
	PhysicsSceneClass * the_scene = PhysicsSceneClass::Get_Instance();

	Vector3 pos;
	Get_Position(&pos);
	float dist = (pos - the_scene->Get_Last_Camera_Position()).Length2();
	if (dist > DEBUG_RENDER_DIST2) {
		return false;
	}

	if (the_scene) {
		return (((Flags & DEBUGDISPLAY) == DEBUGDISPLAY) || the_scene->Is_Debug_Display_Enabled()); 
	} else {
		return ((Flags & DEBUGDISPLAY) == DEBUGDISPLAY); 
	}
}

bool PhysClass::Expire(void)
{
	ExpirationReactionType result = EXPIRATION_APPROVED;
	if (Observer != NULL) {
		result = Observer->Object_Expired(this);
	}
	if (result == EXPIRATION_APPROVED) {
		PhysicsSceneClass::Get_Instance()->Delayed_Remove_Object(this);
		return true;
	} else {
		return false;
	}
}

//
// TSS added this... not efficient to use if you are also 
// setting position
//
void PhysClass::Set_Facing(float new_facing)
{
	Vector3 pos;
	Get_Position(&pos);

	Matrix3D tm(1);
	tm.Translate(pos);
	tm.Rotate_Z(new_facing);
	
	Set_Transform(tm);
}

bool PhysClass::Do_Any_Effects_Suppress_Shadows(void)
{
	RefMaterialEffectListIterator iterator(&MaterialEffectsOnMe);
	for ( ; !iterator.Is_Done() ; iterator.Next()) {
		if (iterator.Peek_Obj()->Are_Shadows_Suppressed()) {
			return true;
		}
	}
	return false;
}

/***********************************************************************************************
**
** PhysDefClass Implementation
** This holds the description for a PhysClass.  Since PhysClass's aren't concrete, this
** definition class isn't either and thus has no persist factory (required by PersistClass)
** or create method (required by DefinitionClass)
**
***********************************************************************************************/

enum 
{
	PHYSDEF_CHUNK_DEFINITION		= 0x055ffe07,			// parent class data.
	PHYSDEF_CHUNK_VARIABLES,									// simple variables

	PHYSDEF_VARIABLE_FLAGS			= 0x00,
	PHYSDEF_VARIABLE_MODELNAME,
	PHYSDEF_VARIABLE_ISPRELIT,

};


PhysDefClass::PhysDefClass(void) : 
	ModelName ("NULL"),
	IsPreLit(false)
{
	FILENAME_PARAM(PhysDefClass,ModelName, "Westwood 3D Files", ".w3d");
}

bool PhysDefClass::Is_Valid_Config (StringClass &message)
{
	bool retval = true;

	if (ModelName.Is_Empty ()) {
		message += "ModelName is invalid!\n";
		retval = false;
	}

	return retval;
}

bool PhysDefClass::Save(ChunkSaveClass &csave)
{
	csave.Begin_Chunk(PHYSDEF_CHUNK_DEFINITION);
	DefinitionClass::Save(csave);
	csave.End_Chunk();

	csave.Begin_Chunk(PHYSDEF_CHUNK_VARIABLES);
	WRITE_MICRO_CHUNK_WWSTRING(csave,PHYSDEF_VARIABLE_MODELNAME,ModelName);
	WRITE_MICRO_CHUNK(csave,PHYSDEF_VARIABLE_ISPRELIT,IsPreLit);	
	csave.End_Chunk();
	return true;

}

bool PhysDefClass::Load(ChunkLoadClass &cload)
{
	while (cload.Open_Chunk()) {

		switch(cload.Cur_Chunk_ID()) {			

			case PHYSDEF_CHUNK_DEFINITION:
				DefinitionClass::Load(cload);
				break;

			case PHYSDEF_CHUNK_VARIABLES:
				WWASSERT(cload.Cur_Chunk_ID() == PHYSDEF_CHUNK_VARIABLES);
				while (cload.Open_Micro_Chunk()) {
					switch(cload.Cur_Micro_Chunk_ID()) {
						OBSOLETE_MICRO_CHUNK(PHYSDEF_VARIABLE_FLAGS);
						READ_MICRO_CHUNK_WWSTRING(cload,PHYSDEF_VARIABLE_MODELNAME,ModelName);
						READ_MICRO_CHUNK(cload,PHYSDEF_VARIABLE_ISPRELIT,IsPreLit);	
					}
					cload.Close_Micro_Chunk();
				}
				break;
		}

		cload.Close_Chunk();
	}
	return true;
}

bool PhysDefClass::Is_Type(const char * type_name)
{
	if (stricmp(type_name,PhysDefClass::Get_Type_Name()) == 0) {
		return true;
	} else {
		return false;
	}
}

