427 lines
19 KiB
C++

/*
** Command & Conquer Generals Zero Hour(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 : Renegade / G *
* *
* $Archive:: /Commando/Code/Tools/max2w3d/hlodsave.cpp $*
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 11/07/00 5:24p $*
* *
* $Revision:: 9 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* -- The constructor builds the whole HLOD tree in a *
* -- Destructor blows away the dynamic memory we used. *
* -- Method called when saving to a W3D file. Saves the chunks *
* -- Write the header *
* -- Writes each LOD *
* -- Writes the mesh to bone connectivity info for each mesh in an LOD. *
* HLodSaveClass::save_aggregate_array -- save the aggregates (if any) *
* HLodSaveClass::save_proxy_array -- save the array of proxies (if any) *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "hlodsave.h"
#include "meshcon.h"
#include "errclass.h"
#include "util.h"
#include "w3dappdata.h"
#include "wwmath.h" // NO_MAX_SCREEN_SIZE
#include "exportlog.h"
/* Behold, the applicable snippets of code from w3d_file.h that are applicable to this module!
W3D_CHUNK_HLOD =0x00000700, // description of an HLod object (see HLodClass)
W3D_CHUNK_HLOD_HEADER, // general information such as name and version
W3D_CHUNK_HLOD_LOD_ARRAY, // wrapper around the array of objects for each level of detail
W3D_CHUNK_HLOD_LOD_ARRAY_HEADER, // info on the objects in this level of detail array
W3D_CHUNK_HLOD_SUB_OBJECT, // an object in this level of detail array
struct W3dHLodHeaderStruct
{
uint32 Version;
uint32 LodCount;
char Name[W3D_NAME_LEN];
char HierarchyName[W3D_NAME_LEN]; // name of the hierarchy tree to use (\0 if none)
};
struct W3dHLodArrayHeaderStruct
{
uint32 ModelCount;
float32 MaxScreenSize; // if model is bigger than this, switch to higher lod.
};
struct W3dHLodSubObjectStruct
{
uint32 BoneIndex;
char Name[W3D_NAME_LEN*2];
};
*/
/***********************************************************************************************
* HLodSaveClass -- The constructor builds the whole HLOD tree in a form suitable for saving *
* to a W3D file. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 9/14/1999 AJA : Created. *
*=============================================================================================*/
HLodSaveClass::HLodSaveClass (MeshConnectionsClass **connections, int lod_count, TimeValue CurTime,
char *name, const char *htree_name, Progress_Meter_Class &meter,
INodeListClass *origin_list)
: lod_array(NULL)
{
/*
** Fill in the W3dHLodHeaderStruct
*/
header.Version = W3D_CURRENT_HLOD_VERSION;
header.LodCount = lod_count;
Set_W3D_Name(header.Name, name);
Set_W3D_Name(header.HierarchyName, htree_name);
ExportLog::printf("\nExporting HLOD object: %s\n",header.Name);
ExportLog::printf(" lod count: %d\n",header.LodCount);
/*
** Create the array of stuff for each LOD.
*/
lod_array = new HLodArrayEntry[lod_count];
if (!lod_array)
throw ErrorClass("Out Of Memory!");
int i;
for (i = 0; i < lod_count; i++)
{
ExportLog::printf(" Exporting LOD Array %d\n",i);
INode *origin = connections[i]->Get_Origin();
int sub_obj_count = connections[i]->Get_Sub_Object_Count();
lod_array[i].Allocate_Sub_Objects(sub_obj_count);
lod_array[i].header.ModelCount = sub_obj_count;
float screen_size = NO_MAX_SCREEN_SIZE;
if (origin)
origin->GetUserPropFloat("MaxScreenSize", screen_size);
lod_array[i].header.MaxScreenSize = screen_size;
/*
** Create the info per mesh in this LOD.
*/
int j;
W3dHLodSubObjectStruct *sub_obj = lod_array[i].sub_obj;
ExportLog::printf(" sub-object count: %d\n",sub_obj_count);
for (j = 0; j < sub_obj_count; j++)
{
char *mesh_name;
int bone_index;
INode *mesh_node;
if (!connections[i]->Get_Sub_Object_Data(j, &mesh_name, &bone_index, &mesh_node))
throw ErrorClass("Model %s is missing connection data!", name);
strcpy(sub_obj[j].Name, mesh_name);
sub_obj[j].BoneIndex = bone_index;
ExportLog::printf(" Sub Object: %s Bone: %d\n",mesh_name,bone_index);
}
}
/*
** Copy aggregates from the Top-Level LOD
*/
int agg_count = connections[lod_count-1]->Get_Aggregate_Count();
aggregate_array.Allocate_Sub_Objects(agg_count);
aggregate_array.header.ModelCount = agg_count;
aggregate_array.header.MaxScreenSize = 0.0f;
ExportLog::printf(" Exporting Aggregates:\n");
ExportLog::printf(" aggregate count: %d\n",agg_count);
for (i=0; i<agg_count; i++) {
char *mesh_name;
int bone_index;
INode *mesh_node;
connections[lod_count-1]->Get_Aggregate_Data(i, &mesh_name, &bone_index, &mesh_node);
W3dHLodSubObjectStruct & sub_obj = aggregate_array.sub_obj[i];
strcpy(sub_obj.Name, mesh_name);
sub_obj.BoneIndex = bone_index;
ExportLog::printf(" Aggregate object: %s Bone: %d\n",mesh_name,bone_index);
}
/*
** Copy the proxy objects from the Top-Level LOD
*/
int proxy_count = connections[lod_count-1]->Get_Proxy_Count();
proxy_array.Allocate_Sub_Objects(proxy_count);
proxy_array.header.ModelCount = proxy_count;
proxy_array.header.MaxScreenSize = 0.0f;
ExportLog::printf(" Exporting Proxies\n");
ExportLog::printf(" proxy count: %d\n",proxy_count);
for (i=0; i<proxy_count; i++) {
char *mesh_name;
int bone_index;
INode *mesh_node;
connections[lod_count-1]->Get_Proxy_Data(i, &mesh_name, &bone_index, &mesh_node);
W3dHLodSubObjectStruct & sub_obj = proxy_array.sub_obj[i];
strcpy(sub_obj.Name, mesh_name);
sub_obj.BoneIndex = bone_index;
ExportLog::printf(" Proxy object: %s Bone: %d\n",mesh_name,bone_index);
}
}
/***********************************************************************************************
* ~HLodSaveClass -- Destructor blows away the dynamic memory we used. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 9/14/1999 AJA : Created. *
*=============================================================================================*/
HLodSaveClass::~HLodSaveClass (void)
{
if (lod_array)
{
delete []lod_array;
lod_array = NULL;
}
}
/***********************************************************************************************
* HLodSaveClass::Save -- Method called when saving to a W3D file. Saves the chunks that *
* define a HLOD. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 9/14/1999 AJA : Created. *
*=============================================================================================*/
bool HLodSaveClass::Save(ChunkSaveClass &csave)
{
if (!lod_array)
return false;
if (!csave.Begin_Chunk(W3D_CHUNK_HLOD))
return false;
if (!save_header(csave))
return false;
if (!save_lod_arrays(csave))
return false;
if (!save_aggregate_array(csave))
return false;
if (!save_proxy_array(csave))
return false;
if (!csave.End_Chunk())
return false;
return true;
}
/***********************************************************************************************
* HLodSaveClass::save_header -- Write the header *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 9/14/1999 AJA : Created. *
*=============================================================================================*/
bool HLodSaveClass::save_header (ChunkSaveClass &csave)
{
if (!csave.Begin_Chunk(W3D_CHUNK_HLOD_HEADER))
return false;
if (csave.Write(&header, sizeof(header)) != sizeof(header))
return false;
if (!csave.End_Chunk())
return false;
return true;
}
/***********************************************************************************************
* HLodSaveClass::save_lod_arrays -- Writes each LOD *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 9/14/1999 AJA : Created. *
*=============================================================================================*/
bool HLodSaveClass::save_lod_arrays(ChunkSaveClass &csave)
{
for (int i = 0; i < header.LodCount; i++)
{
if (!csave.Begin_Chunk(W3D_CHUNK_HLOD_LOD_ARRAY))
return false;
if (!save_sub_object_array(csave, lod_array[i]))
return false;
if (!csave.End_Chunk())
return false;
}
return true;
}
/***********************************************************************************************
* HLodSaveClass::save_aggregate_array -- save the aggregates (if any) *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/25/2000 gth : Created. *
*=============================================================================================*/
bool HLodSaveClass::save_aggregate_array(ChunkSaveClass & csave)
{
if (aggregate_array.num_sub_objects > 0) {
if (!csave.Begin_Chunk(W3D_CHUNK_HLOD_AGGREGATE_ARRAY))
return false;
if (!save_sub_object_array(csave, aggregate_array))
return false;
if (!csave.End_Chunk())
return false;
}
return true;
}
/***********************************************************************************************
* HLodSaveClass::save_proxy_array -- save the array of proxies (if any) *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 10/27/2000 gth : Created. *
*=============================================================================================*/
bool HLodSaveClass::save_proxy_array(ChunkSaveClass & csave)
{
if (proxy_array.num_sub_objects > 0) {
if (!csave.Begin_Chunk(W3D_CHUNK_HLOD_PROXY_ARRAY))
return false;
if (!save_sub_object_array(csave, proxy_array))
return false;
if (!csave.End_Chunk())
return false;
}
return true;
}
/***********************************************************************************************
* HLodSaveClass::save_sub_object_array -- Writes the mesh to bone connectivity info for each *
* mesh in an LOD. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 9/14/1999 AJA : Created. *
*=============================================================================================*/
bool HLodSaveClass::save_sub_object_array(ChunkSaveClass &csave, const HLodArrayEntry & array)
{
if (!csave.Begin_Chunk(W3D_CHUNK_HLOD_SUB_OBJECT_ARRAY_HEADER))
return false;
if (csave.Write(&(array.header), sizeof(array.header)) != sizeof(array.header))
return false;
if (!csave.End_Chunk())
return false;
for (int j = 0; j < array.num_sub_objects; j++)
{
if (!csave.Begin_Chunk(W3D_CHUNK_HLOD_SUB_OBJECT))
return false;
if (csave.Write(&(array.sub_obj[j]), sizeof(array.sub_obj[j])) != sizeof(array.sub_obj[j]))
return false;
if (!csave.End_Chunk())
return false;
}
return true;
}