This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/ww3d2/agg_def.cpp

894 lines
24 KiB
C++

/*
** 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 : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/agg_def.cpp $*
* *
* Author:: Patrick Smith
* *
* $Modtime:: 4/05/01 10:21a $*
* *
* $Revision:: 5 $*
* *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "agg_def.h"
#include "htree.h"
#include "w3derr.h"
#include "chunkio.h"
#include "wwdebug.h"
#include "assetmgr.h"
#include "matinfo.h"
#include "texture.h"
#include "wwstring.h"
#include <windows.h>
///////////////////////////////////////////////////////////////////////////////////
//
// Local constants
//
const char * const EMPTY_STRING = "";
///////////////////////////////////////////////////////////////////////////////////
//
// Global variable initialization
//
AggregateLoaderClass _AggregateLoader;
///////////////////////////////////////////////////////////////////////////////////
//
// AggregateDefClass
//
AggregateDefClass::AggregateDefClass (void)
: m_pName (NULL)
{
// Set our member data to default settings
::memset (&m_Info, 0, sizeof (m_Info));
::memset (&m_MiscInfo, 0, sizeof (m_MiscInfo));
m_MiscInfo.OriginalClassID = RenderObjClass::CLASSID_HLOD;
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// AggregateDefClass
//
AggregateDefClass::AggregateDefClass (const AggregateDefClass &src)
: m_pName (NULL)
{
// Set our member data to default settings
::memset (&m_Info, 0, sizeof (m_Info));
::memset (&m_MiscInfo, 0, sizeof (m_MiscInfo));
m_MiscInfo.OriginalClassID = RenderObjClass::CLASSID_HLOD;
// Invoke the assignment operator
(*this) = src;
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// AggregateDefClass
//
AggregateDefClass::AggregateDefClass (RenderObjClass &base_model)
: m_pName (NULL)
{
// Set our member data to default settings
::memset (&m_Info, 0, sizeof (m_Info));
::memset (&m_MiscInfo, 0, sizeof (m_MiscInfo));
m_MiscInfo.OriginalClassID = RenderObjClass::CLASSID_HLOD;
Initialize (base_model);
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// ~AggregateDefClass
//
AggregateDefClass::~AggregateDefClass (void)
{
// Free the name buffer if necessary
if (m_pName != NULL) {
// free() is used because the buffer was allocated with ::_strdup().
::free (m_pName);
m_pName = NULL;
}
Free_Subobject_List ();
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// operator=
//
const AggregateDefClass &
AggregateDefClass::operator= (const AggregateDefClass &src)
{
int index;
// Free the name buffer if necessary
if (m_pName != NULL) {
::free (m_pName);
m_pName = NULL;
}
// Start with a fresh set of data
Free_Subobject_List ();
// Copy the src object's name and info struct
Set_Name (src.Get_Name ());
::memcpy (&m_Info, &src.m_Info, sizeof (m_Info));
::memcpy (&m_MiscInfo, &src.m_MiscInfo, sizeof (m_MiscInfo));
m_Version = src.m_Version;
// Loop through all the entries in the src object's subobj list
for (index = 0; index < src.m_SubobjectList.Count (); index ++) {
W3dAggregateSubobjectStruct *pinfo = src.m_SubobjectList[index];
if (pinfo != NULL) {
// Copy the src object's info for this subobj
W3dAggregateSubobjectStruct *new_info = new W3dAggregateSubobjectStruct;
::memcpy (new_info, pinfo, sizeof (W3dAggregateSubobjectStruct));
// Add this subobj to our list
m_SubobjectList.Add (new_info);
}
}
// Return a reference to ourselves
return *this;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Free_Subobject_List
//
void
AggregateDefClass::Free_Subobject_List (void)
{
// Delete all the stucture pointers contained in the subobject list
for (int index = 0; index < m_SubobjectList.Count (); index ++) {
W3dAggregateSubobjectStruct *pinfo = m_SubobjectList[index];
if (pinfo) {
delete pinfo;
}
}
// Reset the lists contents
m_SubobjectList.Delete_All ();
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Create
//
RenderObjClass *
AggregateDefClass::Create (void)
{
// Attempt to create an instance of the hierarchy
RenderObjClass *pmodel = Create_Render_Object (m_Info.BaseModelName);
if (pmodel != NULL) {
// Perform the aggregation
Attach_Subobjects (*pmodel);
// Let the new object know what its new name and base name are.
pmodel->Set_Name (m_pName);
pmodel->Set_Base_Model_Name (m_Info.BaseModelName);
pmodel->Set_Sub_Objects_Match_LOD ((m_MiscInfo.Flags & W3D_AGGREGATE_FORCE_SUB_OBJ_LOD) == W3D_AGGREGATE_FORCE_SUB_OBJ_LOD);
} else {
WWDEBUG_SAY (("Unable to load aggregate %s.\r\n", m_Info.BaseModelName));
}
// Return a pointer to the new aggregate
return pmodel;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Find_Subobject
//
RenderObjClass *
AggregateDefClass::Find_Subobject
(
RenderObjClass &model,
const char mesh_path[MESH_PATH_ENTRIES][MESH_PATH_ENTRY_LEN],
const char bone_path[MESH_PATH_ENTRIES][MESH_PATH_ENTRY_LEN]
)
{
RenderObjClass *parent_model = &model;
parent_model->Add_Ref ();
// Loop through all the models in our "path" until we've either failed
// or found the exact mesh we were looking for...
for (int index = 1;
(mesh_path[index][0] != 0) && (parent_model != NULL);
index ++) {
// Look one level deeper into the subobject chain...
RenderObjClass *sub_obj = NULL;
if (bone_path[index][0] == 0) {
sub_obj = parent_model->Get_Sub_Object_By_Name (mesh_path[index]);
} else {
int bone_index = parent_model->Get_Bone_Index (bone_path[index]);
int subobj_count = parent_model->Get_Num_Sub_Objects_On_Bone (bone_index);
// Loop through all the subobjects on this bone
for (int subobj_index = 0; (subobj_index < subobj_count) && (sub_obj == NULL); subobj_index ++) {
// Is this the subobject we were looking for?
RenderObjClass *ptemp_obj = parent_model->Get_Sub_Object_On_Bone (subobj_index, bone_index);
if (::lstrcmpi (ptemp_obj->Get_Name (), mesh_path[index]) == 0) {
sub_obj = ptemp_obj;
} else {
REF_PTR_RELEASE (ptemp_obj);
}
}
}
REF_PTR_RELEASE (parent_model);
// The parent for the next iteration is the subobject on this one.
parent_model = sub_obj;
}
// Return a pointer to the subobject
return parent_model;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Attach_Subobjects
//
void
AggregateDefClass::Attach_Subobjects (RenderObjClass &base_model)
{
// Now loop through all the subobjects and attach them to the appropriate bone
for (int index = 0; index < m_SubobjectList.Count (); index ++) {
W3dAggregateSubobjectStruct *psubobj_info = m_SubobjectList[index];
if (psubobj_info != NULL) {
// Now create this subobject and attach it to its bone.
RenderObjClass *prender_obj = Create_Render_Object (psubobj_info->SubobjectName);
if (prender_obj != NULL) {
// Attach this object to the requested bone
if (base_model.Add_Sub_Object_To_Bone (prender_obj, psubobj_info->BoneName) == false) {
WWDEBUG_SAY (("Unable to attach %s to %s.\r\n", psubobj_info->SubobjectName, psubobj_info->BoneName));
}
// Release our hold on this pointer
prender_obj->Release_Ref ();
} else {
WWDEBUG_SAY (("Unable to load aggregate subobject %s.\r\n", psubobj_info->SubobjectName));
}
}
}
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Create_Render_Object
//
RenderObjClass *
AggregateDefClass::Create_Render_Object (const char *passet_name)
{
// Assume failure
RenderObjClass *prender_obj = NULL;
// Attempt to get an instance of the render object from the asset manager
prender_obj = WW3DAssetManager::Get_Instance()->Create_Render_Obj (passet_name);
// If we couldn't find the render object in the asset manager, then attempt to
// load it from file
if ((prender_obj == NULL) &&
Load_Assets (passet_name)) {
// It should be in the asset manager now, so attempt to get it again.
prender_obj = WW3DAssetManager::Get_Instance()->Create_Render_Obj (passet_name);
}
// Return a pointer to the render object
return prender_obj;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Load_Assets
//
bool
AggregateDefClass::Load_Assets (const char *passet_name)
{
// Assume failure
bool retval = false;
// Param OK?
if (passet_name != NULL) {
// Determine what the current working directory is
char path[MAX_PATH];
::GetCurrentDirectory (sizeof (path), path);
// Ensure the path is directory delimited
if (path[::lstrlen(path)-1] != '\\') {
::lstrcat (path, "\\");
}
// Assume the filename is simply the "asset name" + the w3d extension
::lstrcat (path, passet_name);
::lstrcat (path, ".w3d");
// If the file exists, then load it into the asset manager.
if (::GetFileAttributes (path) != 0xFFFFFFFF) {
retval = WW3DAssetManager::Get_Instance()->Load_3D_Assets (path);
}
}
// Return the true/false result code
return retval;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Initialize
//
void
AggregateDefClass::Initialize (RenderObjClass &base_model)
{
// Start with fresh lists
Free_Subobject_List ();
// Determine what the render objects original name was.
const char *orig_model_name = base_model.Get_Base_Model_Name ();
orig_model_name = (orig_model_name == NULL) ? base_model.Get_Name () : orig_model_name;
// Record information about this base model
::lstrcpy (m_Info.BaseModelName, orig_model_name);
m_Info.SubobjectCount = 0;
m_MiscInfo.OriginalClassID = base_model.Class_ID ();
m_MiscInfo.Flags = 0;
m_MiscInfo.Flags |= base_model.Is_Sub_Objects_Match_LOD_Enabled () ? W3D_AGGREGATE_FORCE_SUB_OBJ_LOD : 0;
// Pass the aggregate name along
Set_Name (base_model.Get_Name ());
// Create a new instance of the model which we can use
// to compare with the supplied model and determine
// which 'bones-models' and textures are new.
RenderObjClass *pvanilla_model = (RenderObjClass *)Create_Render_Object (orig_model_name);
// Build lists of changes from the delta between the original model and the provided one
Build_Subobject_List (*pvanilla_model, base_model);
// Release the model if necessary
REF_PTR_RELEASE (pvanilla_model);
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Build_Subobject_List
//
void
AggregateDefClass::Build_Subobject_List
(
RenderObjClass &original_model,
RenderObjClass &model
)
{
int index;
// Loop through all the bones in this render obj
int bone_count = model.Get_Num_Bones ();
for (int bone_index = 0; bone_index < bone_count; bone_index ++) {
const char *pbone_name = model.Get_Bone_Name (bone_index);
// Build a list of nodes that are contained in the vanilla model
DynamicVectorClass <RenderObjClass *> orig_node_list;
for (index = 0;
index < original_model.Get_Num_Sub_Objects_On_Bone (bone_index);
index ++) {
RenderObjClass *psubobj = original_model.Get_Sub_Object_On_Bone (index, bone_index);
if (psubobj != NULL) {
orig_node_list.Add (psubobj);
}
}
// Build a list of nodes that are contained in this bone
DynamicVectorClass <RenderObjClass *> node_list;
for (index = 0;
index < model.Get_Num_Sub_Objects_On_Bone (bone_index);
index ++) {
RenderObjClass *psubobj = model.Get_Sub_Object_On_Bone (index, bone_index);
if (psubobj != NULL) {
node_list.Add (psubobj);
}
}
int node_count = node_list.Count ();
if (node_count > 0) {
// Loop through the subobjects and add each one to our internal list
W3dAggregateSubobjectStruct subobj_info = { 0 };
for (int node_index = 0; node_index < node_count; node_index ++) {
RenderObjClass *psubobject = node_list[node_index];
WWASSERT (psubobject != NULL);
// Is this subobject new? (i.e. not in a 'vanilla' instance?)
const char *prototype_name = psubobject->Get_Name ();
if (psubobject != NULL &&
(Is_Object_In_List (prototype_name, orig_node_list) == false)) {
// Add this subobject to our list
::lstrcpy (subobj_info.SubobjectName, prototype_name);
::lstrcpy (subobj_info.BoneName, pbone_name);
Add_Subobject (subobj_info);
m_Info.SubobjectCount ++;
// Attach this render object to the 'original' model (this is done
// so we can do texture compares later)
RenderObjClass *prender_obj = WW3DAssetManager::Get_Instance ()->Create_Render_Obj (prototype_name);
((RenderObjClass &)original_model).Add_Sub_Object_To_Bone (prender_obj, pbone_name);
REF_PTR_RELEASE (prender_obj);
}
}
}
// Free our hold on the render objs in the original node list
for (index = 0; index < orig_node_list.Count (); index ++) {
REF_PTR_RELEASE (orig_node_list[index]);
}
orig_node_list.Delete_All ();
// Free our hold on the render objs in the node list
for (index = 0; index < node_list.Count (); index ++) {
REF_PTR_RELEASE (node_list[index]);
}
node_list.Delete_All ();
}
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Is_Object_In_List
//
bool
AggregateDefClass::Is_Object_In_List
(
const char *passet_name,
DynamicVectorClass <RenderObjClass *> &node_list
)
{
// Assume failure
bool retval = false;
// Loop through the nodes in the list until we've found the one
// were are looking for.
for (int node_index = 0; (node_index < node_list.Count ()) && (retval == false); node_index ++) {
RenderObjClass *prender_obj = node_list[node_index];
// Is this the render object we were looking for?
if (prender_obj != NULL &&
::lstrcmpi (prender_obj->Get_Name (), passet_name) == 0) {
retval = true;
}
}
// Return the true/false result code
return retval;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Load
//
WW3DErrorType
AggregateDefClass::Load_W3D (ChunkLoadClass &chunk_load)
{
W3dTextureReplacerHeaderStruct header = { 0 };
while (chunk_load.Open_Chunk()) {
WW3DErrorType error = WW3D_ERROR_OK;
switch (chunk_load.Cur_Chunk_ID()) {
case W3D_CHUNK_AGGREGATE_HEADER:
error = Read_Header(chunk_load);
break;
case W3D_CHUNK_AGGREGATE_INFO:
error = Read_Info(chunk_load);
break;
case W3D_CHUNK_TEXTURE_REPLACER_INFO:
if (chunk_load.Read (&header, sizeof (header)) == sizeof (header)) {
if (header.ReplacedTexturesCount > 0) {
WWDEBUG_SAY(("Obsolete texture replacement chunk encountered in aggregate: %s\r\n",m_pName));
}
}
break;
case W3D_CHUNK_AGGREGATE_CLASS_INFO:
error = Read_Class_Info(chunk_load);
break;
default:
// Unknown chunk.
break;
}
chunk_load.Close_Chunk();
if (error != WW3D_ERROR_OK) return (error);
}
return WW3D_ERROR_OK;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Read_Header
//
WW3DErrorType
AggregateDefClass::Read_Header (ChunkLoadClass &chunk_load)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_LOAD_FAILED;
// Is this the header chunk?
W3dAggregateHeaderStruct header = { 0 };
if (chunk_load.Read (&header, sizeof (header)) == sizeof (header)) {
// Copy the name from the header structure
m_pName = ::_strdup (header.Name);
m_Version = header.Version;
// Success!
ret_val = WW3D_ERROR_OK;
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Read_Info
//
WW3DErrorType
AggregateDefClass::Read_Info (ChunkLoadClass &chunk_load)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_LOAD_FAILED;
// Read the chunk straight into our member structure
::memset (&m_Info, 0, sizeof (m_Info));
if (chunk_load.Read (&m_Info, sizeof (m_Info)) == sizeof (m_Info)) {
// Success!
ret_val = WW3D_ERROR_OK;
// Read all the subobjects from the file
for (UINT isubobject = 0;
(isubobject < m_Info.SubobjectCount) && (ret_val == WW3D_ERROR_OK);
isubobject ++) {
// Read this subobject's definition from the file
ret_val = Read_Subobject (chunk_load);
}
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Read_Subobject
//
WW3DErrorType
AggregateDefClass::Read_Subobject (ChunkLoadClass &chunk_load)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_LOAD_FAILED;
// Read the subobject information from the file
W3dAggregateSubobjectStruct subobj_info = { 0 };
if (chunk_load.Read (&subobj_info, sizeof (subobj_info)) == sizeof (subobj_info)) {
// Add this subobject to our list
Add_Subobject (subobj_info);
// Success!
ret_val = WW3D_ERROR_OK;
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Add_Subobject
//
void
AggregateDefClass::Add_Subobject (const W3dAggregateSubobjectStruct &subobj_info)
{
// Create a new structure and copy the contents of the src
W3dAggregateSubobjectStruct *pnew_entry = new W3dAggregateSubobjectStruct;
::lstrcpy (pnew_entry->SubobjectName, subobj_info.SubobjectName);
::lstrcpy (pnew_entry->BoneName, subobj_info.BoneName);
// Add this new entry to the list
m_SubobjectList.Add (pnew_entry);
return ;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Read_Class_Info
//
WW3DErrorType
AggregateDefClass::Read_Class_Info (ChunkLoadClass &chunk_load)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_LOAD_FAILED;
// Read the chunk straight into our header structure
::memset (&m_MiscInfo, 0, sizeof (m_MiscInfo));
if (chunk_load.Read (&m_MiscInfo, sizeof (m_MiscInfo)) == sizeof (m_MiscInfo)) {
// Success!
ret_val = WW3D_ERROR_OK;
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
//////////////////////////////////////////////////////////////////////////////////
//
// Save
//
WW3DErrorType
AggregateDefClass::Save_W3D (ChunkSaveClass &chunk_save)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_SAVE_FAILED;
// Begin a chunk that identifies an aggregate
if (chunk_save.Begin_Chunk (W3D_CHUNK_AGGREGATE) == TRUE) {
// Attempt to save the different sections of the aggregate definition
if ((Save_Header (chunk_save) == WW3D_ERROR_OK) &&
(Save_Info (chunk_save) == WW3D_ERROR_OK) &&
(Save_Class_Info (chunk_save) == WW3D_ERROR_OK)) {
// Success!
ret_val = WW3D_ERROR_OK;
}
// Close the aggregate chunk
chunk_save.End_Chunk ();
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Save_Header
//
WW3DErrorType
AggregateDefClass::Save_Header (ChunkSaveClass &chunk_save)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_SAVE_FAILED;
// Begin a chunk that identifies the aggregate
if (chunk_save.Begin_Chunk (W3D_CHUNK_AGGREGATE_HEADER) == TRUE) {
// Fill the header structure
W3dAggregateHeaderStruct header = { 0 };
header.Version = W3D_CURRENT_AGGREGATE_VERSION;
::lstrcpyn (header.Name, m_pName, sizeof (header.Name));
header.Name[sizeof (header.Name) - 1] = 0;
// Write the header out to the chunk
if (chunk_save.Write (&header, sizeof (header)) == sizeof (header)) {
// Success!
ret_val = WW3D_ERROR_OK;
}
// End the header chunk
chunk_save.End_Chunk ();
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Save_Info
//
WW3DErrorType
AggregateDefClass::Save_Info (ChunkSaveClass &chunk_save)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_SAVE_FAILED;
// Begin a chunk that identifies the aggregate settings
if (chunk_save.Begin_Chunk (W3D_CHUNK_AGGREGATE_INFO) == TRUE) {
// Write the settings structure out to the chunk
if (chunk_save.Write (&m_Info, sizeof (m_Info)) == sizeof (m_Info)) {
// Success!
ret_val = WW3D_ERROR_OK;
// Write all the subobjects to the file
for (int isubobject = 0;
(isubobject < m_SubobjectList.Count ()) && (ret_val == WW3D_ERROR_OK);
isubobject ++) {
// Write this object to the file
ret_val = Save_Subobject (chunk_save, m_SubobjectList[isubobject]);
}
}
// End the settings chunk
chunk_save.End_Chunk ();
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Save_Subobject
//
WW3DErrorType
AggregateDefClass::Save_Subobject
(
ChunkSaveClass &chunk_save,
W3dAggregateSubobjectStruct *psubobject
)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_SAVE_FAILED;
// Write the subobj structure out to the chunk
if (chunk_save.Write (psubobject, sizeof (W3dAggregateSubobjectStruct)) == sizeof (W3dAggregateSubobjectStruct)) {
// Success!
ret_val = WW3D_ERROR_OK;
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
/////////////////////////////////////////////////////////////////////////////////
//
// Save_Class_Info
//
WW3DErrorType
AggregateDefClass::Save_Class_Info (ChunkSaveClass &chunk_save)
{
// Assume error
WW3DErrorType ret_val = WW3D_ERROR_SAVE_FAILED;
// Begin a chunk that identifies the texture replacer header
if (chunk_save.Begin_Chunk (W3D_CHUNK_AGGREGATE_CLASS_INFO) == TRUE) {
// Write the class information structure out to the chunk
if (chunk_save.Write (&m_MiscInfo, sizeof (m_MiscInfo)) == sizeof (m_MiscInfo)) {
// Success!
ret_val = WW3D_ERROR_OK;
}
// End the class info chunk
chunk_save.End_Chunk ();
}
// Return the WW3D_ERROR_TYPE return code
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Load
//
PrototypeClass *
AggregateLoaderClass::Load_W3D (ChunkLoadClass &chunk_load)
{
// Assume failure
AggregatePrototypeClass *pprototype = NULL;
// Create a definition object
AggregateDefClass *pdefinition = new AggregateDefClass;
if (pdefinition != NULL) {
// Ask the definition object to load the aggregate data
if (pdefinition->Load_W3D (chunk_load) != WW3D_ERROR_OK) {
// Error! Free the definition
delete pdefinition;
pdefinition = NULL;
} else {
// Success! Create a prototype from the definition
pprototype = new AggregatePrototypeClass (pdefinition);
}
}
// Return a pointer to the prototype
return pprototype;
}