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/Tools/LevelEdit/TerrainSectionPersist.cpp

503 lines
13 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 : LevelEdit *
* *
* $Archive:: /Commando/Code/Tools/LevelEdit/TerrainSectionPersist.cpp $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 11/29/01 12:22p $*
* *
* $Revision:: 9 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "stdafx.h"
#include "terrainsectionpersist.h"
#include "node.h"
#include "chunkio.h"
#include "staticphys.h"
#include "lightnode.h"
#include "terrainnode.h"
#include "nodemgr.h"
//////////////////////////////////////////////////////////////////////////////
// Constants
//////////////////////////////////////////////////////////////////////////////
enum
{
CHUNKID_VARIABLES = 0x10251130,
CHUNKID_TERRAIN_SECTION_INFO,
CHUNKID_SECTION_PERSISTDATA
};
enum
{
VARID_POSITION = 0x01,
VARID_DEF_ID,
VARID_INSTANCE_ID,
VARID_VIS_OBJ_ID,
VARID_VIS_SECTOR_ID,
VARID_NAME,
VARID_CULLLINK
};
//////////////////////////////////////////////////////////////////////////////
//
// TerrainSectionPersistClass
//
//////////////////////////////////////////////////////////////////////////////
TerrainSectionPersistClass::TerrainSectionPersistClass (void)
: m_Position (0, 0, 0),
m_DefinitionID (0),
m_InstanceID (0),
m_VisObjectID (0),
m_VisSectorID (0),
m_CullLink (-1)
{
return ;
}
//////////////////////////////////////////////////////////////////////////////
//
// ~TerrainSectionPersistClass
//
//////////////////////////////////////////////////////////////////////////////
TerrainSectionPersistClass::~TerrainSectionPersistClass (void)
{
return ;
}
//////////////////////////////////////////////////////////////////////////////
//
// Apply
//
//////////////////////////////////////////////////////////////////////////////
void
TerrainSectionPersistClass::Apply (NodeClass *node)
{
SANITY_CHECK (node != NULL)
{
return ;
}
//
// Assign the id to the section
//
node->Set_ID (Get_Instance_ID ());
node->Set_Cull_Link (m_CullLink);
//
// Assign the other IDs (these differ based on type)
//
if (node->Get_Type () == NODE_TYPE_TERRAIN) {
m_TerrainSectionInfo.Assign_Section_IDs (reinterpret_cast<TerrainNodeClass *>(node));
} else {
//
// Assign this physics object its vis ID (if applicable)
//
PhysClass *phys_obj = node->Peek_Physics_Obj ();
if (phys_obj != NULL) {
if (phys_obj->As_StaticPhysClass () != NULL) {
((StaticPhysClass *)phys_obj)->Set_Vis_Object_ID (Get_Vis_Obj_ID ());
((StaticPhysClass *)phys_obj)->Set_Vis_Sector_ID (Get_Vis_Sector_ID ());
} else if (node->Get_Type () == NODE_TYPE_LIGHT) {
((LightNodeClass *)node)->Set_Vis_Sector_ID (Get_Vis_Sector_ID ());
}
}
}
return ;
}
//////////////////////////////////////////////////////////////////////////////
//
// Initialize
//
//////////////////////////////////////////////////////////////////////////////
void
TerrainSectionPersistClass::Initialize (NodeClass *node)
{
SANITY_CHECK (node != NULL)
{
return ;
}
m_Position = node->Get_Position ();
m_InstanceID = node->Get_ID ();
m_Name = node->Get_Model_Name ();
node->Update_Cached_Cull_Link ();
m_CullLink = node->Get_Cull_Link ();
//
// Lookup the node's definition ID
//
PresetClass *preset = node->Get_Preset ();
if (preset != NULL) {
m_DefinitionID = preset->Get_Definition ()->Get_ID ();
}
if (node->Get_Type () == NODE_TYPE_LIGHT) {
//
// For lights we have to get the ID straight from the light object (not its phys obj)
//
m_VisSectorID = ((LightNodeClass *)node)->Get_Vis_Sector_ID ();
} else if (node->Get_Type () == NODE_TYPE_TERRAIN) {
//
// For terrain nodes we need to save (recursively possibly) information
// about all of its sections
//
m_TerrainSectionInfo.Build_List (((TerrainNodeClass *)node)->m_Sections);
} else {
//
// Lookup the vis IDs for this object (if it has any)
//
PhysClass *phys_obj = node->Peek_Physics_Obj ();
if (phys_obj != NULL) {
StaticPhysClass *static_phys_obj = phys_obj->As_StaticPhysClass ();
if (static_phys_obj != NULL) {
m_VisObjectID = static_phys_obj->Get_Vis_Object_ID ();
m_VisSectorID = static_phys_obj->Get_Vis_Sector_ID ();
//
// Record the cull-link index from the static object
//
AABTreeLinkClass *link = (AABTreeLinkClass *)static_phys_obj->Get_Cull_Link ();
if (link != NULL) {
m_CullLink = link->Node->Index;
}
}
}
}
return ;
}
/////////////////////////////////////////////////////////////////
//
// Save
//
/////////////////////////////////////////////////////////////////
bool
TerrainSectionPersistClass::Save (ChunkSaveClass &csave)
{
csave.Begin_Chunk (CHUNKID_VARIABLES);
Save_Variables (csave);
csave.End_Chunk ();
csave.Begin_Chunk (CHUNKID_TERRAIN_SECTION_INFO);
m_TerrainSectionInfo.Save (csave);
csave.End_Chunk ();
return true;
}
/////////////////////////////////////////////////////////////////
//
// Load
//
/////////////////////////////////////////////////////////////////
bool
TerrainSectionPersistClass::Load (ChunkLoadClass &cload)
{
while (cload.Open_Chunk ()) {
switch (cload.Cur_Chunk_ID ()) {
case CHUNKID_VARIABLES:
Load_Variables (cload);
break;
case CHUNKID_TERRAIN_SECTION_INFO:
m_TerrainSectionInfo.Load (cload);
break;
}
cload.Close_Chunk ();
}
return true;
}
/////////////////////////////////////////////////////////////////
//
// Save_Variables
//
/////////////////////////////////////////////////////////////////
bool
TerrainSectionPersistClass::Save_Variables (ChunkSaveClass &csave)
{
WRITE_MICRO_CHUNK (csave, VARID_POSITION, m_Position);
WRITE_MICRO_CHUNK (csave, VARID_DEF_ID, m_DefinitionID);
WRITE_MICRO_CHUNK (csave, VARID_INSTANCE_ID, m_InstanceID);
WRITE_MICRO_CHUNK (csave, VARID_VIS_OBJ_ID, m_VisObjectID);
WRITE_MICRO_CHUNK (csave, VARID_VIS_SECTOR_ID, m_VisSectorID);
WRITE_MICRO_CHUNK (csave, VARID_CULLLINK, m_CullLink);
WRITE_MICRO_CHUNK_WWSTRING (csave, VARID_NAME, m_Name);
return true;
}
/////////////////////////////////////////////////////////////////
//
// Load_Variables
//
/////////////////////////////////////////////////////////////////
bool
TerrainSectionPersistClass::Load_Variables (ChunkLoadClass &cload)
{
while (cload.Open_Micro_Chunk ()) {
switch (cload.Cur_Micro_Chunk_ID ()) {
READ_MICRO_CHUNK (cload, VARID_POSITION, m_Position);
READ_MICRO_CHUNK (cload, VARID_DEF_ID, m_DefinitionID);
READ_MICRO_CHUNK (cload, VARID_INSTANCE_ID, m_InstanceID);
READ_MICRO_CHUNK (cload, VARID_VIS_OBJ_ID, m_VisObjectID);
READ_MICRO_CHUNK (cload, VARID_VIS_SECTOR_ID, m_VisSectorID);
READ_MICRO_CHUNK (cload, VARID_CULLLINK, m_CullLink);
READ_MICRO_CHUNK_WWSTRING (cload, VARID_NAME, m_Name);
}
cload.Close_Micro_Chunk ();
}
return true;
}
/////////////////////////////////////////////////////////////////
//
// Build_List
//
/////////////////////////////////////////////////////////////////
void
TerrainSectionPersistListClass::Build_List (DynamicVectorClass<NodeClass *> &node_list)
{
Free_List ();
//
// Build an identifier for each of the sections (based on position
// and type).
//
for (int index = 0; index < node_list.Count (); index ++) {
NodeClass *node = node_list[index];
//
// Create a new perist object to store the node's data
//
TerrainSectionPersistClass *persist_obj = new TerrainSectionPersistClass;
persist_obj->Initialize (node);
Add (persist_obj);
}
return ;
}
//////////////////////////////////////////////////////////////////////////////
//
// Free_List
//
//////////////////////////////////////////////////////////////////////////////
void
TerrainSectionPersistListClass::Free_List (void)
{
//
// Free each persist object
//
for (int index = 0; index < Count (); index ++) {
SAFE_DELETE ((*this)[index]);
}
Delete_All ();
return ;
}
/////////////////////////////////////////////////////////////////
//
// Save
//
/////////////////////////////////////////////////////////////////
void
TerrainSectionPersistListClass::Save (ChunkSaveClass &csave)
{
//
// Write a chunk for each section so we can store instance
// specific data.
//
for (int index = 0; index < Count (); index ++) {
TerrainSectionPersistClass *persist_obj = (*this)[index];
csave.Begin_Chunk (CHUNKID_SECTION_PERSISTDATA);
persist_obj->Save (csave);
csave.End_Chunk ();
}
return ;
}
/////////////////////////////////////////////////////////////////
//
// Load
//
/////////////////////////////////////////////////////////////////
void
TerrainSectionPersistListClass::Load (ChunkLoadClass &cload)
{
Free_List ();
while (cload.Open_Chunk ()) {
switch (cload.Cur_Chunk_ID ()) {
case CHUNKID_SECTION_PERSISTDATA:
{
//
// Create a persist object and get it to load the data
//
TerrainSectionPersistClass *persist_info = new TerrainSectionPersistClass;
persist_info->Load (cload);
Add (persist_info);
}
break;
}
cload.Close_Chunk ();
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Initialize_Virgin_Sections
//
////////////////////////////////////////////////////////////////
void
TerrainSectionPersistListClass::Initialize_Virgin_Sections (void)
{
//
// Give each section a unique ID if we couldn't match it up with
// its saved version.
//
for (int index = 0; index < m_VirginSections.Count (); index ++) {
NodeClass *node = m_VirginSections[index];
if (node != NULL) {
node->Set_ID (NodeMgrClass::Get_Node_ID (node->Get_Type ()));
NodeMgrClass::Setup_Node_Identity (*node);
}
}
//
// Recurse into each sub-section
//
for (index = 0; index < Count (); index ++) {
(*this)[index]->Initialize_Virgin_Sections ();
}
m_VirginSections.Delete_All ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Assign_Section_IDs
//
////////////////////////////////////////////////////////////////
void
TerrainSectionPersistListClass::Assign_Section_IDs (TerrainNodeClass *node)
{
m_VirginSections = node->m_Sections;
for (int index = 0; index < Count (); index ++) {
TerrainSectionPersistClass *persist_obj = (*this)[index];
//
// Try to match this ID up with
//
bool found = false;
for (int node_index = 0; node_index < m_VirginSections.Count () && !found; node_index ++) {
NodeClass *sub_node = m_VirginSections[node_index];
StringClass model_name = sub_node->Get_Model_Name ();
//
// Do the types match up?
//
if ( sub_node->Get_Preset_ID () == persist_obj->Get_Def_ID () &&
::lstrcmpi (model_name, persist_obj->Get_Name ()) == 0)
{
//
// Do the positions match up?
//
const Vector3 &pos = sub_node->Get_Position ();
float delta = (pos - persist_obj->Get_Position ()).Length2 ();
if (delta < 1.0F) {
//
// Success! Assign the ID(s)
//
persist_obj->Apply (sub_node);
m_VirginSections.Delete (node_index);
found = true;
}
}
}
//
// Warn the user
//
if (!found) {
CString message;
message.Format ( "Unable to match static ID %d (%s) with a terrain section!\r\n",
persist_obj->Get_Instance_ID (),
(LPCTSTR)persist_obj->Get_Name ());
::Output_Message (message);
}
}
return ;
}