419 lines
12 KiB
C++
419 lines
12 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/VisMgr.cpp $*
|
|
* *
|
|
* Author:: Patrick Smith *
|
|
* *
|
|
* $Modtime:: 3/26/01 2:46p $*
|
|
* *
|
|
* $Revision:: 8 $*
|
|
* *
|
|
*---------------------------------------------------------------------------------------------*
|
|
* Functions: *
|
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
|
|
#include "stdafx.h"
|
|
#include "vismgr.h"
|
|
#include "sceneeditor.h"
|
|
#include "matrix3d.h"
|
|
#include "w3d_file.h"
|
|
#include "chunkio.h"
|
|
#include "utils.h"
|
|
#include "staticphys.h"
|
|
#include "node.h"
|
|
#include "nodemgr.h"
|
|
#include "cameramgr.h"
|
|
#include "camera.h"
|
|
#include "vispointnode.h"
|
|
#include "groupmgr.h"
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Local constants
|
|
///////////////////////////////////////////////////////////////////////
|
|
enum
|
|
{
|
|
CHUNKID_VIS_OBJECT = 0x06150222,
|
|
CHUNKID_VIS_DATA
|
|
};
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Local structures
|
|
///////////////////////////////////////////////////////////////////////
|
|
typedef struct
|
|
{
|
|
char mesh_name[W3D_NAME_LEN * 2];
|
|
Matrix3D transform;
|
|
int vis_id;
|
|
}
|
|
VIS_OBJECT_INFO;
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Export_Remap_Data
|
|
//
|
|
///////////////////////////////////////////////////////////////////////
|
|
void
|
|
VisMgrClass::Export_Remap_Data (ChunkSaveClass &csave)
|
|
{
|
|
//
|
|
// Get the list of all static objects from the physics scene
|
|
//
|
|
RefPhysListIterator iterator = ::Get_Scene_Editor ()->Get_Static_Object_Iterator ();
|
|
for (iterator.First (); !iterator.Is_Done (); iterator.Next ()) {
|
|
|
|
//
|
|
// Double-check to make sure this object is static
|
|
//
|
|
PhysClass *phys_obj = iterator.Peek_Obj ();
|
|
StaticPhysClass *static_phys_obj = phys_obj->As_StaticPhysClass ();
|
|
if (static_phys_obj != NULL && static_phys_obj->Peek_Model () != NULL) {
|
|
|
|
//
|
|
// Build a structure of information about this static object
|
|
//
|
|
VIS_OBJECT_INFO obj_info;
|
|
::lstrcpy (obj_info.mesh_name, static_phys_obj->Peek_Model ()->Get_Name ());
|
|
obj_info.transform = static_phys_obj->Get_Transform ();
|
|
obj_info.vis_id = static_phys_obj->Get_Vis_Object_ID ();
|
|
|
|
//
|
|
// Save the object info to its own chunk
|
|
//
|
|
csave.Begin_Chunk (CHUNKID_VIS_OBJECT);
|
|
csave.Write (&obj_info, sizeof (obj_info));
|
|
csave.End_Chunk ();
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Now export all the vis data
|
|
//
|
|
csave.Begin_Chunk (CHUNKID_VIS_DATA);
|
|
::Get_Scene_Editor ()->Export_Vis_Data (csave);
|
|
csave.End_Chunk ();
|
|
return ;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Import_Remap_Data
|
|
//
|
|
///////////////////////////////////////////////////////////////////////
|
|
void
|
|
VisMgrClass::Import_Remap_Data (ChunkLoadClass &cload)
|
|
{
|
|
bool is_valid = true;
|
|
|
|
//
|
|
// Start with a clean slate
|
|
//
|
|
::Get_Scene_Editor ()->Discard_Vis ();
|
|
|
|
//
|
|
// Process all the chunks in this file
|
|
//
|
|
while (is_valid && cload.Open_Chunk ()) {
|
|
|
|
switch (cload.Cur_Chunk_ID ())
|
|
{
|
|
//
|
|
// Try to remap the vis-id of this object with the
|
|
// vis-id of the object in the level where vis was generated.
|
|
//
|
|
case CHUNKID_VIS_OBJECT:
|
|
{
|
|
VIS_OBJECT_INFO info;
|
|
cload.Read (&info, sizeof (info));
|
|
|
|
//
|
|
// Attempt to find this physics object in the scene
|
|
//
|
|
StaticPhysClass *vis_object = Find_Static_Phys_Object (info.mesh_name, info.transform);
|
|
if (vis_object != NULL) {
|
|
vis_object->Set_Vis_Object_ID (info.vis_id);
|
|
} else {
|
|
is_valid = false;
|
|
}
|
|
}
|
|
break;
|
|
|
|
//
|
|
// Import the actual vis data. If we successfully
|
|
// remapped the vis-id's of all the objects, then this
|
|
// should work seamlessly
|
|
//
|
|
case CHUNKID_VIS_DATA:
|
|
{
|
|
::Get_Scene_Editor ()->Import_Vis_Data (cload);
|
|
}
|
|
break;
|
|
}
|
|
|
|
cload.Close_Chunk ();
|
|
}
|
|
|
|
//
|
|
// Warn the user if we failed
|
|
//
|
|
if (is_valid == false) {
|
|
::MessageBox ( NULL,
|
|
"The static geometry in this level does not match the geometry of the level in which vis was generated. Unable to import vis data into this level.",
|
|
"Import Error",
|
|
MB_ICONERROR | MB_OK);
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Find_Static_Phys_Object
|
|
//
|
|
///////////////////////////////////////////////////////////////////////
|
|
StaticPhysClass *
|
|
VisMgrClass::Find_Static_Phys_Object (LPCTSTR mesh_name, const Matrix3D &tm)
|
|
{
|
|
StaticPhysClass *retval = NULL;
|
|
|
|
//
|
|
// Get the list of all static objects from the physics scene
|
|
//
|
|
RefPhysListIterator iterator = ::Get_Scene_Editor ()->Get_Static_Object_Iterator ();
|
|
for (iterator.First (); retval == NULL && !iterator.Is_Done (); iterator.Next ()) {
|
|
|
|
//
|
|
// Double-check to make sure this object is static
|
|
//
|
|
PhysClass *phys_obj = iterator.Peek_Obj ();
|
|
StaticPhysClass *static_phys_obj = phys_obj->As_StaticPhysClass ();
|
|
if (static_phys_obj != NULL && static_phys_obj->Peek_Model () != NULL) {
|
|
|
|
//
|
|
// Is this the object we are looking for?
|
|
//
|
|
LPCTSTR curr_name = static_phys_obj->Peek_Model ()->Get_Name ();
|
|
Matrix3D curr_tm = static_phys_obj->Get_Transform ();
|
|
if ((::lstrcmpi (curr_name, mesh_name) == 0) && (curr_tm == tm)) {
|
|
retval = static_phys_obj;
|
|
}
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Build_Node_List
|
|
//
|
|
///////////////////////////////////////////////////////////////////////
|
|
void
|
|
VisMgrClass::Build_Node_List (DynamicVectorClass<NodeClass *> &node_list, bool selection_only)
|
|
{
|
|
if (selection_only) {
|
|
SelectionMgrClass &sel_mgr = ::Get_Scene_Editor ()->Get_Selection_Mgr ();
|
|
|
|
//
|
|
// Build a list of the selected nodes that should be used to calculate
|
|
// vis renders
|
|
//
|
|
for (int index = 0; index < sel_mgr.Get_Count (); index ++) {
|
|
NodeClass *node = sel_mgr.Get_At (index);
|
|
if (node != NULL && node->Is_Static ()) {
|
|
Add_Nodes_To_List (node_list, node);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Build a list of nodes that should be used to calculate
|
|
// vis renders
|
|
//
|
|
for (NodeClass *node = ::Get_Node_Mgr ().Get_First ();
|
|
node != NULL;
|
|
node = ::Get_Node_Mgr ().Get_Next (node))
|
|
{
|
|
if (node->Is_Static ()) {
|
|
Add_Nodes_To_List (node_list, node);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Add_Nodes_To_List
|
|
//
|
|
///////////////////////////////////////////////////////////////////////
|
|
void
|
|
VisMgrClass::Add_Nodes_To_List
|
|
(
|
|
DynamicVectorClass<NodeClass *> &node_list,
|
|
NodeClass *node
|
|
)
|
|
{
|
|
//
|
|
// If the node is an aggregate (like a terrain), we are adding the
|
|
// sub-nodes directly into the list
|
|
//
|
|
int sub_count = node->Get_Sub_Node_Count ();
|
|
if (sub_count > 0) {
|
|
for (int index = 0; index < sub_count; index ++) {
|
|
NodeClass *sub_node = node->Get_Sub_Node (index);
|
|
|
|
//
|
|
// Recurse into this node
|
|
//
|
|
Add_Nodes_To_List (node_list, sub_node);
|
|
}
|
|
} else {
|
|
if (node->Has_Vis_Sectors ()) {
|
|
node_list.Add (node);
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Render_Manual_Vis_Points
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
VisMgrClass::Render_Manual_Vis_Points
|
|
(
|
|
bool farm_mode,
|
|
int processor_index,
|
|
int total_processors,
|
|
VIS_POINT_RENDERED_CALLBACK callback,
|
|
DWORD param
|
|
)
|
|
{
|
|
SceneEditorClass *scene_editor = ::Get_Scene_Editor ();
|
|
VisLogClass &vis_log = scene_editor->Get_Vis_Log ();
|
|
CameraClass *camera = new CameraClass (*::Get_Camera_Mgr ()->Get_Camera ());
|
|
bool keep_going = true;
|
|
|
|
//
|
|
// Bulid a list of vis points
|
|
//
|
|
DynamicVectorClass<VisPointNodeClass *> point_list;
|
|
VisPointNodeClass *vis_point = NULL;
|
|
for ( vis_point = (VisPointNodeClass *)NodeMgrClass::Get_First (NODE_TYPE_VIS_POINT);
|
|
vis_point != NULL && keep_going;
|
|
vis_point = (VisPointNodeClass *)NodeMgrClass::Get_Next (vis_point, NODE_TYPE_VIS_POINT))
|
|
{
|
|
point_list.Add (vis_point);
|
|
}
|
|
|
|
//
|
|
// Determine which nodes to process if we
|
|
// are in farm mode.
|
|
//
|
|
if (farm_mode) {
|
|
int points = point_list.Count ();
|
|
float points_per_processor = ((float)points) / ((float)total_processors);
|
|
|
|
int starting_point = (int)::floor (points_per_processor* (float)processor_index);
|
|
int ending_point = (int)::ceil (points_per_processor * (float)(processor_index + 1));
|
|
ending_point = min (ending_point, points);
|
|
|
|
//
|
|
// Copy the points from the total list into a temporary list
|
|
//
|
|
DynamicVectorClass<VisPointNodeClass *> temp_list;
|
|
for (int index = starting_point; index < ending_point; index ++) {
|
|
temp_list.Add (point_list[index]);
|
|
}
|
|
|
|
point_list.Delete_All ();
|
|
point_list = temp_list;
|
|
}
|
|
|
|
//
|
|
// Loop over all manual vis points in the level
|
|
//
|
|
for (int index = 0; index < point_list.Count (); index ++)
|
|
{
|
|
DWORD before = ::GetTickCount ();
|
|
VisPointNodeClass *vis_point = point_list[index];
|
|
|
|
// Set the camera up to use the same settings we used when
|
|
// we generated this manual vis point
|
|
//
|
|
vis_point->Setup_Camera (*camera);
|
|
|
|
//
|
|
// Get the camera's clip plane settings
|
|
//
|
|
float znear = 0;
|
|
float zfar = 0;
|
|
camera->Get_Clip_Planes (znear, zfar);
|
|
|
|
//
|
|
// Convert the object transform to a camera transform
|
|
//
|
|
Matrix3D transform = vis_point->Get_Transform ();
|
|
Matrix3D cam_transform (Vector3 (0, -1, 0), Vector3 (0, 0, 1), Vector3 (-1, 0, 0), Vector3 (0, 0, 0));
|
|
Matrix3D new_transform = transform * cam_transform;
|
|
Vector3 sample_location = (new_transform * Vector3 (0, 0, -znear));
|
|
|
|
//
|
|
//
|
|
// Render vis for this point
|
|
//
|
|
VisSampleClass vis_sample = scene_editor->Update_Vis (sample_location,
|
|
new_transform,
|
|
VisDirBitsType(VIS_FORWARD_BIT | VIS_DONT_RECENTER),
|
|
camera);
|
|
vis_log.Log_Sample (vis_sample);
|
|
scene_editor->Create_Vis_Point (transform);
|
|
|
|
//
|
|
// Notify the callback that we've renedered a point
|
|
//
|
|
if (callback != NULL) {
|
|
DWORD after = ::GetTickCount ();
|
|
keep_going = (*callback) (after - before, param);
|
|
}
|
|
}
|
|
|
|
MEMBER_RELEASE (camera);
|
|
return ;
|
|
}
|